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,159 @@
|
|
|
1
|
+
import { mkdir } from "node:fs/promises";
|
|
2
|
+
import { createWriteStream, type WriteStream } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { StringDecoder } from "node:string_decoder";
|
|
5
|
+
|
|
6
|
+
export type TaskOutputSnapshot = {
|
|
7
|
+
outputBytes: number;
|
|
8
|
+
outputLines: number;
|
|
9
|
+
preview: string;
|
|
10
|
+
truncated: boolean;
|
|
11
|
+
omittedLines?: number;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const maxPreviewLines = 200;
|
|
15
|
+
const previewEdgeLines = 100;
|
|
16
|
+
|
|
17
|
+
export class TaskOutput {
|
|
18
|
+
private readonly decoder = new StringDecoder("utf8");
|
|
19
|
+
private readonly stream: WriteStream;
|
|
20
|
+
private outputBytes = 0;
|
|
21
|
+
private outputLines = 0;
|
|
22
|
+
private pendingLine = "";
|
|
23
|
+
private fullPreviewLines: string[] | undefined = [];
|
|
24
|
+
private readonly firstLines: string[] = [];
|
|
25
|
+
private readonly lastLines: string[] = [];
|
|
26
|
+
private endPromise?: Promise<TaskOutputSnapshot>;
|
|
27
|
+
|
|
28
|
+
private constructor(readonly filePath: string) {
|
|
29
|
+
this.stream = createWriteStream(filePath, { flags: "a" });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
static async create(filePath: string): Promise<TaskOutput> {
|
|
33
|
+
await mkdir(path.dirname(filePath), { recursive: true });
|
|
34
|
+
return new TaskOutput(filePath);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
write(chunk: Buffer): void {
|
|
38
|
+
this.outputBytes += chunk.byteLength;
|
|
39
|
+
this.stream.write(chunk);
|
|
40
|
+
this.appendText(this.decoder.write(chunk));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async end(): Promise<TaskOutputSnapshot> {
|
|
44
|
+
this.endPromise ??= this.finish();
|
|
45
|
+
return this.endPromise;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private async finish(): Promise<TaskOutputSnapshot> {
|
|
49
|
+
this.appendText(this.decoder.end());
|
|
50
|
+
if (this.pendingLine !== "") {
|
|
51
|
+
this.pushLine(this.pendingLine);
|
|
52
|
+
this.pendingLine = "";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
await new Promise<void>((resolve, reject) => {
|
|
56
|
+
this.stream.end((error?: Error | null) => {
|
|
57
|
+
if (error) {
|
|
58
|
+
reject(error);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
resolve();
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
return this.snapshot();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
snapshot(): TaskOutputSnapshot {
|
|
70
|
+
const outputLines = this.outputLines + (this.pendingLine === "" ? 0 : 1);
|
|
71
|
+
const lines = this.previewLines();
|
|
72
|
+
|
|
73
|
+
if (outputLines <= maxPreviewLines) {
|
|
74
|
+
return {
|
|
75
|
+
outputBytes: this.outputBytes,
|
|
76
|
+
outputLines,
|
|
77
|
+
preview: lines.join("\n"),
|
|
78
|
+
truncated: false,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const omittedLines = outputLines - maxPreviewLines;
|
|
83
|
+
const omittedStartLine = previewEdgeLines + 1;
|
|
84
|
+
const omittedEndLine = outputLines - previewEdgeLines;
|
|
85
|
+
return {
|
|
86
|
+
outputBytes: this.outputBytes,
|
|
87
|
+
outputLines,
|
|
88
|
+
preview: [
|
|
89
|
+
...this.firstLines,
|
|
90
|
+
`... output omitted: lines ${omittedStartLine}-${omittedEndLine} (${omittedLines} ${omittedLines === 1 ? "line" : "lines"}). Full output is available at outputFilePath.`,
|
|
91
|
+
...this.lastPreviewLines(),
|
|
92
|
+
].join("\n"),
|
|
93
|
+
truncated: true,
|
|
94
|
+
omittedLines,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private appendText(text: string): void {
|
|
99
|
+
if (text === "") {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const parts = text.split("\n");
|
|
104
|
+
parts[0] = this.pendingLine + parts[0];
|
|
105
|
+
|
|
106
|
+
for (const line of parts.slice(0, -1)) {
|
|
107
|
+
this.pushLine(trimTrailingCarriageReturn(line));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
this.pendingLine = parts.at(-1) ?? "";
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private pushLine(line: string): void {
|
|
114
|
+
this.outputLines += 1;
|
|
115
|
+
|
|
116
|
+
if (this.firstLines.length < previewEdgeLines) {
|
|
117
|
+
this.firstLines.push(line);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
this.lastLines.push(line);
|
|
121
|
+
if (this.lastLines.length > previewEdgeLines) {
|
|
122
|
+
this.lastLines.shift();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (this.fullPreviewLines !== undefined) {
|
|
126
|
+
this.fullPreviewLines.push(line);
|
|
127
|
+
if (this.fullPreviewLines.length > maxPreviewLines) {
|
|
128
|
+
this.fullPreviewLines = undefined;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private previewLines(): string[] {
|
|
134
|
+
const lines =
|
|
135
|
+
this.fullPreviewLines === undefined
|
|
136
|
+
? [...this.firstLines, ...this.lastLines]
|
|
137
|
+
: [...this.fullPreviewLines];
|
|
138
|
+
|
|
139
|
+
if (this.pendingLine !== "") {
|
|
140
|
+
lines.push(this.pendingLine);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return lines;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private lastPreviewLines(): string[] {
|
|
147
|
+
const lines = [...this.lastLines];
|
|
148
|
+
|
|
149
|
+
if (this.pendingLine !== "") {
|
|
150
|
+
lines.push(this.pendingLine);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return lines.slice(-previewEdgeLines);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function trimTrailingCarriageReturn(line: string): string {
|
|
158
|
+
return line.endsWith("\r") ? line.slice(0, -1) : line;
|
|
159
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { ShellTaskManager } from "./bash-task";
|
|
2
|
+
import { throwIfTurnCancelled } from "../agent/turn-cancellation";
|
|
3
|
+
import { parseTaskIdArgs } from "./task-tool-args";
|
|
4
|
+
import { defineToolExecutor } from "./types";
|
|
5
|
+
import type { TaskStopRawResult, ToolExecutionContext, ToolExecutor } from "./types";
|
|
6
|
+
|
|
7
|
+
export function createTaskStopToolExecutor(options: {
|
|
8
|
+
taskManager: ShellTaskManager;
|
|
9
|
+
}): ToolExecutor {
|
|
10
|
+
return defineToolExecutor("task_stop", {
|
|
11
|
+
definition: {
|
|
12
|
+
name: "TaskStop",
|
|
13
|
+
description: "Stop a running background shell task.",
|
|
14
|
+
parameters: {
|
|
15
|
+
type: "object",
|
|
16
|
+
additionalProperties: false,
|
|
17
|
+
properties: {
|
|
18
|
+
task_id: {
|
|
19
|
+
type: "string",
|
|
20
|
+
description: "The running task ID to stop.",
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
required: ["task_id"],
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
async execute(
|
|
27
|
+
args,
|
|
28
|
+
_call,
|
|
29
|
+
context: ToolExecutionContext,
|
|
30
|
+
): Promise<TaskStopRawResult> {
|
|
31
|
+
throwIfTurnCancelled(context.signal);
|
|
32
|
+
const parsed = parseTaskIdArgs(args, "TaskStop");
|
|
33
|
+
if (!parsed.ok) {
|
|
34
|
+
return { ok: false, taskId: "", error: parsed.error };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const result = await options.taskManager.stopTask(parsed.taskId, "tool");
|
|
39
|
+
return {
|
|
40
|
+
ok: true,
|
|
41
|
+
taskId: parsed.taskId,
|
|
42
|
+
task: result.task,
|
|
43
|
+
status: result.task.status,
|
|
44
|
+
requestedSignal: result.requestedSignal,
|
|
45
|
+
escalated: result.escalated,
|
|
46
|
+
};
|
|
47
|
+
} catch (error) {
|
|
48
|
+
const inspection = options.taskManager.inspectTask(parsed.taskId);
|
|
49
|
+
return {
|
|
50
|
+
ok: false,
|
|
51
|
+
taskId: parsed.taskId,
|
|
52
|
+
task: inspection?.task,
|
|
53
|
+
status: inspection?.task.status,
|
|
54
|
+
error: error instanceof Error ? error.message : String(error),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export function parseTaskIdArgs(
|
|
2
|
+
args: unknown,
|
|
3
|
+
toolName: "TaskOutput" | "TaskStop",
|
|
4
|
+
): { ok: true; taskId: string } | { ok: false; error: string } {
|
|
5
|
+
if (!isRecord(args)) {
|
|
6
|
+
return { ok: false, error: `${toolName} arguments must be an object.` };
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const unexpected = Object.keys(args).filter((key) => key !== "task_id");
|
|
10
|
+
if (unexpected.length > 0) {
|
|
11
|
+
return {
|
|
12
|
+
ok: false,
|
|
13
|
+
error: `${toolName} received unexpected argument: ${unexpected[0]}.`,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (typeof args.task_id !== "string" || args.task_id.trim() === "") {
|
|
18
|
+
return {
|
|
19
|
+
ok: false,
|
|
20
|
+
error: `${toolName}.task_id must be a non-empty string.`,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return { ok: true, taskId: args.task_id };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
28
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
29
|
+
}
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import type { ToolCall } from "../agent/types";
|
|
2
|
+
import type { SessionId } from "../ids/runtime-id";
|
|
3
|
+
import type {
|
|
4
|
+
RecallGetPage,
|
|
5
|
+
RecallSearchFilters,
|
|
6
|
+
RecallSearchPage,
|
|
7
|
+
} from "../session/session-history-reader";
|
|
8
|
+
import type { ShellTaskSnapshot, ShellTaskStatus } from "./bash-task";
|
|
9
|
+
|
|
10
|
+
export type JsonSchema = Record<string, unknown>;
|
|
11
|
+
|
|
12
|
+
export type ToolDefinition = {
|
|
13
|
+
name: string;
|
|
14
|
+
description: string;
|
|
15
|
+
parameters: JsonSchema;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type DiffHunk = {
|
|
19
|
+
oldStart: number;
|
|
20
|
+
oldLines: number;
|
|
21
|
+
newStart: number;
|
|
22
|
+
newLines: number;
|
|
23
|
+
lines: string[];
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type ReadFileRawResult = {
|
|
27
|
+
ok: boolean;
|
|
28
|
+
filePath: string;
|
|
29
|
+
absolutePath?: string;
|
|
30
|
+
content?: string;
|
|
31
|
+
contentBytes?: number;
|
|
32
|
+
sizeBytes?: number;
|
|
33
|
+
totalLines?: number;
|
|
34
|
+
startLine?: number;
|
|
35
|
+
endLine?: number;
|
|
36
|
+
sha256?: string;
|
|
37
|
+
error?: string;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export type WriteFileRawResult = {
|
|
41
|
+
ok: boolean;
|
|
42
|
+
filePath: string;
|
|
43
|
+
absolutePath?: string;
|
|
44
|
+
bytesWritten?: number;
|
|
45
|
+
oldSha256?: string | null;
|
|
46
|
+
newSha256?: string;
|
|
47
|
+
created?: boolean;
|
|
48
|
+
patch?: DiffHunk[];
|
|
49
|
+
patchTruncated?: boolean;
|
|
50
|
+
requiredReadBeforeWrite?: boolean;
|
|
51
|
+
currentSha256?: string;
|
|
52
|
+
lastReadSha256?: string;
|
|
53
|
+
error?: string;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export type EditFileRawResult = {
|
|
57
|
+
ok: boolean;
|
|
58
|
+
filePath: string;
|
|
59
|
+
absolutePath?: string;
|
|
60
|
+
bytesWritten?: number;
|
|
61
|
+
oldSha256?: string | null;
|
|
62
|
+
newSha256?: string;
|
|
63
|
+
replacementCount?: number;
|
|
64
|
+
replaceAll?: boolean;
|
|
65
|
+
created?: boolean;
|
|
66
|
+
patch?: DiffHunk[];
|
|
67
|
+
patchTruncated?: boolean;
|
|
68
|
+
requiredReadBeforeEdit?: boolean;
|
|
69
|
+
currentMtimeMs?: number;
|
|
70
|
+
lastObservedMtimeMs?: number;
|
|
71
|
+
error?: string;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export type GlobRawResult = {
|
|
75
|
+
ok: boolean;
|
|
76
|
+
pattern: string;
|
|
77
|
+
searchPath: string;
|
|
78
|
+
absoluteSearchPath?: string;
|
|
79
|
+
matches?: string[];
|
|
80
|
+
matchCount?: number;
|
|
81
|
+
ignored?: string[];
|
|
82
|
+
error?: string;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export type GrepOutputMode = "content" | "files_with_matches" | "count";
|
|
86
|
+
|
|
87
|
+
export type GrepRawResult = {
|
|
88
|
+
ok: boolean;
|
|
89
|
+
pattern: string;
|
|
90
|
+
searchPath: string;
|
|
91
|
+
absoluteSearchPath?: string;
|
|
92
|
+
mode: GrepOutputMode;
|
|
93
|
+
filenames: string[];
|
|
94
|
+
numFiles: number;
|
|
95
|
+
content?: string;
|
|
96
|
+
numLines?: number;
|
|
97
|
+
numMatches?: number;
|
|
98
|
+
appliedLimit?: number;
|
|
99
|
+
appliedOffset?: number;
|
|
100
|
+
ignored?: string[];
|
|
101
|
+
truncated?: boolean;
|
|
102
|
+
error?: string;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export type BashRawResult = {
|
|
106
|
+
ok: boolean;
|
|
107
|
+
command: string;
|
|
108
|
+
taskId: string;
|
|
109
|
+
sessionId: SessionId;
|
|
110
|
+
status: "completed" | "failed" | "running" | "killed";
|
|
111
|
+
exitCode?: number;
|
|
112
|
+
signal?: string;
|
|
113
|
+
cwd: string;
|
|
114
|
+
outputFilePath: string;
|
|
115
|
+
outputBytes: number;
|
|
116
|
+
outputLines: number;
|
|
117
|
+
preview: string;
|
|
118
|
+
truncated: boolean;
|
|
119
|
+
omittedLines?: number;
|
|
120
|
+
timedOut?: boolean;
|
|
121
|
+
timeoutMs?: number;
|
|
122
|
+
backgrounded?: boolean;
|
|
123
|
+
backgroundedDueToTimeout?: boolean;
|
|
124
|
+
returnCodeInterpretation?: string;
|
|
125
|
+
error?: string;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
export type TaskListRawResult = {
|
|
129
|
+
ok: boolean;
|
|
130
|
+
runningCount: number;
|
|
131
|
+
tasks: ShellTaskSnapshot[];
|
|
132
|
+
error?: string;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
export type TaskOutputRawResult = {
|
|
136
|
+
ok: boolean;
|
|
137
|
+
taskId: string;
|
|
138
|
+
task?: ShellTaskSnapshot;
|
|
139
|
+
status?: ShellTaskStatus;
|
|
140
|
+
command?: string;
|
|
141
|
+
outputBytes?: number;
|
|
142
|
+
outputLines?: number;
|
|
143
|
+
preview?: string;
|
|
144
|
+
truncated?: boolean;
|
|
145
|
+
omittedLines?: number;
|
|
146
|
+
outputFilePath?: string;
|
|
147
|
+
error?: string;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export type TaskStopRawResult = {
|
|
151
|
+
ok: boolean;
|
|
152
|
+
taskId: string;
|
|
153
|
+
task?: ShellTaskSnapshot;
|
|
154
|
+
status?: ShellTaskStatus;
|
|
155
|
+
requestedSignal?: "SIGTERM";
|
|
156
|
+
escalated?: boolean;
|
|
157
|
+
error?: string;
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
export type WebSearchResultItem = {
|
|
161
|
+
title: string;
|
|
162
|
+
url: string;
|
|
163
|
+
publishedDate?: string;
|
|
164
|
+
author?: string;
|
|
165
|
+
highlights?: string[];
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
export type WebSearchRawResult = {
|
|
169
|
+
ok: boolean;
|
|
170
|
+
query: string;
|
|
171
|
+
searchType?: string;
|
|
172
|
+
requestId?: string;
|
|
173
|
+
results?: WebSearchResultItem[];
|
|
174
|
+
resultCount?: number;
|
|
175
|
+
allowedDomains?: string[];
|
|
176
|
+
blockedDomains?: string[];
|
|
177
|
+
costDollars?: number;
|
|
178
|
+
durationMs?: number;
|
|
179
|
+
error?: string;
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
export type WebFetchRawResult = {
|
|
183
|
+
ok: boolean;
|
|
184
|
+
url: string;
|
|
185
|
+
route?: "local" | "exa" | "local-browser";
|
|
186
|
+
finalUrl?: string;
|
|
187
|
+
redirectUrl?: string;
|
|
188
|
+
title?: string;
|
|
189
|
+
publishedDate?: string;
|
|
190
|
+
refined?: boolean;
|
|
191
|
+
content?: string;
|
|
192
|
+
highlights?: string[];
|
|
193
|
+
source?: "cached" | "crawled";
|
|
194
|
+
cacheHit?: boolean;
|
|
195
|
+
errorTag?: string;
|
|
196
|
+
httpStatusCode?: number;
|
|
197
|
+
costDollars?: number;
|
|
198
|
+
durationMs?: number;
|
|
199
|
+
error?: string;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
export type GenericToolRawResult = {
|
|
203
|
+
ok: false;
|
|
204
|
+
toolName: string;
|
|
205
|
+
error: string;
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
export type RecallToolErrorCode =
|
|
209
|
+
| "RECALL_ARGS_INVALID"
|
|
210
|
+
| "RECALL_SOURCE_INVALID"
|
|
211
|
+
| "RECALL_SOURCE_NOT_FOUND"
|
|
212
|
+
| "RECALL_PAGE_INVALID"
|
|
213
|
+
| "RECALL_SNAPSHOT_INVALID";
|
|
214
|
+
|
|
215
|
+
export type RecallSearchRawResult =
|
|
216
|
+
| {
|
|
217
|
+
ok: true;
|
|
218
|
+
mode: "search";
|
|
219
|
+
historical: true;
|
|
220
|
+
query: string;
|
|
221
|
+
filters: RecallSearchFilters;
|
|
222
|
+
page: RecallSearchPage;
|
|
223
|
+
}
|
|
224
|
+
| {
|
|
225
|
+
ok: false;
|
|
226
|
+
mode: "search";
|
|
227
|
+
errorCode: RecallToolErrorCode;
|
|
228
|
+
error: string;
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
export type RecallGetRawResult =
|
|
232
|
+
| {
|
|
233
|
+
ok: true;
|
|
234
|
+
mode: "get";
|
|
235
|
+
historical: true;
|
|
236
|
+
page: RecallGetPage;
|
|
237
|
+
}
|
|
238
|
+
| {
|
|
239
|
+
ok: false;
|
|
240
|
+
mode: "get";
|
|
241
|
+
errorCode: RecallToolErrorCode;
|
|
242
|
+
error: string;
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
export type RecallRawResult = RecallSearchRawResult | RecallGetRawResult;
|
|
246
|
+
|
|
247
|
+
export type McpToolRawResult = {
|
|
248
|
+
ok: boolean;
|
|
249
|
+
toolName: string;
|
|
250
|
+
serverName: string;
|
|
251
|
+
serverToolName: string;
|
|
252
|
+
isError?: boolean;
|
|
253
|
+
text?: string;
|
|
254
|
+
truncated?: boolean;
|
|
255
|
+
contentBlockCount?: number;
|
|
256
|
+
error?: string;
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
export type ToolRawResultByKind = {
|
|
260
|
+
read: ReadFileRawResult;
|
|
261
|
+
write: WriteFileRawResult;
|
|
262
|
+
edit: EditFileRawResult;
|
|
263
|
+
glob: GlobRawResult;
|
|
264
|
+
grep: GrepRawResult;
|
|
265
|
+
bash: BashRawResult;
|
|
266
|
+
task_list: TaskListRawResult;
|
|
267
|
+
task_output: TaskOutputRawResult;
|
|
268
|
+
task_stop: TaskStopRawResult;
|
|
269
|
+
web_search: WebSearchRawResult;
|
|
270
|
+
web_fetch: WebFetchRawResult;
|
|
271
|
+
recall: RecallRawResult;
|
|
272
|
+
mcp: McpToolRawResult;
|
|
273
|
+
generic: GenericToolRawResult;
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
export type ToolRawResultKind = keyof ToolRawResultByKind;
|
|
277
|
+
|
|
278
|
+
export type ToolRawResult = {
|
|
279
|
+
[TKind in ToolRawResultKind]: ToolRawResultByKind[TKind] & { kind: TKind };
|
|
280
|
+
}[ToolRawResultKind];
|
|
281
|
+
|
|
282
|
+
export type ToolExecutor = {
|
|
283
|
+
definition: ToolDefinition;
|
|
284
|
+
execute(
|
|
285
|
+
args: unknown,
|
|
286
|
+
call: ToolCall,
|
|
287
|
+
context: ToolExecutionContext,
|
|
288
|
+
): Promise<ToolRawResult>;
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
type UntaggedToolExecutor<TKind extends ToolRawResultKind> = {
|
|
292
|
+
definition: ToolDefinition;
|
|
293
|
+
execute(
|
|
294
|
+
args: unknown,
|
|
295
|
+
call: ToolCall,
|
|
296
|
+
context: ToolExecutionContext,
|
|
297
|
+
): Promise<ToolRawResultByKind[TKind]>;
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
export function defineToolExecutor<TKind extends ToolRawResultKind>(
|
|
301
|
+
kind: TKind,
|
|
302
|
+
executor: UntaggedToolExecutor<TKind>,
|
|
303
|
+
): ToolExecutor {
|
|
304
|
+
return {
|
|
305
|
+
definition: executor.definition,
|
|
306
|
+
async execute(args, call, context) {
|
|
307
|
+
const raw = await executor.execute(args, call, context);
|
|
308
|
+
return { ...raw, kind } as ToolRawResult;
|
|
309
|
+
},
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export type ToolExecutionContext = {
|
|
314
|
+
signal: AbortSignal;
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
export class ToolExecutionFatalError extends Error {
|
|
318
|
+
constructor(message: string, options?: ErrorOptions) {
|
|
319
|
+
super(message, options);
|
|
320
|
+
this.name = "ToolExecutionFatalError";
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
export type FileSnapshot = {
|
|
325
|
+
sha256: string;
|
|
326
|
+
mtimeMs: number;
|
|
327
|
+
source: "read" | "write" | "edit";
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
export type ReadSnapshotStore = Map<string, FileSnapshot>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ToolExecutionContext } from "../types";
|
|
2
|
+
|
|
3
|
+
export type WebFetchRoute = "local" | "exa" | "local-browser";
|
|
4
|
+
|
|
5
|
+
export type WebFetchBackendResult = {
|
|
6
|
+
ok: boolean;
|
|
7
|
+
finalUrl?: string;
|
|
8
|
+
redirectUrl?: string;
|
|
9
|
+
title?: string;
|
|
10
|
+
publishedDate?: string;
|
|
11
|
+
markdown?: string;
|
|
12
|
+
refined?: string;
|
|
13
|
+
highlights?: string[];
|
|
14
|
+
source?: "cached" | "crawled";
|
|
15
|
+
errorTag?: string;
|
|
16
|
+
httpStatusCode?: number;
|
|
17
|
+
costDollars?: number;
|
|
18
|
+
error?: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type WebFetchBackend = {
|
|
22
|
+
route: WebFetchRoute;
|
|
23
|
+
fetch(
|
|
24
|
+
input: { url: string; prompt: string },
|
|
25
|
+
context: ToolExecutionContext,
|
|
26
|
+
): Promise<WebFetchBackendResult>;
|
|
27
|
+
};
|