@agentproto/corpus-cli 0.1.0-alpha.6 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,193 @@
1
+ # AIP-52 — Harness · Implementer Guide
2
+
3
+ > **Status:** Draft
4
+ > **Schema:** `HARNESS.schema.json`
5
+ > **Canonical runtime:** `@mastra/core/harness` (Mastra's `Harness` class)
6
+
7
+ ## What is a Harness?
8
+
9
+ A Harness is a **stateful, multi-mode conversational shell**. Where an AIP-42 Agent is
10
+ a single-purpose inference unit, a Harness composes multiple agents into an orchestrated
11
+ shell that can:
12
+
13
+ - Switch between **modes** (e.g. plan → execute → review) with distinct agents, tools,
14
+ and permission rules per mode.
15
+ - Hold a **typed conversation state** (`stateSchema` + `initialState`) that persists
16
+ across turns and is visible to all modes.
17
+ - Spawn **subagents** via the built-in `subagent` tool (forked or isolated thread).
18
+ - Gate tool calls via **tool-approval gates** (`permissions`, `submit_plan` builtin).
19
+ - Run **heartbeat handlers** in the background (OM reflector, progress reporter, etc.).
20
+
21
+ A Harness IS NOT a subtype of AIP-42. Agents are referenced _from_ harness manifests
22
+ via `$resolver` / `$ref`; the harness is the orchestrator, not the agent itself.
23
+
24
+ ## Filesystem layout
25
+
26
+ ```
27
+ .harness/
28
+ <slug>.md ← one manifest per harness (YAML frontmatter + prose body)
29
+ ```
30
+
31
+ The markdown body (after the frontmatter block) is purely documentary — it is not
32
+ parsed by the runtime.
33
+
34
+ ## Manifest shape
35
+
36
+ ```yaml
37
+ ---
38
+ spec: harness/v1
39
+ id: my-harness
40
+ name: My Harness
41
+ version: 1.0.0
42
+ description: One-sentence description.
43
+
44
+ storage:
45
+ $resolver: mastra.storage.composite # must resolve to MastraCompositeStore
46
+
47
+ modes:
48
+ - id: plan
49
+ default: true
50
+ name: Plan
51
+ agent: { $resolver: my-agent }
52
+ model: { $resolver: model.plan }
53
+ builtins:
54
+ ask_user: true
55
+ submit_plan: required # blocks until approved
56
+ transitionsTo: execute
57
+
58
+ - id: execute
59
+ name: Execute
60
+ agent: { $resolver: my-agent }
61
+ model: { $resolver: model.execute }
62
+ builtins:
63
+ ask_user: false
64
+ subagent: true
65
+
66
+ models:
67
+ resolver: { $resolver: model.resolver } # (modelId) => LanguageModel
68
+
69
+ subagents:
70
+ - id: researcher
71
+ name: Researcher
72
+ description: Searches and summarises.
73
+ agent: { $resolver: researcher-agent }
74
+ forked: false # isolated thread (default)
75
+ maxSteps: 20
76
+
77
+ memory:
78
+ $resolver: mastra.memory.thread
79
+
80
+ observability:
81
+ $resolver: my.telemetry.entrypoint
82
+ ---
83
+ ```
84
+
85
+ ### Required fields
86
+
87
+ | Field | Type | Notes |
88
+ |---|---|---|
89
+ | `spec` | `"harness/v1"` | Version pin. |
90
+ | `id` | `string` (kebab-case) | Registry key; must be unique within a workspace. |
91
+ | `name` | `string` | Human label (≤ 80 chars). |
92
+ | `version` | `semver` | Bump on breaking mode / subagent changes. |
93
+ | `storage` | `refOrString` | Must resolve to `MastraCompositeStore`. |
94
+ | `modes` | `mode[]` | At least one mode required. |
95
+ | `models.resolver` | `refOrString` | `(modelId: string) => LanguageModel`. |
96
+
97
+ ## Resolver syntax (`refOrString`)
98
+
99
+ Three forms are accepted for any field annotated `$ref: "#/$defs/refOrString"`:
100
+
101
+ ```yaml
102
+ # 1. Inline literal string (if the field accepts a raw value)
103
+ storage: "fs://./data/state"
104
+
105
+ # 2. Named registry ref — resolved at harness boot from the resolver registry
106
+ storage:
107
+ $ref: myApp.storage.main
108
+
109
+ # 3. Dynamic resolver call — arbitrary typed input passed to the resolver
110
+ storage:
111
+ $resolver: myApp.storage.build
112
+ input: { path: "./state", ttl: 3600 }
113
+ ```
114
+
115
+ ## Modes
116
+
117
+ Each mode entry defines **one operational context**:
118
+
119
+ | Field | Required | Notes |
120
+ |---|---|---|
121
+ | `id` | ✅ | Unique within this harness (kebab-case recommended). |
122
+ | `agent` | ✅ | Backing `Mastra Agent` instance (via resolver). |
123
+ | `model` | ✅ | Resolver returning `{ modelId, defaultModelId?, allowed? }`. |
124
+ | `default` | — | First mode with `default: true` activates on new sessions. |
125
+ | `transitionsTo` | — | Auto-switch to this mode id after `submit_plan` approval. |
126
+ | `tools` | — | Replaces the agent's built-in tools entirely (mutually exclusive with `additionalTools`). |
127
+ | `prompt.blocks` | — | Instruction blocks resolved and concatenated as `instructions`. |
128
+ | `builtins.*` | — | Enable / disable built-in tools: `ask_user`, `submit_plan`, `task_write`, `task_check`, `subagent`. |
129
+ | `permissions` | — | `PermissionRules` resolver — default tool-approval policy for this mode. |
130
+
131
+ ### `submit_plan` values
132
+
133
+ | Value | Behaviour |
134
+ |---|---|
135
+ | `false` (default) | Tool not registered. |
136
+ | `true` | Registered; plan approval is optional. |
137
+ | `"required"` | Registered; the mode blocks on plan approval and then transitions to `transitionsTo`. |
138
+
139
+ ## Subagents
140
+
141
+ ```yaml
142
+ subagents:
143
+ - id: drafter
144
+ name: Drafter
145
+ description: Writes first-draft content.
146
+ agent: { $resolver: drafter-agent }
147
+ forked: true # inherits parent thread + prompt cache
148
+ maxSteps: 30
149
+ allowedHarnessTools: [ask_user, task_write]
150
+ allowedWorkspaceTools: [read_file, write_file]
151
+ prompt:
152
+ blocks:
153
+ - "You are a concise technical writer."
154
+ - { $ref: brand.voice.guide }
155
+ ```
156
+
157
+ - `forked: true` — subagent inherits the parent conversation thread and prompt cache
158
+ (cheaper, shares context).
159
+ - `forked: false` (default) — fresh isolated thread; the subagent is context-blind to
160
+ the parent.
161
+
162
+ ## Heartbeats
163
+
164
+ Heartbeats are periodic background handlers registered on the Harness constructor's
165
+ `heartbeats` array. Each entry is a `refOrString` resolving to a `HeartbeatConfig`
166
+ (interval + handler function). Common uses: Observational Memory reflector, progress
167
+ reporter, stale-session reaper.
168
+
169
+ ## Loading algorithm
170
+
171
+ 1. Locate `.harness/<slug>.md` in the workspace (or agent pack).
172
+ 2. Parse the YAML frontmatter block; validate against `HARNESS.schema.json`.
173
+ 3. Resolve all `$ref` / `$resolver` entries against the resolver registry at boot time
174
+ — fail loudly if any resolver is missing.
175
+ 4. Instantiate the `Harness` object (Mastra constructor) with the resolved config.
176
+ 5. Register in the harness registry keyed by `id`.
177
+
178
+ **Never execute a harness at load time** — only parse, validate, and register.
179
+
180
+ ## Relationship to other AIPs
181
+
182
+ | AIP | Relationship |
183
+ |---|---|
184
+ | AIP-42 (Agent) | Harness agents are AIP-42 instances. A Harness orchestrates them; it is NOT an AIP-42 agent itself. |
185
+ | AIP-12 (Playbook) | Agent packs can auto-generate `.harness/` manifests from `modes/*.md` files during ingestion. |
186
+ | AIP-35 (Storage) | `storage` field resolves to a provider defined under AIP-35. |
187
+ | AIP-37 (Lifecycle Events) | Harness emits `harness-session-start`, `harness-mode-switch`, `harness-session-end` events. |
188
+ | AIP-47 (Role) | `permissions` per mode align with AIP-47 role-based access scopes. |
189
+
190
+ ## See also
191
+
192
+ - `HARNESS.schema.json` — canonical validation schema
193
+ - `@mastra/core/harness` — canonical runtime implementation
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentproto/corpus-cli",
3
- "version": "0.1.0-alpha.6",
3
+ "version": "0.1.1",
4
4
  "description": "@agentproto/corpus-cli — the `corpus` binary. Validate, lint, and operate an AIP-10 corpus workspace from the command line. Local-topology host for @agentproto/corpus — pair with a local backing engine (qdrant docker / gbrain) for a fully offline corpus runtime.",
5
5
  "keywords": [
6
6
  "agentproto",
@@ -52,8 +52,8 @@
52
52
  "gray-matter": "^4.0.3",
53
53
  "zod": "^4.4.3",
54
54
  "@agentproto/cli-exec": "0.1.0-alpha.1",
55
- "@agentproto/corpus": "0.1.0-alpha.4",
56
- "@agentproto/corpus-presets": "0.1.0-alpha.1"
55
+ "@agentproto/corpus": "0.1.1",
56
+ "@agentproto/corpus-presets": "0.1.1"
57
57
  },
58
58
  "devDependencies": {
59
59
  "@types/node": "^25.6.2",