@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,464 @@
1
+ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
2
+
3
+ import type { TaskContract } from "../../extensions/guard-types.ts";
4
+ import {
5
+ CONTEXT_GOVERNOR_RECENT_MESSAGE_TOKENS,
6
+ CONTEXT_GOVERNOR_TARGET_MESSAGE_TOKENS
7
+ } from "../runtime-limits.ts";
8
+ import {
9
+ GOVERNOR_VERSION,
10
+ analyzeContextResidency,
11
+ auditToolProtocol,
12
+ detectSemanticBoundary,
13
+ estimateGovernorMessagesTokens,
14
+ minimumContextSavingsTokens,
15
+ reasonForProjection,
16
+ safeSuffixStart,
17
+ type ContextGovernorDecision,
18
+ type MessageLike
19
+ } from "./adaptive-context-analysis.ts";
20
+ import {
21
+ adaptiveContextCompactionCancelled,
22
+ adaptiveContextCanPreserveTask,
23
+ buildAdaptiveContextLedger,
24
+ deterministicTaskCompaction,
25
+ ledgerMessage
26
+ } from "./adaptive-context-ledger.ts";
27
+
28
+ export {
29
+ analyzeContextResidency,
30
+ auditToolProtocol,
31
+ classifySemanticPhase,
32
+ detectSemanticBoundary,
33
+ estimateGovernorMessageTokens,
34
+ estimateGovernorMessagesTokens,
35
+ minimumContextSavingsTokens
36
+ } from "./adaptive-context-analysis.ts";
37
+ export type {
38
+ ContextGovernorAccounting,
39
+ ContextGovernorDecision,
40
+ ContextResidencyMetrics,
41
+ SemanticBoundary,
42
+ SemanticPhase,
43
+ ToolProtocolAudit
44
+ } from "./adaptive-context-analysis.ts";
45
+ export {
46
+ buildAdaptiveContextLedger,
47
+ deterministicTaskCompaction
48
+ } from "./adaptive-context-ledger.ts";
49
+
50
+ export type ContextProjection = {
51
+ messages: MessageLike[];
52
+ decision: ContextGovernorDecision;
53
+ };
54
+
55
+ type ProjectionOptions = {
56
+ reportedTokens?: number | null;
57
+ contextWindow?: number | null;
58
+ task?: TaskContract;
59
+ };
60
+
61
+ type GovernorSessionState = {
62
+ lastProjection?: ContextGovernorDecision;
63
+ lastProjectionTelemetryKey?: string;
64
+ lastNoopTelemetryKey?: string;
65
+ lastCompactionFallbackTelemetryKey?: string;
66
+ };
67
+
68
+ type GovernorDependencies = {
69
+ activeTask: (ctx: ExtensionContext) => TaskContract | undefined;
70
+ telemetry: (ctx: ExtensionContext, payload: Record<string, unknown>) => void;
71
+ };
72
+
73
+ export function projectAdaptiveContext(messages: MessageLike[], options: ProjectionOptions = {}): ContextProjection {
74
+ const original = Array.isArray(messages) ? messages : [];
75
+ const residency = analyzeContextResidency(original);
76
+ const boundary = detectSemanticBoundary(original);
77
+ const providerReportedTokens = options.reportedTokens === null || options.reportedTokens === undefined
78
+ ? null
79
+ : Math.max(0, Number(options.reportedTokens) || 0);
80
+ const reportedTokens = providerReportedTokens ?? 0;
81
+ const contextWindow = Math.max(0, Number(options.contextWindow ?? 0) || 0);
82
+ const effectiveTokens = Math.max(reportedTokens, residency.estimatedMessageTokens);
83
+ const percent = contextWindow > 0 ? (effectiveTokens / contextWindow) * 100 : 0;
84
+ const selected = reasonForProjection(effectiveTokens, contextWindow, boundary, residency);
85
+
86
+ const accounting = (projectedTranscriptTokens: number) => ({
87
+ contextOccupancy: {
88
+ providerReportedTokens,
89
+ transcriptEstimatedTokens: residency.estimatedMessageTokens,
90
+ projectedTranscriptTokens,
91
+ effectivePressureTokens: effectiveTokens,
92
+ contextWindow
93
+ },
94
+ billedTraffic: {
95
+ measured: false as const,
96
+ inputTokens: null,
97
+ outputTokens: null,
98
+ cacheReadTokens: null,
99
+ governorProviderCalls: 0 as const,
100
+ reason: "provider-billing-not-exposed-by-context-hook" as const
101
+ }
102
+ });
103
+ const passthrough = (input: {
104
+ reason?: ContextGovernorDecision["reason"];
105
+ reasonCodes?: string[];
106
+ fallback?: ContextGovernorDecision["fallback"];
107
+ candidateProjectedMessageTokens?: number;
108
+ estimatedSavingsTokens?: number;
109
+ minimumSavingsTokens?: number;
110
+ } = {}): ContextProjection => ({
111
+ messages: original,
112
+ decision: {
113
+ version: GOVERNOR_VERSION,
114
+ action: "passthrough",
115
+ reason: input.reason ?? "none",
116
+ reasonCodes: input.reasonCodes ?? [],
117
+ reportedTokens,
118
+ effectiveTokens,
119
+ contextWindow,
120
+ percent,
121
+ originalMessageTokens: residency.estimatedMessageTokens,
122
+ projectedMessageTokens: residency.estimatedMessageTokens,
123
+ candidateProjectedMessageTokens: input.candidateProjectedMessageTokens ?? residency.estimatedMessageTokens,
124
+ estimatedSavingsTokens: input.estimatedSavingsTokens ?? 0,
125
+ minimumSavingsTokens: input.minimumSavingsTokens ?? 0,
126
+ savingsRatio: residency.estimatedMessageTokens > 0
127
+ ? (input.estimatedSavingsTokens ?? 0) / residency.estimatedMessageTokens
128
+ : 0,
129
+ fallback: input.fallback ?? "none",
130
+ originalMessages: original.length,
131
+ projectedMessages: original.length,
132
+ accounting: accounting(residency.estimatedMessageTokens),
133
+ boundary,
134
+ residency
135
+ }
136
+ });
137
+ if (selected.reason === "none" || original.length < 6) return passthrough();
138
+ if (!adaptiveContextCanPreserveTask(options.task)) return passthrough({
139
+ reason: selected.reason,
140
+ reasonCodes: [...selected.codes, "lossless-task-truth-exceeds-ledger"],
141
+ fallback: "no-safe-boundary",
142
+ minimumSavingsTokens: minimumContextSavingsTokens(residency.estimatedMessageTokens)
143
+ });
144
+ const originalProtocol = auditToolProtocol(original);
145
+ if (!originalProtocol.intact) return passthrough({
146
+ reason: selected.reason,
147
+ reasonCodes: [
148
+ ...selected.codes,
149
+ "unsafe-tool-protocol",
150
+ `orphan-calls:${originalProtocol.orphanCallIds.length}`,
151
+ `orphan-results:${originalProtocol.orphanResultIds.length}`
152
+ ],
153
+ fallback: "unsafe-tool-protocol",
154
+ minimumSavingsTokens: minimumContextSavingsTokens(residency.estimatedMessageTokens)
155
+ });
156
+
157
+ const configuredRecentBudget = selected.reason === "semantic-boundary"
158
+ ? boundary.kind === "task" ? 20_000 : 30_000
159
+ : selected.reason === "provider-ceiling" ? 32_000 : CONTEXT_GOVERNOR_RECENT_MESSAGE_TOKENS;
160
+ // Reported provider usage includes static prompt/tool-schema tokens that are
161
+ // not present in `messages`. When the dynamic transcript itself is smaller
162
+ // than the configured suffix, retain a proportional recent working set so a
163
+ // phase boundary or residency signal can still remove stale transcript.
164
+ const recentBudget = Math.min(
165
+ configuredRecentBudget,
166
+ Math.max(8_000, Math.floor(residency.estimatedMessageTokens * 0.45))
167
+ );
168
+ const suffixStart = safeSuffixStart(original, recentBudget);
169
+ const minimumSavingsTokens = minimumContextSavingsTokens(residency.estimatedMessageTokens);
170
+ if (suffixStart <= 0) return passthrough({
171
+ reason: selected.reason,
172
+ reasonCodes: [...selected.codes, "no-protocol-safe-stale-prefix"],
173
+ fallback: "no-safe-boundary",
174
+ minimumSavingsTokens
175
+ });
176
+ const prefix = original.slice(0, suffixStart);
177
+ const suffix = original.slice(suffixStart);
178
+ const prefixLedger = buildAdaptiveContextLedger(prefix, options.task);
179
+ if (adaptiveContextCompactionCancelled(prefixLedger)) return passthrough({
180
+ reason: selected.reason,
181
+ reasonCodes: [...selected.codes, "lossless-task-truth-exceeds-ledger"],
182
+ fallback: "no-safe-boundary",
183
+ minimumSavingsTokens
184
+ });
185
+ const projected = [ledgerMessage(prefixLedger, prefix), ...suffix];
186
+ let projectedMessageTokens = estimateGovernorMessagesTokens(projected);
187
+
188
+ // A single recent group can be large. Tighten once while preserving tool-call
189
+ // protocol; never trim individual messages or tool result pairs.
190
+ if (projectedMessageTokens > CONTEXT_GOVERNOR_TARGET_MESSAGE_TOKENS && suffix.length > 4) {
191
+ const tighterStart = safeSuffixStart(original, Math.max(16_000, Math.floor(recentBudget * 0.62)));
192
+ if (tighterStart > suffixStart) {
193
+ const tighterPrefix = original.slice(0, tighterStart);
194
+ const tighter = [ledgerMessage(buildAdaptiveContextLedger(tighterPrefix, options.task), tighterPrefix), ...original.slice(tighterStart)];
195
+ const tighterTokens = estimateGovernorMessagesTokens(tighter);
196
+ if (tighterTokens < projectedMessageTokens) {
197
+ projected.splice(0, projected.length, ...tighter);
198
+ projectedMessageTokens = tighterTokens;
199
+ }
200
+ }
201
+ }
202
+
203
+ const candidateProtocol = auditToolProtocol(projected);
204
+ if (!candidateProtocol.intact) return passthrough({
205
+ reason: selected.reason,
206
+ reasonCodes: [
207
+ ...selected.codes,
208
+ "projected-tool-protocol-unsafe",
209
+ `orphan-calls:${candidateProtocol.orphanCallIds.length}`,
210
+ `orphan-results:${candidateProtocol.orphanResultIds.length}`
211
+ ],
212
+ fallback: "unsafe-tool-protocol",
213
+ candidateProjectedMessageTokens: projectedMessageTokens,
214
+ minimumSavingsTokens
215
+ });
216
+
217
+ const estimatedSavingsTokens = Math.max(0, residency.estimatedMessageTokens - projectedMessageTokens);
218
+ if (estimatedSavingsTokens < minimumSavingsTokens) {
219
+ return passthrough({
220
+ reason: selected.reason,
221
+ reasonCodes: [...selected.codes, "minimum-savings-not-met", "projection-no-op"],
222
+ fallback: "insufficient-savings",
223
+ candidateProjectedMessageTokens: projectedMessageTokens,
224
+ estimatedSavingsTokens,
225
+ minimumSavingsTokens
226
+ });
227
+ }
228
+
229
+ return {
230
+ messages: projected,
231
+ decision: {
232
+ version: GOVERNOR_VERSION,
233
+ action: "project",
234
+ reason: selected.reason,
235
+ reasonCodes: selected.codes,
236
+ reportedTokens,
237
+ effectiveTokens,
238
+ contextWindow,
239
+ percent,
240
+ originalMessageTokens: residency.estimatedMessageTokens,
241
+ projectedMessageTokens,
242
+ candidateProjectedMessageTokens: projectedMessageTokens,
243
+ estimatedSavingsTokens,
244
+ minimumSavingsTokens,
245
+ savingsRatio: residency.estimatedMessageTokens > 0
246
+ ? estimatedSavingsTokens / residency.estimatedMessageTokens
247
+ : 0,
248
+ fallback: "none",
249
+ originalMessages: original.length,
250
+ projectedMessages: projected.length,
251
+ accounting: accounting(projectedMessageTokens),
252
+ boundary,
253
+ residency
254
+ }
255
+ };
256
+ }
257
+
258
+ function sessionKey(ctx: ExtensionContext): string {
259
+ return `${ctx.cwd}\u0000${ctx.sessionManager.getSessionId()}`;
260
+ }
261
+
262
+ function stateFor(states: Map<string, GovernorSessionState>, ctx: ExtensionContext): GovernorSessionState {
263
+ const key = sessionKey(ctx);
264
+ let state = states.get(key);
265
+ if (!state) {
266
+ state = {};
267
+ states.set(key, state);
268
+ }
269
+ while (states.size > 100) states.delete(states.keys().next().value as string);
270
+ return state;
271
+ }
272
+
273
+ export function registerAdaptiveContextGovernor(pi: ExtensionAPI, dependencies: GovernorDependencies): void {
274
+ const states = new Map<string, GovernorSessionState>();
275
+
276
+ pi.on("context", (event, ctx) => {
277
+ const state = stateFor(states, ctx);
278
+ const usage = ctx.getContextUsage();
279
+ const projection = projectAdaptiveContext(event.messages as MessageLike[], {
280
+ reportedTokens: usage?.tokens,
281
+ contextWindow: usage?.contextWindow ?? ctx.model?.contextWindow,
282
+ task: dependencies.activeTask(ctx)
283
+ });
284
+ state.lastProjection = projection.decision;
285
+ if (projection.decision.action === "project") {
286
+ state.lastNoopTelemetryKey = undefined;
287
+ const telemetryKey = [
288
+ projection.decision.reason,
289
+ projection.decision.originalMessages,
290
+ projection.decision.projectedMessages,
291
+ Math.round(projection.decision.effectiveTokens / 1_000)
292
+ ].join(":");
293
+ if (state.lastProjectionTelemetryKey !== telemetryKey) {
294
+ state.lastProjectionTelemetryKey = telemetryKey;
295
+ dependencies.telemetry(ctx, {
296
+ event: "context_governor_projection",
297
+ ...projection.decision
298
+ });
299
+ }
300
+ return { messages: projection.messages as any[] };
301
+ }
302
+ if (projection.decision.fallback !== "none") {
303
+ const telemetryKey = [
304
+ projection.decision.reason,
305
+ projection.decision.fallback,
306
+ Math.floor(projection.decision.originalMessageTokens / Math.max(1, projection.decision.minimumSavingsTokens))
307
+ ].join(":");
308
+ if (state.lastNoopTelemetryKey !== telemetryKey) {
309
+ state.lastNoopTelemetryKey = telemetryKey;
310
+ dependencies.telemetry(ctx, {
311
+ event: "context_governor_projection_noop",
312
+ ...projection.decision
313
+ });
314
+ }
315
+ }
316
+ return undefined;
317
+ });
318
+
319
+ pi.on("session_before_compact", (event, ctx) => {
320
+ const task = dependencies.activeTask(ctx);
321
+ const messages = [
322
+ ...(event.preparation.messagesToSummarize as MessageLike[]),
323
+ ...(event.preparation.turnPrefixMessages as MessageLike[])
324
+ ];
325
+ const residency = analyzeContextResidency(messages);
326
+ if (!adaptiveContextCanPreserveTask(task)) {
327
+ const state = stateFor(states, ctx);
328
+ const fallbackKey = `lossless-task-truth:${event.preparation.firstKeptEntryId}`;
329
+ if (state.lastCompactionFallbackTelemetryKey !== fallbackKey) {
330
+ state.lastCompactionFallbackTelemetryKey = fallbackKey;
331
+ dependencies.telemetry(ctx, {
332
+ event: "context_governor_compaction_cancelled",
333
+ reason: event.reason,
334
+ willRetry: event.willRetry,
335
+ fallback: "no-op-lossless-task-truth-exceeds-ledger",
336
+ taskRunId: task?.taskRunId,
337
+ residency
338
+ });
339
+ }
340
+ return { cancel: true };
341
+ }
342
+ if (String(event.customInstructions ?? "").trim()) {
343
+ const state = stateFor(states, ctx);
344
+ const fallbackKey = `directed-host-summary:${event.preparation.firstKeptEntryId}`;
345
+ if (state.lastCompactionFallbackTelemetryKey !== fallbackKey) {
346
+ state.lastCompactionFallbackTelemetryKey = fallbackKey;
347
+ dependencies.telemetry(ctx, {
348
+ event: "context_governor_deterministic_compaction_skipped",
349
+ reason: event.reason,
350
+ willRetry: event.willRetry,
351
+ fallback: "host-model-directed-summary",
352
+ taskRunId: task?.taskRunId,
353
+ residency
354
+ });
355
+ }
356
+ return undefined;
357
+ }
358
+ // Nuanced discussion without a durable task still benefits from the host's
359
+ // model summary. Coding-task and tool-heavy compactions are safely derived
360
+ // from file-backed truth without spending another reasoning turn.
361
+ if (!task && residency.toolResults < 12) return undefined;
362
+ const protocol = auditToolProtocol(messages);
363
+ if (!protocol.intact) {
364
+ const fallbackKey = `unsafe-tool-boundary:${event.preparation.firstKeptEntryId}`;
365
+ const state = stateFor(states, ctx);
366
+ if (state.lastCompactionFallbackTelemetryKey !== fallbackKey) {
367
+ state.lastCompactionFallbackTelemetryKey = fallbackKey;
368
+ dependencies.telemetry(ctx, {
369
+ event: "context_governor_compaction_cancelled",
370
+ reason: event.reason,
371
+ willRetry: event.willRetry,
372
+ fallback: "no-op-unsafe-tool-boundary",
373
+ taskRunId: task?.taskRunId,
374
+ protocol
375
+ });
376
+ }
377
+ return { cancel: true };
378
+ }
379
+ const compaction = deterministicTaskCompaction(event.preparation as any, task);
380
+ const accounting = compaction.details.accounting as {
381
+ minimumSavingsMet: boolean;
382
+ estimatedSavingsTokens: number;
383
+ minimumSavingsTokens: number;
384
+ };
385
+ const emergencyRecovery = event.reason === "overflow"
386
+ && event.willRetry
387
+ && accounting.estimatedSavingsTokens > 0;
388
+ if (event.reason === "overflow" && event.willRetry && accounting.estimatedSavingsTokens === 0) {
389
+ const state = stateFor(states, ctx);
390
+ const fallbackKey = `overflow-host-recovery:${event.preparation.firstKeptEntryId}`;
391
+ if (state.lastCompactionFallbackTelemetryKey !== fallbackKey) {
392
+ state.lastCompactionFallbackTelemetryKey = fallbackKey;
393
+ dependencies.telemetry(ctx, {
394
+ event: "context_governor_deterministic_compaction_skipped",
395
+ reason: event.reason,
396
+ willRetry: event.willRetry,
397
+ fallback: "host-model-overflow-recovery",
398
+ taskRunId: task?.taskRunId,
399
+ residency,
400
+ accounting
401
+ });
402
+ }
403
+ return undefined;
404
+ }
405
+ if (!accounting.minimumSavingsMet && !emergencyRecovery) {
406
+ const state = stateFor(states, ctx);
407
+ const fallbackKey = [
408
+ "insufficient-savings",
409
+ event.preparation.firstKeptEntryId,
410
+ accounting.estimatedSavingsTokens,
411
+ accounting.minimumSavingsTokens
412
+ ].join(":");
413
+ if (state.lastCompactionFallbackTelemetryKey !== fallbackKey) {
414
+ state.lastCompactionFallbackTelemetryKey = fallbackKey;
415
+ dependencies.telemetry(ctx, {
416
+ event: "context_governor_compaction_cancelled",
417
+ reason: event.reason,
418
+ willRetry: event.willRetry,
419
+ fallback: "no-op-insufficient-savings",
420
+ taskRunId: task?.taskRunId,
421
+ residency,
422
+ accounting
423
+ });
424
+ }
425
+ return { cancel: true };
426
+ }
427
+ stateFor(states, ctx).lastCompactionFallbackTelemetryKey = undefined;
428
+ dependencies.telemetry(ctx, {
429
+ event: "context_governor_deterministic_compaction",
430
+ reason: event.reason,
431
+ willRetry: event.willRetry,
432
+ tokensBefore: compaction.tokensBefore,
433
+ taskRunId: task?.taskRunId,
434
+ residency,
435
+ accounting,
436
+ minimumSavingsOverride: emergencyRecovery ? "overflow-recovery" : undefined
437
+ });
438
+ return { compaction: compaction as any };
439
+ });
440
+
441
+ pi.on("agent_settled", (_event, ctx) => {
442
+ const state = stateFor(states, ctx);
443
+ // `agent_settled` is the operation's terminal boundary. Starting a host
444
+ // compaction here can make the just-finished operation look active again
445
+ // and race the next user message. Provider calls are already protected by
446
+ // the wire-only `context` projection above; durable compaction remains
447
+ // deterministic when the host requests it through session_before_compact.
448
+ state.lastProjection = undefined;
449
+ state.lastProjectionTelemetryKey = undefined;
450
+ state.lastNoopTelemetryKey = undefined;
451
+ });
452
+
453
+ pi.on("session_compact", (_event, ctx) => {
454
+ const state = stateFor(states, ctx);
455
+ state.lastProjection = undefined;
456
+ state.lastProjectionTelemetryKey = undefined;
457
+ state.lastNoopTelemetryKey = undefined;
458
+ state.lastCompactionFallbackTelemetryKey = undefined;
459
+ });
460
+
461
+ pi.on("session_shutdown", (_event, ctx) => {
462
+ states.delete(sessionKey(ctx));
463
+ });
464
+ }