agent.libx.js 0.89.6 → 0.89.7

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/dist/index.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- import { a as AgentOptions, H as Hooks, h as RunResult } from './Agent-B0l9qT_j.js';
2
- export { A as Agent, C as ChatFragment, D as DEFAULT_MUTATING, b as Decision, P as PermissionOptions, c as PermissionPolicy, d as PermissionRule, e as PreToolUseDecision, R as ReasoningEffort, f as RecordingHooks, g as RecordingLifecycle, T as ToolUse, i as ToolUseMeta, j as composeHooks, p as planMode, r as reasoningToChatFragment } from './Agent-B0l9qT_j.js';
1
+ import { a as AgentOptions, H as Hooks, h as RunResult, A as Agent } from './Agent-B0l9qT_j.js';
2
+ export { C as ChatFragment, D as DEFAULT_MUTATING, b as Decision, P as PermissionOptions, c as PermissionPolicy, d as PermissionRule, e as PreToolUseDecision, R as ReasoningEffort, f as RecordingHooks, g as RecordingLifecycle, T as ToolUse, i as ToolUseMeta, j as composeHooks, p as planMode, r as reasoningToChatFragment } from './Agent-B0l9qT_j.js';
3
3
  import { IFilesystem, FileMetadata } from '@livx.cc/wcli/core';
4
4
  export { CommandExecutor, FileMetadata, IFilesystem, IndexedDbFilesystem, MemFilesystem, registerHeadlessCommands } from '@livx.cc/wcli/core';
5
5
  import { BodDB } from '@bod.ee/db';
6
- import { A as AgentTool, C as ChatLike, a as ChatOptions, b as ChatResponse, h as ToolCall, H as HostBridge, U as UserQuestion } from './tools-Ch-OzOU8.js';
7
- export { c as ContentPart, d as HostEvent, M as Message, e as MessageContent, R as Role, S as SandboxJobRegistry, f as StreamChunk, T as TodoItem, g as Tool, i as ToolContext, j as bashTool, k as contentText, l as defaultTools, m as editTool, n as imagePart, o as makeContext, p as makeJobTools, r as readTool, t as toWireTools, q as todoWriteTool, s as toolRegistry, u as toolsByName } from './tools-Ch-OzOU8.js';
6
+ import { A as AgentTool, C as ChatLike, a as ChatOptions, b as ChatResponse, h as ToolCall, H as HostBridge, U as UserQuestion, e as MessageContent } from './tools-Ch-OzOU8.js';
7
+ export { c as ContentPart, d as HostEvent, M as Message, R as Role, S as SandboxJobRegistry, f as StreamChunk, T as TodoItem, g as Tool, i as ToolContext, j as bashTool, k as contentText, l as defaultTools, m as editTool, n as imagePart, o as makeContext, p as makeJobTools, r as readTool, t as toWireTools, q as todoWriteTool, s as toolRegistry, u as toolsByName } from './tools-Ch-OzOU8.js';
8
8
  export { M as McpCall, a as McpToolSearchOptions, b as McpToolSpec, m as makeMcpToolSearch, c as mcpToolToAgentTool, d as mcpToolsToAgentTools } from './mcp-Dg3vA1Uj.js';
9
9
  import * as libx_js_src_modules_log from 'libx.js/src/modules/log';
10
10
  export { log } from 'libx.js/src/modules/log';
@@ -556,6 +556,85 @@ declare function reflectOnRun(o: ReflectOptions): Promise<string | null>;
556
556
  */
557
557
  declare function loadInstructions(fs: IFilesystem, names?: string[]): Promise<string>;
558
558
 
559
+ /**
560
+ * DuplexAgent — voice-optimized dual-model conversational engine, composed on top of `Agent`.
561
+ *
562
+ * One FAST voice agent (streams instant replies, owns THE transcript, the only voice the user
563
+ * hears) + detached SLOW worker agents (full tools, deep reasoning) spawned per escalation via a
564
+ * `Delegate` tool. Worker results are pushed back as `[task <id> completed] …` events and
565
+ * re-voiced by the fast persona — push, not poll (unlike subagent.ts's background `Task`).
566
+ *
567
+ * Host events (via the open HostEvent union): the voice agent's standard `text_delta` stream,
568
+ * plus `task_started` / `task_done` / `task_error` / `task_cancelled`.
569
+ */
570
+
571
+ type DuplexTaskStatus = 'running' | 'done' | 'error' | 'cancelled';
572
+ interface TaskRecord {
573
+ id: string;
574
+ label: string;
575
+ status: DuplexTaskStatus;
576
+ controller: AbortController;
577
+ /** Settles when the worker finished AND its completion was processed. Never rejects. */
578
+ promise: Promise<void>;
579
+ }
580
+ declare class DuplexAgentOptions {
581
+ /** Any ai.libx.js AIClient — shared by the voice and worker agents (routed by model). */
582
+ ai: ChatLike;
583
+ /** The WORKER's filesystem. If omitted the worker keeps Agent's jailed-disk-at-cwd default. */
584
+ fs?: IFilesystem;
585
+ voiceModel: string;
586
+ workerModel: string;
587
+ /** Escape hatches merged over the derived per-agent options. */
588
+ voiceOptions?: Partial<AgentOptions>;
589
+ workerOptions?: Partial<AgentOptions>;
590
+ /** Receives the voice text_delta stream + task lifecycle events. */
591
+ host?: HostBridge;
592
+ /** How many recent transcript messages are rendered into a worker's brief. */
593
+ excerptTurns: number;
594
+ /** Voice register: 'neutral' = clean spoken style; 'conversational' = human-like — fillers,
595
+ * backchannels, impulsive first reactions before content (mimics real duplex conversation). */
596
+ voiceStyle: 'neutral' | 'conversational';
597
+ /** Awaited BEFORE a delegated worker spawns — open a per-task checkpoint frame, audit, etc.
598
+ * (post-spawn would race the worker's first edits). */
599
+ onTaskStart?: (id: string, label: string) => void | Promise<void>;
600
+ }
601
+ declare const VOICE_SYSTEM_PROMPT: string;
602
+ /**
603
+ * The duplex orchestrator. `send()` enqueues a user turn on the voice agent; `Delegate`
604
+ * spawns a detached worker whose completion enqueues a re-voice turn. A promise-chain
605
+ * mutex serializes all voice turns so streams never interleave, and completions that
606
+ * pile up while the voice is busy are coalesced into a single re-voice turn.
607
+ */
608
+ declare class DuplexAgent {
609
+ options: DuplexAgentOptions;
610
+ readonly voice: Agent;
611
+ readonly tasks: Map<string, TaskRecord>;
612
+ private queue;
613
+ private seq;
614
+ private pendingEvents;
615
+ private flushQueued;
616
+ constructor(options?: Partial<DuplexAgentOptions>);
617
+ /** One user turn: the voice agent streams the reply (and may Delegate). Serialized with re-voice turns. */
618
+ send(content: MessageContent): Promise<RunResult>;
619
+ /** Resolve when all queued voice turns AND all in-flight worker tasks have settled (tests, graceful shutdown). */
620
+ idle(): Promise<void>;
621
+ /** Promise-chain mutex: turns run strictly one at a time; a failed turn doesn't poison the chain. */
622
+ private enqueue;
623
+ private notify;
624
+ /** Queue a `[task …]` event for re-voicing. Events arriving while the voice is busy coalesce into ONE turn. */
625
+ private queueRevoice;
626
+ /** The worker's brief: the Delegate args + a STATIC text snapshot of the recent conversation. */
627
+ private buildBrief;
628
+ /** Spawn a detached worker for task `id`; its settlement notifies + enqueues the re-voice turn. */
629
+ private spawnWorker;
630
+ private onWorkerSettled;
631
+ private onWorkerFailed;
632
+ private failTask;
633
+ private delegateTool;
634
+ private taskStatusTool;
635
+ private cancelTaskTool;
636
+ }
637
+
559
638
  /**
560
639
  * Typed subagents — named child-agent definitions from `<dir>/<name>.md`, each with a
561
640
  * description, an optional model + tool allowlist (frontmatter), and a system prompt (the
@@ -618,4 +697,4 @@ declare function makeTaskBatchTool(opts: TaskToolOptions): AgentTool;
618
697
  /** Component-scoped logger (libx.js). debug/verbose gated via DEBUG env/localStorage. */
619
698
  declare const forComponent: (name: string) => libx_js_src_modules_log.ComponentLogger;
620
699
 
621
- export { type AgentDef, AgentOptions, AgentTool, type Attempt, BodDbFilesystem, ChatLike, ChatOptions, ChatResponse, type CommandInfo, ConsoleHostBridge, DEFAULT_DENY, FakeAIClient, Hooks, HostBridge, JailOptions, JailedFilesystem, type LessonOptions, LessonOptionsDefaults, type Mount, MountFilesystem, NodeDiskFilesystem, OverlayFilesystem, type ReflectOptions, RunResult, ScriptedHostBridge, type SkillInfo, type TaskToolOptions, ToolCall, type ToolSpec, UserQuestion, type WebFetchOptions, type WebSearchOptions, applyEditsTool, askUserQuestionTool, checkpointTool, checkpointTools, compileSynthesizedTool, diskAgentOptions, expandCommand, expandTemplate, forComponent, fullAgentOptions, globTool, grepTool, htmlToText, idfWeights, lessonCapture, loadAgents, loadCommands, loadInstructions, loadMemory, loadSkills, makeTaskBatchTool, makeTaskTool, makeWebFetchTool, makeWebSearchTool, mkdirp, multiEditTool, raceAttempts, reflectOnRun, relevanceScore, repoIndex, repoMapTool, rollbackTool, sandboxAgentOptions, slugify, tokenize, toolCall, topByRelevance, validateToolCode, webFetchTool, webSearchTool, writeFact, writeTool };
700
+ export { Agent, type AgentDef, AgentOptions, AgentTool, type Attempt, BodDbFilesystem, ChatLike, ChatOptions, ChatResponse, type CommandInfo, ConsoleHostBridge, DEFAULT_DENY, DuplexAgent, DuplexAgentOptions, type DuplexTaskStatus, FakeAIClient, Hooks, HostBridge, JailOptions, JailedFilesystem, type LessonOptions, LessonOptionsDefaults, MessageContent, type Mount, MountFilesystem, NodeDiskFilesystem, OverlayFilesystem, type ReflectOptions, RunResult, ScriptedHostBridge, type SkillInfo, type TaskRecord, type TaskToolOptions, ToolCall, type ToolSpec, UserQuestion, VOICE_SYSTEM_PROMPT, type WebFetchOptions, type WebSearchOptions, applyEditsTool, askUserQuestionTool, checkpointTool, checkpointTools, compileSynthesizedTool, diskAgentOptions, expandCommand, expandTemplate, forComponent, fullAgentOptions, globTool, grepTool, htmlToText, idfWeights, lessonCapture, loadAgents, loadCommands, loadInstructions, loadMemory, loadSkills, makeTaskBatchTool, makeTaskTool, makeWebFetchTool, makeWebSearchTool, mkdirp, multiEditTool, raceAttempts, reflectOnRun, relevanceScore, repoIndex, repoMapTool, rollbackTool, sandboxAgentOptions, slugify, tokenize, toolCall, topByRelevance, validateToolCode, webFetchTool, webSearchTool, writeFact, writeTool };
package/dist/index.js CHANGED
@@ -3448,6 +3448,202 @@ function digestRun(messages, maxChars) {
3448
3448
  return out.length > maxChars ? out.slice(0, maxChars) + "\n\u2026 (truncated)" : out;
3449
3449
  }
3450
3450
 
3451
+ // src/duplex.ts
3452
+ import { MemFilesystem as MemFilesystem2 } from "@livx.cc/wcli/core";
3453
+ init_logging();
3454
+ var log7 = forComponent("DuplexAgent");
3455
+ var DuplexAgentOptions = class {
3456
+ /** Any ai.libx.js AIClient — shared by the voice and worker agents (routed by model). */
3457
+ ai;
3458
+ /** The WORKER's filesystem. If omitted the worker keeps Agent's jailed-disk-at-cwd default. */
3459
+ fs;
3460
+ voiceModel = "anthropic/claude-haiku-4-5";
3461
+ workerModel = "anthropic/claude-sonnet-4-6";
3462
+ /** Escape hatches merged over the derived per-agent options. */
3463
+ voiceOptions;
3464
+ workerOptions;
3465
+ /** Receives the voice text_delta stream + task lifecycle events. */
3466
+ host;
3467
+ /** How many recent transcript messages are rendered into a worker's brief. */
3468
+ excerptTurns = 6;
3469
+ /** Voice register: 'neutral' = clean spoken style; 'conversational' = human-like — fillers,
3470
+ * backchannels, impulsive first reactions before content (mimics real duplex conversation). */
3471
+ voiceStyle = "neutral";
3472
+ /** Awaited BEFORE a delegated worker spawns — open a per-task checkpoint frame, audit, etc.
3473
+ * (post-spawn would race the worker's first edits). */
3474
+ onTaskStart;
3475
+ };
3476
+ var VOICE_SYSTEM_PROMPT = 'You are a spoken voice assistant \u2014 the user HEARS everything you say. Use short sentences. One idea per sentence. No markdown, no bullet lists, no code blocks, no headings, no emoji.\nKeep turns SHORT \u2014 one to three sentences, then stop. Never lecture, enumerate cases, or add caveats unprompted. Conversation is a fast exchange: give the one thing asked, and let the user pull more if they want it.\nYou work in a pair: you talk, and a background worker with FULL access to the user\'s environment (files, shell, web) does the hands-on work. You can find out or do ANYTHING by calling `Delegate` with a clear, self-contained brief \u2014 so NEVER tell the user you can\'t see, access, or do something. Delegate and find out. When the user mentions their project, folder, files, or environment ("this project", "the current folder", "my code"), delegate IMMEDIATELY \u2014 do not ask for paths or details the worker can discover itself. Never pretend to have done the work or invent results \u2014 the worker\'s report is your only source.\nAfter calling Delegate, tell the user you are on it in one short sentence, then end your turn. Do not wait for the result.\nResults arrive later as events like "[task t1 completed] \u2026" or "[task t1 failed] \u2026". When one arrives, summarize it for the ear in one or two short sentences. Never read raw file paths, diffs, or code aloud verbatim.\nDo not fire a second Delegate for work already in flight \u2014 check `TaskStatus` first. Use `CancelTask` when the user asks to stop something.';
3477
+ var VOICE_STYLE_CONVERSATIONAL = `Speak like a person in a live conversation, not an assistant reading a script. React first, then deliver: a quick impulsive beat ("oh nice", "hmm, hold on", "ah, got it") before the substance. Use contractions always. Vary sentence length \u2014 some very short. Light fillers and backchannels are fine ("mm-hm", "right", "let's see") but at most one per reply \u2014 never stack them. When you delegate, say it like a human would ("hang on, let me actually dig into that \u2014 gimme a minute") instead of announcing a task. When a result comes back, react to it like you just found out ("okay so \u2014 turns out\u2026"). Match the user's energy: a quick question gets a quick answer \u2014 a few words is a perfectly good turn. Prefer a short answer plus an offer ("want the details?") over covering everything. Never narrate your own mechanics (no "I will now delegate", no task ids out loud).`;
3478
+ var DuplexAgent = class {
3479
+ options;
3480
+ voice;
3481
+ tasks = /* @__PURE__ */ new Map();
3482
+ queue = Promise.resolve();
3483
+ seq = 0;
3484
+ pendingEvents = [];
3485
+ flushQueued = false;
3486
+ constructor(options) {
3487
+ this.options = { ...new DuplexAgentOptions(), ...options };
3488
+ const o = this.options;
3489
+ this.voice = new Agent({
3490
+ ai: o.ai,
3491
+ fs: new MemFilesystem2(),
3492
+ // scratch — NOT Agent's jailed-disk default (voice has no fs tools; edge-safe)
3493
+ model: o.voiceModel,
3494
+ stream: true,
3495
+ host: o.host,
3496
+ systemPrompt: VOICE_SYSTEM_PROMPT + (o.voiceStyle === "conversational" ? "\n" + VOICE_STYLE_CONVERSATIONAL : ""),
3497
+ instructionFiles: false,
3498
+ maxSteps: 8,
3499
+ // a voice turn should never loop
3500
+ timeoutMs: 3e4,
3501
+ ...o.voiceOptions,
3502
+ // no defaultTools() — the voice can only Delegate, never touch files itself. Set AFTER the
3503
+ // voiceOptions spread (addTools() would be clobbered by the first prepare()); extra voice
3504
+ // tools come in via voiceOptions.tools and are merged here.
3505
+ tools: [...o.voiceOptions?.tools ?? [], this.delegateTool(), this.taskStatusTool(), this.cancelTaskTool()]
3506
+ });
3507
+ }
3508
+ /** One user turn: the voice agent streams the reply (and may Delegate). Serialized with re-voice turns. */
3509
+ send(content) {
3510
+ return this.enqueue(() => this.voice.send(content));
3511
+ }
3512
+ /** Resolve when all queued voice turns AND all in-flight worker tasks have settled (tests, graceful shutdown). */
3513
+ async idle() {
3514
+ while (true) {
3515
+ const q = this.queue;
3516
+ await q.catch(() => {
3517
+ });
3518
+ await Promise.all([...this.tasks.values()].map((t) => t.promise));
3519
+ if (this.queue === q && ![...this.tasks.values()].some((t) => t.status === "running")) return;
3520
+ }
3521
+ }
3522
+ /** Promise-chain mutex: turns run strictly one at a time; a failed turn doesn't poison the chain. */
3523
+ enqueue(fn) {
3524
+ const run = this.queue.then(fn, fn);
3525
+ this.queue = run.then(() => {
3526
+ }, () => {
3527
+ });
3528
+ return run;
3529
+ }
3530
+ notify(kind, message, data) {
3531
+ this.options.host?.notify?.({ kind, message, data });
3532
+ }
3533
+ /** Queue a `[task …]` event for re-voicing. Events arriving while the voice is busy coalesce into ONE turn. */
3534
+ queueRevoice(event) {
3535
+ this.pendingEvents.push(event);
3536
+ if (this.flushQueued) return;
3537
+ this.flushQueued = true;
3538
+ void this.enqueue(async () => {
3539
+ this.flushQueued = false;
3540
+ const events = this.pendingEvents.splice(0);
3541
+ if (!events.length) return;
3542
+ await this.voice.send(events.join("\n"));
3543
+ this.notify("revoice_done", "");
3544
+ });
3545
+ }
3546
+ /** The worker's brief: the Delegate args + a STATIC text snapshot of the recent conversation. */
3547
+ buildBrief(brief) {
3548
+ const recent = this.voice.transcript.filter((m) => (m.role === "user" || m.role === "assistant") && contentText(m.content).trim()).slice(-this.options.excerptTurns).map((m) => `${m.role}: ${contentText(m.content)}`).join("\n");
3549
+ return recent ? `${brief}
3550
+
3551
+ ## Recent conversation (for context)
3552
+ ${recent}` : brief;
3553
+ }
3554
+ /** Spawn a detached worker for task `id`; its settlement notifies + enqueues the re-voice turn. */
3555
+ spawnWorker(id, label, briefText) {
3556
+ const o = this.options;
3557
+ const controller = new AbortController();
3558
+ const worker = new Agent({
3559
+ ai: o.ai,
3560
+ fs: o.fs,
3561
+ model: o.workerModel,
3562
+ ...o.workerOptions,
3563
+ // may override ai/fs/model/tools/… —
3564
+ signal: controller.signal
3565
+ // …but never the per-task cancellation signal
3566
+ });
3567
+ const promise = worker.run(briefText).then((res) => this.onWorkerSettled(id, res)).catch((err) => this.onWorkerFailed(id, err));
3568
+ this.tasks.set(id, { id, label, status: "running", controller, promise });
3569
+ }
3570
+ onWorkerSettled(id, res) {
3571
+ const rec = this.tasks.get(id);
3572
+ if (res.finishReason === "aborted" || rec.status === "cancelled") {
3573
+ rec.status = "cancelled";
3574
+ this.notify("task_cancelled", `task ${id} (${rec.label}) cancelled`);
3575
+ return;
3576
+ }
3577
+ if (res.finishReason === "error") {
3578
+ const msg = res.error instanceof Error ? res.error.message : String(res.error ?? "unknown error");
3579
+ return this.failTask(rec, msg);
3580
+ }
3581
+ rec.status = "done";
3582
+ log7.verbose(`task ${id} done (${res.steps} steps)`);
3583
+ this.notify("task_done", `task ${id} (${rec.label}) completed`, { id, text: res.text, usage: res.usage, usageEstimated: res.usageEstimated });
3584
+ this.queueRevoice(`[task ${id} completed] ${res.text}`);
3585
+ }
3586
+ onWorkerFailed(id, err) {
3587
+ this.failTask(this.tasks.get(id), err instanceof Error ? err.message : String(err));
3588
+ }
3589
+ failTask(rec, msg) {
3590
+ rec.status = "error";
3591
+ log7.warn(`task ${rec.id} failed: ${msg}`);
3592
+ this.notify("task_error", `task ${rec.id} (${rec.label}) failed: ${msg}`);
3593
+ this.queueRevoice(`[task ${rec.id} failed] ${msg}`);
3594
+ }
3595
+ // --- the three voice tools (closures over this instance) ---
3596
+ delegateTool() {
3597
+ return {
3598
+ name: "Delegate",
3599
+ description: 'Escalate real work (reading/editing files, searching, running tasks, deep analysis) to a background worker agent. Returns immediately with a task id; the result arrives later as a "[task <id> completed]" event. Provide a clear, self-contained `brief` (the worker does not hear the live conversation).',
3600
+ parameters: {
3601
+ type: "object",
3602
+ required: ["brief"],
3603
+ properties: {
3604
+ brief: { type: "string", description: "full, self-contained instructions for the worker" },
3605
+ label: { type: "string", description: "a short (2-4 word) label for the task" }
3606
+ }
3607
+ },
3608
+ run: async ({ brief, label }) => {
3609
+ const id = `t${++this.seq}`;
3610
+ const lbl = String(label ?? "task");
3611
+ await this.options.onTaskStart?.(id, lbl);
3612
+ this.spawnWorker(id, lbl, this.buildBrief(String(brief ?? "")));
3613
+ this.notify("task_started", `task ${id} (${lbl}) started`, { id, brief });
3614
+ return `Delegated as task ${id}. Acknowledge briefly; the result will arrive as a [task ${id} completed] event.`;
3615
+ }
3616
+ };
3617
+ }
3618
+ taskStatusTool() {
3619
+ return {
3620
+ name: "TaskStatus",
3621
+ description: "Status of background tasks. Pass `id` for one task, or omit it to list all.",
3622
+ parameters: { type: "object", properties: { id: { type: "string" } } },
3623
+ run: async ({ id }) => {
3624
+ const list = id ? [this.tasks.get(String(id))].filter(Boolean) : [...this.tasks.values()];
3625
+ if (!list.length) return id ? `No task '${id}'.` : "No background tasks.";
3626
+ return list.map((t) => `${t.id} (${t.label}): ${t.status}`).join("\n");
3627
+ }
3628
+ };
3629
+ }
3630
+ cancelTaskTool() {
3631
+ return {
3632
+ name: "CancelTask",
3633
+ description: "Cancel a running background task by id.",
3634
+ parameters: { type: "object", required: ["id"], properties: { id: { type: "string" } } },
3635
+ run: async ({ id }) => {
3636
+ const rec = this.tasks.get(String(id));
3637
+ if (!rec) return `No task '${id}'.`;
3638
+ if (rec.status !== "running") return `Task ${rec.id} is already ${rec.status}.`;
3639
+ rec.status = "cancelled";
3640
+ rec.controller.abort();
3641
+ return `Task ${rec.id} (${rec.label}) cancelled.`;
3642
+ }
3643
+ };
3644
+ }
3645
+ };
3646
+
3451
3647
  // src/mcp.ts
3452
3648
  function toText(result) {
3453
3649
  if (result == null) return "";
@@ -3565,7 +3761,7 @@ var RecordingLifecycle = class {
3565
3761
 
3566
3762
  // src/index.ts
3567
3763
  init_logging();
3568
- import { MemFilesystem as MemFilesystem2, IndexedDbFilesystem, CommandExecutor as CommandExecutor2, registerHeadlessCommands as registerHeadlessCommands2 } from "@livx.cc/wcli/core";
3764
+ import { MemFilesystem as MemFilesystem3, IndexedDbFilesystem, CommandExecutor as CommandExecutor2, registerHeadlessCommands as registerHeadlessCommands2 } from "@livx.cc/wcli/core";
3569
3765
  export {
3570
3766
  Agent,
3571
3767
  AgentOptions,
@@ -3574,12 +3770,14 @@ export {
3574
3770
  ConsoleHostBridge,
3575
3771
  DEFAULT_DENY,
3576
3772
  DEFAULT_MUTATING,
3773
+ DuplexAgent,
3774
+ DuplexAgentOptions,
3577
3775
  FakeAIClient,
3578
3776
  IndexedDbFilesystem,
3579
3777
  JailOptions,
3580
3778
  JailedFilesystem,
3581
3779
  LessonOptionsDefaults,
3582
- MemFilesystem2 as MemFilesystem,
3780
+ MemFilesystem3 as MemFilesystem,
3583
3781
  MountFilesystem,
3584
3782
  NodeDiskFilesystem,
3585
3783
  OverlayFilesystem,
@@ -3589,6 +3787,7 @@ export {
3589
3787
  RecordingLifecycle,
3590
3788
  SandboxJobRegistry,
3591
3789
  ScriptedHostBridge,
3790
+ VOICE_SYSTEM_PROMPT,
3592
3791
  applyEditsTool,
3593
3792
  askUserQuestionTool,
3594
3793
  bashTool,