pi-cohort 5.1.0 → 5.1.2

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## [5.1.2] - 2026-08-07
4
+
5
+ ### Changed
6
+
7
+ - Schema diet round three (subagent tool schema 9,483 -> 8,246 bytes serialized):
8
+ `tasks[]` items collapsed to the round-two stub form (overrides pass through,
9
+ documented in the pi-cohort skill), `ChainItem` allOf conditionals removed
10
+ (runtime-enforced at chain parse), and `runId` no longer advertised by the
11
+ tool schema - still accepted at runtime, `id` preferred.
12
+
13
+ ## [5.1.1] - 2026-08-07
14
+
15
+ ### Changed
16
+
17
+ - subagent tool schema shrunk from 12,374 to 9,483 bytes serialized, plus ~600
18
+ chars off the tool description: the chain parallel-item schemas (both the
19
+ static array items and the dynamic fanout template) and `control` are now
20
+ permissive stubs (`additionalProperties: true`) keeping only structural keys
21
+ explicit (parallel: agent/task/count/as; control: enabled); the
22
+ MANAGEMENT/CONTROL description blocks compressed to one line each. Widening
23
+ only - every previously valid call stays valid; dynamic-template unknown
24
+ keys are still rejected at runtime (assertOnlyKeys), and the control field
25
+ table moved to the pi-cohort skill reference/config-fields.md.
26
+
3
27
  ## [5.1.0] - 2026-08-06
4
28
 
5
29
  ### Changed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-cohort",
3
- "version": "5.1.0",
3
+ "version": "5.1.2",
4
4
  "description": "Delegate Pi work to focused child agents: code review, scouting, implementation, parallel audits, saved chains, and background jobs.",
5
5
  "author": "Jacek Juraszek",
6
6
  "license": "MIT",
@@ -115,7 +115,7 @@ Use this when a broad diff has known reviewer findings across several items and
115
115
 
116
116
  Prefer `async: true`, `context: "fresh"` for planners/validators, `outputMode: "file-only"` for large summaries, and per-stage output names that will not collide. Add `phase` and `label` to make async status readable, and use `as` plus `{outputs.name}` when a later step needs a specific earlier result instead of the whole `{previous}` blob. Use this pattern instead of launching several writer workers into a dirty worktree. Include non-blocking suggestions in the writer prompt only when they are small, safe, and do not expand product scope; otherwise record them as deferred.
117
117
 
118
- When the first step can return a structured target list, prefer dynamic fanout instead of hand-authoring a static parallel group. Use `outputSchema` and `as` on the producer, then an `expand` step with `from: { output, path }`, an explicit `maxItems`, one `parallel` child template, and `collect.as`. Item templates may use `{item}` or a named item such as `{target.path}`. Do not use dynamic fanout for prose outputs, nested fanout, dynamic agent selection, reducers, `when` conditions, or arbitrary expressions; `.chain.md` does not support this syntax, so use direct JSON or a saved `.chain.json`.
118
+ When the first step can return a structured target list, prefer dynamic fanout instead of hand-authoring a static parallel group. Use `outputSchema` and `as` on the producer, then an `expand` step with `from: { output, path }`, an explicit `maxItems`, one `parallel` child template, and `collect.as`. Item templates may use `{item}` or a named item such as `{target.path}`. The template's `label` also supports item templates. Do not use dynamic fanout for prose outputs, nested fanout, dynamic agent selection, reducers, `when` conditions, or arbitrary expressions; `.chain.md` does not support this syntax, so use direct JSON or a saved `.chain.json`.
119
119
 
120
120
  Example shape:
121
121
 
@@ -1,8 +1,8 @@
1
- # subagent `config` field reference
1
+ # subagent field reference: `config` and call-time `control`
2
2
 
3
- Fields accepted by `subagent({ action: "create" | "update", config: {...} })`.
3
+ Fields accepted by `subagent({ action: "create" | "update", config: {...} })`, plus the call-time `control` overrides (last section).
4
4
  `config` may be an object or a JSON string. Presence of `steps` makes it a chain.
5
- Scope: this is the management create/update path (`parseStepList`), which accepts exactly the step fields below and requires `outputSchema` to be a file path; file-authored `.chain.md` chains support additional step fields (parallel, expand, collect, concurrency, failFast, worktree, acceptance, inline outputSchema) - see SKILL.md chain authoring.
5
+ Scope (for `config`): this is the management create/update path (`parseStepList`), which accepts exactly the step fields below and requires `outputSchema` to be a file path; file-authored `.chain.md` chains support additional step fields (parallel, expand, collect, concurrency, failFast, worktree, acceptance, inline outputSchema) - see SKILL.md chain authoring.
6
6
 
7
7
  ## Agent config
8
8
 
@@ -49,3 +49,20 @@ Per step:
49
49
  | `model` | string | Model override. |
50
50
  | `skills` | string[] \| false | Skills to inject (NOTE: plural `skills` here, unlike the execution-time `skill` param). |
51
51
  | `progress` | boolean | progress.md tracking. |
52
+
53
+ ## Call-time `control` overrides
54
+
55
+ Per-call attention-tracking overrides: `subagent({ ..., control: {...} })`. Run-level field - sits beside `tasks`/`chain`, not inside task items. The tool schema declares only `enabled`; all fields below are accepted (`additionalProperties: true`) and re-parsed defensively at runtime (`resolveControlConfig`, `src/runs/shared/subagent-control.ts`) - invalid values fall back to the defaults.
56
+
57
+ | Field | Type | Meaning | Default |
58
+ |---|---|---|---|
59
+ | `enabled` | boolean | Toggle attention tracking. | `true` |
60
+ | `needsAttentionAfterMs` | integer >= 1 | No-activity window before `needs_attention`. | `60000` |
61
+ | `activeNoticeAfterMs` | integer >= 1 | Elapsed-ms threshold for the long-running notice. | `240000` |
62
+ | `inFlightSilenceCeilingMs` | integer >= 1 | Silent ms in-flight before escalation. | `600000` |
63
+ | `inFlightSilenceKillMs` | integer >= 1 | SIGTERM the child after this silent span (clamped above the needs-attention window). | `1800000` |
64
+ | `activeNoticeAfterTurns` | integer >= 1 | Notice by assistant turns. | off |
65
+ | `activeNoticeAfterTokens` | integer >= 1 | Notice by total tokens. | off |
66
+ | `failedToolAttemptsBeforeAttention` | integer >= 1 | Mutating-tool failures before `needs_attention`. | `3` |
67
+ | `notifyOn` | array of `"active_long_running"` \| `"needs_attention"` | Which events notify the parent. | both |
68
+ | `notifyChannels` | array of `"event"` \| `"async"` \| `"intercom"` | Delivery channels. | all three |
@@ -439,18 +439,9 @@ CHAIN TEMPLATE VARIABLES (use in task strings):
439
439
 
440
440
  Example: { chain: [{agent:"agent-a", task:"Analyze {task}"}, {agent:"agent-b", task:"Plan based on {previous}"}] }
441
441
 
442
- MANAGEMENT (use action field, omit agent/task/chain/tasks):
443
- • { action: "list" } - discover executable agents/chains
444
- { action: "get", agent: "name" } - full detail; packaged agents use dotted runtime names like "package.agent"
445
- • { action: "create", config: { name: "custom-agent", package: "code-analysis", systemPrompt, systemPromptMode, inheritProjectContext, inheritSkills, defaultContext, ... } }
446
- • { action: "update", agent: "code-analysis.custom-agent", config: { package: "analysis", ... } } - merge
447
- • { action: "delete", agent: "code-analysis.custom-agent" }
448
- • Use chainName for chain operations; packaged chains also use dotted runtime names
449
-
450
- CONTROL:
451
- • { action: "status", id: "..." } - inspect an async/background run by id or prefix
452
- • { action: "interrupt", id?: "..." } - soft-interrupt the current child turn and leave the run paused
453
- • { action: "resume", id: "...", message: "...", index?: 0 } - follow up with a live async child or revive a completed async/foreground child from its session
442
+ MANAGEMENT: action=list/get/create/update/delete (chainName for chains; packaged agents use dotted names); config fields: pi-cohort skill reference/config-fields.md.
443
+
444
+ CONTROL: action=status/interrupt/resume (id, message, index); details: pi-cohort skill.
454
445
 
455
446
  DIAGNOSTICS:
456
447
  • { action: "doctor" } - read-only report for runtime paths, discovery, sessions, and intercom`,
@@ -137,37 +137,41 @@ const AcceptanceOverrideStub = Type.Unsafe({
137
137
  description: "Acceptance override; same shape as top-level acceptance.",
138
138
  });
139
139
 
140
- const TaskItem = Type.Object({
141
- agent: Type.String(),
142
- task: Type.String(),
143
- cwd: Type.Optional(Type.String()),
144
- count: Type.Optional(Type.Integer({ minimum: 1, description: "Repeat N times (same settings)." })),
145
- output: Type.Optional(brief(OutputOverride, "Output file path, or false.")),
146
- outputMode: Type.Optional(brief(OutputModeOverride, "Default: inline.")),
147
- reads: Type.Optional(ReadsOverride),
148
- progress: Type.Optional(Type.Boolean({ description: "true enables progress.md tracking; omit or false disables." })),
149
- model: Type.Optional(Type.String()),
150
- skill: Type.Optional(brief(SkillOverride, "Skill override.")),
151
- acceptance: Type.Optional(AcceptanceOverrideStub),
140
+ // tasks[] stub (round three): same treatment as ParallelTaskSchema below; structural keys
141
+ // stay explicit (agent/task required, count = arity); every per-task override (model, output,
142
+ // outputMode, reads, progress, skill, cwd, acceptance) passes through additionalProperties
143
+ // and is documented in the pi-cohort skill. No `as` (no {outputs.name} wiring here) and no
144
+ // `label` (TaskParam has no such field at this position). Widening-only: unknown keys were
145
+ // already ignored at runtime.
146
+ const TaskItem = Type.Unsafe({
147
+ type: "object",
148
+ additionalProperties: true,
149
+ required: ["agent", "task"],
150
+ properties: {
151
+ agent: { type: "string" },
152
+ task: { type: "string" },
153
+ count: { type: "integer", minimum: 1, description: "Repeat N times (same settings)." },
154
+ },
155
+ description: "Per-task overrides (model, output, reads, skill, ...): pi-cohort skill.",
152
156
  });
153
157
 
154
- // Parallel task item (within a parallel step)
155
- const ParallelTaskSchema = Type.Object({
156
- agent: Type.String(),
157
- task: Type.Optional(Type.String({ description: "{task},{previous},{chain_dir} template; defaults to {previous}." })),
158
- phase: Type.Optional(Type.String()),
159
- label: Type.Optional(Type.String()),
160
- as: Type.Optional(Type.String({ description: "Identifier for {outputs.name}." })),
161
- outputSchema: Type.Optional(brief(JsonSchemaObject, "Output JSON Schema.")),
162
- cwd: Type.Optional(Type.String()),
163
- count: Type.Optional(Type.Integer({ minimum: 1, description: "Repeat N times." })),
164
- output: Type.Optional(brief(OutputOverride, "Output file path, or false.")),
165
- outputMode: Type.Optional(brief(OutputModeOverride, "Default: inline.")),
166
- reads: Type.Optional(brief(ReadsOverride, "Reads first, or false.")),
167
- progress: Type.Optional(Type.Boolean({ description: "Enable progress.md tracking." })),
168
- model: Type.Optional(Type.String()),
169
- skill: Type.Optional(brief(SkillOverride, "Skill override.")),
170
- acceptance: Type.Optional(AcceptanceOverrideStub),
158
+ // Parallel-item stubs (round two): structural/wiring keys stay explicit (agent/task,
159
+ // count/as = arity + {outputs.name} routing); every per-task override (model, label,
160
+ // phase, output, outputMode, reads, skill, cwd, progress, outputSchema, acceptance)
161
+ // passes through additionalProperties and is documented in the pi-cohort skill.
162
+ // Widening-only: dynamic-arm unknown keys are still rejected at runtime by
163
+ // assertOnlyKeys (dynamic-fanout.ts); static-arm unknown keys were always ignored.
164
+ const ParallelTaskSchema = Type.Unsafe({
165
+ type: "object",
166
+ additionalProperties: true,
167
+ required: ["agent"],
168
+ properties: {
169
+ agent: { type: "string" },
170
+ task: { type: "string", description: "{task},{previous},{chain_dir} template; defaults to {previous}." },
171
+ count: { type: "integer", minimum: 1, description: "Repeat N times." },
172
+ as: { type: "string", description: "Identifier for {outputs.name}." },
173
+ },
174
+ description: "Per-task overrides (model, label, output, skill, ...): pi-cohort skill.",
171
175
  });
172
176
 
173
177
  const DynamicExpandSchema = Type.Object({
@@ -181,21 +185,16 @@ const DynamicExpandSchema = Type.Object({
181
185
  onEmpty: Type.Optional(Type.String({ enum: ["skip", "fail"], description: "Defaults to skip." })),
182
186
  }, { additionalProperties: false });
183
187
 
184
- const DynamicParallelTemplateSchema = Type.Object({
185
- agent: Type.String(),
186
- task: Type.Optional(Type.String({ description: "{item},{item.path},{task},{previous},{chain_dir},{outputs.name} template." })),
187
- phase: Type.Optional(Type.String()),
188
- label: Type.Optional(Type.String({ description: "Label; item templates supported." })),
189
- outputSchema: Type.Optional(brief(JsonSchemaObject, "Output JSON Schema.")),
190
- cwd: Type.Optional(Type.String()),
191
- output: Type.Optional(brief(OutputOverride, "Output file path, or false.")),
192
- outputMode: Type.Optional(brief(OutputModeOverride, "Default: inline.")),
193
- reads: Type.Optional(brief(ReadsOverride, "Reads first, or false.")),
194
- progress: Type.Optional(Type.Boolean({ description: "Enable progress.md tracking." })),
195
- model: Type.Optional(Type.String()),
196
- skill: Type.Optional(brief(SkillOverride, "Skill override.")),
197
- acceptance: Type.Optional(AcceptanceOverrideStub),
198
- }, { additionalProperties: false });
188
+ const DynamicParallelTemplateSchema = Type.Unsafe({
189
+ type: "object",
190
+ additionalProperties: true,
191
+ required: ["agent"],
192
+ properties: {
193
+ agent: { type: "string" },
194
+ task: { type: "string", description: "{item},{item.path},{task},{previous},{chain_dir},{outputs.name} template." },
195
+ },
196
+ description: "Per-task overrides (model, label, output, skill, ...): pi-cohort skill.",
197
+ });
199
198
 
200
199
  const DynamicCollectSchema = Type.Object({
201
200
  as: Type.String({ description: "Collected result array name." }),
@@ -237,28 +236,16 @@ const ChainItem = Type.Object({
237
236
  }, {
238
237
  description: "{agent,task?} seq; {parallel:[..]} concurrent; {expand,parallel,collect} fanout.",
239
238
  additionalProperties: false,
240
- allOf: [
241
- { if: { required: ["expand"] }, then: { required: ["parallel", "collect"], properties: { parallel: { type: "object" } } } },
242
- { if: { required: ["collect"] }, then: { required: ["expand", "parallel"], properties: { parallel: { type: "object" } } } },
243
- { not: { required: ["expand"], properties: { parallel: { type: "array", items: {} } } } },
244
- ],
245
239
  });
246
240
 
247
- const ControlOverrides = Type.Object({
248
- enabled: Type.Optional(Type.Boolean({ description: "Toggle attention tracking." })),
249
- needsAttentionAfterMs: Type.Optional(Type.Integer({ minimum: 1, description: "No-activity window before needs_attention." })),
250
- activeNoticeAfterMs: Type.Optional(Type.Integer({ minimum: 1, description: "Elapsed-ms notice threshold (default: 240000)." })),
251
- inFlightSilenceCeilingMs: Type.Optional(Type.Integer({ minimum: 1, description: "Silent ms before escalation (default: 600000)." })),
252
- inFlightSilenceKillMs: Type.Optional(Type.Integer({ minimum: 1, description: "SIGTERM child (default: 1800000, clamped above needs_attention)." })),
253
- activeNoticeAfterTurns: Type.Optional(Type.Integer({ minimum: 1, description: "Notice by assistant turns (off by default)." })),
254
- activeNoticeAfterTokens: Type.Optional(Type.Integer({ minimum: 1, description: "Notice by total tokens (off by default)." })),
255
- failedToolAttemptsBeforeAttention: Type.Optional(Type.Integer({ minimum: 1, description: "Mutating-tool failures before needs_attention (default: 3)." })),
256
- notifyOn: Type.Optional(Type.Array(Type.String({ enum: ["active_long_running", "needs_attention"] }), {
257
- description: "To parent (default: active_long_running, needs_attention).",
258
- })),
259
- notifyChannels: Type.Optional(Type.Array(Type.String({ enum: ["event", "async", "intercom"] }), {
260
- description: "Default: event, async, intercom.",
261
- })),
241
+ // Control stub (round two): 10 fields collapsed; the full table lives in the pi-cohort
242
+ // skill reference/config-fields.md. resolveControlConfig (subagent-control.ts) re-parses
243
+ // every field defensively at runtime, so mistyped values degrade to defaults.
244
+ const ControlOverrides = Type.Unsafe({
245
+ type: "object",
246
+ additionalProperties: true,
247
+ properties: { enabled: { type: "boolean" } },
248
+ description: "Attention-tracking overrides (needsAttentionAfterMs, notifyOn, ...); fields: pi-cohort skill reference/config-fields.md.",
262
249
  });
263
250
 
264
251
  export const SubagentParams = Type.Object({
@@ -272,9 +259,6 @@ export const SubagentParams = Type.Object({
272
259
  id: Type.Optional(Type.String({
273
260
  description: "Run id/prefix for status/interrupt/resume."
274
261
  })),
275
- runId: Type.Optional(Type.String({
276
- description: "Run ID (interrupt/resume); defaults to latest. Prefer id."
277
- })),
278
262
  dir: Type.Optional(Type.String({
279
263
  description: "Async run dir for status/resume."
280
264
  })),