pi-cohort 5.0.1 → 5.1.0
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,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [5.1.0] - 2026-08-06
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- subagent tool schema shrunk from 18.0KB to 12.4KB serialized: the acceptance
|
|
8
|
+
override shape (previously inlined 5x, 44% of the schema) now appears in full
|
|
9
|
+
only at top-level `acceptance`; the 4 nested sites (tasks items, chain steps,
|
|
10
|
+
parallel members, dynamic fanout template) carry a compact stub. Gate-level
|
|
11
|
+
`evidence`/`severity` dropped from the schema (still accepted at runtime).
|
|
12
|
+
Deep acceptance validation moved to the executor boundary with per-site path
|
|
13
|
+
labels. Nested `acceptance: false` is no longer schema-accepted - use
|
|
14
|
+
`level: "none"`. Unknown acceptance keys are now rejected everywhere,
|
|
15
|
+
including saved `.chain.json` chains that previously loaded with the key
|
|
16
|
+
silently ignored. ([#6](https://github.com/jjuraszek/pi-cohort/issues/6))
|
|
17
|
+
|
|
3
18
|
## [5.0.1] - 2026-08-06
|
|
4
19
|
|
|
5
20
|
### Changed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-cohort",
|
|
3
|
-
"version": "5.0
|
|
3
|
+
"version": "5.1.0",
|
|
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",
|
package/src/extension/schemas.ts
CHANGED
|
@@ -65,12 +65,13 @@ const AcceptanceEvidenceKind = Type.String({
|
|
|
65
65
|
],
|
|
66
66
|
});
|
|
67
67
|
|
|
68
|
+
// Gate-level `evidence`/`severity` are accepted at runtime (normalizeCriteria) but omitted
|
|
69
|
+
// from the schema: dead flexibility from a tool call, and the evidence enum is heavy.
|
|
70
|
+
// additionalProperties stays open so inputs carrying them are not provider-rejected.
|
|
68
71
|
const AcceptanceGateSchema = Type.Object({
|
|
69
72
|
id: Type.String(),
|
|
70
73
|
must: Type.String(),
|
|
71
|
-
|
|
72
|
-
severity: Type.Optional(Type.String({ enum: ["required", "recommended"] })),
|
|
73
|
-
}, { additionalProperties: false });
|
|
74
|
+
}, { additionalProperties: true });
|
|
74
75
|
|
|
75
76
|
const AcceptanceVerifyCommandSchema = Type.Object({
|
|
76
77
|
id: Type.String(),
|
|
@@ -121,6 +122,21 @@ const AcceptanceOverride = Type.Unsafe({
|
|
|
121
122
|
description: "Acceptance: auto if omitted; verified needs cmds.",
|
|
122
123
|
});
|
|
123
124
|
|
|
125
|
+
// Nested acceptance sites carry a compact stub instead of the full AcceptanceOverride:
|
|
126
|
+
// inlined 5x it was 44% of the serialized schema. The full shape is documented once at
|
|
127
|
+
// top-level `acceptance`; deep validation happens at the executor boundary
|
|
128
|
+
// (validateAcceptanceInput), not provider-side. The object branch keeps a non-empty
|
|
129
|
+
// `properties` because some providers reject bare {type:"object"} in function
|
|
130
|
+
// declarations - the same wall that rules out $defs/$ref (see issue #6).
|
|
131
|
+
// {const:false} is intentionally absent: deprecated shorthand, use level:"none".
|
|
132
|
+
const AcceptanceOverrideStub = Type.Unsafe({
|
|
133
|
+
anyOf: [
|
|
134
|
+
{ type: "string", enum: ["auto", "none", "attested", "checked", "verified", "reviewed"] },
|
|
135
|
+
{ type: "object", additionalProperties: true, properties: { level: { type: "string" } } },
|
|
136
|
+
],
|
|
137
|
+
description: "Acceptance override; same shape as top-level acceptance.",
|
|
138
|
+
});
|
|
139
|
+
|
|
124
140
|
const TaskItem = Type.Object({
|
|
125
141
|
agent: Type.String(),
|
|
126
142
|
task: Type.String(),
|
|
@@ -132,7 +148,7 @@ const TaskItem = Type.Object({
|
|
|
132
148
|
progress: Type.Optional(Type.Boolean({ description: "true enables progress.md tracking; omit or false disables." })),
|
|
133
149
|
model: Type.Optional(Type.String()),
|
|
134
150
|
skill: Type.Optional(brief(SkillOverride, "Skill override.")),
|
|
135
|
-
acceptance: Type.Optional(
|
|
151
|
+
acceptance: Type.Optional(AcceptanceOverrideStub),
|
|
136
152
|
});
|
|
137
153
|
|
|
138
154
|
// Parallel task item (within a parallel step)
|
|
@@ -151,7 +167,7 @@ const ParallelTaskSchema = Type.Object({
|
|
|
151
167
|
progress: Type.Optional(Type.Boolean({ description: "Enable progress.md tracking." })),
|
|
152
168
|
model: Type.Optional(Type.String()),
|
|
153
169
|
skill: Type.Optional(brief(SkillOverride, "Skill override.")),
|
|
154
|
-
acceptance: Type.Optional(
|
|
170
|
+
acceptance: Type.Optional(AcceptanceOverrideStub),
|
|
155
171
|
});
|
|
156
172
|
|
|
157
173
|
const DynamicExpandSchema = Type.Object({
|
|
@@ -178,7 +194,7 @@ const DynamicParallelTemplateSchema = Type.Object({
|
|
|
178
194
|
progress: Type.Optional(Type.Boolean({ description: "Enable progress.md tracking." })),
|
|
179
195
|
model: Type.Optional(Type.String()),
|
|
180
196
|
skill: Type.Optional(brief(SkillOverride, "Skill override.")),
|
|
181
|
-
acceptance: Type.Optional(
|
|
197
|
+
acceptance: Type.Optional(AcceptanceOverrideStub),
|
|
182
198
|
}, { additionalProperties: false });
|
|
183
199
|
|
|
184
200
|
const DynamicCollectSchema = Type.Object({
|
|
@@ -203,7 +219,7 @@ const ChainItem = Type.Object({
|
|
|
203
219
|
progress: Type.Optional(Type.Boolean({ description: "Enable progress.md tracking in {chain_dir}" })),
|
|
204
220
|
model: Type.Optional(Type.String()),
|
|
205
221
|
skill: Type.Optional(brief(SkillOverride, "Skill override.")),
|
|
206
|
-
acceptance: Type.Optional(
|
|
222
|
+
acceptance: Type.Optional(AcceptanceOverrideStub),
|
|
207
223
|
parallel: Type.Optional(Type.Unsafe({
|
|
208
224
|
anyOf: [
|
|
209
225
|
Type.Array(ParallelTaskSchema, { minItems: 1 }),
|
|
@@ -17,6 +17,7 @@ import { recordSyncCost, renderGrandTotal, sumNestedCost } from "../../extension
|
|
|
17
17
|
import { resolveModelCandidate } from "../shared/model-fallback.ts";
|
|
18
18
|
import { aggregateParallelOutputs } from "../shared/parallel-utils.ts";
|
|
19
19
|
import { recordRun } from "../shared/run-history.ts";
|
|
20
|
+
import { validateAcceptanceInput } from "../shared/acceptance.ts";
|
|
20
21
|
import {
|
|
21
22
|
buildChainInstructions,
|
|
22
23
|
writeInitialProgressFile,
|
|
@@ -858,6 +859,33 @@ function validateExecutionInput(
|
|
|
858
859
|
}
|
|
859
860
|
}
|
|
860
861
|
|
|
862
|
+
// Nested acceptance sites only carry a compact stub in the tool schema (see
|
|
863
|
+
// AcceptanceOverrideStub in extension/schemas.ts); this is the deep-validation boundary.
|
|
864
|
+
const acceptanceErrors: string[] = [
|
|
865
|
+
...validateAcceptanceInput(params.acceptance, "acceptance"),
|
|
866
|
+
];
|
|
867
|
+
for (const [i, task] of (params.tasks ?? []).entries()) {
|
|
868
|
+
acceptanceErrors.push(...validateAcceptanceInput(task.acceptance, `tasks[${i}].acceptance`));
|
|
869
|
+
}
|
|
870
|
+
for (const [i, rawStep] of (params.chain ?? []).entries()) {
|
|
871
|
+
const step = rawStep as ChainStep;
|
|
872
|
+
acceptanceErrors.push(...validateAcceptanceInput((rawStep as { acceptance?: unknown }).acceptance, `chain[${i}].acceptance`));
|
|
873
|
+
if (isParallelStep(step)) {
|
|
874
|
+
for (const [j, member] of step.parallel.entries()) {
|
|
875
|
+
acceptanceErrors.push(...validateAcceptanceInput(member.acceptance, `chain[${i}].parallel[${j}].acceptance`));
|
|
876
|
+
}
|
|
877
|
+
} else if (isDynamicParallelStep(step)) {
|
|
878
|
+
acceptanceErrors.push(...validateAcceptanceInput(step.parallel.acceptance, `chain[${i}].parallel.acceptance`));
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
if (acceptanceErrors.length > 0) {
|
|
882
|
+
return {
|
|
883
|
+
content: [{ type: "text", text: `Invalid acceptance input:\n${acceptanceErrors.map((error) => `- ${error}`).join("\n")}` }],
|
|
884
|
+
isError: true,
|
|
885
|
+
details: { mode: hasChain ? "chain" as const : hasTasks ? "parallel" as const : "single" as const, results: [] },
|
|
886
|
+
};
|
|
887
|
+
}
|
|
888
|
+
|
|
861
889
|
return null;
|
|
862
890
|
}
|
|
863
891
|
|
|
@@ -131,6 +131,8 @@ function explicitAcceptanceCanDisable(explicit: AcceptanceConfig): boolean {
|
|
|
131
131
|
return explicit.level === "none" && typeof explicit.reason === "string" && explicit.reason.trim().length > 0;
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
+
const KNOWN_ACCEPTANCE_KEYS = new Set(["level", "criteria", "evidence", "verify", "review", "stopRules", "reason"]);
|
|
135
|
+
|
|
134
136
|
export function validateAcceptanceInput(input: unknown, pathLabel = "acceptance"): string[] {
|
|
135
137
|
const errors: string[] = [];
|
|
136
138
|
if (input === undefined) return errors;
|
|
@@ -144,6 +146,11 @@ export function validateAcceptanceInput(input: unknown, pathLabel = "acceptance"
|
|
|
144
146
|
return errors;
|
|
145
147
|
}
|
|
146
148
|
const value = input as Record<string, unknown>;
|
|
149
|
+
for (const key of Object.keys(value)) {
|
|
150
|
+
if (!KNOWN_ACCEPTANCE_KEYS.has(key)) {
|
|
151
|
+
errors.push(`${pathLabel}.${key} is not a recognized acceptance field (known: level, criteria, evidence, verify, review, stopRules, reason).`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
147
154
|
if (value.level !== undefined && (typeof value.level !== "string" || !VALID_LEVELS.has(value.level as AcceptanceLevel))) {
|
|
148
155
|
errors.push(`${pathLabel}.level must be one of auto, none, attested, checked, verified, reviewed.`);
|
|
149
156
|
}
|