tinker-agent 1.0.65 → 1.2.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 (208) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/LICENSE +201 -0
  3. package/NOTICE +6 -0
  4. package/README.md +90 -9
  5. package/bin/tinker.js +39 -0
  6. package/node_modules/ansi-regex/index.d.ts +33 -0
  7. package/node_modules/ansi-regex/index.js +14 -0
  8. package/node_modules/ansi-regex/license +9 -0
  9. package/node_modules/ansi-regex/package.json +61 -0
  10. package/node_modules/ansi-regex/readme.md +66 -0
  11. package/node_modules/ansi-styles/index.d.ts +236 -0
  12. package/node_modules/ansi-styles/index.js +223 -0
  13. package/node_modules/ansi-styles/license +9 -0
  14. package/node_modules/ansi-styles/package.json +54 -0
  15. package/node_modules/ansi-styles/readme.md +173 -0
  16. package/node_modules/chalk/license +9 -0
  17. package/node_modules/chalk/package.json +83 -0
  18. package/node_modules/chalk/readme.md +297 -0
  19. package/node_modules/chalk/source/index.d.ts +325 -0
  20. package/node_modules/chalk/source/index.js +225 -0
  21. package/node_modules/chalk/source/utilities.js +33 -0
  22. package/node_modules/chalk/source/vendor/ansi-styles/index.d.ts +236 -0
  23. package/node_modules/chalk/source/vendor/ansi-styles/index.js +223 -0
  24. package/node_modules/chalk/source/vendor/supports-color/browser.d.ts +1 -0
  25. package/node_modules/chalk/source/vendor/supports-color/browser.js +34 -0
  26. package/node_modules/chalk/source/vendor/supports-color/index.d.ts +55 -0
  27. package/node_modules/chalk/source/vendor/supports-color/index.js +190 -0
  28. package/node_modules/character-entities/index.d.ts +6 -0
  29. package/node_modules/character-entities/index.js +2132 -0
  30. package/node_modules/character-entities/license +22 -0
  31. package/node_modules/character-entities/package.json +78 -0
  32. package/node_modules/character-entities/readme.md +152 -0
  33. package/node_modules/decode-named-character-reference/index.d.ts +13 -0
  34. package/node_modules/decode-named-character-reference/index.d.ts.map +1 -0
  35. package/node_modules/decode-named-character-reference/index.dom.d.ts +6 -0
  36. package/node_modules/decode-named-character-reference/index.dom.d.ts.map +1 -0
  37. package/node_modules/decode-named-character-reference/index.dom.js +32 -0
  38. package/node_modules/decode-named-character-reference/index.js +19 -0
  39. package/node_modules/decode-named-character-reference/license +22 -0
  40. package/node_modules/decode-named-character-reference/package.json +90 -0
  41. package/node_modules/decode-named-character-reference/readme.md +136 -0
  42. package/node_modules/get-east-asian-width/index.d.ts +60 -0
  43. package/node_modules/get-east-asian-width/index.js +30 -0
  44. package/node_modules/get-east-asian-width/license +9 -0
  45. package/node_modules/get-east-asian-width/lookup-data.js +21 -0
  46. package/node_modules/get-east-asian-width/lookup.js +138 -0
  47. package/node_modules/get-east-asian-width/package.json +71 -0
  48. package/node_modules/get-east-asian-width/readme.md +65 -0
  49. package/node_modules/get-east-asian-width/utilities.js +24 -0
  50. package/node_modules/has-flag/index.d.ts +39 -0
  51. package/node_modules/has-flag/index.js +8 -0
  52. package/node_modules/has-flag/license +9 -0
  53. package/node_modules/has-flag/package.json +49 -0
  54. package/node_modules/has-flag/readme.md +74 -0
  55. package/node_modules/is-fullwidth-code-point/index.d.ts +17 -0
  56. package/node_modules/is-fullwidth-code-point/index.js +12 -0
  57. package/node_modules/is-fullwidth-code-point/license +9 -0
  58. package/node_modules/is-fullwidth-code-point/package.json +53 -0
  59. package/node_modules/is-fullwidth-code-point/readme.md +31 -0
  60. package/node_modules/markdansi/LICENSE +21 -0
  61. package/node_modules/markdansi/README.md +167 -0
  62. package/node_modules/markdansi/dist/ast.d.ts +99 -0
  63. package/node_modules/markdansi/dist/ast.js +1 -0
  64. package/node_modules/markdansi/dist/cli.d.ts +17 -0
  65. package/node_modules/markdansi/dist/cli.js +208 -0
  66. package/node_modules/markdansi/dist/hyperlink.d.ts +9 -0
  67. package/node_modules/markdansi/dist/hyperlink.js +26 -0
  68. package/node_modules/markdansi/dist/index.d.ts +10 -0
  69. package/node_modules/markdansi/dist/index.js +16 -0
  70. package/node_modules/markdansi/dist/parser.d.ts +2 -0
  71. package/node_modules/markdansi/dist/parser.js +205 -0
  72. package/node_modules/markdansi/dist/render.d.ts +9 -0
  73. package/node_modules/markdansi/dist/render.js +680 -0
  74. package/node_modules/markdansi/dist/stream.d.ts +39 -0
  75. package/node_modules/markdansi/dist/stream.js +192 -0
  76. package/node_modules/markdansi/dist/theme.d.ts +18 -0
  77. package/node_modules/markdansi/dist/theme.js +105 -0
  78. package/node_modules/markdansi/dist/types.d.ts +58 -0
  79. package/node_modules/markdansi/dist/types.js +1 -0
  80. package/node_modules/markdansi/dist/wrap.d.ts +10 -0
  81. package/node_modules/markdansi/dist/wrap.js +73 -0
  82. package/node_modules/markdansi/docs/spec.md +110 -0
  83. package/node_modules/markdansi/package.json +78 -0
  84. package/node_modules/markdansi/tsconfig.json +18 -0
  85. package/node_modules/marked/LICENSE +44 -0
  86. package/node_modules/marked/README.md +115 -0
  87. package/node_modules/marked/bin/main.js +284 -0
  88. package/node_modules/marked/bin/marked.js +16 -0
  89. package/node_modules/marked/lib/marked.d.ts +760 -0
  90. package/node_modules/marked/lib/marked.esm.js +77 -0
  91. package/node_modules/marked/lib/marked.esm.js.map +7 -0
  92. package/node_modules/marked/lib/marked.umd.js +79 -0
  93. package/node_modules/marked/lib/marked.umd.js.map +7 -0
  94. package/node_modules/marked/man/marked.1 +113 -0
  95. package/node_modules/marked/man/marked.1.md +93 -0
  96. package/node_modules/marked/package.json +103 -0
  97. package/node_modules/slice-ansi/index.d.ts +19 -0
  98. package/node_modules/slice-ansi/index.js +317 -0
  99. package/node_modules/slice-ansi/license +10 -0
  100. package/node_modules/slice-ansi/package.json +59 -0
  101. package/node_modules/slice-ansi/readme.md +55 -0
  102. package/node_modules/slice-ansi/tokenize-ansi.js +752 -0
  103. package/node_modules/string-width/index.d.ts +39 -0
  104. package/node_modules/string-width/index.js +203 -0
  105. package/node_modules/string-width/license +9 -0
  106. package/node_modules/string-width/package.json +65 -0
  107. package/node_modules/string-width/readme.md +66 -0
  108. package/node_modules/strip-ansi/index.d.ts +15 -0
  109. package/node_modules/strip-ansi/index.js +19 -0
  110. package/node_modules/strip-ansi/license +9 -0
  111. package/node_modules/strip-ansi/package.json +59 -0
  112. package/node_modules/strip-ansi/readme.md +37 -0
  113. package/node_modules/supports-color/browser.d.ts +1 -0
  114. package/node_modules/supports-color/browser.js +35 -0
  115. package/node_modules/supports-color/index.d.ts +55 -0
  116. package/node_modules/supports-color/index.js +202 -0
  117. package/node_modules/supports-color/license +9 -0
  118. package/node_modules/supports-color/package.json +64 -0
  119. package/node_modules/supports-color/readme.md +75 -0
  120. package/node_modules/supports-hyperlinks/browser.js +10 -0
  121. package/node_modules/supports-hyperlinks/index.d.ts +25 -0
  122. package/node_modules/supports-hyperlinks/index.js +163 -0
  123. package/node_modules/supports-hyperlinks/license +10 -0
  124. package/node_modules/supports-hyperlinks/package.json +47 -0
  125. package/node_modules/supports-hyperlinks/readme.md +45 -0
  126. package/package.json +41 -10
  127. package/patches/markdansi@0.3.2.patch +22 -2
  128. package/src/agent/context-builder.ts +24 -10
  129. package/src/agent/context-meter.ts +89 -5
  130. package/src/agent/loop.ts +107 -10
  131. package/src/agent/runtime-session.ts +1437 -82
  132. package/src/agent/session-ledger.ts +169 -29
  133. package/src/agent/types.ts +12 -1
  134. package/src/agent/user-prompt-projection.ts +62 -0
  135. package/src/cli/config.ts +57 -14
  136. package/src/cli/model-profiles.ts +124 -0
  137. package/src/cli/run-runner.ts +4 -1
  138. package/src/cli/tui-runner.tsx +68 -30
  139. package/src/context/compiled-context-hash.ts +1 -1
  140. package/src/context/compiled-context-validator.ts +92 -15
  141. package/src/context/context-automation-policy.ts +121 -0
  142. package/src/context/context-manager.ts +390 -11
  143. package/src/context/context-policy.ts +8 -0
  144. package/src/context/context-protocol-validator.ts +11 -1
  145. package/src/context/context-revision-compiler.ts +190 -27
  146. package/src/context/context-revision.ts +105 -19
  147. package/src/context/context-surface.ts +329 -0
  148. package/src/context/prefix-retirement-planner.ts +570 -0
  149. package/src/context/protocol-frame.ts +30 -3
  150. package/src/context/recall-retirement-contract.ts +27 -0
  151. package/src/context/swap-planner.ts +34 -10
  152. package/src/events/observation-text-log.ts +3 -3
  153. package/src/events/stdout-event-printer.ts +39 -3
  154. package/src/events/types.ts +172 -35
  155. package/src/ids/runtime-id.ts +6 -0
  156. package/src/ids/uuid-v7.ts +7 -0
  157. package/src/image/image-asset-store.ts +319 -0
  158. package/src/image/image-input-policy.ts +14 -0
  159. package/src/image/image-probe.ts +233 -0
  160. package/src/image/image-types.ts +262 -0
  161. package/src/instructions/project-instructions.ts +4 -1
  162. package/src/mcp/mcp-manager.ts +83 -19
  163. package/src/model/fake-model-client.ts +9 -1
  164. package/src/model/input-token-estimator.ts +25 -0
  165. package/src/model/model-client.ts +60 -0
  166. package/src/model/model-request-preflight.ts +1 -0
  167. package/src/model/moonshot-input-token-estimator.ts +107 -0
  168. package/src/model/openai-chat-mapping.ts +77 -1
  169. package/src/model/openai-chat-model-client.ts +389 -59
  170. package/src/model/openai-chat-stream.ts +272 -0
  171. package/src/model/prompt-prefix-hash.ts +2 -4
  172. package/src/model/token-estimator.ts +4 -0
  173. package/src/observation/observation-builder.ts +29 -0
  174. package/src/session/resume-projection.ts +94 -34
  175. package/src/session/session-catalog.ts +4 -4
  176. package/src/session/session-errors.ts +1 -1
  177. package/src/session/session-history-reader.ts +151 -4
  178. package/src/session/session-last-response-reader.ts +189 -0
  179. package/src/session/session-schema.ts +349 -35
  180. package/src/session/session-store.ts +3266 -255
  181. package/src/session/sqlite-session-ledger.ts +9 -4
  182. package/src/skills/skill-catalog.ts +182 -0
  183. package/src/skills/skill-context.ts +364 -0
  184. package/src/skills/skill-loader.ts +779 -0
  185. package/src/skills/skill-tool.ts +297 -0
  186. package/src/tools/edit.ts +51 -64
  187. package/src/tools/ensure-parent-directory.ts +6 -0
  188. package/src/tools/read.ts +2 -2
  189. package/src/tools/recall.ts +43 -40
  190. package/src/tools/registry.ts +24 -3
  191. package/src/tools/ripgrep.ts +3 -2
  192. package/src/tools/types.ts +43 -4
  193. package/src/tools/write.ts +20 -31
  194. package/src/tui/app.tsx +333 -96
  195. package/src/tui/clipboard.ts +5 -0
  196. package/src/tui/components/mcp-panel.tsx +28 -0
  197. package/src/tui/components/prompt-input.tsx +790 -257
  198. package/src/tui/components/skills-panel.tsx +31 -0
  199. package/src/tui/components/timeline.tsx +30 -1
  200. package/src/tui/event-store.ts +85 -2
  201. package/src/tui/file-mention.ts +234 -0
  202. package/src/tui/line-editor.ts +1 -0
  203. package/src/tui/project-slash-commands.ts +229 -0
  204. package/src/tui/prompt-draft.ts +510 -0
  205. package/src/tui/prompt-history.ts +294 -22
  206. package/src/tui/slash-commands.ts +46 -2
  207. package/src/tui/tui-session-controller.ts +110 -48
  208. package/src/tui/workspace-file-search.ts +94 -0
@@ -14,8 +14,9 @@ import { CompiledContextError } from "./compiled-context-validator";
14
14
  import type { SwapOnlyPolicyV1 } from "./context-policy";
15
15
  import { ContextProtocolError } from "./context-protocol-validator";
16
16
  import type {
17
+ BuiltContextRequest,
17
18
  CompiledRevisionContext,
18
- StoredContextRevisionV5,
19
+ StoredContextRevisionV8,
19
20
  SwapOverride,
20
21
  } from "./context-revision";
21
22
  import {
@@ -48,13 +49,14 @@ export type SwapRevisionPlan = {
48
49
  readonly policyVersion: "swap-only-v1";
49
50
  readonly baseRevisionId: CompiledRevisionContext["revisionId"];
50
51
  readonly baseRevisionNumber: number;
52
+ readonly baseKeepFromOrdinal: number;
51
53
  readonly baseCanonicalThroughOrdinal: number;
52
- readonly baseOverrideManifestSha256: string;
54
+ readonly baseActiveOverrideManifestSha256: string;
53
55
  readonly basePrefixHash: string;
54
56
  readonly requestConfigHash: string;
55
57
  readonly toolSchemaHash: string;
56
58
  readonly addedOverrides: readonly SwapOverride[];
57
- readonly nextOverrideManifestSha256: string;
59
+ readonly nextActiveOverrideManifestSha256: string;
58
60
  readonly targetTokens: number;
59
61
  readonly rawTokensBefore: number;
60
62
  readonly rawTokensAfter: number;
@@ -66,7 +68,8 @@ export type SwapRevisionPlan = {
66
68
 
67
69
  export type SwapPlanningInput = {
68
70
  readonly active: CompiledRevisionContext;
69
- readonly revision: StoredContextRevisionV5;
71
+ readonly revision: StoredContextRevisionV8;
72
+ readonly surface: BuiltContextRequest["surface"];
70
73
  readonly activeOverrides: readonly SwapOverride[];
71
74
  readonly canonical: ProtocolContextView;
72
75
  readonly activePrepared: PreparedModelRequest;
@@ -176,6 +179,7 @@ export class SwapPlanner {
176
179
  input.canonical,
177
180
  input.activeOverrides,
178
181
  input.policy,
182
+ input.revision.keepFromOrdinal,
179
183
  );
180
184
  if (scan.eligible.length === 0) {
181
185
  return {
@@ -206,10 +210,12 @@ export class SwapPlanner {
206
210
  canonical: input.canonical,
207
211
  activeOverrides: input.activeOverrides,
208
212
  addedOverrides,
213
+ activeSurface: input.surface,
209
214
  });
210
215
  const built = this.requestBuilder.build({
211
216
  canonical: input.canonical,
212
217
  revision: input.revision,
218
+ surface: input.surface,
213
219
  activeOverrides: [...input.activeOverrides, ...addedOverrides],
214
220
  compiled,
215
221
  tools: input.tools,
@@ -344,6 +350,7 @@ export class SwapPlanner {
344
350
  canonical: ProtocolContextView,
345
351
  activeOverrides: readonly SwapOverride[],
346
352
  policy: SwapOnlyPolicyV1,
353
+ keepFromOrdinal: number,
347
354
  ): CandidateScan {
348
355
  const alreadySwapped = new Set(
349
356
  activeOverrides.map((override) => override.messageId),
@@ -367,6 +374,10 @@ export class SwapPlanner {
367
374
  if (message.role !== "tool") {
368
375
  continue;
369
376
  }
377
+ if (message.ordinal < keepFromOrdinal) {
378
+ increment(exclusions, "retired_prefix");
379
+ continue;
380
+ }
370
381
  if (alreadySwapped.has(message.messageId)) {
371
382
  increment(exclusions, "already_swapped");
372
383
  continue;
@@ -428,7 +439,7 @@ export function assertPlanBaseCurrent(
428
439
  plan: SwapRevisionPlan,
429
440
  current: {
430
441
  readonly active: CompiledRevisionContext;
431
- readonly revision: StoredContextRevisionV5;
442
+ readonly revision: StoredContextRevisionV8;
432
443
  readonly activeOverrides: readonly SwapOverride[];
433
444
  readonly activePrepared: PreparedModelRequest;
434
445
  },
@@ -438,8 +449,9 @@ export function assertPlanBaseCurrent(
438
449
  plan.baseRevisionId !== current.active.revisionId ||
439
450
  plan.baseRevisionId !== current.revision.revisionId ||
440
451
  plan.baseRevisionNumber !== current.revision.revisionNumber ||
452
+ plan.baseKeepFromOrdinal !== current.revision.keepFromOrdinal ||
441
453
  plan.baseCanonicalThroughOrdinal !== current.active.canonicalThroughOrdinal ||
442
- plan.baseOverrideManifestSha256 !==
454
+ plan.baseActiveOverrideManifestSha256 !==
443
455
  activeOverrideManifestHash(current.activeOverrides) ||
444
456
  plan.basePrefixHash !== fingerprint.prefixHash ||
445
457
  plan.requestConfigHash !== fingerprint.requestConfigHash ||
@@ -463,14 +475,24 @@ function validatePlanningInput(input: SwapPlanningInput): void {
463
475
  input.active.sessionId !== input.canonical.sessionId ||
464
476
  input.revision.sessionId !== input.canonical.sessionId ||
465
477
  input.revision.revisionId !== input.active.revisionId ||
478
+ input.revision.keepFromOrdinal !== input.active.manifest.keepFromOrdinal ||
466
479
  input.active.canonicalThroughOrdinal !== input.canonical.messages.length ||
467
- input.revision.overrideManifestSha256 !==
480
+ input.revision.activeOverrideManifestSha256 !==
468
481
  activeOverrideManifestHash(input.activeOverrides)
469
482
  ) {
470
483
  throw new ContextRevisionError(
471
484
  "Active revision does not match canonical planning input.",
472
485
  );
473
486
  }
487
+ if (
488
+ input.activeOverrides.some(
489
+ (override) => override.ordinal < input.revision.keepFromOrdinal,
490
+ )
491
+ ) {
492
+ throw new ContextRevisionError(
493
+ "Active swap planning input contains a retired override.",
494
+ );
495
+ }
474
496
  if (
475
497
  input.forcedTargetTokens !== undefined &&
476
498
  (!Number.isSafeInteger(input.forcedTargetTokens) || input.forcedTargetTokens < 0)
@@ -654,7 +676,7 @@ function createPlan(input: {
654
676
  guardedTokensAfter: number;
655
677
  }): SwapRevisionPlan {
656
678
  const planningInput = input.input;
657
- const nextOverrideManifestSha256 = activeOverrideManifestHash([
679
+ const nextActiveOverrideManifestSha256 = activeOverrideManifestHash([
658
680
  ...planningInput.activeOverrides,
659
681
  ...input.addedOverrides,
660
682
  ]);
@@ -663,8 +685,10 @@ function createPlan(input: {
663
685
  policyVersion: "swap-only-v1",
664
686
  baseRevisionId: planningInput.active.revisionId,
665
687
  baseRevisionNumber: planningInput.revision.revisionNumber,
688
+ baseKeepFromOrdinal: planningInput.revision.keepFromOrdinal,
666
689
  baseCanonicalThroughOrdinal: planningInput.active.canonicalThroughOrdinal,
667
- baseOverrideManifestSha256: planningInput.revision.overrideManifestSha256,
690
+ baseActiveOverrideManifestSha256:
691
+ planningInput.revision.activeOverrideManifestSha256,
668
692
  basePrefixHash: input.activeFingerprint.prefixHash,
669
693
  requestConfigHash: input.activeFingerprint.requestConfigHash,
670
694
  toolSchemaHash: input.activeFingerprint.toolSchemaHash,
@@ -673,7 +697,7 @@ function createPlan(input: {
673
697
  originalContentSha256: override.originalContentSha256,
674
698
  renderedContentSha256: override.renderedContentSha256,
675
699
  })),
676
- nextOverrideManifestSha256,
700
+ nextActiveOverrideManifestSha256,
677
701
  targetTokens: input.targetTokens,
678
702
  projectedPrefixHash: input.projectedFingerprint.prefixHash,
679
703
  } as const;
@@ -10,7 +10,7 @@ export class ObservationTextLog implements EventSink {
10
10
  constructor(private readonly filePath: string) {}
11
11
 
12
12
  async append(event: AgentEvent): Promise<void> {
13
- const text = renderObservationLogBlock(event);
13
+ const text = renderObservationLogEvent(event);
14
14
  if (text === undefined) {
15
15
  return;
16
16
  }
@@ -19,7 +19,7 @@ export class ObservationTextLog implements EventSink {
19
19
  }
20
20
  }
21
21
 
22
- function renderObservationLogBlock(event: AgentEvent): string | undefined {
22
+ export function renderObservationLogEvent(event: AgentEvent): string | undefined {
23
23
  switch (event.type) {
24
24
  case "session.started":
25
25
  return renderSessionStarted(event);
@@ -72,7 +72,7 @@ function renderTurnStarted(
72
72
  return [
73
73
  `## Turn ${event.turnNumber ?? ""} - Prompt`,
74
74
  "",
75
- event.data.userPrompt,
75
+ event.data.userPrompt.text,
76
76
  "",
77
77
  "---",
78
78
  "",
@@ -24,7 +24,7 @@ export class StdoutEventPrinter implements EventSink {
24
24
  break;
25
25
  case "session.resumed":
26
26
  this.stdout.write(
27
- `session.resumed sessionId=${event.sessionId} openCount=${event.data.openCount} recallIndexRebuilt=${event.data.recallIndexRebuilt}\n`,
27
+ `session.resumed sessionId=${event.sessionId} openCount=${event.data.openCount} recallIndexRebuilt=${event.data.recallIndexRebuilt}${event.data.contextRefresh === undefined ? "" : ` contextRevision=${event.data.contextRefresh.revisionNumber}`}\n`,
28
28
  );
29
29
  break;
30
30
  case "session.interrupted_frame_recovered":
@@ -67,12 +67,24 @@ export class StdoutEventPrinter implements EventSink {
67
67
  break;
68
68
  case "context.revision.started":
69
69
  this.stdout.write(
70
- `context.revision.started reason=${event.data.reason} policy=${event.data.policyVersion}\n`,
70
+ event.data.strategy === "swap"
71
+ ? `context.revision.started reason=${event.data.reason} policy=${event.data.policyVersion}\n`
72
+ : event.data.strategy === "surface_refresh"
73
+ ? `context.revision.started reason=${event.data.reason} strategy=surface_refresh changed=${event.data.changed.join(",")}\n`
74
+ : event.data.strategy === "skills_update"
75
+ ? `context.revision.started reason=${event.data.reason} strategy=skills_update names=${event.data.names.join(",")}\n`
76
+ : `context.revision.started reason=${event.data.reason} strategy=retire_prefix policy=${event.data.policyVersion}\n`,
71
77
  );
72
78
  break;
73
79
  case "context.revision.finished":
74
80
  this.stdout.write(
75
- `context.revision.finished outcome=${event.data.outcome} revision=${event.data.revisionNumber ?? event.data.baseRevisionNumber} added=${event.data.addedOverrideCount} before=${event.data.guardedTokensBefore} after=${event.data.guardedTokensAfter ?? "n/a"}\n`,
81
+ event.data.strategy === "swap"
82
+ ? `context.revision.finished outcome=${event.data.outcome} revision=${event.data.revisionNumber ?? event.data.baseRevisionNumber} added=${event.data.addedOverrideCount} before=${event.data.guardedTokensBefore} after=${event.data.guardedTokensAfter ?? "n/a"}\n`
83
+ : event.data.strategy === "surface_refresh"
84
+ ? `context.revision.finished strategy=surface_refresh revision=${event.data.revisionNumber} changed=${event.data.changed.join(",")} tools=${event.data.toolCountBefore}->${event.data.toolCountAfter}\n`
85
+ : event.data.strategy === "skills_update"
86
+ ? `context.revision.finished strategy=skills_update revision=${event.data.revisionNumber} activated=${event.data.activated.join(",")} refreshed=${event.data.refreshed.join(",")} deactivated=${event.data.deactivated.join(",")} unavailable=${event.data.unavailable.join(",")}\n`
87
+ : `context.revision.finished strategy=retire_prefix outcome=${event.data.outcome} revision=${event.data.revisionNumber ?? event.data.baseRevisionNumber} retired_turns=${event.data.retiredTurnCount} before=${event.data.guardedTokensBefore} after=${event.data.guardedTokensAfter ?? "n/a"}\n`,
76
88
  );
77
89
  break;
78
90
  case "context.revision.failed":
@@ -151,6 +163,16 @@ export class StdoutEventPrinter implements EventSink {
151
163
  case "session.finished":
152
164
  this.stdout.write(`session.finished reason=${event.data.reason}\n`);
153
165
  break;
166
+ case "skills.catalog.loaded":
167
+ this.stdout.write(
168
+ `skills.catalog.loaded available=${event.data.availableCount} active=${event.data.activeNames.join(",")} shadowed=${event.data.shadowedNames.join(",")}\n`,
169
+ );
170
+ break;
171
+ case "skills.updated":
172
+ this.stdout.write(
173
+ `skills.updated reason=${event.data.reason} activated=${event.data.activated.join(",")} refreshed=${event.data.refreshed.join(",")} deactivated=${event.data.deactivated.join(",")} unavailable=${event.data.unavailable.join(",")}\n`,
174
+ );
175
+ break;
154
176
  default:
155
177
  break;
156
178
  }
@@ -168,6 +190,8 @@ function formatToolRawResult(call: ToolCall, raw: ToolRawResult): string[] {
168
190
  case "task_list":
169
191
  case "task_stop":
170
192
  return optionalLine(formatTaskResult(call, raw));
193
+ case "skill":
194
+ return [formatSkillResult(raw)];
171
195
  case "read":
172
196
  case "glob":
173
197
  case "grep":
@@ -182,6 +206,18 @@ function formatToolRawResult(call: ToolCall, raw: ToolRawResult): string[] {
182
206
  }
183
207
  }
184
208
 
209
+ function formatSkillResult(raw: Extract<ToolRawResult, { kind: "skill" }>): string {
210
+ if (!raw.ok) {
211
+ return `skill ${raw.name || "(unknown)"} failed -> ${boundedToolError(raw.error)}\n`;
212
+ }
213
+ return `skill ${raw.name} ${raw.status.replaceAll("_", " ")}\n`;
214
+ }
215
+
216
+ function boundedToolError(error: string): string {
217
+ const limit = 1_000;
218
+ return error.length <= limit ? error : `${error.slice(0, limit)}…`;
219
+ }
220
+
185
221
  function optionalLine(line: string | undefined): string[] {
186
222
  return line === undefined ? [] : [line];
187
223
  }
@@ -5,6 +5,7 @@ import type {
5
5
  TurnCancellation,
6
6
  TurnIdentity,
7
7
  } from "../agent/types";
8
+ import type { UserPromptProjection } from "../agent/user-prompt-projection";
8
9
  import type {
9
10
  IterationId,
10
11
  ProtocolFrameId,
@@ -25,6 +26,7 @@ import type {
25
26
  ProjectInstructionFileName,
26
27
  ProjectInstructionManifest,
27
28
  } from "../instructions/project-instructions";
29
+ import type { ContextSurfaceComponent } from "../context/context-surface";
28
30
 
29
31
  export type SessionStartedData = {
30
32
  workspaceRoot: string;
@@ -44,43 +46,165 @@ export type ContextUsageUpdatedData = {
44
46
  snapshot: ContextUsageSnapshot;
45
47
  };
46
48
 
47
- export type ContextRevisionStartedData = {
48
- strategy: "swap";
49
- reason: "manual";
50
- policyVersion: "swap-only-v1";
51
- rendererFormat: "swap-observation-v1";
52
- };
49
+ export type ContextRevisionStartedData =
50
+ | {
51
+ strategy: "swap";
52
+ reason: "manual" | "runtime_pressure";
53
+ policyVersion: "swap-only-v1";
54
+ rendererFormat: "swap-observation-v1";
55
+ qualificationId?: string;
56
+ }
57
+ | {
58
+ strategy: "surface_refresh";
59
+ reason: "resume";
60
+ baseRevisionNumber: number;
61
+ changed: readonly ContextSurfaceComponent[];
62
+ }
63
+ | {
64
+ strategy: "retire_prefix";
65
+ reason: "manual" | "runtime_pressure";
66
+ policyVersion: "recall-first-retirement-v1";
67
+ baseRevisionNumber: number;
68
+ qualificationId?: string;
69
+ }
70
+ | {
71
+ strategy: "skills_update";
72
+ reason: "activation" | "resume";
73
+ baseRevisionNumber: number;
74
+ names: readonly string[];
75
+ };
53
76
 
54
- export type ContextRevisionFinishedData = {
55
- strategy: "swap";
56
- reason: "manual";
57
- policyVersion: "swap-only-v1";
58
- outcome:
59
- | "below_target"
60
- | "no_eligible_candidates"
61
- | "target_reached"
62
- | "insufficient_candidates";
63
- baseRevisionNumber: number;
64
- revisionNumber?: number;
65
- addedOverrideCount: number;
66
- totalOverrideCount: number;
67
- originalObservationBytes: number;
68
- projectedObservationBytes: number;
69
- rawTokensBefore: number;
70
- rawTokensAfter?: number;
71
- guardedTokensBefore: number;
72
- guardedTokensAfter?: number;
73
- targetTokens: number;
74
- planHash?: string;
75
- durationMs: number;
77
+ export type ContextRevisionFinishedData =
78
+ | {
79
+ strategy: "swap";
80
+ reason: "manual" | "runtime_pressure";
81
+ policyVersion: "swap-only-v1";
82
+ outcome:
83
+ | "below_trigger"
84
+ | "below_target"
85
+ | "no_eligible_candidates"
86
+ | "target_reached"
87
+ | "insufficient_candidates";
88
+ baseRevisionNumber: number;
89
+ revisionNumber?: number;
90
+ addedOverrideCount: number;
91
+ activeOverrideCount: number;
92
+ originalObservationBytes: number;
93
+ projectedObservationBytes: number;
94
+ rawTokensBefore: number;
95
+ rawTokensAfter?: number;
96
+ guardedTokensBefore: number;
97
+ guardedTokensAfter?: number;
98
+ targetTokens: number;
99
+ planHash?: string;
100
+ durationMs: number;
101
+ qualificationId?: string;
102
+ }
103
+ | {
104
+ strategy: "surface_refresh";
105
+ reason: "resume";
106
+ baseRevisionNumber: number;
107
+ revisionNumber: number;
108
+ changed: readonly ContextSurfaceComponent[];
109
+ toolCountBefore: number;
110
+ toolCountAfter: number;
111
+ measuredAnchorCleared: true;
112
+ durationMs: number;
113
+ }
114
+ | {
115
+ strategy: "retire_prefix";
116
+ reason: "manual" | "runtime_pressure";
117
+ policyVersion: "recall-first-retirement-v1";
118
+ outcome:
119
+ | "below_target"
120
+ | "no_complete_prefix"
121
+ | "target_reached"
122
+ | "retirement_floor";
123
+ baseRevisionNumber: number;
124
+ revisionNumber?: number;
125
+ previousKeepFromOrdinal: number;
126
+ keepFromOrdinal: number;
127
+ retiredTurnCount: number;
128
+ retiredFrameCount: number;
129
+ retiredMessageCount: number;
130
+ activeOverrideCount: number;
131
+ rawTokensBefore?: number;
132
+ rawTokensAfter?: number;
133
+ guardedTokensBefore: number;
134
+ guardedTokensAfter?: number;
135
+ targetTokens: number;
136
+ planHash?: string;
137
+ planningDurationMs: number;
138
+ validationDurationMs?: number;
139
+ transactionDurationMs?: number;
140
+ activationDurationMs?: number;
141
+ durationMs: number;
142
+ qualificationId?: string;
143
+ }
144
+ | {
145
+ strategy: "skills_update";
146
+ reason: "activation" | "resume";
147
+ baseRevisionNumber: number;
148
+ revisionNumber: number;
149
+ activated: readonly string[];
150
+ refreshed: readonly string[];
151
+ deactivated: readonly string[];
152
+ unavailable: readonly string[];
153
+ addedOverrideCount: number;
154
+ measuredAnchorCleared: true;
155
+ durationMs: number;
156
+ };
157
+
158
+ export type ContextRevisionFailedData =
159
+ | {
160
+ strategy: "swap";
161
+ reason: "manual" | "runtime_pressure";
162
+ stage: "snapshot" | "plan" | "validate" | "commit" | "activate";
163
+ errorCode: string;
164
+ error: string;
165
+ qualificationId?: string;
166
+ }
167
+ | {
168
+ strategy: "surface_refresh";
169
+ reason: "resume";
170
+ stage: "prepare" | "commit" | "activate";
171
+ errorCode: string;
172
+ error: string;
173
+ committed: boolean;
174
+ }
175
+ | {
176
+ strategy: "retire_prefix";
177
+ reason: "manual" | "runtime_pressure";
178
+ stage: "snapshot" | "plan" | "validate" | "commit" | "activate";
179
+ errorCode: string;
180
+ error: string;
181
+ committed: boolean;
182
+ qualificationId?: string;
183
+ }
184
+ | {
185
+ strategy: "skills_update";
186
+ reason: "activation" | "resume";
187
+ stage: "prepare" | "commit" | "activate";
188
+ errorCode: string;
189
+ error: string;
190
+ committed: boolean;
191
+ };
192
+
193
+ export type SkillsCatalogLoadedData = {
194
+ availableCount: number;
195
+ projectCount: number;
196
+ userCount: number;
197
+ activeNames: readonly string[];
198
+ shadowedNames: readonly string[];
76
199
  };
77
200
 
78
- export type ContextRevisionFailedData = {
79
- strategy: "swap";
80
- reason: "manual";
81
- stage: "snapshot" | "plan" | "validate" | "commit" | "activate";
82
- errorCode: string;
83
- error: string;
201
+ export type SkillsUpdatedData = {
202
+ reason: "activation" | "resume";
203
+ activated: readonly string[];
204
+ refreshed: readonly string[];
205
+ deactivated: readonly string[];
206
+ unavailable: readonly string[];
207
+ revisionNumber?: number;
84
208
  };
85
209
 
86
210
  export type ContextShadowPlannedData = {
@@ -131,7 +255,16 @@ export type SessionResumedData = {
131
255
  recoveredFrameId?: ProtocolFrameId;
132
256
  syntheticCompletionCount: number;
133
257
  recallIndexRebuilt: boolean;
258
+ contextProfile: ModelContextProfile;
259
+ contextBudget: ModelContextBudget;
134
260
  projectInstructionFile?: ProjectInstructionFileName;
261
+ contextRefresh?: {
262
+ previousRevisionNumber: number;
263
+ revisionNumber: number;
264
+ changed: readonly ContextSurfaceComponent[];
265
+ toolCountBefore: number;
266
+ toolCountAfter: number;
267
+ };
135
268
  };
136
269
 
137
270
  export type InterruptedFrameRecoveredData = {
@@ -152,7 +285,7 @@ export type AgentEventDataMap = {
152
285
  "session.resumed": SessionResumedData;
153
286
  "session.interrupted_frame_recovered": InterruptedFrameRecoveredData;
154
287
  "session.finished": SessionFinishedData;
155
- "turn.started": { userPrompt: string };
288
+ "turn.started": { userPrompt: UserPromptProjection };
156
289
  "turn.finished": TurnFinishedData;
157
290
  "turn.failed": { error: string };
158
291
  "turn.cancelled": { cancellation: TurnCancellation };
@@ -165,6 +298,8 @@ export type AgentEventDataMap = {
165
298
  "context.revision.failed": ContextRevisionFailedData;
166
299
  "context.shadow.planned": ContextShadowPlannedData;
167
300
  "context.shadow.failed": ContextShadowFailedData;
301
+ "skills.catalog.loaded": SkillsCatalogLoadedData;
302
+ "skills.updated": SkillsUpdatedData;
168
303
  "assistant.progress": { content: string };
169
304
  "tool.started": { call: ToolCall };
170
305
  "tool.raw_result": { call: ToolCall; raw: ToolRawResult };
@@ -237,6 +372,8 @@ export type AgentEventInput =
237
372
  | "context.revision.started"
238
373
  | "context.revision.finished"
239
374
  | "context.revision.failed"
375
+ | "skills.catalog.loaded"
376
+ | "skills.updated"
240
377
  >
241
378
  | SessionEventInput<"mcp.server.connected" | "mcp.server.failed">
242
379
  | SessionEventInput<"diagnostic.sink_failed">
@@ -1,4 +1,5 @@
1
1
  import { createUuidV7 } from "./uuid-v7";
2
+ import type { ImageAttachmentId } from "../image/image-types";
2
3
 
3
4
  export type RuntimeId<Name extends string> = string & {
4
5
  readonly __runtimeId: Name;
@@ -11,6 +12,7 @@ export type ToolCallId = RuntimeId<"tool-call">;
11
12
  export type MessageId = RuntimeId<"message">;
12
13
  export type ProtocolFrameId = RuntimeId<"protocol-frame">;
13
14
  export type ContextRevisionId = RuntimeId<"context-revision">;
15
+ export type ContextSurfaceId = RuntimeId<"context-surface">;
14
16
 
15
17
  export type RuntimeIdFactory = {
16
18
  createSessionId(): SessionId;
@@ -20,6 +22,8 @@ export type RuntimeIdFactory = {
20
22
  createMessageId(): MessageId;
21
23
  createProtocolFrameId(): ProtocolFrameId;
22
24
  createContextRevisionId(): ContextRevisionId;
25
+ createContextSurfaceId(): ContextSurfaceId;
26
+ createImageAttachmentId(): ImageAttachmentId;
23
27
  };
24
28
 
25
29
  export const runtimeIdFactory: RuntimeIdFactory = {
@@ -30,6 +34,8 @@ export const runtimeIdFactory: RuntimeIdFactory = {
30
34
  createMessageId: () => createUuidV7() as MessageId,
31
35
  createProtocolFrameId: () => createUuidV7() as ProtocolFrameId,
32
36
  createContextRevisionId: () => createUuidV7() as ContextRevisionId,
37
+ createContextSurfaceId: () => createUuidV7() as ContextSurfaceId,
38
+ createImageAttachmentId: () => createUuidV7() as ImageAttachmentId,
33
39
  };
34
40
 
35
41
  const canonicalUuidV7Pattern =
@@ -1,5 +1,12 @@
1
1
  import { v7 as uuidv7 } from "uuid";
2
2
 
3
+ const CANONICAL_UUID_V7_PATTERN =
4
+ /^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
5
+
3
6
  export function createUuidV7(): string {
4
7
  return uuidv7();
5
8
  }
9
+
10
+ export function isCanonicalUuidV7(value: string): boolean {
11
+ return CANONICAL_UUID_V7_PATTERN.test(value);
12
+ }