gsd-pi 2.81.0-dev.72a81bdf3 → 2.82.0-dev.2841a1e44

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 (243) hide show
  1. package/README.md +49 -30
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/GSD-WORKFLOW.md +3 -1
  4. package/dist/resources/extensions/browser-tools/tools/screenshot.js +1 -0
  5. package/dist/resources/extensions/browser-tools/tools/zoom.js +1 -0
  6. package/dist/resources/extensions/cmux/index.js +5 -0
  7. package/dist/resources/extensions/gsd/auto/orchestrator.js +113 -6
  8. package/dist/resources/extensions/gsd/auto/phases.js +9 -0
  9. package/dist/resources/extensions/gsd/auto-post-unit.js +169 -124
  10. package/dist/resources/extensions/gsd/auto-prompts.js +13 -5
  11. package/dist/resources/extensions/gsd/auto-verification.js +28 -22
  12. package/dist/resources/extensions/gsd/auto.js +128 -52
  13. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +5 -0
  14. package/dist/resources/extensions/gsd/bootstrap/subagent-input.js +16 -7
  15. package/dist/resources/extensions/gsd/bootstrap/system-context.js +55 -12
  16. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +3 -1
  17. package/dist/resources/extensions/gsd/clean-root-preflight.js +170 -8
  18. package/dist/resources/extensions/gsd/commands/catalog.js +4 -1
  19. package/dist/resources/extensions/gsd/commands/handlers/core.js +22 -1
  20. package/dist/resources/extensions/gsd/commands-bootstrap.js +5 -0
  21. package/dist/resources/extensions/gsd/commands-handlers.js +15 -2
  22. package/dist/resources/extensions/gsd/context-store.js +112 -0
  23. package/dist/resources/extensions/gsd/db-writer.js +150 -84
  24. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  25. package/dist/resources/extensions/gsd/doctor-git-checks.js +41 -6
  26. package/dist/resources/extensions/gsd/knowledge-backfill.js +144 -0
  27. package/dist/resources/extensions/gsd/knowledge-capture.js +136 -0
  28. package/dist/resources/extensions/gsd/knowledge-parser.js +154 -0
  29. package/dist/resources/extensions/gsd/knowledge-projection.js +210 -0
  30. package/dist/resources/extensions/gsd/markdown-renderer.js +6 -1
  31. package/dist/resources/extensions/gsd/md-importer.js +1 -1
  32. package/dist/resources/extensions/gsd/memory-backfill.js +73 -17
  33. package/dist/resources/extensions/gsd/memory-consolidation-scanner.js +222 -0
  34. package/dist/resources/extensions/gsd/migrate/command.js +5 -0
  35. package/dist/resources/extensions/gsd/migrate/preview.js +9 -0
  36. package/dist/resources/extensions/gsd/migrate/transformer.js +51 -4
  37. package/dist/resources/extensions/gsd/migrate/writer.js +11 -1
  38. package/dist/resources/extensions/gsd/prompts/system.md +2 -2
  39. package/dist/resources/extensions/gsd/provider-switch-observer.js +146 -0
  40. package/dist/resources/extensions/gsd/templates/knowledge.md +2 -2
  41. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +119 -0
  42. package/dist/resources/extensions/gsd/unit-context-manifest.js +25 -2
  43. package/dist/resources/extensions/gsd/verification-verdict.js +26 -0
  44. package/dist/resources/extensions/gsd/worktree-lifecycle.js +21 -2
  45. package/dist/resources/extensions/subagent/index.js +448 -78
  46. package/dist/resources/extensions/subagent/launch.js +77 -0
  47. package/dist/resources/extensions/subagent/run-store.js +148 -0
  48. package/dist/resources/extensions/visual-brief/artifact-policy.js +29 -0
  49. package/dist/resources/extensions/visual-brief/extension-manifest.json +8 -0
  50. package/dist/resources/extensions/visual-brief/index.js +5 -0
  51. package/dist/resources/extensions/visual-brief/page-contract.js +122 -0
  52. package/dist/resources/extensions/visual-brief/prompts.js +111 -0
  53. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  54. package/dist/web/standalone/.next/BUILD_ID +1 -1
  55. package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
  56. package/dist/web/standalone/.next/build-manifest.json +3 -3
  57. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  58. package/dist/web/standalone/.next/react-loadable-manifest.json +3 -3
  59. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  60. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  61. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  69. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  70. package/dist/web/standalone/.next/server/app/_not-found.rsc +2 -2
  71. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  72. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  74. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  77. package/dist/web/standalone/.next/server/app/index.html +1 -1
  78. package/dist/web/standalone/.next/server/app/index.rsc +2 -2
  79. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +2 -2
  81. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +2 -2
  83. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  84. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  85. package/dist/web/standalone/.next/server/app-paths-manifest.json +15 -15
  86. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  87. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  88. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  89. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  90. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  91. package/dist/web/standalone/.next/static/chunks/2973.33f26573894b6153.js +2 -0
  92. package/dist/web/standalone/.next/static/chunks/{8359.e059d86b255fce1c.js → 8359.7eb3bb8f8ecf4c01.js} +2 -2
  93. package/dist/web/standalone/.next/static/chunks/{webpack-de742b64187e13fe.js → webpack-6a95bc41e0f7ec89.js} +1 -1
  94. package/dist/web/standalone/.next/static/css/0262768ec1b89d34.css +1 -0
  95. package/package.json +5 -4
  96. package/packages/contracts/dist/rpc.test.js +7 -0
  97. package/packages/contracts/dist/rpc.test.js.map +1 -1
  98. package/packages/contracts/dist/workflow.d.ts +21 -0
  99. package/packages/contracts/dist/workflow.d.ts.map +1 -1
  100. package/packages/contracts/dist/workflow.js +24 -0
  101. package/packages/contracts/dist/workflow.js.map +1 -1
  102. package/packages/contracts/src/rpc.test.ts +8 -0
  103. package/packages/contracts/src/workflow.ts +24 -0
  104. package/packages/daemon/package.json +2 -2
  105. package/packages/mcp-server/README.md +14 -3
  106. package/packages/mcp-server/dist/workflow-tools.d.ts +0 -3
  107. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  108. package/packages/mcp-server/dist/workflow-tools.js +80 -0
  109. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  110. package/packages/mcp-server/package.json +2 -2
  111. package/packages/mcp-server/src/workflow-tools-parity.test.ts +244 -0
  112. package/packages/mcp-server/src/workflow-tools.test.ts +22 -0
  113. package/packages/mcp-server/src/workflow-tools.ts +168 -0
  114. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  115. package/packages/native/package.json +1 -1
  116. package/packages/pi-agent-core/package.json +1 -1
  117. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  118. package/packages/pi-ai/dist/index.d.ts +2 -2
  119. package/packages/pi-ai/dist/index.d.ts.map +1 -1
  120. package/packages/pi-ai/dist/index.js +1 -1
  121. package/packages/pi-ai/dist/index.js.map +1 -1
  122. package/packages/pi-ai/dist/providers/transform-messages.d.ts +11 -0
  123. package/packages/pi-ai/dist/providers/transform-messages.d.ts.map +1 -1
  124. package/packages/pi-ai/dist/providers/transform-messages.js +20 -0
  125. package/packages/pi-ai/dist/providers/transform-messages.js.map +1 -1
  126. package/packages/pi-ai/package.json +1 -1
  127. package/packages/pi-ai/src/index.ts +7 -2
  128. package/packages/pi-ai/src/providers/transform-messages.ts +24 -0
  129. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  130. package/packages/pi-coding-agent/dist/core/system-prompt.js +4 -4
  131. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  132. package/packages/pi-coding-agent/dist/tests/system-prompt-file-safety.test.d.ts +2 -0
  133. package/packages/pi-coding-agent/dist/tests/system-prompt-file-safety.test.d.ts.map +1 -0
  134. package/packages/pi-coding-agent/dist/tests/system-prompt-file-safety.test.js +17 -0
  135. package/packages/pi-coding-agent/dist/tests/system-prompt-file-safety.test.js.map +1 -0
  136. package/packages/pi-coding-agent/package.json +1 -1
  137. package/packages/pi-coding-agent/src/core/system-prompt.ts +4 -4
  138. package/packages/pi-coding-agent/src/tests/system-prompt-file-safety.test.ts +22 -0
  139. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  140. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  141. package/packages/pi-tui/dist/tui.js +5 -0
  142. package/packages/pi-tui/dist/tui.js.map +1 -1
  143. package/packages/pi-tui/package.json +1 -1
  144. package/packages/pi-tui/src/tui.ts +6 -0
  145. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  146. package/packages/rpc-client/package.json +1 -1
  147. package/packages/rpc-client/tsconfig.tsbuildinfo +1 -1
  148. package/pkg/package.json +1 -1
  149. package/src/resources/GSD-WORKFLOW.md +3 -1
  150. package/src/resources/extensions/browser-tools/tools/screenshot.ts +1 -0
  151. package/src/resources/extensions/browser-tools/tools/zoom.ts +1 -0
  152. package/src/resources/extensions/cmux/index.ts +6 -0
  153. package/src/resources/extensions/gsd/auto/contracts.ts +46 -11
  154. package/src/resources/extensions/gsd/auto/orchestrator.ts +118 -6
  155. package/src/resources/extensions/gsd/auto/phases.ts +14 -0
  156. package/src/resources/extensions/gsd/auto-post-unit.ts +194 -137
  157. package/src/resources/extensions/gsd/auto-prompts.ts +13 -5
  158. package/src/resources/extensions/gsd/auto-verification.ts +36 -34
  159. package/src/resources/extensions/gsd/auto.ts +136 -51
  160. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +6 -0
  161. package/src/resources/extensions/gsd/bootstrap/subagent-input.ts +16 -6
  162. package/src/resources/extensions/gsd/bootstrap/system-context.ts +58 -15
  163. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +3 -2
  164. package/src/resources/extensions/gsd/clean-root-preflight.ts +174 -8
  165. package/src/resources/extensions/gsd/commands/catalog.ts +4 -1
  166. package/src/resources/extensions/gsd/commands/handlers/core.ts +25 -1
  167. package/src/resources/extensions/gsd/commands-bootstrap.ts +10 -0
  168. package/src/resources/extensions/gsd/commands-handlers.ts +19 -2
  169. package/src/resources/extensions/gsd/context-store.ts +120 -1
  170. package/src/resources/extensions/gsd/db-writer.ts +167 -84
  171. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  172. package/src/resources/extensions/gsd/doctor-git-checks.ts +44 -6
  173. package/src/resources/extensions/gsd/doctor-types.ts +2 -0
  174. package/src/resources/extensions/gsd/knowledge-backfill.ts +164 -0
  175. package/src/resources/extensions/gsd/knowledge-capture.ts +160 -0
  176. package/src/resources/extensions/gsd/knowledge-parser.ts +174 -0
  177. package/src/resources/extensions/gsd/knowledge-projection.ts +241 -0
  178. package/src/resources/extensions/gsd/markdown-renderer.ts +6 -1
  179. package/src/resources/extensions/gsd/md-importer.ts +1 -1
  180. package/src/resources/extensions/gsd/memory-backfill.ts +89 -17
  181. package/src/resources/extensions/gsd/memory-consolidation-scanner.ts +277 -0
  182. package/src/resources/extensions/gsd/migrate/command.ts +5 -0
  183. package/src/resources/extensions/gsd/migrate/preview.ts +10 -0
  184. package/src/resources/extensions/gsd/migrate/transformer.ts +58 -4
  185. package/src/resources/extensions/gsd/migrate/writer.ts +14 -1
  186. package/src/resources/extensions/gsd/prompts/system.md +2 -2
  187. package/src/resources/extensions/gsd/provider-switch-observer.ts +185 -0
  188. package/src/resources/extensions/gsd/templates/knowledge.md +2 -2
  189. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +75 -0
  190. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +408 -4
  191. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +6 -5
  192. package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +4 -4
  193. package/src/resources/extensions/gsd/tests/brief-command.test.ts +89 -0
  194. package/src/resources/extensions/gsd/tests/browser-tools-compatibility-declarations.test.ts +62 -0
  195. package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +107 -2
  196. package/src/resources/extensions/gsd/tests/closeout-git-deferral.test.ts +16 -0
  197. package/src/resources/extensions/gsd/tests/context-store-decisions-from-memories.test.ts +312 -0
  198. package/src/resources/extensions/gsd/tests/db-writer.test.ts +13 -8
  199. package/src/resources/extensions/gsd/tests/decisions-projection-from-memories.test.ts +453 -0
  200. package/src/resources/extensions/gsd/tests/decisions-stop-table-writes.test.ts +348 -0
  201. package/src/resources/extensions/gsd/tests/evidence-cross-ref.test.ts +38 -0
  202. package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +8 -4
  203. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +11 -7
  204. package/src/resources/extensions/gsd/tests/integration/doctor-git.test.ts +44 -0
  205. package/src/resources/extensions/gsd/tests/integration/integration-lifecycle.test.ts +13 -5
  206. package/src/resources/extensions/gsd/tests/integration/migrate-command.test.ts +48 -3
  207. package/src/resources/extensions/gsd/tests/knowledge-backfill-projection.test.ts +323 -0
  208. package/src/resources/extensions/gsd/tests/knowledge-capture.test.ts +242 -0
  209. package/src/resources/extensions/gsd/tests/knowledge.test.ts +47 -2
  210. package/src/resources/extensions/gsd/tests/load-knowledge-block-rules-only.test.ts +209 -0
  211. package/src/resources/extensions/gsd/tests/memory-consolidation-scanner.test.ts +316 -0
  212. package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +5 -1
  213. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +6 -1
  214. package/src/resources/extensions/gsd/tests/plan-milestone-sketch-render.test.ts +157 -0
  215. package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +79 -1
  216. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +8 -0
  217. package/src/resources/extensions/gsd/tests/provider-switch-observer.test.ts +252 -0
  218. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +16 -4
  219. package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +6 -0
  220. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +21 -0
  221. package/src/resources/extensions/gsd/tests/verification-verdict.test.ts +78 -0
  222. package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +25 -0
  223. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +16 -0
  224. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +135 -0
  225. package/src/resources/extensions/gsd/unit-context-manifest.ts +35 -2
  226. package/src/resources/extensions/gsd/verification-verdict.ts +47 -0
  227. package/src/resources/extensions/gsd/workflow-logger.ts +4 -0
  228. package/src/resources/extensions/gsd/worktree-lifecycle.ts +20 -2
  229. package/src/resources/extensions/subagent/index.ts +567 -103
  230. package/src/resources/extensions/subagent/launch.ts +131 -0
  231. package/src/resources/extensions/subagent/run-store.ts +218 -0
  232. package/src/resources/extensions/subagent/tests/launch.test.ts +115 -0
  233. package/src/resources/extensions/subagent/tests/run-store.test.ts +111 -0
  234. package/src/resources/extensions/visual-brief/artifact-policy.ts +41 -0
  235. package/src/resources/extensions/visual-brief/extension-manifest.json +8 -0
  236. package/src/resources/extensions/visual-brief/index.ts +8 -0
  237. package/src/resources/extensions/visual-brief/page-contract.ts +134 -0
  238. package/src/resources/extensions/visual-brief/prompts.ts +147 -0
  239. package/src/resources/extensions/visual-brief/tests/visual-brief.test.ts +172 -0
  240. package/dist/web/standalone/.next/static/chunks/2556.0527fea66e123b7f.js +0 -1
  241. package/dist/web/standalone/.next/static/css/54ec2745c1da488b.css +0 -1
  242. /package/dist/web/standalone/.next/static/{rIkMv4YSNlfSeqmGqWVns → Qgr2B_MRhPxC0z8fwv4vT}/_buildManifest.js +0 -0
  243. /package/dist/web/standalone/.next/static/{rIkMv4YSNlfSeqmGqWVns → Qgr2B_MRhPxC0z8fwv4vT}/_ssgManifest.js +0 -0
@@ -0,0 +1,185 @@
1
+ // GSD-2 — ADR-005 Phase 3b: surface ProviderSwitchReport from pi-ai.
2
+ //
3
+ // pi-ai builds a ProviderSwitchReport on every cross-provider transform but
4
+ // only logs it to stderr when GSD_VERBOSE=1. This module installs a
5
+ // single-subscriber observer that surfaces non-empty reports through GSD's
6
+ // three usual telemetry surfaces:
7
+ //
8
+ // 1. UOK audit event (category model-policy, type provider-switch) — only
9
+ // when an auto trace is active.
10
+ // 2. Persistent notification (.gsd/notifications.jsonl, severity warning) —
11
+ // whenever the GSD basePath is known, so users see the loss in the
12
+ // dashboard / status surface without GSD_VERBOSE.
13
+ // 3. In-memory counter, exposed via getProviderSwitchStats() so any
14
+ // caller (dashboard, doctor, tests) can read the rollup.
15
+
16
+ import { setProviderSwitchObserver, type ProviderSwitchReport } from "@gsd/pi-ai";
17
+
18
+ import { autoSession } from "./auto-runtime-state.js";
19
+ import { appendNotification } from "./notification-store.js";
20
+ import { buildAuditEnvelope, emitUokAuditEvent } from "./uok/audit.js";
21
+
22
+ /** Rollup of cross-provider context transformations, grouped by trace. */
23
+ export interface ProviderSwitchStats {
24
+ /** Total non-empty reports observed since process start (or last reset). */
25
+ totalSwitches: number;
26
+ /** Sum of every counted transformation across all reports. */
27
+ totals: {
28
+ thinkingBlocksDropped: number;
29
+ thinkingBlocksDowngraded: number;
30
+ toolCallIdsRemapped: number;
31
+ syntheticToolResultsInserted: number;
32
+ thoughtSignaturesDropped: number;
33
+ };
34
+ /** Per-trace breakdown. Key "interactive" covers reports outside auto-mode. */
35
+ byTrace: Record<string, TraceSwitchStats>;
36
+ /** The most recent non-empty report, if any. */
37
+ lastReport: ProviderSwitchReport | null;
38
+ /** ISO timestamp of the most recent non-empty report, if any. */
39
+ lastAt: string | null;
40
+ }
41
+
42
+ export interface TraceSwitchStats {
43
+ switches: number;
44
+ lastReport: ProviderSwitchReport;
45
+ lastAt: string;
46
+ }
47
+
48
+ const INTERACTIVE_TRACE_KEY = "interactive";
49
+
50
+ interface MutableTraceStats {
51
+ switches: number;
52
+ lastReport: ProviderSwitchReport;
53
+ lastAt: string;
54
+ }
55
+
56
+ let installed = false;
57
+ let totalSwitches = 0;
58
+ const totals = {
59
+ thinkingBlocksDropped: 0,
60
+ thinkingBlocksDowngraded: 0,
61
+ toolCallIdsRemapped: 0,
62
+ syntheticToolResultsInserted: 0,
63
+ thoughtSignaturesDropped: 0,
64
+ };
65
+ const byTrace = new Map<string, MutableTraceStats>();
66
+ let lastReport: ProviderSwitchReport | null = null;
67
+ let lastAt: string | null = null;
68
+
69
+ /** Format a one-line summary suitable for a notification message. */
70
+ function summarize(report: ProviderSwitchReport): string {
71
+ const parts: string[] = [];
72
+ if (report.thinkingBlocksDropped > 0) parts.push(`${report.thinkingBlocksDropped} thinking dropped`);
73
+ if (report.thinkingBlocksDowngraded > 0) parts.push(`${report.thinkingBlocksDowngraded} thinking downgraded`);
74
+ if (report.toolCallIdsRemapped > 0) parts.push(`${report.toolCallIdsRemapped} tool ids remapped`);
75
+ if (report.syntheticToolResultsInserted > 0) parts.push(`${report.syntheticToolResultsInserted} synthetic tool results`);
76
+ if (report.thoughtSignaturesDropped > 0) parts.push(`${report.thoughtSignaturesDropped} thought signatures dropped`);
77
+ return `Provider switch ${report.fromApi} → ${report.toApi}: ${parts.join(", ")}`;
78
+ }
79
+
80
+ function recordReport(report: ProviderSwitchReport): void {
81
+ const now = new Date().toISOString();
82
+ totalSwitches += 1;
83
+ totals.thinkingBlocksDropped += report.thinkingBlocksDropped;
84
+ totals.thinkingBlocksDowngraded += report.thinkingBlocksDowngraded;
85
+ totals.toolCallIdsRemapped += report.toolCallIdsRemapped;
86
+ totals.syntheticToolResultsInserted += report.syntheticToolResultsInserted;
87
+ totals.thoughtSignaturesDropped += report.thoughtSignaturesDropped;
88
+ lastReport = report;
89
+ lastAt = now;
90
+
91
+ const traceKey = autoSession.currentTraceId ?? INTERACTIVE_TRACE_KEY;
92
+ const existing = byTrace.get(traceKey);
93
+ if (existing) {
94
+ existing.switches += 1;
95
+ existing.lastReport = report;
96
+ existing.lastAt = now;
97
+ } else {
98
+ byTrace.set(traceKey, { switches: 1, lastReport: report, lastAt: now });
99
+ }
100
+ }
101
+
102
+ function emitAudit(report: ProviderSwitchReport): void {
103
+ const traceId = autoSession.currentTraceId;
104
+ const basePath = autoSession.basePath;
105
+ if (!traceId || !basePath) return;
106
+ try {
107
+ emitUokAuditEvent(
108
+ basePath,
109
+ buildAuditEnvelope({
110
+ traceId,
111
+ category: "model-policy",
112
+ type: "provider-switch",
113
+ payload: {
114
+ fromApi: report.fromApi,
115
+ toApi: report.toApi,
116
+ thinkingBlocksDropped: report.thinkingBlocksDropped,
117
+ thinkingBlocksDowngraded: report.thinkingBlocksDowngraded,
118
+ toolCallIdsRemapped: report.toolCallIdsRemapped,
119
+ syntheticToolResultsInserted: report.syntheticToolResultsInserted,
120
+ thoughtSignaturesDropped: report.thoughtSignaturesDropped,
121
+ },
122
+ }),
123
+ );
124
+ } catch {
125
+ // Audit emission is best-effort. Counter + notification still fire.
126
+ }
127
+ }
128
+
129
+ function emitNotification(report: ProviderSwitchReport): void {
130
+ try {
131
+ appendNotification(summarize(report), "warning", "workflow-logger");
132
+ } catch {
133
+ // Notification persistence is best-effort.
134
+ }
135
+ }
136
+
137
+ function handleReport(report: ProviderSwitchReport): void {
138
+ recordReport(report);
139
+ emitAudit(report);
140
+ emitNotification(report);
141
+ }
142
+
143
+ /**
144
+ * Install the pi-ai observer. Idempotent — calling more than once is a no-op
145
+ * after the first install.
146
+ */
147
+ export function installProviderSwitchObserver(): void {
148
+ if (installed) return;
149
+ setProviderSwitchObserver(handleReport);
150
+ installed = true;
151
+ }
152
+
153
+ /** Uninstall the observer. Intended for tests. */
154
+ export function uninstallProviderSwitchObserver(): void {
155
+ setProviderSwitchObserver(undefined);
156
+ installed = false;
157
+ }
158
+
159
+ /** Read-only snapshot of the in-memory rollup. */
160
+ export function getProviderSwitchStats(): ProviderSwitchStats {
161
+ const trace: Record<string, TraceSwitchStats> = {};
162
+ for (const [key, value] of byTrace) {
163
+ trace[key] = { switches: value.switches, lastReport: { ...value.lastReport }, lastAt: value.lastAt };
164
+ }
165
+ return {
166
+ totalSwitches,
167
+ totals: { ...totals },
168
+ byTrace: trace,
169
+ lastReport: lastReport ? { ...lastReport } : null,
170
+ lastAt,
171
+ };
172
+ }
173
+
174
+ /** Reset the in-memory rollup. Intended for tests. */
175
+ export function _resetProviderSwitchStats(): void {
176
+ totalSwitches = 0;
177
+ totals.thinkingBlocksDropped = 0;
178
+ totals.thinkingBlocksDowngraded = 0;
179
+ totals.toolCallIdsRemapped = 0;
180
+ totals.syntheticToolResultsInserted = 0;
181
+ totals.thoughtSignaturesDropped = 0;
182
+ byTrace.clear();
183
+ lastReport = null;
184
+ lastAt = null;
185
+ }
@@ -1,7 +1,7 @@
1
1
  # Project Knowledge
2
2
 
3
- Append-only register of project-specific rules, patterns, and lessons learned.
4
- Agents read this before every unit. Add entries when you discover something worth remembering.
3
+ Project-specific rules plus projected patterns and lessons learned.
4
+ Rules are maintained in this file. Patterns and lessons are persisted to the memories table and rendered here on the next session-start projection.
5
5
 
6
6
  ## Rules
7
7
 
@@ -2548,6 +2548,81 @@ test("resolveAgentEndCancelled with errorContext passes it through to resolved p
2548
2548
  assert.equal(resolved.errorContext!.isTransient, true);
2549
2549
  });
2550
2550
 
2551
+ test("runUnitPhase pauses transient aborted cancellations instead of hard-stopping", async (t) => {
2552
+ _resetPendingResolve();
2553
+
2554
+ const basePath = mkdtempSync(join(tmpdir(), "gsd-aborted-cancel-"));
2555
+ t.after(() => {
2556
+ rmSync(basePath, { recursive: true, force: true });
2557
+ });
2558
+
2559
+ const ctx = {
2560
+ ...makeMockCtx(),
2561
+ ui: {
2562
+ notify: () => {},
2563
+ setStatus: () => {},
2564
+ setWorkingMessage: () => {},
2565
+ },
2566
+ sessionManager: {
2567
+ getEntries: () => [],
2568
+ },
2569
+ modelRegistry: {
2570
+ getProviderAuthMode: () => undefined,
2571
+ isProviderRequestReady: () => true,
2572
+ },
2573
+ } as any;
2574
+ const pi = {
2575
+ ...makeMockPi(),
2576
+ sendMessage: () => {
2577
+ queueMicrotask(() => resolveAgentEndCancelled({
2578
+ message: "Claude Code process aborted by user",
2579
+ category: "aborted",
2580
+ isTransient: true,
2581
+ }));
2582
+ },
2583
+ } as any;
2584
+ const s = makeLoopSession({
2585
+ basePath,
2586
+ canonicalProjectRoot: basePath,
2587
+ originalBasePath: basePath,
2588
+ });
2589
+ const deps = makeMockDeps();
2590
+ let seq = 0;
2591
+
2592
+ const result = await runUnitPhase(
2593
+ { ctx, pi, s, deps, prefs: undefined, iteration: 1, flowId: "flow-aborted", nextSeq: () => ++seq },
2594
+ {
2595
+ unitType: "execute-task",
2596
+ unitId: "M001/S01/T01",
2597
+ prompt: "do work",
2598
+ finalPrompt: "do work",
2599
+ pauseAfterUatDispatch: false,
2600
+ state: {
2601
+ phase: "executing",
2602
+ activeMilestone: { id: "M001", title: "Milestone" },
2603
+ activeSlice: { id: "S01", title: "Slice" },
2604
+ activeTask: { id: "T01", title: "Task" },
2605
+ registry: [{ id: "M001", title: "Milestone", status: "active" }],
2606
+ recentDecisions: [],
2607
+ blockers: [],
2608
+ nextAction: "",
2609
+ progress: { milestones: { done: 0, total: 1 } },
2610
+ requirements: { active: 0, validated: 0, deferred: 0, outOfScope: 0, blocked: 0, total: 0 },
2611
+ } as any,
2612
+ mid: "M001",
2613
+ midTitle: "Milestone",
2614
+ isRetry: false,
2615
+ previousTier: undefined,
2616
+ },
2617
+ { recentUnits: [{ key: "execute-task/M001/S01/T01" }], stuckRecoveryAttempts: 0, consecutiveFinalizeTimeouts: 0 },
2618
+ );
2619
+
2620
+ assert.equal(result.action, "break");
2621
+ assert.equal((result as any).reason, "unit-aborted-pause");
2622
+ assert.equal(deps.callLog.includes("pauseAuto"), true);
2623
+ assert.equal(deps.callLog.includes("stopAuto"), false);
2624
+ });
2625
+
2551
2626
  test("runUnitPhase pauses ghost completions before closeout and finalize side effects", async (t) => {
2552
2627
  _resetPendingResolve();
2553
2628