Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,344 changes: 1,286 additions & 58 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions packages/agentspec-core/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.agentspec/
tsp-output.txt
99 changes: 99 additions & 0 deletions packages/agentspec-core/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# @agentspec/core

> **The OpenAPI of agent definitions — framework-agnostic, compiler-validated, narrative-native.**

`@agentspec/core` is a TypeSpec library that defines 12 universal agent primitives via TypeSpec decorators. Define your agent once in a `.tsp` file. Framework-specific emitters produce native artifacts for each target.

## Prerequisites

Before publishing `@agentspec/core@0.1.0`, complete these gates:

- [ ] Register the `agentspec` npm org (P0 — do this immediately)
- [ ] Enable npm org 2FA enforcement
- [ ] Configure provenance attestation on publish workflow (`npm publish --provenance`)
- [ ] Restrict publish token to GitHub OIDC identity (no static tokens)

## Install

```bash
npm install --save-dev @agentspec/core @typespec/compiler
```

Peer dependency: `@typespec/compiler >=0.60.0 <0.62.0`

## The 12 decorators

| Decorator | Purpose |
|---|---|
| `@agent(id, description)` | Marks a model as an agent; `id` is the wire identifier |
| `@role(title)` | Agent's role/purpose |
| `@version(semver)` | Agent schema version |
| `@instruction(text)` | System prompt ⚠️ omitted from A2A Agent Card by default |
| `@capability(id, description?)` | What this agent can do (repeatable) |
| `@boundary(handles, doesNotHandle)` | Explicit scope declaration |
| `@tool(id, description?)` | External tool at runtime (repeatable) |
| `@knowledge(source, description?)` | Data source (repeatable) |
| `@memory(strategy)` | Persistence strategy |
| `@conversationStarter(prompt)` | Suggested prompt (repeatable) |
| `@inputMode(mode)` | Supported input modality (repeatable) |
| `@outputMode(mode)` | Supported output modality (repeatable) |

## Quick start

```typespec
import "@agentspec/core";
using AgentSpec;

@agent("flight", "Architecture decisions that compound")
@role("Lead")
@version("0.1.0")
@instruction("You are Flight, the technical lead...")
@capability("architecture-review", "Evaluates system-level design decisions")
@boundary(
handles: "Architecture decisions, PR reviews",
doesNotHandle: "Feature implementation, test writing"
)
@memory(MemoryStrategy.persistent)
@conversationStarter("What's the right architecture for this feature?")
model Flight {}
```

Run `tsp compile` → produces `.agentspec/flight-agent-manifest.json`.

## Security

⚠️ **Decorator values are committed plaintext.** Do not include:
- Email addresses or phone numbers (PII)
- Secrets, API keys, bearer tokens
- Internal URLs or SharePoint links

The compiler emits a warning (configurable to error) when PII patterns are detected.

## A2A Agent Card

`@instruction` is **omitted from A2A Agent Card output by default**. Opt in:

```yaml
# tspconfig.yaml
options:
"@agentspec/core":
a2a-publish-instructions: true
```

Sensitivity gates:
- `"public"` — card may be served at `/.well-known/agent-card`
- `"internal"` *(default)* — card generated but not published
- `"restricted"` — no card generated

## TypeSpec version policy

Peer dep is minor-locked: `>=0.60.0 <0.62.0`. TypeSpec is pre-1.0 and ships breaking changes between minors. When TypeSpec ships a new minor: test, update peer dep range, update lockfile in a single PR. Never float the range.

## Examples

- [`examples/weather-agent.tsp`](./examples/weather-agent.tsp) — minimal single-agent example
- [`examples/squad-team.tsp`](./examples/squad-team.tsp) — multi-agent team definition

## License

MIT
92 changes: 92 additions & 0 deletions packages/agentspec-core/examples/squad-team.tsp
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import "@agentspec/core";
using AgentSpec;

/**
* Squad team example — the actual agents on this project.
* Demonstrates multi-agent definition in a single .tsp file.
* Run: tsp compile examples/squad-team.tsp
* Output: .agentspec/{id}-agent-manifest.json for each agent
*/

@agent("flight", "Architecture decisions that compound — patterns that make future features easier")
@role("Lead")
@version("0.1.0")
@instruction("""
You are Flight, the technical lead on this project. Your job is to make
architecture decisions that compound: every choice should open more doors
than it closes. You review PRs for architectural coherence, not style.
You write proposals before code. You never break existing contracts.
""")
@capability("architecture-review", "Evaluates system-level design decisions")
@capability("proposal-authoring", "Writes structured design proposals before implementation")
@boundary(
handles: "Architecture decisions, PR reviews, scope triage, proposal authoring",
doesNotHandle: "Feature implementation, release management, test writing"
)
@memory(MemoryStrategy.persistent)
@conversationStarter("What's the right architecture for this feature?")
@conversationStarter("Review this PR for architectural issues")
model Flight {}

@agent("eecom", "Core systems — make it work, then make it right")
@role("Core Dev")
@version("0.1.0")
@instruction("""
You are EECOM, the core dev. You implement the features that matter:
practical, thorough, correct. You write code that compiles, tests that pass,
and implementations that match the spec. When something is ambiguous, you
resolve it by reading the code, not by asking. You work until it works.
""")
@capability("feature-implementation", "Implements well-specified features end-to-end")
@capability("debugging", "Diagnoses and fixes bugs with reproducible test cases")
@capability("scaffolding", "Creates new packages, modules, and boilerplate")
@boundary(
handles: "Feature implementation, bug fixes, scaffolding, TypeScript/Node.js development",
doesNotHandle: "Architecture decisions, release management, security policy"
)
@memory(MemoryStrategy.persistent)
@conversationStarter("Implement this feature from the spec")
@conversationStarter("Here's a failing test — make it pass")
model EECOM {}

@agent("fido", "Test coverage and quality gates — if it isn't tested, it isn't done")
@role("Tester")
@version("0.1.0")
@capability("test-authoring", "Writes unit, integration, and snapshot tests")
@capability("coverage-analysis", "Identifies untested paths and coverage gaps")
@boundary(
handles: "Test strategy, test authoring, coverage analysis, quality gates",
doesNotHandle: "Feature implementation, deployment, architecture"
)
@memory(MemoryStrategy.session)
@conversationStarter("What test cases are missing for this feature?")
@conversationStarter("Write tests for this module")
model FIDO {}

@agent("retro", "Security review — trust boundaries, secrets hygiene, audit trail")
@role("Security Reviewer")
@version("0.1.0")
@capability("security-review", "Reviews code for trust boundary violations and secrets leakage")
@capability("pii-audit", "Identifies PII exposure in committed artifacts")
@boundary(
handles: "Security review, secrets hygiene, trust boundary analysis, PII audit",
doesNotHandle: "Feature implementation, test writing, documentation"
)
@memory(MemoryStrategy.persistent)
@conversationStarter("Review this code for security issues")
@conversationStarter("Does this change expose any secrets?")
model RETRO {}

@agent("control", "API contracts and type integrity — the spec is the law")
@role("API Guardian")
@version("0.1.0")
@capability("contract-review", "Reviews API surfaces for breaking changes and contract violations")
@capability("type-integrity", "Ensures TypeScript types match runtime behavior")
@boundary(
handles: "API contract review, type integrity, breaking change analysis",
doesNotHandle: "Feature implementation, security, deployment"
)
@memory(MemoryStrategy.persistent)
@conversationStarter("Does this change break any existing contracts?")
@conversationStarter("Review the types in this PR")
model CONTROL {}
33 changes: 33 additions & 0 deletions packages/agentspec-core/examples/weather-agent.tsp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import "@agentspec/core";
using AgentSpec;

/**
* Minimal weather agent example — illustrates the core decorator API.
* Run: tsp compile examples/weather-agent.tsp
* Output: .agentspec/weather-agent-manifest.json
*/

@agent("weather-agent", "Provides real-time weather data and forecasts for any location")
@role("Weather Specialist")
@version("0.1.0")
@instruction("""
You are a weather assistant. Answer questions about current conditions,
forecasts, and historical weather data. Always cite your data source.
Provide temperatures in both Celsius and Fahrenheit unless the user specifies.
""")
@capability("current-conditions", "Real-time weather for a given location")
@capability("forecast", "Multi-day weather forecasts up to 10 days")
@capability("historical", "Historical weather data and averages")
@boundary(
"Weather queries, forecasts, climate data, severe weather alerts",
"Air quality, UV index, astronomical events, sports or travel recommendations"
)
@tool("openweathermap", "OpenWeatherMap API for current conditions and forecasts")
@tool("noaa", "NOAA API for historical data and severe weather alerts")
@memory(MemoryStrategy.session)
@conversationStarter("What's the weather like in Seattle right now?")
@conversationStarter("Will it rain this weekend in Chicago?")
@inputMode(InputMode.text)
@outputMode(OutputMode.text)
@outputMode(OutputMode.structured)
model WeatherAgent {}
103 changes: 103 additions & 0 deletions packages/agentspec-core/generated/agent-manifest.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://agentspec.dev/schemas/agent-manifest/0.1.json",
"title": "AgentManifest",
"description": "The @agentspec/core agent manifest schema. Generated from model AgentManifest in lib/main.tsp.",
"type": "object",
"required": ["specVersion", "id", "description", "sensitivity", "behavior", "runtime", "communication"],
"properties": {
"$schema": { "type": "string" },
"specVersion": {
"type": "string",
"description": "Protocol version — bumps only on breaking schema changes. Distinct from package semver."
},
"id": { "type": "string" },
"description": { "type": "string" },
"role": { "type": "string" },
"agentVersion": { "type": "string" },
"sensitivity": {
"type": "string",
"enum": ["public", "internal", "restricted"],
"default": "internal"
},
"behavior": {
"type": "object",
"required": ["capabilities"],
"properties": {
"instructions": { "type": "string" },
"capabilities": {
"type": "array",
"items": {
"type": "object",
"required": ["id"],
"properties": {
"id": { "type": "string" },
"description": { "type": "string" },
"level": { "type": "string" }
}
}
},
"boundaries": {
"type": "object",
"required": ["handles", "doesNotHandle"],
"properties": {
"handles": { "type": "string" },
"doesNotHandle": { "type": "string" }
}
}
}
},
"runtime": {
"type": "object",
"required": ["tools", "knowledge", "memory"],
"properties": {
"tools": {
"type": "array",
"items": {
"type": "object",
"required": ["id"],
"properties": {
"id": { "type": "string" },
"description": { "type": "string" }
}
}
},
"knowledge": {
"type": "array",
"items": {
"type": "object",
"required": ["source"],
"properties": {
"source": { "type": "string" },
"description": { "type": "string" }
}
}
},
"memory": {
"type": "string",
"enum": ["none", "session", "persistent", "shared"]
}
}
},
"communication": {
"type": "object",
"required": ["conversationStarters", "inputModes", "outputModes"],
"properties": {
"conversationStarters": {
"type": "array",
"items": { "type": "string" }
},
"inputModes": {
"type": "array",
"items": { "type": "string", "enum": ["text", "file", "image", "audio", "structured"] }
},
"outputModes": {
"type": "array",
"items": { "type": "string", "enum": ["text", "file", "image", "audio", "structured", "stream"] }
}
}
}
},
"additionalProperties": false,
"$comment": "This schema is generated from model AgentManifest in lib/main.tsp. Do not edit by hand — regenerate with: tsp compile --emit @typespec/json-schema"
}
Loading
Loading