@flue/sdk 0.3.11 → 0.4.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/README.md CHANGED
@@ -42,15 +42,15 @@ export default async function ({ init, payload }: FlueContext) {
42
42
  const session = await agent.session();
43
43
 
44
44
  // prompt() sends a message in the session, triggering action.
45
- const result = await session.prompt(`Translate this to ${payload.language}: "${payload.text}"`, {
46
- // Pass a result schema to get typed, schema-validated data back from your agent.
47
- result: v.object({
45
+ const { data } = await session.prompt(`Translate this to ${payload.language}: "${payload.text}"`, {
46
+ // Pass a `schema` to get typed, schema-validated data back from your agent.
47
+ schema: v.object({
48
48
  translation: v.string(),
49
49
  confidence: v.picklist(['low', 'medium', 'high']),
50
50
  }),
51
51
  });
52
52
 
53
- return result;
53
+ return data;
54
54
  }
55
55
  ```
56
56
 
@@ -91,28 +91,21 @@ export default async function ({ init, payload, env }: FlueContext) {
91
91
 
92
92
  ### Issue Triage (CI)
93
93
 
94
- A triage agent that runs in CI whenever an issue is opened on GitHub. The `"local"` sandbox mounts the host filesystem and lets you connect privileged CLIs (`gh`, `npm`, `git`) to the agent without leaking secrets.
94
+ A triage agent that runs in CI whenever an issue is opened on GitHub. The `"local"` sandbox gives the agent direct access to the host filesystem and shell perfect for CI runners, where `gh`, `git`, and `npm` are already on `$PATH` and the runner itself is your isolation boundary.
95
95
 
96
96
  ```ts
97
97
  // .flue/agents/triage.ts
98
98
  import { type FlueContext } from '@flue/sdk/client';
99
- import { defineCommand } from '@flue/sdk/node';
100
99
  import * as v from 'valibot';
101
100
 
102
101
  // Because we are running this in CI, we don't need to expose this as an HTTP endpoint.
103
102
  // The CLI can run any agent from the command line, `flue run triage ...`
104
103
  export const triggers = {};
105
104
 
106
- // Connect privileged CLIs to your agent without leaking sensitive keys and secrets.
107
- // Secrets are hooked up inside the command definition here, so your agent never sees them.
108
- // Commands are controlled per-prompt, so you can be as granular with access as you need.
109
- const npm = defineCommand('npm');
110
- const gh = defineCommand('gh', { env: { GH_TOKEN: process.env.GH_TOKEN } });
111
-
112
105
  export default async function ({ init, payload }: FlueContext) {
113
- // 'local' mounts the host filesystem at /workspace ideal for CI
114
- // where the repo is already checked out. Skills and AGENTS.md are
115
- // discovered automatically from the workspace directory.
106
+ // 'local' gives the agent direct access to the host filesystem and
107
+ // shell. The agent's bash tool can run `gh`, `git`, `npm` directly.
108
+ // Skills and AGENTS.md are discovered from process.cwd().
116
109
  //
117
110
  // `model` sets the default model for every prompt/skill call in this
118
111
  // agent. Override per-call with `{ model: '...' }` on prompt()/skill().
@@ -126,14 +119,12 @@ export default async function ({ init, payload }: FlueContext) {
126
119
  // or by a relative path under `.agents/skills/` — e.g.
127
120
  // `session.skill('triage/reproduce.md', ...)`. Path references are handy for
128
121
  // skill packs that group multiple stages under one directory.
129
- const result = await session.skill('triage', {
122
+ const { data } = await session.skill('triage', {
130
123
  // Pass arguments to any prompt or skill.
131
124
  args: { issueNumber: payload.issueNumber },
132
- // Grant access to `gh` and `npm` for the life of this skill.
133
- commands: [gh, npm],
134
- // Result schemas are great for being able to act/orchestrate
135
- // based on the result of your prompt or skill call.
136
- result: v.object({
125
+ // Schemas are great for being able to act/orchestrate based on
126
+ // the structured `data` returned from your prompt or skill call.
127
+ schema: v.object({
137
128
  severity: v.picklist(['low', 'medium', 'high', 'critical']),
138
129
  reproducible: v.boolean(),
139
130
  summary: v.string(),
@@ -141,7 +132,7 @@ export default async function ({ init, payload }: FlueContext) {
141
132
  }),
142
133
  });
143
134
 
144
- return result;
135
+ return data;
145
136
  }
146
137
  ```
147
138
 
@@ -171,7 +162,7 @@ export default async function ({ init, payload, env }: FlueContext) {
171
162
  const client = new Daytona({ apiKey: env.DAYTONA_API_KEY });
172
163
  const sandbox = await client.create();
173
164
  const setupAgent = await init({
174
- sandbox: daytona(sandbox, { cleanup: true }),
165
+ sandbox: daytona(sandbox),
175
166
  model: 'openai/gpt-5.5',
176
167
  });
177
168
  const setup = await setupAgent.session();
@@ -0,0 +1,43 @@
1
+ //#region src/abort.ts
2
+ /** Build a standard `AbortError` (`DOMException`) carrying the signal's reason as `cause`. */
3
+ function abortErrorFor(signal) {
4
+ const reason = signal.reason;
5
+ const message = reason instanceof Error && reason.message ? reason.message : typeof reason === "string" && reason ? reason : "The operation was aborted.";
6
+ const error = new DOMException(message, "AbortError");
7
+ try {
8
+ Object.defineProperty(error, "cause", {
9
+ value: reason,
10
+ configurable: true
11
+ });
12
+ } catch {}
13
+ return error;
14
+ }
15
+ /**
16
+ * Wrap an async `run` function in a `CallHandle`. The handle's internal
17
+ * signal fires when `externalSignal` aborts or when `handle.abort()` is
18
+ * called.
19
+ */
20
+ function createCallHandle(externalSignal, run) {
21
+ const controller = new AbortController();
22
+ let externalListener;
23
+ if (externalSignal) if (externalSignal.aborted) controller.abort(externalSignal.reason);
24
+ else {
25
+ externalListener = () => controller.abort(externalSignal.reason);
26
+ externalSignal.addEventListener("abort", externalListener, { once: true });
27
+ }
28
+ const promise = run(controller.signal).finally(() => {
29
+ if (externalListener && externalSignal) externalSignal.removeEventListener("abort", externalListener);
30
+ });
31
+ return {
32
+ signal: controller.signal,
33
+ abort(reason) {
34
+ controller.abort(reason);
35
+ },
36
+ then(onFulfilled, onRejected) {
37
+ return promise.then(onFulfilled, onRejected);
38
+ }
39
+ };
40
+ }
41
+
42
+ //#endregion
43
+ export { createCallHandle as n, abortErrorFor as t };
package/dist/app.d.mts ADDED
@@ -0,0 +1,106 @@
1
+ import { w as ProviderSettings } from "./types-BAmV4f3Q.mjs";
2
+ import { i as flue } from "./flue-app-CG8i4wNG.mjs";
3
+ import { t as CLOUDFLARE_AI_BINDING_API } from "./cloudflare-model-BeiZ1pLz.mjs";
4
+ import { Api, Model, registerApiProvider as registerApiProvider$1 } from "@mariozechner/pi-ai";
5
+
6
+ //#region src/runtime/providers.d.ts
7
+ /**
8
+ * Minimal Workers AI binding shape. Kept structural so `@flue/sdk/app` stays
9
+ * importable on Node.
10
+ */
11
+ interface CloudflareAIBinding {
12
+ run(model: string, inputs: Record<string, unknown>, options?: Record<string, unknown>): Promise<Response | Record<string, unknown>>;
13
+ }
14
+ /**
15
+ * Provider declarations keyed by URL prefix. HTTP providers carry endpoint
16
+ * settings; Workers AI binding providers carry the captured binding object.
17
+ */
18
+ type ProviderRegistration = HttpProviderRegistration | CloudflareAIBindingRegistration;
19
+ interface HttpProviderRegistration {
20
+ api: Api;
21
+ /** Endpoint root, e.g. `'https://api.anthropic.com/v1'`. */
22
+ baseUrl: string;
23
+ /**
24
+ * Optional API key. Propagated to pi-ai via the harness's per-call
25
+ * `getApiKey(provider)` callback. Falls back to whatever pi-ai's normal
26
+ * env-var lookup produces if unset.
27
+ */
28
+ apiKey?: string;
29
+ /** Optional default headers for every outgoing request. */
30
+ headers?: Record<string, string>;
31
+ /**
32
+ * Override the pi-ai `provider` slug surfaced on AssistantMessage records
33
+ * and `configureProvider()` overrides. Defaults to the registry name.
34
+ */
35
+ provider?: string;
36
+ }
37
+ interface CloudflareAIBindingRegistration {
38
+ api: typeof CLOUDFLARE_AI_BINDING_API;
39
+ /** The captured `env.AI` reference. Read at registration time. */
40
+ binding: CloudflareAIBinding;
41
+ /**
42
+ * Override the pi-ai `provider` slug. Defaults to `'workers-ai'`,
43
+ * matching pi-ai's catalog convention for Cloudflare-Workers-AI models.
44
+ */
45
+ provider?: string;
46
+ }
47
+ /**
48
+ * Register a Flue-level model provider keyed by URL prefix.
49
+ *
50
+ * Last-write-wins. On Cloudflare, the generated entry reserves the
51
+ * `cloudflare` prefix for the built-in Workers AI binding integration.
52
+ */
53
+ declare function registerProvider(name: string, registration: ProviderRegistration): void;
54
+ /**
55
+ * Re-export of pi-ai's `registerApiProvider`. Use to register a brand-new
56
+ * wire-protocol handler for an `api` slug pi-ai doesn't ship. Then call
57
+ * {@link registerProvider} to alias a URL prefix to that api.
58
+ *
59
+ * ```ts
60
+ * registerApiProvider({ api: 'my-novel-api', stream, streamSimple });
61
+ * registerProvider('thing', { api: 'my-novel-api', baseUrl: '...', apiKey: '...' });
62
+ * ```
63
+ *
64
+ * pi-ai's registry is also module-scoped and last-write-wins. Calling
65
+ * `registerApiProvider` repeatedly with the same `api` string overwrites,
66
+ * so generated code can register on every isolate boot without dedupe
67
+ * bookkeeping.
68
+ */
69
+ declare const registerApiProvider: typeof registerApiProvider$1;
70
+ /**
71
+ * Provider settings accepted by {@link configureProvider}.
72
+ */
73
+ type ProviderConfiguration = ProviderSettings;
74
+ /**
75
+ * Patch transport-level settings on an existing provider while preserving its
76
+ * resolved Model metadata (cost, context window, token limits, etc.).
77
+ *
78
+ * ```ts
79
+ * import { configureProvider } from '@flue/sdk/app';
80
+ *
81
+ * configureProvider('anthropic', {
82
+ * baseUrl: 'https://gateway.example.com/anthropic',
83
+ * apiKey: process.env.GATEWAY_KEY,
84
+ * });
85
+ * ```
86
+ *
87
+ * Keyed by the resolved `Model.provider` value, not necessarily the URL
88
+ * prefix. Last-write-wins.
89
+ */
90
+ declare function configureProvider(provider: string, settings: ProviderConfiguration): void;
91
+ //#endregion
92
+ //#region src/app.d.ts
93
+ /**
94
+ * Shape contract for a user-authored `app.ts` default export. Any
95
+ * object exposing a `fetch(request, env?, ctx?)` method satisfies it,
96
+ * including a `new Hono()` instance.
97
+ *
98
+ * The `env` and `ctx` parameters are passed through on the Cloudflare
99
+ * target (env = bindings, ctx = ExecutionContext); on Node they are
100
+ * undefined.
101
+ */
102
+ interface Fetchable {
103
+ fetch(request: Request, env?: unknown, ctx?: unknown): Response | Promise<Response>;
104
+ }
105
+ //#endregion
106
+ export { type CloudflareAIBinding, type CloudflareAIBindingRegistration, Fetchable, type HttpProviderRegistration, type ProviderConfiguration, type ProviderRegistration, configureProvider, flue, registerApiProvider, registerProvider };
package/dist/app.mjs ADDED
@@ -0,0 +1,4 @@
1
+ import { r as flue } from "./flue-app-DeTOZjPs.mjs";
2
+ import { a as registerApiProvider, o as registerProvider, t as configureProvider } from "./providers-DeFRIwp0.mjs";
3
+
4
+ export { configureProvider, flue, registerApiProvider, registerProvider };
package/dist/client.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { A as ShellResult, D as SessionOptions, E as SessionEnv, F as ToolParameters, M as SkillOptions, N as TaskOptions, O as SessionStore, P as ToolDef, S as ProvidersConfig, T as SessionData, _ as FlueSessions, a as BashLike, b as PromptResponse, d as FileStat, f as FlueAgent, g as FlueSession, h as FlueEventCallback, i as BashFactory, k as ShellOptions, l as Command, m as FlueEvent, p as FlueContext, r as AgentInit, t as AgentConfig, v as ModelConfig, w as SandboxFactory, x as ProviderSettings, y as PromptOptions } from "./types-DGpyKMFm.mjs";
2
- import { i as connectMcpServer, n as McpServerOptions, r as McpTransport, t as McpServerConnection } from "./mcp-CcRxAwXW.mjs";
1
+ import { A as SessionStore, C as PromptUsage, D as SessionData, E as SandboxFactory, F as TaskOptions, I as ThinkingLevel, L as ToolDef, M as ShellResult, O as SessionEnv, P as SkillOptions, R as ToolParameters, S as PromptResultResponse, _ as FlueSessions, a as BashLike, b as PromptOptions, d as FlueAgent, f as FlueContext, g as FlueSession, h as FlueFs, i as BashFactory, j as ShellOptions, k as SessionOptions, m as FlueEventCallback, p as FlueEvent, r as AgentInit, t as AgentConfig, u as FileStat, v as ModelConfig, w as ProviderSettings, x as PromptResponse, y as PromptModel } from "./types-BAmV4f3Q.mjs";
2
+ import { i as connectMcpServer, n as McpServerOptions, r as McpTransport, t as McpServerConnection } from "./mcp-C3UBXVkR.mjs";
3
3
  import { Type } from "@mariozechner/pi-ai";
4
4
 
5
5
  //#region src/client.d.ts
@@ -16,6 +16,12 @@ interface FlueContextConfig {
16
16
  * Returns SessionEnv to use, or null to fall through to default logic.
17
17
  */
18
18
  resolveSandbox?: (sandbox: unknown) => Promise<SessionEnv> | null;
19
+ /**
20
+ * The current HTTP request, if any. Surfaced to handlers as `ctx.req`.
21
+ * Build plugins pass the standard Fetch `Request` through; non-HTTP entry
22
+ * points (e.g. future cron triggers) leave it undefined.
23
+ */
24
+ req?: Request;
19
25
  }
20
26
  /** Extends FlueContext with server-only methods. Agent handlers only see FlueContext. */
21
27
  interface FlueContextInternal extends FlueContext {
@@ -23,4 +29,4 @@ interface FlueContextInternal extends FlueContext {
23
29
  }
24
30
  declare function createFlueContext(config: FlueContextConfig): FlueContextInternal;
25
31
  //#endregion
26
- export { type AgentInit, type BashFactory, type BashLike, type Command, type FileStat, type FlueAgent, type FlueContext, FlueContextConfig, FlueContextInternal, type FlueEvent, type FlueEventCallback, type FlueSession, type FlueSessions, type McpServerConnection, type McpServerOptions, type McpTransport, type ModelConfig, type PromptOptions, type PromptResponse, type ProviderSettings, type ProvidersConfig, type SandboxFactory, type SessionData, type SessionEnv, type SessionOptions, type SessionStore, type ShellOptions, type ShellResult, type SkillOptions, type TaskOptions, type ToolDef, type ToolParameters, Type, connectMcpServer, createFlueContext };
32
+ export { type AgentInit, type BashFactory, type BashLike, type FileStat, type FlueAgent, type FlueContext, FlueContextConfig, FlueContextInternal, type FlueEvent, type FlueEventCallback, type FlueFs, type FlueSession, type FlueSessions, type McpServerConnection, type McpServerOptions, type McpTransport, type ModelConfig, type PromptModel, type PromptOptions, type PromptResponse, type PromptResultResponse, type PromptUsage, type ProviderSettings, type SandboxFactory, type SessionData, type SessionEnv, type SessionOptions, type SessionStore, type ShellOptions, type ShellResult, type SkillOptions, type TaskOptions, type ThinkingLevel, type ToolDef, type ToolParameters, Type, connectMcpServer, createFlueContext };
package/dist/client.mjs CHANGED
@@ -1,7 +1,8 @@
1
- import { r as discoverSessionContext } from "./agent-Cahthgu3.mjs";
2
- import { a as assertRoleExists } from "./session-DlwIt7wq.mjs";
1
+ import { u as discoverSessionContext } from "./result-K1IRhWKM.mjs";
2
+ import "./providers-DeFRIwp0.mjs";
3
+ import { a as assertRoleExists } from "./session-CO_uGVOk.mjs";
3
4
  import { bashFactoryToSessionEnv, createCwdSessionEnv } from "./sandbox.mjs";
4
- import { n as AgentClient, t as connectMcpServer } from "./mcp-DmDTeVXW.mjs";
5
+ import { n as AgentClient, t as connectMcpServer } from "./mcp-2SW_tpox.mjs";
5
6
  import { Type } from "@mariozechner/pi-ai";
6
7
 
7
8
  //#region src/client.ts
@@ -18,6 +19,9 @@ function createFlueContext(config) {
18
19
  get env() {
19
20
  return config.env;
20
21
  },
22
+ get req() {
23
+ return config.req;
24
+ },
21
25
  async init(options) {
22
26
  if (!options || !("model" in options)) throw new Error("[flue] init() requires a model. Pass { model: \"provider/model-id\" } or { model: false }.");
23
27
  if (options.model !== false && typeof options.model !== "string") throw new Error("[flue] init({ model }) must be a model string or false.");
@@ -31,16 +35,15 @@ function createFlueContext(config) {
31
35
  const env = options.cwd ? createCwdSessionEnv(baseEnv, options.cwd) : baseEnv;
32
36
  const store = options.persist ?? config.defaultStore;
33
37
  const localContext = await discoverSessionContext(env);
34
- const providers = mergeProvidersConfig(config.agentConfig.providers, options.providers);
35
- const agentModel = config.agentConfig.resolveModel(options.model, providers);
38
+ const agentModel = config.agentConfig.resolveModel(options.model);
36
39
  return new AgentClient(id, {
37
40
  ...config.agentConfig,
38
41
  systemPrompt: localContext.systemPrompt,
39
42
  skills: localContext.skills,
40
43
  model: agentModel,
41
44
  role: options.role ?? config.agentConfig.role,
42
- providers
43
- }, env, store, currentEventCallback, options.commands, options.tools);
45
+ thinkingLevel: options.thinkingLevel ?? config.agentConfig.thinkingLevel
46
+ }, env, store, currentEventCallback, options.tools);
44
47
  } catch (error) {
45
48
  initializedAgentIds.delete(id);
46
49
  throw error;
@@ -77,23 +80,6 @@ async function resolveSessionEnv(id, sandbox, config, cwd) {
77
80
  });
78
81
  throw new Error("[flue] Invalid sandbox option passed to init().");
79
82
  }
80
- function mergeProvidersConfig(base, settings) {
81
- if (!base) return settings;
82
- if (!settings) return base;
83
- const merged = { ...base };
84
- for (const [provider, config] of Object.entries(settings)) {
85
- const previous = merged[provider];
86
- merged[provider] = {
87
- ...previous,
88
- ...config,
89
- headers: previous?.headers || config.headers ? {
90
- ...previous?.headers ?? {},
91
- ...config.headers ?? {}
92
- } : void 0
93
- };
94
- }
95
- return merged;
96
- }
97
83
 
98
84
  //#endregion
99
85
  export { Type, connectMcpServer, createFlueContext };
@@ -1,5 +1,6 @@
1
- import { E as SessionEnv, O as SessionStore, l as Command } from "../types-DGpyKMFm.mjs";
2
- import { t as CommandExecutor } from "../command-helpers-eVG1-Iru.mjs";
1
+ import { A as SessionStore, O as SessionEnv } from "../types-BAmV4f3Q.mjs";
2
+ import { n as CloudflareAIBindingApi } from "../cloudflare-model-BeiZ1pLz.mjs";
3
+ import { ApiProvider, StreamOptions } from "@mariozechner/pi-ai";
3
4
 
4
5
  //#region src/cloudflare/virtual-sandbox.d.ts
5
6
  interface VirtualSandboxOptions {
@@ -9,9 +10,6 @@ interface VirtualSandboxOptions {
9
10
  declare function getVirtualSandbox(): Promise<any>;
10
11
  declare function getVirtualSandbox(bucket: unknown, options?: VirtualSandboxOptions): Promise<any>;
11
12
  //#endregion
12
- //#region src/cloudflare/define-command.d.ts
13
- declare function defineCommand(name: string, execute: CommandExecutor): Command;
14
- //#endregion
15
13
  //#region src/cloudflare/cf-sandbox.d.ts
16
14
  declare function cfSandboxToSessionEnv(sandbox: any, cwd?: string): Promise<SessionEnv>;
17
15
  //#endregion
@@ -32,4 +30,10 @@ interface CloudflareContext {
32
30
  declare function runWithCloudflareContext<T>(ctx: CloudflareContext, fn: () => T): T;
33
31
  declare function getCloudflareContext(): CloudflareContext;
34
32
  //#endregion
35
- export { type CloudflareContext, type VirtualSandboxOptions, cfSandboxToSessionEnv, defineCommand, getCloudflareContext, getVirtualSandbox, runWithCloudflareContext, store };
33
+ //#region src/cloudflare/workers-ai-provider.d.ts
34
+ /**
35
+ * Return the pi-ai `ApiProvider` definition for the Cloudflare AI binding.
36
+ */
37
+ declare function getCloudflareAIBindingApiProvider(): ApiProvider<CloudflareAIBindingApi, StreamOptions>;
38
+ //#endregion
39
+ export { type CloudflareContext, type VirtualSandboxOptions, cfSandboxToSessionEnv, getCloudflareAIBindingApiProvider, getCloudflareContext, getVirtualSandbox, runWithCloudflareContext, store };