@flue/sdk 0.2.0 → 0.3.1

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.
@@ -33,19 +33,22 @@ interface CommandDef {
33
33
  name: string;
34
34
  env?: Record<string, string>;
35
35
  }
36
+ type ToolParameters = TSchema | Record<string, unknown>;
36
37
  /**
37
- * Custom tool passed to prompt() or skill(). Scoped to the duration of the call.
38
- * Parameters use TypeBox schemas import `Type` from `@flue/sdk/client`.
38
+ * Custom tool passed to init(), prompt(), skill(), or task(). init() tools are
39
+ * available to every session call; prompt/skill/task tools are scoped to that call.
40
+ * Parameters are JSON Schema-compatible. Use `Type` from `@flue/sdk/client` for
41
+ * hand-written tools, or pass schemas discovered from adapters such as MCP.
39
42
  */
40
- interface ToolDef<TParams extends TSchema = TSchema> {
43
+ interface ToolDef<TParams extends ToolParameters = ToolParameters> {
41
44
  /** Must be unique across built-in and custom tools. */
42
45
  name: string;
43
46
  /** Tells the LLM when and how to use this tool. */
44
47
  description: string;
45
- /** TypeBox parameter schema. */
48
+ /** JSON Schema-compatible parameter schema. */
46
49
  parameters: TParams;
47
50
  /** Returns a string result sent back to the LLM. Thrown errors become tool errors. */
48
- execute: (args: Record<string, any>) => Promise<string>;
51
+ execute: (args: Record<string, any>, signal?: AbortSignal) => Promise<string>;
49
52
  }
50
53
  interface FileStat {
51
54
  isFile: boolean;
@@ -54,11 +57,6 @@ interface FileStat {
54
57
  size: number;
55
58
  mtime: Date;
56
59
  }
57
- /** Registers commands into the isolate's bash. Only present when the sandbox supports it. */
58
- interface CommandSupport {
59
- register(cmd: Command): void;
60
- unregister(name: string): void;
61
- }
62
60
  /**
63
61
  * Universal session environment interface. All sandbox modes (isolate, local, remote)
64
62
  * implement this — no mode-specific branching needed in core logic.
@@ -70,6 +68,10 @@ interface SessionEnv {
70
68
  cwd?: string;
71
69
  env?: Record<string, string>;
72
70
  }): Promise<ShellResult>;
71
+ /** Create an operation-scoped environment, usually backed by a fresh Bash runtime. */
72
+ scope?(options?: {
73
+ commands?: Command[];
74
+ }): Promise<SessionEnv>;
73
75
  readFile(path: string): Promise<string>;
74
76
  readFileBuffer(path: string): Promise<Uint8Array>;
75
77
  writeFile(path: string, content: string | Uint8Array): Promise<void>;
@@ -90,8 +92,6 @@ interface SessionEnv {
90
92
  * for your own logic (e.g., extracting the parent directory).
91
93
  */
92
94
  resolvePath(p: string): string;
93
- /** Only present with isolate/local sandboxes. Undefined for remote sandboxes. */
94
- commandSupport?: CommandSupport;
95
95
  cleanup(): Promise<void>;
96
96
  }
97
97
  interface CompactionConfig {
@@ -108,53 +108,90 @@ interface AgentConfig {
108
108
  skills: Record<string, Skill>;
109
109
  roles: Record<string, Role>;
110
110
  /**
111
- * Session-wide default model. Undefined by default — the user must set it via
111
+ * Agent-wide default model. Undefined by default — the user must set it via
112
112
  * `init({ model: "provider/model-id" })` or pass `{ model }` at each prompt/
113
113
  * skill/task call site. Calls with no model resolved throw clearly at runtime.
114
114
  */
115
115
  model: Model<any> | undefined;
116
+ /** Agent-wide default role. Per-session and per-call roles override this. */
117
+ role?: string;
116
118
  /** Resolve a "provider/modelId" string to a Model instance. Throws on invalid input. */
117
119
  resolveModel?: (modelString: string) => Model<any>;
118
120
  compaction?: CompactionConfig;
119
121
  }
120
122
  /** Request context passed to agent handler functions. */
121
123
  interface FlueContext {
122
- readonly sessionId: string;
124
+ readonly id: string;
123
125
  readonly payload: any;
124
126
  /** Platform env bindings (process.env on Node, Worker env on Cloudflare). */
125
127
  readonly env: Record<string, any>;
126
- /** Create a session with sandbox + persistence. Can only be called once per request. */
127
- init(options?: SessionInit): Promise<FlueSession>;
128
+ /** Initialize an agent runtime with sandbox + persistence. */
129
+ init(options?: AgentInit): Promise<FlueAgent>;
128
130
  }
129
131
  /** All fields are optional — omitting gives platform defaults (empty sandbox, platform store, build-time model). */
130
- interface SessionInit {
132
+ interface AgentInit {
133
+ /** Agent/sandbox scope id. Defaults to the route/context id. */
134
+ id?: string;
135
+ /** Working directory for context discovery, tools, and shell calls. Defaults to the sandbox cwd. */
136
+ cwd?: string;
131
137
  /**
132
138
  * - `'empty'` (default): In-memory sandbox, no files, no host access.
133
139
  * - `'local'`: Mounts process.cwd() at /workspace. Node only.
134
- * - `BashLike`: User-configured just-bash instance.
140
+ * - `BashFactory`: User-configured just-bash factory. Must return a fresh Bash-like instance.
135
141
  * - `SandboxFactory`: Connector-wrapped external sandbox (Daytona, CF Containers, etc.).
136
142
  */
137
- sandbox?: 'empty' | 'local' | SandboxFactory | BashLike;
143
+ sandbox?: 'empty' | 'local' | SandboxFactory | BashFactory;
138
144
  /** Defaults to platform store (in-memory on Node, DO SQLite on Cloudflare). */
139
145
  persist?: SessionStore;
140
146
  /**
141
- * Override the default model for this session. Applies to all prompt(), skill(),
142
- * and task() calls unless overridden at the call site.
147
+ * Override the default model for this agent. Applies to all prompt() and skill()
148
+ * calls unless overridden at the call site.
143
149
  *
144
150
  * Format: `'provider/modelId'` (e.g. `'anthropic/claude-opus-4-20250514'`).
145
151
  *
146
- * Precedence (highest wins): per-call `model` > role `model` > session `model` > build-time default.
152
+ * Precedence (highest wins): per-call `model` > role `model` > agent `model` > build-time default.
147
153
  */
148
154
  model?: string;
155
+ /** Agent-wide default role. Overridden by session-level or per-call roles. */
156
+ role?: string;
149
157
  /**
150
- * Session-wide commands. Every prompt(), skill(), and shell() call inherits
158
+ * Agent-wide tools. Every prompt(), skill(), and task() call can use these.
159
+ * Per-call tools are added on top and must not reuse the same names.
160
+ */
161
+ tools?: ToolDef[];
162
+ /**
163
+ * Agent-wide commands. Every prompt(), skill(), and shell() call inherits
151
164
  * this list. Per-call `commands` are merged on top — if a per-call command
152
- * shares a name with a session command, the per-call version wins for that
165
+ * shares a name with an agent command, the per-call version wins for that
153
166
  * call.
154
167
  */
155
168
  commands?: Command[];
156
169
  }
170
+ interface FlueAgent {
171
+ readonly id: string;
172
+ /** Get or create a session in this agent. Defaults to the "default" session. */
173
+ session(id?: string, options?: SessionOptions): Promise<FlueSession>;
174
+ /** Explicit session management helpers. */
175
+ readonly sessions: FlueSessions;
176
+ /** Run a shell command in the agent sandbox without recording it in a conversation. */
177
+ shell(command: string, options?: ShellOptions): Promise<ShellResult>;
178
+ /** Destroy the agent runtime and clean up the sandbox resources it owns. */
179
+ destroy(): Promise<void>;
180
+ }
181
+ interface FlueSessions {
182
+ /** Load an existing session. Throws if it does not exist. */
183
+ get(id?: string, options?: SessionOptions): Promise<FlueSession>;
184
+ /** Create a new session. Throws if it already exists. */
185
+ create(id?: string, options?: SessionOptions): Promise<FlueSession>;
186
+ /** Delete a session's stored conversation state. No-op when missing. */
187
+ delete(id?: string): Promise<void>;
188
+ }
189
+ interface SessionOptions {
190
+ /** Session-wide default role. Per-call roles override this. */
191
+ role?: string;
192
+ }
157
193
  interface FlueSession {
194
+ readonly id: string;
158
195
  prompt<S extends v.GenericSchema>(text: string, options: PromptOptions<S> & {
159
196
  result: S;
160
197
  }): Promise<v.InferOutput<S>>;
@@ -164,30 +201,51 @@ interface FlueSession {
164
201
  result: S;
165
202
  }): Promise<v.InferOutput<S>>;
166
203
  skill(name: string, options?: SkillOptions): Promise<PromptResponse>;
167
- /** Sub-agent task with its own conversation history, context discovery, and compaction. */
168
- task<S extends v.GenericSchema>(prompt: string, options: TaskOptions<S> & {
204
+ task<S extends v.GenericSchema>(text: string, options: TaskOptions<S> & {
169
205
  result: S;
170
206
  }): Promise<v.InferOutput<S>>;
171
- task(prompt: string, options?: TaskOptions): Promise<PromptResponse>;
172
- destroy(): Promise<void>;
207
+ task(text: string, options?: TaskOptions): Promise<PromptResponse>;
208
+ delete(): Promise<void>;
173
209
  }
174
210
  interface PromptResponse {
175
211
  text: string;
176
212
  }
177
213
  interface SessionData {
178
- messages: AgentMessage[];
214
+ version: 2;
215
+ entries: SessionEntry[];
216
+ leafId: string | null;
179
217
  metadata: Record<string, any>;
180
218
  createdAt: string;
181
219
  updatedAt: string;
182
- lastCompaction?: {
183
- summary: string;
184
- firstKeptIndex: number;
185
- details?: {
186
- readFiles: string[];
187
- modifiedFiles: string[];
188
- };
220
+ }
221
+ type SessionEntry = MessageEntry | CompactionEntry | BranchSummaryEntry;
222
+ interface SessionEntryBase {
223
+ type: string;
224
+ id: string;
225
+ parentId: string | null;
226
+ timestamp: string;
227
+ }
228
+ interface MessageEntry extends SessionEntryBase {
229
+ type: 'message';
230
+ message: AgentMessage;
231
+ source?: 'prompt' | 'skill' | 'shell' | 'task' | 'retry';
232
+ }
233
+ interface CompactionEntry extends SessionEntryBase {
234
+ type: 'compaction';
235
+ summary: string;
236
+ firstKeptEntryId: string;
237
+ tokensBefore: number;
238
+ details?: {
239
+ readFiles: string[];
240
+ modifiedFiles: string[];
189
241
  };
190
242
  }
243
+ interface BranchSummaryEntry extends SessionEntryBase {
244
+ type: 'branch_summary';
245
+ fromId: string;
246
+ summary: string;
247
+ details?: unknown;
248
+ }
191
249
  interface SessionStore {
192
250
  save(id: string, data: SessionData): Promise<void>;
193
251
  load(id: string): Promise<SessionData | null>;
@@ -213,11 +271,13 @@ interface SkillOptions<S extends v.GenericSchema | undefined = undefined> {
213
271
  model?: string;
214
272
  }
215
273
  interface TaskOptions<S extends v.GenericSchema | undefined = undefined> {
216
- /** Workspace directory — AGENTS.md and skills are discovered from here. */
217
- workspace?: string;
218
274
  result?: S;
275
+ commands?: Command[];
276
+ tools?: ToolDef[];
219
277
  role?: string;
220
278
  model?: string;
279
+ /** Working directory for the detached task session. Defaults to the parent session cwd. */
280
+ cwd?: string;
221
281
  }
222
282
  interface ShellOptions {
223
283
  env?: Record<string, string>;
@@ -233,8 +293,8 @@ interface ShellResult {
233
293
  /** Wraps external sandboxes (Daytona, CF Containers, etc.) into Flue's SessionEnv. */
234
294
  interface SandboxFactory {
235
295
  createSessionEnv(options: {
236
- sessionId: string;
237
- workspace?: string;
296
+ id: string;
297
+ cwd?: string;
238
298
  }): Promise<SessionEnv>;
239
299
  }
240
300
  /**
@@ -265,7 +325,9 @@ interface BashLike {
265
325
  };
266
326
  registerCommand?(cmd: any): void;
267
327
  }
268
- type FlueEvent = {
328
+ /** Factory for a fresh Bash-like runtime. Share `fs` inside the closure to persist files. */
329
+ type BashFactory = () => BashLike | Promise<BashLike>;
330
+ type FlueEvent = ({
269
331
  type: 'agent_start';
270
332
  } | {
271
333
  type: 'text_delta';
@@ -291,6 +353,17 @@ type FlueEvent = {
291
353
  type: 'command_end';
292
354
  command: string;
293
355
  exitCode: number;
356
+ } | {
357
+ type: 'task_start';
358
+ taskId: string;
359
+ prompt: string;
360
+ role?: string;
361
+ cwd?: string;
362
+ } | {
363
+ type: 'task_end';
364
+ taskId: string;
365
+ isError: boolean;
366
+ result?: any;
294
367
  } | {
295
368
  type: 'compaction_start';
296
369
  reason: 'threshold' | 'overflow';
@@ -300,15 +373,14 @@ type FlueEvent = {
300
373
  messagesBefore: number;
301
374
  messagesAfter: number;
302
375
  } | {
303
- type: 'task_start';
304
- workspace: string;
305
- } | {
306
- type: 'task_end';
307
- } | {
308
- type: 'done';
376
+ type: 'idle';
309
377
  } | {
310
378
  type: 'error';
311
379
  error: string;
380
+ }) & {
381
+ sessionId?: string;
382
+ parentSessionId?: string;
383
+ taskId?: string;
312
384
  };
313
385
  type FlueEventCallback = (event: FlueEvent) => void;
314
386
  interface AgentInfo {
@@ -328,13 +400,44 @@ interface BuildContext {
328
400
  outputDir: string;
329
401
  options: BuildOptions;
330
402
  }
331
- /** Controls the build output format for a target platform. */
403
+ /**
404
+ * Controls the build output format for a target platform.
405
+ *
406
+ * A plugin can either ship a fully-bundled JavaScript artifact (Node target)
407
+ * or hand over a TypeScript/ESM entry source that some downstream tool will
408
+ * bundle (Cloudflare target — wrangler does the bundling). Pre-bundling on
409
+ * top of a tool that bundles for itself causes subtle resolution conflicts
410
+ * (we hit this with `tar`/`fs`/etc. via `nodejs_compat`), so the Cloudflare
411
+ * path explicitly opts out.
412
+ */
332
413
  interface BuildPlugin {
333
414
  name: string;
334
- generateEntryPoint(ctx: BuildContext): string;
335
- esbuildOptions(ctx: BuildContext): Record<string, any>;
336
- /** Additional files to write to dist/ (e.g., wrangler.jsonc, Dockerfile). */
337
- additionalOutputs?(ctx: BuildContext): Record<string, string>;
415
+ /**
416
+ * The source of the entry point (TS or JS). May be async — the Cloudflare
417
+ * plugin reads the user's wrangler config (via wrangler's reader) which is
418
+ * a sync call but lives behind a lazy `await import('wrangler')`.
419
+ */
420
+ generateEntryPoint(ctx: BuildContext): string | Promise<string>;
421
+ /**
422
+ * Bundling strategy:
423
+ * - `'esbuild'` (default): run the SDK's esbuild pass to produce a
424
+ * bundled `dist/server.mjs`. Use when the deploy target is "just run
425
+ * this file" with no further bundling step.
426
+ * - `'none'`: skip esbuild. The entry is written as-is to `dist/` and
427
+ * becomes the input for whatever tool will deploy it (e.g. wrangler).
428
+ * The plugin must also implement `entryFilename` to set the file name.
429
+ */
430
+ bundle?: 'esbuild' | 'none';
431
+ /**
432
+ * The filename to use for the entry, written under `dist/`. Required when
433
+ * `bundle === 'none'`. For `bundle === 'esbuild'` the output is always
434
+ * `server.mjs` and this field is ignored.
435
+ */
436
+ entryFilename?: string;
437
+ /** esbuild options. Only consulted when `bundle === 'esbuild'`. */
438
+ esbuildOptions?(ctx: BuildContext): Record<string, any>;
439
+ /** Additional files to write to dist/ (e.g., wrangler.jsonc, Dockerfile). May be async. */
440
+ additionalOutputs?(ctx: BuildContext): Record<string, string> | Promise<Record<string, string>>;
338
441
  }
339
442
  interface BuildOptions {
340
443
  /**
@@ -355,4 +458,4 @@ interface BuildOptions {
355
458
  plugin?: BuildPlugin;
356
459
  }
357
460
  //#endregion
358
- export { ShellOptions as C, TaskOptions as D, SkillOptions as E, ToolDef as O, SessionStore as S, Skill as T, Role as _, BuildOptions as a, SessionEnv as b, CommandDef as c, FlueContext as d, FlueEvent as f, PromptResponse as g, PromptOptions as h, BuildContext as i, CommandSupport as l, FlueSession as m, AgentInfo as n, BuildPlugin as o, FlueEventCallback as p, BashLike as r, Command as s, AgentConfig as t, FileStat as u, SandboxFactory as v, ShellResult as w, SessionInit as x, SessionData as y };
461
+ export { TaskOptions as A, SessionEnv as C, ShellResult as D, ShellOptions as E, ToolParameters as M, Skill as O, SessionData as S, SessionStore as T, FlueSessions as _, BashLike as a, Role as b, BuildPlugin as c, FileStat as d, FlueAgent as f, FlueSession as g, FlueEventCallback as h, BashFactory as i, ToolDef as j, SkillOptions as k, Command as l, FlueEvent as m, AgentInfo as n, BuildContext as o, FlueContext as p, AgentInit as r, BuildOptions as s, AgentConfig as t, CommandDef as u, PromptOptions as v, SessionOptions as w, SandboxFactory as x, PromptResponse as y };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flue/sdk",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "exports": {
@@ -39,18 +39,26 @@
39
39
  "@hono/node-server": "^1.14.0",
40
40
  "@mariozechner/pi-agent-core": "*",
41
41
  "@mariozechner/pi-ai": "*",
42
+ "@modelcontextprotocol/sdk": "^1.29.0",
42
43
  "@valibot/to-json-schema": "^1.0.0",
43
- "agentfs-sdk": "^0.6.4",
44
44
  "esbuild": "^0.25.0",
45
45
  "hono": "^4.7.0",
46
- "jsonc-parser": "^3.3.1",
47
46
  "just-bash": "^2.14.2",
48
47
  "package-up": "^5.0.0",
49
48
  "valibot": "^1.0.0"
50
49
  },
51
50
  "devDependencies": {
52
51
  "@cloudflare/workers-types": "^4.20250410.0",
53
- "tsdown": "^0.20.3"
52
+ "tsdown": "^0.20.3",
53
+ "wrangler": "^4.83.0"
54
+ },
55
+ "peerDependencies": {
56
+ "wrangler": "^4.0.0"
57
+ },
58
+ "peerDependenciesMeta": {
59
+ "wrangler": {
60
+ "optional": true
61
+ }
54
62
  },
55
63
  "scripts": {
56
64
  "build": "tsdown",