tinker-agent 1.0.65

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 (110) hide show
  1. package/README.md +173 -0
  2. package/package.json +78 -0
  3. package/patches/markdansi@0.3.2.patch +37 -0
  4. package/src/agent/context-builder.ts +43 -0
  5. package/src/agent/context-meter.ts +310 -0
  6. package/src/agent/loop.ts +525 -0
  7. package/src/agent/runtime-session.ts +1212 -0
  8. package/src/agent/session-ledger.ts +828 -0
  9. package/src/agent/turn-cancellation.ts +44 -0
  10. package/src/agent/types.ts +77 -0
  11. package/src/cli/config.ts +283 -0
  12. package/src/cli/index.ts +29 -0
  13. package/src/cli/model-profiles.ts +289 -0
  14. package/src/cli/run-runner.ts +107 -0
  15. package/src/cli/tui-runner.tsx +290 -0
  16. package/src/context/compiled-context-hash.ts +138 -0
  17. package/src/context/compiled-context-validator.ts +209 -0
  18. package/src/context/context-manager.ts +362 -0
  19. package/src/context/context-policy.ts +8 -0
  20. package/src/context/context-protocol-validator.ts +463 -0
  21. package/src/context/context-revision-compiler.ts +281 -0
  22. package/src/context/context-revision.ts +111 -0
  23. package/src/context/context-source.ts +30 -0
  24. package/src/context/context-swap-renderer.ts +272 -0
  25. package/src/context/protocol-frame.ts +240 -0
  26. package/src/context/swap-planner.ts +725 -0
  27. package/src/events/append-private-file.ts +16 -0
  28. package/src/events/bash-result-detail.ts +70 -0
  29. package/src/events/composite-event-sink.ts +82 -0
  30. package/src/events/event-sink.ts +16 -0
  31. package/src/events/jsonl-event-log.ts +13 -0
  32. package/src/events/observation-text-log.ts +195 -0
  33. package/src/events/stdout-event-printer.ts +396 -0
  34. package/src/events/types.ts +263 -0
  35. package/src/ids/runtime-id.ts +68 -0
  36. package/src/ids/uuid-v7.ts +5 -0
  37. package/src/instructions/project-instructions.ts +242 -0
  38. package/src/mcp/mcp-config.ts +144 -0
  39. package/src/mcp/mcp-manager.ts +216 -0
  40. package/src/mcp/mcp-tool-executor.ts +178 -0
  41. package/src/model/committed-prefix-auditor.ts +68 -0
  42. package/src/model/fake-model-client.ts +280 -0
  43. package/src/model/model-client.ts +64 -0
  44. package/src/model/model-context-profile.ts +134 -0
  45. package/src/model/model-request-preflight.ts +120 -0
  46. package/src/model/openai-chat-mapping.ts +444 -0
  47. package/src/model/openai-chat-model-client.ts +190 -0
  48. package/src/model/prompt-prefix-hash.ts +47 -0
  49. package/src/model/token-estimator.ts +148 -0
  50. package/src/observation/observation-builder.ts +481 -0
  51. package/src/session/resume-projection.ts +616 -0
  52. package/src/session/session-catalog.ts +270 -0
  53. package/src/session/session-errors.ts +121 -0
  54. package/src/session/session-history-reader.ts +535 -0
  55. package/src/session/session-lock.ts +291 -0
  56. package/src/session/session-schema.ts +741 -0
  57. package/src/session/session-store.ts +3067 -0
  58. package/src/session/sqlite-session-ledger.ts +153 -0
  59. package/src/tools/bash-task.ts +617 -0
  60. package/src/tools/bash.ts +450 -0
  61. package/src/tools/cwd-state.ts +22 -0
  62. package/src/tools/edit.ts +428 -0
  63. package/src/tools/file-diff.ts +116 -0
  64. package/src/tools/glob.ts +202 -0
  65. package/src/tools/grep.ts +550 -0
  66. package/src/tools/hash.ts +9 -0
  67. package/src/tools/path-safety.ts +33 -0
  68. package/src/tools/read.ts +319 -0
  69. package/src/tools/recall.ts +400 -0
  70. package/src/tools/registry.ts +213 -0
  71. package/src/tools/ripgrep.ts +220 -0
  72. package/src/tools/task-list.ts +59 -0
  73. package/src/tools/task-output-snapshot.ts +47 -0
  74. package/src/tools/task-output-tool.ts +62 -0
  75. package/src/tools/task-output.ts +159 -0
  76. package/src/tools/task-stop.ts +59 -0
  77. package/src/tools/task-tool-args.ts +29 -0
  78. package/src/tools/types.ts +330 -0
  79. package/src/tools/web-fetch/backend.ts +27 -0
  80. package/src/tools/web-fetch/browser-backend.ts +126 -0
  81. package/src/tools/web-fetch/exa-backend.ts +172 -0
  82. package/src/tools/web-fetch/index.ts +298 -0
  83. package/src/tools/web-fetch/local-backend.ts +267 -0
  84. package/src/tools/web-fetch/refiner.ts +78 -0
  85. package/src/tools/web-fetch/route.ts +95 -0
  86. package/src/tools/web-search.ts +300 -0
  87. package/src/tools/write.ts +244 -0
  88. package/src/tui/app.tsx +497 -0
  89. package/src/tui/components/assistant-markdown.tsx +47 -0
  90. package/src/tui/components/background-tasks.tsx +92 -0
  91. package/src/tui/components/bash-result-view.tsx +47 -0
  92. package/src/tui/components/context-status.tsx +127 -0
  93. package/src/tui/components/diff-view.tsx +151 -0
  94. package/src/tui/components/file-viewer.tsx +212 -0
  95. package/src/tui/components/footer.tsx +60 -0
  96. package/src/tui/components/header.tsx +21 -0
  97. package/src/tui/components/model-picker.tsx +142 -0
  98. package/src/tui/components/prompt-input.tsx +432 -0
  99. package/src/tui/components/resume-session-picker.tsx +273 -0
  100. package/src/tui/components/timeline.tsx +121 -0
  101. package/src/tui/context-format.ts +24 -0
  102. package/src/tui/event-store.ts +865 -0
  103. package/src/tui/git-branch.ts +23 -0
  104. package/src/tui/line-editor.ts +157 -0
  105. package/src/tui/prompt-history.ts +94 -0
  106. package/src/tui/slash-commands.ts +126 -0
  107. package/src/tui/tui-projection-policy.ts +35 -0
  108. package/src/tui/tui-projection-store.ts +123 -0
  109. package/src/tui/tui-session-controller.ts +170 -0
  110. package/src/tui/view-file.ts +122 -0
@@ -0,0 +1,396 @@
1
+ import type { ToolCall } from "../agent/types";
2
+ import { countPatchChanges, parseDiffHunks } from "../tools/file-diff";
3
+ import type { ToolRawResult } from "../tools/types";
4
+ import { bashResultDetail } from "./bash-result-detail";
5
+ import type { EventSink } from "./event-sink";
6
+ import type { AgentEvent } from "./types";
7
+
8
+ export type WritableLike = {
9
+ write(chunk: string): unknown;
10
+ };
11
+
12
+ export class StdoutEventPrinter implements EventSink {
13
+ readonly name = "stdout-event-printer";
14
+
15
+ constructor(
16
+ private readonly stdout: WritableLike,
17
+ private readonly stderr: WritableLike,
18
+ ) {}
19
+
20
+ async append(event: AgentEvent): Promise<void> {
21
+ switch (event.type) {
22
+ case "session.started":
23
+ this.stdout.write(`session.started sessionId=${event.sessionId}\n`);
24
+ break;
25
+ case "session.resumed":
26
+ this.stdout.write(
27
+ `session.resumed sessionId=${event.sessionId} openCount=${event.data.openCount} recallIndexRebuilt=${event.data.recallIndexRebuilt}\n`,
28
+ );
29
+ break;
30
+ case "session.interrupted_frame_recovered":
31
+ this.stdout.write(
32
+ `session.interrupted_frame_recovered frameId=${event.data.frameId} completions=${event.data.syntheticCompletionCount}\n`,
33
+ );
34
+ break;
35
+ case "turn.started":
36
+ this.stdout.write(
37
+ `turn.started turn=${event.turnNumber} turnId=${event.turnId}\n`,
38
+ );
39
+ break;
40
+ case "agent.iteration.started":
41
+ this.stdout.write(
42
+ `agent.iteration.started iteration=${event.iterationNumber} iterationId=${event.iterationId}\n`,
43
+ );
44
+ break;
45
+ case "model.request.started":
46
+ this.stdout.write(`model.request.started iteration=${event.iterationNumber}\n`);
47
+ break;
48
+ case "model.request.finished":
49
+ this.stdout.write(
50
+ `model.request.finished iteration=${event.iterationNumber}\n`,
51
+ );
52
+ break;
53
+ case "context.usage.updated":
54
+ this.stdout.write(
55
+ `context.usage.updated phase=${event.data.phase} used=${event.data.snapshot.usedInputTokens} budget=${event.data.snapshot.inputBudgetTokens} source=${event.data.snapshot.source} pressure=${event.data.snapshot.pressure}\n`,
56
+ );
57
+ break;
58
+ case "context.shadow.planned":
59
+ this.stdout.write(
60
+ `context.shadow.planned outcome=${event.data.outcome} eligible=${event.data.eligibleCandidateCount} selected=${event.data.selectedCandidateCount} before=${event.data.guardedTokensBefore} after=${event.data.guardedTokensAfter ?? "n/a"}\n`,
61
+ );
62
+ break;
63
+ case "context.shadow.failed":
64
+ this.stderr.write(
65
+ `context.shadow.failed stage=${event.data.stage} code=${event.data.errorCode}\n`,
66
+ );
67
+ break;
68
+ case "context.revision.started":
69
+ this.stdout.write(
70
+ `context.revision.started reason=${event.data.reason} policy=${event.data.policyVersion}\n`,
71
+ );
72
+ break;
73
+ case "context.revision.finished":
74
+ 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`,
76
+ );
77
+ break;
78
+ case "context.revision.failed":
79
+ this.stderr.write(
80
+ `context.revision.failed stage=${event.data.stage} code=${event.data.errorCode}\n`,
81
+ );
82
+ break;
83
+ case "assistant.progress":
84
+ this.stdout.write(
85
+ `assistant.progress iteration=${event.iterationNumber}\n${event.data.content}\n`,
86
+ );
87
+ break;
88
+ case "tool.started":
89
+ this.stdout.write(formatToolLine("tool.started", event.data.call));
90
+ break;
91
+ case "tool.raw_result": {
92
+ for (const line of formatToolRawResult(event.data.call, event.data.raw)) {
93
+ this.stdout.write(line);
94
+ }
95
+ break;
96
+ }
97
+ case "tool.finished":
98
+ this.stdout.write(
99
+ `${formatToolLine("tool.finished", event.data.call).trimEnd()} ok=${event.data.ok}\n`,
100
+ );
101
+ break;
102
+ case "mcp.server.connected":
103
+ this.stdout.write(
104
+ `mcp.server.connected name=${event.data.serverName} tools=${event.data.toolCount}\n`,
105
+ );
106
+ break;
107
+ case "bash.task.backgrounded":
108
+ this.stdout.write(
109
+ `bash.task.backgrounded task=${event.data.task.taskId} status=${event.data.task.status}\n`,
110
+ );
111
+ break;
112
+ case "bash.task.stopping":
113
+ this.stdout.write(
114
+ `bash.task.stopping task=${event.data.task.taskId} status=${event.data.task.status}\n`,
115
+ );
116
+ break;
117
+ case "bash.task.finished":
118
+ this.stdout.write(
119
+ `bash.task.finished task=${event.data.task.taskId} status=${event.data.task.status}${
120
+ event.data.task.exitCode === undefined
121
+ ? ""
122
+ : ` exit=${event.data.task.exitCode}`
123
+ }${event.data.task.signal === undefined ? "" : ` signal=${event.data.task.signal}`}\n`,
124
+ );
125
+ break;
126
+ case "mcp.server.failed":
127
+ this.stderr.write(
128
+ `mcp.server.failed name=${event.data.serverName} error=${event.data.error}\n`,
129
+ );
130
+ break;
131
+ case "diagnostic.sink_failed":
132
+ this.stderr.write(
133
+ `diagnostic.sink_failed sink=${event.data.sinkName} event=${event.data.failedEventType} error=${event.data.error}\n`,
134
+ );
135
+ break;
136
+ case "turn.finished":
137
+ this.stdout.write(`turn.finished status=${event.data.status}\n`);
138
+ break;
139
+ case "turn.cancelled":
140
+ this.stdout.write(
141
+ `turn.cancelled phase=${event.data.cancellation.phase} iteration=${event.data.cancellation.iterationNumber}${
142
+ event.data.cancellation.toolName === undefined
143
+ ? ""
144
+ : ` tool=${event.data.cancellation.toolName}`
145
+ }\n`,
146
+ );
147
+ break;
148
+ case "turn.failed":
149
+ this.stderr.write(`turn.failed error=${event.data.error}\n`);
150
+ break;
151
+ case "session.finished":
152
+ this.stdout.write(`session.finished reason=${event.data.reason}\n`);
153
+ break;
154
+ default:
155
+ break;
156
+ }
157
+ }
158
+ }
159
+
160
+ function formatToolRawResult(call: ToolCall, raw: ToolRawResult): string[] {
161
+ switch (raw.kind) {
162
+ case "write":
163
+ case "edit":
164
+ return optionalLine(formatDiff(call, raw));
165
+ case "bash":
166
+ case "task_output":
167
+ return optionalLine(formatBashResult(call, raw));
168
+ case "task_list":
169
+ case "task_stop":
170
+ return optionalLine(formatTaskResult(call, raw));
171
+ case "read":
172
+ case "glob":
173
+ case "grep":
174
+ case "web_search":
175
+ case "web_fetch":
176
+ case "recall":
177
+ case "mcp":
178
+ case "generic":
179
+ return [];
180
+ default:
181
+ return assertNever(raw);
182
+ }
183
+ }
184
+
185
+ function optionalLine(line: string | undefined): string[] {
186
+ return line === undefined ? [] : [line];
187
+ }
188
+
189
+ function assertNever(value: never): never {
190
+ throw new Error(`Unhandled tool raw result: ${JSON.stringify(value)}`);
191
+ }
192
+
193
+ function formatDiff(call: ToolCall, raw: ToolRawResult): string | undefined {
194
+ if (raw.kind !== "edit" && raw.kind !== "write") {
195
+ return undefined;
196
+ }
197
+ if (!raw.ok) {
198
+ return undefined;
199
+ }
200
+
201
+ const hunks = parseDiffHunks(raw.patch);
202
+ if (hunks === undefined || hunks.length === 0) {
203
+ return undefined;
204
+ }
205
+
206
+ const changes = countPatchChanges(hunks);
207
+ const filePath = toolFilePath(call);
208
+ const lines: string[] = [
209
+ `tool.diff name=${call.name}${filePath === undefined ? "" : ` path=${filePath}`} +${changes.additions} -${changes.deletions}`,
210
+ ];
211
+
212
+ for (const hunk of hunks) {
213
+ lines.push(
214
+ `@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@`,
215
+ );
216
+ lines.push(...hunk.lines);
217
+ }
218
+
219
+ if (raw.patchTruncated === true) {
220
+ lines.push("(diff truncated)");
221
+ }
222
+
223
+ return `${lines.join("\n")}\n`;
224
+ }
225
+
226
+ function formatBashResult(_call: ToolCall, raw: ToolRawResult): string | undefined {
227
+ if (raw.kind !== "bash" && raw.kind !== "task_output") {
228
+ return undefined;
229
+ }
230
+
231
+ const detail = bashResultDetail(raw);
232
+ if (detail === undefined) {
233
+ return undefined;
234
+ }
235
+
236
+ const lines = detail.command
237
+ .split("\n")
238
+ .map((line, index) => (index === 0 ? `$ ${line}` : ` ${line}`));
239
+ lines.push(...(detail.outputPreview ?? []));
240
+
241
+ const omitted = detail.omittedOutputLines ?? 0;
242
+ if (omitted > 0) {
243
+ const location =
244
+ detail.outputFilePath === undefined
245
+ ? ""
246
+ : ` (full output: ${detail.outputFilePath})`;
247
+ lines.push(`… +${omitted} line${omitted === 1 ? "" : "s"}${location}`);
248
+ }
249
+
250
+ return `${lines.join("\n")}\n`;
251
+ }
252
+
253
+ function formatTaskResult(_call: ToolCall, raw: ToolRawResult): string | undefined {
254
+ if (!raw.ok) {
255
+ return undefined;
256
+ }
257
+
258
+ if (raw.kind === "task_list") {
259
+ return `task.list total=${raw.tasks.length} running=${raw.runningCount}\n`;
260
+ }
261
+
262
+ if (raw.kind === "task_stop") {
263
+ if (raw.status !== undefined) {
264
+ const signal = raw.task?.signal;
265
+ return `task.stop task=${raw.taskId} status=${raw.status}${signal === undefined ? "" : ` signal=${signal}`}\n`;
266
+ }
267
+ }
268
+
269
+ return undefined;
270
+ }
271
+
272
+ function formatToolLine(prefix: string, call: ToolCall): string {
273
+ if (call.name === "Bash") {
274
+ const description = bashDescription(call);
275
+ return `${prefix} name=${call.name}${description === undefined ? "" : ` desc=${description}`}\n`;
276
+ }
277
+
278
+ if (call.name === "Grep" || call.name === "Glob") {
279
+ const pattern = toolPattern(call);
280
+ return `${prefix} name=${call.name}${pattern === undefined ? "" : ` pattern=${pattern}`}\n`;
281
+ }
282
+
283
+ if (call.name === "WebSearch") {
284
+ const query = toolQuery(call);
285
+ return `${prefix} name=${call.name}${query === undefined ? "" : ` query=${query}`}\n`;
286
+ }
287
+
288
+ if (call.name === "WebFetch") {
289
+ const url = toolUrl(call);
290
+ return `${prefix} name=${call.name}${url === undefined ? "" : ` url=${url}`}\n`;
291
+ }
292
+
293
+ if (call.name === "TaskList") {
294
+ return `${prefix} name=${call.name}\n`;
295
+ }
296
+
297
+ if (call.name === "TaskOutput" || call.name === "TaskStop") {
298
+ const taskId = toolTaskId(call);
299
+ return `${prefix} name=${call.name}${taskId === undefined ? "" : ` task=${taskId}`}\n`;
300
+ }
301
+
302
+ const filePath = toolFilePath(call);
303
+ return `${prefix} name=${call.name}${filePath === undefined ? "" : ` path=${filePath}`}\n`;
304
+ }
305
+
306
+ function bashDescription(call: ToolCall): string | undefined {
307
+ if (
308
+ typeof call.args === "object" &&
309
+ call.args !== null &&
310
+ !Array.isArray(call.args)
311
+ ) {
312
+ if ("description" in call.args && typeof call.args.description === "string") {
313
+ return call.args.description;
314
+ }
315
+
316
+ if ("command" in call.args && typeof call.args.command === "string") {
317
+ return call.args.command;
318
+ }
319
+ }
320
+
321
+ return undefined;
322
+ }
323
+
324
+ function toolPattern(call: ToolCall): string | undefined {
325
+ if (
326
+ typeof call.args === "object" &&
327
+ call.args !== null &&
328
+ !Array.isArray(call.args) &&
329
+ "pattern" in call.args &&
330
+ typeof call.args.pattern === "string"
331
+ ) {
332
+ return call.args.pattern;
333
+ }
334
+
335
+ return undefined;
336
+ }
337
+
338
+ function toolQuery(call: ToolCall): string | undefined {
339
+ if (
340
+ typeof call.args === "object" &&
341
+ call.args !== null &&
342
+ !Array.isArray(call.args) &&
343
+ "query" in call.args &&
344
+ typeof call.args.query === "string"
345
+ ) {
346
+ return call.args.query;
347
+ }
348
+
349
+ return undefined;
350
+ }
351
+
352
+ function toolUrl(call: ToolCall): string | undefined {
353
+ if (
354
+ typeof call.args === "object" &&
355
+ call.args !== null &&
356
+ !Array.isArray(call.args) &&
357
+ "url" in call.args &&
358
+ typeof call.args.url === "string"
359
+ ) {
360
+ return call.args.url;
361
+ }
362
+
363
+ return undefined;
364
+ }
365
+
366
+ function toolFilePath(call: ToolCall): string | undefined {
367
+ if (
368
+ typeof call.args === "object" &&
369
+ call.args !== null &&
370
+ !Array.isArray(call.args) &&
371
+ "file_path" in call.args &&
372
+ typeof call.args.file_path === "string"
373
+ ) {
374
+ return call.args.file_path;
375
+ }
376
+
377
+ return undefined;
378
+ }
379
+
380
+ function toolTaskId(call: ToolCall): string | undefined {
381
+ const args = asRecord(call.args);
382
+ return stringProperty(args, "task_id");
383
+ }
384
+
385
+ function asRecord(value: unknown): Record<string, unknown> {
386
+ return typeof value === "object" && value !== null && !Array.isArray(value)
387
+ ? (value as Record<string, unknown>)
388
+ : {};
389
+ }
390
+
391
+ function stringProperty(
392
+ record: Record<string, unknown>,
393
+ property: string,
394
+ ): string | undefined {
395
+ return typeof record[property] === "string" ? record[property] : undefined;
396
+ }
@@ -0,0 +1,263 @@
1
+ import type {
2
+ IterationIdentity,
3
+ ToolCall,
4
+ ToolCallIdentity,
5
+ TurnCancellation,
6
+ TurnIdentity,
7
+ } from "../agent/types";
8
+ import type {
9
+ IterationId,
10
+ ProtocolFrameId,
11
+ SessionId,
12
+ ToolCallId,
13
+ TurnId,
14
+ } from "../ids/runtime-id";
15
+ import type { ModelRequestOutput } from "../model/model-client";
16
+ import type {
17
+ ModelContextBudget,
18
+ ModelContextProfile,
19
+ } from "../model/model-context-profile";
20
+ import type { ContextUsageSnapshot } from "../agent/context-meter";
21
+ import type { ShellTaskSnapshot } from "../tools/bash-task";
22
+ import type { ToolObservation } from "../observation/observation-builder";
23
+ import type { ToolRawResult } from "../tools/types";
24
+ import type {
25
+ ProjectInstructionFileName,
26
+ ProjectInstructionManifest,
27
+ } from "../instructions/project-instructions";
28
+
29
+ export type SessionStartedData = {
30
+ workspaceRoot: string;
31
+ model: string;
32
+ profileName?: string;
33
+ maxIterations: number;
34
+ includeReasoningContent: boolean;
35
+ contextProfile: ModelContextProfile;
36
+ contextBudget: ModelContextBudget;
37
+ projectInstructions: {
38
+ instruction?: ProjectInstructionManifest;
39
+ };
40
+ };
41
+
42
+ export type ContextUsageUpdatedData = {
43
+ phase: "initial" | "preflight" | "measured" | "revision";
44
+ snapshot: ContextUsageSnapshot;
45
+ };
46
+
47
+ export type ContextRevisionStartedData = {
48
+ strategy: "swap";
49
+ reason: "manual";
50
+ policyVersion: "swap-only-v1";
51
+ rendererFormat: "swap-observation-v1";
52
+ };
53
+
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;
76
+ };
77
+
78
+ export type ContextRevisionFailedData = {
79
+ strategy: "swap";
80
+ reason: "manual";
81
+ stage: "snapshot" | "plan" | "validate" | "commit" | "activate";
82
+ errorCode: string;
83
+ error: string;
84
+ };
85
+
86
+ export type ContextShadowPlannedData = {
87
+ policyVersion: "swap-only-v1";
88
+ trigger: "runtime_pressure" | "benchmark_forced";
89
+ outcome:
90
+ | "below_trigger"
91
+ | "below_target"
92
+ | "no_eligible_candidates"
93
+ | "target_reached"
94
+ | "insufficient_candidates";
95
+ canonicalMessageCount: number;
96
+ eligibleCandidateCount: number;
97
+ selectedCandidateCount: number;
98
+ excludedByReason: Record<string, number>;
99
+ selectedByRawKind: Record<string, number>;
100
+ originalObservationBytes: number;
101
+ projectedObservationBytes: number;
102
+ rawTokensBefore: number;
103
+ rawTokensAfter?: number;
104
+ guardedTokensBefore: number;
105
+ guardedTokensAfter?: number;
106
+ targetTokens: number;
107
+ planHash?: string;
108
+ durationMs: number;
109
+ };
110
+
111
+ export type ContextShadowFailedData = {
112
+ policyVersion: "swap-only-v1";
113
+ stage: "candidate" | "render" | "prepare" | "validate";
114
+ errorCode: string;
115
+ error: string;
116
+ };
117
+
118
+ export type SessionFinishedData = {
119
+ reason:
120
+ | "oneshot_complete"
121
+ | "tui_exit"
122
+ | "session_switch"
123
+ | "runner_failed"
124
+ | "initialization_failed";
125
+ error?: string;
126
+ };
127
+
128
+ export type SessionResumedData = {
129
+ openCount: number;
130
+ recoveredTurnId?: TurnId;
131
+ recoveredFrameId?: ProtocolFrameId;
132
+ syntheticCompletionCount: number;
133
+ recallIndexRebuilt: boolean;
134
+ projectInstructionFile?: ProjectInstructionFileName;
135
+ };
136
+
137
+ export type InterruptedFrameRecoveredData = {
138
+ turnId: TurnId;
139
+ frameId: ProtocolFrameId;
140
+ syntheticCompletionCount: number;
141
+ };
142
+
143
+ export type TurnFinishedData = {
144
+ status: "completed";
145
+ finalText: string;
146
+ lastIteration: IterationIdentity;
147
+ messageCount: number;
148
+ };
149
+
150
+ export type AgentEventDataMap = {
151
+ "session.started": SessionStartedData;
152
+ "session.resumed": SessionResumedData;
153
+ "session.interrupted_frame_recovered": InterruptedFrameRecoveredData;
154
+ "session.finished": SessionFinishedData;
155
+ "turn.started": { userPrompt: string };
156
+ "turn.finished": TurnFinishedData;
157
+ "turn.failed": { error: string };
158
+ "turn.cancelled": { cancellation: TurnCancellation };
159
+ "agent.iteration.started": { iterationNumber: number };
160
+ "model.request.started": Record<string, never>;
161
+ "model.request.finished": { output: ModelRequestOutput };
162
+ "context.usage.updated": ContextUsageUpdatedData;
163
+ "context.revision.started": ContextRevisionStartedData;
164
+ "context.revision.finished": ContextRevisionFinishedData;
165
+ "context.revision.failed": ContextRevisionFailedData;
166
+ "context.shadow.planned": ContextShadowPlannedData;
167
+ "context.shadow.failed": ContextShadowFailedData;
168
+ "assistant.progress": { content: string };
169
+ "tool.started": { call: ToolCall };
170
+ "tool.raw_result": { call: ToolCall; raw: ToolRawResult };
171
+ "tool.finished": { call: ToolCall; ok: boolean };
172
+ "tool.observation": { call: ToolCall; observation: ToolObservation };
173
+ "agent.iteration.finished": {
174
+ outcome: "continue" | "completed";
175
+ toolCallCount: number;
176
+ };
177
+ "bash.task.backgrounded": { task: ShellTaskSnapshot };
178
+ "bash.task.stopping": { task: ShellTaskSnapshot };
179
+ "bash.task.finished": { task: ShellTaskSnapshot };
180
+ "mcp.server.connected": { serverName: string; toolCount: number };
181
+ "mcp.server.failed": { serverName: string; error: string };
182
+ "diagnostic.sink_failed": {
183
+ sinkName: string;
184
+ failedEventType: AgentEventType;
185
+ error: string;
186
+ };
187
+ };
188
+
189
+ export type AgentEventType = keyof AgentEventDataMap;
190
+
191
+ type AgentEventEnvelope<TType extends AgentEventType> = {
192
+ type: TType;
193
+ sessionId: SessionId;
194
+ turnId?: TurnId;
195
+ iterationId?: IterationId;
196
+ toolCallId?: ToolCallId;
197
+ turnNumber?: number;
198
+ iterationNumber?: number;
199
+ toolCallNumber?: number;
200
+ eventSequence: number;
201
+ timestamp: string;
202
+ data: AgentEventDataMap[TType];
203
+ };
204
+
205
+ export type AgentEvent = {
206
+ [TType in AgentEventType]: AgentEventEnvelope<TType>;
207
+ }[AgentEventType];
208
+
209
+ type SessionEventInput<TType extends AgentEventType> = {
210
+ type: TType;
211
+ sessionId: SessionId;
212
+ data: AgentEventDataMap[TType];
213
+ };
214
+
215
+ type TurnEventInput<TType extends AgentEventType> = TurnIdentity & {
216
+ type: TType;
217
+ data: AgentEventDataMap[TType];
218
+ };
219
+
220
+ type IterationEventInput<TType extends AgentEventType> = IterationIdentity & {
221
+ type: TType;
222
+ data: AgentEventDataMap[TType];
223
+ };
224
+
225
+ type ToolEventInput<TType extends AgentEventType> = ToolCallIdentity & {
226
+ type: TType;
227
+ data: AgentEventDataMap[TType];
228
+ };
229
+
230
+ export type AgentEventInput =
231
+ | SessionEventInput<
232
+ | "session.started"
233
+ | "session.resumed"
234
+ | "session.interrupted_frame_recovered"
235
+ | "session.finished"
236
+ | "context.usage.updated"
237
+ | "context.revision.started"
238
+ | "context.revision.finished"
239
+ | "context.revision.failed"
240
+ >
241
+ | SessionEventInput<"mcp.server.connected" | "mcp.server.failed">
242
+ | SessionEventInput<"diagnostic.sink_failed">
243
+ | TurnEventInput<"turn.started" | "turn.finished">
244
+ | (
245
+ | TurnEventInput<"turn.failed" | "turn.cancelled">
246
+ | IterationEventInput<"turn.failed" | "turn.cancelled">
247
+ )
248
+ | IterationEventInput<
249
+ | "agent.iteration.started"
250
+ | "model.request.started"
251
+ | "model.request.finished"
252
+ | "context.usage.updated"
253
+ | "context.shadow.planned"
254
+ | "context.shadow.failed"
255
+ | "assistant.progress"
256
+ | "agent.iteration.finished"
257
+ >
258
+ | ToolEventInput<
259
+ "tool.started" | "tool.raw_result" | "tool.finished" | "tool.observation"
260
+ >
261
+ | ToolEventInput<
262
+ "bash.task.backgrounded" | "bash.task.stopping" | "bash.task.finished"
263
+ >;