@flue/sdk 0.1.0 → 0.1.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.
- package/README.md +32 -15
- package/dist/client.d.mts +1 -1
- package/dist/client.mjs +4 -2
- package/dist/cloudflare/index.d.mts +1 -1
- package/dist/cloudflare/index.mjs +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +43 -15
- package/dist/sandbox.d.mts +1 -1
- package/dist/sandbox.mjs +1 -1
- package/dist/{session-BD0MEuO3.mjs → session-0gnaB_aY.mjs} +57 -4
- package/dist/{types-xNvqlohs.d.mts → types-C97_qJ21.d.mts} +17 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
# flue
|
|
2
|
-
|
|
3
1
|
> **Experimental** — Flue is under active development. APIs may change.
|
|
2
|
+
>
|
|
3
|
+
> Looking for `v0.0.x`? [See here.](https://github.com/withastro/flue/tree/v0.0.x)
|
|
4
|
+
|
|
5
|
+
# Flue
|
|
6
|
+
|
|
7
|
+
Flue is **The Sandbox Agent Framework.** If you know how to use Claude Code (or OpenCode, Codex, Gemini, etc)... then you already know the basics of how to build agents with Flue.
|
|
4
8
|
|
|
5
|
-
Agent
|
|
9
|
+
A [Sandbox Agent](https://developers.openai.com/api/docs/guides/agents/sandboxes) pairs an **agent harness** (like Claude Code) with a secure, isolated container workspace. Sandbox Agents can edit files, write and execute code, spin up subagents, run terminal commands, and drive themselves autonomously to solve any given task. This pattern unlocks more powerful, intelligent agents that traditional AI frameworks wouldn't otherwise let you build.
|
|
10
|
+
|
|
11
|
+
Our take is that 1) any agent can be represented as a Sandbox Agent, and 2) any agent is _best_ represented as a Sandbox Agent. So we designed Flue to deliver on this vision.
|
|
6
12
|
|
|
7
13
|
## Packages
|
|
8
14
|
|
|
@@ -18,6 +24,8 @@ Agent framework where agents are directories compiled into deployable server art
|
|
|
18
24
|
|
|
19
25
|
The simplest agent — no container, no tools, just a prompt and a typed result.
|
|
20
26
|
|
|
27
|
+
Unless you opt-in to initializing a full container sandbox, Flue will default to a virtual sandbox for every agent, powered by [just-bash](https://github.com/vercel-labs/just-bash). A virtual sandbox is going to be dramatically faster, cheaper, and more scalable than running a full container for every agent, which makes it perfect for building high-traffic/high-scale agents.
|
|
28
|
+
|
|
21
29
|
```ts
|
|
22
30
|
// .flue/agents/hello-world.ts
|
|
23
31
|
import type { FlueContext } from '@flue/sdk/client';
|
|
@@ -34,8 +42,8 @@ export default async function ({ init, payload, sessionId }: FlueContext) {
|
|
|
34
42
|
const session = await init();
|
|
35
43
|
|
|
36
44
|
// prompt() sends a message in the session, triggering action.
|
|
37
|
-
// You can pass a schema to `result` to get typed, validated JSON back.
|
|
38
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.
|
|
39
47
|
result: v.object({
|
|
40
48
|
translation: v.string(),
|
|
41
49
|
confidence: v.picklist(['low', 'medium', 'high']),
|
|
@@ -48,9 +56,9 @@ export default async function ({ init, payload, sessionId }: FlueContext) {
|
|
|
48
56
|
|
|
49
57
|
### Support Agent
|
|
50
58
|
|
|
51
|
-
A support agent
|
|
59
|
+
A support agent can also run in a virtual sandbox, but we now add a file-system using an R2 bucket. The knowledge base is stored in R2 and mounted directly into the agent's filesystem — the agent searches it with its built-in tools (grep, glob, read). Skills are also defined in the bucket that help the agent perform its task.
|
|
52
60
|
|
|
53
|
-
|
|
61
|
+
Because this agent is deployed to Cloudflare, message history and session state are automatically persisted for you. So you (or your customer) can revisit this support session days, weeks, or years later and pick up exactly where you left off.
|
|
54
62
|
|
|
55
63
|
```ts
|
|
56
64
|
// .flue/agents/support.ts
|
|
@@ -72,18 +80,17 @@ export default async function ({ init, payload, env }: FlueContext) {
|
|
|
72
80
|
relevant to this request, then write a helpful response.
|
|
73
81
|
|
|
74
82
|
Customer: ${payload.message}`,
|
|
83
|
+
{
|
|
84
|
+
// Provide roles (aka subagents) to guide your agent. Defined in .flue/roles/
|
|
85
|
+
role: 'triager',
|
|
86
|
+
},
|
|
75
87
|
);
|
|
76
88
|
}
|
|
77
89
|
```
|
|
78
90
|
|
|
79
91
|
### Issue Triage (CI)
|
|
80
92
|
|
|
81
|
-
A triage agent that runs whenever
|
|
82
|
-
|
|
83
|
-
Flue was designed to power CI workflows since day one. The `"local"` filesystem sandbox enables two things:
|
|
84
|
-
|
|
85
|
-
1. Mount the current directory to your virtual file system.
|
|
86
|
-
2. Connect privileged CLIs to your agent (`gh`, `glab`, `git`) without leaking sensitive keys and secrets.
|
|
93
|
+
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.
|
|
87
94
|
|
|
88
95
|
```ts
|
|
89
96
|
// .flue/agents/triage.ts
|
|
@@ -92,6 +99,8 @@ import { execFile } from 'node:child_process';
|
|
|
92
99
|
import { promisify } from 'node:util';
|
|
93
100
|
import * as v from 'valibot';
|
|
94
101
|
|
|
102
|
+
// Because we are running this in CI, we don't need to expose this as an HTTP endpoint.
|
|
103
|
+
// The CLI can run any agent from the command line, `flue run triage ...`
|
|
95
104
|
export const triggers = {};
|
|
96
105
|
|
|
97
106
|
// Connect privileged CLIs to your agent without leaking sensitive keys and secrets.
|
|
@@ -108,15 +117,23 @@ export default async function ({ init, payload }: FlueContext) {
|
|
|
108
117
|
// 'local' mounts the host filesystem at /workspace — ideal for CI
|
|
109
118
|
// where the repo is already checked out. Skills and AGENTS.md are
|
|
110
119
|
// discovered automatically from the workspace directory.
|
|
111
|
-
|
|
120
|
+
//
|
|
121
|
+
// `model` sets the default model for every prompt/skill call in this
|
|
122
|
+
// session. Override per-call with `{ model: '...' }` on prompt()/skill().
|
|
123
|
+
const session = await init({
|
|
124
|
+
sandbox: 'local',
|
|
125
|
+
model: 'anthropic/claude-opus-4-20250514',
|
|
126
|
+
});
|
|
112
127
|
|
|
128
|
+
// Skills can be referenced either by their frontmatter `name:` (shown below)
|
|
129
|
+
// or by a relative path under `.agents/skills/` — e.g.
|
|
130
|
+
// `session.skill('triage/reproduce.md', ...)`. Path references are handy for
|
|
131
|
+
// skill packs that group multiple stages under one directory.
|
|
113
132
|
const result = await session.skill('triage', {
|
|
114
133
|
// Pass arguments to any prompt or skill.
|
|
115
134
|
args: { issueNumber: payload.issueNumber },
|
|
116
135
|
// Grant access to `gh` and `npm` for the life of this skill.
|
|
117
136
|
commands: [gh, npm],
|
|
118
|
-
// Provide roles (aka subagents) to guide your agent. Defined in .flue/roles/
|
|
119
|
-
role: 'triager',
|
|
120
137
|
// Result schemas are great for being able to act/orchestrate
|
|
121
138
|
// based on the result of your prompt or skill call.
|
|
122
139
|
result: v.object({
|
package/dist/client.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as ShellOptions, D as TaskOptions, E as SkillOptions, O as ToolDef, S as SessionStore, b as SessionEnv, d as FlueContext, f as FlueEvent, g as PromptResponse, h as PromptOptions, l as CommandSupport, m as FlueSession, p as FlueEventCallback, r as BashLike, s as Command, t as AgentConfig, u as FileStat, v as SandboxFactory, w as ShellResult, x as SessionInit, y as SessionData } from "./types-
|
|
1
|
+
import { C as ShellOptions, D as TaskOptions, E as SkillOptions, O as ToolDef, S as SessionStore, b as SessionEnv, d as FlueContext, f as FlueEvent, g as PromptResponse, h as PromptOptions, l as CommandSupport, m as FlueSession, p as FlueEventCallback, r as BashLike, s as Command, t as AgentConfig, u as FileStat, v as SandboxFactory, w as ShellResult, x as SessionInit, y as SessionData } from "./types-C97_qJ21.mjs";
|
|
2
2
|
import { Type } from "@mariozechner/pi-ai";
|
|
3
3
|
|
|
4
4
|
//#region src/client.d.ts
|
package/dist/client.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as Session, o as discoverSessionContext } from "./session-
|
|
1
|
+
import { n as Session, o as discoverSessionContext } from "./session-0gnaB_aY.mjs";
|
|
2
2
|
import { bashToSessionEnv } from "./sandbox.mjs";
|
|
3
3
|
import { Type } from "@mariozechner/pi-ai";
|
|
4
4
|
|
|
@@ -24,10 +24,12 @@ function createFlueContext(config) {
|
|
|
24
24
|
const store = options?.persist ?? config.defaultStore;
|
|
25
25
|
const savedData = await store.load(config.sessionId);
|
|
26
26
|
const localContext = await discoverSessionContext(env);
|
|
27
|
+
const sessionModel = options?.model && config.agentConfig.resolveModel ? config.agentConfig.resolveModel(options.model) : config.agentConfig.model;
|
|
27
28
|
const sessionConfig = {
|
|
28
29
|
...config.agentConfig,
|
|
29
30
|
systemPrompt: localContext.systemPrompt,
|
|
30
|
-
skills: localContext.skills
|
|
31
|
+
skills: localContext.skills,
|
|
32
|
+
model: sessionModel
|
|
31
33
|
};
|
|
32
34
|
return new Session(config.sessionId, sessionConfig, env, store, savedData, currentEventCallback);
|
|
33
35
|
},
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as ShellOptions, D as TaskOptions, E as SkillOptions, O as ToolDef, S as SessionStore, T as Skill, _ as Role, a as BuildOptions, b as SessionEnv, c as CommandDef, d as FlueContext, f as FlueEvent, g as PromptResponse, h as PromptOptions, i as BuildContext, l as CommandSupport, m as FlueSession, n as AgentInfo, o as BuildPlugin, p as FlueEventCallback, r as BashLike, s as Command, t as AgentConfig, u as FileStat, v as SandboxFactory, w as ShellResult, x as SessionInit, y as SessionData } from "./types-
|
|
1
|
+
import { C as ShellOptions, D as TaskOptions, E as SkillOptions, O as ToolDef, S as SessionStore, T as Skill, _ as Role, a as BuildOptions, b as SessionEnv, c as CommandDef, d as FlueContext, f as FlueEvent, g as PromptResponse, h as PromptOptions, i as BuildContext, l as CommandSupport, m as FlueSession, n as AgentInfo, o as BuildPlugin, p as FlueEventCallback, r as BashLike, s as Command, t as AgentConfig, u as FileStat, v as SandboxFactory, w as ShellResult, x as SessionInit, y as SessionData } from "./types-C97_qJ21.mjs";
|
|
2
2
|
import { FlueContextConfig, FlueContextInternal, createFlueContext } from "./client.mjs";
|
|
3
3
|
import { AgentTool } from "@mariozechner/pi-agent-core";
|
|
4
4
|
import "valibot";
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as createTools, i as BUILTIN_TOOL_NAMES, s as parseFrontmatterFile, t as InMemorySessionStore } from "./session-
|
|
1
|
+
import { a as createTools, i as BUILTIN_TOOL_NAMES, s as parseFrontmatterFile, t as InMemorySessionStore } from "./session-0gnaB_aY.mjs";
|
|
2
2
|
import { createFlueContext } from "./client.mjs";
|
|
3
3
|
import * as esbuild from "esbuild";
|
|
4
4
|
import * as fs from "node:fs";
|
|
@@ -9,9 +9,7 @@ import { packageUpSync } from "package-up";
|
|
|
9
9
|
var CloudflarePlugin = class {
|
|
10
10
|
name = "cloudflare";
|
|
11
11
|
generateEntryPoint(ctx) {
|
|
12
|
-
const { agents, roles,
|
|
13
|
-
const modelProvider = options.model?.provider ?? "anthropic";
|
|
14
|
-
const modelId = options.model?.modelId ?? "claude-haiku-4-5";
|
|
12
|
+
const { agents, roles, resolveSDKImport } = ctx;
|
|
15
13
|
const rolesJson = JSON.stringify(roles);
|
|
16
14
|
const webhookAgents = agents.filter((a) => a.triggers.webhook);
|
|
17
15
|
const agentImports = agents.map((a) => {
|
|
@@ -51,14 +49,30 @@ const manifest = ${manifest};
|
|
|
51
49
|
|
|
52
50
|
// ─── Infrastructure ─────────────────────────────────────────────────────────
|
|
53
51
|
|
|
54
|
-
|
|
52
|
+
// No build-time model default. The user sets model at runtime via
|
|
53
|
+
// \`init({ model: "provider/model-id" })\` for a session default, or via
|
|
54
|
+
// \`{ model: "provider/model-id" }\` on any individual prompt/skill/task call.
|
|
55
|
+
const model = undefined;
|
|
55
56
|
|
|
56
57
|
function resolveModel(modelString) {
|
|
57
58
|
const slash = modelString.indexOf('/');
|
|
58
|
-
if (slash
|
|
59
|
-
|
|
59
|
+
if (slash === -1) {
|
|
60
|
+
throw new Error(
|
|
61
|
+
'[flue] Invalid model "' + modelString + '". ' +
|
|
62
|
+
'Use the "provider/model-id" format (e.g. "anthropic/claude-haiku-4-5").'
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
const provider = modelString.slice(0, slash);
|
|
66
|
+
const modelId = modelString.slice(slash + 1);
|
|
67
|
+
const resolved = getModel(provider, modelId);
|
|
68
|
+
if (!resolved) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
'[flue] Unknown model "' + modelString + '". ' +
|
|
71
|
+
'Provider "' + provider + '" / model id "' + modelId + '" ' +
|
|
72
|
+
'is not registered with @mariozechner/pi-ai.'
|
|
73
|
+
);
|
|
60
74
|
}
|
|
61
|
-
return
|
|
75
|
+
return resolved;
|
|
62
76
|
}
|
|
63
77
|
|
|
64
78
|
// ─── Sandbox Environments ───────────────────────────────────────────────────
|
|
@@ -370,9 +384,7 @@ function agentClassName(name) {
|
|
|
370
384
|
var NodePlugin = class {
|
|
371
385
|
name = "node";
|
|
372
386
|
generateEntryPoint(ctx) {
|
|
373
|
-
const { agents, roles,
|
|
374
|
-
const modelProvider = options.model?.provider ?? "anthropic";
|
|
375
|
-
const modelId = options.model?.modelId ?? "claude-haiku-4-5";
|
|
387
|
+
const { agents, roles, resolveSDKImport } = ctx;
|
|
376
388
|
const rolesJson = JSON.stringify(roles);
|
|
377
389
|
const webhookAgents = agents.filter((a) => a.triggers.webhook);
|
|
378
390
|
const agentImports = agents.map((a) => {
|
|
@@ -414,14 +426,30 @@ const manifest = ${manifest};
|
|
|
414
426
|
|
|
415
427
|
// ─── Infrastructure ─────────────────────────────────────────────────────────
|
|
416
428
|
|
|
417
|
-
|
|
429
|
+
// No build-time model default. The user sets model at runtime via
|
|
430
|
+
// \`init({ model: "provider/model-id" })\` for a session default, or via
|
|
431
|
+
// \`{ model: "provider/model-id" }\` on any individual prompt/skill/task call.
|
|
432
|
+
const model = undefined;
|
|
418
433
|
|
|
419
434
|
function resolveModel(modelString) {
|
|
420
435
|
const slash = modelString.indexOf('/');
|
|
421
|
-
if (slash
|
|
422
|
-
|
|
436
|
+
if (slash === -1) {
|
|
437
|
+
throw new Error(
|
|
438
|
+
'[flue] Invalid model "' + modelString + '". ' +
|
|
439
|
+
'Use the "provider/model-id" format (e.g. "anthropic/claude-haiku-4-5").'
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
const provider = modelString.slice(0, slash);
|
|
443
|
+
const modelId = modelString.slice(slash + 1);
|
|
444
|
+
const resolved = getModel(provider, modelId);
|
|
445
|
+
if (!resolved) {
|
|
446
|
+
throw new Error(
|
|
447
|
+
'[flue] Unknown model "' + modelString + '". ' +
|
|
448
|
+
'Provider "' + provider + '" / model id "' + modelId + '" ' +
|
|
449
|
+
'is not registered with @mariozechner/pi-ai.'
|
|
450
|
+
);
|
|
423
451
|
}
|
|
424
|
-
return
|
|
452
|
+
return resolved;
|
|
425
453
|
}
|
|
426
454
|
|
|
427
455
|
// ─── Sandbox Environments ───────────────────────────────────────────────────
|
package/dist/sandbox.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { b as SessionEnv, c as CommandDef, r as BashLike, u as FileStat, v as SandboxFactory, w as ShellResult } from "./types-
|
|
1
|
+
import { b as SessionEnv, c as CommandDef, r as BashLike, u as FileStat, v as SandboxFactory, w as ShellResult } from "./types-C97_qJ21.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/sandbox.d.ts
|
|
4
4
|
declare function bashToSessionEnv(bash: BashLike): SessionEnv;
|
package/dist/sandbox.mjs
CHANGED
|
@@ -39,6 +39,28 @@ async function readAgentsMd(env, basePath) {
|
|
|
39
39
|
}
|
|
40
40
|
return parts.join("\n\n");
|
|
41
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Load a skill directly by relative path under `.agents/skills/`.
|
|
44
|
+
*
|
|
45
|
+
* The path is taken as-is — no extension is auto-appended. Callers reference
|
|
46
|
+
* the full filename, e.g. `'triage/reproduce.md'`. Returns `null` if the file
|
|
47
|
+
* doesn't exist.
|
|
48
|
+
*
|
|
49
|
+
* Used as a fallback by `session.skill()` when the requested name doesn't match
|
|
50
|
+
* a discovered skill's frontmatter `name:` field. Lets users organise skills as
|
|
51
|
+
* a pack of sibling markdown files under one directory (orchestration SKILL.md
|
|
52
|
+
* + stage files) without forcing each stage into its own `SKILL.md` subdirectory.
|
|
53
|
+
*/
|
|
54
|
+
async function loadSkillByPath(env, basePath, relPath) {
|
|
55
|
+
const filePath = `${basePath.endsWith("/") ? `${basePath}.agents/skills` : `${basePath}/.agents/skills`}/${relPath}`;
|
|
56
|
+
if (!await env.exists(filePath)) return null;
|
|
57
|
+
const parsed = parseFrontmatterFile(await env.readFile(filePath), relPath.replace(/\.(md|markdown)$/i, ""));
|
|
58
|
+
return {
|
|
59
|
+
name: parsed.name,
|
|
60
|
+
description: parsed.description,
|
|
61
|
+
instructions: parsed.body
|
|
62
|
+
};
|
|
63
|
+
}
|
|
42
64
|
/** Discover skills from .agents/skills/<name>/SKILL.md under basePath. */
|
|
43
65
|
async function discoverLocalSkills(env, basePath) {
|
|
44
66
|
const skillsDir = basePath.endsWith("/") ? `${basePath}.agents/skills` : `${basePath}/.agents/skills`;
|
|
@@ -987,6 +1009,7 @@ var Session = class Session {
|
|
|
987
1009
|
});
|
|
988
1010
|
}
|
|
989
1011
|
async prompt(text, options) {
|
|
1012
|
+
this.assertRoleExists(options?.role);
|
|
990
1013
|
this.resolveModelForCall(options?.model, options?.role);
|
|
991
1014
|
const promptWithRole = this.injectRoleInstructions(text, options?.role);
|
|
992
1015
|
const schema = options?.result;
|
|
@@ -1007,8 +1030,16 @@ var Session = class Session {
|
|
|
1007
1030
|
}
|
|
1008
1031
|
}
|
|
1009
1032
|
async skill(name, options) {
|
|
1010
|
-
|
|
1011
|
-
|
|
1033
|
+
this.assertRoleExists(options?.role);
|
|
1034
|
+
let registeredSkill = this.config.skills[name];
|
|
1035
|
+
if (!registeredSkill && (name.includes("/") || /\.(md|markdown)$/i.test(name))) {
|
|
1036
|
+
const loaded = await loadSkillByPath(this.env, this.env.cwd, name);
|
|
1037
|
+
if (loaded) registeredSkill = loaded;
|
|
1038
|
+
}
|
|
1039
|
+
if (!registeredSkill) {
|
|
1040
|
+
const available = Object.keys(this.config.skills).join(", ") || "(none)";
|
|
1041
|
+
throw new Error(`Skill "${name}" not registered. Available: ${available}. Skills can also be referenced by relative path under .agents/skills/ (e.g. "triage/reproduce.md").`);
|
|
1042
|
+
}
|
|
1012
1043
|
this.resolveModelForCall(options?.model, options?.role);
|
|
1013
1044
|
const schema = options?.result;
|
|
1014
1045
|
const skillPrompt = buildSkillPrompt(registeredSkill.instructions, options?.args, schema);
|
|
@@ -1046,6 +1077,7 @@ var Session = class Session {
|
|
|
1046
1077
|
}
|
|
1047
1078
|
}
|
|
1048
1079
|
async task(prompt, options) {
|
|
1080
|
+
this.assertRoleExists(options?.role);
|
|
1049
1081
|
if (!options?.workspace) throw new Error("[flue] task() requires a workspace option.");
|
|
1050
1082
|
const taskCwd = options.workspace.startsWith("/") ? options.workspace : normalizePath(this.env.cwd + "/" + options.workspace);
|
|
1051
1083
|
function taskResolvePath(p) {
|
|
@@ -1081,7 +1113,7 @@ var Session = class Session {
|
|
|
1081
1113
|
systemPrompt: localContext.systemPrompt,
|
|
1082
1114
|
skills: localContext.skills,
|
|
1083
1115
|
roles: this.config.roles,
|
|
1084
|
-
model: taskModel,
|
|
1116
|
+
model: this.requireModel(taskModel, "this task() call"),
|
|
1085
1117
|
resolveModel: this.config.resolveModel,
|
|
1086
1118
|
compaction: this.config.compaction
|
|
1087
1119
|
};
|
|
@@ -1113,7 +1145,28 @@ var Session = class Session {
|
|
|
1113
1145
|
let model = this.config.model;
|
|
1114
1146
|
if (roleName && this.config.roles[roleName]?.model && this.config.resolveModel) model = this.config.resolveModel(this.config.roles[roleName].model);
|
|
1115
1147
|
if (promptModel && this.config.resolveModel) model = this.config.resolveModel(promptModel);
|
|
1116
|
-
this.agent.state.model = model;
|
|
1148
|
+
this.agent.state.model = this.requireModel(model, "this prompt()/skill()/task() call");
|
|
1149
|
+
}
|
|
1150
|
+
/**
|
|
1151
|
+
* Throws a clear, actionable error when no model is configured for a call.
|
|
1152
|
+
* Use with the resolved model (post-precedence) to guarantee we never hand
|
|
1153
|
+
* `undefined` to the underlying agent.
|
|
1154
|
+
*/
|
|
1155
|
+
requireModel(model, callSite) {
|
|
1156
|
+
if (model) return model;
|
|
1157
|
+
throw new Error(`[flue] No model configured for ${callSite}. Pass \`{ model: "provider/model-id" }\` to \`init()\` for a session-wide default, or to this prompt()/skill()/task() call for a one-off override.`);
|
|
1158
|
+
}
|
|
1159
|
+
/**
|
|
1160
|
+
* Throws a clear error when a caller references a role that isn't registered.
|
|
1161
|
+
* Roles are loaded from `.flue/roles/` at build time. Called eagerly at the top
|
|
1162
|
+
* of prompt()/skill()/task() so typos surface before any LLM work begins.
|
|
1163
|
+
*/
|
|
1164
|
+
assertRoleExists(roleName) {
|
|
1165
|
+
if (!roleName) return;
|
|
1166
|
+
if (this.config.roles[roleName]) return;
|
|
1167
|
+
const available = Object.keys(this.config.roles);
|
|
1168
|
+
const list = available.length > 0 ? available.join(", ") : "(none defined)";
|
|
1169
|
+
throw new Error(`[flue] Role "${roleName}" not registered. Available roles: ${list}. Define roles as markdown files under \`.flue/roles/\`.`);
|
|
1117
1170
|
}
|
|
1118
1171
|
injectRoleInstructions(text, roleName) {
|
|
1119
1172
|
if (!roleName) return text;
|
|
@@ -107,8 +107,13 @@ interface AgentConfig {
|
|
|
107
107
|
/** Discovered at runtime from .agents/skills/ in the session's cwd. */
|
|
108
108
|
skills: Record<string, Skill>;
|
|
109
109
|
roles: Record<string, Role>;
|
|
110
|
-
|
|
111
|
-
|
|
110
|
+
/**
|
|
111
|
+
* Session-wide default model. Undefined by default — the user must set it via
|
|
112
|
+
* `init({ model: "provider/model-id" })` or pass `{ model }` at each prompt/
|
|
113
|
+
* skill/task call site. Calls with no model resolved throw clearly at runtime.
|
|
114
|
+
*/
|
|
115
|
+
model: Model<any> | undefined;
|
|
116
|
+
/** Resolve a "provider/modelId" string to a Model instance. Throws on invalid input. */
|
|
112
117
|
resolveModel?: (modelString: string) => Model<any>;
|
|
113
118
|
compaction?: CompactionConfig;
|
|
114
119
|
}
|
|
@@ -121,7 +126,7 @@ interface FlueContext {
|
|
|
121
126
|
/** Create a session with sandbox + persistence. Can only be called once per request. */
|
|
122
127
|
init(options?: SessionInit): Promise<FlueSession>;
|
|
123
128
|
}
|
|
124
|
-
/**
|
|
129
|
+
/** All fields are optional — omitting gives platform defaults (empty sandbox, platform store, build-time model). */
|
|
125
130
|
interface SessionInit {
|
|
126
131
|
/**
|
|
127
132
|
* - `'empty'` (default): In-memory sandbox, no files, no host access.
|
|
@@ -132,6 +137,15 @@ interface SessionInit {
|
|
|
132
137
|
sandbox?: 'empty' | 'local' | SandboxFactory | BashLike;
|
|
133
138
|
/** Defaults to platform store (in-memory on Node, DO SQLite on Cloudflare). */
|
|
134
139
|
persist?: SessionStore;
|
|
140
|
+
/**
|
|
141
|
+
* Override the default model for this session. Applies to all prompt(), skill(),
|
|
142
|
+
* and task() calls unless overridden at the call site.
|
|
143
|
+
*
|
|
144
|
+
* Format: `'provider/modelId'` (e.g. `'anthropic/claude-opus-4-20250514'`).
|
|
145
|
+
*
|
|
146
|
+
* Precedence (highest wins): per-call `model` > role `model` > session `model` > build-time default.
|
|
147
|
+
*/
|
|
148
|
+
model?: string;
|
|
135
149
|
}
|
|
136
150
|
interface FlueSession {
|
|
137
151
|
prompt<S extends v.GenericSchema>(text: string, options: PromptOptions<S> & {
|
|
@@ -318,10 +332,6 @@ interface BuildOptions {
|
|
|
318
332
|
target?: 'node' | 'cloudflare';
|
|
319
333
|
/** Overrides `target` when provided. */
|
|
320
334
|
plugin?: BuildPlugin;
|
|
321
|
-
model?: {
|
|
322
|
-
provider: string;
|
|
323
|
-
modelId: string;
|
|
324
|
-
};
|
|
325
335
|
}
|
|
326
336
|
//#endregion
|
|
327
337
|
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 };
|