@fugood/buttress-server 2.25.10 → 2.25.11

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
@@ -241,7 +241,7 @@ Every generator entry has a `type`, an optional `[generators.backend]` table, an
241
241
 
242
242
  ```toml
243
243
  [[generators]]
244
- type = "ggml-llm" # or "ggml-stt" / "ggml-tts" / "mlx-llm"
244
+ type = "ggml-llm" # or "ggml-stt" / "ggml-tts" / "ggml-decision" / "mlx-llm"
245
245
 
246
246
  [generators.backend]
247
247
  # (see per-type sections below)
@@ -261,7 +261,7 @@ Shared by **all** generator types:
261
261
  | `revision` | string | Default `"main"` |
262
262
  | `download` | boolean | Pre-download at server startup (default `false`) |
263
263
 
264
- Additional keys honored by **ggml-llm**, **ggml-stt** and **ggml-tts** (mlx-llm gets quantization from the repo itself and does not use these):
264
+ Additional keys honored by **ggml-llm**, **ggml-stt**, **ggml-tts** and **ggml-decision** (mlx-llm gets quantization from the repo itself and does not use these):
265
265
 
266
266
  | Key | Type | Notes |
267
267
  | ------------------------- | --------- | -------------------------------------------------------------------------------- |
@@ -493,6 +493,66 @@ vocoder_batch_size = 4096
493
493
 
494
494
  ---
495
495
 
496
+ ### `ggml-decision` (typed decisions via `@fugood/llama.node`)
497
+
498
+ Loads a typed decision model (System One — Julia-1, Laya, Kev, Jevling, lev,
499
+ ...) and answers `choice` / `score` / `noul` questions about a state with
500
+ calibrated probabilities: one forward pass per answer, no generated tokens.
501
+ Requests and results follow the TypeSafe `/v1/systemone` API that llama-server
502
+ serves. Clients reach it through the Typed Decision (GGML) generator; local
503
+ functions through `context.buttress.decide`.
504
+
505
+ The model is checked when its context loads: a GGUF with no decision metadata
506
+ (`<arch>.decision.type`) and no `system_one` template, or one whose decision
507
+ type this build cannot serve, fails the load with the reason. Requests on one
508
+ generator run one at a time.
509
+
510
+ **`[generators.backend]`** — same keys as `ggml-tts` (`variant`,
511
+ `variant_preference`, `gpu_memory_fraction`, `cpu_memory_fraction`).
512
+
513
+ **`[generators.model]`** — common keys plus:
514
+
515
+ | Key | Type | Default | Notes |
516
+ | ------------------------- | -------- | -------------------------------- | ------------------------------------------------------------------ |
517
+ | `preferred_quantizations` | string[] | `["q8_0", "q4_k_m", <no-quant>]` | Default fallback chain |
518
+ | `mmproj_repo_id` | string | falls back to `repo_id` | Vision projector repo, for models whose prompt takes images |
519
+ | `mmproj_filename` | string | auto (`*mmproj*`) | Pin a specific projector artifact |
520
+ | `mmproj_url` | string | — | Direct projector URL (skips manifest lookup) |
521
+ | `mmproj_revision` | string | falls back to `revision` | |
522
+ | `mmproj_local_path` | string | — | Requires `allow_local_file` |
523
+ | `rank_pooling` | boolean | `false` | `system_one` models with a classification head (`rank_head` readout) |
524
+ | `use_gpu` | boolean | `true` | Force-disable GPU even when available |
525
+ | `n_gpu_layers` | number | `99` | Forced to `0` when the GPU is unused |
526
+
527
+ Setting any `mmproj_*` key loads a projector; requests can then pass `images`
528
+ (file paths or data URLs). The projector is ignored, with a warning, when the
529
+ model's prompt has no place for images.
530
+
531
+ **Runtime extras** — under `[runtime]` for ggml-decision:
532
+
533
+ | Key | Type | Default | Notes |
534
+ | -------------------------- | ------ | --------------- | ---------------------------------------- |
535
+ | `n_ctx` | number | `4096` | Must hold the state plus every question |
536
+ | `n_batch` / `n_ubatch` | number | `2048` / `512` | |
537
+ | `max_threads` | number | auto | Caps the llama.cpp thread count |
538
+ | `flash_attn_type` | string | `auto` | `auto` / `on` / `off` |
539
+ | `context_release_delay_ms` | number | `10000` | `0` releases the context right away |
540
+
541
+ **Example**
542
+
543
+ ```toml
544
+ [[generators]]
545
+ type = "ggml-decision"
546
+ [generators.model]
547
+ repo_id = "ggml-org/Julia-1-GGUF"
548
+ filename = "Julia-1-Q8_0.gguf"
549
+ download = true
550
+ [generators.runtime]
551
+ n_ctx = 4096
552
+ ```
553
+
554
+ ---
555
+
496
556
  ### `mlx-llm` (Apple Silicon, Python `mlx-lm` / `mlx-vlm` bridge)
497
557
 
498
558
  Loads an MLX-format model on Apple Silicon. On first use, the backend creates a virtualenv at `{cache_dir}/mlx-env` and installs `mlx_lm_package`, `mlx_vlm_package`, plus `torch` and `torchvision` (required by some VLM processors). If an existing venv already has `mlx_vlm` and `torch` importable, the install step is skipped. There is no `[generators.backend]` section.
@@ -719,6 +779,7 @@ export default async function ({ path }: { path: string }, context: ButtressFunc
719
779
  | `buttress.detokenize({ model?, tokens })` | Convert token ids back into text with the same model. |
720
780
  | `buttress.transcribe({ model?, filePath \| audioData, options? })` | Transcribe audio with this server's STT generator. |
721
781
  | `buttress.synthesize({ model?, text, options? })` | Synthesize speech with this server's TTS generator (`onnx-tts` or `ggml-tts`); the WAV lands in `tempDir` → `{ path, sampling_rate, channels }`. |
782
+ | `buttress.decide({ model?, state, questions, images? })` | Answer typed `choice` / `score` / `noul` questions about `state` with this server's `ggml-decision` generator (TypeSafe `/v1/systemone` shape) → `{ model, answers, usage }`. `answers[id]` is typed from its question. `images` are local paths or data URLs. |
722
783
  | `bank.list({ keyword?, meta?, ids? })` / `bank.get(id)` / `bank.update(properties, { dontNotify? })` / `bank.remove(id)` | Read/write the bound workspace's remote **Data Bank** (see below). To listen for changes, use `bank.subscribe` inside a daemon function. |
723
784
  | `daemons.emit(name, event, data?)` / `daemons.list()` | Signal a running **daemon function**'s `onEvent` handlers (see below) / list running daemon names. |
724
785
  | `emit(event, data)` | Progress event; delivered to SSE callers, ignored otherwise. |
@@ -894,6 +955,7 @@ system_prompt_file = "./prompts/ops.md" # or inline: system_prompt = "..."
894
955
  tools = ["get_server_status", "restart_service"] # local function names (explicit; no wildcard)
895
956
  local_devices = false # opt in to LAN scan + device DevTools tools
896
957
  max_turns = 30 # assistant↔tool round-trips per run (default 30)
958
+ # codemode = true # opt in: a sandboxed JS tool that calls the other tools
897
959
  # max_tokens_per_run = 200000 # per-run token budget; unset = unlimited
898
960
  # temperature = 0.2 # unrecognized keys pass through to generation
899
961
 
@@ -901,6 +963,9 @@ max_turns = 30 # assistant↔tool round-trips pe
901
963
  url = "https://api.githubcopilot.com/mcp/"
902
964
  # headers = { Authorization = "Bearer ..." }
903
965
  # optional = true # continue without this server if it won't connect
966
+ # timeout = 60 # per-request seconds (progress renews it)
967
+ # exposure = "direct" # "hidden": unreachable; "codemode": scripts only (needs codemode = true)
968
+ # tool_exposure = { "delete_*" = "hidden" } # per tool, by exact name or * pattern
904
969
 
905
970
  [agents_options] # optional; defaults shown
906
971
  # sessions_dir = "./.buttress-agent/sessions" # relative to this config file
@@ -924,8 +989,18 @@ tracking, and the function's own `meta.timeout` deadline), and aborting the
924
989
  run aborts in-flight tool calls and their spawned processes. A listed function
925
990
  that is missing fails the run loudly rather than letting a headless automation
926
991
  improvise around it. MCP tools get server-qualified names
927
- (`mcp__github__create_issue`); MCP servers connect lazily on the first run and
928
- fail closed unless marked `optional = true`.
992
+ (`mcp__github__create_issue`; characters outside `[A-Za-z0-9_]` become `_`);
993
+ MCP servers connect lazily on the first run and fail closed unless marked
994
+ `optional = true`.
995
+
996
+ **Codemode.** `codemode = true` on an `[[agents]]` entry (default `false`) adds a
997
+ `codemode` tool: the model writes JavaScript that runs in a QuickJS sandbox and
998
+ calls the agent's other tools (`await tools.<name>(args)`), so it can batch calls
999
+ and filter large results before they reach the context. MCP servers of such an
1000
+ agent may use `exposure = "codemode"` (or `"deferred"`) to keep their tools out
1001
+ of the model's tool list; scripts find them with `searchTools()`.
1002
+ `models.classify()` runs pi-ai classifier models with the agent's credentials.
1003
+ Small local models may write poor scripts — prefer it with capable models.
929
1004
 
930
1005
  **Local devices.** Set `local_devices = true` on an individual `[[agents]]` entry
931
1006
  (default `false`) to add just two built-in tools, independently of local functions/MCP:
@@ -14,6 +14,7 @@ needed. On first start the server scaffolds `buttress-functions.d.ts` and a
14
14
  | `simple-rag.ts` | Token chunking, embeddings, `sqlite3` + `sqlite-vec` retrieval, then completion | a chat LLM + a GGML embedding `[[generators]]` entry (below) |
15
15
  | `transcribe-media.ts` | `context.spawn` (ffmpeg), the scratch dir, SSE progress, STT | `ffmpeg` on PATH + an STT `[[generators]]` entry |
16
16
  | `text-to-speech.ts` | TTS (`context.buttress.synthesize`) + downloadable output (`context.fileUrl`) | an `onnx-tts` `[[generators]]` entry |
17
+ | `classify-ticket.ts` | Typed decisions (`context.buttress.decide`): a choice, a score and yes/no flags with calibrated probabilities | a `ggml-decision` `[[generators]]` entry |
17
18
  | `bank-note.ts` | Reading/writing the workspace's remote Data Bank (`context.bank`) | `bricks buttress bank-key` run on this host |
18
19
  | `bank-watch-daemon.ts` | A daemon (`meta.daemon = true`): `context.bank.subscribe`, `context.setInterval`, `context.onEvent` | `bricks buttress bank-key` run on this host |
19
20
  | `_auth.ts` | Custom auth: keep workspace tokens working, add static API keys | see the file header |
@@ -0,0 +1,73 @@
1
+ // Triage a support ticket with this server's typed decision model: which team
2
+ // takes it, how urgent it is, and two yes/no flags — calibrated probabilities
3
+ // from one forward pass per answer, no text generation and no output parsing.
4
+ //
5
+ // Needs a ggml-decision generator in the server config, e.g.:
6
+ //
7
+ // [[generators]]
8
+ // type = "ggml-decision"
9
+ // [generators.model]
10
+ // repo_id = "ggml-org/Julia-1-GGUF"
11
+ // filename = "Julia-1-Q8_0.gguf"
12
+ //
13
+ // curl -X POST http://<host>:<port>/functions/classify-ticket \
14
+ // -H 'Content-Type: application/json' \
15
+ // -d '{"message": "I was charged twice and nobody replies. I want my money back today."}'
16
+ // # → { "result": { "route": "billing", "urgency": 1.53, "urgencyLabel": "Today", "angry": true, … } }
17
+
18
+ export const meta: ButtressFunctionMeta = {
19
+ description:
20
+ 'Triage a support ticket: route it to a team, rate its urgency, and flag anger or a refund request',
21
+ parameters: {
22
+ type: 'object',
23
+ properties: {
24
+ message: { type: 'string', description: 'The customer message' },
25
+ model: { type: 'string', description: 'Optional decision model override' },
26
+ },
27
+ required: ['message'],
28
+ },
29
+ timeout: '2m',
30
+ }
31
+
32
+ type Input = { message: string; model?: string }
33
+
34
+ const URGENCY_LEVELS = ['Can wait', 'This week', 'Today', 'Right now']
35
+
36
+ export default async function ({ message, model }: Input, context: ButtressFunctionContext) {
37
+ const { answers, usage } = await context.buttress.decide({
38
+ model,
39
+ state: message,
40
+ questions: {
41
+ route: {
42
+ type: 'choice',
43
+ instructions: 'Which team should handle this?',
44
+ criteria: {
45
+ billing: 'Payments, charges, refunds',
46
+ shipping: 'Delivery and tracking',
47
+ technical: 'App or website problems',
48
+ account: 'Login and profile',
49
+ },
50
+ },
51
+ urgency: { type: 'score', instructions: 'How urgent is this?', criteria: URGENCY_LEVELS },
52
+ angry: { type: 'noul', instructions: 'The customer is angry' },
53
+ wants_refund: { type: 'noul', instructions: 'The customer asks for a refund' },
54
+ },
55
+ })
56
+
57
+ // `answers.route.choice` is typed as 'billing' | 'shipping' | 'technical' | 'account'
58
+ return {
59
+ route: answers.route.choice,
60
+ routeConfidence: answers.route.confidence,
61
+ // Expected level index (0 = "Can wait"), can fall between two levels
62
+ urgency: answers.urgency.score,
63
+ urgencyLabel: URGENCY_LEVELS[Math.round(answers.urgency.score)],
64
+ angry: answers.angry.noul >= 0.5,
65
+ wantsRefund: answers.wants_refund.noul >= 0.5,
66
+ probabilities: {
67
+ route: answers.route.probabilities,
68
+ angry: answers.angry.noul,
69
+ wantsRefund: answers.wants_refund.noul,
70
+ },
71
+ inputTokens: usage.input_tokens,
72
+ }
73
+ }
@@ -300,3 +300,25 @@ use_gpu = true
300
300
  [generators.runtime]
301
301
  n_ctx = 8192
302
302
  vocoder_batch_size = 4096
303
+
304
+ # Typed decision generators
305
+ #
306
+ # A typed decision model (System One) answers `choice` / `score` / `noul`
307
+ # questions about a state with calibrated probabilities — one forward pass per
308
+ # answer, no generated tokens. Clients reach it through the Typed Decision
309
+ # (GGML) generator, and local functions through `context.buttress.decide`.
310
+ # Uncomment to enable:
311
+ #
312
+ # [[generators]]
313
+ # type = "ggml-decision"
314
+ # [generators.model]
315
+ # repo_id = "ggml-org/Julia-1-GGUF"
316
+ # filename = "Julia-1-Q8_0.gguf"
317
+ # download = true
318
+ # # Models whose prompt takes images also need a projector:
319
+ # # mmproj_repo_id = "..."
320
+ # # mmproj_filename = "mmproj-....gguf"
321
+ # # system_one models with a classification head (rank_head readout):
322
+ # # rank_pooling = true
323
+ # [generators.runtime]
324
+ # n_ctx = 4096
@@ -0,0 +1 @@
1
+ import '@earendil-works/pi-codemode/worker';
@@ -0,0 +1,7 @@
1
+ import type { MutableModels } from '@earendil-works/pi-ai';
2
+ export declare function createAgentCodemodeTool({ tools, declaredNames, getState, models, }: {
3
+ tools: any[];
4
+ declaredNames: Set<string>;
5
+ getState: () => any;
6
+ models: MutableModels;
7
+ }): Promise<any>;
@@ -4,12 +4,11 @@ export type McpAgentTool = {
4
4
  label: string;
5
5
  description: string;
6
6
  parameters: Record<string, any>;
7
- execute: (toolCallId: string, args: any, signal?: AbortSignal) => Promise<any>;
7
+ execute: (toolCallId: string, args: any, signal?: AbortSignal, onUpdate?: any) => Promise<any>;
8
8
  _mcpServer: string;
9
9
  _mcpToolName: string;
10
+ _mcpExposure: string;
10
11
  };
11
- /** `mcp__<server>__<tool>`, hashed into the cap when too long or colliding. */
12
- export declare const qualifyToolName: (serverName: string, toolName: string, usedNames: Set<string>) => string;
13
12
  export type McpManager = {
14
13
  /**
15
14
  * Tools for every configured server of an agent. Fail-closed: a server that
@@ -1,17 +1,30 @@
1
- import type { AgentMessage, Session } from '@earendil-works/pi-agent-core';
1
+ import type { AgentMessage } from '@earendil-works/pi-agent-core';
2
2
  import type { AgentSessionSummary, AgentsConfig } from './types';
3
+ type SessionMeta = {
4
+ id: string;
5
+ createdAt: number;
6
+ parentSessionId?: string;
7
+ path: string;
8
+ modifiedAt: number;
9
+ /** Written by pi-agent-core's v4 repo; converted on the first append. */
10
+ legacy: boolean;
11
+ };
12
+ export type AgentSession = {
13
+ metadata: SessionMeta;
14
+ /** True once the file is in the current format with no torn tail. */
15
+ appendable: boolean;
16
+ };
3
17
  export type AgentSessionStore = {
4
- create: (agentName: string) => Promise<Session<any>>;
18
+ create: (agentName: string) => Promise<AgentSession>;
5
19
  /** Throws when the id is unknown within the agent's scope. */
6
- open: (agentName: string, sessionId: string) => Promise<Session<any>>;
7
- fork: (agentName: string, sessionId: string) => Promise<Session<any>>;
8
- append: (session: Session<any>, message: AgentMessage) => Promise<string>;
9
- close: (session: Session<any>) => Promise<void>;
20
+ open: (agentName: string, sessionId: string) => Promise<AgentSession>;
21
+ fork: (agentName: string, sessionId: string) => Promise<AgentSession>;
22
+ append: (session: AgentSession, message: AgentMessage) => Promise<void>;
10
23
  list: (agentName: string, limit?: number) => Promise<AgentSessionSummary[]>;
11
24
  /** Reconstruct the pi message history for continuing a session. */
12
- messages: (session: Session<any>) => Promise<AgentMessage[]>;
25
+ messages: (session: AgentSession) => Promise<AgentMessage[]>;
13
26
  /** Retention sweep across every configured agent scope. */
14
27
  sweep: (agentNames: string[]) => Promise<number>;
15
- dispose: () => Promise<void>;
16
28
  };
17
29
  export declare const createAgentSessionStore: (config: AgentsConfig) => AgentSessionStore;
30
+ export {};
@@ -13,7 +13,18 @@ export type AgentMcpServerConfig = {
13
13
  env?: Record<string, string>;
14
14
  /** Degrade-and-continue when this server fails to connect (default: fail the run). */
15
15
  optional?: boolean;
16
+ /** Per-request timeout in seconds (default 60; progress notifications renew it). */
17
+ timeout?: number;
18
+ /**
19
+ * `direct` (default) declares every tool; `codemode` / `deferred` leave tools
20
+ * to codemode scripts (agents with `codemode = true` only); `hidden` makes
21
+ * them unreachable.
22
+ */
23
+ exposure?: AgentMcpExposure;
24
+ /** Per-tool exposure by exact tool name or `*` pattern. */
25
+ toolExposure?: Record<string, AgentMcpExposure>;
16
26
  };
27
+ export type AgentMcpExposure = 'direct' | 'codemode' | 'deferred' | 'hidden';
17
28
  export type AgentDefinition = {
18
29
  /** Unique agent name — the session scope key. */
19
30
  name: string;
@@ -29,6 +40,11 @@ export type AgentDefinition = {
29
40
  tools: string[];
30
41
  /** Opt-in LAN discovery and device DevTools access (default false). */
31
42
  localDevices: boolean;
43
+ /**
44
+ * Opt-in `codemode` tool (default false): scripts that call the agent's
45
+ * tools, including MCP tools with `codemode` / `deferred` exposure.
46
+ */
47
+ codemode: boolean;
32
48
  mcpServers: Record<string, AgentMcpServerConfig>;
33
49
  /** Assistant↔tool round-trips per run. */
34
50
  maxTurns: number;
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import{a as e,i as t,n,r,t as i}from"./client-DzfRSFcJ.mjs";import a from"node:readline";const o=`\x1B[2m`,s=`\x1B[33m`,c=`\x1B[31m`,l=`\x1B[0m`,u=e=>{let t={name:null,configArg:null,url:null,token:null,sessionId:null,fork:!1,listSessions:!1,plain:!1};for(let n=0;n<e.length;n+=1){let r=e[n];r===`-h`||r===`--help`?(console.log(`
2
+ import{a as e,i as t,n,r,t as i}from"./client-DEUSDK5d.mjs";import a from"node:readline";const o=`\x1B[2m`,s=`\x1B[33m`,c=`\x1B[31m`,l=`\x1B[0m`,u=e=>{let t={name:null,configArg:null,url:null,token:null,sessionId:null,fork:!1,listSessions:!1,plain:!1};for(let n=0;n<e.length;n+=1){let r=e[n];r===`-h`||r===`--help`?(console.log(`
3
3
  bricks-buttress agent — chat with a configured agent on a running server
4
4
 
5
5
  Usage:
@@ -19,4 +19,4 @@ In the chat: /exit quits, /new starts a fresh session, Ctrl+C aborts the
19
19
  current run (a second Ctrl+C quits).
20
20
  `),process.exit(0)):r===`-c`||r===`--config`?t.configArg=e[++n]??null:r===`--url`?t.url=e[++n]??null:r===`--token`?t.token=e[++n]??null:r===`--session`?t.sessionId=e[++n]??null:r===`--fork`?(t.sessionId=e[++n]??null,t.fork=!0):r===`--sessions`?t.listSessions=!0:r===`--plain`?t.plain=!0:!r.startsWith(`-`)&&!t.name?t.name=r:(console.error(`Unknown argument: ${r}`),process.exit(1))}return t},d=e=>{console.error(`${c}${e}${l}`),process.exit(1)},f=async(t,n,r,i,a,u)=>{let d=i,f=!1,p=!1,m=()=>{f&&=(process.stdout.write(`${l}\n`),!1)},h=await e(t,n,{prompt:r,sessionId:i??void 0,fork:a},{signal:u,onFrame:({event:e,payload:t})=>{if(e===`session`){t.sessionId&&t.sessionId!==d&&(d=t.sessionId,i||console.log(`${o}session ${d}${l}`));return}if(e!==`agent`)return;let n=t?.event;if(t?.type===`message_update`&&n)n.type===`thinking_delta`?(f||=(process.stdout.write(`${o}`),!0),process.stdout.write(n.delta??``),p=!0):n.type===`text_delta`&&(m(),process.stdout.write(n.delta??``),p=!0);else if(t?.type===`tool_execution_start`){m(),p&&process.stdout.write(`
21
21
  `);let e=JSON.stringify(t.args??{});console.log(`${s}⚙ ${t.toolName}${l}${o}(${e.length>160?`${e.slice(0,160)}…`:e})${l}`),p=!1}else t?.type===`tool_execution_end`?console.log(t.isError?`${c}✗ ${t.toolName} failed${l}`:`${o}✓ ${t.toolName}${l}`):t?.type===`tool_emit`&&console.log(`${o} ${t.toolName} → ${t.event}${l}`)}});if(m(),h.kind===`result`){let e=h.result;d=e.sessionId??d;let t=e.usage||{};return process.stdout.write(`
22
- `),console.log(`${o}— ${e.stopReason} · ${t.totalTurns??`?`} turn(s) · ${t.input??0} in / ${t.output??0} out · session ${d}${l}`),{sessionId:d,ok:!0}}return h.kind===`aborted`?(console.log(`\n${s}(aborted)${l}`),{sessionId:h.sessionId??d,ok:!1}):h.kind===`error`?(console.error(`\n${c}Run failed: ${h.message}${l}`),{sessionId:h.sessionId??d,ok:!1}):(console.error(`\n${c}Connection lost mid-run.${l}`),{sessionId:h.sessionId??d,ok:!1})},p=async(e,t,n,r)=>{let s=n,u=r;console.log(`${t}${l} @ ${e.baseUrl}`+(s?` ${o}(${u?`forking`:`continuing`} ${s})${l}`:``)),console.log(`${o}/exit to quit, /new for a fresh session, Ctrl+C aborts a running turn${l}`);let d=a.createInterface({input:process.stdin,output:process.stdout}),p=!1;d.on(`close`,()=>{p=!0});let m=e=>new Promise(t=>{if(p){t(null);return}d.question(e,t),d.once(`close`,()=>t(null))}),h=null;for(d.on(`SIGINT`,()=>{h?(h.abort(),h=null):(d.close(),process.exit(0))});;){let n=await m(`> ${l}`);if(n==null)break;let r=n.trim();if(!r)continue;if(r===`/exit`||r===`/quit`)break;if(r===`/new`){s=null,u=!1,console.log(`${o}Started a fresh session.${l}`);continue}h=new AbortController;let a=s;try{let n=await f(e,t,r,s,u,h.signal);n.sessionId&&(s=n.sessionId),i(a,s)&&(u=!1)}catch(e){console.error(`${c}${e?.message||e}${l}`)}finally{h=null}}d.close()},m=async e=>{let i=u(e),{config:a,agentsConfig:s}=n(i.configArg),c=a.server.port||2080,f=(i.url||`http://127.0.0.1:${c}`).replace(/\/$/,``),m={baseUrl:f,token:i.token||(s?r(s.runtimeTokenFile):null)||process.env.BUTTRESS_AGENT_TOKEN||null},h=await t(m,`/agents`).catch(e=>d(`Cannot reach ${f}: ${e?.message||e} (is the server running?)`));if(!h.ok){let e=await h.text().catch(()=>``);d(`Server rejected the request (${h.status}): ${e.slice(0,300)}`)}let{agents:g}=await h.json();if(!i.name){console.log(`Configured agents on ${f}:`);for(let e of g)console.log(`- ${e}`);g.length===0&&console.log(`(none — add [[agents]] to the server config)`);return}if(g.includes(i.name)||d(`Unknown agent "${i.name}" (configured: ${g.join(`, `)||`none`})`),i.listSessions){let{sessions:e}=await(await t(m,`/agents/${encodeURIComponent(i.name)}/sessions`)).json();if(!e?.length){console.log(`No sessions yet.`);return}for(let t of e)console.log(`${t.sessionId} ${o}${t.updatedAt}${l}${t.parentSession?` ${o}(fork of ${t.parentSession})${l}`:``}`),t.preview&&console.log(` ${o}${t.preview}${l}`);return}if(process.stdin.isTTY&&process.stdout.isTTY&&!i.plain){let{runAgentTui:e}=await import(`./tui-DbQ0zW-C.mjs`);await e({connection:m,agentName:i.name,sessionId:i.sessionId,fork:i.fork});return}await p(m,i.name,i.sessionId,i.fork)};export{m as runAgentCommand};
22
+ `),console.log(`${o}— ${e.stopReason} · ${t.totalTurns??`?`} turn(s) · ${t.input??0} in / ${t.output??0} out · session ${d}${l}`),{sessionId:d,ok:!0}}return h.kind===`aborted`?(console.log(`\n${s}(aborted)${l}`),{sessionId:h.sessionId??d,ok:!1}):h.kind===`error`?(console.error(`\n${c}Run failed: ${h.message}${l}`),{sessionId:h.sessionId??d,ok:!1}):(console.error(`\n${c}Connection lost mid-run.${l}`),{sessionId:h.sessionId??d,ok:!1})},p=async(e,t,n,r)=>{let s=n,u=r;console.log(`${t}${l} @ ${e.baseUrl}`+(s?` ${o}(${u?`forking`:`continuing`} ${s})${l}`:``)),console.log(`${o}/exit to quit, /new for a fresh session, Ctrl+C aborts a running turn${l}`);let d=a.createInterface({input:process.stdin,output:process.stdout}),p=!1;d.on(`close`,()=>{p=!0});let m=e=>new Promise(t=>{if(p){t(null);return}d.question(e,t),d.once(`close`,()=>t(null))}),h=null;for(d.on(`SIGINT`,()=>{h?(h.abort(),h=null):(d.close(),process.exit(0))});;){let n=await m(`> ${l}`);if(n==null)break;let r=n.trim();if(!r)continue;if(r===`/exit`||r===`/quit`)break;if(r===`/new`){s=null,u=!1,console.log(`${o}Started a fresh session.${l}`);continue}h=new AbortController;let a=s;try{let n=await f(e,t,r,s,u,h.signal);n.sessionId&&(s=n.sessionId),i(a,s)&&(u=!1)}catch(e){console.error(`${c}${e?.message||e}${l}`)}finally{h=null}}d.close()},m=async e=>{let i=u(e),{config:a,agentsConfig:s}=n(i.configArg),c=a.server.port||2080,f=(i.url||`http://127.0.0.1:${c}`).replace(/\/$/,``),m={baseUrl:f,token:i.token||(s?r(s.runtimeTokenFile):null)||process.env.BUTTRESS_AGENT_TOKEN||null},h=await t(m,`/agents`).catch(e=>d(`Cannot reach ${f}: ${e?.message||e} (is the server running?)`));if(!h.ok){let e=await h.text().catch(()=>``);d(`Server rejected the request (${h.status}): ${e.slice(0,300)}`)}let{agents:g}=await h.json();if(!i.name){console.log(`Configured agents on ${f}:`);for(let e of g)console.log(`- ${e}`);g.length===0&&console.log(`(none — add [[agents]] to the server config)`);return}if(g.includes(i.name)||d(`Unknown agent "${i.name}" (configured: ${g.join(`, `)||`none`})`),i.listSessions){let{sessions:e}=await(await t(m,`/agents/${encodeURIComponent(i.name)}/sessions`)).json();if(!e?.length){console.log(`No sessions yet.`);return}for(let t of e)console.log(`${t.sessionId} ${o}${t.updatedAt}${l}${t.parentSession?` ${o}(fork of ${t.parentSession})${l}`:``}`),t.preview&&console.log(` ${o}${t.preview}${l}`);return}if(process.stdin.isTTY&&process.stdout.isTTY&&!i.plain){let{runAgentTui:e}=await import(`./tui-Bl2ADqOw.mjs`);await e({connection:m,agentName:i.name,sessionId:i.sessionId,fork:i.fork});return}await p(m,i.name,i.sessionId,i.fork)};export{m as runAgentCommand};
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import{a as e,n as t}from"./config-DbRjQnNp.mjs";import n from"node:path";import r from"node:fs";import i from"@iarna/toml";const a=a=>{let o={},s=process.cwd();a&&(r.existsSync(a)?(o=i.parse(r.readFileSync(a,`utf8`)),s=n.dirname(n.resolve(a))):o=i.parse(a));let c=e(o);return{config:c,configDir:s,agentsConfig:t(c,{configDir:s})}},o=e=>{try{return r.readFileSync(e,`utf8`).trim()||null}catch{return null}},s=async(e,t,n)=>fetch(`${e.baseUrl}${t}`,{...n,headers:{"content-type":`application/json`,...e.token?{authorization:`Bearer ${e.token}`}:{},...n?.headers}});async function*c(e){let t=e.getReader(),n=new TextDecoder,r=``;try{for(;;){let{done:e,value:i}=await t.read();if(e)break;r+=n.decode(i,{stream:!0});let a=r.indexOf(`
2
+ import{a as e,n as t}from"./config-DtcwsKrn.mjs";import n from"node:path";import r from"node:fs";import i from"@iarna/toml";const a=a=>{let o={},s=process.cwd();a&&(r.existsSync(a)?(o=i.parse(r.readFileSync(a,`utf8`)),s=n.dirname(n.resolve(a))):o=i.parse(a));let c=e(o);return{config:c,configDir:s,agentsConfig:t(c,{configDir:s})}},o=e=>{try{return r.readFileSync(e,`utf8`).trim()||null}catch{return null}},s=async(e,t,n)=>fetch(`${e.baseUrl}${t}`,{...n,headers:{"content-type":`application/json`,...e.token?{authorization:`Bearer ${e.token}`}:{},...n?.headers}});async function*c(e){let t=e.getReader(),n=new TextDecoder,r=``;try{for(;;){let{done:e,value:i}=await t.read();if(e)break;r+=n.decode(i,{stream:!0});let a=r.indexOf(`
3
3
 
4
4
  `);for(;a!==-1;){let e=r.slice(0,a);r=r.slice(a+2),a=r.indexOf(`
5
5
 
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env node
2
+ import{n as e}from"./protocol-RJSV46H1.mjs";import{createRequire as t}from"node:module";import{readFile as n}from"node:fs/promises";import{runToolCall as r}from"@earendil-works/pi-agent-core";import{Worker as i}from"node:worker_threads";function a(e){let t=``;for(let n of e){let e=t===``?/^[A-Za-z_$]$/.test(n):/^[A-Za-z0-9_$]$/.test(n);t+=e?n:`_`}return t===``?`_`:t}const o=/^[A-Za-z_$][A-Za-z0-9_$]*$/;function s(e,t={}){let n=e.inputSchema===void 0?`unknown`:m(e.inputSchema,{maxChars:t.inputMaxChars??16e3});return`${a(e.name)}(args: ${n}): Promise<${u(e.outputSchema)}>;`}function c(e,t={}){let n=`declare const tools: { ${s(e,t)} };`;return`${e.description?.trim()??``}\n\ncodemode tool declaration:\n\`\`\`ts\n${n}\n\`\`\``}function l(e){if(!f(e)||!f(e.properties))return;let{content:t,isError:n,_meta:r,structuredContent:i}=e.properties;if(!(!f(t)||t.type!==`array`||!f(t.items)||t.items.type!==`object`)&&!(!f(n)||n.type!==`boolean`||!f(r)||r.type!==`object`))return f(i)||typeof i==`boolean`?i:!0}function u(e){let t=l(e);if(t!==void 0){let e=m(t);return e===`unknown`?`CallToolResult`:`CallToolResult<${e}>`}return e===void 0?`unknown`:m(e)}function d(e){return o.test(e)?e:JSON.stringify(e)}function f(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function p(e){let t=[...new Set(e)];return t.includes(`unknown`)?`unknown`:t.length===0?`never`:t.join(` | `)}function m(e,t={}){let n=g(e,{root:e,resolving:new Set,expansions:0});return t.maxChars!==void 0&&n.length>t.maxChars?`unknown`:n}function h(e,t){if(e!==`#`&&!e.startsWith(`#/`))return;let n=t;for(let t of e.slice(2).split(`/`).filter(Boolean)){let e=decodeURIComponent(t).replaceAll(`~1`,`/`).replaceAll(`~0`,`~`);if(!f(n)||!(e in n))return;n=n[e]}return typeof n==`boolean`||f(n)?n:void 0}function g(e,t){if(e===!0)return`unknown`;if(e===!1)return`never`;if(!f(e))return`unknown`;if(typeof e.$ref==`string`){let n=e.$ref;if(t.resolving.has(n)||t.expansions>=32)return`unknown`;let r=h(n,t.root);if(r===void 0)return`unknown`;t.expansions++,t.resolving.add(n);try{return g(r,t)}finally{t.resolving.delete(n)}}if(`const`in e)return JSON.stringify(e.const)??`unknown`;if(Array.isArray(e.enum))return p(e.enum.map(e=>JSON.stringify(e)??`unknown`));let n=Array.isArray(e.anyOf)?e.anyOf:Array.isArray(e.oneOf)?e.oneOf:void 0;if(n)return p(n.map(e=>g(e,t)));if(Array.isArray(e.allOf)){let n=e.allOf.map(e=>g(e,t)).filter(e=>e!==`unknown`);return n.length===0?`unknown`:n.map(e=>e.includes(` | `)?`(${e})`:e).join(` & `)}let r=e.type;if(Array.isArray(r))return p(r.map(n=>g({...e,type:n},t)));switch(r){case`string`:return`string`;case`number`:case`integer`:return`number`;case`boolean`:return`boolean`;case`null`:return`null`;case`array`:return _(e,t);case`object`:return y(e,t);case void 0:return`properties`in e||`additionalProperties`in e||`required`in e?y(e,t):`items`in e||`prefixItems`in e?_(e,t):`unknown`;default:return`unknown`}}function _(e,t){if(e.items!==void 0&&!Array.isArray(e.items))return`Array<${g(e.items,t)}>`;let n=Array.isArray(e.prefixItems)?e.prefixItems:Array.isArray(e.items)?e.items:[];return n.length>0?`[${n.map(e=>g(e,t)).join(`, `)}]`:`unknown[]`}function v(e){return f(e)&&typeof e.description==`string`?e.description.trim():``}function y(e,t){let n=f(e.properties)?e.properties:{},r=new Set(Array.isArray(e.required)?e.required:[]),i=Object.keys(n).sort(),a=i.map(e=>{let i=r.has(e)?``:`?`;return`${d(e)}${i}: ${g(n[e],t)};`}),o=e.additionalProperties;if(o!==void 0&&o!==!1){let e=o===!0?`unknown`:g(o,t);a.push(`[key: string]: ${e};`)}else o===void 0&&i.length===0&&a.push(`[key: string]: unknown;`);if(a.length===0)return`{}`;if(!i.some(e=>v(n[e])))return`{ ${a.join(` `)} }`;let s=[`{`];i.forEach((e,t)=>{for(let t of v(n[e]).split(/\r?\n/))t.trim()&&s.push(` // ${t.trim()}`);s.push(` ${a[t].replaceAll(`
3
+ `,`
4
+ `)}`)});for(let e of a.slice(i.length))s.push(` ${e}`);return s.push(`}`),s.join(`
5
+ `)}const b=3e3,x={type:`string`};function S(e){return[`Globals:`,"- `text(value)`, `image(dataUrlOrImageBlock)`, `console.log(...)`, and top-level `return` add output; `exit()` ends the script.","- `store(key, value)` and `load(key)` keep small JSON values across codemode calls.","- `ALL_TOOLS`, `searchTools(query, { limit?, namespace? })`, `describeTool(name)`, `describeNamespace(name)`: find tools you were not given directly, such as MCP tools, and read their declarations.",...e?['- `models`: non-chat models with your credentials. `await models.getAvailableOfType("image" | "classifier")` lists them; `models.generateImages(model, { input: [{ type: "text", text }, ...{ type: "image", data, mimeType }] })` resolves to `{ output, stopReason, errorMessage? }` (show its image blocks with `image(block)`; can take minutes, so do not set a short timeout_ms); `models.classify(model, { state: {...}, questions: { id: { type: "choice" | "score" | "bool", instructions, criteria } } })` resolves to `{ answers, stopReason, errorMessage? }`. They do not throw on provider errors: check `stopReason`.']:[]].join(`
6
+ `)}function ee(e){return{name:e.name,description:e.description,inputSchema:e.parameters,outputSchema:e.outputSchema??x}}function C(e){let t=a(e.name);return`${t===e.name?`### \`${t}\``:`### \`${t}\` (\`${e.name}\`)`}\n${c(e).trim()}`}function te(e,t){let n=e.map(e=>[...e.entries].sort((e,t)=>e.cost-t.cost)),r=new Set,i=t,a=n.filter(e=>e.length>0);for(;a.length>0;)a=a.filter(e=>{let t=e[0];return t.cost>i?!1:(i-=t.cost,r.add(t.name),e.shift(),e.length>0)});return r}function w({tools:e,declaredNames:t=new Set,isDeferred:n=()=>!1,getNamespace:r=()=>void 0,models:i=!1,inlineBudget:a=b}){let o=['Run JavaScript that calls other tools. The input is raw JavaScript (not JSON, no code fence), run as an async function body in a QuickJS sandbox: top-level `await` and `return` work. No Node, file system, network, or timers.\n- `await tools.<name>({ ...args })` calls a tool by its name (characters outside [A-Za-z0-9_] become `_`), including most tools you can call directly (`ALL_TOOLS` is the exact list). It resolves to the tool\'s text output; MCP tools (`mcp__*`) resolve to their `CallToolResult` (`{ content, structuredContent?, isError? }`). A failed or blocked call rejects with an Error. Calls still running when the script ends are cancelled.\n- Use it to batch independent calls (`Promise.allSettled`), chain calls, or filter large output; only what the script outputs reaches you.\n- Optional first line: `// @options: {"max_output_tokens": 10000, "timeout_ms": 60000}`',S(i)],s=new Map;for(let i of e){if(t.has(i.name))continue;let e=r(i);if(!e||!n(i))continue;let a=s.get(e.name)??{...e,count:0};a.count+=1,s.set(e.name,a)}if(s.size>0){let e=[...s.values()].sort((e,t)=>e.name.localeCompare(t.name)).map(e=>{let t=e.description?.trim().split(`
7
+ `)[0];return`- \`${e.name}\` (${e.count} tool${e.count===1?``:`s`})${t?`: ${t}`:``}`});o.push(`Tool namespaces (find their tools with \`searchTools(query, { namespace })\` or \`describeNamespace(name)\`):\n${e.join(`
8
+ `)}`)}let c=e.filter(e=>!t.has(e.name)&&!n(e)).map(ee);if(c.length===0)return o.join(`
9
+
10
+ `);let u=new Map([[``,{namespace:void 0,entries:[]}]]);for(let t of c){let n=r(e.find(e=>e.name===t.name)),i=n?`ns:${n.name}`:``,a=u.get(i)??{namespace:n,entries:[]};u.set(i,a);let o=C(t);a.entries.push({name:t.name,section:o,cost:Math.ceil(o.length/4)})}let d=[...u.values()].sort((e,t)=>e.namespace===void 0?-1:t.namespace===void 0?1:e.namespace.name.localeCompare(t.namespace.name)),f=te(d,a);c.some(e=>f.has(e.name)&&l(e.outputSchema)!==void 0)&&o.push(`Shared MCP Types:
11
+ \`\`\`ts
12
+ type Role = "user" | "assistant";
13
+ type MetaObject = Record<string, unknown>;
14
+ type Annotations = {
15
+ audience?: Role[];
16
+ priority?: number;
17
+ lastModified?: string;
18
+ };
19
+ type Icon = {
20
+ src: string;
21
+ mimeType?: string;
22
+ sizes?: string[];
23
+ theme?: "light" | "dark";
24
+ };
25
+ type TextResourceContents = {
26
+ uri: string;
27
+ mimeType?: string;
28
+ _meta?: MetaObject;
29
+ text: string;
30
+ };
31
+ type BlobResourceContents = {
32
+ uri: string;
33
+ mimeType?: string;
34
+ _meta?: MetaObject;
35
+ blob: string;
36
+ };
37
+ type TextContent = {
38
+ type: "text";
39
+ text: string;
40
+ annotations?: Annotations;
41
+ _meta?: MetaObject;
42
+ };
43
+ type ImageContent = {
44
+ type: "image";
45
+ data: string;
46
+ mimeType: string;
47
+ annotations?: Annotations;
48
+ _meta?: MetaObject;
49
+ };
50
+ type AudioContent = {
51
+ type: "audio";
52
+ data: string;
53
+ mimeType: string;
54
+ annotations?: Annotations;
55
+ _meta?: MetaObject;
56
+ };
57
+ type ResourceLink = {
58
+ icons?: Icon[];
59
+ name: string;
60
+ title?: string;
61
+ uri: string;
62
+ description?: string;
63
+ mimeType?: string;
64
+ annotations?: Annotations;
65
+ size?: number;
66
+ _meta?: MetaObject;
67
+ type: "resource_link";
68
+ };
69
+ type EmbeddedResource = {
70
+ type: "resource";
71
+ resource: TextResourceContents | BlobResourceContents;
72
+ annotations?: Annotations;
73
+ _meta?: MetaObject;
74
+ };
75
+ type ContentBlock =
76
+ | TextContent
77
+ | ImageContent
78
+ | AudioContent
79
+ | ResourceLink
80
+ | EmbeddedResource;
81
+ type CallToolResult<TStructured = { [key: string]: unknown }> = {
82
+ _meta?: MetaObject;
83
+ content: ContentBlock[];
84
+ isError?: boolean;
85
+ structuredContent?: TStructured;
86
+ [key: string]: unknown;
87
+ };
88
+ \`\`\``);let p=[`Script-only tools:`];for(let{namespace:e,entries:t}of d){let n=t.filter(e=>f.has(e.name));if(e){let r=n.length===t.length?``:n.length===0?` (tools not listed)`:` (some tools not listed)`;p.push(`## ${e.name}${r}`)}for(let e of n)p.push(e.section)}return o.push(p.join(`
89
+
90
+ `)),o.join(`
91
+
92
+ `)}const T=new Map;function ne(e){let r=e??t(import.meta.url).resolve(`quickjs-wasi/quickjs.wasm`),i=T.get(r);if(!i){let{WebAssembly:e}=globalThis;i=n(r).then(t=>e.compile(t)).catch(e=>{throw T.delete(r),e}),T.set(r,i)}return i}const re=/^[A-Za-z_$][A-Za-z0-9_$]*$/,E=new Set([`tools`,`ALL_TOOLS`,`console`,`text`,`image`,`exit`,`globalThis`,`store`,`load`]);function D(e){return e instanceof Error?e.message:String(e)}function O(e){let t=Object.create(null);for(let[n,r]of Object.entries(e??{})){let e=JSON.stringify(r);e!==void 0&&(t[n]=e)}return t}function ie(e){let t={set:Object.create(null),delete:[]};for(let[n,r]of JSON.parse(e))r===void 0?t.delete.push(n):t.set[n]=JSON.parse(r);return t}function k(){return new URL(import.meta.url.endsWith(`.ts`)?`./worker.ts`:`./worker.js`,import.meta.url)}var A=class{promise;resolveResult;worker;interrupt=new SharedArrayBuffer(4);tools;globals;signal;timer;output=[];calls=[];pending=new Map;finished=!1;constructor(e){this.promise=new Promise(e=>{this.resolveResult=e}),this.tools=e.tools,this.globals=e.globals,this.signal=e.signal,Number.isFinite(e.timeoutMs)&&(this.timer=setTimeout(()=>{this.finish({kind:`timeout`,message:`Execution timed out after ${e.timeoutMs} ms`})},e.timeoutMs)),e.signal&&(e.signal.aborted?this.onAbort():e.signal.addEventListener(`abort`,this.onAbort,{once:!0})),e.wasm.then(t=>this.start(e,t),e=>{this.finish({kind:`sandbox`,message:`Failed to load QuickJS: ${D(e)}`})})}abort(e){return this.finish({kind:`aborted`,message:e}),this.promise}start(e,t){if(this.finished)return;let n={code:e.code,tools:[...e.tools.values()].map(e=>({name:e.name,jsName:a(e.name),description:e.description??``})),globals:[...e.globals.values()].map(e=>({name:e.name,spread:e.spread===!0})),wasm:t,memoryLimitBytes:e.memoryLimitBytes,store:e.store,interrupt:this.interrupt},r;try{r=new i(e.workerUrl,{workerData:n})}catch(e){this.finish({kind:`sandbox`,message:`Failed to start worker: ${D(e)}`});return}this.worker=r,r.on(`message`,e=>this.handleMessage(e)),r.on(`error`,e=>{this.finish({kind:`sandbox`,name:e instanceof Error?e.name:void 0,message:D(e)})}),r.on(`exit`,e=>{this.finish({kind:`sandbox`,message:`Worker exited with code ${e} before the script settled`})})}onAbort=()=>{let e=this.signal?.reason;this.finish({kind:`aborted`,message:e instanceof Error?e.message:`Execution aborted`})};post(e){this.worker?.postMessage(e)}handleMessage(t){if(!(this.finished||!e(t)))switch(t.type){case`output`:this.output.push(t.item);break;case`call`:this.handleCall(t);break;case`done`:this.handleDone(t);break;case`crash`:this.finish({kind:`sandbox`,message:t.message});break}}handleDone(e){if(!e.ok){let t=JSON.parse(e.error);this.finish({kind:`script`,...t});return}this.finish(void 0,e.value===void 0?void 0:JSON.parse(e.value),e.writes)}async handleCall(e){let{id:t,name:n}=e,r=e.target===`tool`,i=r?{name:n,status:`cancelled`,durationMs:0}:void 0;i&&this.calls.push(i);let a={record:i,startedAt:performance.now(),controller:new AbortController};this.pending.set(t,a);let o,s;try{let i=(r?this.tools:this.globals).get(n);if(!i)throw Error(`Unknown ${r?`tool`:`global`} "${n}"`);let c=e.args===void 0?void 0:JSON.parse(e.args),l=await i.execute(c,{signal:a.controller.signal});s={type:`result`,id:t,ok:!0,payload:l===void 0?void 0:JSON.stringify(l)},o=`ok`}catch(e){s={type:`result`,id:t,ok:!1,payload:D(e)},o=`error`}this.pending.delete(t)&&(i&&(i.status=o,i.durationMs=performance.now()-a.startedAt),this.post(s))}finish(e,t,n){if(this.finished)return;this.finished=!0,clearTimeout(this.timer),this.signal?.removeEventListener(`abort`,this.onAbort);let r=performance.now();for(let e of this.pending.values())e.record&&(e.record.durationMs=r-e.startedAt),e.controller.abort();this.pending.clear();let i=e?{ok:!1,error:e,output:this.output,calls:this.calls}:{ok:!0,value:t,output:this.output,calls:this.calls,storeWrites:n===void 0?{set:{},delete:[]}:ie(n)};if(!this.worker){this.resolveResult(i);return}Atomics.store(new Int32Array(this.interrupt),0,1),this.worker.terminate().catch(()=>void 0).then(()=>this.resolveResult(i))}},ae=class{toolsByName=new Map;globalsByName=new Map;timeoutMs;memoryLimitBytes;wasm;workerUrl;running=new Set;closed=!1;constructor(e={}){this.timeoutMs=e.timeoutMs??3e5,this.memoryLimitBytes=e.memoryLimitBytes,this.wasm=e.wasm,this.workerUrl=e.workerUrl??k();for(let t of e.tools??[])this.registerTool(t);let t=new Set;for(let n of e.globals??[]){let e=n.name.split(`.`);if(e.length>2||!e.every(e=>re.test(e))||E.has(e[0]))throw Error(`Invalid global name "${n.name}"`);if(this.globalsByName.has(n.name))throw Error(`Global "${n.name}" is already registered`);e.length===2&&t.add(e[0]),this.globalsByName.set(n.name,n)}for(let e of t)if(this.globalsByName.has(e))throw Error(`Global "${e}" conflicts with the namespace "${e}"`)}registerTool(e){if(this.toolsByName.has(e.name))throw Error(`Tool "${e.name}" is already registered`);this.toolsByName.set(e.name,e)}unregisterTool(e){return this.toolsByName.delete(e)}get tools(){return[...this.toolsByName.values()]}get globals(){return[...this.globalsByName.values()]}execute(e,t={}){if(this.closed)return Promise.reject(Error(`Sandbox is closed`));let n=new A({code:e,tools:new Map(this.toolsByName),globals:this.globalsByName,timeoutMs:t.timeoutMs??this.timeoutMs,signal:t.signal,memoryLimitBytes:this.memoryLimitBytes,store:O(t.store),wasm:this.wasm===void 0?ne():Promise.resolve(this.wasm),workerUrl:this.workerUrl});return this.running.add(n),n.promise.finally(()=>this.running.delete(n))}async close(){this.closed=!0,await Promise.all([...this.running].map(e=>e.abort(`Sandbox closed`)))}};const j=[`max_output_tokens`,`timeout_ms`],M="`max_output_tokens` and `timeout_ms`",oe=2147483647;String.raw`
93
+ start: options_source | plain_source
94
+ options_source: OPTIONS_LINE NEWLINE SOURCE
95
+ plain_source: SOURCE
96
+
97
+ OPTIONS_LINE: /[ \t]*\/\/ @options:[^\r\n]*/
98
+ NEWLINE: /\r?\n/
99
+ SOURCE: /[\s\S]+/
100
+ `;var N=class extends Error{constructor(e){super(e),this.name=`CodemodeSourceError`}};function se(e){return typeof e==`number`&&Number.isSafeInteger(e)&&e>=0}function ce(e){if(e===``)throw new N(`@options must be a JSON object with supported fields ${M}`);let t;try{t=JSON.parse(e)}catch(e){throw new N(`@options must be valid JSON with supported fields ${M}: ${e instanceof Error?e.message:String(e)}`)}if(typeof t!=`object`||!t||Array.isArray(t))throw new N(`@options must be a JSON object with supported fields ${M}`);let n=t;for(let e of Object.keys(n))if(!j.includes(e))throw new N(`@options only supports ${M}; got \`${e}\``);let r={},{max_output_tokens:i,timeout_ms:a}=n;if(i!==void 0){if(!se(i))throw new N("@options field `max_output_tokens` must be a non-negative safe integer");r.maxOutputTokens=i}if(a!==void 0){if(!se(a)||a===0||a>oe)throw new N(`@options field \`timeout_ms\` must be a positive integer up to ${oe}`);r.timeoutMs=a}return r}function le(e){if(e.trim()===``)throw new N('Expected JavaScript source text (non-empty). Provide JS only, optionally with a first line `// @options: {"max_output_tokens": 1000}`.');let t=e.indexOf(`
101
+ `),n=(t===-1?e:e.slice(0,t)).replace(/\r$/,``).trimStart();if(!n.startsWith(`// @options:`))return{code:e,options:{}};let r=t===-1?``:e.slice(t);if(r.trim()===``)throw new N(`The @options line must be followed by JavaScript source on subsequent lines`);return{code:r,options:ce(n.slice(12).trim())}}function P(e){return String(e||``).toLowerCase().match(/[\p{L}\p{N}]+/gu)||[]}function ue(e){try{return JSON.stringify(e||{}).toLowerCase()}catch{return``}}function de(e,t,n){let r=e.name.toLowerCase(),i=String(e.label||``).toLowerCase(),a=n?.(e),o=String(e._mcpServer||a?.name||``).toLowerCase(),s=String([e.description,a?.description].filter(Boolean).join(` `)).toLowerCase(),c=P(`${r} ${i}`);return{tool:e,index:t,name:r,label:i,server:o,namespace:a?.name,description:s,schema:ue(e.parameters),namePhrase:c.join(` `),nameTerms:new Set(c),serverTerms:new Set(P(o))}}function fe(e,t,n,r){let i=0;(e.name===t||e.label===t)&&(i+=100),n&&e.namePhrase.includes(n)&&(i+=24);for(let t of r)e.nameTerms.has(t)?i+=14:(e.name.includes(t)||e.label.includes(t))&&(i+=9),e.serverTerms.has(t)?i+=7:e.server.includes(t)&&(i+=4),e.description.includes(t)&&(i+=3),e.schema.includes(t)&&(i+=1);return i}function pe(e,{getNamespace:t}={}){return e.map((e,n)=>de(e,n,t))}function me(e,{max:t=10}={}){return Number.isInteger(e)?Math.max(1,Math.min(e,t)):5}function he(e,t,n,{namespace:r,maxLimit:i}={}){let a=String(t||``).trim().toLowerCase(),o=P(a);if(o.length===0)return[];let s=o.join(` `);return e.filter(e=>!r||e.namespace===r).map(e=>({entry:e,score:fe(e,a,s,o)})).filter(({score:e})=>e>0).sort((e,t)=>t.score-e.score||e.entry.index-t.entry.index).slice(0,me(n,{max:i})).map(({entry:e})=>e.tool)}const ge=new Set([`chat`,`image`,`classifier`]),F=e=>typeof e==`object`&&!!e&&!Array.isArray(e),_e=(e,t)=>e.length>t?`${e.slice(0,t-3)}...`:e,I=e=>`${/^[aeiou]/.test(e)?`an`:`a`} ${e}`;function ve(e){return{read:async t=>{let n=await e(t);return n?{type:`api_key`,key:n}:void 0},list:async()=>[],modify:async()=>void 0,delete:async()=>{}}}function ye({getApiKey:e}){let t;return async()=>{if(!t){let{builtinModels:n}=await import(`@earendil-works/pi-ai/providers/all`);t=n({credentials:ve(e)})}return t}}function be({classifiers:e,images:t}={}){let n=async(n,r)=>{let i=n===`classifier`?e?(await e()).getModelsOfType(`classifier`):[]:n===`image`?t?.list()??[]:[];return r?i.filter(e=>e.provider===r):i};return{getModelsOfType:n,getAvailableOfType:async(t,r,i)=>t===`classifier`&&e?(await e()).getAvailableOfType(`classifier`,r,i):n(t,r),getModelOfType:async(e,t,r)=>(await n(e,t)).find(e=>e.id===r),classify:async(t,n,r)=>(await e()).classify(t,n,r),generateImages:(e,n,r)=>t.generateImages(e,n,r)}}function L(e){if(typeof e==`string`&&ge.has(e))return e;throw Error(`Unknown model type ${JSON.stringify(e)}. Use "chat", "image", or "classifier".`)}function xe(e){if(e!=null){if(typeof e!=`string`)throw Error(`provider must be a string`);return e}}function R(e){let{headers:t,...n}=e;return n}function z(e){if(e==null)return String(e);if(Array.isArray(e))return e.length===0?`an empty array`:`an array`;if(typeof e==`object`){let t=Object.keys(e);return t.length===0?`{}`:`{ ${t.slice(0,6).join(`, `)}${t.length>6?`, ...`:``} }`}return typeof e==`string`?`a string`:`a ${typeof e}`}function Se(e){let t=e=>Error(`models.classify() ${e}. Expected context: { state: { ... }, questions: { <id>: { type: "choice", instructions, criteria: { <label>: <meaning> } } | { type: "score", instructions, criteria: [<lowest level>, ..., <highest level>] } | { type: "bool", instructions, criteria: { true: <meaning>, false: <meaning> } } } }.`);if(!F(e))throw t(`expects a context object as its second argument, got ${z(e)}`);if(!F(e.state))throw t(`context.state must be an object, got ${z(e.state)}`);let{questions:n}=e;if(!F(n)||Object.keys(n).length===0)throw t(`context.questions must map question IDs to questions, got ${z(n)}`);let r=e=>e.length>0&&e.every(e=>typeof e==`string`);for(let[e,i]of Object.entries(n)){let n=`context.questions.${e}`;if(!F(i))throw t(`${n} must be a question object, got ${z(i)}`);if(typeof i.instructions!=`string`)throw t(`${n}.instructions must be a string`);let{criteria:a}=i;if(i.type===`choice`){if(!F(a)||!r(Object.values(a)))throw t(`${n} is a "choice" question, so criteria must map each label to its meaning`)}else if(i.type===`score`){if(!Array.isArray(a)||!r(a))throw t(`${n} is a "score" question, so criteria must list the levels as strings, lowest first`)}else if(i.type===`bool`){if(!F(a)||typeof a.true!=`string`||typeof a.false!=`string`)throw t(`${n} is a "bool" question, so criteria must be { true: string, false: string }`)}else throw t(`${n}.type must be "choice", "score", or "bool", got ${JSON.stringify(i.type)}`)}return e}function Ce(e){let t=e=>Error(`models.generateImages() ${e}. Expected context: { input: [{ type: "text", text: <prompt> }, ...optional { type: "image", data: <base64>, mimeType } references] }.`);if(!F(e))throw t(`expects a context object as its second argument, got ${z(e)}`);let{input:n}=e;if(!Array.isArray(n)||n.length===0)throw t(`context.input must be a non-empty array of blocks, got ${z(n)}`);return n.forEach((e,n)=>{if(!(F(e)&&e.type===`text`&&typeof e.text==`string`)&&!(F(e)&&e.type===`image`&&typeof e.data==`string`&&typeof e.mimeType==`string`))throw t(`context.input[${n}] must be a text or image block, got ${z(e)}`)}),e}function we(e){let t=0,n=[];return async r=>{t>=e?await new Promise(e=>n.push(e)):t+=1;try{return await r()}finally{let e=n.shift();e?e():--t}}}function Te(e,{toolCallId:t,track:n,publish:r,addUsage:i,addGeneratedImages:a}){let o=we(4),s=0,c=async(a,c,[l,u],d,f)=>{let p=`List the ${c} models you can use with models.getAvailableOfType("${c}").`;if(!F(l)||typeof l.provider!=`string`||typeof l.id!=`string`){let e=l==null?` models.getModelOfType() returns undefined for an unknown provider or id.`:``;throw Error(`${a}() expects ${I(c)} model as its first argument, got ${z(l)}.${e} ${p}`)}let{provider:m,id:h}=l,g=await e.getModelOfType(c,m,h);if(!g){let t;for(let n of ge)n!==c&&await e.getModelOfType(n,m,h)&&(t=n);throw Error(t?`"${m}/${h}" is ${I(t)} model, not ${I(c)} model. ${p}`:`Unknown ${c} model "${m}/${h}". ${p}`)}let _=d(u),v=n({id:`${t}/${a}/${++s}`,name:a,args:`${g.provider}/${g.id}`,status:`running`});r();let y=performance.now(),b=await o(()=>f(g,_));return v.durationMs=performance.now()-y,v.status=b.stopReason===`stop`?`ok`:b.stopReason===`aborted`?`cancelled`:`error`,b.errorMessage&&(v.error=_e(b.errorMessage,500)),b.usage&&(v.cost=b.usage.cost?.total,i(b.usage)),r(),b};return Object.entries({"models.getModelsOfType":async([t,n])=>(await e.getModelsOfType(L(t),xe(n))).map(R),"models.getAvailableOfType":async([t,n],{signal:r})=>(await e.getAvailableOfType(L(t),xe(n),{signal:r})).map(R),"models.getModelOfType":async t=>{let[n,r,i]=t;if(typeof r!=`string`||typeof i!=`string`)throw Error(`models.getModelOfType(type, provider, id) expects three strings, got (${t.map(z).join(`, `)}). The provider and the id are separate arguments.`);let a=await e.getModelOfType(L(n),r,i);return a===void 0?void 0:R(a)},"models.classify":(t,{signal:n})=>c(`models.classify`,`classifier`,t,Se,(t,r)=>e.classify(t,r,{signal:n})),"models.generateImages":(t,{signal:n})=>c(`models.generateImages`,`image`,t,Ce,async(t,r)=>{let i=await e.generateImages(t,r,{signal:n});return a(i.output.filter(e=>e.type===`image`).length),i})}).map(([e,t])=>({name:e,spread:!0,execute:t}))}const Ee=256*1024*1024,De=5*6e4,Oe=30*6e4,B=(e,t)=>e.length>t?`${e.slice(0,t-3)}...`:e;function ke(e){if(e===void 0)return``;try{return B(JSON.stringify(e)??``,200)}catch{return``}}const V=e=>(e?.content??[]).filter(e=>e.type===`text`).map(e=>e.text).join(`
102
+ `),Ae=e=>typeof e==`string`?e:JSON.stringify(e)??String(e);function je(e,t){if(!e)return structuredClone(t);for(let n of[`input`,`output`,`cacheRead`,`cacheWrite`,`totalTokens`])e[n]=(e[n]??0)+(t[n]??0);if(t.cost){e.cost??={};for(let[n,r]of Object.entries(t.cost))e.cost[n]=(e.cost[n]??0)+(r??0)}return e}function Me(e){return e.length===0?`No tool calls were made.`:`Tool calls made before the failure (they are not undone): ${e.map(e=>`${e.name} (${e.status})`).join(`, `)}`}function Ne(e,t){return`${e.kind===`script`?e.stack??`${e.name??`Error`}: ${e.message}`:e.kind===`timeout`?`Script timed out: ${e.message}`:e.kind===`aborted`?`Script aborted: ${e.message}`:`Script sandbox failed: ${e.message}`}\n\n${Me(t)}`}function H(e,t){let n=Math.floor(t/2),r=t-n,i=e.length-n-r;return`${e.slice(0,n)}…${Math.ceil(i/4)} tokens truncated…${r>0?e.slice(-r):``}`}function Pe(e,t){let n=Array.from({length:e.length}),r=e.map((e,t)=>({length:e,index:t})).sort((e,t)=>e.length-t.length),i=t;return r.forEach(({length:e,index:t},a)=>{n[t]=Math.min(e,Math.floor(i/(r.length-a))),i-=n[t]}),n}async function Fe(e,t,n){let r=e.filter(e=>e.type===`text`).map(e=>e.text),i=r.join(`
103
+ `),a=t*4;if(r.length===0||i.length<=a)return{items:e};let o=Pe(r.map(e=>e.length),a),s=r.every((e,t)=>e.length<=o[t]||o[t]>=400)?r.map((e,t)=>e.length>o[t]?H(e,o[t]):e).join(`
104
+ `):H(i,a),c=`Warning: truncated output (original token count: ${Math.ceil(i.length/4)})\nTotal output lines: ${i.split(`
105
+ `).length}\n\n${s}`,l;if(n)try{l=await n(i),l&&(c+=`\n\n[Full output: ${l} (read with offset/limit)]`)}catch(e){c+=`\n\n[Could not save the full output: ${e?.message||e}]`}return{items:[{type:`text`,text:c},...e.filter(e=>e.type===`image`)],...l?{fullOutputPath:l}:{}}}function Ie(e,t){let{result:n}=t;if(e.outputSchema&&n.structuredContent!==void 0)return n.structuredContent;let r=V(n);if(t.isError)throw Error(r||`Tool "${e.name}" failed`);return r}function Le(e,t){let n=a(e),r=a(t),i=e=>e.includes(`__`)?e.slice(e.lastIndexOf(`__`)+2):void 0;return e===t||n===r||i(e)===t||i(n)===r}function Re(e,t,n){let r=pe(e,{getNamespace:n}),i=e=>({name:a(e.name),description:t.get(e.name)??``}),o=t=>e.map(n).find(e=>e&&Le(e.name,t));return[{name:`searchTools`,spread:!0,execute:([e,t])=>{if(typeof e!=`string`)throw Error(`searchTools() expects a query string`);let n=t?.limit??8;if(typeof n!=`number`||!Number.isInteger(n)||n<=0)throw Error(`searchTools() limit must be a positive integer`);let a=t?.namespace;if(a!=null&&typeof a!=`string`)throw Error(`searchTools() namespace must be a string`);let s=a?o(a):void 0;return a&&!s?[]:he(r,e,n,{namespace:s?.name,maxLimit:Math.max(n,8)}).map(i)}},{name:`describeTool`,spread:!0,execute:([n])=>{if(typeof n!=`string`)throw Error(`describeTool() expects a tool name`);let r=e.find(e=>e.name===n||a(e.name)===n);return r?t.get(r.name):void 0}},{name:`describeNamespace`,spread:!0,execute:([t])=>{if(typeof t!=`string`)throw Error(`describeNamespace() expects a namespace name`);let r=o(t);if(r)return{name:r.name,...r.description?{description:r.description}:{},...r.instructions?{instructions:r.instructions}:{},tools:e.filter(e=>n(e)?.name===r.name).map(e=>a(e.name))}}}]}async function U({toolCallId:e,code:t,signal:n,onUpdate:r,tools:i,executeTool:a,store:o={},models:s,getNamespace:l=()=>void 0,sandbox:u={},spillOutput:d}){let f=performance.now(),p;try{p=le(t)}catch(e){if(!(e instanceof N))throw e;return{content:[{type:`text`,text:`Script failed\nScript error:\n${e.message}`}],details:{calls:[]},isError:!0}}let{code:m,options:h}=p,g=[],_=0,v,y=0,b=()=>g.slice(-200).map(e=>({...e})),x=()=>r?.({content:[],details:{calls:b()}}),S=e=>(g.push(e),e),C=new Map(i.map(e=>[e.name,c(ee(e))])),te=i.map(t=>({name:t.name,description:C.get(t.name),execute:async(n,{signal:r})=>{let i=S({id:`${e}/${++_}`,name:t.name,args:ke(n),status:`running`});x();let o=performance.now(),s=await a(t,n,{id:i.id,parentToolCallId:e,signal:r});return i.durationMs=performance.now()-o,s.isError?(i.status=r.aborted?`cancelled`:`error`,i.error=B(V(s.result)||`Tool "${t.name}" failed`,500)):i.status=`ok`,x(),Ie(t,s)}})),w=Math.min(h.timeoutMs??u.defaultTimeoutMs??1/0,u.maxTimeoutMs??1/0),T=new ae({tools:te,globals:[...Re(i,C,l),...s?Te(s,{toolCallId:e,track:S,publish:x,addUsage:e=>{v=je(v,e)},addGeneratedImages:e=>{y+=e}}):[]],timeoutMs:w,memoryLimitBytes:u.memoryLimitBytes??268435456,...u.wasmPath?{wasm:ne(u.wasmPath)}:{},...u.workerUrl?{workerUrl:u.workerUrl}:{}}),re=u.limit??(e=>e()),E;try{E=await re(()=>T.execute(m,{signal:n,store:o}))}finally{await T.close()}for(let e of g)e.status===`running`&&(e.status=`cancelled`);let D=E.output.map(e=>e.type===`text`?{type:`text`,text:e.text}:e);E.ok?E.value!==void 0&&D.push({type:`text`,text:Ae(E.value)}):D.push({type:`text`,text:`Script error:\n${Ne(E.error,g)}`}),y>0&&!D.some(e=>e.type===`image`)&&D.push({type:`text`,text:`Note: models.generateImages() returned ${y} image${y===1?``:`s`} that the script did not show. Show each image block of result.output with image(block).`});let O=await Fe(D,h.maxOutputTokens??1e4,d),ie=((performance.now()-f)/1e3).toFixed(1),k=`${E.ok?`Script completed`:`Script failed`}\nWall time ${ie} seconds\nOutput:\n`,A=E.ok?E.storeWrites:null,j=A&&(Object.keys(A.set).length>0||A.delete.length>0);return{content:[{type:`text`,text:k},...O.items],details:{calls:b(),...g.length>200?{omittedCalls:g.length-200}:{},...j?{storeWrites:A}:{},...O.fullOutputPath?{fullOutputPath:O.fullOutputPath}:{}},...v?{usage:v}:{},...E.ok?{}:{isError:!0}}}const W=`codemode`,G=`codemodeStore`,K=e=>typeof e==`object`&&!!e&&!Array.isArray(e);function ze(e,t){if(!(!K(t)||!K(t.set)||!Array.isArray(t.delete))){for(let n of t.delete)delete e[n];Object.assign(e,t.set)}}function Be(e){let t=Object.create(null);for(let n of Array.isArray(e)?e:[]){if(K(n?.codemodeStore)){for(let e of Object.keys(t))delete t[e];Object.assign(t,n[G])}n?.role===`toolResult`&&n.toolName===`codemode`&&ze(t,n.details?.storeWrites)}return t}function Ve(e,t){let n=Be(t);Object.keys(n).length>0&&(e[G]=n)}var He=class extends Error{constructor(e,t){super(e),this.name=`ParseError`,this.type=t.type,this.field=t.field,this.value=t.value,this.line=t.line}};function q(e){}function Ue(e){if(typeof e==`function`)throw TypeError("`callbacks` must be an object, got a function instead. Did you mean `{onEvent: fn}`?");let{onEvent:t=q,onError:n=q,onRetry:r=q,onComment:i}=e,a=``,o=!0,s,c=``,l=``;function u(e){let t=o?e.replace(/^\xEF\xBB\xBF/,``):e,[n,r]=We(`${a}${t}`);for(let e of n)d(e);a=r,o=!1}function d(e){if(e===``){p();return}if(e.startsWith(`:`)){i&&i(e.slice(e.startsWith(`: `)?2:1));return}let t=e.indexOf(`:`);if(t!==-1){let n=e.slice(0,t),r=e[t+1]===` `?2:1;f(n,e.slice(t+r),e);return}f(e,``,e)}function f(e,t,i){switch(e){case`event`:l=t;break;case`data`:c=`${c}${t}
106
+ `;break;case`id`:s=t.includes(`\0`)?void 0:t;break;case`retry`:/^\d+$/.test(t)?r(parseInt(t,10)):n(new He(`Invalid \`retry\` value: "${t}"`,{type:`invalid-retry`,value:t,line:i}));break;default:n(new He(`Unknown field "${e.length>20?`${e.slice(0,20)}\u2026`:e}"`,{type:`unknown-field`,field:e,value:t,line:i}));break}}function p(){c.length>0&&t({id:s,event:l||void 0,data:c.endsWith(`
107
+ `)?c.slice(0,-1):c}),s=void 0,c=``,l=``}function m(e={}){a&&e.consume&&d(a),o=!0,s=void 0,c=``,l=``,a=``}return{feed:u,reset:m}}function We(e){let t=[],n=``,r=0;for(;r<e.length;){let i=e.indexOf(`\r`,r),a=e.indexOf(`
108
+ `,r),o=-1;if(i!==-1&&a!==-1?o=Math.min(i,a):i===-1?a!==-1&&(o=a):o=i===e.length-1?-1:i,o===-1){n=e.slice(r);break}else{let n=e.slice(r,o);t.push(n),r=o+1,e[r-1]===`\r`&&e[r]===`
109
+ `&&r++}}return[t,n]}const J=`ctor-images`,Y=2e3;async function X(e,t,n){let r=await e(t,n),i=await r.text();if(!r.ok)throw Error(`HTTP ${r.status}: ${i.slice(0,Y)}`);try{return JSON.parse(i)}catch{throw Error(`Provider returned invalid JSON: ${i.slice(0,Y)}`)}}function Z(e){return{prompt:e.filter(e=>e.type===`text`).map(e=>e.text).join(`
110
+ `),images:e.filter(e=>e.type===`image`)}}const Q=(e,t,n)=>{let r=e[t];if(!r)throw Error(n);return r};function Ge(e){let{prompt:t,images:n}=Z(e);return{contents:[{role:`user`,parts:[{text:t},...n.map(e=>({inlineData:{mimeType:e.mimeType,data:e.data}}))]}],generationConfig:{responseModalities:[`TEXT`,`IMAGE`]}}}function Ke(e){let t=e?.candidates?.[0]?.content?.parts||e?.parts||[],n=[];for(let e of t){e?.text&&n.push({type:`text`,text:e.text});let t=e?.inlineData||e?.inline_data;t?.data&&n.push({type:`image`,data:t.data,mimeType:t.mimeType||t.mime_type||`image/png`})}return n}function qe(e,t,n){let r=encodeURIComponent(t);if(e===`bricks`){let e=Q(n,`BRICKS_AI_GATEWAY_URL`,`BRICKS image generation needs a signed-in BRICKS account.`),t;try{t=JSON.parse(n.BRICKS_AI_GATEWAY_HEADERS||`{}`)}catch{throw Error(`BRICKS_AI_GATEWAY_HEADERS must be a JSON object of request headers.`)}return{url:`${e.replace(/\/+$/,``)}/v1/gemini/models/${r}:generateContent`,headers:t}}if(e===`google`){let e=n.GEMINI_API_KEY||n.GOOGLE_API_KEY;if(!e)throw Error(`Google image generation needs a Gemini API key.`);return{url:`https://generativelanguage.googleapis.com/v1beta/models/${r}:generateContent?key=${encodeURIComponent(e)}`,headers:{}}}let i=Q(n,`GOOGLE_CLOUD_PROJECT`,`Vertex AI image generation needs a Google Cloud project.`),a=n.GOOGLE_CLOUD_LOCATION||`global`,o=a===`global`?`aiplatform.googleapis.com`:`${a}-aiplatform.googleapis.com`,s=n.GOOGLE_VERTEX_API_KEY?{"x-goog-api-key":n.GOOGLE_VERTEX_API_KEY}:n.GOOGLE_VERTEX_ACCESS_TOKEN?{Authorization:`Bearer ${n.GOOGLE_VERTEX_ACCESS_TOKEN}`}:null;if(!s)throw Error(`Vertex AI image generation needs an API key or an access token.`);return{url:`https://${o}/v1/projects/${encodeURIComponent(i)}/locations/${encodeURIComponent(a)}/publishers/google/models/${r}:generateContent`,headers:s}}async function $(e,t,n,r,{fetch:i,signal:a}){let{url:o,headers:s}=qe(e,t,r);return Ke(await X(i,o,{method:`POST`,headers:{...s,"Content-Type":`application/json`},body:JSON.stringify(Ge(n)),signal:a}))}async function Je(e,t,n){let r=[];for(let i of Array.isArray(t?.data)?t.data:[])if(i?.b64_json)r.push({type:`image`,data:i.b64_json,mimeType:`image/png`});else if(i?.url){let t=await e(i.url,{signal:n});if(!t.ok)throw Error(`Failed to download image URL: HTTP ${t.status}`);let a=t.headers.get(`content-type`)||`image/png`;r.push({type:`image`,data:Buffer.from(await t.arrayBuffer()).toString(`base64`),mimeType:a})}return r}async function Ye(e,t,n,{fetch:r,signal:i}){let a=Q(n,`OPENAI_API_KEY`,`OpenAI image generation needs an API key.`),o=(n.OPENAI_BASE_URL||`https://api.openai.com/v1`).replace(/\/$/,``),{prompt:s,images:c}=Z(t),l={model:e,prompt:s,n:1,size:`auto`,quality:`medium`,output_format:`png`};if(c.length===0)return Je(r,await X(r,`${o}/images/generations`,{method:`POST`,headers:{Authorization:`Bearer ${a}`,"Content-Type":`application/json`},body:JSON.stringify(l),signal:i}),i);let u=new FormData;for(let[e,t]of Object.entries(l))u.append(e,String(t));return c.forEach((e,t)=>{let n=e.mimeType.split(`/`)[1]||`png`;u.append(c.length>1?`image[]`:`image`,new Blob([Buffer.from(e.data,`base64`)],{type:e.mimeType}),`reference-${t+1}.${n}`)}),Je(r,await X(r,`${o}/images/edits`,{method:`POST`,headers:{Authorization:`Bearer ${a}`},body:u,signal:i}),i)}function Xe(e){let t=(e.OPENAI_CODEX_BASE_URL||`https://chatgpt.com/backend-api`).replace(/\/+$/,``);return t.endsWith(`/codex/responses`)?t:t.endsWith(`/codex`)?`${t}/responses`:`${t}/codex/responses`}function Ze(e){try{let t=JSON.parse(Buffer.from(e.split(`.`)[1]||``,`base64url`).toString(`utf8`))?.[`https://api.openai.com/auth`]?.chatgpt_account_id;if(typeof t==`string`&&t)return t}catch{}throw Error(`The OpenAI Codex token does not include a ChatGPT account ID.`)}async function*Qe(e){if(!e.body)return;let t=e.body.getReader(),n=new TextDecoder,r=[],i=Ue({onEvent:({data:e})=>r.push(e)}),a=!1;try{for(;;){let{done:e,value:o}=await t.read(),s=n.decode(o,{stream:!e});i.feed(s),s&&(a=s.endsWith(`\r`)),e&&a&&i.feed(`
111
+ `);for(let e of r)if(e&&e!==`[DONE]`)try{yield JSON.parse(e)}catch{}if(r.length=0,e)break}}finally{await t.cancel().catch(()=>{}),t.releaseLock()}}async function $e(e,t,n,{fetch:r,signal:i}){let a=Q(n,`OPENAI_CODEX_ACCESS_TOKEN`,`OpenAI Codex image generation needs a ChatGPT sign-in.`),{prompt:o,images:s}=Z(t),c=await r(Xe(n),{method:`POST`,headers:{Authorization:`Bearer ${a}`,"chatgpt-account-id":Ze(a),originator:`pi`,"OpenAI-Beta":`responses=experimental`,Accept:`text/event-stream`,"Content-Type":`application/json`},body:JSON.stringify({model:e,store:!1,stream:!0,instructions:[`You are an image generation agent.`,`Use the image_generation tool for the user request.`,`Do not finish with text only when an image can be generated.`,s.length>0?`Use the attached image(s) as visual input and create one edited image.`:`Create one image.`].join(` `),input:[{role:`user`,content:[{type:`input_text`,text:o},...s.map(e=>({type:`input_image`,detail:`auto`,image_url:`data:${e.mimeType};base64,${e.data}`}))]}],tools:[{type:`image_generation`,output_format:`png`}],tool_choice:`auto`,parallel_tool_calls:!1,text:{verbosity:`low`}}),signal:i});if(!c.ok)throw Error(`HTTP ${c.status}: ${(await c.text()).slice(0,Y)}`);let l=[];for await(let e of Qe(c)){if(e?.type===`error`)throw Error(e.message||e.code||`Codex error`);if(e?.type===`response.failed`)throw Error(e.response?.error?.message||`OpenAI Codex response failed.`);let t=e?.item;if(t?.type===`message`)for(let e of Array.isArray(t.content)?t.content:[]){let t=e?.text||e?.content?.[0]?.text;typeof t==`string`&&t.trim()&&l.push({type:`text`,text:t})}t?.type===`image_generation_call`&&t.result&&l.push({type:`image`,data:t.result,mimeType:`image/png`})}return l}const et={bricks:(e,t,n,r)=>$(`bricks`,e,t,n,r),google:(e,t,n,r)=>$(`google`,e,t,n,r),"google-vertex":(e,t,n,r)=>$(`google-vertex`,e,t,n,r),openai:Ye,"openai-codex":$e};function tt({listProviders:e,resolveEnv:t,fetch:n=globalThis.fetch}){return{list:()=>e().filter(({provider:e})=>et[e]).map(({provider:e,defaultModel:t})=>({type:`image`,provider:e,id:t,name:t,api:J,input:[`text`,`image`],output:[`text`,`image`]})),generateImages:async(e,r,{signal:i}={})=>{let a={api:J,provider:e.provider,model:e.id};try{let o=await t(e.provider),s=await et[e.provider](e.id,r.input,o,{fetch:n,signal:i}),c=s.some(e=>e.type===`image`);return{...a,output:s,stopReason:c?`stop`:`error`,...c?{}:{errorMessage:`The model returned no image.`},timestamp:Date.now()}}catch(e){return{...a,output:[],stopReason:i?.aborted?`aborted`:`error`,errorMessage:e instanceof Error?e.message:String(e),timestamp:Date.now()}}}}}const nt={type:`object`,properties:{code:{type:`string`,description:`Raw JavaScript source.`}},required:[`code`],additionalProperties:!1};function rt(e){if(!e?._mcpNamespace)return;let t=e._mcpServerInstructions?.trim();return{name:e._mcpNamespace,description:e._mcpServerDescription??t?.split(`
112
+ `)[0],...t?{instructions:t}:{}}}const it=e=>!!e?._mcpServer&&e._mcpExposure!==`direct`;function at(e,t,{id:n,parentToolCallId:i,signal:a,hooks:o,state:s}){let c={type:`toolCall`,id:n,name:e.name,arguments:t,parentToolCallId:i},l=s?.messages??[],u=[...l].reverse().find(e=>e.role===`assistant`);return r(c,{tools:[e],assistantMessage:u??{role:`assistant`,content:[c]},context:{systemPrompt:s?.systemPrompt??``,messages:l,tools:s?.tools??[e]},beforeToolCall:o?.beforeToolCall,afterToolCall:o?.afterToolCall,signal:a})}function ot(e){return{...e,execute:(t,n,r,i)=>e.execute(t,n,r,i&&(e=>i(e.details)))}}function st({getTools:e,declaredNames:t,isDeferred:n=it,getNamespace:r=rt,executeTool:i,getStore:a=()=>({}),models:o,sandbox:s,spillOutput:c,inlineBudget:l}){let u=()=>e().filter(e=>e.name!==W);return{name:W,label:`Codemode`,description:w({tools:u(),declaredNames:t,isDeferred:n,getNamespace:r,models:!!o,inlineBudget:l}),parameters:nt,execute:(e,t,n,l)=>U({toolCallId:e,code:t?.code??``,signal:n,onUpdate:l,tools:u(),executeTool:i,store:a(),models:o,getNamespace:r,sandbox:s,spillOutput:c})}}export{nt as CODEMODE_PARAMETERS,G as CODEMODE_STORE_SNAPSHOT_KEY,W as CODEMODE_TOOL_NAME,J as CTOR_IMAGE_API,b as DEFAULT_CODEMODE_INLINE_BUDGET,Ee as DEFAULT_CODEMODE_MEMORY_LIMIT_BYTES,De as DEFAULT_CODEMODE_TIMEOUT_MS,Oe as MAX_CODEMODE_TIMEOUT_MS,Ve as annotateCodemodeStore,Be as collectCodemodeStore,ye as createClassifierModels,w as createCodemodeDescription,be as createCodemodeModels,st as createCodemodeTool,tt as createImageModels,we as createLimiter,U as executeCodemode,it as isSearchOnlyMcpTool,rt as mcpToolNamespace,at as runNestedToolCall,ot as withDetailsUpdates};