@demicodes/agent 0.18.0 → 0.19.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { b as AgentMetadata, d as TranscriptPatch, l as ServerFrame, n as AgentServerTransport, o as ClientFrame, r as AgentTransport, s as ClientSessionEvent, t as AgentClientTransport, u as ShellCommandStatusLike } from "./transport-DtBCCS_A.mjs";
2
- import { a as AgentClient, i as AgentActionOptions, n as createWebSocketClientTransport, o as AgentClientListener, r as createWebSocketServerTransport, t as JsonWebSocket } from "./websocket-transport-DdfGgBjO.mjs";
1
+ import { f as TranscriptPatch, l as ServerFrame, n as AgentServerTransport, o as ClientFrame, r as AgentTransport, s as ClientSessionEvent, t as AgentClientTransport, u as ShellCommandStatusLike, x as AgentMetadata } from "./transport-BRqZoaeo.mjs";
2
+ import { a as AgentClient, i as AgentActionOptions, n as createWebSocketClientTransport, o as AgentClientListener, r as createWebSocketServerTransport, t as JsonWebSocket } from "./websocket-transport-C6Y5CeLG.mjs";
3
3
  import { ProviderSelection } from "@demicodes/provider";
4
4
  export { type AgentActionOptions, AgentClient, type AgentClientListener, type AgentClientTransport, type AgentMetadata, type AgentServerTransport, type AgentTransport, type ClientFrame, type ClientSessionEvent, type JsonWebSocket, type ProviderSelection, type ServerFrame, type ShellCommandStatusLike, type TranscriptPatch, createWebSocketClientTransport, createWebSocketServerTransport };
@@ -1,2 +1,2 @@
1
- import { n as createWebSocketServerTransport, r as AgentClient, t as createWebSocketClientTransport } from "./websocket-transport-c6P3NBuf.mjs";
1
+ import { n as createWebSocketServerTransport, r as AgentClient, t as createWebSocketClientTransport } from "./websocket-transport-CIOotpd-.mjs";
2
2
  export { AgentClient, createWebSocketClientTransport, createWebSocketServerTransport };
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
- import { A as AgentTool, B as TranscriptLog, C as AgentSessionCheckpoint, D as AgentSessionRestoreParams, E as AgentSessionParams, F as ModelSwitchApply, G as isRetryableCode, H as estimateTranscriptBlockTokens, I as SessionEvent, K as resolveRetryPolicy, L as SessionEventListener, M as AgentToolInvokeContext, N as AgentToolInvokeResult, O as AgentSessionStore, P as ExternalMutationReservation, R as CompactionWindow, S as AgentReferenceResolveContext, T as AgentSessionOptions, U as DEFAULT_TURN_RETRY_POLICY, V as TranscriptOptions, W as TurnRetryPolicy, _ as AgentHarnessRuntime, a as createInProcessTransportPair, b as AgentMetadata, c as ConversationSummary, d as TranscriptPatch, f as AbortResult, g as AgentHarnessContext, h as AgentHarness, i as InProcessTransportPair, j as AgentToolContext, k as AgentSystemPromptContext, l as ServerFrame, m as AgentDisposeContext, n as AgentServerTransport, o as ClientFrame, p as AbortTarget, q as retryDelayMs, r as AgentTransport, s as ClientSessionEvent, t as AgentClientTransport, u as ShellCommandStatusLike, v as AgentHostContext, w as AgentSessionCloneParams, x as AgentPromptContext, y as AgentLifecycleEvent, z as DrainedTranscriptPatches } from "./transport-DtBCCS_A.mjs";
2
- import { a as AgentClient, i as AgentActionOptions, n as createWebSocketClientTransport, o as AgentClientListener, r as createWebSocketServerTransport, t as JsonWebSocket } from "./websocket-transport-DdfGgBjO.mjs";
3
- import { BashAuditEvent, BashEnvironment, BashEnvironmentOptions, CommandMetadataRecord, Host, ShellCommandStatus, ShellOutputChunk } from "@demicodes/shell";
1
+ import { A as AgentSystemPromptContext, B as CompactionWindow, C as AgentReferenceResolveContext, D as AgentSessionParams, E as AgentSessionOptions, F as ExternalMutationReservation, G as DEFAULT_TURN_RETRY_POLICY, H as TranscriptLog, I as ModelSwitchApply, J as resolveRetryPolicy, K as TurnRetryPolicy, L as SessionEvent, M as AgentToolContext, N as AgentToolInvokeContext, O as AgentSessionRestoreParams, P as AgentToolInvokeResult, R as SessionEventListener, S as AgentPromptContext, T as AgentSessionCloneParams, U as TranscriptOptions, V as DrainedTranscriptPatches, W as estimateTranscriptBlockTokens, Y as retryDelayMs, _ as AgentHarnessContext, a as createInProcessTransportPair, b as AgentLifecycleEvent, c as ConversationSummary, d as SubagentJob, f as TranscriptPatch, g as AgentHarness, h as AgentDisposeContext, i as InProcessTransportPair, j as AgentTool, k as AgentSessionStore, l as ServerFrame, m as AbortTarget, n as AgentServerTransport, o as ClientFrame, p as AbortResult, q as isRetryableCode, r as AgentTransport, s as ClientSessionEvent, t as AgentClientTransport, u as ShellCommandStatusLike, v as AgentHarnessRuntime, w as AgentSessionCheckpoint, x as AgentMetadata, y as AgentHostContext, z as SubagentProfile } from "./transport-BRqZoaeo.mjs";
2
+ import { a as AgentClient, i as AgentActionOptions, n as createWebSocketClientTransport, o as AgentClientListener, r as createWebSocketServerTransport, t as JsonWebSocket } from "./websocket-transport-C6Y5CeLG.mjs";
3
+ import { BashAuditEvent, BashEnvironment, BashEnvironmentOptions, Command, CommandMetadataRecord, Host, ShellCommandStatus, ShellOutputChunk } from "@demicodes/shell";
4
4
  import { AgentProvider, Provider } from "@demicodes/provider";
5
5
  import { Block, Model, ModelMediaKind, ModelSelection, ProviderErrorDiagnostics, QueuedMessage, SessionPhase, UserContentBlock } from "@demicodes/core";
6
6
  //#region src/recovery.d.ts
@@ -149,6 +149,13 @@ declare class AgentSession<State> {
149
149
  state(): State;
150
150
  id(): string;
151
151
  phase(): SessionPhase;
152
+ /** Granular in-turn phase for observers (null when no turn is active). */
153
+ turnPhase(): ActiveTurnPhase | null;
154
+ hasPendingYields(): boolean;
155
+ /** Metadata of the currently executing action, if any. */
156
+ actionMetadata(): AgentMetadata | null;
157
+ /** Independent provider runtime with this session's configuration (for child sessions). */
158
+ cloneProviderRuntime(): AgentProvider;
152
159
  queuedMessages(): QueuedMessage[];
153
160
  dequeueMessage(id: string): boolean;
154
161
  sendQueuedMessage(id: string): boolean;
@@ -264,6 +271,14 @@ interface AgentServerOptions {
264
271
  providers: Provider[];
265
272
  shell?: Omit<BashEnvironmentOptions, 'host' | 'commands'>;
266
273
  session?: AgentServerSessionOptions;
274
+ subagents?: {
275
+ /**
276
+ * When false, a child closing never wakes an idle parent with an automatic
277
+ * user send; the host app observes the `subagent closed` frame and drives
278
+ * the parent itself. Defaults to true.
279
+ */
280
+ notifyParentOnIdle?: boolean;
281
+ };
267
282
  /**
268
283
  * Optional host-side shell prep (env, PATH, etc.). Implementation-agnostic —
269
284
  * command bridge wiring lives in `@demicodes/host-local`, not here.
@@ -307,6 +322,7 @@ declare class AgentServer {
307
322
  private readonly shellOptions;
308
323
  private readonly sessionOptions;
309
324
  private readonly prepareShell;
325
+ private readonly notifyParentOnIdle;
310
326
  private readonly bindings;
311
327
  private readonly sessionOwnership;
312
328
  constructor(options: AgentServerOptions);
@@ -321,6 +337,94 @@ declare class AgentServer {
321
337
  runCommandLine(shellId: string, name: string, args: string[], opts: RunCommandLineOptions): Promise<RunCommandLineResult>;
322
338
  }
323
339
  //#endregion
340
+ //#region src/subagent.d.ts
341
+ declare const MAX_LIVE_SUBAGENTS = 8;
342
+ declare const SUBAGENT_RESULT_MAX_BYTES: number;
343
+ type SubagentExecution = 'idle' | 'provider_streaming' | 'tool_executing' | 'compacting' | 'finalizing' | 'pending_yield';
344
+ interface ChildSupervisorOptions<State> {
345
+ agent: AgentHarness<State>;
346
+ cwd: string;
347
+ /** Harness-configured profiles; null means the implicit `default` only. */
348
+ profiles: SubagentProfile<State>[] | null;
349
+ /** Parent registered commands (harness list, before the `demi agent` injection). */
350
+ parentCommands: Command[];
351
+ shellOptions: Omit<BashEnvironmentOptions, 'host' | 'commands'>;
352
+ prepareShell: PrepareShell | null;
353
+ sessionOptions: AgentServerSessionOptions;
354
+ /** When false, a child closing never wakes an idle parent; the host app orchestrates the wakeup from the `subagent closed` frame. */
355
+ notifyParentOnIdle: boolean;
356
+ emit(frame: ServerFrame): void;
357
+ }
358
+ /**
359
+ * Per-parent subagent supervisor. Owns every child `AgentSession` this parent
360
+ * spawns: lifecycle (spawn / steer / abort / natural end), the `demi agent`
361
+ * command tree, child shell environments, and the `subagent*` protocol frames
362
+ * on the parent connection. Children are in-memory only: no store, no
363
+ * checkpoint, gone with the parent.
364
+ */
365
+ declare class ChildSupervisor<State = unknown> {
366
+ private readonly options;
367
+ private readonly jobs;
368
+ private parentSession;
369
+ private isDisposed;
370
+ constructor(options: ChildSupervisorOptions<State>);
371
+ attachParent(session: AgentSession<State>): void;
372
+ /** The `agent` node AgentServer grafts under the parent registry's `demi` root. */
373
+ rootCommandNode(): Command;
374
+ hasShell(shellId: string): boolean;
375
+ /** Resolves the child scope owning a shell, for the command bridge dispatch. */
376
+ environmentScopeForShell(shellId: string): {
377
+ environment: BashEnvironment;
378
+ commandNames: ReadonlySet<string>;
379
+ agentSessionId: string;
380
+ } | null;
381
+ /** Re-emits `subagent started` + transcript reset for every running child (transcript resync). */
382
+ replay(): void;
383
+ dispose(): Promise<void>;
384
+ abortSubtree(id: string): Promise<void>;
385
+ private spawn;
386
+ /** Delivers one steer to a child: mid-turn as a steer, idle as a new user turn. */
387
+ private steerChild;
388
+ private pumpStdinSteers;
389
+ private watchChild;
390
+ /** Tracks one child turn to session quiescence, then closes the job with its outcome. */
391
+ private watchTurn;
392
+ /** Resolves when the child is idle with no pending yield wakeups (or the job closed). */
393
+ private waitForQuiescence;
394
+ private closeJob;
395
+ /** Wakes an idle parent with a user send; a parent blocked in the spawn gets the tool result instead. */
396
+ private notifyIdleParent;
397
+ private createChildAgentNode;
398
+ private deliverToParent;
399
+ private childEnvironment;
400
+ private createChildEnvironment;
401
+ private resolveProfile;
402
+ private configuredProfileNames;
403
+ private subagentPreamble;
404
+ private recordTelemetry;
405
+ private executionOf;
406
+ private activityOf;
407
+ private executionForMs;
408
+ private snapshot;
409
+ private renderListLine;
410
+ private renderShow;
411
+ private wireJob;
412
+ }
413
+ /**
414
+ * Grafts the `agent` node under a `demi` root: onto an existing harness `demi`
415
+ * tree, or as a new `demi` root when the harness has none.
416
+ */
417
+ declare function injectSubagentCommand(commands: Command[], agentNode: Command): Command[];
418
+ /**
419
+ * Host wrapper for read-only subagent profiles: filesystem mutation is
420
+ * rejected outside the shell's own command-artifacts tree, and process spawn
421
+ * is rejected outright (a real process cannot be write-restricted).
422
+ *
423
+ * Every facet delegates method by method: Host facets are class instances
424
+ * whose methods live on the prototype, so object spread would drop them.
425
+ */
426
+ declare function createReadonlyHost(host: Host): Host;
427
+ //#endregion
324
428
  //#region src/tools.d.ts
325
429
  interface StandardAgentToolOptions<State = unknown> {
326
430
  environment: BashEnvironment | ((ctx: AgentToolInvokeContext<State>, handle: {
@@ -387,4 +491,4 @@ interface ShellToolView {
387
491
  declare function finishShellToolResult<State>(environment: BashEnvironment, result: ShellCommandStatus, ctx: AgentToolInvokeContext<State>): Promise<AgentToolInvokeResult>;
388
492
  declare function shellCommandHandleRequired(result: ShellCommandStatus, budgetTokens: number): boolean;
389
493
  //#endregion
390
- export { AbortResult, AbortTarget, ActiveTurnPhase, AgentActionOptions, AgentClient, AgentClientListener, AgentClientTransport, AgentDisposeContext, AgentHarness, AgentHarnessContext, AgentHarnessRuntime, AgentHostContext, AgentLifecycleEvent, AgentMetadata, AgentPromptContext, AgentReferenceResolveContext, AgentServer, AgentServerOptions, AgentServerSessionOptions, AgentServerTransport, AgentSession, AgentSessionCheckpoint, AgentSessionCloneParams, AgentSessionOptions, AgentSessionParams, AgentSessionRestoreParams, AgentSessionStore, AgentSystemPromptContext, AgentTool, AgentToolContext, AgentToolInvokeContext, AgentToolInvokeResult, AgentTransport, AgentTransportBinding, ClientFrame, ClientSessionEvent, CompactionWindow, ConversationSummary, DEFAULT_MAX_MEDIA_BYTES, DEFAULT_TURN_RETRY_POLICY, DrainedTranscriptPatches, ExternalMutationReservation, InProcessTransportPair, JsonWebSocket, ModelSwitchApply, PrepareShell, PrepareShellContext, ProviderStreamError, ResumePoint, RunCommandLineCommandNotRegisteredError, RunCommandLineOptions, RunCommandLineResult, RunCommandLineShellNotFoundError, RunCommandLineTimeoutError, SHELL_VIEW_MAX_CHARS, ServerFrame, SessionEvent, SessionEventListener, ShellCommandStatusLike, ShellToolResultOptions, ShellToolView, StandardAgentToolOptions, TranscriptLog, TranscriptOptions, TranscriptPatch, TurnRetryPolicy, applyTranscriptPatches, cloneBlocks, createInProcessTransportPair, createStandardAgentTools, createWebSocketClientTransport, createWebSocketServerTransport, estimateTranscriptBlockTokens, findResumePoint, finishShellToolResult, isContextLengthExceeded, isRetryableCode, resolveRetryPolicy, retryDelayMs, shellCommandHandleRequired, shellPreviewBudgetTokens, toShellToolResult };
494
+ export { AbortResult, AbortTarget, ActiveTurnPhase, AgentActionOptions, AgentClient, AgentClientListener, AgentClientTransport, AgentDisposeContext, AgentHarness, AgentHarnessContext, AgentHarnessRuntime, AgentHostContext, AgentLifecycleEvent, AgentMetadata, AgentPromptContext, AgentReferenceResolveContext, AgentServer, AgentServerOptions, AgentServerSessionOptions, AgentServerTransport, AgentSession, AgentSessionCheckpoint, AgentSessionCloneParams, AgentSessionOptions, AgentSessionParams, AgentSessionRestoreParams, AgentSessionStore, AgentSystemPromptContext, AgentTool, AgentToolContext, AgentToolInvokeContext, AgentToolInvokeResult, AgentTransport, AgentTransportBinding, ChildSupervisor, ChildSupervisorOptions, ClientFrame, ClientSessionEvent, CompactionWindow, ConversationSummary, DEFAULT_MAX_MEDIA_BYTES, DEFAULT_TURN_RETRY_POLICY, DrainedTranscriptPatches, ExternalMutationReservation, InProcessTransportPair, JsonWebSocket, MAX_LIVE_SUBAGENTS, ModelSwitchApply, PrepareShell, PrepareShellContext, ProviderStreamError, ResumePoint, RunCommandLineCommandNotRegisteredError, RunCommandLineOptions, RunCommandLineResult, RunCommandLineShellNotFoundError, RunCommandLineTimeoutError, SHELL_VIEW_MAX_CHARS, SUBAGENT_RESULT_MAX_BYTES, ServerFrame, SessionEvent, SessionEventListener, ShellCommandStatusLike, ShellToolResultOptions, ShellToolView, StandardAgentToolOptions, SubagentExecution, SubagentJob, SubagentProfile, TranscriptLog, TranscriptOptions, TranscriptPatch, TurnRetryPolicy, applyTranscriptPatches, cloneBlocks, createInProcessTransportPair, createReadonlyHost, createStandardAgentTools, createWebSocketClientTransport, createWebSocketServerTransport, estimateTranscriptBlockTokens, findResumePoint, finishShellToolResult, injectSubagentCommand, isContextLengthExceeded, isRetryableCode, resolveRetryPolicy, retryDelayMs, shellCommandHandleRequired, shellPreviewBudgetTokens, toShellToolResult };
package/dist/index.mjs CHANGED
@@ -1,8 +1,9 @@
1
- import { a as cloneBlocks, i as applyTranscriptPatches, n as createWebSocketServerTransport, o as ProviderStreamError, r as AgentClient, s as isContextLengthExceeded, t as createWebSocketClientTransport } from "./websocket-transport-c6P3NBuf.mjs";
2
- import { AbortError, abortable, asError, asRecord, asString, bytesToBase64, createId, delay, isAbortError, isRecord, noop, parseJsonOrString, safeJsonStringify, sliceHead, sliceTail, throwIfAborted, toWellFormedText, truncate } from "@demicodes/utils";
1
+ import { a as cloneBlocks, i as applyTranscriptPatches, n as createWebSocketServerTransport, o as ProviderStreamError, r as AgentClient, s as isContextLengthExceeded, t as createWebSocketClientTransport } from "./websocket-transport-CIOotpd-.mjs";
2
+ import { AbortError, abortable, asError, asRecord, asString, bytesToBase64, createId, decodeUtf8, delay, errorMessage, isAbortError, isRecord, noop, parseJsonOrString, safeJsonStringify, sliceHead, sliceTail, throwIfAborted, toWellFormedText, truncate, utf8Slice } from "@demicodes/utils";
3
3
  import { BashEnvironment, CommandRegistry, MAX_TIMEOUT_MS, heredocDelimiter, shellQuote } from "@demicodes/shell";
4
4
  import { providerRuntime } from "@demicodes/provider";
5
5
  import { modelAcceptsMediaType, sniffModelMediaType } from "@demicodes/core";
6
+ import { z } from "zod";
6
7
  //#region src/transcript.ts
7
8
  const DEFAULT_MODEL_TEXT_HEAD_CHARS = 8e3;
8
9
  const DEFAULT_MODEL_TEXT_TAIL_CHARS = 8e3;
@@ -912,7 +913,7 @@ var CompactionController = class {
912
913
  text: COMPACTION_SUMMARY_INSTRUCTION
913
914
  }]);
914
915
  throwIfAborted(parentSignal);
915
- return lastAssistantText(clone.transcript(), baseBlockCount).trim();
916
+ return lastAssistantText$1(clone.transcript(), baseBlockCount).trim();
916
917
  } finally {
917
918
  parentSignal.removeEventListener("abort", abortClone);
918
919
  unsubscribe();
@@ -920,7 +921,7 @@ var CompactionController = class {
920
921
  }
921
922
  }
922
923
  };
923
- function lastAssistantText(transcript, startIndex) {
924
+ function lastAssistantText$1(transcript, startIndex) {
924
925
  for (let index = transcript.blocks.length - 1; index >= startIndex; index -= 1) {
925
926
  const block = transcript.blocks[index];
926
927
  if (block?.type === "text") return block.text;
@@ -1583,6 +1584,21 @@ var AgentSession = class AgentSession {
1583
1584
  phase() {
1584
1585
  return this.currentPhase;
1585
1586
  }
1587
+ /** Granular in-turn phase for observers (null when no turn is active). */
1588
+ turnPhase() {
1589
+ return this.activeTurnPhase;
1590
+ }
1591
+ hasPendingYields() {
1592
+ return this.yields.hasPending;
1593
+ }
1594
+ /** Metadata of the currently executing action, if any. */
1595
+ actionMetadata() {
1596
+ return this.activeMetadata;
1597
+ }
1598
+ /** Independent provider runtime with this session's configuration (for child sessions). */
1599
+ cloneProviderRuntime() {
1600
+ return this.provider.clone();
1601
+ }
1586
1602
  queuedMessages() {
1587
1603
  return this.queued.map((message) => ({
1588
1604
  ...message,
@@ -2635,6 +2651,767 @@ function shellCommandHandleRequired(result, budgetTokens) {
2635
2651
  return preview.truncated || result.output.truncated || result.output.bytes > maxChars || result.stdout.truncated || result.stderr.truncated;
2636
2652
  }
2637
2653
  //#endregion
2654
+ //#region src/subagent.ts
2655
+ const MAX_LIVE_SUBAGENTS = 8;
2656
+ const SUBAGENT_RESULT_MAX_BYTES = 32768;
2657
+ const SHOW_RECENT_TOOLS = 8;
2658
+ const SPAWN_PROMPT_DESCRIPTION = "The child's first user message and only task brief. The child starts with an empty transcript and cannot see this conversation: do not refer to prior turns, and do not paste this conversation or the product user's message unchanged. Include the goal for this child, applicable decisions and constraints, whether to edit or only report, how to verify, and every concrete identifier it needs (paths, ids, error text, commands already tried and their key results). State the exact shape of the last assistant text it should return.";
2659
+ /**
2660
+ * Per-parent subagent supervisor. Owns every child `AgentSession` this parent
2661
+ * spawns: lifecycle (spawn / steer / abort / natural end), the `demi agent`
2662
+ * command tree, child shell environments, and the `subagent*` protocol frames
2663
+ * on the parent connection. Children are in-memory only: no store, no
2664
+ * checkpoint, gone with the parent.
2665
+ */
2666
+ var ChildSupervisor = class {
2667
+ options;
2668
+ jobs = /* @__PURE__ */ new Map();
2669
+ parentSession = null;
2670
+ isDisposed = false;
2671
+ constructor(options) {
2672
+ this.options = options;
2673
+ }
2674
+ attachParent(session) {
2675
+ this.parentSession = session;
2676
+ }
2677
+ /** The `agent` node AgentServer grafts under the parent registry's `demi` root. */
2678
+ rootCommandNode() {
2679
+ const profileNames = this.configuredProfileNames();
2680
+ return {
2681
+ name: "agent",
2682
+ summary: "Start an isolated child agent session and wait for its result. The command stays running until the child session ends; stdout is the child's last assistant text. While it is the foreground job, shell_write steers the child and shell_abort aborts it. Run several in separate shell_exec calls with short timeoutMs to fan out.",
2683
+ successOutput: "first stderr line is \"subagentId: <id>\" at start; stdout is the child's last assistant text (empty is valid), written only at exit",
2684
+ failureOutput: "non-zero exit with the abort or failure reason on stderr",
2685
+ input: {
2686
+ prompt: z.string().optional().describe(SPAWN_PROMPT_DESCRIPTION),
2687
+ profile: z.string().optional().describe(`Named subagent profile configured at harness assembly. Available: ${profileNames.join(", ")}.`),
2688
+ description: z.string().optional().describe("Short UI title distinguishing concurrent children.")
2689
+ },
2690
+ positionals: ["prompt"],
2691
+ stdinField: "prompt",
2692
+ output: { json: z.object({
2693
+ subagentId: z.string(),
2694
+ text: z.string()
2695
+ }) },
2696
+ run: async ({ parsed, io, signal, stdinStream }) => {
2697
+ const prompt = String(parsed.values.prompt ?? "").trim();
2698
+ if (!prompt) {
2699
+ await io.stderr("demi agent: prompt must not be empty\n");
2700
+ return { exitCode: 1 };
2701
+ }
2702
+ let job;
2703
+ try {
2704
+ job = await this.spawn({
2705
+ prompt,
2706
+ profileName: parsed.values.profile === void 0 ? void 0 : String(parsed.values.profile),
2707
+ description: parsed.values.description === void 0 ? "" : String(parsed.values.description)
2708
+ });
2709
+ } catch (error) {
2710
+ await io.stderr(`demi agent: ${errorMessage(error)}\n`);
2711
+ return { exitCode: 1 };
2712
+ }
2713
+ await io.stderr(`subagentId: ${job.id}\n`);
2714
+ const onAbort = () => {
2715
+ this.abortSubtree(job.id).catch(noop);
2716
+ };
2717
+ if (signal.aborted) onAbort();
2718
+ else signal.addEventListener("abort", onAbort, { once: true });
2719
+ this.pumpStdinSteers(job.id, stdinStream);
2720
+ const close = await job.closed;
2721
+ signal.removeEventListener("abort", onAbort);
2722
+ if (close.phase === "completed") {
2723
+ const text = close.result ?? "";
2724
+ if (parsed.json) await io.stdout(`${JSON.stringify({
2725
+ subagentId: job.id,
2726
+ text
2727
+ })}\n`);
2728
+ else if (text.length > 0) await io.stdout(text.endsWith("\n") ? text : `${text}\n`);
2729
+ return { exitCode: 0 };
2730
+ }
2731
+ if (close.phase === "aborted") {
2732
+ await io.stderr(`demi agent: subagent ${job.id} aborted\n`);
2733
+ return { exitCode: 130 };
2734
+ }
2735
+ await io.stderr(`demi agent: subagent ${job.id} failed: ${close.failure ?? "unknown error"}\n`);
2736
+ return { exitCode: 1 };
2737
+ },
2738
+ subcommands: [
2739
+ {
2740
+ name: "steer",
2741
+ summary: "Send a user steer to a running child. Queues until the child can take it; does not wait.",
2742
+ input: {
2743
+ id: z.string().describe("subagentId from spawn stderr"),
2744
+ message: z.string().optional().describe("Message body; positional, or stdin/heredoc when omitted.")
2745
+ },
2746
+ positionals: ["id", "message"],
2747
+ stdinField: "message",
2748
+ output: { json: z.object({
2749
+ id: z.string(),
2750
+ accepted: z.boolean()
2751
+ }) },
2752
+ run: async ({ parsed, io }) => {
2753
+ const id = String(parsed.values.id);
2754
+ const message = String(parsed.values.message ?? "").trim();
2755
+ if (!message) {
2756
+ await io.stderr("demi agent steer: message must not be empty\n");
2757
+ return { exitCode: 1 };
2758
+ }
2759
+ const job = this.jobs.get(id);
2760
+ if (!job) {
2761
+ await io.stderr(`demi agent steer: no running subagent "${id}"\n`);
2762
+ return { exitCode: 1 };
2763
+ }
2764
+ await this.steerChild(job, message);
2765
+ await io.stdout(parsed.json ? `${JSON.stringify({
2766
+ id,
2767
+ accepted: true
2768
+ })}\n` : `steered ${id}\n`);
2769
+ return { exitCode: 0 };
2770
+ }
2771
+ },
2772
+ {
2773
+ name: "abort",
2774
+ summary: "Abort a running child and its subtree. Siblings are untouched.",
2775
+ input: { id: z.string().describe("subagentId from spawn stderr") },
2776
+ positionals: ["id"],
2777
+ output: { json: z.object({
2778
+ id: z.string(),
2779
+ aborted: z.boolean()
2780
+ }) },
2781
+ run: async ({ parsed, io }) => {
2782
+ const id = String(parsed.values.id);
2783
+ if (!this.jobs.has(id)) {
2784
+ await io.stderr(`demi agent abort: no running subagent "${id}"\n`);
2785
+ return { exitCode: 1 };
2786
+ }
2787
+ await this.abortSubtree(id);
2788
+ await io.stdout(parsed.json ? `${JSON.stringify({
2789
+ id,
2790
+ aborted: true
2791
+ })}\n` : `aborted ${id}\n`);
2792
+ return { exitCode: 0 };
2793
+ }
2794
+ },
2795
+ {
2796
+ name: "list",
2797
+ summary: "Snapshot roster of this session's running children (finished children are absent). One line per child with ages relative to now. A read, not a wait — not for polling loops.",
2798
+ output: { json: z.object({ agents: z.array(z.unknown()) }) },
2799
+ run: async ({ parsed, io }) => {
2800
+ const jobs = [...this.jobs.values()];
2801
+ if (parsed.json) {
2802
+ await io.stdout(`${JSON.stringify({ agents: jobs.map((job) => this.snapshot(job, false)) })}\n`);
2803
+ return { exitCode: 0 };
2804
+ }
2805
+ if (jobs.length === 0) {
2806
+ await io.stdout("no running subagents\n");
2807
+ return { exitCode: 0 };
2808
+ }
2809
+ for (const job of jobs) await io.stdout(`${this.renderListLine(job)}\n`);
2810
+ return { exitCode: 0 };
2811
+ }
2812
+ },
2813
+ {
2814
+ name: "show",
2815
+ summary: "Bounded snapshot of one running child: execution state, recent tool titles with durations, last assistant text. Every duration is relative to now — use the ages to tell motion from stall. Omits tool outputs, file contents, and older turns. A read, not a wait — not for polling loops.",
2816
+ input: { id: z.string().describe("subagentId from spawn stderr") },
2817
+ positionals: ["id"],
2818
+ output: { json: z.object({ agent: z.unknown() }) },
2819
+ run: async ({ parsed, io }) => {
2820
+ const id = String(parsed.values.id);
2821
+ const job = this.jobs.get(id);
2822
+ if (!job) {
2823
+ await io.stderr(`demi agent show: no running subagent "${id}"\n`);
2824
+ return { exitCode: 1 };
2825
+ }
2826
+ if (parsed.json) await io.stdout(`${JSON.stringify({ agent: this.snapshot(job, true) })}\n`);
2827
+ else await io.stdout(this.renderShow(job));
2828
+ return { exitCode: 0 };
2829
+ }
2830
+ }
2831
+ ]
2832
+ };
2833
+ }
2834
+ hasShell(shellId) {
2835
+ return this.environmentScopeForShell(shellId) !== null;
2836
+ }
2837
+ /** Resolves the child scope owning a shell, for the command bridge dispatch. */
2838
+ environmentScopeForShell(shellId) {
2839
+ for (const job of this.jobs.values()) for (const environment of job.environments.values()) if (environment.getShell(shellId)) return {
2840
+ environment,
2841
+ commandNames: new Set(job.commandNames),
2842
+ agentSessionId: job.id
2843
+ };
2844
+ return null;
2845
+ }
2846
+ /** Re-emits `subagent started` + transcript reset for every running child (transcript resync). */
2847
+ replay() {
2848
+ for (const job of this.jobs.values()) {
2849
+ this.options.emit({
2850
+ type: "subagent",
2851
+ event: "started",
2852
+ job: this.wireJob(job)
2853
+ });
2854
+ const transcript = job.session.transcript();
2855
+ this.options.emit({
2856
+ type: "subagent_transcript_reset",
2857
+ subagentId: job.id,
2858
+ blocks: structuredClone(transcript.blocks),
2859
+ revision: transcript.revision
2860
+ });
2861
+ }
2862
+ }
2863
+ async dispose() {
2864
+ this.isDisposed = true;
2865
+ for (const job of [...this.jobs.values()]) await this.closeJob(job, "aborted");
2866
+ }
2867
+ async abortSubtree(id) {
2868
+ const job = this.jobs.get(id);
2869
+ if (!job) return;
2870
+ await this.closeJob(job, "aborted");
2871
+ }
2872
+ async spawn(input) {
2873
+ const parent = this.parentSession;
2874
+ if (!parent) throw new Error("subagent supervisor has no parent session");
2875
+ if (this.isDisposed) throw new Error("parent session is closing");
2876
+ if (this.jobs.size >= 8) throw new Error(`at most 8 running subagents per session; abort one or wait for a result`);
2877
+ const profile = this.resolveProfile(input.profileName);
2878
+ const id = createId();
2879
+ const metadata = parent.actionMetadata();
2880
+ const agentNode = this.createChildAgentNode(id, input.description);
2881
+ const commands = injectSubagentCommand(profile.commands ? profile.commands([...this.options.parentCommands]) : [...this.options.parentCommands], agentNode);
2882
+ const commandRegistry = new CommandRegistry();
2883
+ for (const command of commands) commandRegistry.register(command);
2884
+ const commandNames = commandRegistry.list().map((command) => command.name);
2885
+ const commandsPrompt = commandRegistry.renderHelp();
2886
+ let settleClosed;
2887
+ const closed = new Promise((resolve) => {
2888
+ settleClosed = resolve;
2889
+ });
2890
+ const job = {
2891
+ id,
2892
+ description: input.description,
2893
+ profile,
2894
+ profileName: input.profileName ?? (this.options.profiles ? profile.name : null),
2895
+ metadata,
2896
+ session: null,
2897
+ commandRegistry,
2898
+ commandNames,
2899
+ environments: /* @__PURE__ */ new Map(),
2900
+ pendingEnvironments: /* @__PURE__ */ new Map(),
2901
+ readonlyHosts: /* @__PURE__ */ new WeakMap(),
2902
+ spawnedAt: Date.now(),
2903
+ lastEventAt: Date.now(),
2904
+ tools: [],
2905
+ lastAssistantTextAt: null,
2906
+ phase: "running",
2907
+ failure: null,
2908
+ unsubscribe: noop,
2909
+ closed,
2910
+ settleClosed,
2911
+ isClosing: false
2912
+ };
2913
+ const agent = this.options.agent;
2914
+ const preamble = this.subagentPreamble(id);
2915
+ const runtime = {
2916
+ harnessName: agent.name,
2917
+ initialState: () => agent.initialState(),
2918
+ systemPrompt: (ctx) => (profile.systemPrompt ?? agent.systemPrompt).call(agent, {
2919
+ ...ctx,
2920
+ commandsPrompt
2921
+ }),
2922
+ preamble: async (ctx) => {
2923
+ const inheritedPreamble = profile.systemPrompt ? null : await agent.preamble?.(ctx) ?? null;
2924
+ return inheritedPreamble ? `${inheritedPreamble}\n\n${preamble}` : preamble;
2925
+ },
2926
+ tools: () => createStandardAgentTools({
2927
+ environment: (ctx) => this.childEnvironment(job, ctx),
2928
+ scheduleYield: (ctx, durationMs) => job.session.scheduleYieldWakeup(durationMs, ctx.metadata)
2929
+ })
2930
+ };
2931
+ job.session = new AgentSession({
2932
+ provider: parent.cloneProviderRuntime(),
2933
+ model: profile.model ?? structuredClone(parent.modelSelection),
2934
+ cwd: this.options.cwd,
2935
+ runtime,
2936
+ state: agent.initialState()
2937
+ }, {
2938
+ agentSessionId: id,
2939
+ ...this.options.sessionOptions
2940
+ });
2941
+ job.unsubscribe = job.session.subscribe((event) => {
2942
+ if (event.type !== "transcript_changed") return;
2943
+ this.recordTelemetry(job, event.patches);
2944
+ this.options.emit({
2945
+ type: "subagent_transcript_patch",
2946
+ subagentId: id,
2947
+ patches: event.patches,
2948
+ revision: event.revision
2949
+ });
2950
+ });
2951
+ this.jobs.set(id, job);
2952
+ this.options.emit({
2953
+ type: "subagent",
2954
+ event: "started",
2955
+ job: this.wireJob(job)
2956
+ });
2957
+ this.options.emit({
2958
+ type: "subagent_transcript_reset",
2959
+ subagentId: id,
2960
+ blocks: [],
2961
+ revision: 0
2962
+ });
2963
+ this.watchChild(job, input.prompt);
2964
+ return job;
2965
+ }
2966
+ /** Delivers one steer to a child: mid-turn as a steer, idle as a new user turn. */
2967
+ async steerChild(job, message) {
2968
+ const content = [{
2969
+ type: "text",
2970
+ text: message
2971
+ }];
2972
+ if (job.session.phase() !== "idle") try {
2973
+ await job.session.steer(content);
2974
+ return;
2975
+ } catch {}
2976
+ this.watchTurn(job, job.session.send(content));
2977
+ }
2978
+ async pumpStdinSteers(id, stdinStream) {
2979
+ try {
2980
+ for await (const chunk of stdinStream) {
2981
+ const job = this.jobs.get(id);
2982
+ if (!job) return;
2983
+ const message = decodeUtf8(chunk).trim();
2984
+ if (message) await this.steerChild(job, message);
2985
+ }
2986
+ } catch {}
2987
+ }
2988
+ watchChild(job, prompt) {
2989
+ const opening = job.session.send([{
2990
+ type: "text",
2991
+ text: prompt
2992
+ }], job.metadata ? { metadata: job.metadata } : {});
2993
+ this.watchTurn(job, opening);
2994
+ }
2995
+ /** Tracks one child turn to session quiescence, then closes the job with its outcome. */
2996
+ async watchTurn(job, turn) {
2997
+ try {
2998
+ await turn;
2999
+ } catch (error) {
3000
+ job.failure = errorMessage(error);
3001
+ await this.closeJob(job, "error");
3002
+ return;
3003
+ }
3004
+ await this.waitForQuiescence(job);
3005
+ if (job.phase === "running" && !job.isClosing) await this.closeJob(job, "completed");
3006
+ }
3007
+ /** Resolves when the child is idle with no pending yield wakeups (or the job closed). */
3008
+ async waitForQuiescence(job) {
3009
+ while (!job.isClosing && (job.session.phase() !== "idle" || job.session.hasPendingYields())) await new Promise((resolve) => {
3010
+ let settled = false;
3011
+ const finish = () => {
3012
+ if (settled) return;
3013
+ settled = true;
3014
+ unsubscribe();
3015
+ resolve();
3016
+ };
3017
+ const unsubscribe = job.session.subscribe((event) => {
3018
+ if (event.type === "phase_changed") finish();
3019
+ });
3020
+ job.closed.then(finish);
3021
+ });
3022
+ }
3023
+ async closeJob(job, phase) {
3024
+ if (job.isClosing) return;
3025
+ job.isClosing = true;
3026
+ job.phase = phase;
3027
+ job.unsubscribe();
3028
+ this.jobs.delete(job.id);
3029
+ const result = phase === "completed" ? boundedResultText(lastAssistantText(job.session.transcript().blocks)) : void 0;
3030
+ await job.session.dispose().catch(noop);
3031
+ const environments = /* @__PURE__ */ new Set([...job.environments.values()]);
3032
+ for (const pending of job.pendingEnvironments.values()) {
3033
+ const environment = await pending.catch(() => null);
3034
+ if (environment) environments.add(environment);
3035
+ }
3036
+ for (const environment of environments) await environment.disposeAllShells().catch(noop);
3037
+ const close = {
3038
+ phase,
3039
+ ...result !== void 0 ? { result } : {},
3040
+ ...job.failure ? { failure: job.failure } : {}
3041
+ };
3042
+ this.options.emit({
3043
+ type: "subagent",
3044
+ event: "closed",
3045
+ job: this.wireJob(job, result)
3046
+ });
3047
+ job.settleClosed(close);
3048
+ this.notifyIdleParent(job, close);
3049
+ }
3050
+ /** Wakes an idle parent with a user send; a parent blocked in the spawn gets the tool result instead. */
3051
+ notifyIdleParent(job, close) {
3052
+ if (this.isDisposed || !this.options.notifyParentOnIdle) return;
3053
+ const parent = this.parentSession;
3054
+ if (!parent || parent.phase() !== "idle") return;
3055
+ const label = `subagent ${job.id}${job.description ? ` — ${job.description}` : ""}`;
3056
+ const body = close.phase === "completed" ? `[${label}] completed.\nResult:\n${close.result || "(empty)"}` : close.phase === "aborted" ? `[${label}] aborted.` : `[${label}] failed: ${close.failure ?? "unknown error"}`;
3057
+ parent.send([{
3058
+ type: "text",
3059
+ text: body
3060
+ }], job.metadata ? { metadata: job.metadata } : {}).catch(noop);
3061
+ }
3062
+ createChildAgentNode(childId, description) {
3063
+ return {
3064
+ name: "agent",
3065
+ summary: "Subagent bridge to the parent session.",
3066
+ subcommands: [{
3067
+ name: "send-parent",
3068
+ summary: "Send an interim user message to the parent session. The parent sees it only when it is not blocked waiting on this session. Your result is still the last assistant text when this session ends, not this message.",
3069
+ input: { message: z.string().optional().describe("Message body; positional, or stdin/heredoc when omitted.") },
3070
+ positionals: ["message"],
3071
+ stdinField: "message",
3072
+ output: { json: z.object({ accepted: z.boolean() }) },
3073
+ run: async ({ parsed, io }) => {
3074
+ const message = String(parsed.values.message ?? "").trim();
3075
+ if (!message) {
3076
+ await io.stderr("demi agent send-parent: message must not be empty\n");
3077
+ return { exitCode: 1 };
3078
+ }
3079
+ this.deliverToParent(childId, description, message);
3080
+ await io.stdout(parsed.json ? `${JSON.stringify({ accepted: true })}\n` : "sent\n");
3081
+ return { exitCode: 0 };
3082
+ }
3083
+ }]
3084
+ };
3085
+ }
3086
+ deliverToParent(childId, description, message) {
3087
+ const parent = this.parentSession;
3088
+ if (!parent || this.isDisposed) return;
3089
+ const content = [{
3090
+ type: "text",
3091
+ text: `[subagent ${childId}${description ? ` — ${description}` : ""}] ${message}`
3092
+ }];
3093
+ const metadata = this.jobs.get(childId)?.metadata ?? null;
3094
+ const sendOptions = metadata ? { metadata } : {};
3095
+ if (parent.phase() !== "idle") {
3096
+ parent.steer(content).catch(() => {
3097
+ parent.send(content, sendOptions).catch(noop);
3098
+ });
3099
+ return;
3100
+ }
3101
+ parent.send(content, sendOptions).catch(noop);
3102
+ }
3103
+ async childEnvironment(job, ctx) {
3104
+ const resolved = await this.options.agent.host({
3105
+ agentSessionId: job.id,
3106
+ state: ctx.state,
3107
+ cwd: this.options.cwd,
3108
+ metadata: ctx.metadata
3109
+ });
3110
+ let host = resolved;
3111
+ if (job.profile.readonly) {
3112
+ const wrapped = job.readonlyHosts.get(resolved) ?? createReadonlyHost(resolved);
3113
+ job.readonlyHosts.set(resolved, wrapped);
3114
+ host = wrapped;
3115
+ }
3116
+ const existing = job.environments.get(host);
3117
+ if (existing) return existing;
3118
+ const pending = job.pendingEnvironments.get(host);
3119
+ if (pending) return pending;
3120
+ const creation = this.createChildEnvironment(job, host);
3121
+ job.pendingEnvironments.set(host, creation);
3122
+ try {
3123
+ const environment = await creation;
3124
+ job.environments.set(host, environment);
3125
+ return environment;
3126
+ } finally {
3127
+ job.pendingEnvironments.delete(host);
3128
+ }
3129
+ }
3130
+ async createChildEnvironment(job, host) {
3131
+ const prepared = this.options.prepareShell && !job.profile.readonly ? await this.options.prepareShell({
3132
+ agentSessionId: job.id,
3133
+ host,
3134
+ commandNames: job.commandNames,
3135
+ shell: this.options.shellOptions
3136
+ }) : this.options.shellOptions;
3137
+ return new BashEnvironment({
3138
+ ...prepared,
3139
+ initialEnv: {
3140
+ ...prepared.initialEnv,
3141
+ DEMI_SUBAGENT_ID: job.id,
3142
+ DEMI_PARENT_SESSION_ID: this.parentSession?.id() ?? "",
3143
+ DEMI_SUBAGENT_DEPTH: "1"
3144
+ },
3145
+ host,
3146
+ commands: job.commandRegistry
3147
+ });
3148
+ }
3149
+ resolveProfile(name) {
3150
+ const profiles = this.options.profiles;
3151
+ const implicitDefault = {
3152
+ name: "default",
3153
+ description: "Inherits the parent harness, model, Host, and commands."
3154
+ };
3155
+ if (!profiles || profiles.length === 0) {
3156
+ if (name !== void 0 && name !== "default") throw new Error(`unknown profile "${name}" (available: default)`);
3157
+ return implicitDefault;
3158
+ }
3159
+ const target = name ?? "default";
3160
+ const profile = profiles.find((candidate) => candidate.name === target);
3161
+ if (profile) return profile;
3162
+ if (name === void 0) return implicitDefault;
3163
+ throw new Error(`unknown profile "${name}" (available: ${this.configuredProfileNames().join(", ")})`);
3164
+ }
3165
+ configuredProfileNames() {
3166
+ const names = (this.options.profiles ?? []).map((profile) => profile.name);
3167
+ return names.includes("default") ? names : ["default", ...names];
3168
+ }
3169
+ subagentPreamble(childId) {
3170
+ return [
3171
+ `You are a subagent: an isolated child agent session (id ${childId}) spawned by a parent agent session. Your transcript starts empty; the task brief in the first user message is your entire context.`,
3172
+ "When you end your turn with no scheduled wakeups, the session ends and your last assistant text is returned to the parent as the result. Write it for the parent agent, in the shape the task brief asked for.",
3173
+ "`demi agent send-parent <message>` sends an interim user message to the parent; it is seen only when the parent is not blocked waiting on this session.",
3174
+ "You are not talking to the product user; do not address them."
3175
+ ].join("\n");
3176
+ }
3177
+ recordTelemetry(job, patches) {
3178
+ const now = Date.now();
3179
+ for (const patch of patches) {
3180
+ if (patch.op === "add") {
3181
+ const block = patch.value;
3182
+ if (block.type === "tool_call") {
3183
+ job.tools.push({
3184
+ toolUseId: block.toolUseId,
3185
+ title: toolCallTitle(block),
3186
+ startedAt: now,
3187
+ endedAt: null,
3188
+ status: "executing"
3189
+ });
3190
+ trimToolRecords(job.tools);
3191
+ job.lastEventAt = now;
3192
+ } else if (block.type === "text") {
3193
+ job.lastAssistantTextAt = now;
3194
+ job.lastEventAt = now;
3195
+ }
3196
+ continue;
3197
+ }
3198
+ if (patch.op === "append_text") {
3199
+ if (job.session.transcript().blocks[patch.path[1]]?.type === "text") {
3200
+ job.lastAssistantTextAt = now;
3201
+ job.lastEventAt = now;
3202
+ }
3203
+ continue;
3204
+ }
3205
+ if (patch.op === "replace_block") {
3206
+ const block = patch.value;
3207
+ if (block.type !== "tool_call" || block.status === "executing") continue;
3208
+ const record = job.tools.find((tool) => tool.toolUseId === block.toolUseId && tool.endedAt === null);
3209
+ if (record) {
3210
+ record.endedAt = now;
3211
+ record.status = block.status === "error" ? "error" : "completed";
3212
+ }
3213
+ job.lastEventAt = now;
3214
+ }
3215
+ }
3216
+ }
3217
+ executionOf(job) {
3218
+ const phase = job.session.phase();
3219
+ if (phase === "compacting") return "compacting";
3220
+ if (phase === "idle") return job.session.hasPendingYields() ? "pending_yield" : "idle";
3221
+ return job.session.turnPhase() ?? "provider_streaming";
3222
+ }
3223
+ activityOf(job, execution) {
3224
+ const inflight = [...job.tools].reverse().find((tool) => tool.endedAt === null);
3225
+ if (execution === "tool_executing" && inflight) return inflight.title;
3226
+ if (execution === "provider_streaming") return "streaming";
3227
+ return execution;
3228
+ }
3229
+ executionForMs(job, execution, now) {
3230
+ if (execution === "tool_executing") {
3231
+ const inflight = [...job.tools].reverse().find((tool) => tool.endedAt === null);
3232
+ if (inflight) return now - inflight.startedAt;
3233
+ }
3234
+ return now - job.lastEventAt;
3235
+ }
3236
+ snapshot(job, detailed) {
3237
+ const now = Date.now();
3238
+ const execution = this.executionOf(job);
3239
+ const base = {
3240
+ subagentId: job.id,
3241
+ description: job.description,
3242
+ profile: job.profileName,
3243
+ phase: job.phase,
3244
+ elapsedMs: now - job.spawnedAt,
3245
+ lastEventMs: now - job.lastEventAt,
3246
+ execution,
3247
+ activity: this.activityOf(job, execution)
3248
+ };
3249
+ if (!detailed) return base;
3250
+ const text = lastAssistantText(job.session.transcript().blocks);
3251
+ return {
3252
+ ...base,
3253
+ executionForMs: this.executionForMs(job, execution, now),
3254
+ tools: job.tools.slice(-8).map((tool) => ({
3255
+ title: tool.title,
3256
+ status: tool.status,
3257
+ durationMs: (tool.endedAt ?? now) - tool.startedAt,
3258
+ endedAgoMs: tool.endedAt === null ? null : now - tool.endedAt
3259
+ })),
3260
+ lastAssistantText: boundedResultText(text),
3261
+ lastAssistantTextAgoMs: job.lastAssistantTextAt === null ? null : now - job.lastAssistantTextAt
3262
+ };
3263
+ }
3264
+ renderListLine(job) {
3265
+ const now = Date.now();
3266
+ const execution = this.executionOf(job);
3267
+ return [
3268
+ job.id,
3269
+ job.phase,
3270
+ `up ${formatDuration(now - job.spawnedAt)}`,
3271
+ `last-event ${formatDuration(now - job.lastEventAt)} ago`,
3272
+ `profile=${job.profileName ?? "default"}`,
3273
+ job.description ? `"${job.description}"` : "(no description)",
3274
+ `execution=${execution}`,
3275
+ `activity=${this.activityOf(job, execution)}`
3276
+ ].join(" ");
3277
+ }
3278
+ renderShow(job) {
3279
+ const now = Date.now();
3280
+ const execution = this.executionOf(job);
3281
+ const lines = [
3282
+ `id: ${job.id}`,
3283
+ `description: ${job.description || "(none)"}`,
3284
+ `profile: ${job.profileName ?? "default"}`,
3285
+ `phase: ${job.phase}`,
3286
+ `elapsed: ${formatDuration(now - job.spawnedAt)}`,
3287
+ `execution: ${execution} (for ${formatDuration(this.executionForMs(job, execution, now))})`,
3288
+ `last-event: ${formatDuration(now - job.lastEventAt)} ago`,
3289
+ `activity: ${this.activityOf(job, execution)}`
3290
+ ];
3291
+ const recent = job.tools.slice(-8);
3292
+ if (recent.length > 0) {
3293
+ lines.push(`recent tool calls (last ${recent.length}):`);
3294
+ for (const tool of recent) if (tool.endedAt === null) lines.push(` [executing for ${formatDuration(now - tool.startedAt)}] ${tool.title}`);
3295
+ else lines.push(` [${tool.status} in ${formatDuration(tool.endedAt - tool.startedAt)}, ended ${formatDuration(now - tool.endedAt)} ago] ${tool.title}`);
3296
+ }
3297
+ const text = boundedResultText(lastAssistantText(job.session.transcript().blocks));
3298
+ if (text && job.lastAssistantTextAt !== null) {
3299
+ lines.push(`last assistant text (${formatDuration(now - job.lastAssistantTextAt)} ago):`);
3300
+ lines.push(text);
3301
+ } else lines.push("last assistant text: (none yet)");
3302
+ return `${lines.join("\n")}\n`;
3303
+ }
3304
+ wireJob(job, result) {
3305
+ return {
3306
+ subagentId: job.id,
3307
+ parentSessionId: this.parentSession?.id() ?? "",
3308
+ description: job.description,
3309
+ profile: job.profileName,
3310
+ phase: job.phase,
3311
+ ...result !== void 0 ? { result } : {}
3312
+ };
3313
+ }
3314
+ };
3315
+ /**
3316
+ * Grafts the `agent` node under a `demi` root: onto an existing harness `demi`
3317
+ * tree, or as a new `demi` root when the harness has none.
3318
+ */
3319
+ function injectSubagentCommand(commands, agentNode) {
3320
+ const demiIndex = commands.findIndex((command) => command.name === "demi");
3321
+ if (demiIndex === -1) return [...commands, {
3322
+ name: "demi",
3323
+ summary: "Demi agent runtime commands.",
3324
+ subcommands: [agentNode]
3325
+ }];
3326
+ const demi = commands[demiIndex];
3327
+ const subcommands = [...(demi.subcommands ?? []).filter((command) => command.name !== "agent"), agentNode];
3328
+ const next = [...commands];
3329
+ next[demiIndex] = {
3330
+ ...demi,
3331
+ subcommands
3332
+ };
3333
+ return next;
3334
+ }
3335
+ /**
3336
+ * Host wrapper for read-only subagent profiles: filesystem mutation is
3337
+ * rejected outside the shell's own command-artifacts tree, and process spawn
3338
+ * is rejected outright (a real process cannot be write-restricted).
3339
+ *
3340
+ * Every facet delegates method by method: Host facets are class instances
3341
+ * whose methods live on the prototype, so object spread would drop them.
3342
+ */
3343
+ function createReadonlyHost(host) {
3344
+ const artifactsRoot = host.commandArtifactsDir.replace(/\/+$/, "");
3345
+ const isArtifactPath = (path, cwd) => {
3346
+ const resolved = path.startsWith("/") ? path : `${(cwd ?? host.defaultCwd).replace(/\/+$/, "")}/${path}`;
3347
+ return resolved === artifactsRoot || resolved.startsWith(`${artifactsRoot}/`);
3348
+ };
3349
+ const deny = (operation) => Promise.reject(/* @__PURE__ */ new Error(`read-only subagent: ${operation} is not permitted on this Host`));
3350
+ return {
3351
+ defaultCwd: host.defaultCwd,
3352
+ commandArtifactsDir: host.commandArtifactsDir,
3353
+ identity: host.identity,
3354
+ store: host.store,
3355
+ fs: {
3356
+ readFile: (path, options) => host.fs.readFile(path, options),
3357
+ exists: (path, options) => host.fs.exists(path, options),
3358
+ stat: (path, options) => host.fs.stat(path, options),
3359
+ lstat: (path, options) => host.fs.lstat(path, options),
3360
+ readdir: ((path, options) => host.fs.readdir(path, options)),
3361
+ readlink: (path, options) => host.fs.readlink(path, options),
3362
+ realpath: (path, options) => host.fs.realpath(path, options),
3363
+ writeFile: (path, data, options) => isArtifactPath(path, options?.cwd) ? host.fs.writeFile(path, data, options) : deny(`write ${path}`),
3364
+ appendFile: (path, data, options) => isArtifactPath(path, options?.cwd) ? host.fs.appendFile(path, data, options) : deny(`append ${path}`),
3365
+ mkdir: (path, options) => isArtifactPath(path, options?.cwd) ? host.fs.mkdir(path, options) : deny(`mkdir ${path}`),
3366
+ rm: (path) => deny(`rm ${path}`),
3367
+ cp: (path, destination, options) => isArtifactPath(destination, options?.cwd) ? host.fs.cp(path, destination, options) : deny(`cp to ${destination}`),
3368
+ mv: (path, destination) => deny(`mv ${path} ${destination}`),
3369
+ chmod: (path) => deny(`chmod ${path}`),
3370
+ symlink: (_target, path) => deny(`symlink ${path}`),
3371
+ link: (_existingPath, path) => deny(`link ${path}`),
3372
+ utimes: (path) => deny(`utimes ${path}`)
3373
+ },
3374
+ process: {
3375
+ openCwd: (path) => host.process.openCwd(path),
3376
+ spawn: () => deny("process spawn")
3377
+ }
3378
+ };
3379
+ }
3380
+ function trimToolRecords(tools) {
3381
+ while (tools.length > SHOW_RECENT_TOOLS) {
3382
+ const index = tools.findIndex((tool) => tool.endedAt !== null);
3383
+ if (index === -1) return;
3384
+ tools.splice(index, 1);
3385
+ }
3386
+ }
3387
+ function toolCallTitle(block) {
3388
+ try {
3389
+ const input = JSON.parse(block.input);
3390
+ if (typeof input.description === "string" && input.description.trim()) return input.description.trim();
3391
+ } catch {}
3392
+ return block.toolName;
3393
+ }
3394
+ function lastAssistantText(blocks) {
3395
+ for (let i = blocks.length - 1; i >= 0; i -= 1) {
3396
+ const block = blocks[i];
3397
+ if (block.type === "text") return block.text;
3398
+ }
3399
+ return "";
3400
+ }
3401
+ function boundedResultText(text) {
3402
+ return utf8Slice(text, 0, SUBAGENT_RESULT_MAX_BYTES);
3403
+ }
3404
+ function formatDuration(ms) {
3405
+ const totalSeconds = Math.max(0, Math.round(ms / 1e3));
3406
+ if (totalSeconds < 60) return `${totalSeconds}s`;
3407
+ const minutes = Math.floor(totalSeconds / 60);
3408
+ const seconds = totalSeconds % 60;
3409
+ if (minutes < 60) return seconds > 0 ? `${minutes}m${seconds}s` : `${minutes}m`;
3410
+ const hours = Math.floor(minutes / 60);
3411
+ const remMinutes = minutes % 60;
3412
+ return remMinutes > 0 ? `${hours}h${remMinutes}m` : `${hours}h`;
3413
+ }
3414
+ //#endregion
2638
3415
  //#region src/server.ts
2639
3416
  var RunCommandLineShellNotFoundError = class extends Error {
2640
3417
  shellId;
@@ -2670,6 +3447,7 @@ var AgentServer = class {
2670
3447
  shellOptions;
2671
3448
  sessionOptions;
2672
3449
  prepareShell;
3450
+ notifyParentOnIdle;
2673
3451
  bindings = /* @__PURE__ */ new Set();
2674
3452
  sessionOwnership = new SessionOwnershipRegistry();
2675
3453
  constructor(options) {
@@ -2678,6 +3456,7 @@ var AgentServer = class {
2678
3456
  this.shellOptions = options.shell ?? {};
2679
3457
  this.sessionOptions = options.session ?? {};
2680
3458
  this.prepareShell = options.prepareShell ?? null;
3459
+ this.notifyParentOnIdle = options.subagents?.notifyParentOnIdle ?? true;
2681
3460
  }
2682
3461
  client() {
2683
3462
  const transports = createInProcessTransportPair();
@@ -2692,6 +3471,7 @@ var AgentServer = class {
2692
3471
  shell: this.shellOptions,
2693
3472
  session: this.sessionOptions,
2694
3473
  prepareShell: this.prepareShell,
3474
+ notifyParentOnIdle: this.notifyParentOnIdle,
2695
3475
  sessions: this.sessionOwnership
2696
3476
  });
2697
3477
  this.bindings.add(binding);
@@ -2741,6 +3521,7 @@ var AgentTransportBindingImpl = class {
2741
3521
  shellOptions;
2742
3522
  sessionOptions;
2743
3523
  prepareShell;
3524
+ notifyParentOnIdle;
2744
3525
  sessions;
2745
3526
  session = null;
2746
3527
  currentAgent = null;
@@ -2751,6 +3532,7 @@ var AgentTransportBindingImpl = class {
2751
3532
  currentProviderId = null;
2752
3533
  currentSessionId = null;
2753
3534
  currentCommandNames = /* @__PURE__ */ new Set();
3535
+ supervisor = null;
2754
3536
  unsubscribeSession = null;
2755
3537
  unsubscribeTransport = null;
2756
3538
  closed = false;
@@ -2761,6 +3543,7 @@ var AgentTransportBindingImpl = class {
2761
3543
  this.shellOptions = options.shell ?? {};
2762
3544
  this.sessionOptions = options.session ?? {};
2763
3545
  this.prepareShell = options.prepareShell;
3546
+ this.notifyParentOnIdle = options.notifyParentOnIdle;
2764
3547
  this.sessions = options.sessions;
2765
3548
  this.unsubscribeTransport = this.transport.onFrame((frame) => {
2766
3549
  this.handleFrame(frame);
@@ -2928,6 +3711,7 @@ var AgentTransportBindingImpl = class {
2928
3711
  const session = this.sessionFor("sync_transcript");
2929
3712
  if (!session) return;
2930
3713
  this.sendTranscriptReset(session);
3714
+ this.supervisor?.replay();
2931
3715
  return;
2932
3716
  }
2933
3717
  case "close":
@@ -2965,7 +3749,20 @@ var AgentTransportBindingImpl = class {
2965
3749
  state,
2966
3750
  cwd: frame.cwd
2967
3751
  };
2968
- const commands = await agent.commands?.(harnessContext) ?? [];
3752
+ const harnessCommands = await agent.commands?.(harnessContext) ?? [];
3753
+ const profiles = await agent.agents?.(harnessContext) ?? null;
3754
+ const supervisor = new ChildSupervisor({
3755
+ agent,
3756
+ cwd: frame.cwd,
3757
+ profiles,
3758
+ parentCommands: harnessCommands,
3759
+ shellOptions: this.shellOptions,
3760
+ prepareShell: this.prepareShell,
3761
+ sessionOptions: this.sessionOptions,
3762
+ notifyParentOnIdle: this.notifyParentOnIdle,
3763
+ emit: (subagentFrame) => this.send(subagentFrame)
3764
+ });
3765
+ const commands = injectSubagentCommand(harnessCommands, supervisor.rootCommandNode());
2969
3766
  const commandRegistry = new CommandRegistry();
2970
3767
  for (const command of commands) commandRegistry.register(command);
2971
3768
  const commandNames = commandRegistry.list().map((command) => command.name);
@@ -3014,6 +3811,8 @@ var AgentTransportBindingImpl = class {
3014
3811
  });
3015
3812
  sessionRef = session;
3016
3813
  this.session = session;
3814
+ supervisor.attachParent(session);
3815
+ this.supervisor = supervisor;
3017
3816
  this.currentAgent = agent;
3018
3817
  this.currentCommandRegistry = commandRegistry;
3019
3818
  this.currentCwd = frame.cwd;
@@ -3107,6 +3906,7 @@ var AgentTransportBindingImpl = class {
3107
3906
  const agent = this.currentAgent;
3108
3907
  const cwd = this.currentCwd;
3109
3908
  try {
3909
+ if (this.supervisor) await this.supervisor.dispose();
3110
3910
  if (session) await session.dispose();
3111
3911
  const pendingEnvironments = await Promise.allSettled(this.pendingEnvironmentsByHost.values());
3112
3912
  const environments = new Set(this.environmentsByHost.values());
@@ -3122,6 +3922,7 @@ var AgentTransportBindingImpl = class {
3122
3922
  this.unsubscribeSession?.();
3123
3923
  this.unsubscribeSession = null;
3124
3924
  this.session = null;
3925
+ this.supervisor = null;
3125
3926
  this.currentAgent = null;
3126
3927
  this.environmentsByHost.clear();
3127
3928
  this.pendingEnvironmentsByHost.clear();
@@ -3170,10 +3971,15 @@ var AgentTransportBindingImpl = class {
3170
3971
  }
3171
3972
  /** Backs `AgentServer.runCommandLine` — see its doc comment for the contract. */
3172
3973
  async runCommandLine(shellId, name, args, opts) {
3173
- const environment = this.environmentForShell(shellId);
3174
- const agentSessionId = this.currentSessionId;
3175
- if (!environment || !agentSessionId) throw new Error("runCommandLine: session has no active shell environment");
3176
- if (!this.currentCommandNames.has(name)) throw new RunCommandLineCommandNotRegisteredError(name);
3974
+ const parentEnvironment = this.environmentForShell(shellId);
3975
+ const scope = parentEnvironment && this.currentSessionId ? {
3976
+ environment: parentEnvironment,
3977
+ commandNames: this.currentCommandNames,
3978
+ agentSessionId: this.currentSessionId
3979
+ } : this.supervisor?.environmentScopeForShell(shellId) ?? null;
3980
+ if (!scope) throw new Error("runCommandLine: session has no active shell environment");
3981
+ const { environment, agentSessionId } = scope;
3982
+ if (!scope.commandNames.has(name)) throw new RunCommandLineCommandNotRegisteredError(name);
3177
3983
  const words = [name, ...args].map(shellQuote).join(" ");
3178
3984
  let script = words;
3179
3985
  if (opts.stdin.length > 0) {
@@ -3213,7 +4019,7 @@ var AgentTransportBindingImpl = class {
3213
4019
  }
3214
4020
  }
3215
4021
  hasShell(shellId) {
3216
- return this.environmentForShell(shellId) !== null;
4022
+ return this.environmentForShell(shellId) !== null || (this.supervisor?.hasShell(shellId) ?? false);
3217
4023
  }
3218
4024
  async resolveEnvironment(ctx, handle) {
3219
4025
  const agent = this.currentAgent;
@@ -3441,4 +4247,4 @@ function createProviderMap(providers) {
3441
4247
  return map;
3442
4248
  }
3443
4249
  //#endregion
3444
- export { AgentClient, AgentServer, AgentSession, DEFAULT_MAX_MEDIA_BYTES, DEFAULT_TURN_RETRY_POLICY, ProviderStreamError, RunCommandLineCommandNotRegisteredError, RunCommandLineShellNotFoundError, RunCommandLineTimeoutError, SHELL_VIEW_MAX_CHARS, TranscriptLog, applyTranscriptPatches, cloneBlocks, createInProcessTransportPair, createStandardAgentTools, createWebSocketClientTransport, createWebSocketServerTransport, estimateTranscriptBlockTokens, findResumePoint, finishShellToolResult, isContextLengthExceeded, isRetryableCode, resolveRetryPolicy, retryDelayMs, shellCommandHandleRequired, shellPreviewBudgetTokens, toShellToolResult };
4250
+ export { AgentClient, AgentServer, AgentSession, ChildSupervisor, DEFAULT_MAX_MEDIA_BYTES, DEFAULT_TURN_RETRY_POLICY, MAX_LIVE_SUBAGENTS, ProviderStreamError, RunCommandLineCommandNotRegisteredError, RunCommandLineShellNotFoundError, RunCommandLineTimeoutError, SHELL_VIEW_MAX_CHARS, SUBAGENT_RESULT_MAX_BYTES, TranscriptLog, applyTranscriptPatches, cloneBlocks, createInProcessTransportPair, createReadonlyHost, createStandardAgentTools, createWebSocketClientTransport, createWebSocketServerTransport, estimateTranscriptBlockTokens, findResumePoint, finishShellToolResult, injectSubagentCommand, isContextLengthExceeded, isRetryableCode, resolveRetryPolicy, retryDelayMs, shellCommandHandleRequired, shellPreviewBudgetTokens, toShellToolResult };
@@ -1,4 +1,4 @@
1
- import { n as AgentServerTransport, t as AgentClientTransport } from "./transport-DtBCCS_A.mjs";
1
+ import { n as AgentServerTransport, t as AgentClientTransport } from "./transport-BRqZoaeo.mjs";
2
2
  import { Readable, Writable } from "node:stream";
3
3
  //#region src/stdio-transport.d.ts
4
4
  declare function createStdioClientTransport(readable: Readable, writable: Writable): AgentClientTransport;
@@ -180,12 +180,33 @@ interface AgentHostContext<State> extends AgentHarnessContext<State> {
180
180
  agentSessionId: string;
181
181
  metadata: AgentMetadata | null;
182
182
  }
183
+ /**
184
+ * A named subagent configuration. Every field is an override on the parent's
185
+ * setup: omitted fields inherit the parent harness (system prompt), the
186
+ * parent's registered commands, the parent Host, and the parent model.
187
+ */
188
+ interface SubagentProfile<State = unknown> {
189
+ name: string;
190
+ /** Shown in the spawn command help so the parent model can pick a profile. */
191
+ description: string;
192
+ systemPrompt?(ctx: AgentSystemPromptContext<State>): Promise<string> | string;
193
+ /** Derives the child's registered commands from the parent's list. */
194
+ commands?(parentCommands: Command[]): Command[];
195
+ /** Reject filesystem writes and process spawns on the child's Host. */
196
+ readonly?: boolean;
197
+ model?: ModelSelection;
198
+ }
183
199
  interface AgentHarness<State = unknown> {
184
200
  name: string;
185
201
  initialState(): State;
186
202
  /** Return the same Host object for calls that target the same execution environment. */
187
203
  host(ctx: AgentHarnessContext<State> | AgentHostContext<State>): Host | Promise<Host>;
188
204
  commands?(ctx: AgentHarnessContext<State>): Promise<Command[]> | Command[];
205
+ /**
206
+ * Named subagent profiles for `demi agent`. Omitted: one implicit profile
207
+ * named `default` that fully inherits the parent's setup.
208
+ */
209
+ agents?(ctx: AgentHarnessContext<State>): Promise<SubagentProfile<State>[]> | SubagentProfile<State>[];
189
210
  systemPrompt(ctx: AgentSystemPromptContext<State>): Promise<string> | string;
190
211
  preamble?(ctx: AgentPromptContext<State>): Promise<string | null> | string | null;
191
212
  resolveReferences?(ctx: AgentReferenceResolveContext<State>, content: UserContentBlock[]): Promise<UserContentBlock[]> | UserContentBlock[];
@@ -370,6 +391,16 @@ interface ConversationSummary {
370
391
  createdAt: string;
371
392
  updatedAt: string;
372
393
  }
394
+ /** One child agent session as seen on the parent connection. */
395
+ interface SubagentJob {
396
+ subagentId: string;
397
+ parentSessionId: string;
398
+ description: string;
399
+ profile: string | null;
400
+ phase: 'running' | 'completed' | 'aborted' | 'error';
401
+ /** Present on `closed`: the child's last assistant text, at most 32 KiB. */
402
+ result?: string;
403
+ }
373
404
  type ClientFrame = {
374
405
  type: 'open';
375
406
  provider: ProviderSelection;
@@ -489,6 +520,20 @@ type ServerFrame = {
489
520
  message: string;
490
521
  code?: string;
491
522
  diagnostics?: ProviderErrorDiagnostics;
523
+ } | {
524
+ type: 'subagent';
525
+ event: 'started' | 'closed';
526
+ job: SubagentJob;
527
+ } | {
528
+ type: 'subagent_transcript_reset';
529
+ subagentId: string;
530
+ blocks: Block[];
531
+ revision: number;
532
+ } | {
533
+ type: 'subagent_transcript_patch';
534
+ subagentId: string;
535
+ patches: TranscriptPatch[];
536
+ revision: number;
492
537
  } | {
493
538
  type: 'closed';
494
539
  };
@@ -575,6 +620,18 @@ type ClientSessionEvent = {
575
620
  message: string;
576
621
  code?: string;
577
622
  diagnostics?: ProviderErrorDiagnostics;
623
+ } | {
624
+ type: 'subagent';
625
+ event: 'started' | 'closed';
626
+ job: SubagentJob;
627
+ } | {
628
+ type: 'subagent_transcript_reset';
629
+ subagentId: string;
630
+ blocks: Block[];
631
+ } | {
632
+ type: 'subagent_transcript_patch';
633
+ subagentId: string;
634
+ patches: TranscriptPatch[];
578
635
  } | {
579
636
  type: 'opened';
580
637
  } | {
@@ -595,4 +652,4 @@ interface InProcessTransportPair {
595
652
  }
596
653
  declare function createInProcessTransportPair(): InProcessTransportPair;
597
654
  //#endregion
598
- export { AgentTool as A, TranscriptLog as B, AgentSessionCheckpoint as C, AgentSessionRestoreParams as D, AgentSessionParams as E, ModelSwitchApply as F, isRetryableCode as G, estimateTranscriptBlockTokens as H, SessionEvent as I, resolveRetryPolicy as K, SessionEventListener as L, AgentToolInvokeContext as M, AgentToolInvokeResult as N, AgentSessionStore as O, ExternalMutationReservation as P, CompactionWindow as R, AgentReferenceResolveContext as S, AgentSessionOptions as T, DEFAULT_TURN_RETRY_POLICY as U, TranscriptOptions as V, TurnRetryPolicy as W, AgentHarnessRuntime as _, createInProcessTransportPair as a, AgentMetadata as b, ConversationSummary as c, TranscriptPatch as d, AbortResult as f, AgentHarnessContext as g, AgentHarness as h, InProcessTransportPair as i, AgentToolContext as j, AgentSystemPromptContext as k, ServerFrame as l, AgentDisposeContext as m, AgentServerTransport as n, ClientFrame as o, AbortTarget as p, retryDelayMs as q, AgentTransport as r, ClientSessionEvent as s, AgentClientTransport as t, ShellCommandStatusLike as u, AgentHostContext as v, AgentSessionCloneParams as w, AgentPromptContext as x, AgentLifecycleEvent as y, DrainedTranscriptPatches as z };
655
+ export { AgentSystemPromptContext as A, CompactionWindow as B, AgentReferenceResolveContext as C, AgentSessionParams as D, AgentSessionOptions as E, ExternalMutationReservation as F, DEFAULT_TURN_RETRY_POLICY as G, TranscriptLog as H, ModelSwitchApply as I, resolveRetryPolicy as J, TurnRetryPolicy as K, SessionEvent as L, AgentToolContext as M, AgentToolInvokeContext as N, AgentSessionRestoreParams as O, AgentToolInvokeResult as P, SessionEventListener as R, AgentPromptContext as S, AgentSessionCloneParams as T, TranscriptOptions as U, DrainedTranscriptPatches as V, estimateTranscriptBlockTokens as W, retryDelayMs as Y, AgentHarnessContext as _, createInProcessTransportPair as a, AgentLifecycleEvent as b, ConversationSummary as c, SubagentJob as d, TranscriptPatch as f, AgentHarness as g, AgentDisposeContext as h, InProcessTransportPair as i, AgentTool as j, AgentSessionStore as k, ServerFrame as l, AbortTarget as m, AgentServerTransport as n, ClientFrame as o, AbortResult as p, isRetryableCode as q, AgentTransport as r, ClientSessionEvent as s, AgentClientTransport as t, ShellCommandStatusLike as u, AgentHarnessRuntime as v, AgentSessionCheckpoint as w, AgentMetadata as x, AgentHostContext as y, SubagentProfile as z };
@@ -1,4 +1,4 @@
1
- import { F as ModelSwitchApply, b as AgentMetadata, c as ConversationSummary, f as AbortResult, n as AgentServerTransport, s as ClientSessionEvent, t as AgentClientTransport } from "./transport-DtBCCS_A.mjs";
1
+ import { I as ModelSwitchApply, c as ConversationSummary, n as AgentServerTransport, p as AbortResult, s as ClientSessionEvent, t as AgentClientTransport, x as AgentMetadata } from "./transport-BRqZoaeo.mjs";
2
2
  import { ProviderSelection } from "@demicodes/provider";
3
3
  import { Block, UserContentBlock } from "@demicodes/core";
4
4
  //#region src/client.d.ts
@@ -304,8 +304,23 @@ var AgentClient = class {
304
304
  case "shell_write_result":
305
305
  case "audit":
306
306
  case "retry_scheduled":
307
+ case "subagent":
307
308
  this.emit(frame);
308
309
  return;
310
+ case "subagent_transcript_reset":
311
+ this.emit({
312
+ type: "subagent_transcript_reset",
313
+ subagentId: frame.subagentId,
314
+ blocks: frame.blocks
315
+ });
316
+ return;
317
+ case "subagent_transcript_patch":
318
+ this.emit({
319
+ type: "subagent_transcript_patch",
320
+ subagentId: frame.subagentId,
321
+ patches: frame.patches
322
+ });
323
+ return;
309
324
  case "conversations":
310
325
  this.pendingConversationWaiters.shift()?.(frame.conversations);
311
326
  return;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@demicodes/agent",
3
3
  "description": "Session runtime and transport-neutral client and server protocol for Demi.",
4
- "version": "0.18.0",
4
+ "version": "0.19.1",
5
5
  "private": false,
6
6
  "type": "module",
7
7
  "exports": {
@@ -19,10 +19,11 @@
19
19
  }
20
20
  },
21
21
  "dependencies": {
22
- "@demicodes/core": "^0.18.0",
23
- "@demicodes/provider": "^0.18.0",
24
- "@demicodes/shell": "^0.18.0",
25
- "@demicodes/utils": "^0.18.0"
22
+ "@demicodes/core": "^0.19.1",
23
+ "@demicodes/provider": "^0.19.1",
24
+ "@demicodes/shell": "^0.19.1",
25
+ "@demicodes/utils": "^0.19.1",
26
+ "zod": "^4.0.0"
26
27
  },
27
28
  "license": "Apache-2.0",
28
29
  "main": "./dist/index.mjs",
@@ -43,8 +44,5 @@
43
44
  "directory": "packages/agent"
44
45
  },
45
46
  "homepage": "https://github.com/wspl/demi/tree/main/packages/agent#readme",
46
- "bugs": "https://github.com/wspl/demi/issues",
47
- "devDependencies": {
48
- "zod": "^4.0.0"
49
- }
47
+ "bugs": "https://github.com/wspl/demi/issues"
50
48
  }