@piagent/platform 1.5.4 → 1.6.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.
Files changed (230) hide show
  1. package/CHANGELOG.md +136 -0
  2. package/README.md +9 -8
  3. package/README.vi.md +9 -5
  4. package/adapters/web-frontend/profile.json +2 -4
  5. package/architecture/layers.json +18 -3
  6. package/benchmarks/deep-logic-v1/SPECIALIST_PROTOCOL.vi.md +122 -0
  7. package/benchmarks/deep-logic-v1/grade.mjs +272 -10
  8. package/benchmarks/deep-logic-v1/project/apps/admin/src/billing-summary.js +3 -0
  9. package/benchmarks/deep-logic-v1/project/benchmark-contract/public-smoke.test.js +166 -0
  10. package/benchmarks/deep-logic-v1/project/package.json +1 -1
  11. package/benchmarks/deep-logic-v1/project/packages/billing/src/close-period.js +3 -0
  12. package/benchmarks/deep-logic-v1/project/packages/billing/src/plan-timeline.js +3 -0
  13. package/benchmarks/deep-logic-v1/project/test/smoke.test.js +7 -1
  14. package/benchmarks/deep-logic-v1/prompts/budgeted-context-graph.md +2 -0
  15. package/benchmarks/deep-logic-v1/prompts/fair-dependency-scheduler.md +2 -0
  16. package/benchmarks/deep-logic-v1/prompts/layered-policy-resolution.md +2 -0
  17. package/benchmarks/deep-logic-v1/prompts/resumable-stream-assembly.md +3 -1
  18. package/benchmarks/deep-logic-v1/prompts/revision-event-reconciliation.md +2 -0
  19. package/benchmarks/deep-logic-v1/prompts/temporal-usage-billing-close.md +123 -0
  20. package/benchmarks/deep-logic-v1/prompts/transactional-config-merge.md +2 -0
  21. package/benchmarks/deep-logic-v1/suite.json +51 -3
  22. package/benchmarks/deep-logic-v1/variant.mjs +22 -1
  23. package/benchmarks/production-v1/spend-control.v1.json +68 -0
  24. package/benchmarks/production-v1/suite.json +42 -3
  25. package/catalog/capabilities.json +6 -6
  26. package/docs/command-reference-vietnamese.md +6 -6
  27. package/docs/context-engine.md +7 -1
  28. package/docs/distribution-standard.md +7 -7
  29. package/docs/en/architecture.md +1 -0
  30. package/docs/languages.json +4 -4
  31. package/docs/model-options.md +8 -5
  32. package/docs/operator-manual-vietnamese.md +13 -11
  33. package/docs/project-adapters.md +2 -2
  34. package/docs/publishing-for-teams.md +9 -0
  35. package/docs/quality-benchmark.md +271 -62
  36. package/docs/release-install-policy.md +10 -9
  37. package/docs/runtime-policy-design.md +4 -4
  38. package/docs/runtime-quality-baseline.md +13 -6
  39. package/docs/security-threat-model.md +2 -2
  40. package/docs/task-lifecycle-vietnamese.md +2 -2
  41. package/docs/team-onboarding.md +2 -2
  42. package/docs/usage-observability.md +7 -2
  43. package/docs/vi/architecture.md +1 -0
  44. package/docs-site/architecture.html +1 -1
  45. package/docs-site/benchmark.html +1 -1
  46. package/docs-site/commands.html +1 -1
  47. package/docs-site/context-engine.html +1 -1
  48. package/docs-site/en/architecture.html +1 -1
  49. package/docs-site/en/benchmark.html +1 -1
  50. package/docs-site/en/commands.html +1 -1
  51. package/docs-site/en/context-engine.html +1 -1
  52. package/docs-site/en/index.html +1 -1
  53. package/docs-site/en/mcp-approval.html +1 -1
  54. package/docs-site/en/mcp-auth.html +1 -1
  55. package/docs-site/en/mcp-servers.html +1 -1
  56. package/docs-site/en/mcp.html +1 -1
  57. package/docs-site/en/permissions.html +1 -1
  58. package/docs-site/en/profiles.html +1 -1
  59. package/docs-site/en/quickstart.html +1 -1
  60. package/docs-site/en/runtime.html +1 -1
  61. package/docs-site/en/scope.html +1 -1
  62. package/docs-site/en/security.html +1 -1
  63. package/docs-site/en/team.html +1 -1
  64. package/docs-site/en/whats-new.html +1 -1
  65. package/docs-site/en/workflows.html +1 -1
  66. package/docs-site/index.html +1 -1
  67. package/docs-site/mcp-approval.html +1 -1
  68. package/docs-site/mcp-auth.html +1 -1
  69. package/docs-site/mcp-servers.html +1 -1
  70. package/docs-site/mcp.html +1 -1
  71. package/docs-site/permissions.html +1 -1
  72. package/docs-site/profiles.html +1 -1
  73. package/docs-site/quickstart.html +1 -1
  74. package/docs-site/runtime.html +1 -1
  75. package/docs-site/scope.html +1 -1
  76. package/docs-site/security.html +1 -1
  77. package/docs-site/team.html +1 -1
  78. package/docs-site/whats-new.html +1 -1
  79. package/docs-site/workflows.html +1 -1
  80. package/evals/fixtures/benchmark-assurance-evidence.invalid.json +1 -1
  81. package/evals/fixtures/benchmark-assurance-evidence.valid.json +1 -1
  82. package/evals/fixtures/piagent-webui/session-live-state-v1.invalid.json +18 -0
  83. package/evals/fixtures/piagent-webui/session-live-state-v1.valid.json +27 -0
  84. package/evals/fs4-readiness-matrix.v1.json +2 -2
  85. package/evals/fs5-causal-arm.v1.json +2 -2
  86. package/evals/fs5-pilot-protocol.v1.json +2 -2
  87. package/evals/fs5-pilot-protocol.v4.json +6 -5
  88. package/evals/fs5-pilot-protocol.v5.json +7 -6
  89. package/evals/private-holdout-v1/public-exposure.v1.json +1 -1
  90. package/package.json +3 -1
  91. package/packages/piagent-core/benchmark/benchmark-assurance.js +4 -1
  92. package/packages/piagent-core/benchmark/benchmark-bootstrap.js +91 -9
  93. package/packages/piagent-core/benchmark/benchmark-claim-restrictions.js +38 -7
  94. package/packages/piagent-core/benchmark/benchmark-cli.js +7 -7
  95. package/packages/piagent-core/benchmark/benchmark-codex.js +3 -1
  96. package/packages/piagent-core/benchmark/benchmark-comparison.js +271 -2
  97. package/packages/piagent-core/benchmark/benchmark-core.js +732 -661
  98. package/packages/piagent-core/benchmark/benchmark-execution-guard.js +7 -5
  99. package/packages/piagent-core/benchmark/benchmark-forensics.js +439 -4
  100. package/packages/piagent-core/benchmark/benchmark-host-readiness.js +639 -0
  101. package/packages/piagent-core/benchmark/benchmark-matrix.js +22 -4
  102. package/packages/piagent-core/benchmark/benchmark-normalized-cost.js +317 -0
  103. package/packages/piagent-core/benchmark/benchmark-preflight.js +3 -1
  104. package/packages/piagent-core/benchmark/benchmark-process.js +6 -5
  105. package/packages/piagent-core/benchmark/benchmark-provider-wire.js +126 -0
  106. package/packages/piagent-core/benchmark/benchmark-record-validation.js +183 -0
  107. package/packages/piagent-core/benchmark/benchmark-report.js +116 -12
  108. package/packages/piagent-core/benchmark/benchmark-resume-recovery.js +14 -0
  109. package/packages/piagent-core/benchmark/benchmark-runner-policy.js +26 -0
  110. package/packages/piagent-core/benchmark/benchmark-stage-diagnostic.js +782 -0
  111. package/packages/piagent-core/benchmark/benchmark-statistics.js +64 -0
  112. package/packages/piagent-core/benchmark/benchmark-stop-policy.js +8 -5
  113. package/packages/piagent-core/benchmark/benchmark-suite-identity.js +31 -0
  114. package/packages/piagent-core/benchmark/benchmark-suite-runtime.js +13 -9
  115. package/packages/piagent-core/benchmark/benchmark-suite.js +128 -2
  116. package/packages/piagent-core/benchmark/benchmark-summary-support.js +376 -0
  117. package/packages/piagent-core/benchmark/benchmark-timing-diagnostics.js +971 -0
  118. package/packages/piagent-core/benchmark/benchmark-usage.js +311 -0
  119. package/packages/piagent-core/benchmark/benchmark-workflow.js +166 -0
  120. package/packages/piagent-core/docs/context-efficiency-improvement-plan.md +1080 -0
  121. package/packages/piagent-core/extensions/acceptance-contract-semantics.js +83 -69
  122. package/packages/piagent-core/extensions/acceptance-error-classes.js +160 -0
  123. package/packages/piagent-core/extensions/acceptance-executable-evidence.js +12 -9
  124. package/packages/piagent-core/extensions/acceptance-receipt.js +34 -11
  125. package/packages/piagent-core/extensions/acceptance-state-machine-evidence.js +518 -0
  126. package/packages/piagent-core/extensions/acceptance-test-binding-evidence.js +60 -0
  127. package/packages/piagent-core/extensions/context-efficiency-metrics.js +302 -0
  128. package/packages/piagent-core/extensions/context-efficiency-report.js +167 -0
  129. package/packages/piagent-core/extensions/context-engine.js +148 -145
  130. package/packages/piagent-core/extensions/context-evidence.js +48 -0
  131. package/packages/piagent-core/extensions/context-import-links.js +296 -0
  132. package/packages/piagent-core/extensions/context-telemetry-integrity.js +65 -0
  133. package/packages/piagent-core/extensions/criterion-context-pack.js +422 -7
  134. package/packages/piagent-core/extensions/criterion-graph.js +78 -8
  135. package/packages/piagent-core/extensions/guard-types.ts +3 -0
  136. package/packages/piagent-core/extensions/javascript-regex-evidence.js +32 -0
  137. package/packages/piagent-core/extensions/piagent-guard.ts +250 -43
  138. package/packages/piagent-core/extensions/policy-core.js +1 -1
  139. package/packages/piagent-core/extensions/task-contract-view.js +17 -3
  140. package/packages/piagent-core/extensions/task-lifecycle.js +7 -2
  141. package/packages/piagent-core/extensions/task-state.js +40 -134
  142. package/packages/piagent-core/extensions/verification-intelligence.js +3 -1
  143. package/packages/piagent-core/extensions/workspace-evidence-roots.js +157 -0
  144. package/packages/piagent-core/package.json +1 -1
  145. package/packages/piagent-core/policies/base-policy.json +3 -0
  146. package/packages/piagent-core/runtime/context/context-delivery.ts +103 -0
  147. package/packages/piagent-core/runtime/context/context-delta-shadow.ts +53 -0
  148. package/packages/piagent-core/runtime/context/context-efficiency-format.ts +47 -0
  149. package/packages/piagent-core/runtime/context/context-evidence-qualification.ts +32 -0
  150. package/packages/piagent-core/runtime/context/prefix-telemetry.ts +47 -0
  151. package/packages/piagent-core/runtime/context/repository-memory-hints.ts +27 -0
  152. package/packages/piagent-core/runtime/hooks/agent-prompt-telemetry.ts +56 -0
  153. package/packages/piagent-core/runtime/hooks/agent-start-hook.ts +162 -102
  154. package/packages/piagent-core/runtime/hooks/completion-hook.ts +5 -6
  155. package/packages/piagent-core/runtime/hooks/input-hook.ts +11 -1
  156. package/packages/piagent-core/runtime/hooks/session-hooks.ts +22 -2
  157. package/packages/piagent-core/runtime/hooks/session-start-hook.ts +7 -2
  158. package/packages/piagent-core/runtime/hooks/tool-result-content-guards.ts +95 -0
  159. package/packages/piagent-core/runtime/hooks/tool-result-hook.ts +84 -102
  160. package/packages/piagent-core/runtime/inspection/activity-event-adapter.ts +9 -3
  161. package/packages/piagent-core/runtime/inspection/activity-recovery.ts +104 -0
  162. package/packages/piagent-core/runtime/inspection/approval-broker.ts +29 -5
  163. package/packages/piagent-core/runtime/inspection/task-compaction-history.ts +1 -1
  164. package/packages/piagent-core/runtime/inspection/task-handoff-history.ts +1 -1
  165. package/packages/piagent-core/runtime/inspection/tool-failure-classification.ts +53 -0
  166. package/packages/piagent-core/runtime/inspection/webui-snapshot.ts +34 -6
  167. package/packages/piagent-core/runtime/model/authenticated-catalog.ts +17 -9
  168. package/packages/piagent-core/runtime/model/openai-codex-reasoning.ts +83 -0
  169. package/packages/piagent-core/runtime/model/provider-wire-fingerprint.ts +238 -0
  170. package/packages/piagent-core/runtime/orchestration/role-policy.ts +1 -1
  171. package/packages/piagent-core/runtime/product/activity-inspector.ts +7 -2
  172. package/packages/piagent-core/runtime/product/efficiency-metrics.ts +4 -2
  173. package/packages/piagent-core/runtime/quality/exact-output-contract.ts +2 -1
  174. package/packages/piagent-core/runtime/quality/performance-assurance-signals.ts +23 -0
  175. package/packages/piagent-core/runtime/quality/performance-assurance.ts +6 -2
  176. package/packages/piagent-core/runtime/recovery/edit-recovery-context.ts +113 -0
  177. package/packages/piagent-core/runtime/recovery/edit-recovery-delivery.ts +42 -0
  178. package/packages/piagent-core/runtime/recovery/semantic-repair-handshake.ts +55 -2
  179. package/packages/piagent-core/runtime/recovery/semantic-repair-runtime.ts +55 -2
  180. package/packages/piagent-core/runtime/registration/context-commands.ts +66 -5
  181. package/packages/piagent-core/runtime/registration/policy-tools.ts +46 -11
  182. package/packages/piagent-core/runtime/registration/task-completion-tools.ts +49 -16
  183. package/packages/piagent-core/runtime/registration/task-start-guidance.ts +163 -0
  184. package/packages/piagent-core/runtime/registration/task-start-retry-helpers.ts +37 -0
  185. package/packages/piagent-core/runtime/registration/task-start-tool.ts +67 -62
  186. package/packages/piagent-core/runtime/session/runtime-state.ts +154 -2
  187. package/packages/piagent-core/runtime/session/system-prompt.ts +4 -4
  188. package/packages/piagent-core/runtime/tools/apply-patch-tool.ts +500 -0
  189. package/packages/piagent-core/runtime/trajectory/trajectory-runtime.ts +2 -1
  190. package/packages/piagent-core/runtime/workflows/task-intake.ts +43 -2
  191. package/packages/piagent-core/security/sensitive-data.js +13 -218
  192. package/packages/piagent-core/security/sensitive-project-file.js +42 -0
  193. package/packages/piagent-core/security/sensitive-source-expression.js +330 -0
  194. package/packages/piagent-core/security/sensitive-source-formats.js +394 -0
  195. package/packages/piagent-core/security/sensitive-text.js +234 -0
  196. package/packages/piagent-core/subagents/piagent-worker.md +1 -1
  197. package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-rD9WJKxs.js → MarkdownMessage-CxeZqTte.js} +1 -1
  198. package/packages/piagent-webui/dist/client/assets/index-DynIMe8I.js +176 -0
  199. package/packages/piagent-webui/dist/client/index.html +1 -1
  200. package/packages/piagent-webui/gateway/gateway-events.ts +109 -5
  201. package/packages/piagent-webui/gateway/gateway-service.ts +3 -0
  202. package/packages/piagent-webui/gateway/gateway-session-stream.ts +93 -7
  203. package/packages/piagent-webui/gateway/session-command-controller.ts +18 -3
  204. package/packages/piagent-webui/gateway/session-effective-options.ts +47 -0
  205. package/packages/piagent-webui/gateway/session-live-state.ts +21 -0
  206. package/packages/piagent-webui/gateway/session-operation-start.ts +5 -1
  207. package/packages/piagent-webui/gateway/session-operation-watchdog.ts +158 -0
  208. package/packages/piagent-webui/gateway/session-runtime-factory.ts +45 -0
  209. package/packages/piagent-webui/gateway/session-runtime-supervisor.ts +139 -120
  210. package/packages/piagent-webui/server/loopback-server.ts +8 -0
  211. package/packages/piagent-webui/server/transcript-projection.ts +22 -4
  212. package/packages/piagent-webui/shared/text-visibility.ts +10 -0
  213. package/schemas/piagent-webui/catalog-v1.json +9 -0
  214. package/schemas/piagent-webui/gateway-protocol-v1.schema.json +53 -3
  215. package/schemas/piagent-webui/session-live-state-v1.schema.json +76 -0
  216. package/schemas/task-contract.schema.json +12 -1
  217. package/scripts/benchmark-runner-core.mjs +355 -359
  218. package/scripts/benchmark-runner-finalization.mjs +309 -0
  219. package/scripts/benchmark-runner-host-readiness.mjs +94 -0
  220. package/scripts/benchmark-runner-support.mjs +200 -0
  221. package/scripts/benchmark-session.mjs +95 -41
  222. package/scripts/configure-model-scope.sh +4 -3
  223. package/scripts/helper-evaluation.mjs +1 -1
  224. package/scripts/install-global.sh +2 -2
  225. package/scripts/pi-usage-history.mjs +37 -10
  226. package/scripts/piagent-dashboard.mjs +13 -1
  227. package/scripts/setup.sh +2 -2
  228. package/scripts/verify-local.sh +5 -1
  229. package/templates/global/settings.json +2 -1
  230. package/packages/piagent-webui/dist/client/assets/index-BZRFw1u6.js +0 -174
@@ -7,7 +7,10 @@ import {
7
7
  ensureContextIndexV2,
8
8
  estimateContextTokens
9
9
  } from "../../extensions/context-engine.js";
10
- import { buildSelectedContextPack } from "../../extensions/criterion-context-pack.js";
10
+ import {
11
+ buildSelectedContextPack,
12
+ composeCriterionContextEntries
13
+ } from "../../extensions/criterion-context-pack.js";
11
14
  import { matchesProtectedPath } from "../../extensions/policy-core.js";
12
15
  import { selectRepositoryMemoryFacts } from "../../extensions/repository-memory.js";
13
16
  import type { TaskContract } from "../../extensions/guard-types.js";
@@ -15,9 +18,12 @@ import {
15
18
  contextPlanAcceptsConfidence,
16
19
  planAdaptiveContext
17
20
  } from "../context/adaptive-planner.ts";
21
+ import { stageContextDelivery } from "../context/context-delivery.ts";
22
+ import { measureContextDeltaShadow, type ContextDeltaShadowMode } from "../context/context-delta-shadow.ts";
23
+ import { buildPrefixTelemetry } from "../context/prefix-telemetry.ts";
24
+ import { formatRepositoryMemoryHints } from "../context/repository-memory-hints.ts";
18
25
  import { modelCapabilityFromContext } from "../model/capabilities.ts";
19
26
  import type { RuntimeModelSnapshot } from "../model/runtime-snapshot.ts";
20
- import { runtimeModelSnapshotDigest } from "../model/runtime-snapshot.ts";
21
27
  import type { ModelRouteEvaluation } from "../model/model-route-runtime.ts";
22
28
  import { buildTaskResumeContext } from "../recovery/resume-state.ts";
23
29
  import { planRetrievalRoute } from "../context/retrieval-route-policy.ts";
@@ -25,7 +31,7 @@ import type { SolverShadowEvaluation } from "../solver/solver-shadow.ts";
25
31
  import { observeTrajectorySync } from "../trajectory/trajectory-observability.ts";
26
32
  import type { TrajectorySyncOptions, TrajectorySyncResult } from "../trajectory/trajectory-runtime.ts";
27
33
  import { trajectoryRecommendationRef } from "../trajectory/trajectory-runtime.ts";
28
- import { RuntimeSessionState } from "../session/runtime-state.ts";
34
+ import { RuntimeSessionState, type ContextInjectionItem } from "../session/runtime-state.ts";
29
35
  import {
30
36
  compactManagedProjectInstructions,
31
37
  rewriteLegacyProjectInstructions
@@ -37,16 +43,20 @@ import {
37
43
  automaticTaskIntakeMode
38
44
  } from "../workflows/task-intake.ts";
39
45
  import { CONTEXT_PACK_MAX_TOKENS } from "../runtime-limits.ts";
46
+ import { modelRouteTelemetry, runtimeSnapshotTelemetry, solverShadowTelemetry } from "./agent-prompt-telemetry.ts";
40
47
 
41
48
  type RuntimeIntakeResult = {
42
49
  started: boolean;
43
50
  text: string;
44
51
  task?: TaskContract;
52
+ plannedContext?: Array<{ path: string; reason: string }>;
53
+ plannedContextComplete?: boolean;
45
54
  };
46
55
 
47
56
  type AgentStartHookDependencies = {
48
57
  state: RuntimeSessionState;
49
58
  autoContextEnabled: boolean;
59
+ contextDeltaShadowMode: ContextDeltaShadowMode;
50
60
  activeTask: (ctx: ExtensionContext) => TaskContract | undefined;
51
61
  readProtectedPaths: (ctx: ExtensionContext) => string[];
52
62
  contextExcludePatterns: (ctx: ExtensionContext) => string[];
@@ -71,36 +81,18 @@ type AgentStartHookDependencies = {
71
81
  telemetry: (ctx: ExtensionContext, payload: Record<string, unknown>) => void;
72
82
  };
73
83
 
74
- function formatRepositoryMemoryHints(candidates: Array<{
75
- record: { id?: string; fact: string; citations: Array<{ path: string }> };
76
- matchedTerms: string[];
77
- }>, budgetTokens: number): { text: string; ids: string[] } {
78
- if (budgetTokens < 80 || candidates.length === 0) return { text: "", ids: [] };
79
- const lines = [
80
- "[Piagent repository memory: advisory only]",
81
- "Verify every hint against the cited current file before relying on it."
82
- ];
83
- const ids: string[] = [];
84
- for (const candidate of candidates) {
85
- const paths = candidate.record.citations.slice(0, 4).map((citation) => citation.path).join(", ");
86
- const line = `- ${candidate.record.fact.slice(0, 320)} [sources: ${paths}]`;
87
- const proposed = [...lines, line].join("\n");
88
- if (estimateContextTokens(proposed) > budgetTokens) break;
89
- lines.push(line);
90
- if (candidate.record.id) ids.push(candidate.record.id);
91
- }
92
- return ids.length > 0 ? { text: lines.join("\n"), ids } : { text: "", ids: [] };
93
- }
94
-
95
84
  export function registerAgentStartHook(pi: ExtensionAPI, dependencies: AgentStartHookDependencies): void {
96
85
  pi.on("before_agent_start", async (event, ctx) => {
97
86
  const projectInstructions = rewriteLegacyProjectInstructions(event.systemPrompt);
98
87
  const query = looksLikeGovernedBoilerplate(event.prompt) ? extractTaskRequest(event.prompt) : event.prompt.trim();
99
88
  const signal = classifyContextTask(query);
89
+ const turn = dependencies.state.currentTurn(ctx, signal.promptHash) ?? dependencies.state.beginTurn(ctx, signal.promptHash);
100
90
  const readProtectedPaths = dependencies.readProtectedPaths(ctx);
91
+ const protectedTarget = signal.paths.some((candidate) => matchesProtectedPath(candidate, readProtectedPaths));
101
92
  const protectedOnlyTarget = signal.paths.length > 0
102
93
  && signal.paths.every((candidate) => matchesProtectedPath(candidate, readProtectedPaths));
103
- const activeTask = dependencies.activeTask(ctx);
94
+ const sessionTask = dependencies.activeTask(ctx);
95
+ const activeTask = sessionTask?.trace.outcome === "pending" ? sessionTask : undefined;
104
96
  const runtimeIntakeMode = !activeTask ? automaticTaskIntakeMode(query, readProtectedPaths) : undefined;
105
97
  const runtimeIntake = Boolean(runtimeIntakeMode);
106
98
  const compactMode = protectedOnlyTarget
@@ -122,10 +114,10 @@ export function registerAgentStartHook(pi: ExtensionAPI, dependencies: AgentStar
122
114
  .map((tool) => ({
123
115
  name: tool.name,
124
116
  description: tool.description,
125
- parameters: tool.parameters,
126
- promptGuidelines: tool.promptGuidelines
117
+ parameters: tool.parameters
127
118
  }));
128
- const toolSchemaTokens = estimateContextTokens(JSON.stringify(toolMetadata));
119
+ const prefix = buildPrefixTelemetry(effectiveSystemPrompt, toolMetadata);
120
+ const toolSchemaTokens = estimateContextTokens(prefix.canonicalToolSurface);
129
121
  const systemPromptTokens = estimateContextTokens(effectiveSystemPrompt);
130
122
  const autoPackUseful = activeTask?.trace.outcome !== "pending"
131
123
  && (runtimeIntake || signal.paths.length === 0);
@@ -154,6 +146,7 @@ export function registerAgentStartHook(pi: ExtensionAPI, dependencies: AgentStar
154
146
  if (activeTask) observeTrajectorySync(ctx, dependencies.syncTrajectory?.(ctx, activeTask, { sourceHook: "agent-start", recommendationRef }), dependencies.telemetry);
155
147
  dependencies.telemetry(ctx, {
156
148
  event: "agent_prompt",
149
+ turnId: turn.turnId,
157
150
  promptHash: signal.promptHash,
158
151
  promptChars: signal.promptChars,
159
152
  workflow: signal.workflow,
@@ -162,56 +155,16 @@ export function registerAgentStartHook(pi: ExtensionAPI, dependencies: AgentStar
162
155
  activePiagentTools: [...active].filter((toolName) => PIAGENT_TOOL_NAMES.has(toolName)).length,
163
156
  toolSchemaTokens,
164
157
  systemPromptTokens,
165
- systemPromptHash: crypto.createHash("sha256").update(effectiveSystemPrompt).digest("hex"),
158
+ systemPromptHash: prefix.systemPromptHash,
159
+ toolSchemaHash: prefix.toolSchemaHash,
160
+ prefixSurfaceHash: prefix.prefixSurfaceHash,
161
+ taskRunId: activeTask?.taskRunId,
166
162
  legacyProjectInstructionsRewritten: projectInstructions.rewritten,
167
163
  managedInstructionsCompacted: compactedInstructions.compacted,
168
164
  contextUsage: ctx.getContextUsage(),
169
- runtimeSnapshot: runtimeSnapshot
170
- ? {
171
- schemaVersion: runtimeSnapshot.schemaVersion,
172
- digest: runtimeModelSnapshotDigest(runtimeSnapshot),
173
- provider: runtimeSnapshot.provider,
174
- modelId: runtimeSnapshot.modelId,
175
- contextWindow: runtimeSnapshot.contextWindow,
176
- requestedThinkingLevel: runtimeSnapshot.requestedThinkingLevel,
177
- effectiveThinkingLevel: runtimeSnapshot.effectiveThinkingLevel,
178
- warningCount: runtimeSnapshot.warnings.length
179
- }
180
- : undefined,
181
- solverShadow: solverShadow?.status === "ok"
182
- ? {
183
- mode: solverShadow.decision.mode,
184
- route: solverShadow.decision.route,
185
- featureHash: solverShadow.features.featureHash,
186
- reasonCodes: solverShadow.decision.reasonCodes,
187
- confidence: solverShadow.decision.confidence,
188
- reused: solverShadow.reused,
189
- persisted: solverShadow.persisted,
190
- durationMs: solverShadow.durationMs,
191
- warnings: solverShadow.warnings
192
- }
193
- : solverShadow,
194
- modelRoute: modelRoute?.status === "ok"
195
- ? {
196
- mode: modelRoute.decision.mode,
197
- objective: modelRoute.decision.objective,
198
- capabilityBand: modelRoute.decision.capabilityBand,
199
- safetyFloor: modelRoute.decision.safetyFloor,
200
- disposition: modelRoute.decision.disposition,
201
- selectionSource: modelRoute.decision.selectionSource,
202
- provider: modelRoute.decision.provider,
203
- modelId: modelRoute.decision.modelId,
204
- effort: modelRoute.decision.effort,
205
- downgradeSteps: modelRoute.decision.downgradeSteps,
206
- enforced: modelRoute.decision.enforced,
207
- decisionDigest: modelRoute.decision.decisionDigest,
208
- reasonCodes: modelRoute.decision.reasonCodes,
209
- reused: modelRoute.reused,
210
- persisted: modelRoute.persisted,
211
- durationMs: modelRoute.durationMs,
212
- warnings: modelRoute.warnings
213
- }
214
- : modelRoute
165
+ runtimeSnapshot: runtimeSnapshotTelemetry(runtimeSnapshot),
166
+ solverShadow: solverShadowTelemetry(solverShadow),
167
+ modelRoute: modelRouteTelemetry(modelRoute)
215
168
  });
216
169
 
217
170
  const finishAgentStart = async (contextMessage?: {
@@ -225,36 +178,133 @@ export function registerAgentStartHook(pi: ExtensionAPI, dependencies: AgentStar
225
178
  const intake = await dependencies.startAutomaticTask(query, ctx);
226
179
  if (intake?.task) observeTrajectorySync(ctx, dependencies.syncTrajectory?.(ctx, intake.task, { sourceHook: "agent-start", recommendationRef }), dependencies.telemetry);
227
180
  if (!selectedContext && !intake) return systemPromptUpdate;
228
- const criterionContext = !selectedContext && intake?.task?.criterionGraph?.mode === "criterion-graph"
229
- ? buildSelectedContextPack(ctx.cwd, intake.task.contextManifest.filter((entry) => /^criterion-/.test(entry.reason)), {
230
- budgetTokens: CONTEXT_PACK_MAX_TOKENS, limit: 6, excludePatterns: readProtectedPaths
181
+ const discoveryItems = selectedContext?.customType === "piagent-context-pack-v2" && Array.isArray(selectedContext.details.selectedItems)
182
+ ? selectedContext.details.selectedItems as Array<Record<string, unknown>>
183
+ : [];
184
+ const discoveryPlan = selectedContext?.customType === "piagent-context-pack-v2"
185
+ && selectedContext.details.contextPlan && typeof selectedContext.details.contextPlan === "object"
186
+ ? selectedContext.details.contextPlan as { budgetTokens?: unknown; limit?: unknown }
187
+ : undefined;
188
+ const fallbackBudget = signal.paths.length === 0 ? 680 : signal.lane === "tiny" ? 420 : 560;
189
+ const criterionBudget = Number.isFinite(Number(discoveryPlan?.budgetTokens))
190
+ ? Math.max(100, Math.min(CONTEXT_PACK_MAX_TOKENS, Math.trunc(Number(discoveryPlan?.budgetTokens))))
191
+ : fallbackBudget;
192
+ const criterionLimit = Number.isFinite(Number(discoveryPlan?.limit))
193
+ ? Math.max(1, Math.min(6, Math.trunc(Number(discoveryPlan?.limit))))
194
+ : signal.paths.length > 0 ? 3 : 4;
195
+ const criterionMode = dependencies.autoContextEnabled && intake?.task?.criterionGraph?.mode === "criterion-graph";
196
+ const criterionEntries = intake?.task?.criterionGraph?.mode === "criterion-graph"
197
+ ? composeCriterionContextEntries({
198
+ explicitPaths: signal.paths,
199
+ criteria: intake.task.acceptanceCriteria,
200
+ plannedEntries: intake.plannedContext ?? [],
201
+ plannedSelectionComplete: intake.plannedContextComplete === true,
202
+ retrievedItems: discoveryItems
203
+ }, { limit: criterionLimit })
204
+ : [];
205
+ const criterionContext = criterionMode
206
+ ? buildSelectedContextPack(ctx.cwd, criterionEntries, {
207
+ budgetTokens: criterionBudget,
208
+ limit: criterionLimit,
209
+ focusText: [query, ...intake.task.acceptanceCriteria].join("\n"),
210
+ excludePatterns: dependencies.contextExcludePatterns(ctx)
231
211
  })
232
212
  : undefined;
233
- if (criterionContext?.selected.length) dependencies.telemetry(ctx, {
234
- event: "criterion_context_pack", selected: criterionContext.selected.length,
235
- estimatedTokens: criterionContext.estimatedTokens, selectedPaths: criterionContext.selected.map((entry) => entry.path)
213
+ const criterionReasonCode = !dependencies.autoContextEnabled
214
+ ? "auto-context-disabled"
215
+ : intake?.task?.criterionGraph?.mode !== "criterion-graph"
216
+ ? "criterion-graph-unavailable"
217
+ : criterionEntries.length === 0
218
+ ? "no-candidates"
219
+ : !criterionContext?.selected.length ? "no-readable-selection" : "selected";
220
+ if (intake?.task) dependencies.telemetry(ctx, {
221
+ event: "criterion_context_pack", turnId: turn.turnId, selected: criterionContext?.selected.length ?? 0,
222
+ candidates: criterionEntries.length,
223
+ budgetTokens: criterionBudget,
224
+ estimatedTokens: criterionContext?.estimatedTokens ?? 0,
225
+ reasonCode: criterionReasonCode,
226
+ selectedPaths: criterionContext?.selected.map((entry) => entry.path) ?? []
236
227
  });
237
- const content = [selectedContext?.content, intake?.text, criterionContext?.text].filter(Boolean).join("\n\n");
228
+ const composedContext = criterionContext?.selected.length ? criterionContext : undefined;
229
+ const deliveredSelectedContext = criterionMode ? undefined : selectedContext;
230
+ const content = [deliveredSelectedContext?.content, intake?.text, composedContext?.text].filter(Boolean).join("\n\n");
231
+ const deliveryTask = intake?.task ?? activeTask;
232
+ const selectedPackPaths = deliveredSelectedContext?.customType === "piagent-context-pack-v2" && Array.isArray(deliveredSelectedContext.details.paths)
233
+ ? deliveredSelectedContext.details.paths.filter((value): value is string => typeof value === "string")
234
+ : [];
235
+ const criterionPaths = composedContext?.selected.map((entry) => entry.path) ?? [];
236
+ const selectedPackItems = (deliveredSelectedContext?.details.selectedItems as ContextInjectionItem[] | undefined)
237
+ ?? selectedPackPaths.map((path) => ({ path, estimatedTokens: 0 }));
238
+ const injectionItems: ContextInjectionItem[] = composedContext?.selected ?? selectedPackItems;
239
+ const deliveryEntries = new Map<string, { path: string; reason: string }>();
240
+ for (const filePath of selectedPackPaths) {
241
+ deliveryEntries.set(filePath, {
242
+ path: filePath,
243
+ reason: "Runtime confirmed delivery of a bounded Context Engine navigation pack."
244
+ });
245
+ }
246
+ for (const filePath of criterionPaths) {
247
+ deliveryEntries.set(filePath, {
248
+ path: filePath,
249
+ reason: "Runtime confirmed delivery of criterion-selected context."
250
+ });
251
+ }
252
+ const deliveryId = deliveryTask && deliveryEntries.size > 0 ? crypto.randomUUID() : undefined;
253
+ if (deliveryTask && deliveryId) {
254
+ const retrievalKey = typeof deliveredSelectedContext?.details.retrievalKey === "string" ? deliveredSelectedContext.details.retrievalKey : undefined;
255
+ stageContextDelivery(ctx, {
256
+ deliveryId,
257
+ taskRunId: deliveryTask.taskRunId,
258
+ turnId: turn.turnId,
259
+ entries: [...deliveryEntries.values()],
260
+ pack: retrievalKey
261
+ ? {
262
+ retrievalKey,
263
+ queryHash: String(deliveredSelectedContext?.details.queryHash ?? ""),
264
+ confidence: String(deliveredSelectedContext?.details.confidence ?? "unknown"),
265
+ estimatedTokens: Number(deliveredSelectedContext?.details.estimatedTokens ?? 0),
266
+ paths: selectedPackPaths
267
+ }
268
+ : undefined,
269
+ injection: {
270
+ source: composedContext ? "criterion-pack" : deliveredSelectedContext?.customType === "piagent-context-pack-v2" ? "auto-pack" : "criterion-seed",
271
+ queryHash: String(deliveredSelectedContext?.details.queryHash ?? signal.promptHash),
272
+ confidence: String(deliveredSelectedContext?.details.confidence ?? (criterionPaths.length > 0 ? "high" : "unknown")),
273
+ estimatedTokens: Number(deliveredSelectedContext?.details.estimatedTokens ?? composedContext?.estimatedTokens ?? 0),
274
+ selectedItems: injectionItems
275
+ }
276
+ }, { state: dependencies.state, telemetry: dependencies.telemetry });
277
+ }
238
278
  return {
239
279
  ...(systemPromptUpdate ?? {}),
240
280
  message: {
241
- customType: selectedContext?.customType ?? "piagent-runtime-task-intake",
281
+ customType: deliveredSelectedContext?.customType ?? "piagent-runtime-task-intake",
242
282
  content,
243
283
  display: false,
244
284
  details: {
245
- ...(selectedContext?.details ?? {}),
285
+ ...(deliveredSelectedContext?.details ?? {}),
286
+ ...(composedContext ? {
287
+ schemaVersion: 1,
288
+ queryHash: signal.promptHash,
289
+ estimatedTokens: composedContext.estimatedTokens,
290
+ paths: composedContext.selected.map((entry) => entry.path),
291
+ selectedItems: composedContext.selected
292
+ } : {}),
293
+ contextDelivery: deliveryId ? { schemaVersion: 1, deliveryId } : undefined,
246
294
  runtimeTask: intake?.task
247
295
  ? {
248
296
  taskId: intake.task.taskId,
249
297
  taskRunId: intake.task.taskRunId,
298
+ changeMode: intake.task.changeMode,
299
+ mutationPolicy: intake.task.mutationPolicy,
250
300
  scope: intake.task.scope,
251
301
  verifyCommands: intake.task.verifyCommands,
252
302
  criterionGraph: intake.task.criterionGraph ? { mode: intake.task.criterionGraph.mode, graphDigest: intake.task.criterionGraph.graphDigest, nodes: intake.task.criterionGraph.nodes.length } : null,
253
303
  intakeMode: intake.task.intakeMode
254
304
  }
255
305
  : undefined,
256
- criterionContext: criterionContext?.selected.length ? {
257
- paths: criterionContext.selected.map((entry) => entry.path), estimatedTokens: criterionContext.estimatedTokens
306
+ criterionContext: composedContext ? {
307
+ paths: composedContext.selected.map((entry) => entry.path), estimatedTokens: composedContext.estimatedTokens
258
308
  } : undefined,
259
309
  runtimeIntakeStarted: intake?.started ?? false
260
310
  }
@@ -263,6 +313,9 @@ export function registerAgentStartHook(pi: ExtensionAPI, dependencies: AgentStar
263
313
  };
264
314
 
265
315
  const packKey = dependencies.promptPackKey(ctx, signal.promptHash);
316
+ if (!autoPackUseful && activeTask?.trace.outcome === "pending" && query.length >= 20 && signal.workflow !== "usage" && dependencies.autoContextEnabled) {
317
+ await measureContextDeltaShadow({ ctx, query, turnId: turn.turnId, task: activeTask, mode: dependencies.contextDeltaShadowMode, protectedTarget, excludePatterns: dependencies.contextExcludePatterns(ctx), telemetry: dependencies.telemetry });
318
+ }
266
319
  if (
267
320
  query.length < 20
268
321
  || signal.workflow === "usage"
@@ -304,6 +357,7 @@ export function registerAgentStartHook(pi: ExtensionAPI, dependencies: AgentStar
304
357
  if (!status.exists || status.stale) {
305
358
  dependencies.telemetry(ctx, {
306
359
  event: "context_pack",
360
+ turnId: turn.turnId,
307
361
  queryHash: signal.promptHash,
308
362
  confidence: "none",
309
363
  candidates: 0,
@@ -348,6 +402,7 @@ export function registerAgentStartHook(pi: ExtensionAPI, dependencies: AgentStar
348
402
  });
349
403
  dependencies.telemetry(ctx, {
350
404
  event: "context_pack",
405
+ turnId: turn.turnId,
351
406
  queryHash: pack.queryHash,
352
407
  confidence: pack.confidence,
353
408
  candidates: pack.candidates,
@@ -379,10 +434,12 @@ export function registerAgentStartHook(pi: ExtensionAPI, dependencies: AgentStar
379
434
  }
380
435
  if (!contextPlanAcceptsConfidence(plan, pack.confidence) || pack.selected.length === 0) return finishAgentStart();
381
436
  const memoryBudgetTokens = Math.max(0, plan.budgetTokens - pack.estimatedTokens);
382
- const memoryHints = formatRepositoryMemoryHints(
383
- selectRepositoryMemoryFacts(ctx.cwd, query, { limit: 2, excludePatterns }),
384
- memoryBudgetTokens
385
- );
437
+ const memoryHints = runtimeIntake
438
+ ? { text: "", ids: [] }
439
+ : formatRepositoryMemoryHints(
440
+ selectRepositoryMemoryFacts(ctx.cwd, query, { limit: 2, excludePatterns }),
441
+ memoryBudgetTokens
442
+ );
386
443
  dependencies.telemetry(ctx, {
387
444
  event: "repository_memory_selected",
388
445
  queryHash: signal.promptHash,
@@ -392,27 +449,29 @@ export function registerAgentStartHook(pi: ExtensionAPI, dependencies: AgentStar
392
449
  estimatedTokens: estimateContextTokens(memoryHints.text),
393
450
  planReceipt: plan.receipt
394
451
  });
395
- dependencies.state.rememberInjectedContextPack(ctx, dependencies.retrievalKey(ctx, query), {
396
- queryHash: pack.queryHash,
397
- confidence: pack.confidence,
398
- estimatedTokens: pack.estimatedTokens,
399
- paths: pack.selected.map((item) => item.path)
400
- });
401
- for (const selected of pack.selected) {
402
- dependencies.state.rememberObservedContext(ctx, {
403
- path: selected.path,
404
- reason: "Runtime injected a bounded Context Engine navigation pack."
405
- });
406
- }
407
452
  return finishAgentStart({
408
453
  customType: "piagent-context-pack-v2",
409
454
  content: [pack.text, memoryHints.text].filter(Boolean).join("\n\n"),
410
455
  details: {
411
456
  schemaVersion: 2,
412
457
  queryHash: pack.queryHash,
458
+ retrievalKey: dependencies.retrievalKey(ctx, query),
413
459
  confidence: pack.confidence,
414
460
  estimatedTokens: pack.estimatedTokens,
415
461
  paths: pack.selected.map((item) => item.path),
462
+ selectedItems: pack.selected.map((item) => ({
463
+ path: item.path,
464
+ estimatedTokens: item.estimatedTokens,
465
+ sources: item.sources,
466
+ links: item.links,
467
+ fileContentHash: item.fileContentHash,
468
+ ...(item.sanitizedContentDigest ? { sanitizedContentDigest: item.sanitizedContentDigest } : {}),
469
+ payloadHash: item.payloadHash,
470
+ representation: item.representation,
471
+ ranges: item.ranges,
472
+ generation: item.generation,
473
+ sensitiveContentRedacted: item.sensitiveContentRedacted
474
+ })),
416
475
  repositoryMemoryIds: memoryHints.ids,
417
476
  currentSnapshot: plan.currentSnapshot,
418
477
  contextPlan: plan
@@ -421,6 +480,7 @@ export function registerAgentStartHook(pi: ExtensionAPI, dependencies: AgentStar
421
480
  } catch (error) {
422
481
  dependencies.telemetry(ctx, {
423
482
  event: "context_pack",
483
+ turnId: turn.turnId,
424
484
  queryHash: signal.promptHash,
425
485
  confidence: "none",
426
486
  candidates: 0,
@@ -6,6 +6,7 @@ import {
6
6
  acceptanceCriticalRecoveryProjection,
7
7
  applyAcceptanceRecoveryProvenance
8
8
  } from "../../extensions/acceptance-receipt.js";
9
+ import { hasDurableContextEvidence } from "../../extensions/context-evidence.js";
9
10
  import type { TaskContract } from "../../extensions/guard-types.js";
10
11
  import { runtimeLifecycleMode, workingTreeEvidenceDigest } from "../../extensions/task-lifecycle.js";
11
12
  import { recordCompletionAudit } from "../../extensions/task-runtime-audit.js";
@@ -18,8 +19,7 @@ import {
18
19
  looksLikeCompletionClaim,
19
20
  looksLikeIncompleteHandoff
20
21
  } from "../session/message-signals.ts";
21
- import { RuntimeSessionState } from "../session/runtime-state.ts";
22
- import type { ObservedTaskContext } from "../session/runtime-state.ts";
22
+ import { RuntimeSessionState, type ObservedTaskContext } from "../session/runtime-state.ts";
23
23
  import type { RecoveryDecision } from "../recovery/recovery-policy.ts";
24
24
  import { buildHandoffProjection, handoffProjectionPath, writeHandoffProjection } from "../recovery/handoff-projection.ts";
25
25
  import { evaluateExactFinalOutputContract } from "../quality/exact-output-contract.ts";
@@ -177,7 +177,7 @@ export function registerCompletionHook(pi: ExtensionAPI, dependencies: Completio
177
177
  let task = flushObservedTaskContext(
178
178
  pi,
179
179
  ctx,
180
- state.observedContext(ctx),
180
+ state.qualifiedTaskContext(ctx),
181
181
  maxManifestFiles,
182
182
  "context_observed_before_handoff"
183
183
  ) ?? activeTask(ctx);
@@ -187,7 +187,7 @@ export function registerCompletionHook(pi: ExtensionAPI, dependencies: Completio
187
187
  const completionClaim = looksLikeCompletionClaim(text);
188
188
  const incompleteHandoff = looksLikeIncompleteHandoff(text);
189
189
  if (task.trace.outcome !== "pending") return;
190
- const readOnlyEvidenceObserved = task.changeMode === "read-only" && task.contextManifest.length > 0;
190
+ const readOnlyEvidenceObserved = (task.changeMode === "read-only" || task.mutationPolicy === "forbidden") && hasDurableContextEvidence(task);
191
191
  const latestExactVerifier = latestObservedVerification(task.verifyEvidence.filter((evidence) => evidence.matchedProfileCommand === true));
192
192
  const potentiallyFinalEvidence = task.observedChangedFiles.length > 0
193
193
  || latestExactVerifier?.exitCode === 0
@@ -298,7 +298,6 @@ export function registerCompletionHook(pi: ExtensionAPI, dependencies: Completio
298
298
  projected = withRecoveryProvenance(ctx, projected, projectedGate, currentDigests, null);
299
299
  task = writeTask(ctx.cwd, projected);
300
300
  observeTrajectorySync(ctx, syncTrajectory?.(ctx, task, { sourceHook: "completion" }), telemetry);
301
- state.cacheTaskIdentity(ctx, task);
302
301
  state.clearPerformanceReview(task.taskRunId);
303
302
  state.clearObservedContext(ctx);
304
303
  activateBaseTools(ctx);
@@ -387,7 +386,7 @@ export function registerCompletionHook(pi: ExtensionAPI, dependencies: Completio
387
386
  ...verifierInstructions(gate.missingVerifyCommands),
388
387
  "Do not broaden task scope, repeat a failed hypothesis, expand permission, or perform an external action."
389
388
  ]
390
- : selectedRecovery.reasonCodes.includes("unknown-diagnostic-pass") || task.changeMode === "read-only"
389
+ : selectedRecovery.reasonCodes.includes("unknown-diagnostic-pass") || task.changeMode === "read-only" || task.mutationPolicy === "forbidden"
391
390
  ? [
392
391
  ...(gate.missingVerifyCommands.length > 0
393
392
  ? ["Run only the missing exact verifier commands against the current working tree.", ...verifierInstructions(gate.missingVerifyCommands)]
@@ -11,6 +11,7 @@ import { buildHandoffProjection, writeHandoffProjection } from "../recovery/hand
11
11
  import { buildContextPreflight, buildUsageSnapshot } from "../session/usage.ts";
12
12
  import { activeTaskToolGroups, toolGroupsForPrompt } from "../tools/tool-groups.ts";
13
13
  import type { PiagentToolGroup } from "../tools/tool-groups.ts";
14
+ import type { RuntimeSessionState } from "../session/runtime-state.ts";
14
15
  import {
15
16
  buildFreshCommand,
16
17
  chooseFreshWorkflow,
@@ -26,6 +27,7 @@ import {
26
27
  } from "../workflows/task-intake.ts";
27
28
 
28
29
  type InputHookDependencies = {
30
+ state: RuntimeSessionState;
29
31
  boilerplateCollapseChars: number;
30
32
  activeTask: (ctx: ExtensionContext) => TaskContract | undefined;
31
33
  authorityPolicy: (ctx: ExtensionContext, task: TaskContract) => AuthorityResumeDecision;
@@ -44,7 +46,14 @@ export function registerInputHook(pi: ExtensionAPI, dependencies: InputHookDepen
44
46
  }
45
47
 
46
48
  const taskSignal = classifyContextTask(text);
47
- const activeTask = dependencies.activeTask(ctx);
49
+ const sessionTask = dependencies.activeTask(ctx);
50
+ if (sessionTask && sessionTask.trace.outcome !== "pending") dependencies.state.clearTaskBoundary(ctx, sessionTask.taskRunId);
51
+ else if (!sessionTask) {
52
+ const cachedTask = dependencies.state.taskIdentity(ctx);
53
+ if (cachedTask) dependencies.state.clearTaskBoundary(ctx, cachedTask.taskRunId);
54
+ }
55
+ const activeTask = sessionTask?.trace.outcome === "pending" ? sessionTask : undefined;
56
+ const turn = dependencies.state.beginTurn(ctx, taskSignal.promptHash);
48
57
  const authorityPolicy = activeTask?.trace.outcome === "pending" ? dependencies.authorityPolicy(ctx, activeTask) : undefined;
49
58
  let authorityHandoffReady = false;
50
59
  if (activeTask && authorityPolicy?.disposition === "new-attempt-required") {
@@ -79,6 +88,7 @@ export function registerInputHook(pi: ExtensionAPI, dependencies: InputHookDepen
79
88
  : promptGroups);
80
89
  dependencies.telemetry(ctx, {
81
90
  event: "user_input",
91
+ turnId: turn.turnId,
82
92
  source: event.source,
83
93
  promptHash: taskSignal.promptHash,
84
94
  promptChars: taskSignal.promptChars,
@@ -4,6 +4,7 @@ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-a
4
4
  import type { TaskContract } from "../../extensions/guard-types.js";
5
5
  import { RuntimeSessionState } from "../session/runtime-state.ts";
6
6
  import type { ObservedTaskContext } from "../session/runtime-state.ts";
7
+ import { confirmContextDelivery, contextDeliveryIdFromDetails } from "../context/context-delivery.ts";
7
8
 
8
9
  type TelemetryWriter = (ctx: ExtensionContext, payload: Record<string, unknown>) => void;
9
10
 
@@ -39,6 +40,20 @@ export function registerSessionHooks(pi: ExtensionAPI, dependencies: SessionHook
39
40
  flushObservedTaskContext
40
41
  } = dependencies;
41
42
 
43
+ pi.on("message_start", async (event, ctx) => {
44
+ const message = event.message as unknown as { role?: unknown; details?: unknown };
45
+ if (message.role !== "custom") return;
46
+ const deliveryId = contextDeliveryIdFromDetails(message.details);
47
+ if (!deliveryId) return;
48
+ confirmContextDelivery(pi, ctx, deliveryId, {
49
+ state,
50
+ maxManifestFiles,
51
+ activeTask: (deliveryContext) => activeTask(deliveryContext.cwd, deliveryContext.sessionManager.getSessionId()),
52
+ flushObservedTaskContext,
53
+ telemetry
54
+ });
55
+ });
56
+
42
57
  pi.on("session_info_changed", async (event, ctx) => {
43
58
  const sessionId = ctx.sessionManager.getSessionId();
44
59
  const task = activeTask(ctx.cwd, sessionId);
@@ -58,6 +73,7 @@ export function registerSessionHooks(pi: ExtensionAPI, dependencies: SessionHook
58
73
 
59
74
  pi.on("turn_end", async (event, ctx) => {
60
75
  const message = event.message as unknown as { usage?: unknown; stopReason?: unknown; role?: unknown };
76
+ const task = activeTask(ctx.cwd, ctx.sessionManager.getSessionId());
61
77
  telemetry(ctx, {
62
78
  event: "turn_end",
63
79
  turnIndex: event.turnIndex,
@@ -65,9 +81,13 @@ export function registerSessionHooks(pi: ExtensionAPI, dependencies: SessionHook
65
81
  role: message.role,
66
82
  stopReason: message.stopReason,
67
83
  usage: message.usage,
68
- contextUsage: ctx.getContextUsage()
84
+ contextUsage: ctx.getContextUsage(),
85
+ taskId: task?.taskId,
86
+ taskRunId: task?.taskRunId,
87
+ taskOutcome: task?.trace.outcome
69
88
  });
70
89
  dependencies.onTurnEnd?.(ctx);
90
+ if (task && task.trace.outcome !== "pending") state.clearTaskBoundary(ctx, task.taskRunId);
71
91
  });
72
92
 
73
93
  pi.on("agent_settled", async (_event, ctx) => {
@@ -96,7 +116,7 @@ export function registerSessionHooks(pi: ExtensionAPI, dependencies: SessionHook
96
116
  flushObservedTaskContext(
97
117
  pi,
98
118
  ctx,
99
- state.observedContext(ctx),
119
+ state.qualifiedTaskContext(ctx),
100
120
  maxManifestFiles,
101
121
  "context_observed_before_shutdown"
102
122
  );
@@ -139,8 +139,9 @@ export function registerSessionStartHook(pi: ExtensionAPI, dependencies: Session
139
139
  dependencies.bindTask(ctx.cwd, sessionId, sessionName, resumedTask);
140
140
  }
141
141
  if (resumedTask?.workingTreeDigestMigration && resumedTask.workingTreeDigestMigration.status !== "refreshed") dependencies.state.clearDigestMigrationState(ctx, resumedTask.taskRunId, resumedTask.taskId);
142
- dependencies.state.cacheTaskIdentity(ctx, resumedTask);
143
- const resumeState = resumedTask ? dependencies.inspectResume(ctx.cwd, resumedTask, sessionId) : undefined;
142
+ if (resumedTask?.trace.outcome === "pending") dependencies.state.cacheTaskIdentity(ctx, resumedTask);
143
+ else dependencies.state.clearSession(ctx);
144
+ const resumeState = resumedTask?.trace.outcome === "pending" ? dependencies.inspectResume(ctx.cwd, resumedTask, sessionId) : undefined;
144
145
  if (resumeState) dependencies.state.rememberResumeState(resumeState);
145
146
  if (resumedTask && resumeState?.enforcementSafe) observeTrajectorySync(ctx, dependencies.syncTrajectory?.(ctx, resumedTask), dependencies.telemetry);
146
147
  let taskRecovery: Record<string, unknown> | undefined;
@@ -217,6 +218,10 @@ export function registerSessionStartHook(pi: ExtensionAPI, dependencies: Session
217
218
  }
218
219
  dependencies.telemetry(ctx, {
219
220
  event: "session_start",
221
+ // Benchmark and efficiency projections must be able to distinguish a
222
+ // genuine zero-recovery session from telemetry produced before this
223
+ // receipt/result protocol existed.
224
+ editRecoveryContextTelemetryVersion: 1,
220
225
  activeTools: pi.getActiveTools().length,
221
226
  index: engineStatus,
222
227
  taskMigration: migration,