@iowarp/clio-coder 0.3.4 → 0.3.6

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 (265) hide show
  1. package/CHANGELOG.md +37 -2
  2. package/CONTRIBUTING.md +6 -6
  3. package/README.md +2 -2
  4. package/dist/{acp-S5R4RR5B.js → acp-2BEHC4DL.js} +4 -4
  5. package/dist/{agents-P6DMMVZY.js → agents-LNNFTM53.js} +13 -11
  6. package/dist/assets/codewiki.json +1 -1
  7. package/dist/{auth-2XCZLPKS.js → auth-KXXFI2VS.js} +6 -6
  8. package/dist/{chunk-YCWGATWI.js → chunk-24I7BN55.js} +2 -2
  9. package/dist/{chunk-EKMEHE4H.js → chunk-33YXPOE3.js} +2 -3
  10. package/dist/chunk-3BPUFZDL.js +37 -0
  11. package/dist/{chunk-WPQLXFOZ.js → chunk-43AOLP7E.js} +2 -2
  12. package/dist/{chunk-N4CZJQRK.js → chunk-5JGRAMKL.js} +4 -4
  13. package/dist/{chunk-BRXQQJFP.js → chunk-6US73PDB.js} +568 -47
  14. package/dist/{chunk-K6WL7QZT.js → chunk-6XXKFVSN.js} +2 -2
  15. package/dist/{chunk-QQK64KLB.js → chunk-CJUB2JJ2.js} +138 -20
  16. package/dist/{chunk-HV5X7OR2.js → chunk-CKXWIANG.js} +12 -12
  17. package/dist/{chunk-UZHIZC5S.js → chunk-CYQKWTG3.js} +61 -76
  18. package/dist/{chunk-QWU7ZBO7.js → chunk-DJVECN66.js} +204 -45
  19. package/dist/{chunk-ZWMF7253.js → chunk-E2ER4LJF.js} +304 -9
  20. package/dist/{chunk-7RXG6QRZ.js → chunk-EKY57CSP.js} +2 -75
  21. package/dist/{chunk-EDRHSCIE.js → chunk-EYPA3EGJ.js} +10 -2
  22. package/dist/{chunk-TTNYS3EA.js → chunk-G7MUEIGA.js} +1 -1
  23. package/dist/{chunk-BPGS2WCQ.js → chunk-GEYXPTRF.js} +2 -1
  24. package/dist/{chunk-BEY543CS.js → chunk-GOXNB3AO.js} +5 -2
  25. package/dist/{chunk-G4BMMOKF.js → chunk-HVDIIIQW.js} +2 -2
  26. package/dist/chunk-HWUFFB6L.js +83 -0
  27. package/dist/{chunk-35MKKU5R.js → chunk-K7T3E2SR.js} +15 -8
  28. package/dist/{chunk-VAWWTKDP.js → chunk-KHSFENX2.js} +2 -2
  29. package/dist/chunk-LCGCVYZ4.js +57 -0
  30. package/dist/{chunk-X6COSD2O.js → chunk-LYF7OHWH.js} +41 -14
  31. package/dist/{chunk-POHLU5DW.js → chunk-M6L6IDJG.js} +3 -3
  32. package/dist/{chunk-X4RCMKVQ.js → chunk-NDINPTJ4.js} +2 -2
  33. package/dist/{chunk-5M54SPOL.js → chunk-ODFEOB4F.js} +161 -5
  34. package/dist/{chunk-3JLKSKD7.js → chunk-OH3TOQTB.js} +5 -1
  35. package/dist/{chunk-MEQ45TQ4.js → chunk-PBTHKCPN.js} +18 -4
  36. package/dist/{chunk-ED4KHGC3.js → chunk-PPAMZ32Z.js} +9 -2
  37. package/dist/{chunk-QQL5RT5M.js → chunk-QM3F2GKX.js} +94 -36
  38. package/dist/{chunk-A2GZF7DC.js → chunk-QNQHSOLF.js} +4 -4
  39. package/dist/{chunk-KRPY7NTG.js → chunk-R46L2BIR.js} +3 -3
  40. package/dist/{chunk-BP4OYD6A.js → chunk-RY3LY4J5.js} +20 -2
  41. package/dist/{chunk-34475P3I.js → chunk-TSHXZTOQ.js} +5 -4
  42. package/dist/{chunk-VJWL6YS5.js → chunk-UUVG37B4.js} +2 -2
  43. package/dist/{chunk-2TZWSW76.js → chunk-WHGPSPT5.js} +2 -2
  44. package/dist/{chunk-TW3WDMVS.js → chunk-WHJYKASB.js} +2 -2
  45. package/dist/{chunk-YHZX5GEU.js → chunk-XAKHZX5N.js} +2 -2
  46. package/dist/{chunk-HXG4IURW.js → chunk-XE2VEJHX.js} +2 -2
  47. package/dist/{chunk-3HZ5RWN2.js → chunk-XF5N4U5A.js} +7 -6
  48. package/dist/{chunk-ZYKPLLNQ.js → chunk-XXQNGV4M.js} +590 -32
  49. package/dist/{chunk-4JUF2NNX.js → chunk-XYDYPRZI.js} +4 -4
  50. package/dist/{chunk-VMNQ6OZA.js → chunk-ZRGEBJ4T.js} +971 -794
  51. package/dist/{chunk-2LZI5CAG.js → chunk-ZXF4XRKW.js} +75 -33
  52. package/dist/{chunk-VSNATDE6.js → chunk-ZZMN5OM4.js} +2 -2
  53. package/dist/cli/index.js +31 -31
  54. package/dist/{clio-J5JIOIDS.js → clio-M2KGYUFZ.js} +2 -2
  55. package/dist/{code-nav-AXCXSBHX.js → code-nav-GQNL7XA6.js} +5 -5
  56. package/dist/codewiki/build-worker.js +4 -4
  57. package/dist/{components-KELWS457.js → components-5TTYYX6G.js} +3 -3
  58. package/dist/{config-OEBMIN2U.js → config-XUUYQIWO.js} +27 -25
  59. package/dist/{configure-PUQOSIXQ.js → configure-IHJ7YOMV.js} +7 -7
  60. package/dist/{context-URSXPBCK.js → context-74JLXAWD.js} +12 -12
  61. package/dist/{context-MGSE4Z2T.js → context-75MIWW3U.js} +24 -22
  62. package/dist/{context-EKDCKUUZ.js → context-ZQ7SIFJV.js} +8 -7
  63. package/dist/{context-clear-KDAJRNUK.js → context-clear-GYKWNUML.js} +24 -22
  64. package/dist/{context-index-BZ4UYMTC.js → context-index-SSR5ECNE.js} +3 -3
  65. package/dist/{context-working-set-SBKMPPI2.js → context-working-set-UX5KEP4J.js} +11 -10
  66. package/dist/{dispatch-runner-MSWN72NK.js → dispatch-runner-GIJBHNFL.js} +21 -20
  67. package/dist/{docs-2C2LTVT2.js → docs-6FZSCG5B.js} +3 -3
  68. package/dist/{doctor-7BSE27PJ.js → doctor-SVJ5BZCW.js} +4 -4
  69. package/dist/{eval-IZGDOO4H.js → eval-CG6LLBLD.js} +47 -232
  70. package/dist/{evidence-SR7WXB5B.js → evidence-ZYFIEN42.js} +19 -18
  71. package/dist/{evolve-K7VE2CBX.js → evolve-QGEXEMDW.js} +19 -18
  72. package/dist/{extensions-QVDOHDGJ.js → extensions-ADGNCJJD.js} +3 -3
  73. package/dist/{fleet-7XMJNQNF.js → fleet-S5R4ZOQY.js} +49 -30
  74. package/dist/{fleet-preflight-AQNAH644.js → fleet-preflight-BHSNPBMH.js} +2 -2
  75. package/dist/{init-JGNPAYXT.js → init-5DRU55YR.js} +31 -29
  76. package/dist/memory-7YKKR6UC.js +467 -0
  77. package/dist/{models-ZMMLFJNN.js → models-ZPOLRU2C.js} +10 -10
  78. package/dist/{monitor-2F3T5KHP.js → monitor-US5F5YGZ.js} +33 -18
  79. package/dist/{orchestrator-ORHT43JB.js → orchestrator-E2AL4T5N.js} +1092 -659
  80. package/dist/{paths-UXLN5YYZ.js → paths-E7KYAQWE.js} +3 -3
  81. package/dist/{reset-NXGTYNUO.js → reset-KZ652EK6.js} +3 -3
  82. package/dist/{run-RF4WJGMT.js → run-SRNBKDWD.js} +52 -40
  83. package/dist/{share-UT3W6E4M.js → share-CGZE33UP.js} +3 -3
  84. package/dist/{skills-PSACKC5Q.js → skills-S2X4DLY5.js} +4 -4
  85. package/dist/{skills-eval-WJSI55RZ.js → skills-eval-W2GGIC4R.js} +19 -18
  86. package/dist/{targets-PIIRAOYS.js → targets-54SWINWB.js} +14 -12
  87. package/dist/{terminal-lease-ULWXWNVY.js → terminal-lease-SAIF2OGY.js} +5 -4
  88. package/dist/{uninstall-FZCQCDKC.js → uninstall-BVLWXKBT.js} +3 -3
  89. package/dist/{upgrade-346TZ6AV.js → upgrade-JKAR27XC.js} +8 -8
  90. package/dist/{usage-6KKXR32N.js → usage-MSAWCLX4.js} +60 -27
  91. package/dist/{verifiers-4UUM6TEE.js → verifiers-NCBTHHN2.js} +60 -54
  92. package/dist/{wiki-generate-7STOCIFZ.js → wiki-generate-GUSOQ6ZP.js} +30 -28
  93. package/dist/worker/entry.js +69 -58
  94. package/dist/{workspace-G4ZWUIPR.js → workspace-ZJ6BFM3Q.js} +4 -4
  95. package/docs/README.md +3 -3
  96. package/docs/acp.md +1 -1
  97. package/docs/alcf-provider.md +1 -1
  98. package/docs/architecture.md +2 -2
  99. package/docs/artifact-placement.md +1 -2
  100. package/docs/artifact-versions.md +1 -1
  101. package/docs/built-in-agents.md +1 -1
  102. package/docs/capacity-and-scheduling.md +1 -1
  103. package/docs/commands-and-modes.md +9 -7
  104. package/docs/configuration-and-targets.md +12 -1
  105. package/docs/context-engine.md +4 -2
  106. package/docs/context-working-set.md +4 -4
  107. package/docs/development-pipeline.md +1 -1
  108. package/docs/documentation-coverage.md +3 -3
  109. package/docs/documentation-guide.md +2 -2
  110. package/docs/eval-runner.md +1 -1
  111. package/docs/evals-internal.md +4 -45
  112. package/docs/evidence-and-memory.md +67 -7
  113. package/docs/evolution.md +1 -1
  114. package/docs/exit-codes-and-output.md +1 -1
  115. package/docs/extensions-and-sharing.md +2 -2
  116. package/docs/fleet-dispatch.md +28 -2
  117. package/docs/installation-and-lifecycle.md +2 -2
  118. package/docs/middleware-and-components.md +19 -2
  119. package/docs/model-catalog.md +1 -1
  120. package/docs/observability.md +3 -3
  121. package/docs/proactive-memory.md +26 -16
  122. package/docs/prompt-envelope-and-tools.md +4 -2
  123. package/docs/provider-adapter-cookbook.md +1 -1
  124. package/docs/release-cut-checklist.md +38 -35
  125. package/docs/safety-model.md +29 -7
  126. package/docs/scientific-validation.md +3 -3
  127. package/docs/session-lifecycle.md +1 -1
  128. package/docs/skills-marketplace.md +1 -1
  129. package/docs/tool-usage.md +2 -2
  130. package/docs/trace-store.md +1 -1
  131. package/docs/troubleshooting.md +1 -1
  132. package/docs/tui-design.md +38 -4
  133. package/docs/worker-dispatch-mechanics.md +1 -1
  134. package/package.json +7 -4
  135. package/src/cli/agents.ts +2 -3
  136. package/src/cli/argv.ts +14 -1
  137. package/src/cli/fleet.ts +15 -0
  138. package/src/cli/index.ts +1 -1
  139. package/src/cli/memory.ts +272 -10
  140. package/src/cli/modes/json-stream.ts +2 -2
  141. package/src/cli/modes/print.ts +12 -1
  142. package/src/cli/run.ts +22 -2
  143. package/src/cli/targets.ts +12 -3
  144. package/src/cli/usage.ts +55 -7
  145. package/src/core/bus-events.ts +3 -0
  146. package/src/core/response-model-id.ts +134 -0
  147. package/src/core/toml.ts +62 -0
  148. package/src/core/workspace-files.ts +0 -1
  149. package/src/domains/agents/builtins/architect.md +1 -1
  150. package/src/domains/agents/catalog.ts +5 -4
  151. package/src/domains/agents/recipe.ts +54 -14
  152. package/src/domains/agents/result-contract.ts +7 -4
  153. package/src/domains/context/bootstrap.ts +36 -27
  154. package/src/domains/context/project-metadata.ts +19 -63
  155. package/src/domains/context/prompt-context.ts +8 -0
  156. package/src/domains/context/working-set/policies/index.ts +3 -4
  157. package/src/domains/dispatch/budget-envelope.ts +396 -0
  158. package/src/domains/dispatch/contract.ts +2 -0
  159. package/src/domains/dispatch/extension.ts +81 -27
  160. package/src/domains/dispatch/orphan-recovery.ts +1 -0
  161. package/src/domains/dispatch/receipt-integrity.ts +4 -0
  162. package/src/domains/dispatch/state.ts +1 -0
  163. package/src/domains/dispatch/types.ts +10 -3
  164. package/src/domains/dispatch/validation.ts +14 -0
  165. package/src/domains/dispatch/worker-spawn.ts +14 -3
  166. package/src/domains/eval/metrics/evidence.ts +0 -116
  167. package/src/domains/eval/metrics/invariants.ts +1 -1
  168. package/src/domains/eval/runners/clio-run.ts +1 -10
  169. package/src/domains/eval/runners/external-command.ts +2 -29
  170. package/src/domains/eval/schema/suite.ts +0 -7
  171. package/src/domains/eval/suites/run.ts +1 -7
  172. package/src/domains/memory/index.ts +22 -0
  173. package/src/domains/memory/operations.ts +58 -1
  174. package/src/domains/memory/promotion.ts +281 -0
  175. package/src/domains/memory/prompt-section.ts +25 -5
  176. package/src/domains/memory/proposal.ts +51 -7
  177. package/src/domains/memory/task-bank.ts +3 -2
  178. package/src/domains/memory/task-memory-handoff.ts +181 -24
  179. package/src/domains/memory/task-memory-policy.ts +3 -1
  180. package/src/domains/memory/types.ts +37 -0
  181. package/src/domains/memory/validate.ts +178 -0
  182. package/src/domains/middleware/memory-intervention.ts +35 -25
  183. package/src/domains/middleware/runtime.ts +6 -0
  184. package/src/domains/middleware/skills-reminder.ts +19 -4
  185. package/src/domains/middleware/stalled-turn.ts +43 -1
  186. package/src/domains/middleware/types.ts +10 -0
  187. package/src/domains/observability/contract.ts +6 -1
  188. package/src/domains/observability/cost.ts +20 -4
  189. package/src/domains/observability/extension.ts +2 -2
  190. package/src/domains/providers/index.ts +3 -0
  191. package/src/domains/providers/model-discovery.ts +9 -0
  192. package/src/domains/providers/runtime-resolution.ts +38 -1
  193. package/src/domains/providers/runtimes/common/probe-helpers.ts +97 -16
  194. package/src/domains/providers/types/context-window-slots.ts +18 -0
  195. package/src/domains/providers/types/runtime-descriptor.ts +3 -1
  196. package/src/domains/safety/call-target.ts +211 -14
  197. package/src/domains/safety/decision-presentation.ts +268 -0
  198. package/src/domains/safety/redaction.ts +73 -0
  199. package/src/domains/session/context-ledger.ts +10 -1
  200. package/src/domains/session/decision-board.ts +4 -0
  201. package/src/domains/session/entries.ts +3 -0
  202. package/src/domains/session/history.ts +68 -19
  203. package/src/domains/session/usage.ts +24 -7
  204. package/src/engine/acp/event-mapper.ts +7 -0
  205. package/src/engine/acp/server.ts +29 -2
  206. package/src/engine/apis/lmstudio.ts +25 -4
  207. package/src/engine/apis/openai-completions.ts +147 -22
  208. package/src/engine/claude/sdk-runtime.ts +8 -2
  209. package/src/engine/claude/tool-safety.ts +13 -0
  210. package/src/engine/loop-guard.ts +27 -3
  211. package/src/engine/worker-events.ts +4 -3
  212. package/src/engine/worker-runtime.ts +59 -54
  213. package/src/entry/orchestrator.ts +18 -1
  214. package/src/interactive/chat-loop-messages.ts +22 -0
  215. package/src/interactive/chat-loop.ts +13 -0
  216. package/src/interactive/chat-renderer.ts +19 -3
  217. package/src/interactive/clio-editor.ts +44 -7
  218. package/src/interactive/context-overlay.ts +43 -5
  219. package/src/interactive/cost-overlay.ts +39 -8
  220. package/src/interactive/dispatch-board.ts +212 -35
  221. package/src/interactive/footer/widgets.ts +13 -0
  222. package/src/interactive/interactive-application.ts +6 -1
  223. package/src/interactive/interactive-input-runtime.ts +11 -1
  224. package/src/interactive/interactive-presentation.ts +11 -1
  225. package/src/interactive/memory-overlay.ts +89 -4
  226. package/src/interactive/overlay-ask-user-lifecycle.ts +1 -1
  227. package/src/interactive/overlay-frame.ts +5 -2
  228. package/src/interactive/overlay-general-openers.ts +40 -1
  229. package/src/interactive/overlay-key-routing.ts +41 -1
  230. package/src/interactive/overlay-lifecycle.ts +11 -4
  231. package/src/interactive/overlay-permission-lifecycle.ts +23 -8
  232. package/src/interactive/overlay-transitions.ts +11 -0
  233. package/src/interactive/overlays/ask-user.ts +74 -30
  234. package/src/interactive/overlays/decisions.ts +3 -1
  235. package/src/interactive/permission-hint.ts +35 -0
  236. package/src/interactive/permission-overlay.ts +95 -45
  237. package/src/interactive/renderers/tool-execution.ts +19 -49
  238. package/src/interactive/session-last-turn.ts +8 -1
  239. package/src/interactive/session-usage-reseed.ts +36 -10
  240. package/src/interactive/slash-commands.ts +2 -2
  241. package/src/interactive/status/summary.ts +5 -0
  242. package/src/interactive/status/types.ts +5 -0
  243. package/src/interactive/terminal-lease.ts +1 -0
  244. package/src/interactive/turn-context.ts +96 -23
  245. package/src/interactive/turn-middleware.ts +1 -0
  246. package/src/interactive/turn-runtime.ts +37 -8
  247. package/src/interactive/turn-state.ts +3 -0
  248. package/src/interactive/worker-progress.ts +440 -0
  249. package/src/interactive/worker-stream.ts +51 -110
  250. package/src/tools/agent-tools.ts +28 -3
  251. package/src/tools/ask-user.ts +21 -1
  252. package/src/tools/context/index.ts +2 -2
  253. package/src/tools/dispatch-arguments.ts +8 -0
  254. package/src/tools/dispatch-event-text.ts +19 -0
  255. package/src/tools/dispatch.ts +24 -1
  256. package/src/tools/monitor.ts +15 -0
  257. package/src/tools/registry.ts +15 -5
  258. package/src/tools/result-disposition.ts +156 -0
  259. package/src/tools/result-shaping.ts +59 -1
  260. package/src/tools/verify/authoring.ts +55 -54
  261. package/src/tools/worker-evidence.ts +19 -0
  262. package/src/worker/spec-contract.ts +43 -3
  263. package/dist/chunk-EFADSJET.js +0 -18
  264. package/dist/memory-4ALKDJ4Q.js +0 -246
  265. package/src/domains/eval/metrics/chaos-stream.ts +0 -93
@@ -14,6 +14,11 @@
14
14
  * only the sink wiring between that fold and the observability projection.
15
15
  */
16
16
 
17
+ import {
18
+ addResponseModelIdObservationCount,
19
+ emptyResponseModelIdObservationCounts,
20
+ type ResponseModelIdObservationCounts,
21
+ } from "../core/response-model-id.js";
17
22
  import { ledgerUsageCalls, type SessionEntry, type SessionUsageDefaults } from "../domains/session/index.js";
18
23
  import { filterEntriesToActivePath } from "../domains/session/tree/active-path.js";
19
24
 
@@ -22,7 +27,7 @@ export interface SessionUsageSink {
22
27
  resetSession(): void;
23
28
  recordTokens(
24
29
  providerId: string,
25
- modelId: string,
30
+ attributedModelId: string,
26
31
  tokens: number,
27
32
  costUsd?: number,
28
33
  breakdown?: {
@@ -35,6 +40,10 @@ export interface SessionUsageSink {
35
40
  apiCalls?: number;
36
41
  },
37
42
  costProvenance?: never,
43
+ modelIdFacts?: {
44
+ requestedModelIds: ReadonlyArray<string>;
45
+ responseModelIdObservationCounts: Readonly<ResponseModelIdObservationCounts>;
46
+ },
38
47
  ): void;
39
48
  }
40
49
 
@@ -61,14 +70,31 @@ export function reseedSessionUsageFromLedger(
61
70
  sink.resetSession();
62
71
  const scoped = filterEntriesToActivePath(entries, activeLeafTurnId ?? undefined);
63
72
  for (const call of ledgerUsageCalls(scoped, defaults)) {
64
- sink.recordTokens(call.providerId, call.modelId, call.totalTokens, call.costUsd, {
65
- input: call.input,
66
- output: call.output,
67
- cacheRead: call.cacheRead,
68
- cacheWrite: call.cacheWrite,
69
- reasoningTokens: call.reasoningTokens,
70
- totalTokens: call.totalTokens,
71
- apiCalls: call.apiCalls ?? 1,
72
- });
73
+ const responseModelIdObservationCounts = emptyResponseModelIdObservationCounts();
74
+ addResponseModelIdObservationCount(
75
+ responseModelIdObservationCounts,
76
+ call.responseModelIdObservation,
77
+ call.apiCalls ?? 1,
78
+ );
79
+ sink.recordTokens(
80
+ call.providerId,
81
+ call.attributedModelId,
82
+ call.totalTokens,
83
+ call.costUsd,
84
+ {
85
+ input: call.input,
86
+ output: call.output,
87
+ cacheRead: call.cacheRead,
88
+ cacheWrite: call.cacheWrite,
89
+ reasoningTokens: call.reasoningTokens,
90
+ totalTokens: call.totalTokens,
91
+ apiCalls: call.apiCalls ?? 1,
92
+ },
93
+ undefined,
94
+ {
95
+ requestedModelIds: [call.requestedModelId],
96
+ responseModelIdObservationCounts,
97
+ },
98
+ );
73
99
  }
74
100
  }
@@ -393,7 +393,7 @@ export interface SlashCommandContext {
393
393
  done(id: string): UserTask;
394
394
  drop(id: string): UserTask;
395
395
  };
396
- /** Open the read-only `/memory` overlay: approved lessons and the live task bank. */
396
+ /** Open `/memory` for approved lessons, the live task bank, and reviewed promotion. */
397
397
  openMemory: () => void;
398
398
  /** Opt-in import from the newest structured handoff; absent when the host has no task bank. */
399
399
  seedTaskMemory?: () => TaskMemorySeedCommandResult;
@@ -1036,7 +1036,7 @@ export const BUILTIN_SLASH_COMMANDS: ReadonlyArray<BuiltinSlashCommand> = [
1036
1036
  },
1037
1037
  {
1038
1038
  name: "memory",
1039
- description: "Inspect task memory or seed it from the newest handoff",
1039
+ description: "Inspect, promote, or seed task memory",
1040
1040
  group: "Inspect",
1041
1041
  kinds: ["memory", "memory-seed"],
1042
1042
  subcommandDescriptions: { seed: "Seed the task bank from the newest handoff" },
@@ -1,3 +1,4 @@
1
+ import { responseModelIdObservationFromRecord } from "../../core/response-model-id.js";
1
2
  import { estimateReasoningTextTokens, extractReasoningTokens } from "../../domains/session/context-accounting.js";
2
3
  import type { AgentMessage } from "../../engine/types.js";
3
4
  import type { ReasoningTokenProvenance, RunTally, TurnStopReason, TurnSummary, WatchdogTier } from "./types.js";
@@ -65,6 +66,7 @@ export function emptyRunTally(): RunTally {
65
66
  hadEstimatedReasoning: false,
66
67
  toolCount: 0,
67
68
  toolErrorCount: 0,
69
+ responseModelIdObservation: { state: "not-observed" },
68
70
  };
69
71
  }
70
72
 
@@ -85,8 +87,10 @@ export function foldMessageIntoRunTally(tally: RunTally, message: AgentMessage):
85
87
  }
86
88
  if (message.role !== "assistant") return tally;
87
89
  const usage = (message as { usage?: UsageLike }).usage;
90
+ const record = message as unknown as Record<string, unknown>;
88
91
  const next: RunTally = {
89
92
  ...tally,
93
+ responseModelIdObservation: responseModelIdObservationFromRecord(record, "not-observed"),
90
94
  inputTokens: tally.inputTokens + (usage ? finite(usage.input) : 0),
91
95
  outputTokens: tally.outputTokens + (usage ? finite(usage.output) : 0),
92
96
  cacheReadTokens: tally.cacheReadTokens + (usage ? finite(usage.cacheRead) : 0),
@@ -145,6 +149,7 @@ export function summaryFromRunTally(tally: RunTally, input: SummaryFromTallyInpu
145
149
  ...(input.stopDetail !== undefined ? { stopDetail: input.stopDetail } : {}),
146
150
  watchdogPeak: input.watchdogPeak,
147
151
  truncated: input.truncated === true,
152
+ responseModelIdObservation: tally.responseModelIdObservation,
148
153
  };
149
154
  if (tally.hadProviderReasoning || tally.hadEstimatedReasoning) {
150
155
  summary.reasoningTokens = tally.reasoningTokens;
@@ -1,4 +1,5 @@
1
1
  import type { StatusPhase, WatchdogTier } from "../../core/bus-events.js";
2
+ import type { ResponseModelIdObservation } from "../../core/response-model-id.js";
2
3
 
3
4
  // StatusPhase, WatchdogTier, and AgentStatusChangedPayload moved to
4
5
  // src/core/bus-events.ts (the phase taxonomy rides the agent.status.changed
@@ -29,6 +30,8 @@ export interface DispatchOverlay {
29
30
  export interface TurnSummary {
30
31
  elapsedMs: number;
31
32
  modelId: string;
33
+ /** Direct response model-id observation for the last API call in the turn. */
34
+ responseModelIdObservation: ResponseModelIdObservation;
32
35
  targetId: string;
33
36
  inputTokens: number;
34
37
  outputTokens: number;
@@ -70,6 +73,8 @@ export interface RunTally {
70
73
  hadEstimatedReasoning: boolean;
71
74
  toolCount: number;
72
75
  toolErrorCount: number;
76
+ /** Direct response model-id observation for the last assistant call folded. */
77
+ responseModelIdObservation: ResponseModelIdObservation;
73
78
  }
74
79
 
75
80
  export interface OverlayFrame {
@@ -189,6 +189,7 @@ export function createProcessTerminalLease(options: CreateProcessTerminalLeaseOp
189
189
  getModelLabel: () => editorChrome.getModelLabel(),
190
190
  getThinkingLabel: () => editorChrome.getThinkingLabel(),
191
191
  isStreaming: () => editorChrome.isStreaming?.() ?? false,
192
+ isAwaitingApproval: () => editorChrome.isAwaitingApproval?.() ?? false,
192
193
  willEnterSteer: (text) => editorChrome.willEnterSteer?.(text) ?? false,
193
194
  getSubmitKeyLabel: () => editorChrome.getSubmitKeyLabel?.() ?? "Enter",
194
195
  getNewlineKeyLabel: () => editorChrome.getNewlineKeyLabel?.() ?? "Shift+Enter",
@@ -23,7 +23,12 @@ import { selectVisibleEntries } from "../domains/context/working-set/visible.js"
23
23
  import type { ObservabilityContract } from "../domains/observability/contract.js";
24
24
  import type { CompiledSessionPrompt, SessionPromptInputs } from "../domains/prompts/compiler.js";
25
25
  import type { PromptsContract } from "../domains/prompts/contract.js";
26
- import type { ProvidersContract } from "../domains/providers/index.js";
26
+ import {
27
+ type ContextWindowDetails,
28
+ type ContextWindowSource,
29
+ type ProvidersContract,
30
+ resolveRuntimeTarget,
31
+ } from "../domains/providers/index.js";
27
32
  import type { LocalModelQuirks } from "../domains/providers/types/local-model-quirks.js";
28
33
  import {
29
34
  AutoCompactionTrigger,
@@ -309,6 +314,75 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
309
314
  return ceilChars(snapshot.pendingUserInput.length);
310
315
  };
311
316
 
317
+ /**
318
+ * The window the next turn will resolve to, read the way ensureRuntime
319
+ * reads it but without building an agent. Before the first turn of a
320
+ * process, /resume included, state.runtime is null, and the ledger and the
321
+ * footer meter said `context window unknown · 0 tokens` until a turn ran
322
+ * (issue #189). Null when no target is configured or it does not resolve,
323
+ * and the overlay's "unknown" is then true.
324
+ */
325
+ const resolveWindowWithoutRuntime = (): ContextWindowDetails | null => {
326
+ const settings = deps.getSettings();
327
+ const targetId = settings.orchestrator?.target?.trim();
328
+ const wireModelId = settings.orchestrator?.model?.trim();
329
+ if (!targetId || !wireModelId) return null;
330
+ const resolved = resolveRuntimeTarget(deps.providers, {
331
+ targetId,
332
+ wireModelId,
333
+ requestedThinkingLevel: settings.orchestrator?.thinkingLevel ?? "off",
334
+ use: "orchestrator",
335
+ requireTools: false,
336
+ requireOutputBudget: true,
337
+ });
338
+ return resolved.ok ? resolved.target.contextWindowDetails : null;
339
+ };
340
+
341
+ const CONTEXT_WINDOW_SOURCES: ReadonlySet<string> = new Set<ContextWindowSource>([
342
+ "catalog",
343
+ "probe",
344
+ "loaded",
345
+ "target-override",
346
+ "model-hint",
347
+ "descriptor-default",
348
+ "unknown",
349
+ ]);
350
+
351
+ /** The snapshot stores its source as a plain string; only a known label is worth repeating. */
352
+ const snapshotWindowSource = (snapshot: ContextSnapshot): ContextWindowSource | null =>
353
+ CONTEXT_WINDOW_SOURCES.has(snapshot.contextWindowSource)
354
+ ? (snapshot.contextWindowSource as ContextWindowSource)
355
+ : null;
356
+
357
+ /**
358
+ * Window facts while no runtime exists: the live resolution first, then the
359
+ * resumed snapshot's recorded window, which is what the previous process
360
+ * measured the same messages against.
361
+ */
362
+ const windowWithoutRuntime = (): {
363
+ contextWindow: number;
364
+ contextWindowSource: ContextWindowSource | null;
365
+ contextWindowSlots: ContextWindowDetails["contextWindowSlots"];
366
+ } => {
367
+ const details = resolveWindowWithoutRuntime();
368
+ if (details) {
369
+ return {
370
+ contextWindow: details.effectiveContextWindow,
371
+ contextWindowSource: details.contextWindowSource,
372
+ contextWindowSlots: details.contextWindowSlots,
373
+ };
374
+ }
375
+ const snapshot = currentContextSnapshot;
376
+ if (snapshot && snapshot.effectiveContextWindow > 0) {
377
+ return {
378
+ contextWindow: snapshot.effectiveContextWindow,
379
+ contextWindowSource: snapshotWindowSource(snapshot),
380
+ contextWindowSlots: null,
381
+ };
382
+ }
383
+ return { contextWindow: 0, contextWindowSource: null, contextWindowSlots: null };
384
+ };
385
+
312
386
  const liveContextEstimate = (agentRuntime: AgentRuntime, pendingUserText?: string): LiveContextEstimate => {
313
387
  const contextWindow = agentRuntime.runtimeResolution.contextWindowDetails.effectiveContextWindow;
314
388
  const estimateInput = {
@@ -846,8 +920,9 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
846
920
  },
847
921
 
848
922
  contextUsage(): ContextUsageSnapshot {
849
- if (!state.runtime) return contextUsageSnapshot(null, 0);
850
- const effectiveWindow = state.runtime.runtimeResolution.contextWindowDetails.effectiveContextWindow;
923
+ const effectiveWindow = state.runtime
924
+ ? state.runtime.runtimeResolution.contextWindowDetails.effectiveContextWindow
925
+ : windowWithoutRuntime().contextWindow;
851
926
  if (!currentContextSnapshot) {
852
927
  return contextUsageSnapshot(null, effectiveWindow);
853
928
  }
@@ -867,28 +942,27 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
867
942
  const settings = deps.getSettings();
868
943
  const compactionThreshold = settings.compaction?.threshold ?? null;
869
944
  const compactionAuto = settings.compaction?.auto !== false;
870
- if (!state.runtime) {
871
- return buildContextLedger({
872
- provider: settings.orchestrator?.target ?? null,
873
- model: settings.orchestrator?.model ?? null,
874
- contextWindow: 0,
875
- compactionThreshold,
876
- compactionAuto,
877
- });
878
- }
879
- const windowDetails = state.runtime.runtimeResolution.contextWindowDetails;
880
- const effectiveWindow = windowDetails.effectiveContextWindow;
881
-
882
- const provider = state.runtime.targetId ?? settings.orchestrator?.target ?? null;
883
- const model = state.runtime.wireModelId ?? settings.orchestrator?.model ?? null;
945
+ // Without a runtime (before the first turn of this process, /resume
946
+ // included) the window comes from the live resolution or the resumed
947
+ // snapshot, and the token facts from the snapshot: the resumed
948
+ // messages are the ones it measured.
949
+ const window = state.runtime
950
+ ? {
951
+ contextWindow: state.runtime.runtimeResolution.contextWindowDetails.effectiveContextWindow,
952
+ contextWindowSource: state.runtime.runtimeResolution.contextWindowDetails.contextWindowSource,
953
+ contextWindowSlots: state.runtime.runtimeResolution.contextWindowDetails.contextWindowSlots,
954
+ }
955
+ : windowWithoutRuntime();
956
+ const provider = state.runtime?.targetId ?? settings.orchestrator?.target ?? null;
957
+ const model = state.runtime?.wireModelId ?? settings.orchestrator?.model ?? null;
958
+ const liveToolCount = state.runtime?.agent.state.tools.length ?? 0;
884
959
 
885
960
  if (!currentContextSnapshot) {
886
961
  return buildContextLedger({
887
962
  provider,
888
963
  model,
889
- contextWindow: effectiveWindow,
890
- contextWindowSource: windowDetails.contextWindowSource,
891
- toolCount: state.runtime.agent.state.tools.length,
964
+ ...window,
965
+ toolCount: liveToolCount,
892
966
  compactionThreshold,
893
967
  compactionAuto,
894
968
  promptCache: lastPromptCache,
@@ -903,15 +977,14 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
903
977
  return buildContextLedger({
904
978
  provider,
905
979
  model,
906
- contextWindow: effectiveWindow,
907
- contextWindowSource: windowDetails.contextWindowSource,
980
+ ...window,
908
981
  compactionThreshold,
909
982
  compactionAuto,
910
983
  systemPromptTokens: currentContextSnapshot.categories.system,
911
984
  toolSchemaTokens: currentContextSnapshot.categories.tools,
912
985
  // Persisted snapshots strip the captured schemas; fall back to
913
986
  // the live agent state after a session resume.
914
- toolCount: currentContextSnapshot.activeToolSchemas?.length ?? state.runtime.agent.state.tools.length,
987
+ toolCount: currentContextSnapshot.activeToolSchemas?.length ?? liveToolCount,
915
988
  messageTokens: currentContextSnapshot.categories.messages,
916
989
  agentsTokens: currentContextSnapshot.categories.agents,
917
990
  skillsTokens: currentContextSnapshot.categories.skills,
@@ -205,6 +205,7 @@ export function createTurnMiddleware(deps: TurnMiddlewareDeps): TurnMiddleware {
205
205
  runtimeTier: agentRuntime.runtimeResolution.runtimeTier ?? "",
206
206
  activeToolNames: toolNamesFromAgentState(agentRuntime.agent.state.tools).join(","),
207
207
  turnToolCalls: state.turnToolCalls,
208
+ turnToolNames: state.turnToolNames.join(","),
208
209
  sharedWorkerNote: state.turnSharedWorkerNote,
209
210
  };
210
211
  if (terminalToolResult !== undefined) {
@@ -7,6 +7,7 @@
7
7
 
8
8
  import { performance } from "node:perf_hooks";
9
9
  import type { ClioSettings } from "../core/config.js";
10
+ import { attributedModelId } from "../core/response-model-id.js";
10
11
  import type { MiddlewareToolChoiceControl } from "../domains/middleware/index.js";
11
12
  import type { ObservabilityContract } from "../domains/observability/contract.js";
12
13
  import {
@@ -15,7 +16,7 @@ import {
15
16
  type ProvidersContract,
16
17
  refineRuntimeTargetWithModelHints,
17
18
  resolveRuntimeTarget,
18
- runtimeResolutionWarnings,
19
+ runtimeResolutionWarningsBesideThinkingNotice,
19
20
  type TargetStatus,
20
21
  targetRequiresAuth,
21
22
  } from "../domains/providers/index.js";
@@ -199,7 +200,12 @@ export function createTurnRuntime(deps: TurnRuntimeDeps): TurnRuntime {
199
200
  failure?.code ?? "admission-failed",
200
201
  );
201
202
  }
202
- for (const message of runtimeResolutionWarnings(resolved.diagnostics)) {
203
+ // emitThinkingClampNotice prints the one combined thinking line when the
204
+ // dial takes effect; the thinking diagnostics are its two halves.
205
+ for (const message of runtimeResolutionWarningsBesideThinkingNotice(
206
+ resolved.diagnostics,
207
+ resolved.target.modelRuntime.thinking.notice,
208
+ )) {
203
209
  const key = `${targetId}|${wireModelId}|${message}`;
204
210
  if (announcedResolutionWarnings.has(key)) continue;
205
211
  announcedResolutionWarnings.add(key);
@@ -229,6 +235,7 @@ export function createTurnRuntime(deps: TurnRuntimeDeps): TurnRuntime {
229
235
  */
230
236
  const TARGET_PROBE_TTL_MS = 5 * 60 * 1000;
231
237
  let lastTargetProbe: { key: string; at: number } | null = null;
238
+ const targetProbesInFlight = new Map<string, Promise<void>>();
232
239
  const ensureLiveCapabilitiesForSelectedModel = async (): Promise<void> => {
233
240
  const settings = deps.getSettings();
234
241
  const targetId = settings.orchestrator.target?.trim();
@@ -239,16 +246,29 @@ export function createTurnRuntime(deps: TurnRuntimeDeps): TurnRuntime {
239
246
  const runtimeDesc = deps.providers.getRuntime(target.runtime);
240
247
  if (!runtimeDesc || runtimeDesc.tier === "cloud") return;
241
248
  const key = `${targetId}|${wireModelId}`;
249
+ const inFlight = targetProbesInFlight.get(key);
250
+ if (inFlight) {
251
+ await inFlight;
252
+ return;
253
+ }
242
254
  const now = Date.now();
243
255
  if (lastTargetProbe?.key === key && now - lastTargetProbe.at < TARGET_PROBE_TTL_MS) return;
244
256
  lastTargetProbe = { key, at: now };
245
- let status: Awaited<ReturnType<ProvidersContract["probeTarget"]>> = null;
257
+ const probe = (async (): Promise<void> => {
258
+ let status: Awaited<ReturnType<ProvidersContract["probeTarget"]>> = null;
259
+ try {
260
+ status = await deps.providers.probeTarget(targetId);
261
+ } catch {
262
+ // Fall back to the last known target state.
263
+ }
264
+ announceColdModel(status, targetId, wireModelId);
265
+ })();
266
+ targetProbesInFlight.set(key, probe);
246
267
  try {
247
- status = await deps.providers.probeTarget(targetId);
248
- } catch {
249
- // Fall back to the last known target state.
268
+ await probe;
269
+ } finally {
270
+ if (targetProbesInFlight.get(key) === probe) targetProbesInFlight.delete(key);
250
271
  }
251
- announceColdModel(status, targetId, wireModelId);
252
272
  };
253
273
 
254
274
  /** One notice per target+model+state, so a repeated probe stays quiet. */
@@ -589,6 +609,7 @@ export function createTurnRuntime(deps: TurnRuntimeDeps): TurnRuntime {
589
609
  middlewareToolChoice.toolStarted(event.toolName);
590
610
  deps.toolStartTimes.set(event.toolCallId, eventClock);
591
611
  state.turnToolCalls += 1;
612
+ state.turnToolNames.push(event.toolName);
592
613
  } else if (event.type === "tool_execution_end") {
593
614
  toolsInFlight = Math.max(0, toolsInFlight - 1);
594
615
  const startedAt = deps.toolStartTimes.get(event.toolCallId);
@@ -713,7 +734,11 @@ export function createTurnRuntime(deps: TurnRuntimeDeps): TurnRuntime {
713
734
  if (summary.hadUsage && (summary.tokens > 0 || summary.costUsd > 0)) {
714
735
  deps.observability.recordTokens(
715
736
  localRuntime.targetId,
716
- localRuntime.wireModelId,
737
+ attributedModelId(
738
+ summary.lastResponseModelIdObservation,
739
+ localRuntime.wireModelId,
740
+ summary.lastDifferingResponseModelId,
741
+ ),
717
742
  summary.tokens,
718
743
  summary.costUsd,
719
744
  {
@@ -726,6 +751,10 @@ export function createTurnRuntime(deps: TurnRuntimeDeps): TurnRuntime {
726
751
  apiCalls: summary.apiCalls,
727
752
  },
728
753
  localRuntime.runtimeResolution.costProvenance,
754
+ {
755
+ requestedModelIds: [localRuntime.wireModelId],
756
+ responseModelIdObservationCounts: summary.responseModelIdObservationCounts,
757
+ },
729
758
  );
730
759
  }
731
760
  if (summary.output > 0 && firstAssistantDeltaAt !== null) {
@@ -97,6 +97,8 @@ export interface ChatTurnState {
97
97
  toolProseAssessedChars: number;
98
98
  /** Tool calls observed in the current turn; feeds turn_end metadata. */
99
99
  turnToolCalls: number;
100
+ /** Names of those calls, in order, so turn_end can tell a listing-only turn from a working one. */
101
+ turnToolNames: string[];
100
102
  /** A `[worker result]` note the operator shared entered this turn, as the prompt or as a steer; feeds turn_end metadata. */
101
103
  turnSharedWorkerNote: boolean;
102
104
  /** One stalled-turn nudge per user prompt; reset when a real prompt arrives. */
@@ -123,6 +125,7 @@ export function createTurnState(initialThinkingLevel: ThinkingLevel): ChatTurnSt
123
125
  streamStallReason: null,
124
126
  toolProseAssessedChars: 0,
125
127
  turnToolCalls: 0,
128
+ turnToolNames: [],
126
129
  turnSharedWorkerNote: false,
127
130
  stalledTurnNudgeSpent: false,
128
131
  pendingRequestContinuation: false,