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.
- package/README.md +173 -0
- package/package.json +78 -0
- package/patches/markdansi@0.3.2.patch +37 -0
- package/src/agent/context-builder.ts +43 -0
- package/src/agent/context-meter.ts +310 -0
- package/src/agent/loop.ts +525 -0
- package/src/agent/runtime-session.ts +1212 -0
- package/src/agent/session-ledger.ts +828 -0
- package/src/agent/turn-cancellation.ts +44 -0
- package/src/agent/types.ts +77 -0
- package/src/cli/config.ts +283 -0
- package/src/cli/index.ts +29 -0
- package/src/cli/model-profiles.ts +289 -0
- package/src/cli/run-runner.ts +107 -0
- package/src/cli/tui-runner.tsx +290 -0
- package/src/context/compiled-context-hash.ts +138 -0
- package/src/context/compiled-context-validator.ts +209 -0
- package/src/context/context-manager.ts +362 -0
- package/src/context/context-policy.ts +8 -0
- package/src/context/context-protocol-validator.ts +463 -0
- package/src/context/context-revision-compiler.ts +281 -0
- package/src/context/context-revision.ts +111 -0
- package/src/context/context-source.ts +30 -0
- package/src/context/context-swap-renderer.ts +272 -0
- package/src/context/protocol-frame.ts +240 -0
- package/src/context/swap-planner.ts +725 -0
- package/src/events/append-private-file.ts +16 -0
- package/src/events/bash-result-detail.ts +70 -0
- package/src/events/composite-event-sink.ts +82 -0
- package/src/events/event-sink.ts +16 -0
- package/src/events/jsonl-event-log.ts +13 -0
- package/src/events/observation-text-log.ts +195 -0
- package/src/events/stdout-event-printer.ts +396 -0
- package/src/events/types.ts +263 -0
- package/src/ids/runtime-id.ts +68 -0
- package/src/ids/uuid-v7.ts +5 -0
- package/src/instructions/project-instructions.ts +242 -0
- package/src/mcp/mcp-config.ts +144 -0
- package/src/mcp/mcp-manager.ts +216 -0
- package/src/mcp/mcp-tool-executor.ts +178 -0
- package/src/model/committed-prefix-auditor.ts +68 -0
- package/src/model/fake-model-client.ts +280 -0
- package/src/model/model-client.ts +64 -0
- package/src/model/model-context-profile.ts +134 -0
- package/src/model/model-request-preflight.ts +120 -0
- package/src/model/openai-chat-mapping.ts +444 -0
- package/src/model/openai-chat-model-client.ts +190 -0
- package/src/model/prompt-prefix-hash.ts +47 -0
- package/src/model/token-estimator.ts +148 -0
- package/src/observation/observation-builder.ts +481 -0
- package/src/session/resume-projection.ts +616 -0
- package/src/session/session-catalog.ts +270 -0
- package/src/session/session-errors.ts +121 -0
- package/src/session/session-history-reader.ts +535 -0
- package/src/session/session-lock.ts +291 -0
- package/src/session/session-schema.ts +741 -0
- package/src/session/session-store.ts +3067 -0
- package/src/session/sqlite-session-ledger.ts +153 -0
- package/src/tools/bash-task.ts +617 -0
- package/src/tools/bash.ts +450 -0
- package/src/tools/cwd-state.ts +22 -0
- package/src/tools/edit.ts +428 -0
- package/src/tools/file-diff.ts +116 -0
- package/src/tools/glob.ts +202 -0
- package/src/tools/grep.ts +550 -0
- package/src/tools/hash.ts +9 -0
- package/src/tools/path-safety.ts +33 -0
- package/src/tools/read.ts +319 -0
- package/src/tools/recall.ts +400 -0
- package/src/tools/registry.ts +213 -0
- package/src/tools/ripgrep.ts +220 -0
- package/src/tools/task-list.ts +59 -0
- package/src/tools/task-output-snapshot.ts +47 -0
- package/src/tools/task-output-tool.ts +62 -0
- package/src/tools/task-output.ts +159 -0
- package/src/tools/task-stop.ts +59 -0
- package/src/tools/task-tool-args.ts +29 -0
- package/src/tools/types.ts +330 -0
- package/src/tools/web-fetch/backend.ts +27 -0
- package/src/tools/web-fetch/browser-backend.ts +126 -0
- package/src/tools/web-fetch/exa-backend.ts +172 -0
- package/src/tools/web-fetch/index.ts +298 -0
- package/src/tools/web-fetch/local-backend.ts +267 -0
- package/src/tools/web-fetch/refiner.ts +78 -0
- package/src/tools/web-fetch/route.ts +95 -0
- package/src/tools/web-search.ts +300 -0
- package/src/tools/write.ts +244 -0
- package/src/tui/app.tsx +497 -0
- package/src/tui/components/assistant-markdown.tsx +47 -0
- package/src/tui/components/background-tasks.tsx +92 -0
- package/src/tui/components/bash-result-view.tsx +47 -0
- package/src/tui/components/context-status.tsx +127 -0
- package/src/tui/components/diff-view.tsx +151 -0
- package/src/tui/components/file-viewer.tsx +212 -0
- package/src/tui/components/footer.tsx +60 -0
- package/src/tui/components/header.tsx +21 -0
- package/src/tui/components/model-picker.tsx +142 -0
- package/src/tui/components/prompt-input.tsx +432 -0
- package/src/tui/components/resume-session-picker.tsx +273 -0
- package/src/tui/components/timeline.tsx +121 -0
- package/src/tui/context-format.ts +24 -0
- package/src/tui/event-store.ts +865 -0
- package/src/tui/git-branch.ts +23 -0
- package/src/tui/line-editor.ts +157 -0
- package/src/tui/prompt-history.ts +94 -0
- package/src/tui/slash-commands.ts +126 -0
- package/src/tui/tui-projection-policy.ts +35 -0
- package/src/tui/tui-projection-store.ts +123 -0
- package/src/tui/tui-session-controller.ts +170 -0
- package/src/tui/view-file.ts +122 -0
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
import type { ToolCall } from "../agent/types";
|
|
2
|
+
import type {
|
|
3
|
+
BashRawResult,
|
|
4
|
+
EditFileRawResult,
|
|
5
|
+
GenericToolRawResult,
|
|
6
|
+
GlobRawResult,
|
|
7
|
+
GrepRawResult,
|
|
8
|
+
McpToolRawResult,
|
|
9
|
+
ReadFileRawResult,
|
|
10
|
+
RecallRawResult,
|
|
11
|
+
TaskListRawResult,
|
|
12
|
+
TaskOutputRawResult,
|
|
13
|
+
TaskStopRawResult,
|
|
14
|
+
ToolRawResult,
|
|
15
|
+
WebFetchRawResult,
|
|
16
|
+
WebSearchRawResult,
|
|
17
|
+
WriteFileRawResult,
|
|
18
|
+
} from "../tools/types";
|
|
19
|
+
|
|
20
|
+
export type ToolObservation = {
|
|
21
|
+
content: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export class ObservationBuilder {
|
|
25
|
+
build(input: { call: ToolCall; raw: ToolRawResult }): ToolObservation {
|
|
26
|
+
switch (input.raw.kind) {
|
|
27
|
+
case "glob":
|
|
28
|
+
return { content: renderGlobObservation(input.raw) };
|
|
29
|
+
case "grep":
|
|
30
|
+
return { content: renderGrepObservation(input.raw) };
|
|
31
|
+
case "read":
|
|
32
|
+
return { content: renderReadObservation(input.raw) };
|
|
33
|
+
case "recall":
|
|
34
|
+
return { content: renderRecallObservation(input.raw) };
|
|
35
|
+
case "write":
|
|
36
|
+
return { content: renderWriteObservation(input.raw) };
|
|
37
|
+
case "edit":
|
|
38
|
+
return { content: renderEditObservation(input.raw) };
|
|
39
|
+
case "bash":
|
|
40
|
+
return { content: renderBashObservation(input.raw) };
|
|
41
|
+
case "task_list":
|
|
42
|
+
return { content: renderTaskListObservation(input.raw) };
|
|
43
|
+
case "task_output":
|
|
44
|
+
return { content: renderTaskOutputObservation(input.raw) };
|
|
45
|
+
case "task_stop":
|
|
46
|
+
return { content: renderTaskStopObservation(input.raw) };
|
|
47
|
+
case "web_search":
|
|
48
|
+
return { content: renderWebSearchObservation(input.raw) };
|
|
49
|
+
case "web_fetch":
|
|
50
|
+
return { content: renderWebFetchObservation(input.raw) };
|
|
51
|
+
case "mcp":
|
|
52
|
+
return { content: renderMcpObservation(input.raw) };
|
|
53
|
+
case "generic":
|
|
54
|
+
return { content: renderGenericObservation(input.raw) };
|
|
55
|
+
default:
|
|
56
|
+
return assertNever(input.raw);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function assertNever(value: never): never {
|
|
62
|
+
throw new Error(`Unhandled tool raw result: ${JSON.stringify(value)}`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function renderGlobObservation(raw: GlobRawResult): string {
|
|
66
|
+
if (!raw.ok) {
|
|
67
|
+
return `Glob failed for pattern=${JSON.stringify(raw.pattern)}: ${raw.error ?? "Unknown error."}`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const matches = raw.matches ?? [];
|
|
71
|
+
|
|
72
|
+
return [
|
|
73
|
+
`Glob succeeded for pattern=${JSON.stringify(raw.pattern)}.`,
|
|
74
|
+
`searchPath=${raw.searchPath}`,
|
|
75
|
+
`matchCount=${raw.matchCount ?? matches.length}`,
|
|
76
|
+
`ignored=${(raw.ignored ?? []).join(",")}`,
|
|
77
|
+
"matches:",
|
|
78
|
+
matches.length === 0 ? "(no matches)" : matches.join("\n"),
|
|
79
|
+
].join("\n");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function renderGrepObservation(raw: GrepRawResult): string {
|
|
83
|
+
if (!raw.ok) {
|
|
84
|
+
return `Grep failed for pattern=${JSON.stringify(raw.pattern)}: ${raw.error ?? "Unknown error."}`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const sections: string[] = [];
|
|
88
|
+
|
|
89
|
+
if (raw.mode === "files_with_matches") {
|
|
90
|
+
sections.push(
|
|
91
|
+
raw.numFiles === 0
|
|
92
|
+
? "No files found"
|
|
93
|
+
: [
|
|
94
|
+
`Found ${raw.numFiles} file${raw.numFiles === 1 ? "" : "s"}`,
|
|
95
|
+
...raw.filenames,
|
|
96
|
+
].join("\n"),
|
|
97
|
+
);
|
|
98
|
+
} else if (raw.mode === "count") {
|
|
99
|
+
if (raw.numFiles === 0) {
|
|
100
|
+
sections.push("No matches found");
|
|
101
|
+
} else {
|
|
102
|
+
sections.push(raw.content ?? "");
|
|
103
|
+
sections.push(
|
|
104
|
+
`Found ${raw.numMatches ?? 0} total occurrence${(raw.numMatches ?? 0) === 1 ? "" : "s"} across ${raw.numFiles} file${raw.numFiles === 1 ? "" : "s"}.`,
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
} else {
|
|
108
|
+
sections.push(
|
|
109
|
+
raw.content === undefined || raw.content === ""
|
|
110
|
+
? "No matches found"
|
|
111
|
+
: raw.content,
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const pagination = renderGrepPagination(raw);
|
|
116
|
+
if (pagination !== undefined) {
|
|
117
|
+
sections.push(pagination);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (raw.truncated === true && raw.error !== undefined) {
|
|
121
|
+
sections.push(`Warning: results are incomplete. ${raw.error}`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return sections.join("\n\n");
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function renderGrepPagination(raw: GrepRawResult): string | undefined {
|
|
128
|
+
const parts: string[] = [];
|
|
129
|
+
|
|
130
|
+
if (raw.appliedLimit !== undefined) {
|
|
131
|
+
parts.push(`limit: ${raw.appliedLimit}`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (raw.appliedOffset !== undefined) {
|
|
135
|
+
parts.push(`offset: ${raw.appliedOffset}`);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return parts.length === 0
|
|
139
|
+
? undefined
|
|
140
|
+
: `[Showing results with pagination = ${parts.join(", ")}]`;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function renderReadObservation(raw: ReadFileRawResult): string {
|
|
144
|
+
if (!raw.ok) {
|
|
145
|
+
return `Read failed for ${raw.filePath || "(unknown path)"}: ${raw.error ?? "Unknown error."}`;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const range =
|
|
149
|
+
raw.startLine !== undefined && raw.endLine !== undefined
|
|
150
|
+
? `lines ${raw.startLine}-${raw.endLine}`
|
|
151
|
+
: "empty file";
|
|
152
|
+
|
|
153
|
+
return [
|
|
154
|
+
`Read succeeded for ${raw.filePath}.`,
|
|
155
|
+
`sha256=${raw.sha256}`,
|
|
156
|
+
`sizeBytes=${raw.sizeBytes ?? 0}`,
|
|
157
|
+
`contentBytes=${raw.contentBytes ?? 0}`,
|
|
158
|
+
`totalLines=${raw.totalLines ?? 0}`,
|
|
159
|
+
`displayed=${range}`,
|
|
160
|
+
"content:",
|
|
161
|
+
raw.content ?? "",
|
|
162
|
+
].join("\n");
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function renderRecallObservation(raw: RecallRawResult): string {
|
|
166
|
+
if (!raw.ok) {
|
|
167
|
+
return `Recall ${raw.mode} failed (${raw.errorCode}): ${raw.error}`;
|
|
168
|
+
}
|
|
169
|
+
if (raw.mode === "get") {
|
|
170
|
+
const page = raw.page;
|
|
171
|
+
return [
|
|
172
|
+
"Recall retrieved historical session data.",
|
|
173
|
+
"historical=true",
|
|
174
|
+
`source=${page.source}`,
|
|
175
|
+
`role=${page.role}`,
|
|
176
|
+
page.toolName === undefined ? undefined : `toolName=${page.toolName}`,
|
|
177
|
+
`turnNumber=${page.turnNumber}`,
|
|
178
|
+
`ordinal=${page.ordinal}`,
|
|
179
|
+
`createdAt=${page.createdAt}`,
|
|
180
|
+
`contentSha256=${page.contentSha256}`,
|
|
181
|
+
`totalBytes=${page.totalBytes}`,
|
|
182
|
+
`byteOffset=${page.byteOffset}`,
|
|
183
|
+
`returnedBytes=${page.returnedBytes}`,
|
|
184
|
+
`nextByteOffset=${page.nextByteOffset ?? "null"}`,
|
|
185
|
+
"currentWorkspaceGuidance=Use Read/Grep to verify current files; this content is historical.",
|
|
186
|
+
"content:",
|
|
187
|
+
page.content,
|
|
188
|
+
]
|
|
189
|
+
.filter((line): line is string => line !== undefined)
|
|
190
|
+
.join("\n");
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const page = raw.page;
|
|
194
|
+
const header = [
|
|
195
|
+
"Recall searched historical session data.",
|
|
196
|
+
"historical=true",
|
|
197
|
+
`query=${JSON.stringify(raw.query)}`,
|
|
198
|
+
`strategy=${page.strategy}`,
|
|
199
|
+
`snapshotThroughOrdinal=${page.snapshotThroughOrdinal}`,
|
|
200
|
+
`offset=${page.offset}`,
|
|
201
|
+
`limit=${page.limit}`,
|
|
202
|
+
`nextOffset=${page.nextOffset ?? "null"}`,
|
|
203
|
+
`matchesReturned=${page.hits.length}`,
|
|
204
|
+
].join("\n");
|
|
205
|
+
if (page.hits.length === 0) {
|
|
206
|
+
return `${header}\n\nNo matches were found in the current session for the supplied query, filters, and search snapshot. This does not prove that the information does not exist.`;
|
|
207
|
+
}
|
|
208
|
+
const hits = page.hits.map((hit, index) =>
|
|
209
|
+
[
|
|
210
|
+
`[${index + 1}]`,
|
|
211
|
+
`source=${hit.source}`,
|
|
212
|
+
`role=${hit.role}`,
|
|
213
|
+
hit.toolName === undefined ? undefined : `toolName=${hit.toolName}`,
|
|
214
|
+
`turnNumber=${hit.turnNumber}`,
|
|
215
|
+
`ordinal=${hit.ordinal}`,
|
|
216
|
+
`createdAt=${hit.createdAt}`,
|
|
217
|
+
`contentSha256=${hit.contentSha256}`,
|
|
218
|
+
"excerpt:",
|
|
219
|
+
hit.excerpt,
|
|
220
|
+
]
|
|
221
|
+
.filter((line): line is string => line !== undefined)
|
|
222
|
+
.join("\n"),
|
|
223
|
+
);
|
|
224
|
+
return [header, ...hits].join("\n\n");
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function renderWriteObservation(raw: WriteFileRawResult): string {
|
|
228
|
+
if (!raw.ok) {
|
|
229
|
+
const guidance = raw.requiredReadBeforeWrite
|
|
230
|
+
? " Call Read on this file before trying Write again."
|
|
231
|
+
: "";
|
|
232
|
+
return `Write failed for ${raw.filePath || "(unknown path)"}: ${raw.error ?? "Unknown error."}${guidance}`;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return [
|
|
236
|
+
`Write succeeded for ${raw.filePath}.`,
|
|
237
|
+
`bytesWritten=${raw.bytesWritten ?? 0}`,
|
|
238
|
+
`oldSha256=${raw.oldSha256 ?? "null"}`,
|
|
239
|
+
`newSha256=${raw.newSha256}`,
|
|
240
|
+
].join("\n");
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function renderEditObservation(raw: EditFileRawResult): string {
|
|
244
|
+
if (!raw.ok) {
|
|
245
|
+
const guidance = raw.requiredReadBeforeEdit
|
|
246
|
+
? " Call Read on this file before trying Edit again."
|
|
247
|
+
: "";
|
|
248
|
+
return `Edit failed for ${raw.filePath || "(unknown path)"}: ${raw.error ?? "Unknown error."}${guidance}`;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return [
|
|
252
|
+
`Edit succeeded for ${raw.filePath}.`,
|
|
253
|
+
`bytesWritten=${raw.bytesWritten ?? 0}`,
|
|
254
|
+
`replacementCount=${raw.replacementCount ?? 0}`,
|
|
255
|
+
`replaceAll=${raw.replaceAll ?? false}`,
|
|
256
|
+
`created=${raw.created ?? false}`,
|
|
257
|
+
`oldSha256=${raw.oldSha256 ?? "null"}`,
|
|
258
|
+
`newSha256=${raw.newSha256}`,
|
|
259
|
+
].join("\n");
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function renderBashObservation(raw: BashRawResult): string {
|
|
263
|
+
if (raw.taskId === "" && !raw.ok) {
|
|
264
|
+
return `Bash failed: ${raw.error ?? "Unknown error."}`;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (raw.status === "running" && raw.backgroundedDueToTimeout) {
|
|
268
|
+
return [
|
|
269
|
+
"Bash command exceeded foreground timeout and is still running.",
|
|
270
|
+
`taskId=${raw.taskId}`,
|
|
271
|
+
`timeoutMs=${raw.timeoutMs ?? 0}`,
|
|
272
|
+
`command=${raw.command}`,
|
|
273
|
+
`cwd=${raw.cwd}`,
|
|
274
|
+
`outputFilePath=${raw.outputFilePath}`,
|
|
275
|
+
"Use Read on outputFilePath to inspect current output.",
|
|
276
|
+
].join("\n");
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (raw.status === "running") {
|
|
280
|
+
return [
|
|
281
|
+
"Bash command is running in background.",
|
|
282
|
+
`taskId=${raw.taskId}`,
|
|
283
|
+
`command=${raw.command}`,
|
|
284
|
+
`cwd=${raw.cwd}`,
|
|
285
|
+
`outputFilePath=${raw.outputFilePath}`,
|
|
286
|
+
"Use Read on outputFilePath to inspect current output.",
|
|
287
|
+
].join("\n");
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return [
|
|
291
|
+
`Bash ${raw.status}.`,
|
|
292
|
+
`command=${raw.command}`,
|
|
293
|
+
`exitCode=${raw.exitCode ?? "null"}`,
|
|
294
|
+
`status=${raw.status}`,
|
|
295
|
+
`cwd=${raw.cwd}`,
|
|
296
|
+
`outputFilePath=${raw.outputFilePath}`,
|
|
297
|
+
`outputBytes=${raw.outputBytes}`,
|
|
298
|
+
`outputLines=${raw.outputLines}`,
|
|
299
|
+
`truncated=${raw.truncated}`,
|
|
300
|
+
raw.omittedLines === undefined ? undefined : `omittedLines=${raw.omittedLines}`,
|
|
301
|
+
raw.returnCodeInterpretation === undefined
|
|
302
|
+
? undefined
|
|
303
|
+
: `returnCodeInterpretation=${raw.returnCodeInterpretation}`,
|
|
304
|
+
raw.error === undefined ? undefined : `error=${raw.error}`,
|
|
305
|
+
"preview:",
|
|
306
|
+
raw.preview,
|
|
307
|
+
]
|
|
308
|
+
.filter((line): line is string => line !== undefined)
|
|
309
|
+
.join("\n");
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function renderTaskListObservation(raw: TaskListRawResult): string {
|
|
313
|
+
if (!raw.ok) {
|
|
314
|
+
return `TaskList failed: ${raw.error ?? "Unknown error."}`;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const header = `Background tasks: ${raw.tasks.length} total, ${raw.runningCount} running.`;
|
|
318
|
+
if (raw.tasks.length === 0) {
|
|
319
|
+
return `${header}\n\n(no background tasks)`;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return [header, ...raw.tasks.map(renderTaskSummary)].join("\n\n");
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function renderTaskOutputObservation(raw: TaskOutputRawResult): string {
|
|
326
|
+
if (!raw.ok || raw.task === undefined) {
|
|
327
|
+
return `TaskOutput failed for ${raw.taskId || "(unknown task ID)"}: ${raw.error ?? "Unknown error."}`;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return [
|
|
331
|
+
"Task output retrieved.",
|
|
332
|
+
`taskId=${raw.taskId}`,
|
|
333
|
+
`status=${raw.task.status}`,
|
|
334
|
+
`command=${raw.task.command}`,
|
|
335
|
+
`outputFilePath=${raw.outputFilePath}`,
|
|
336
|
+
`outputBytes=${raw.outputBytes ?? 0}`,
|
|
337
|
+
`outputLines=${raw.outputLines ?? 0}`,
|
|
338
|
+
`truncated=${raw.truncated ?? false}`,
|
|
339
|
+
raw.omittedLines === undefined ? undefined : `omittedLines=${raw.omittedLines}`,
|
|
340
|
+
"preview:",
|
|
341
|
+
raw.preview ?? "",
|
|
342
|
+
]
|
|
343
|
+
.filter((line): line is string => line !== undefined)
|
|
344
|
+
.join("\n");
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function renderTaskStopObservation(raw: TaskStopRawResult): string {
|
|
348
|
+
if (!raw.ok || raw.task === undefined) {
|
|
349
|
+
return `TaskStop failed for ${raw.taskId || "(unknown task ID)"}: ${raw.error ?? "Unknown error."}`;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return [
|
|
353
|
+
"Task stopped.",
|
|
354
|
+
`taskId=${raw.taskId}`,
|
|
355
|
+
`status=${raw.task.status}`,
|
|
356
|
+
raw.task.exitCode === undefined ? undefined : `exitCode=${raw.task.exitCode}`,
|
|
357
|
+
raw.task.signal === undefined ? undefined : `signal=${raw.task.signal}`,
|
|
358
|
+
`escalated=${raw.escalated ?? false}`,
|
|
359
|
+
`endedAt=${raw.task.endedAt}`,
|
|
360
|
+
`outputFilePath=${raw.task.outputFilePath}`,
|
|
361
|
+
]
|
|
362
|
+
.filter((line): line is string => line !== undefined)
|
|
363
|
+
.join("\n");
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function renderTaskSummary(task: TaskListRawResult["tasks"][number]): string {
|
|
367
|
+
return [
|
|
368
|
+
`taskId=${task.taskId}`,
|
|
369
|
+
`description=${task.description}`,
|
|
370
|
+
`status=${task.status}`,
|
|
371
|
+
`startedAt=${task.startedAt}`,
|
|
372
|
+
task.endedAt === undefined ? undefined : `endedAt=${task.endedAt}`,
|
|
373
|
+
task.exitCode === undefined ? undefined : `exitCode=${task.exitCode}`,
|
|
374
|
+
task.signal === undefined ? undefined : `signal=${task.signal}`,
|
|
375
|
+
task.error === undefined ? undefined : `error=${task.error}`,
|
|
376
|
+
`outputFilePath=${task.outputFilePath}`,
|
|
377
|
+
]
|
|
378
|
+
.filter((line): line is string => line !== undefined)
|
|
379
|
+
.join("\n");
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function renderWebSearchObservation(raw: WebSearchRawResult): string {
|
|
383
|
+
if (!raw.ok) {
|
|
384
|
+
return `WebSearch failed for query=${JSON.stringify(raw.query)}: ${raw.error ?? "Unknown error."}`;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const results = raw.results ?? [];
|
|
388
|
+
const header = `Web search results for query ${JSON.stringify(raw.query)} (${results.length} result${results.length === 1 ? "" : "s"}):`;
|
|
389
|
+
|
|
390
|
+
if (results.length === 0) {
|
|
391
|
+
return `${header}\n\n(no results)`;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const blocks = results.map((result, index) => {
|
|
395
|
+
const lines = [
|
|
396
|
+
`${index + 1}. ${result.title === "" ? result.url : result.title}`,
|
|
397
|
+
` URL: ${result.url}`,
|
|
398
|
+
];
|
|
399
|
+
|
|
400
|
+
if (result.publishedDate !== undefined) {
|
|
401
|
+
lines.push(` Published: ${result.publishedDate}`);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
for (const highlight of result.highlights ?? []) {
|
|
405
|
+
lines.push(` - ${collapseWhitespace(highlight)}`);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return lines.join("\n");
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
return [header, ...blocks].join("\n\n");
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function collapseWhitespace(text: string): string {
|
|
415
|
+
return text.replace(/\s+/g, " ").trim();
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
function renderWebFetchObservation(raw: WebFetchRawResult): string {
|
|
419
|
+
if (!raw.ok) {
|
|
420
|
+
return `WebFetch failed for ${raw.url || "(unknown url)"}: ${raw.error ?? "Unknown error."}`;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
if (raw.redirectUrl !== undefined) {
|
|
424
|
+
return [
|
|
425
|
+
`WebFetch was redirected to ${raw.redirectUrl}.`,
|
|
426
|
+
"The redirect crosses hosts, so it was not followed automatically.",
|
|
427
|
+
"Call WebFetch again with this URL if the redirect target is expected.",
|
|
428
|
+
].join("\n");
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const sections = [
|
|
432
|
+
`Web fetch result for ${raw.url} (route=${raw.route ?? "unknown"}, refined=${raw.refined ?? false}):`,
|
|
433
|
+
];
|
|
434
|
+
|
|
435
|
+
if (raw.title !== undefined && raw.title !== "") {
|
|
436
|
+
sections.push(`Title: ${raw.title}`);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
sections.push(raw.content ?? "");
|
|
440
|
+
|
|
441
|
+
const highlights = raw.highlights ?? [];
|
|
442
|
+
if (highlights.length > 0) {
|
|
443
|
+
sections.push(
|
|
444
|
+
[
|
|
445
|
+
"Highlights:",
|
|
446
|
+
...highlights.map((entry) => `- ${collapseWhitespace(entry)}`),
|
|
447
|
+
].join("\n"),
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
return sections.join("\n\n");
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
function renderMcpObservation(raw: McpToolRawResult): string {
|
|
455
|
+
if (!raw.ok && raw.isError !== true) {
|
|
456
|
+
return `${raw.toolName} failed: ${raw.error ?? "Unknown error."}`;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
const text = raw.text ?? "";
|
|
460
|
+
const sections: string[] = [];
|
|
461
|
+
|
|
462
|
+
if (raw.isError === true) {
|
|
463
|
+
sections.push(`${raw.toolName} failed (server reported error):`);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
sections.push(
|
|
467
|
+
text.trim() === ""
|
|
468
|
+
? `(no text content, ${raw.contentBlockCount ?? 0} content block${(raw.contentBlockCount ?? 0) === 1 ? "" : "s"})`
|
|
469
|
+
: text,
|
|
470
|
+
);
|
|
471
|
+
|
|
472
|
+
if (raw.truncated === true) {
|
|
473
|
+
sections.push(`[Output truncated to ${text.length} characters.]`);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
return sections.join("\n");
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
function renderGenericObservation(raw: GenericToolRawResult): string {
|
|
480
|
+
return `${raw.toolName} failed: ${raw.error}`;
|
|
481
|
+
}
|