@piagent/platform 1.6.0 → 1.6.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.
Files changed (213) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README.md +17 -14
  3. package/README.vi.md +26 -1
  4. package/benchmarks/production-v1/spend-control.v1.json +27 -3
  5. package/benchmarks/production-v1/suite.json +5 -1
  6. package/catalog/capabilities.json +16 -16
  7. package/docs/authority-manifest.md +1 -1
  8. package/docs/auto-delegation-policy.md +38 -73
  9. package/docs/command-reference-vietnamese.md +23 -31
  10. package/docs/context-engine.md +5 -3
  11. package/docs/context-window-policy.md +37 -1
  12. package/docs/distribution-standard.md +7 -7
  13. package/docs/en/architecture.md +26 -13
  14. package/docs/languages.json +4 -4
  15. package/docs/operator-manual-vietnamese.md +21 -41
  16. package/docs/publishing-for-teams.md +5 -0
  17. package/docs/quality-benchmark.md +179 -23
  18. package/docs/release-install-policy.md +6 -6
  19. package/docs/runtime-harness-standard.md +3 -3
  20. package/docs/security-threat-model.md +1 -1
  21. package/docs/subagent-orchestration-capabilities.md +19 -51
  22. package/docs/subagents-and-multiagent.md +21 -47
  23. package/docs/task-implementation-contract.md +3 -3
  24. package/docs/task-lifecycle-vietnamese.md +1 -1
  25. package/docs/team-onboarding.md +2 -2
  26. package/docs/vi/architecture.md +26 -13
  27. package/docs-site/architecture.html +1 -1
  28. package/docs-site/benchmark.html +89 -95
  29. package/docs-site/commands.html +1 -1
  30. package/docs-site/content/benchmark.html +88 -94
  31. package/docs-site/content/en/benchmark.html +134 -90
  32. package/docs-site/content/en/index.html +19 -19
  33. package/docs-site/content/en/runtime.html +4 -4
  34. package/docs-site/content/en/whats-new.html +12 -1
  35. package/docs-site/content/en/workflows.html +2 -2
  36. package/docs-site/content/index.html +19 -19
  37. package/docs-site/content/runtime.html +4 -4
  38. package/docs-site/content/whats-new.html +12 -1
  39. package/docs-site/content/workflows.html +2 -2
  40. package/docs-site/context-engine.html +1 -1
  41. package/docs-site/en/architecture.html +1 -1
  42. package/docs-site/en/benchmark.html +135 -91
  43. package/docs-site/en/commands.html +1 -1
  44. package/docs-site/en/context-engine.html +1 -1
  45. package/docs-site/en/index.html +20 -20
  46. package/docs-site/en/mcp-approval.html +1 -1
  47. package/docs-site/en/mcp-auth.html +1 -1
  48. package/docs-site/en/mcp-servers.html +1 -1
  49. package/docs-site/en/mcp.html +1 -1
  50. package/docs-site/en/permissions.html +1 -1
  51. package/docs-site/en/profiles.html +1 -1
  52. package/docs-site/en/quickstart.html +1 -1
  53. package/docs-site/en/runtime.html +5 -5
  54. package/docs-site/en/scope.html +1 -1
  55. package/docs-site/en/security.html +1 -1
  56. package/docs-site/en/team.html +1 -1
  57. package/docs-site/en/whats-new.html +13 -2
  58. package/docs-site/en/workflows.html +3 -3
  59. package/docs-site/index.html +20 -20
  60. package/docs-site/mcp-approval.html +1 -1
  61. package/docs-site/mcp-auth.html +1 -1
  62. package/docs-site/mcp-servers.html +1 -1
  63. package/docs-site/mcp.html +1 -1
  64. package/docs-site/permissions.html +1 -1
  65. package/docs-site/profiles.html +1 -1
  66. package/docs-site/quickstart.html +1 -1
  67. package/docs-site/runtime.html +5 -5
  68. package/docs-site/scope.html +1 -1
  69. package/docs-site/security.html +1 -1
  70. package/docs-site/team.html +1 -1
  71. package/docs-site/whats-new.html +13 -2
  72. package/docs-site/workflows.html +3 -3
  73. package/evals/fixtures/benchmark-assurance-evidence.invalid.json +1 -1
  74. package/evals/fixtures/benchmark-assurance-evidence.valid.json +1 -1
  75. package/evals/fixtures/codex-relative-efficiency-v1.invalid.json +4 -0
  76. package/evals/fixtures/codex-relative-efficiency-v1.valid.json +94 -0
  77. package/evals/fixtures/helper-request.invalid.json +1 -1
  78. package/evals/fixtures/helper-request.valid.json +1 -1
  79. package/evals/fixtures/piagent-webui/subagent-tree-v1.valid.json +7 -0
  80. package/evals/fixtures/task-authority-snapshot.valid.json +4 -4
  81. package/evals/fs4-readiness-matrix.v1.json +2 -2
  82. package/evals/fs5-causal-arm.v1.json +2 -2
  83. package/evals/fs5-pilot-protocol.v1.json +3 -3
  84. package/evals/fs5-pilot-protocol.v2.json +8 -8
  85. package/evals/fs5-pilot-protocol.v3.json +8 -8
  86. package/evals/fs5-pilot-protocol.v4.json +6 -6
  87. package/evals/fs5-pilot-protocol.v5.json +7 -6
  88. package/evals/private-holdout-v1/public-exposure.v1.json +1 -1
  89. package/evals/runtime-conformance-v1/README.md +43 -0
  90. package/evals/runtime-conformance-v1/evaluate-paid-canary.mjs +198 -0
  91. package/evals/runtime-conformance-v1/lane.json +57 -0
  92. package/evals/runtime-conformance-v1/paid-canary.json +31 -0
  93. package/evals/runtime-conformance-v1/runner.mjs +456 -0
  94. package/package.json +1 -1
  95. package/packages/piagent-core/README.md +13 -4
  96. package/packages/piagent-core/benchmark/benchmark-codex-relative-efficiency.js +698 -0
  97. package/packages/piagent-core/benchmark/benchmark-core.js +4 -3
  98. package/packages/piagent-core/benchmark/benchmark-forensics.js +4 -2
  99. package/packages/piagent-core/benchmark/benchmark-preflight.js +3 -1
  100. package/packages/piagent-core/benchmark/benchmark-provider-free-evidence.js +345 -0
  101. package/packages/piagent-core/benchmark/benchmark-record-validation.js +74 -5
  102. package/packages/piagent-core/benchmark/benchmark-report.js +15 -1
  103. package/packages/piagent-core/benchmark/benchmark-runtime-causal.js +244 -0
  104. package/packages/piagent-core/benchmark/benchmark-stage-diagnostic.js +201 -4
  105. package/packages/piagent-core/benchmark/benchmark-suite.js +22 -1
  106. package/packages/piagent-core/benchmark/benchmark-summary-support.js +4 -0
  107. package/packages/piagent-core/benchmark/benchmark-usage.js +15 -4
  108. package/packages/piagent-core/capabilities/runtime-integrity.js +3 -0
  109. package/packages/piagent-core/extensions/acceptance-behavior-proof.js +806 -0
  110. package/packages/piagent-core/extensions/acceptance-receipt.js +89 -48
  111. package/packages/piagent-core/extensions/context-engine.js +1 -11
  112. package/packages/piagent-core/extensions/context-index-policy.js +4 -0
  113. package/packages/piagent-core/extensions/context-query-paths.js +57 -0
  114. package/packages/piagent-core/extensions/criterion-context-pack.js +16 -3
  115. package/packages/piagent-core/extensions/criterion-graph.js +4 -1
  116. package/packages/piagent-core/extensions/fallback-base-policy.ts +101 -0
  117. package/packages/piagent-core/extensions/guard-types.ts +4 -0
  118. package/packages/piagent-core/extensions/piagent-guard.ts +194 -226
  119. package/packages/piagent-core/extensions/task-lifecycle.js +32 -0
  120. package/packages/piagent-core/extensions/task-state.js +69 -8
  121. package/packages/piagent-core/extensions/verification-intelligence.js +49 -12
  122. package/packages/piagent-core/extensions/workspace-evidence-roots.js +52 -16
  123. package/packages/piagent-core/package.json +1 -1
  124. package/packages/piagent-core/policies/base-policy.json +42 -2
  125. package/packages/piagent-core/policy/authority-manifest.v1.json +3 -3
  126. package/packages/piagent-core/prompts/be-to-fe.md +2 -8
  127. package/packages/piagent-core/prompts/plan.md +1 -1
  128. package/packages/piagent-core/prompts/platform-improve.md +2 -6
  129. package/packages/piagent-core/prompts/review.md +2 -7
  130. package/packages/piagent-core/prompts/scout.md +1 -1
  131. package/packages/piagent-core/prompts/task.md +4 -3
  132. package/packages/piagent-core/runtime/hooks/completion-hook.ts +7 -6
  133. package/packages/piagent-core/runtime/hooks/input-hook.ts +6 -0
  134. package/packages/piagent-core/runtime/hooks/session-start-hook.ts +100 -1
  135. package/packages/piagent-core/runtime/hooks/tool-result-hook.ts +58 -72
  136. package/packages/piagent-core/runtime/hooks/tool-result-value-helpers.ts +66 -0
  137. package/packages/piagent-core/runtime/inspection/bounded-emission-guard.ts +286 -0
  138. package/packages/piagent-core/runtime/inspection/runtime-event-store.ts +37 -7
  139. package/packages/piagent-core/runtime/inspection/task-subagent-tree.ts +27 -4
  140. package/packages/piagent-core/runtime/inspection/tool-failure-classification.ts +20 -3
  141. package/packages/piagent-core/runtime/inspection/webui-snapshot.ts +47 -11
  142. package/packages/piagent-core/runtime/orchestration/helper-lifecycle.ts +16 -12
  143. package/packages/piagent-core/runtime/orchestration/minimal-delegation-policy.ts +71 -0
  144. package/packages/piagent-core/runtime/orchestration/owned-work-budget.ts +28 -20
  145. package/packages/piagent-core/runtime/orchestration/role-policy.ts +27 -9
  146. package/packages/piagent-core/runtime/orchestration/subagent-tool-policy.ts +329 -0
  147. package/packages/piagent-core/runtime/policy/block-remedy.ts +0 -4
  148. package/packages/piagent-core/runtime/product/efficiency-metrics.ts +3 -0
  149. package/packages/piagent-core/runtime/quality/edit-freshness-guard.ts +102 -0
  150. package/packages/piagent-core/runtime/quality/exact-output-contract.ts +20 -10
  151. package/packages/piagent-core/runtime/quality/performance-assurance.ts +3 -3
  152. package/packages/piagent-core/runtime/recovery/continuation-budget.ts +1 -1
  153. package/packages/piagent-core/runtime/recovery/handoff-projection.ts +4 -1
  154. package/packages/piagent-core/runtime/recovery/pristine-task-policy-refresh.ts +91 -0
  155. package/packages/piagent-core/runtime/recovery/recovery-policy.ts +7 -4
  156. package/packages/piagent-core/runtime/recovery/resume-state.ts +112 -14
  157. package/packages/piagent-core/runtime/recovery/semantic-repair-handshake.ts +7 -13
  158. package/packages/piagent-core/runtime/recovery/task-evidence-coverage-refresh.ts +217 -0
  159. package/packages/piagent-core/runtime/registration/context-commands.ts +16 -2
  160. package/packages/piagent-core/runtime/registration/knowledge-tools.ts +9 -5
  161. package/packages/piagent-core/runtime/registration/operator-request-intake.ts +49 -0
  162. package/packages/piagent-core/runtime/registration/policy-tools.ts +4 -4
  163. package/packages/piagent-core/runtime/registration/session-commands.ts +1 -1
  164. package/packages/piagent-core/runtime/registration/task-completion-tools.ts +12 -9
  165. package/packages/piagent-core/runtime/registration/task-evidence-tools.ts +21 -12
  166. package/packages/piagent-core/runtime/registration/task-start-guidance.ts +19 -13
  167. package/packages/piagent-core/runtime/registration/task-start-manifest.ts +54 -0
  168. package/packages/piagent-core/runtime/registration/task-start-tool.ts +57 -57
  169. package/packages/piagent-core/runtime/runtime-limits.ts +16 -2
  170. package/packages/piagent-core/runtime/session/adaptive-context-analysis.ts +461 -0
  171. package/packages/piagent-core/runtime/session/adaptive-context-governor.ts +464 -0
  172. package/packages/piagent-core/runtime/session/adaptive-context-ledger.ts +265 -0
  173. package/packages/piagent-core/runtime/session/message-signals.ts +7 -3
  174. package/packages/piagent-core/runtime/session/operator-request-carry.ts +44 -0
  175. package/packages/piagent-core/runtime/session/runtime-state.ts +11 -7
  176. package/packages/piagent-core/runtime/session/source-checkout-read-grants.ts +35 -0
  177. package/packages/piagent-core/runtime/session/system-prompt.ts +76 -14
  178. package/packages/piagent-core/runtime/session/task-contract-presentation.ts +39 -0
  179. package/packages/piagent-core/runtime/tools/phase-tools.ts +8 -8
  180. package/packages/piagent-core/runtime/tools/tool-groups.ts +12 -2
  181. package/packages/piagent-core/runtime/verification/exact-verifier-reuse.ts +101 -0
  182. package/packages/piagent-core/runtime/workflows/runtime-intake-compaction.ts +164 -0
  183. package/packages/piagent-core/runtime/workflows/task-intake.ts +167 -39
  184. package/packages/piagent-core/subagents/piagent-oracle.md +5 -3
  185. package/packages/piagent-core/subagents/piagent-planner.md +6 -4
  186. package/packages/piagent-core/subagents/piagent-reviewer.md +5 -3
  187. package/packages/piagent-core/subagents/piagent-scout.md +4 -2
  188. package/packages/piagent-core/subagents/piagent-worker.md +6 -4
  189. package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-CxeZqTte.js → MarkdownMessage-B4XehTCU.js} +1 -1
  190. package/packages/piagent-webui/dist/client/assets/{index-DynIMe8I.js → index-CgOEyRPG.js} +4 -4
  191. package/packages/piagent-webui/dist/client/index.html +1 -1
  192. package/packages/piagent-webui/extension/piagent-webui.ts +3 -1
  193. package/packages/piagent-webui/extension/same-session-bridge.ts +1 -1
  194. package/packages/piagent-webui/extension/session-stream-adapter.ts +33 -10
  195. package/packages/piagent-webui/gateway/gateway-session-stream.ts +37 -21
  196. package/packages/piagent-webui/gateway/session-operation-lifecycle.ts +237 -0
  197. package/packages/piagent-webui/gateway/session-operation-retry-control.ts +38 -0
  198. package/packages/piagent-webui/gateway/session-operation-watchdog.ts +9 -2
  199. package/packages/piagent-webui/gateway/session-runtime-supervisor.ts +8 -7
  200. package/packages/piagent-webui/ownership/session-lease-store.ts +5 -2
  201. package/packages/piagent-webui/server/core-inspection-provider.ts +2 -1
  202. package/packages/piagent-webui/server/transcript-projection.ts +91 -5
  203. package/schemas/codex-relative-efficiency-v1.schema.json +155 -0
  204. package/schemas/piagent-webui/runtime-event-v2.schema.json +15 -5
  205. package/schemas/piagent-webui/subagent-tree-v1.schema.json +11 -1
  206. package/schemas/project-profile.schema.json +1 -1
  207. package/schemas/task-contract.schema.json +15 -0
  208. package/scripts/benchmark-runner-core.mjs +14 -6
  209. package/scripts/benchmark-runner-finalization.mjs +4 -0
  210. package/scripts/configure-subagents.sh +42 -34
  211. package/scripts/helper-evaluation.mjs +1 -1
  212. package/scripts/team-doctor.sh +1 -1
  213. package/scripts/verify-local.sh +5 -0
@@ -0,0 +1,456 @@
1
+ import assert from "node:assert/strict";
2
+ import fs from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ import {
8
+ auditToolProtocol,
9
+ minimumContextSavingsTokens,
10
+ projectAdaptiveContext
11
+ } from "../../packages/piagent-core/runtime/session/adaptive-context-governor.ts";
12
+ import {
13
+ BoundedEmissionGuard,
14
+ runtimeEventEmissionCandidate
15
+ } from "../../packages/piagent-core/runtime/inspection/bounded-emission-guard.ts";
16
+ import { EditFreshnessGuard } from "../../packages/piagent-core/runtime/quality/edit-freshness-guard.ts";
17
+ import {
18
+ SessionOperationLifecycle,
19
+ sessionOperationRetryPolicy
20
+ } from "../../packages/piagent-webui/gateway/session-operation-lifecycle.ts";
21
+
22
+ const laneRoot = path.dirname(fileURLToPath(import.meta.url));
23
+ const lane = JSON.parse(fs.readFileSync(path.join(laneRoot, "lane.json"), "utf8"));
24
+ const argumentsList = process.argv.slice(2);
25
+ const option = (name) => {
26
+ const index = argumentsList.indexOf(name);
27
+ return index >= 0 ? argumentsList[index + 1] : undefined;
28
+ };
29
+ const outputArgument = option("--output");
30
+ if (argumentsList.includes("--output") && !outputArgument) throw new Error("--output requires a path");
31
+ const outputPath = outputArgument ? path.resolve(outputArgument) : null;
32
+ const startedAtMs = Date.now();
33
+ let timestamp = 1;
34
+
35
+ function user(text) {
36
+ return { role: "user", content: text, timestamp: timestamp++ };
37
+ }
38
+
39
+ function toolRound(id, name, args, output) {
40
+ return [
41
+ {
42
+ role: "assistant",
43
+ content: [{ type: "toolCall", id, name, arguments: args }],
44
+ timestamp: timestamp++
45
+ },
46
+ {
47
+ role: "toolResult",
48
+ toolCallId: id,
49
+ toolName: name,
50
+ content: [{ type: "text", text: output }],
51
+ isError: false,
52
+ timestamp: timestamp++
53
+ }
54
+ ];
55
+ }
56
+
57
+ function task() {
58
+ return {
59
+ taskId: "runtime-conformance",
60
+ taskRunId: "runtime-conformance-run-1",
61
+ summary: "Implement a frontend contract without changing backend behavior.",
62
+ riskLane: "normal",
63
+ acceptanceCriteria: ["Preserve behavior", "Pass focused verification"],
64
+ scope: ["src/**"],
65
+ outOfScope: ["backend mutation"],
66
+ changedFiles: ["src/page.tsx"],
67
+ verifyCommands: ["node --test"],
68
+ trace: { outcome: "pending" }
69
+ };
70
+ }
71
+
72
+ function boundedCandidate(text, updateKey, reference) {
73
+ return {
74
+ channel: "reviewer",
75
+ identityKey: "runtime-conformance-reviewer",
76
+ updateKey,
77
+ text,
78
+ actionable: true,
79
+ reference
80
+ };
81
+ }
82
+
83
+ function finalMessageCandidate() {
84
+ return runtimeEventEmissionCandidate({
85
+ kind: "message.completed",
86
+ sessionRef: "runtime-conformance-session",
87
+ taskRunId: "runtime-conformance-run-1",
88
+ agentOperationId: "runtime-conformance-operation",
89
+ turnIndex: 1,
90
+ toolCallId: null,
91
+ revision: { runtimeRevision: "runtime-revision-1" },
92
+ payload: { role: "assistant", textPreview: "Final response remains visible." }
93
+ }, "message-final");
94
+ }
95
+
96
+ const results = [];
97
+ const metrics = {
98
+ context: {
99
+ projections: 0,
100
+ noops: 0,
101
+ estimatedSavingsTokens: 0,
102
+ governorProviderCalls: 0,
103
+ toolProtocolOrphans: 0
104
+ },
105
+ operations: {
106
+ pristineRetriesAllowed: 0,
107
+ unsafeRetriesAllowed: 0,
108
+ unsafeRetriesAborted: 0,
109
+ terminalSettlements: 0,
110
+ duplicateTerminalSettlements: 0,
111
+ lateEventsIgnored: 0,
112
+ compactionRetryBudgetConsumed: 0
113
+ },
114
+ emissions: {
115
+ accepted: 0,
116
+ suppressed: 0,
117
+ retained: 0,
118
+ finalMessagesSuppressed: 0
119
+ },
120
+ edits: {
121
+ staleEvaluations: 0,
122
+ staleMutationsAllowed: 0,
123
+ freshRereadRecoveries: 0
124
+ }
125
+ };
126
+
127
+ function runCase(id, area, execute) {
128
+ const caseStartedAt = Date.now();
129
+ try {
130
+ const evidence = execute() ?? {};
131
+ results.push({ id, area, status: "passed", durationMilliseconds: Date.now() - caseStartedAt, evidence });
132
+ } catch (error) {
133
+ results.push({
134
+ id,
135
+ area,
136
+ status: "failed",
137
+ durationMilliseconds: Date.now() - caseStartedAt,
138
+ error: error instanceof Error ? error.message : String(error)
139
+ });
140
+ }
141
+ }
142
+
143
+ runCase("context-small-passthrough", "adaptive-context-governor", () => {
144
+ const messages = [user("Implement src/cart.ts"), ...toolRound("small-read", "read", { path: "src/cart.ts" }, "export const cart = true;")];
145
+ const projection = projectAdaptiveContext(messages, {
146
+ reportedTokens: 9_000,
147
+ contextWindow: 400_000,
148
+ task: task()
149
+ });
150
+ assert.equal(projection.decision.action, "passthrough");
151
+ assert.equal(projection.messages, messages);
152
+ assert.equal(projection.decision.accounting.billedTraffic.governorProviderCalls, 0);
153
+ metrics.context.governorProviderCalls += projection.decision.accounting.billedTraffic.governorProviderCalls;
154
+ return { action: projection.decision.action, originalMessages: messages.length };
155
+ });
156
+
157
+ runCase("context-insufficient-savings-noop", "adaptive-context-governor", () => {
158
+ const messages = [user("Continue the same implementation")];
159
+ for (let index = 0; index < 8; index += 1) {
160
+ messages.push(...toolRound(`noop-${index}`, "read", { path: `src/noop-${index}.ts` }, "x".repeat(6_000)));
161
+ }
162
+ const projection = projectAdaptiveContext(messages, {
163
+ reportedTokens: 250_000,
164
+ contextWindow: 400_000,
165
+ task: task()
166
+ });
167
+ assert.equal(projection.decision.action, "passthrough");
168
+ assert.equal(projection.decision.fallback, "insufficient-savings");
169
+ assert.equal(projection.messages, messages);
170
+ assert.ok(projection.decision.estimatedSavingsTokens < projection.decision.minimumSavingsTokens);
171
+ assert.equal(
172
+ projection.decision.minimumSavingsTokens,
173
+ minimumContextSavingsTokens(projection.decision.originalMessageTokens)
174
+ );
175
+ assert.equal(projection.decision.accounting.billedTraffic.governorProviderCalls, 0);
176
+ metrics.context.noops += 1;
177
+ metrics.context.governorProviderCalls += projection.decision.accounting.billedTraffic.governorProviderCalls;
178
+ return {
179
+ action: projection.decision.action,
180
+ fallback: projection.decision.fallback,
181
+ candidateSavingsTokens: projection.decision.estimatedSavingsTokens,
182
+ minimumSavingsTokens: projection.decision.minimumSavingsTokens
183
+ };
184
+ });
185
+
186
+ runCase("context-projection-protocol-safe", "adaptive-context-governor", () => {
187
+ const messages = [user("Scout the backend subscription contract")];
188
+ for (let index = 0; index < 14; index += 1) {
189
+ messages.push(...toolRound(
190
+ `project-${index}`,
191
+ "read",
192
+ { path: `backend/src/module-${index}.ts` },
193
+ `source-${index}\n${"x".repeat(5_000)}`
194
+ ));
195
+ }
196
+ messages.push(user("Proceed to implement the frontend subscription mapping"));
197
+ const projection = projectAdaptiveContext(messages, {
198
+ reportedTokens: 92_000,
199
+ contextWindow: 400_000,
200
+ task: task()
201
+ });
202
+ const protocol = auditToolProtocol(projection.messages);
203
+ assert.equal(projection.decision.action, "project");
204
+ assert.equal(projection.decision.reason, "semantic-boundary");
205
+ assert.equal(protocol.intact, true);
206
+ assert.equal(protocol.orphanCallIds.length + protocol.orphanResultIds.length, 0);
207
+ assert.ok(projection.decision.estimatedSavingsTokens >= projection.decision.minimumSavingsTokens);
208
+ assert.match(String(projection.messages.at(-1)?.content ?? ""), /implement the frontend subscription mapping/i);
209
+ assert.equal(projection.decision.accounting.billedTraffic.governorProviderCalls, 0);
210
+ metrics.context.projections += 1;
211
+ metrics.context.estimatedSavingsTokens += projection.decision.estimatedSavingsTokens;
212
+ metrics.context.governorProviderCalls += projection.decision.accounting.billedTraffic.governorProviderCalls;
213
+ metrics.context.toolProtocolOrphans += protocol.orphanCallIds.length + protocol.orphanResultIds.length;
214
+ return {
215
+ action: projection.decision.action,
216
+ reason: projection.decision.reason,
217
+ originalMessageTokens: projection.decision.originalMessageTokens,
218
+ projectedMessageTokens: projection.decision.projectedMessageTokens,
219
+ estimatedSavingsTokens: projection.decision.estimatedSavingsTokens,
220
+ savingsRatio: projection.decision.savingsRatio,
221
+ toolProtocolIntact: protocol.intact
222
+ };
223
+ });
224
+
225
+ runCase("context-orphan-protocol-fail-closed", "adaptive-context-governor", () => {
226
+ const messages = [user("Continue implementation")];
227
+ for (let index = 0; index < 8; index += 1) {
228
+ messages.push(...toolRound(`valid-${index}`, "read", { path: `src/valid-${index}.ts` }, "x".repeat(6_000)));
229
+ }
230
+ messages.push({
231
+ role: "toolResult",
232
+ toolCallId: "missing-call",
233
+ toolName: "read",
234
+ content: [{ type: "text", text: "orphan result" }],
235
+ timestamp: timestamp++
236
+ });
237
+ messages.push({
238
+ role: "assistant",
239
+ content: [{ type: "toolCall", id: "missing-result", name: "read", arguments: { path: "src/missing.ts" } }],
240
+ timestamp: timestamp++
241
+ });
242
+ const sourceProtocol = auditToolProtocol(messages);
243
+ const projection = projectAdaptiveContext(messages, {
244
+ reportedTokens: 250_000,
245
+ contextWindow: 400_000,
246
+ task: task()
247
+ });
248
+ assert.equal(sourceProtocol.intact, false);
249
+ assert.equal(projection.decision.action, "passthrough");
250
+ assert.equal(projection.decision.fallback, "unsafe-tool-protocol");
251
+ assert.equal(projection.messages, messages);
252
+ assert.ok(projection.decision.reasonCodes.includes("unsafe-tool-protocol"));
253
+ assert.equal(projection.decision.accounting.billedTraffic.governorProviderCalls, 0);
254
+ metrics.context.noops += 1;
255
+ metrics.context.governorProviderCalls += projection.decision.accounting.billedTraffic.governorProviderCalls;
256
+ return {
257
+ sourceOrphanCalls: sourceProtocol.orphanCallIds.length,
258
+ sourceOrphanResults: sourceProtocol.orphanResultIds.length,
259
+ projected: false,
260
+ fallback: projection.decision.fallback
261
+ };
262
+ });
263
+
264
+ runCase("operation-pristine-retry-bounded", "webui-operation-lifecycle", () => {
265
+ assert.deepEqual(sessionOperationRetryPolicy(), { maximumAttempts: 1, maximumDelayMs: 8_000 });
266
+ const lifecycle = new SessionOperationLifecycle({ operationRef: "operation-pristine" });
267
+ lifecycle.observe({ type: "agent_start", operationRef: "operation-pristine" });
268
+ const first = lifecycle.observe({
269
+ type: "auto_retry_start",
270
+ operationRef: "operation-pristine",
271
+ attempt: 1,
272
+ delayMs: 100
273
+ });
274
+ assert.equal(first.retry, "allowed");
275
+ metrics.operations.pristineRetriesAllowed += 1;
276
+ lifecycle.observe({ type: "auto_retry_end", operationRef: "operation-pristine", attempt: 1, success: false });
277
+ const second = lifecycle.observe({
278
+ type: "auto_retry_start",
279
+ operationRef: "operation-pristine",
280
+ attempt: 2,
281
+ delayMs: 100
282
+ });
283
+ assert.equal(second.retry, "abort");
284
+ assert.equal(second.reasonCode, "automatic-retry-attempt-limit");
285
+ return { maximumAttempts: lifecycle.retryPolicy.maximumAttempts, firstRetry: first.retry, secondRetry: second.retry };
286
+ });
287
+
288
+ runCase("operation-replay-unsafe-and-exactly-once", "webui-operation-lifecycle", () => {
289
+ const visible = new SessionOperationLifecycle({ operationRef: "operation-visible" });
290
+ visible.observe({ type: "agent_start" });
291
+ visible.observe({
292
+ type: "message_update",
293
+ message: { role: "assistant", content: [{ type: "text", text: "Visible output" }] },
294
+ assistantMessageEvent: { type: "text_delta", delta: "Visible output" }
295
+ });
296
+ const visibleRetry = visible.observe({ type: "auto_retry_start", attempt: 1, delayMs: 0 });
297
+ assert.equal(visibleRetry.retry, "abort");
298
+ assert.equal(visibleRetry.reasonCode, "automatic-retry-replay-unsafe");
299
+
300
+ const tool = new SessionOperationLifecycle({ operationRef: "operation-tool" });
301
+ tool.observe({ type: "agent_start" });
302
+ tool.observe({ type: "tool_execution_start", toolCallId: "write-1", toolName: "write" });
303
+ const toolRetry = tool.observe({ type: "auto_retry_start", attempt: 1, delayMs: 0 });
304
+ assert.equal(toolRetry.retry, "abort");
305
+ assert.equal(toolRetry.reasonCode, "automatic-retry-replay-unsafe");
306
+ metrics.operations.unsafeRetriesAborted += 2;
307
+
308
+ assert.equal(tool.markTerminal(), true);
309
+ metrics.operations.terminalSettlements += 1;
310
+ const duplicateTerminal = tool.markTerminal();
311
+ assert.equal(duplicateTerminal, false);
312
+ if (duplicateTerminal) metrics.operations.duplicateTerminalSettlements += 1;
313
+ const late = tool.observe({ type: "message_update", assistantMessageEvent: { type: "text_delta", delta: "late" } });
314
+ assert.equal(late.accepted, false);
315
+ assert.equal(late.reasonCode, "operation-event-after-settlement");
316
+ metrics.operations.lateEventsIgnored += 1;
317
+ return {
318
+ visibleRetry: visibleRetry.retry,
319
+ toolRetry: toolRetry.retry,
320
+ terminalSettlements: 1,
321
+ duplicateTerminalSettlements: 0,
322
+ lateEvent: late.reasonCode
323
+ };
324
+ });
325
+
326
+ runCase("operation-compaction-isolated", "webui-operation-lifecycle", () => {
327
+ const lifecycle = new SessionOperationLifecycle({ operationRef: "operation-compaction" });
328
+ lifecycle.observe({ type: "agent_start" });
329
+ const start = lifecycle.observe({ type: "compaction_start", reason: "overflow" });
330
+ assert.equal(start.phase, "compaction");
331
+ const summarization = lifecycle.observe({ type: "summarization_retry_scheduled", attempt: 1, delayMs: 5 });
332
+ assert.equal(summarization.accepted, true);
333
+ assert.equal(summarization.retry, "none");
334
+ const end = lifecycle.observe({ type: "compaction_end", reason: "overflow" });
335
+ assert.equal(end.phase, "running");
336
+ const providerRetry = lifecycle.observe({ type: "auto_retry_start", attempt: 1, delayMs: 5 });
337
+ assert.equal(providerRetry.retry, "allowed");
338
+ metrics.operations.pristineRetriesAllowed += 1;
339
+ return { compactionPhase: start.phase, summarizationRetry: summarization.retry, providerRetry: providerRetry.retry };
340
+ });
341
+
342
+ runCase("emission-bounded-final-preserved", "bounded-emission-guard", () => {
343
+ const guard = new BoundedEmissionGuard({ capacity: 2 });
344
+ const filler = guard.inspect(boundedCandidate("No issue; continue.", "update-0", "review-0"));
345
+ assert.equal(filler.reason, "filler");
346
+
347
+ const first = boundedCandidate("Potential race in queue.", "update-1", "review-1");
348
+ assert.equal(guard.inspect(first).accepted, true);
349
+ guard.recordAccepted(first);
350
+ assert.equal(guard.inspect(boundedCandidate("Potential race in queue.", "update-2", "review-2")).reason, "duplicate-exact");
351
+ assert.equal(guard.inspect(boundedCandidate("potential RACE in queue!", "update-3", "review-3")).reason, "duplicate-normalized");
352
+
353
+ const second = boundedCandidate("Mutation lacks a verifier.", "update-4", "review-4");
354
+ assert.equal(guard.inspect(second).accepted, true);
355
+ guard.recordAccepted(second);
356
+ const third = boundedCandidate("Late activity remains bounded.", "update-5", "review-5");
357
+ assert.equal(guard.inspect(third).accepted, true);
358
+ guard.recordAccepted(third);
359
+
360
+ const telemetry = guard.telemetry();
361
+ assert.equal(telemetry.retained, 2);
362
+ assert.equal(finalMessageCandidate(), null, "final assistant messages must bypass suppression");
363
+ metrics.emissions.accepted += telemetry.accepted;
364
+ metrics.emissions.suppressed += telemetry.suppressed;
365
+ metrics.emissions.retained = telemetry.retained;
366
+ return {
367
+ capacity: telemetry.capacity,
368
+ retained: telemetry.retained,
369
+ accepted: telemetry.accepted,
370
+ suppressed: telemetry.suppressed,
371
+ suppressedByReason: telemetry.suppressedByReason,
372
+ finalMessageBypassesGuard: true
373
+ };
374
+ });
375
+
376
+ runCase("edit-stale-blocked-until-reread", "edit-freshness-guard", () => {
377
+ const cwd = fs.mkdtempSync(path.join(os.tmpdir(), "piagent-runtime-conformance-edit-"));
378
+ try {
379
+ fs.mkdirSync(path.join(cwd, "src"), { recursive: true });
380
+ const target = path.join(cwd, "src", "value.ts");
381
+ fs.writeFileSync(target, "export const value = 1;\n");
382
+ const ctx = { cwd, sessionManager: { getSessionId: () => "runtime-conformance-session" } };
383
+ const guard = new EditFreshnessGuard("enforce");
384
+ assert.deepEqual(guard.observe(ctx, "runtime-conformance-run-1", ["src/value.ts"], "read"), ["src/value.ts"]);
385
+ assert.equal(guard.evaluate(ctx, "runtime-conformance-run-1", ["src/value.ts"]).decision, "current");
386
+
387
+ fs.writeFileSync(target, "// concurrent change\nexport const value = 1;\n");
388
+ const beforeRejectedMutation = fs.readFileSync(target, "utf8");
389
+ const stale = guard.evaluate(ctx, "runtime-conformance-run-1", ["src/value.ts"]);
390
+ assert.equal(stale.decision, "stale");
391
+ assert.equal(stale.enforce, true);
392
+ assert.deepEqual(stale.stalePaths, ["src/value.ts"]);
393
+ assert.equal(fs.readFileSync(target, "utf8"), beforeRejectedMutation, "evaluation must not mutate stale content");
394
+ metrics.edits.staleEvaluations += 1;
395
+
396
+ guard.observe(ctx, "runtime-conformance-run-1", ["src/value.ts"], "read");
397
+ const recovered = guard.evaluate(ctx, "runtime-conformance-run-1", ["src/value.ts"]);
398
+ assert.equal(recovered.decision, "current");
399
+ metrics.edits.freshRereadRecoveries += 1;
400
+ return { staleDecision: stale.decision, stalePaths: stale.stalePaths, recoveredDecision: recovered.decision };
401
+ } finally {
402
+ fs.rmSync(cwd, { recursive: true, force: true });
403
+ }
404
+ });
405
+
406
+ const completedAtMs = Date.now();
407
+ const failed = results.filter((result) => result.status !== "passed");
408
+ const configuredCaseIds = lane.cases.map((item) => item.id);
409
+ const observedCaseIds = results.map((item) => item.id);
410
+ const configuredCasesMatched = JSON.stringify(configuredCaseIds) === JSON.stringify(observedCaseIds);
411
+ const gates = {
412
+ allCasesPass: failed.length === 0 && configuredCasesMatched,
413
+ providerCalls: metrics.context.governorProviderCalls === lane.releaseGates.providerCalls,
414
+ modelInputTokens: lane.releaseGates.modelInputTokens === 0,
415
+ modelOutputTokens: lane.releaseGates.modelOutputTokens === 0,
416
+ toolProtocolOrphans: metrics.context.toolProtocolOrphans === lane.releaseGates.toolProtocolOrphans,
417
+ unsafeRetriesAllowed: metrics.operations.unsafeRetriesAllowed === lane.releaseGates.unsafeRetriesAllowed,
418
+ duplicateTerminalSettlements: metrics.operations.duplicateTerminalSettlements === lane.releaseGates.duplicateTerminalSettlements,
419
+ finalMessagesSuppressed: metrics.emissions.finalMessagesSuppressed === lane.releaseGates.finalMessagesSuppressed,
420
+ staleMutationsAllowed: metrics.edits.staleMutationsAllowed === lane.releaseGates.staleMutationsAllowed
421
+ };
422
+ const passed = failed.length === 0 && configuredCasesMatched && Object.values(gates).every(Boolean);
423
+ const report = {
424
+ schemaVersion: 1,
425
+ laneId: lane.id,
426
+ evidenceClass: "provider-free-runtime-conformance",
427
+ startedAt: new Date(startedAtMs).toISOString(),
428
+ completedAt: new Date(completedAtMs).toISOString(),
429
+ wallClockMilliseconds: completedAtMs - startedAtMs,
430
+ provider: {
431
+ required: lane.providerRequired,
432
+ used: false,
433
+ calls: 0,
434
+ modelInputTokens: 0,
435
+ modelOutputTokens: 0
436
+ },
437
+ summary: {
438
+ configuredCases: lane.cases.length,
439
+ executedCases: results.length,
440
+ passedCases: results.length - failed.length,
441
+ failedCases: failed.length,
442
+ passed
443
+ },
444
+ metrics,
445
+ gates,
446
+ cases: results,
447
+ claimBoundary: lane.claimBoundary
448
+ };
449
+
450
+ const serialized = `${JSON.stringify(report, null, 2)}\n`;
451
+ if (outputPath) {
452
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
453
+ fs.writeFileSync(outputPath, serialized);
454
+ }
455
+ process.stdout.write(serialized);
456
+ if (!passed) process.exitCode = 1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@piagent/platform",
3
- "version": "1.6.0",
3
+ "version": "1.6.1",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -22,8 +22,11 @@ Architecture boundaries and non-growth file budgets are enforced by `npm run arc
22
22
  The root package exposes `piagent-benchmark` for an automatic paired Piagent
23
23
  steady-state benchmark against Raw Pi or the `codex-cli` surface. Treatment
24
24
  onboarding/context preparation happens before measured model usage;
25
- post-baseline mutations remain scope violations. Codex runs through a strict
26
- streaming JSONL adapter and defaults to controlled ephemeral execution with an
25
+ post-baseline mutations remain auditable. Task scope is an advisory retrieval
26
+ and review focus rather than filesystem authority; Codex can follow repository
27
+ evidence across packages and child repositories. Protected/read-only paths,
28
+ destructive commands, and external actions remain governed. Codex runs through a
29
+ strict streaming JSONL adapter and defaults to controlled ephemeral execution with an
27
30
  isolated temporary Codex home. Run
28
31
  `piagent-benchmark --dry-run` before starting billed model sessions; see
29
32
  `docs/quality-benchmark.md` for score and verdict rules.
@@ -130,10 +133,15 @@ When `pi-subagents` is installed, this package exposes:
130
133
 
131
134
  - `piagent-scout`
132
135
  - `piagent-planner`
133
- - `piagent-worker`
136
+ - `piagent-worker` (compatibility metadata; disabled by Piagent config)
134
137
  - `piagent-reviewer`
135
138
  - `piagent-oracle`
136
139
 
140
+ The parent model works directly. Helpers default to `off`; explicit opt-in still
141
+ allows only one fresh read-only helper after a runtime-owned estimate proves at
142
+ least 30% projected net token saving. Worker, parallel, nested, and retry paths
143
+ remain disabled.
144
+
137
145
  ## Install
138
146
 
139
147
  ```bash
@@ -197,7 +205,8 @@ rebuild and database vacuum before the new policy is considered clean.
197
205
  Passing source final gates require an observed exit `0` result for every
198
206
  meaningful entry in `task.verifyCommands`. Other observed commands are traceable
199
207
  but advisory. Declared changed files must also differ from the task baseline,
200
- match observed tool results and remain inside `task.scope`.
208
+ and match observed tool results. Changes beyond `task.scope` are retained as an
209
+ auditable focus expansion and do not block source mutation or completion.
201
210
 
202
211
  Raw path-like tool access to protected paths is blocked before execution. This includes Pi built-ins (`read`, `write`, `edit`, `grep`, `find`, `ls`) and custom/MCP tools with nested path-like strings, arrays, or `file://` URIs. Path-like strings are percent-decoded once, and input nesting above `MAX_TOOL_INPUT_INSPECTION_DEPTH=32` fails closed. Known content fields such as `content`, `query`, `pattern`, `text`, and `command` are excluded from generic extraction. `grep.glob` and `find.pattern` are checked when they explicitly target protected paths, while broad `grep`, `find`, and `ls` results are filtered so protected content lines or path metadata are redacted before reaching the model.
203
212