@shardflux/sdk 0.6.1 → 0.7.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/CHANGELOG.md +105 -1
- package/README.md +216 -11
- package/dist/capture-adapters.d.ts +178 -0
- package/dist/capture-adapters.js +432 -0
- package/dist/capture-serialize.d.ts +97 -0
- package/dist/capture-serialize.js +481 -0
- package/dist/capture-text.d.ts +14 -0
- package/dist/capture-text.js +13 -0
- package/dist/capture.d.ts +288 -0
- package/dist/capture.js +1326 -0
- package/dist/cell.d.ts +7 -0
- package/dist/cell.js +13 -0
- package/dist/client.d.ts +24 -2
- package/dist/client.js +24 -5
- package/dist/egress.d.ts +18 -0
- package/dist/errors.d.ts +21 -2
- package/dist/errors.js +19 -2
- package/dist/generated/app-api.d.ts +2900 -193
- package/dist/http.d.ts +1 -1
- package/dist/http.js +1 -1
- package/dist/index.d.ts +10 -3
- package/dist/index.js +4 -1
- package/dist/lifecycle.d.ts +5 -2
- package/dist/lifecycle.js +5 -1
- package/dist/progress.d.ts +17 -3
- package/dist/progress.js +18 -4
- package/dist/tar.d.ts +40 -0
- package/dist/tar.js +150 -0
- package/dist/template-file.d.ts +92 -0
- package/dist/template-file.js +326 -0
- package/dist/templates.d.ts +318 -9
- package/dist/templates.js +432 -13
- package/dist/tools.d.ts +6 -13
- package/dist/tools.js +23 -3
- package/dist/workspace.d.ts +28 -3
- package/dist/workspace.js +52 -3
- package/package.json +24 -2
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import type { CellClient } from './cell.js';
|
|
2
|
+
import type { CaptureAdapters } from './capture-adapters.js';
|
|
3
|
+
export type { CapturePart, DropReason } from './capture-serialize.js';
|
|
4
|
+
/** `status` of an index line. */
|
|
5
|
+
export type CaptureStatus = 'ok' | 'error' | 'cancelled' | 'incomplete' | 'retry';
|
|
6
|
+
/** The integration that recorded a call (`source` of an index line). */
|
|
7
|
+
export type CaptureSource = 'manual' | 'wrap' | 'ai-sdk' | 'mastra' | 'anthropic' | 'openai-agents' | 'claude-agent-sdk' | 'langchain' | 'mcp' | 'workspace' | (string & {});
|
|
8
|
+
/** Tool names, a pattern, or a predicate. Applies to hook-level capture only. */
|
|
9
|
+
export type CaptureSelector = readonly string[] | RegExp | ((tool: string, source: CaptureSource) => boolean);
|
|
10
|
+
/** A call as `record()` takes it. */
|
|
11
|
+
export interface CaptureCall {
|
|
12
|
+
tool: string;
|
|
13
|
+
input?: unknown;
|
|
14
|
+
/** The result as the tool returned it (bytes, strings, objects, MCP results, content blocks...). */
|
|
15
|
+
output?: unknown;
|
|
16
|
+
/** What the tool threw: recorded as `{type, message}`; the status defaults to `error` (`cancelled` for an AbortError). */
|
|
17
|
+
error?: unknown;
|
|
18
|
+
status?: CaptureStatus;
|
|
19
|
+
/** The model's tool call id (`toolu_…`, `call_…`): deduplicates, and lets the agent find the call. */
|
|
20
|
+
callId?: string | null;
|
|
21
|
+
/** When the call started (Date, epoch milliseconds or ISO string). */
|
|
22
|
+
startedAt?: Date | number | string | null;
|
|
23
|
+
durationMs?: number | null;
|
|
24
|
+
meta?: Record<string, unknown>;
|
|
25
|
+
source?: CaptureSource;
|
|
26
|
+
}
|
|
27
|
+
/** A recorded call: its run and sequence number. `seq` names its files (`<seq>-<tool>…`). */
|
|
28
|
+
export interface CallRef {
|
|
29
|
+
run: string;
|
|
30
|
+
seq: number;
|
|
31
|
+
callId: string | null;
|
|
32
|
+
}
|
|
33
|
+
/** What `transform` receives (a copy) and returns. Return null to drop the call. */
|
|
34
|
+
export interface CaptureEvent {
|
|
35
|
+
tool: string;
|
|
36
|
+
callId: string | null;
|
|
37
|
+
source: CaptureSource;
|
|
38
|
+
status: CaptureStatus;
|
|
39
|
+
input: unknown;
|
|
40
|
+
output: unknown;
|
|
41
|
+
error: {
|
|
42
|
+
type: string;
|
|
43
|
+
message: string;
|
|
44
|
+
} | null;
|
|
45
|
+
startedAt: string | null;
|
|
46
|
+
durationMs: number | null;
|
|
47
|
+
meta: Record<string, unknown>;
|
|
48
|
+
}
|
|
49
|
+
export type CaptureErrorKind = 'write' | 'queue_full' | 'too_large' | 'serialize' | 'transform' | 'gone' | 'discarded' | 'timeout';
|
|
50
|
+
/** A capture problem, reported to `onError` (never thrown into the harness). */
|
|
51
|
+
export declare class CaptureError extends Error {
|
|
52
|
+
readonly kind: CaptureErrorKind;
|
|
53
|
+
readonly seq: number | undefined;
|
|
54
|
+
readonly tool: string | undefined;
|
|
55
|
+
readonly callId: string | null | undefined;
|
|
56
|
+
/** The workspace path of a failed write. */
|
|
57
|
+
readonly path: string | undefined;
|
|
58
|
+
constructor(kind: CaptureErrorKind, message: string, extra?: {
|
|
59
|
+
seq?: number;
|
|
60
|
+
tool?: string;
|
|
61
|
+
callId?: string | null;
|
|
62
|
+
path?: string;
|
|
63
|
+
cause?: unknown;
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
export interface ToolCallCaptureOptions {
|
|
67
|
+
/** Absolute directory in the workspace (default `/home/user/tool-calls`). Point it at a subdirectory to group runs. */
|
|
68
|
+
dir?: string;
|
|
69
|
+
/** Hook-level adapters capture only these tools (explicit `record`/`run`/`wrap`/`tools` always record). */
|
|
70
|
+
include?: CaptureSelector;
|
|
71
|
+
/** Hook-level adapters skip these tools. */
|
|
72
|
+
exclude?: CaptureSelector;
|
|
73
|
+
/**
|
|
74
|
+
* Redacts or drops a call before it is written: receives a copy, returns the event to write or null to drop it. If it
|
|
75
|
+
* throws, the call is dropped (never written unredacted) and `onError` gets a `transform` error.
|
|
76
|
+
*/
|
|
77
|
+
transform?: (event: CaptureEvent) => CaptureEvent | null | undefined;
|
|
78
|
+
/** Per call (default 32 MiB). Text and JSON over it are cut (`.part`, `truncated`); binary is not stored (`too_large`). */
|
|
79
|
+
maxOutputBytes?: number;
|
|
80
|
+
/** Inputs whose JSON is larger go to `<seq>-<tool>.input.json` (default 64 KiB). */
|
|
81
|
+
maxInlineInputBytes?: number;
|
|
82
|
+
/**
|
|
83
|
+
* Bytes held for pending calls: files, index lines and inline inputs (default 128 MiB). Over it the output (and a
|
|
84
|
+
* large input) is dropped and the call keeps a small index line with `dropped: "queue_full"`; the call never blocks.
|
|
85
|
+
*/
|
|
86
|
+
maxPendingBytes?: number;
|
|
87
|
+
/**
|
|
88
|
+
* Calls recorded but not yet written (default 10 000), the hard bound on memory while the workspace is unreachable.
|
|
89
|
+
* Over it a call is not recorded at all (no index line; `record()` returns null), reported to `onError` as
|
|
90
|
+
* `queue_full` and counted in `stats.dropped`.
|
|
91
|
+
*/
|
|
92
|
+
maxPendingCalls?: number;
|
|
93
|
+
/** Parallel file writes (default 4). */
|
|
94
|
+
concurrency?: number;
|
|
95
|
+
/** Index lines are appended every `batchDelayMs` (default 50) or 1 MiB. */
|
|
96
|
+
batchDelayMs?: number;
|
|
97
|
+
/** How long one write is retried (default 120 000 ms); then it is dropped as `write_failed`. */
|
|
98
|
+
retryWindowMs?: number;
|
|
99
|
+
/** Bound on the wait of `flush()`, `close()` and the read-your-writes barrier (default 30 000 ms). */
|
|
100
|
+
settleTimeoutMs?: number;
|
|
101
|
+
/** Tool-token attribution label for the capture's writes (default: the workspace handle's). */
|
|
102
|
+
agentLabel?: string;
|
|
103
|
+
/** Wake-on-use for the capture's writes (default: `workspace.wake()`); `null`: never wake, writes retry and drop. */
|
|
104
|
+
wake?: ((timeoutMs: number, signal?: AbortSignal) => Promise<boolean | void>) | null;
|
|
105
|
+
/** Lifecycle wait budget per write (busy waits plus wakes; default 120 000 ms). */
|
|
106
|
+
transitionTimeoutMs?: number;
|
|
107
|
+
/** Merged into every index line's `meta`. */
|
|
108
|
+
metadata?: Record<string, unknown>;
|
|
109
|
+
/** Write failures, drops, transform errors. Never thrown into the harness. */
|
|
110
|
+
onError?: (error: CaptureError) => void;
|
|
111
|
+
}
|
|
112
|
+
export interface CaptureStats {
|
|
113
|
+
/** Calls accepted (a sequence number was assigned). */
|
|
114
|
+
recorded: number;
|
|
115
|
+
/** Index lines appended (a call whose output was dropped still counts: its line says why). */
|
|
116
|
+
written: number;
|
|
117
|
+
/** Calls whose index line could not be written (retries exhausted, workspace gone, close timeout). */
|
|
118
|
+
failed: number;
|
|
119
|
+
/**
|
|
120
|
+
* Outputs not stored (`queue_full`, `too_large`, `write_failed`, `serialize_failed`), calls a throwing transform
|
|
121
|
+
* dropped, and calls not recorded because `maxPendingCalls` were pending.
|
|
122
|
+
*/
|
|
123
|
+
dropped: number;
|
|
124
|
+
/** Calls `transform` returned null for. */
|
|
125
|
+
skipped: number;
|
|
126
|
+
/** Records ignored because the call id was already recorded. */
|
|
127
|
+
duplicates: number;
|
|
128
|
+
/** Pending calls dropped by delete/reset/close({discard}). */
|
|
129
|
+
discarded: number;
|
|
130
|
+
/** Write attempts retried. */
|
|
131
|
+
retries: number;
|
|
132
|
+
/** Calls recorded but not yet settled. */
|
|
133
|
+
pending: number;
|
|
134
|
+
/** Bytes held for pending calls (files, index lines, inline inputs). */
|
|
135
|
+
pendingBytes: number;
|
|
136
|
+
}
|
|
137
|
+
export interface CaptureFlushResult {
|
|
138
|
+
/** True when every call recorded before the call was settled within the timeout. */
|
|
139
|
+
complete: boolean;
|
|
140
|
+
written: number;
|
|
141
|
+
failed: number;
|
|
142
|
+
dropped: number;
|
|
143
|
+
}
|
|
144
|
+
/** A tool call in a harness's own shape: Anthropic `tool_use`, Responses `function_call`, Chat `tool_calls[i]`, or `{name, input}`. */
|
|
145
|
+
export interface CallLike {
|
|
146
|
+
/** `tool_use`, `function_call`, `function`, `tool_call` (not used). */
|
|
147
|
+
type?: string;
|
|
148
|
+
id?: string | null;
|
|
149
|
+
call_id?: string | null;
|
|
150
|
+
callId?: string | null;
|
|
151
|
+
toolCallId?: string | null;
|
|
152
|
+
name?: string;
|
|
153
|
+
toolName?: string;
|
|
154
|
+
input?: unknown;
|
|
155
|
+
args?: unknown;
|
|
156
|
+
arguments?: unknown;
|
|
157
|
+
function?: {
|
|
158
|
+
name?: string;
|
|
159
|
+
arguments?: unknown;
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
export interface WrapOptions<A extends unknown[] = unknown[]> {
|
|
163
|
+
/** The call id from the arguments (default none). */
|
|
164
|
+
callId?: (...args: A) => string | null | undefined;
|
|
165
|
+
/** What to record as the input (default: the only argument, else the argument list). */
|
|
166
|
+
input?: (...args: A) => unknown;
|
|
167
|
+
source?: CaptureSource;
|
|
168
|
+
meta?: Record<string, unknown>;
|
|
169
|
+
}
|
|
170
|
+
/** The workspace side a capture writes through (created by `workspace.captureToolCalls()`). */
|
|
171
|
+
export interface CaptureHost {
|
|
172
|
+
workspaceId: string;
|
|
173
|
+
/** The capture's own cell client: no read-your-writes barrier (writes never wait on themselves). */
|
|
174
|
+
cell: CellClient;
|
|
175
|
+
registry: CaptureRegistry;
|
|
176
|
+
/** Backoff sleep (the client's injected sleep, else an unref'd timer). */
|
|
177
|
+
sleep?: (ms: number) => Promise<void>;
|
|
178
|
+
}
|
|
179
|
+
/** Internal hooks the registry calls. */
|
|
180
|
+
export declare const SETTLE: unique symbol;
|
|
181
|
+
export declare const DISCARD: unique symbol;
|
|
182
|
+
/**
|
|
183
|
+
* The captures of one client, by workspace id: Shardflux calls on the same client wait for writes recorded before them
|
|
184
|
+
* (read-your-writes), and delete/reset discard pending writes.
|
|
185
|
+
*/
|
|
186
|
+
export declare class CaptureRegistry {
|
|
187
|
+
#private;
|
|
188
|
+
add(workspaceId: string, capture: ToolCallCapture): void;
|
|
189
|
+
remove(workspaceId: string, capture: ToolCallCapture): void;
|
|
190
|
+
/**
|
|
191
|
+
* Waits for writes recorded before this call (not for later ones), each capture bounded by its settle timeout.
|
|
192
|
+
* Undefined when nothing is pending, so callers skip the await. Never rejects.
|
|
193
|
+
*/
|
|
194
|
+
settle(workspaceId: string): Promise<void> | undefined;
|
|
195
|
+
/** Drops pending writes of the workspace (delete: the captures also close; reset: the README is written again). */
|
|
196
|
+
discard(workspaceId: string, reason: 'delete' | 'reset'): void;
|
|
197
|
+
}
|
|
198
|
+
/** `YYYYMMDDTHHMMSSmmmZ-xxxxxx`: sortable UTC start time plus 6 random base36 characters. */
|
|
199
|
+
export declare function newRunId(now?: Date): string;
|
|
200
|
+
/**
|
|
201
|
+
* Epoch milliseconds from a Date, a number or an ISO string; null when absent, invalid or out of range (epoch
|
|
202
|
+
* nanoseconds or seconds passed by mistake must not reach toISOString(), which throws for them).
|
|
203
|
+
*/
|
|
204
|
+
export declare function toMs(v: unknown): number | null;
|
|
205
|
+
/**
|
|
206
|
+
* Wraps `fn` so each call is recorded into the capture active at call time (`capture.activate()`), and passes
|
|
207
|
+
* straight through when none is. For tools defined at import time in multi-tenant servers.
|
|
208
|
+
*/
|
|
209
|
+
export declare function captureTool<F extends (...args: never[]) => unknown>(name: string, fn: F, opts?: WrapOptions<Parameters<F>>): F;
|
|
210
|
+
export declare class ToolCallCapture {
|
|
211
|
+
#private;
|
|
212
|
+
/** `YYYYMMDDTHHMMSSmmmZ-xxxxxx`. */
|
|
213
|
+
readonly runId: string;
|
|
214
|
+
/** The capture directory (`dir`). */
|
|
215
|
+
readonly dir: string;
|
|
216
|
+
/** `<dir>/<runId>`: index.jsonl and the output files. */
|
|
217
|
+
readonly runDir: string;
|
|
218
|
+
readonly workspaceId: string;
|
|
219
|
+
constructor(host: CaptureHost, opts?: ToolCallCaptureOptions);
|
|
220
|
+
/** Counters (a snapshot). */
|
|
221
|
+
get stats(): CaptureStats;
|
|
222
|
+
/** True after close() (or when the workspace was deleted): records are ignored. */
|
|
223
|
+
get closed(): boolean;
|
|
224
|
+
/**
|
|
225
|
+
* Records one finished call. Synchronous and cheap (serialization happens later); never throws. Returns null when the
|
|
226
|
+
* capture is closed or the call id was already recorded.
|
|
227
|
+
*/
|
|
228
|
+
record(call: CaptureCall): CallRef | null;
|
|
229
|
+
/**
|
|
230
|
+
* Runs `fn` (your tool) for a model's tool call and records it: Anthropic `tool_use {id, name, input}`, Responses
|
|
231
|
+
* `function_call {call_id, name, arguments}`, a Chat `tool_calls` item `{id, function: {name, arguments}}`, or
|
|
232
|
+
* `{name, input}`. Returns exactly what `fn` returns (the same promise object; a sync result stays sync).
|
|
233
|
+
*/
|
|
234
|
+
run<T>(call: CallLike, fn: () => T): T;
|
|
235
|
+
/**
|
|
236
|
+
* Wraps a tool function: every call is recorded. The wrapper returns what `fn` returns (the same value, the same
|
|
237
|
+
* promise object, the same thrown error; sync stays sync). An async generator is passed through item by item and its
|
|
238
|
+
* items are stored as `.jsonl`.
|
|
239
|
+
*/
|
|
240
|
+
wrap<F extends (...args: never[]) => unknown>(name: string, fn: F, opts?: WrapOptions<Parameters<F>>): F;
|
|
241
|
+
/**
|
|
242
|
+
* Wraps a collection of tools and returns a copy of the same shape (objects are copied with their prototype; the
|
|
243
|
+
* originals are not modified). Detects `execute` (AI SDK, Mastra, Shardflux workspaceTools), `run` (Anthropic tool
|
|
244
|
+
* runner), `invoke` (OpenAI Agents FunctionTool, LangChain tools) and plain functions in an object. Throws TypeError
|
|
245
|
+
* for any other shape.
|
|
246
|
+
*/
|
|
247
|
+
tools<T>(tools: T): T;
|
|
248
|
+
/** Runs `fn` with this capture active for `captureTool()` wrappers (AsyncLocalStorage). */
|
|
249
|
+
activate<T>(fn: () => T): T;
|
|
250
|
+
/** The paragraph you can put in a system prompt so the agent knows where its tool calls are (never injected). */
|
|
251
|
+
promptHint(): string;
|
|
252
|
+
/**
|
|
253
|
+
* Waits until every call recorded before it is written (or has failed), at most `timeoutMs` (default
|
|
254
|
+
* settleTimeoutMs). Never rejects. Serverless: `waitUntil(capture.flush())`, or `await capture.flush()` before
|
|
255
|
+
* returning.
|
|
256
|
+
*/
|
|
257
|
+
flush(opts?: {
|
|
258
|
+
timeoutMs?: number;
|
|
259
|
+
}): Promise<CaptureFlushResult>;
|
|
260
|
+
/**
|
|
261
|
+
* Stops the capture: flushes what was recorded (bounded by `timeoutMs`), or drops it with `discard: true`, then
|
|
262
|
+
* ignores later records. Never rejects; calling it again returns the first close's result.
|
|
263
|
+
*/
|
|
264
|
+
close(opts?: {
|
|
265
|
+
timeoutMs?: number;
|
|
266
|
+
discard?: boolean;
|
|
267
|
+
}): Promise<CaptureFlushResult>;
|
|
268
|
+
/** Framework adapters (created on first use). */
|
|
269
|
+
get adapters(): CaptureAdapters;
|
|
270
|
+
/** Vercel AI SDK: `tools: capture.aiSdk.tools(tools)` or `...capture.aiSdk.callbacks()`. */
|
|
271
|
+
get aiSdk(): CaptureAdapters['aiSdk'];
|
|
272
|
+
/** Mastra: `hooks: capture.mastra.hooks()` or `tools: capture.mastra.tools({...})`. */
|
|
273
|
+
get mastra(): CaptureAdapters['mastra'];
|
|
274
|
+
/** Anthropic SDK tool runner: `tools: capture.anthropic.tools([...])`. */
|
|
275
|
+
get anthropic(): CaptureAdapters['anthropic'];
|
|
276
|
+
/** OpenAI Agents JS: `capture.openaiAgents.attach(runner)` or `tools: capture.openaiAgents.tools([...])`. */
|
|
277
|
+
get openaiAgents(): CaptureAdapters['openaiAgents'];
|
|
278
|
+
/** Claude Agent SDK: `options.hooks = capture.claude.hooks(myHooks)`. */
|
|
279
|
+
get claude(): CaptureAdapters['claude'];
|
|
280
|
+
/** LangChain.js / LangGraph.js: `callbacks: [capture.langchain.handler()]`. */
|
|
281
|
+
get langchain(): CaptureAdapters['langchain'];
|
|
282
|
+
/** MCP client: `const release = capture.mcp.instrument(client, { server: 'github' })`. */
|
|
283
|
+
get mcp(): CaptureAdapters['mcp'];
|
|
284
|
+
/** Read-your-writes: waits for calls recorded so far (bounded, never rejects); undefined when nothing is pending. */
|
|
285
|
+
[SETTLE](): Promise<boolean> | undefined;
|
|
286
|
+
/** delete: drops pending writes and closes; reset: drops pending writes, the README is written again. */
|
|
287
|
+
[DISCARD](reason: 'delete' | 'reset'): void;
|
|
288
|
+
}
|