@zocomputer/agent-sdk 0.5.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.
- package/LICENSE +21 -0
- package/README.md +673 -0
- package/dist/attachments.js +52 -0
- package/dist/gateway-fetch.js +67 -0
- package/dist/index.js +4169 -0
- package/dist/initiator-auth.js +49 -0
- package/dist/platform/agent-sandbox/index.js +691 -0
- package/dist/platform/cloud-tools/image.js +498 -0
- package/dist/platform/cloud-tools/index.js +507 -0
- package/dist/platform/cloud-tools/web-search.js +87 -0
- package/dist/platform/runtime-ai/gateway.js +87 -0
- package/dist/platform/runtime-ai/index.js +91 -0
- package/dist/platform/runtime-ai/register.js +88 -0
- package/dist/platform/runtime-ai/session-fetch.js +54 -0
- package/dist/platform/runtime-auth/index.js +141 -0
- package/dist/state-files.js +287 -0
- package/dist/state-sandbox.js +383 -0
- package/dist/state.js +29 -0
- package/dist/steer-inbox.js +84 -0
- package/dist/steer.js +83 -0
- package/package.json +143 -0
- package/platform/agent-sandbox/api-client.ts +196 -0
- package/platform/agent-sandbox/index.ts +27 -0
- package/platform/agent-sandbox/pure.ts +83 -0
- package/platform/agent-sandbox/sftp.ts +141 -0
- package/platform/agent-sandbox/ssh-connection.ts +165 -0
- package/platform/agent-sandbox/ssh-exec.ts +98 -0
- package/platform/agent-sandbox/ssh-session.ts +487 -0
- package/platform/agent-sandbox/zo-backend.ts +88 -0
- package/platform/agent-sandbox/zo-sandbox.ts +39 -0
- package/platform/cloud-tools/image-path.ts +44 -0
- package/platform/cloud-tools/image.ts +225 -0
- package/platform/cloud-tools/index.ts +22 -0
- package/platform/cloud-tools/state-files.ts +368 -0
- package/platform/cloud-tools/tool-meta.ts +32 -0
- package/platform/cloud-tools/web-search.ts +15 -0
- package/platform/runtime-ai/gateway.ts +76 -0
- package/platform/runtime-ai/index.ts +20 -0
- package/platform/runtime-ai/register.ts +26 -0
- package/platform/runtime-ai/session-fetch.ts +124 -0
- package/platform/runtime-auth/index.ts +331 -0
- package/src/async-tasks.ts +273 -0
- package/src/attachments.ts +109 -0
- package/src/backgroundable.ts +88 -0
- package/src/bounded-output.ts +159 -0
- package/src/dir-conventions.ts +238 -0
- package/src/extract/cache.ts +40 -0
- package/src/extract/docx.ts +18 -0
- package/src/extract/pdf.ts +54 -0
- package/src/extract/sheet.ts +56 -0
- package/src/file-kind.ts +258 -0
- package/src/file-view.ts +80 -0
- package/src/gateway-fetch.ts +115 -0
- package/src/glob-match.ts +13 -0
- package/src/hooks.ts +213 -0
- package/src/index.ts +419 -0
- package/src/initiator-auth.ts +81 -0
- package/src/instructions.ts +224 -0
- package/src/list-files.ts +41 -0
- package/src/mock-model.ts +572 -0
- package/src/park-delivery.ts +247 -0
- package/src/path-locks.ts +52 -0
- package/src/read-file-content.ts +142 -0
- package/src/read-text.ts +40 -0
- package/src/redeliver.ts +155 -0
- package/src/run.ts +159 -0
- package/src/sandbox-io.ts +414 -0
- package/src/state-files.ts +460 -0
- package/src/state-sandbox.ts +710 -0
- package/src/state.ts +96 -0
- package/src/steer-inbox.ts +105 -0
- package/src/steer-tool.ts +83 -0
- package/src/steer.ts +146 -0
- package/src/task.ts +320 -0
- package/src/tools/bash.ts +143 -0
- package/src/tools/edit.ts +58 -0
- package/src/tools/glob.ts +56 -0
- package/src/tools/grep.ts +188 -0
- package/src/tools/read.ts +319 -0
- package/src/tools/tasks.ts +241 -0
- package/src/tools/webfetch.ts +368 -0
- package/src/tools/write.ts +42 -0
- package/src/walk.ts +112 -0
- package/src/watch-output.ts +104 -0
- package/src/web-fetch.ts +324 -0
- package/src/web-page.ts +179 -0
- package/src/workspace-io.ts +225 -0
- package/src/workspace.ts +41 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_MAX_INLINE_IMAGE_BYTES,
|
|
4
|
+
DEFAULT_MAX_INLINE_MEDIA_BYTES,
|
|
5
|
+
} from "./attachments";
|
|
6
|
+
import { createTaskRegistry, type TaskRegistry } from "./async-tasks";
|
|
7
|
+
import { createBashOp, type BackgroundableOp } from "./backgroundable";
|
|
8
|
+
import { TOOL_OUTPUT_DIRNAME } from "./bounded-output";
|
|
9
|
+
import { createDirConventionsTracker } from "./dir-conventions";
|
|
10
|
+
import {
|
|
11
|
+
createCommunicationInstruction,
|
|
12
|
+
createHitlInstruction,
|
|
13
|
+
createParallelToolsInstruction,
|
|
14
|
+
createRepoConventionsInstruction,
|
|
15
|
+
createSubagentInstruction,
|
|
16
|
+
createWorkflowInstruction,
|
|
17
|
+
type SubagentRosterEntry,
|
|
18
|
+
} from "./instructions";
|
|
19
|
+
import { createCommandRunner, type CommandRunner } from "./run";
|
|
20
|
+
import { createSteerInbox, type SteerInbox } from "./steer-inbox";
|
|
21
|
+
import { createSteerWrapper } from "./steer-tool";
|
|
22
|
+
import { createBashTool } from "./tools/bash";
|
|
23
|
+
import { createEditTool } from "./tools/edit";
|
|
24
|
+
import { createGlobTool } from "./tools/glob";
|
|
25
|
+
import { createGrepTool } from "./tools/grep";
|
|
26
|
+
import { createReadTool } from "./tools/read";
|
|
27
|
+
import { createTasksTools } from "./tools/tasks";
|
|
28
|
+
import { createWebFetchTool } from "./tools/webfetch";
|
|
29
|
+
import { createWriteTool } from "./tools/write";
|
|
30
|
+
import { createWorkspace, type Workspace } from "./workspace";
|
|
31
|
+
import { sandboxIoProvider, type SandboxIoOptions } from "./sandbox-io";
|
|
32
|
+
|
|
33
|
+
// One call wires the whole standard library for a real-filesystem eve agent:
|
|
34
|
+
// a workspace-scoped toolset (read/edit/write/glob/grep + host bash + webfetch),
|
|
35
|
+
// the background-task machinery (run_async/check_tasks/await_task over a
|
|
36
|
+
// persisted registry), and the matching dynamic instructions. Each
|
|
37
|
+
// `agent/tools/<name>.ts` file re-exports one tool — the filename is the wire
|
|
38
|
+
// name, so agents keep naming control (and can vacate eve's built-in
|
|
39
|
+
// read_file/write_file/bash with disable shims). Everything is also exported à
|
|
40
|
+
// la carte for agents that want a subset.
|
|
41
|
+
|
|
42
|
+
export interface StdlibOptions {
|
|
43
|
+
/** Directory the agent works in; file tools refuse paths that escape it. */
|
|
44
|
+
workspaceRoot: string;
|
|
45
|
+
/**
|
|
46
|
+
* Directory for the stdlib's local state: the background-task store
|
|
47
|
+
* (`tasks.json`) and spilled oversized tool output (`tool-outputs/`).
|
|
48
|
+
* Typically a gitignored dot-directory inside the workspace.
|
|
49
|
+
*/
|
|
50
|
+
stateDir: string;
|
|
51
|
+
/**
|
|
52
|
+
* What tool descriptions call the workspace — "repo" for a coding agent in a
|
|
53
|
+
* git checkout, "project", … Defaults to "workspace". Interpolated once at
|
|
54
|
+
* build time, so descriptions stay prompt-cache stable.
|
|
55
|
+
*/
|
|
56
|
+
workspaceNoun?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Replaces bash's default "avoid interactive CLIs" warning — point it at
|
|
59
|
+
* your agent's real-terminal tool if it has one.
|
|
60
|
+
*/
|
|
61
|
+
bashInteractiveHint?: string;
|
|
62
|
+
/** Extra run_async-able ops beyond bash (see defineOp). */
|
|
63
|
+
extraBackgroundables?: (ctx: {
|
|
64
|
+
workspace: Workspace;
|
|
65
|
+
runner: CommandRunner;
|
|
66
|
+
}) => readonly BackgroundableOp[];
|
|
67
|
+
/**
|
|
68
|
+
* When `read` or `webfetch` hits an image, embed its bytes on the tool
|
|
69
|
+
* result so a client can re-inject it as a viewable attachment on the next
|
|
70
|
+
* turn (see ./attachments and the README). Requires a client that consumes
|
|
71
|
+
* the attachment (rib, Zo); generic eve consumers can leave this off and get
|
|
72
|
+
* the metadata-only "ask the user" note. Defaults to `true`.
|
|
73
|
+
*/
|
|
74
|
+
attachImagesToChat?: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Max image size (bytes) to inline on the tool result; larger images fall
|
|
77
|
+
* back to the metadata-only note. Defaults to 3 MiB — eve's attachment
|
|
78
|
+
* staging inlines images up to that size at model-call time and text-stubs
|
|
79
|
+
* bigger ones, so staying under it keeps the "queued" promise truthful.
|
|
80
|
+
* Also bounds durable-stream bloat (the data URL rides the stream once per
|
|
81
|
+
* read/fetch).
|
|
82
|
+
*/
|
|
83
|
+
maxInlineImageBytes?: number;
|
|
84
|
+
/**
|
|
85
|
+
* Attach video files (mp4/mov/webm/mkv/avi) the way images attach. Defaults
|
|
86
|
+
* to `false`: video input is provider-gated (Gemini accepts it; Claude and
|
|
87
|
+
* most others don't), and eve's attachment staging currently hydrates only
|
|
88
|
+
* images/PDFs back into the model call (see the README's eve-maintainer
|
|
89
|
+
* notes) — enable once both hold for your agent.
|
|
90
|
+
*/
|
|
91
|
+
attachVideoToChat?: boolean;
|
|
92
|
+
/** Attach audio files (mp3/wav/ogg/flac/m4a). Same gating as video. */
|
|
93
|
+
attachAudioToChat?: boolean;
|
|
94
|
+
/**
|
|
95
|
+
* Max video/audio size (bytes) to inline on the tool result. Defaults to
|
|
96
|
+
* 10 MB (read's stat guard rejects bigger files outright; webfetch's 5 MB
|
|
97
|
+
* response cap bites first for fetches).
|
|
98
|
+
*/
|
|
99
|
+
maxInlineMediaBytes?: number;
|
|
100
|
+
/**
|
|
101
|
+
* Verify command mentioned by the workflow instruction (e.g. "bun run
|
|
102
|
+
* check"). Interpolated once at build time; omit for a generic hint.
|
|
103
|
+
*/
|
|
104
|
+
verifyCommandHint?: string;
|
|
105
|
+
/**
|
|
106
|
+
* Attach a directory's conventions file to the first `read` under it, once
|
|
107
|
+
* per directory per session (see ./dir-conventions.ts). The root file is
|
|
108
|
+
* excluded — `instructions.repoConventions` covers it. Defaults to `true`.
|
|
109
|
+
*/
|
|
110
|
+
injectDirConventions?: boolean;
|
|
111
|
+
/**
|
|
112
|
+
* Conventions filename the read riders look for. Defaults to "AGENTS.md".
|
|
113
|
+
*/
|
|
114
|
+
conventionsFileName?: string;
|
|
115
|
+
/**
|
|
116
|
+
* Enable steering: a directory (typically under `stateDir`) where UI
|
|
117
|
+
* clients queue mid-turn messages (see ./steer-inbox). Every stdlib tool is
|
|
118
|
+
* wrapped so queued messages ride the next completing tool result; pass the
|
|
119
|
+
* same dir to `createParkDeliveryHook({ steer })` so messages a turn ends
|
|
120
|
+
* before delivering go out on park instead.
|
|
121
|
+
*/
|
|
122
|
+
steer?: { dir: string };
|
|
123
|
+
/**
|
|
124
|
+
* Declared subagents the delegation playbook should route work to (e.g. the
|
|
125
|
+
* model-tier task preset — see ./task.ts). Grows `instructions.subagents`
|
|
126
|
+
* with a "Choosing a subagent" section. Interpolated once at build time.
|
|
127
|
+
*/
|
|
128
|
+
subagentRoster?: readonly SubagentRosterEntry[];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function createStdlib(options: StdlibOptions) {
|
|
132
|
+
const noun = options.workspaceNoun ?? "workspace";
|
|
133
|
+
const workspace = createWorkspace(options.workspaceRoot);
|
|
134
|
+
const spillDir = join(options.stateDir, TOOL_OUTPUT_DIRNAME);
|
|
135
|
+
const runner = createCommandRunner({ workspace, spillDir });
|
|
136
|
+
const registry: TaskRegistry = createTaskRegistry({
|
|
137
|
+
storePath: join(options.stateDir, "tasks.json"),
|
|
138
|
+
});
|
|
139
|
+
const backgroundables: readonly BackgroundableOp[] = [
|
|
140
|
+
createBashOp(runner),
|
|
141
|
+
...(options.extraBackgroundables?.({ workspace, runner }) ?? []),
|
|
142
|
+
];
|
|
143
|
+
const conventionsFileName = options.conventionsFileName ?? "AGENTS.md";
|
|
144
|
+
const dirConventions =
|
|
145
|
+
(options.injectDirConventions ?? true)
|
|
146
|
+
? {
|
|
147
|
+
tracker: createDirConventionsTracker({
|
|
148
|
+
workspaceRoot: workspace.root,
|
|
149
|
+
fileName: conventionsFileName,
|
|
150
|
+
}),
|
|
151
|
+
fileName: conventionsFileName,
|
|
152
|
+
}
|
|
153
|
+
: undefined;
|
|
154
|
+
const steerInbox: SteerInbox | null = options.steer
|
|
155
|
+
? createSteerInbox({ dir: options.steer.dir })
|
|
156
|
+
: null;
|
|
157
|
+
const steer = createSteerWrapper(steerInbox);
|
|
158
|
+
return {
|
|
159
|
+
workspace,
|
|
160
|
+
runner,
|
|
161
|
+
registry,
|
|
162
|
+
spillDir,
|
|
163
|
+
backgroundables,
|
|
164
|
+
steerInbox,
|
|
165
|
+
tools: {
|
|
166
|
+
read: steer(
|
|
167
|
+
createReadTool({
|
|
168
|
+
workspace,
|
|
169
|
+
noun,
|
|
170
|
+
attachImagesToChat: options.attachImagesToChat ?? true,
|
|
171
|
+
maxInlineImageBytes:
|
|
172
|
+
options.maxInlineImageBytes ?? DEFAULT_MAX_INLINE_IMAGE_BYTES,
|
|
173
|
+
attachVideoToChat: options.attachVideoToChat ?? false,
|
|
174
|
+
attachAudioToChat: options.attachAudioToChat ?? false,
|
|
175
|
+
maxInlineMediaBytes:
|
|
176
|
+
options.maxInlineMediaBytes ?? DEFAULT_MAX_INLINE_MEDIA_BYTES,
|
|
177
|
+
dirConventions,
|
|
178
|
+
}),
|
|
179
|
+
),
|
|
180
|
+
edit: steer(createEditTool({ workspace, noun })),
|
|
181
|
+
write: steer(createWriteTool({ workspace, noun })),
|
|
182
|
+
glob: steer(createGlobTool({ workspace, noun })),
|
|
183
|
+
grep: steer(createGrepTool({ workspace, noun, spillDir })),
|
|
184
|
+
bash: steer(
|
|
185
|
+
createBashTool({
|
|
186
|
+
workspace,
|
|
187
|
+
runner,
|
|
188
|
+
registry,
|
|
189
|
+
noun,
|
|
190
|
+
interactiveHint: options.bashInteractiveHint,
|
|
191
|
+
}),
|
|
192
|
+
),
|
|
193
|
+
tasks: createTasksTools({ registry, backgroundables, steerInbox }),
|
|
194
|
+
webfetch: steer(
|
|
195
|
+
createWebFetchTool({
|
|
196
|
+
workspace,
|
|
197
|
+
spillDir,
|
|
198
|
+
attachImagesToChat: options.attachImagesToChat ?? true,
|
|
199
|
+
maxInlineImageBytes:
|
|
200
|
+
options.maxInlineImageBytes ?? DEFAULT_MAX_INLINE_IMAGE_BYTES,
|
|
201
|
+
attachVideoToChat: options.attachVideoToChat ?? false,
|
|
202
|
+
attachAudioToChat: options.attachAudioToChat ?? false,
|
|
203
|
+
maxInlineMediaBytes:
|
|
204
|
+
options.maxInlineMediaBytes ?? DEFAULT_MAX_INLINE_MEDIA_BYTES,
|
|
205
|
+
}),
|
|
206
|
+
),
|
|
207
|
+
},
|
|
208
|
+
instructions: {
|
|
209
|
+
parallelTools: createParallelToolsInstruction(),
|
|
210
|
+
repoConventions: createRepoConventionsInstruction({ workspaceRoot: workspace.root }),
|
|
211
|
+
subagents: createSubagentInstruction({
|
|
212
|
+
workspaceNoun: noun,
|
|
213
|
+
roster: options.subagentRoster,
|
|
214
|
+
}),
|
|
215
|
+
workflow: createWorkflowInstruction({
|
|
216
|
+
workspaceNoun: noun,
|
|
217
|
+
verifyCommandHint: options.verifyCommandHint,
|
|
218
|
+
}),
|
|
219
|
+
communication: createCommunicationInstruction(),
|
|
220
|
+
hitl: createHitlInstruction(),
|
|
221
|
+
},
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export type Stdlib = ReturnType<typeof createStdlib>;
|
|
226
|
+
|
|
227
|
+
// The sandbox-backed counterpart to the stdlib's file tools, for the hosted
|
|
228
|
+
// topology: the eve process runs on one machine (a Vercel Function) and the
|
|
229
|
+
// workspace lives in the session's sandbox (`ctx.getSandbox()`) on another.
|
|
230
|
+
// The stdlib's `node:fs` tools would read the harness's own disk there — the
|
|
231
|
+
// wrong filesystem entirely — so these route every effect through the
|
|
232
|
+
// sandbox session instead (see ./sandbox-io.ts). Consumers wire them exactly
|
|
233
|
+
// like stdlib tools (one `agent/tools/<name>.ts` re-export each) and vacate
|
|
234
|
+
// eve's built-in `read_file`/`write_file`/`glob`/`grep` with disable shims;
|
|
235
|
+
// `bash` stays eve's built-in — it is already sandbox-native.
|
|
236
|
+
//
|
|
237
|
+
// Host-process machinery that needs a local disk or shell (bash, the task
|
|
238
|
+
// registry, steering) is deliberately absent: on a hosted agent those either
|
|
239
|
+
// stay eve built-ins or don't apply.
|
|
240
|
+
|
|
241
|
+
export interface SandboxFileToolsOptions {
|
|
242
|
+
/**
|
|
243
|
+
* Absolute workspace root **inside the sandbox** (e.g. "/workspace").
|
|
244
|
+
* File tools refuse paths that escape it.
|
|
245
|
+
*/
|
|
246
|
+
workspaceRoot: string;
|
|
247
|
+
/** What tool descriptions call the workspace. Defaults to "workspace". */
|
|
248
|
+
workspaceNoun?: string;
|
|
249
|
+
/**
|
|
250
|
+
* Resolves the sandbox session for one tool call. Defaults to
|
|
251
|
+
* `ctx.getSandbox()`; injectable for tests.
|
|
252
|
+
*/
|
|
253
|
+
resolveSession?: SandboxIoOptions["resolveSession"];
|
|
254
|
+
/**
|
|
255
|
+
* Sandbox directory for grep's overflow match lists (spilled through the
|
|
256
|
+
* sandbox, so the model's follow-up `read` can reach them). Omit to keep
|
|
257
|
+
* the stop-at-cap behavior.
|
|
258
|
+
*/
|
|
259
|
+
spillDir?: string;
|
|
260
|
+
/**
|
|
261
|
+
* See {@link StdlibOptions.attachImagesToChat}. Defaults to `false` here —
|
|
262
|
+
* the attachment path only works when the agent registers
|
|
263
|
+
* `createParkDeliveryHook` AND its runtime can reach itself over loopback
|
|
264
|
+
* to send the next-turn message; hosted serverless runtimes haven't
|
|
265
|
+
* validated that leg. Until a consumer wires and verifies it, the honest
|
|
266
|
+
* default is the metadata-only note (a "queued" promise that never
|
|
267
|
+
* delivers is the silent failure the attachment contract exists to avoid).
|
|
268
|
+
*/
|
|
269
|
+
attachImagesToChat?: boolean;
|
|
270
|
+
/** See {@link StdlibOptions.maxInlineImageBytes}. */
|
|
271
|
+
maxInlineImageBytes?: number;
|
|
272
|
+
/** See {@link StdlibOptions.attachVideoToChat}. Defaults to `false`. */
|
|
273
|
+
attachVideoToChat?: boolean;
|
|
274
|
+
/** See {@link StdlibOptions.attachAudioToChat}. Defaults to `false`. */
|
|
275
|
+
attachAudioToChat?: boolean;
|
|
276
|
+
/** See {@link StdlibOptions.maxInlineMediaBytes}. */
|
|
277
|
+
maxInlineMediaBytes?: number;
|
|
278
|
+
/** See {@link StdlibOptions.injectDirConventions}. Defaults to `true`. */
|
|
279
|
+
injectDirConventions?: boolean;
|
|
280
|
+
/** See {@link StdlibOptions.conventionsFileName}. Defaults to "AGENTS.md". */
|
|
281
|
+
conventionsFileName?: string;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export function createSandboxFileTools(options: SandboxFileToolsOptions) {
|
|
285
|
+
const noun = options.workspaceNoun ?? "workspace";
|
|
286
|
+
const workspace = createWorkspace(options.workspaceRoot);
|
|
287
|
+
const io = sandboxIoProvider({
|
|
288
|
+
root: workspace.root,
|
|
289
|
+
...(options.resolveSession !== undefined
|
|
290
|
+
? { resolveSession: options.resolveSession }
|
|
291
|
+
: {}),
|
|
292
|
+
});
|
|
293
|
+
const conventionsFileName = options.conventionsFileName ?? "AGENTS.md";
|
|
294
|
+
const dirConventions =
|
|
295
|
+
(options.injectDirConventions ?? true)
|
|
296
|
+
? {
|
|
297
|
+
tracker: createDirConventionsTracker({
|
|
298
|
+
workspaceRoot: workspace.root,
|
|
299
|
+
fileName: conventionsFileName,
|
|
300
|
+
}),
|
|
301
|
+
fileName: conventionsFileName,
|
|
302
|
+
}
|
|
303
|
+
: undefined;
|
|
304
|
+
return {
|
|
305
|
+
workspace,
|
|
306
|
+
io,
|
|
307
|
+
tools: {
|
|
308
|
+
read: createReadTool({
|
|
309
|
+
workspace,
|
|
310
|
+
noun,
|
|
311
|
+
io,
|
|
312
|
+
attachImagesToChat: options.attachImagesToChat ?? false,
|
|
313
|
+
maxInlineImageBytes:
|
|
314
|
+
options.maxInlineImageBytes ?? DEFAULT_MAX_INLINE_IMAGE_BYTES,
|
|
315
|
+
attachVideoToChat: options.attachVideoToChat ?? false,
|
|
316
|
+
attachAudioToChat: options.attachAudioToChat ?? false,
|
|
317
|
+
maxInlineMediaBytes:
|
|
318
|
+
options.maxInlineMediaBytes ?? DEFAULT_MAX_INLINE_MEDIA_BYTES,
|
|
319
|
+
dirConventions,
|
|
320
|
+
}),
|
|
321
|
+
edit: createEditTool({ workspace, noun, io }),
|
|
322
|
+
write: createWriteTool({ workspace, noun, io }),
|
|
323
|
+
glob: createGlobTool({ workspace, noun, io }),
|
|
324
|
+
grep: createGrepTool({
|
|
325
|
+
workspace,
|
|
326
|
+
noun,
|
|
327
|
+
io,
|
|
328
|
+
...(options.spillDir !== undefined ? { spillDir: options.spillDir } : {}),
|
|
329
|
+
}),
|
|
330
|
+
},
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export type SandboxFileTools = ReturnType<typeof createSandboxFileTools>;
|
|
335
|
+
|
|
336
|
+
// À la carte surface: the tool/instruction factories and every lib module the
|
|
337
|
+
// tools are built from, for agents that compose their own set.
|
|
338
|
+
export { createBashTool } from "./tools/bash";
|
|
339
|
+
export { createEditTool } from "./tools/edit";
|
|
340
|
+
export { createGlobTool } from "./tools/glob";
|
|
341
|
+
export { createGrepTool } from "./tools/grep";
|
|
342
|
+
export { createReadTool } from "./tools/read";
|
|
343
|
+
export { buildTasksToolset, createTasksTools } from "./tools/tasks";
|
|
344
|
+
export { createWebFetchTool } from "./tools/webfetch";
|
|
345
|
+
export { createWriteTool } from "./tools/write";
|
|
346
|
+
|
|
347
|
+
export * from "./attachments";
|
|
348
|
+
export {
|
|
349
|
+
createParkDeliveryHook,
|
|
350
|
+
type ParkDeliveryOptions,
|
|
351
|
+
} from "./hooks";
|
|
352
|
+
export {
|
|
353
|
+
buildRedeliveryMessage,
|
|
354
|
+
clientContinuationToken,
|
|
355
|
+
createRedeliveryState,
|
|
356
|
+
type PendingRedelivery,
|
|
357
|
+
redeliveryFromEvent,
|
|
358
|
+
type RedeliveryMessagePart,
|
|
359
|
+
type RedeliveryRequest,
|
|
360
|
+
type RedeliveryState,
|
|
361
|
+
} from "./redeliver";
|
|
362
|
+
export {
|
|
363
|
+
createParkDeliveryState,
|
|
364
|
+
type ParkDeliveryItem,
|
|
365
|
+
type ParkDeliveryRequest,
|
|
366
|
+
type ParkDeliveryState,
|
|
367
|
+
type ParkNotification,
|
|
368
|
+
postParkNotification,
|
|
369
|
+
setParkNotificationHandler,
|
|
370
|
+
} from "./park-delivery";
|
|
371
|
+
export * from "./async-tasks";
|
|
372
|
+
export * from "./backgroundable";
|
|
373
|
+
export * from "./bounded-output";
|
|
374
|
+
export * from "./dir-conventions";
|
|
375
|
+
export * from "./task";
|
|
376
|
+
export * from "./extract/cache";
|
|
377
|
+
export * from "./extract/docx";
|
|
378
|
+
export * from "./extract/pdf";
|
|
379
|
+
export * from "./extract/sheet";
|
|
380
|
+
export * from "./file-kind";
|
|
381
|
+
export * from "./file-view";
|
|
382
|
+
export * from "./glob-match";
|
|
383
|
+
export * from "./web-fetch";
|
|
384
|
+
export * from "./instructions";
|
|
385
|
+
export * from "./list-files";
|
|
386
|
+
export * from "./read-file-content";
|
|
387
|
+
export * from "./read-text";
|
|
388
|
+
export * from "./run";
|
|
389
|
+
export * from "./steer";
|
|
390
|
+
export {
|
|
391
|
+
createSteerInbox,
|
|
392
|
+
type SteerInbox,
|
|
393
|
+
type SteerInboxOptions,
|
|
394
|
+
} from "./steer-inbox";
|
|
395
|
+
export {
|
|
396
|
+
createSteerWrapper,
|
|
397
|
+
type SteerSource,
|
|
398
|
+
withSteerDelivery,
|
|
399
|
+
} from "./steer-tool";
|
|
400
|
+
export {
|
|
401
|
+
createMockStoryModel,
|
|
402
|
+
lastUserTextFrom,
|
|
403
|
+
markdownChunks,
|
|
404
|
+
MOCK_SCENARIOS,
|
|
405
|
+
mockScenarioFrom,
|
|
406
|
+
scriptActionFor,
|
|
407
|
+
scriptStepFrom,
|
|
408
|
+
toolInputFragments,
|
|
409
|
+
type MockScenario,
|
|
410
|
+
type MockScriptAction,
|
|
411
|
+
type MockScriptedScenario,
|
|
412
|
+
type MockStoryModelOptions,
|
|
413
|
+
type MockToolCall,
|
|
414
|
+
} from "./mock-model";
|
|
415
|
+
export * from "./sandbox-io";
|
|
416
|
+
export * from "./walk";
|
|
417
|
+
export * from "./watch-output";
|
|
418
|
+
export * from "./workspace";
|
|
419
|
+
export * from "./workspace-io";
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { AuthFn } from "eve/channels/auth";
|
|
2
|
+
|
|
3
|
+
// The Zo channel auth for agents that live behind a Zo identity-injecting
|
|
4
|
+
// proxy (apps/api's builder eve-proxy and the tenant runtime router). The
|
|
5
|
+
// proxy verifies the browser's signed identity bearer and injects the resolved
|
|
6
|
+
// `{ userId, agentId }` as the trusted plaintext `x-zo-initiator` header; this
|
|
7
|
+
// AuthFn turns it into the session's durable `initiator` SessionAuthContext.
|
|
8
|
+
//
|
|
9
|
+
// The agent holds no secret and does NOT verify a signature — it trusts the
|
|
10
|
+
// header because only the edge-authenticated proxy can reach it (Vercel
|
|
11
|
+
// Deployment Protection; the proxy always strips a client-supplied value). An
|
|
12
|
+
// absent/malformed header returns null so a `none()` entry after this one in
|
|
13
|
+
// the channel's auth array degrades to anonymous (local dev, where there is no
|
|
14
|
+
// proxy to inject it) rather than 401ing.
|
|
15
|
+
//
|
|
16
|
+
// The header name + JSON shape MIRROR `@zocomputer/runtime-auth`'s
|
|
17
|
+
// `INITIATOR_HEADER`/`formatInitiator`/`parseInitiator` (the proxy side of the
|
|
18
|
+
// contract) — mirrored, not imported, so this package keeps zero
|
|
19
|
+
// `@zocomputer/*` deps (the same stance as `state.ts`'s vocabulary; rib's
|
|
20
|
+
// standalone `file:` install can't resolve workspace packages).
|
|
21
|
+
|
|
22
|
+
/** The proxy → agent initiator header (`@zocomputer/runtime-auth`'s INITIATOR_HEADER). */
|
|
23
|
+
export const INITIATOR_HEADER = "x-zo-initiator";
|
|
24
|
+
|
|
25
|
+
/** The initiator identity carried on `INITIATOR_HEADER`. */
|
|
26
|
+
export interface InitiatorIdentity {
|
|
27
|
+
readonly userId: string;
|
|
28
|
+
readonly agentId: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Parse-then-narrow the `INITIATOR_HEADER` value; `null` on absent/malformed. */
|
|
32
|
+
export function parseInitiator(value: string | null | undefined): InitiatorIdentity | null {
|
|
33
|
+
if (!value) return null;
|
|
34
|
+
let parsed: unknown;
|
|
35
|
+
try {
|
|
36
|
+
parsed = JSON.parse(value);
|
|
37
|
+
} catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
41
|
+
const { userId, agentId } = parsed as Record<string, unknown>;
|
|
42
|
+
if (typeof userId !== "string" || !userId) return null;
|
|
43
|
+
if (typeof agentId !== "string" || !agentId) return null;
|
|
44
|
+
return { userId, agentId };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** AuthFn that reads `x-zo-initiator` into the session's initiator identity. */
|
|
48
|
+
export const initiatorAuth: AuthFn = (request) => {
|
|
49
|
+
const identity = parseInitiator(request.headers.get(INITIATOR_HEADER));
|
|
50
|
+
if (!identity) return null;
|
|
51
|
+
return {
|
|
52
|
+
principalId: identity.userId,
|
|
53
|
+
principalType: "user",
|
|
54
|
+
authenticator: "zo-initiator",
|
|
55
|
+
subject: identity.userId,
|
|
56
|
+
attributes: { agentId: identity.agentId },
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/** What a tool/hook reads off `session.auth.initiator` to authorize its work. */
|
|
61
|
+
interface InitiatorReadable {
|
|
62
|
+
readonly attributes?: Readonly<Record<string, string | readonly string[]>>;
|
|
63
|
+
readonly subject?: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Pull `{ userId, agentId }` off a session's `initiator` SessionAuthContext, or
|
|
68
|
+
* `null` when it's absent (local dev, unscoped session) or malformed —
|
|
69
|
+
* `agentId` from `attributes.agentId`, `userId` from `subject` (matching
|
|
70
|
+
* `initiatorAuth` above).
|
|
71
|
+
*/
|
|
72
|
+
export function readInitiator(
|
|
73
|
+
initiator: InitiatorReadable | null | undefined,
|
|
74
|
+
): InitiatorIdentity | null {
|
|
75
|
+
if (!initiator) return null;
|
|
76
|
+
const agentId = initiator.attributes?.agentId;
|
|
77
|
+
const userId = initiator.subject;
|
|
78
|
+
if (typeof agentId !== "string" || !agentId) return null;
|
|
79
|
+
if (typeof userId !== "string" || !userId) return null;
|
|
80
|
+
return { userId, agentId };
|
|
81
|
+
}
|