@demicodes/agent 0.19.0 → 0.19.2

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 { 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";
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-CHrPzcrU.mjs";
2
+ import { a as AgentClient, i as AgentActionOptions, n as createWebSocketClientTransport, o as AgentClientListener, r as createWebSocketServerTransport, t as JsonWebSocket } from "./websocket-transport-NZ5Btmb4.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 };
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
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";
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-CHrPzcrU.mjs";
2
+ import { a as AgentClient, i as AgentActionOptions, n as createWebSocketClientTransport, o as AgentClientListener, r as createWebSocketServerTransport, t as JsonWebSocket } from "./websocket-transport-NZ5Btmb4.mjs";
3
+ import { BashAuditEvent, BashEnvironment, BashEnvironmentOptions, Command, CommandMetadataRecord, Host, HostStore, 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
@@ -271,6 +271,14 @@ interface AgentServerOptions {
271
271
  providers: Provider[];
272
272
  shell?: Omit<BashEnvironmentOptions, 'host' | 'commands'>;
273
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
+ };
274
282
  /**
275
283
  * Optional host-side shell prep (env, PATH, etc.). Implementation-agnostic —
276
284
  * command bridge wiring lives in `@demicodes/host-local`, not here.
@@ -314,6 +322,7 @@ declare class AgentServer {
314
322
  private readonly shellOptions;
315
323
  private readonly sessionOptions;
316
324
  private readonly prepareShell;
325
+ private readonly notifyParentOnIdle;
317
326
  private readonly bindings;
318
327
  private readonly sessionOwnership;
319
328
  constructor(options: AgentServerOptions);
@@ -342,14 +351,20 @@ interface ChildSupervisorOptions<State> {
342
351
  shellOptions: Omit<BashEnvironmentOptions, 'host' | 'commands'>;
343
352
  prepareShell: PrepareShell | null;
344
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
+ /** Backing store for child checkpoints and job metadata, keyed under the parent's session directory. */
357
+ store: HostStore;
345
358
  emit(frame: ServerFrame): void;
346
359
  }
347
360
  /**
348
361
  * Per-parent subagent supervisor. Owns every child `AgentSession` this parent
349
362
  * spawns: lifecycle (spawn / steer / abort / natural end), the `demi agent`
350
363
  * command tree, child shell environments, and the `subagent*` protocol frames
351
- * on the parent connection. Children are in-memory only: no store, no
352
- * checkpoint, gone with the parent.
364
+ * on the parent connection. Children persist exactly like the parent: each
365
+ * live child keeps a checkpoint and job record under the parent's session
366
+ * directory, and reopening the parent restores and resumes them (`restore`).
367
+ * A closed child leaves nothing behind.
353
368
  */
354
369
  declare class ChildSupervisor<State = unknown> {
355
370
  private readonly options;
@@ -369,9 +384,29 @@ declare class ChildSupervisor<State = unknown> {
369
384
  } | null;
370
385
  /** Re-emits `subagent started` + transcript reset for every running child (transcript resync). */
371
386
  replay(): void;
387
+ /**
388
+ * Detaches every live child on connection teardown: aborts in-flight turns,
389
+ * flushes checkpoints, and keeps the persisted job so the next open of this
390
+ * parent restores it — the same dispose semantics as the parent session.
391
+ * No `closed` frame is emitted: the children are not done, just paused.
392
+ */
372
393
  dispose(): Promise<void>;
394
+ /**
395
+ * Rebuilds every persisted live child of this parent and finishes what it was
396
+ * doing: an interrupted turn resumes from its resume point; an already
397
+ * quiescent child closes with its result. Children share the parent's
398
+ * persistence lifecycle — a parent restore is a child restore.
399
+ */
400
+ restore(): Promise<void>;
401
+ private restoreJob;
373
402
  abortSubtree(id: string): Promise<void>;
374
403
  private spawn;
404
+ /** Everything spawn and restore share: the command tree, the harness runtime, and the job record (session attached separately). */
405
+ private assembleJob;
406
+ private attachSession;
407
+ private childStoreKey;
408
+ private childSessionStore;
409
+ private deletePersistedJob;
375
410
  /** Delivers one steer to a child: mid-turn as a steer, idle as a new user turn. */
376
411
  private steerChild;
377
412
  private pumpStdinSteers;
@@ -381,6 +416,7 @@ declare class ChildSupervisor<State = unknown> {
381
416
  /** Resolves when the child is idle with no pending yield wakeups (or the job closed). */
382
417
  private waitForQuiescence;
383
418
  private closeJob;
419
+ private disposeJobShells;
384
420
  /** Wakes an idle parent with a user send; a parent blocked in the spawn gets the tool result instead. */
385
421
  private notifyIdleParent;
386
422
  private createChildAgentNode;
package/dist/index.mjs CHANGED
@@ -2660,8 +2660,10 @@ const SPAWN_PROMPT_DESCRIPTION = "The child's first user message and only task b
2660
2660
  * Per-parent subagent supervisor. Owns every child `AgentSession` this parent
2661
2661
  * spawns: lifecycle (spawn / steer / abort / natural end), the `demi agent`
2662
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.
2663
+ * on the parent connection. Children persist exactly like the parent: each
2664
+ * live child keeps a checkpoint and job record under the parent's session
2665
+ * directory, and reopening the parent restores and resumes them (`restore`).
2666
+ * A closed child leaves nothing behind.
2665
2667
  */
2666
2668
  var ChildSupervisor = class {
2667
2669
  options;
@@ -2860,9 +2862,70 @@ var ChildSupervisor = class {
2860
2862
  });
2861
2863
  }
2862
2864
  }
2865
+ /**
2866
+ * Detaches every live child on connection teardown: aborts in-flight turns,
2867
+ * flushes checkpoints, and keeps the persisted job so the next open of this
2868
+ * parent restores it — the same dispose semantics as the parent session.
2869
+ * No `closed` frame is emitted: the children are not done, just paused.
2870
+ */
2863
2871
  async dispose() {
2864
2872
  this.isDisposed = true;
2865
- for (const job of [...this.jobs.values()]) await this.closeJob(job, "aborted");
2873
+ for (const job of [...this.jobs.values()]) {
2874
+ job.isClosing = true;
2875
+ job.unsubscribe();
2876
+ this.jobs.delete(job.id);
2877
+ await job.session.dispose().catch(noop);
2878
+ await this.disposeJobShells(job);
2879
+ job.settleClosed({ phase: "aborted" });
2880
+ }
2881
+ }
2882
+ /**
2883
+ * Rebuilds every persisted live child of this parent and finishes what it was
2884
+ * doing: an interrupted turn resumes from its resume point; an already
2885
+ * quiescent child closes with its result. Children share the parent's
2886
+ * persistence lifecycle — a parent restore is a child restore.
2887
+ */
2888
+ async restore() {
2889
+ const parent = this.parentSession;
2890
+ if (!parent || this.isDisposed) return;
2891
+ const prefix = `agent-sessions/${parent.id()}/subagents/`;
2892
+ const keys = await this.options.store.list(prefix).catch(() => []);
2893
+ const ids = [...new Set(keys.map((key) => key.slice(prefix.length).split("/")[0] ?? "").filter(Boolean))];
2894
+ for (const id of ids) {
2895
+ if (this.jobs.has(id)) continue;
2896
+ try {
2897
+ await this.restoreJob(id);
2898
+ } catch {
2899
+ await this.deletePersistedJob(id);
2900
+ }
2901
+ }
2902
+ }
2903
+ async restoreJob(id) {
2904
+ const parent = this.parentSession;
2905
+ if (!parent) return;
2906
+ const meta = await this.options.store.readJson(this.childStoreKey(id, "job.json"));
2907
+ const checkpoint = await this.childSessionStore(id).loadCheckpoint();
2908
+ if (!meta || !checkpoint) throw new Error("incomplete persisted subagent");
2909
+ const profile = this.resolveProfile(meta.profileName ?? void 0);
2910
+ const { job, runtime } = this.assembleJob({
2911
+ id,
2912
+ description: meta.description,
2913
+ profileName: meta.profileName,
2914
+ profile,
2915
+ metadata: meta.metadata,
2916
+ spawnedAt: meta.spawnedAt
2917
+ });
2918
+ const session = AgentSession.fromCheckpoint({
2919
+ provider: parent.cloneProviderRuntime(),
2920
+ runtime,
2921
+ checkpoint
2922
+ }, {
2923
+ agentSessionId: id,
2924
+ store: this.childSessionStore(id),
2925
+ ...this.options.sessionOptions
2926
+ });
2927
+ this.attachSession(job, session);
2928
+ this.watchTurn(job, session.resume(meta.metadata ? { metadata: meta.metadata } : {}));
2866
2929
  }
2867
2930
  async abortSubtree(id) {
2868
2931
  const job = this.jobs.get(id);
@@ -2877,6 +2940,40 @@ var ChildSupervisor = class {
2877
2940
  const profile = this.resolveProfile(input.profileName);
2878
2941
  const id = createId();
2879
2942
  const metadata = parent.actionMetadata();
2943
+ const profileName = input.profileName ?? (this.options.profiles ? profile.name : null);
2944
+ const spawnedAt = Date.now();
2945
+ const { job, runtime } = this.assembleJob({
2946
+ id,
2947
+ description: input.description,
2948
+ profileName,
2949
+ profile,
2950
+ metadata,
2951
+ spawnedAt
2952
+ });
2953
+ await this.options.store.writeJson(this.childStoreKey(id, "job.json"), {
2954
+ description: input.description,
2955
+ profileName,
2956
+ metadata,
2957
+ spawnedAt
2958
+ });
2959
+ const session = new AgentSession({
2960
+ provider: parent.cloneProviderRuntime(),
2961
+ model: profile.model ?? structuredClone(parent.modelSelection),
2962
+ cwd: this.options.cwd,
2963
+ runtime,
2964
+ state: this.options.agent.initialState()
2965
+ }, {
2966
+ agentSessionId: id,
2967
+ store: this.childSessionStore(id),
2968
+ ...this.options.sessionOptions
2969
+ });
2970
+ this.attachSession(job, session);
2971
+ this.watchChild(job, input.prompt);
2972
+ return job;
2973
+ }
2974
+ /** Everything spawn and restore share: the command tree, the harness runtime, and the job record (session attached separately). */
2975
+ assembleJob(input) {
2976
+ const { id, profile } = input;
2880
2977
  const agentNode = this.createChildAgentNode(id, input.description);
2881
2978
  const commands = injectSubagentCommand(profile.commands ? profile.commands([...this.options.parentCommands]) : [...this.options.parentCommands], agentNode);
2882
2979
  const commandRegistry = new CommandRegistry();
@@ -2891,15 +2988,15 @@ var ChildSupervisor = class {
2891
2988
  id,
2892
2989
  description: input.description,
2893
2990
  profile,
2894
- profileName: input.profileName ?? (this.options.profiles ? profile.name : null),
2895
- metadata,
2991
+ profileName: input.profileName,
2992
+ metadata: input.metadata,
2896
2993
  session: null,
2897
2994
  commandRegistry,
2898
2995
  commandNames,
2899
2996
  environments: /* @__PURE__ */ new Map(),
2900
2997
  pendingEnvironments: /* @__PURE__ */ new Map(),
2901
2998
  readonlyHosts: /* @__PURE__ */ new WeakMap(),
2902
- spawnedAt: Date.now(),
2999
+ spawnedAt: input.spawnedAt,
2903
3000
  lastEventAt: Date.now(),
2904
3001
  tools: [],
2905
3002
  lastAssistantTextAt: null,
@@ -2912,56 +3009,64 @@ var ChildSupervisor = class {
2912
3009
  };
2913
3010
  const agent = this.options.agent;
2914
3011
  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
- })
3012
+ return {
3013
+ job,
3014
+ runtime: {
3015
+ harnessName: agent.name,
3016
+ initialState: () => agent.initialState(),
3017
+ systemPrompt: (ctx) => (profile.systemPrompt ?? agent.systemPrompt).call(agent, {
3018
+ ...ctx,
3019
+ commandsPrompt
3020
+ }),
3021
+ preamble: async (ctx) => {
3022
+ const inheritedPreamble = profile.systemPrompt ? null : await agent.preamble?.(ctx) ?? null;
3023
+ return inheritedPreamble ? `${inheritedPreamble}\n\n${preamble}` : preamble;
3024
+ },
3025
+ tools: () => createStandardAgentTools({
3026
+ environment: (ctx) => this.childEnvironment(job, ctx),
3027
+ scheduleYield: (ctx, durationMs) => job.session.scheduleYieldWakeup(durationMs, ctx.metadata)
3028
+ })
3029
+ }
2930
3030
  };
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) => {
3031
+ }
3032
+ attachSession(job, session) {
3033
+ job.session = session;
3034
+ job.unsubscribe = session.subscribe((event) => {
2942
3035
  if (event.type !== "transcript_changed") return;
2943
3036
  this.recordTelemetry(job, event.patches);
2944
3037
  this.options.emit({
2945
3038
  type: "subagent_transcript_patch",
2946
- subagentId: id,
3039
+ subagentId: job.id,
2947
3040
  patches: event.patches,
2948
3041
  revision: event.revision
2949
3042
  });
2950
3043
  });
2951
- this.jobs.set(id, job);
3044
+ this.jobs.set(job.id, job);
2952
3045
  this.options.emit({
2953
3046
  type: "subagent",
2954
3047
  event: "started",
2955
3048
  job: this.wireJob(job)
2956
3049
  });
3050
+ const transcript = session.transcript();
2957
3051
  this.options.emit({
2958
3052
  type: "subagent_transcript_reset",
2959
- subagentId: id,
2960
- blocks: [],
2961
- revision: 0
3053
+ subagentId: job.id,
3054
+ blocks: structuredClone(transcript.blocks),
3055
+ revision: transcript.revision
2962
3056
  });
2963
- this.watchChild(job, input.prompt);
2964
- return job;
3057
+ }
3058
+ childStoreKey(childId, file) {
3059
+ return `agent-sessions/${this.parentSession?.id() ?? ""}/subagents/${childId}/${file}`;
3060
+ }
3061
+ childSessionStore(childId) {
3062
+ return {
3063
+ saveCheckpoint: (checkpoint) => this.options.store.writeJson(this.childStoreKey(childId, "checkpoint.json"), checkpoint),
3064
+ loadCheckpoint: () => this.options.store.readJson(this.childStoreKey(childId, "checkpoint.json"))
3065
+ };
3066
+ }
3067
+ async deletePersistedJob(id) {
3068
+ await this.options.store.delete(this.childStoreKey(id, "checkpoint.json")).catch(noop);
3069
+ await this.options.store.delete(this.childStoreKey(id, "job.json")).catch(noop);
2965
3070
  }
2966
3071
  /** Delivers one steer to a child: mid-turn as a steer, idle as a new user turn. */
2967
3072
  async steerChild(job, message) {
@@ -3028,12 +3133,8 @@ var ChildSupervisor = class {
3028
3133
  this.jobs.delete(job.id);
3029
3134
  const result = phase === "completed" ? boundedResultText(lastAssistantText(job.session.transcript().blocks)) : void 0;
3030
3135
  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);
3136
+ await this.disposeJobShells(job);
3137
+ await this.deletePersistedJob(job.id);
3037
3138
  const close = {
3038
3139
  phase,
3039
3140
  ...result !== void 0 ? { result } : {},
@@ -3047,9 +3148,17 @@ var ChildSupervisor = class {
3047
3148
  job.settleClosed(close);
3048
3149
  this.notifyIdleParent(job, close);
3049
3150
  }
3151
+ async disposeJobShells(job) {
3152
+ const environments = /* @__PURE__ */ new Set([...job.environments.values()]);
3153
+ for (const pending of job.pendingEnvironments.values()) {
3154
+ const environment = await pending.catch(() => null);
3155
+ if (environment) environments.add(environment);
3156
+ }
3157
+ for (const environment of environments) await environment.disposeAllShells().catch(noop);
3158
+ }
3050
3159
  /** Wakes an idle parent with a user send; a parent blocked in the spawn gets the tool result instead. */
3051
3160
  notifyIdleParent(job, close) {
3052
- if (this.isDisposed) return;
3161
+ if (this.isDisposed || !this.options.notifyParentOnIdle) return;
3053
3162
  const parent = this.parentSession;
3054
3163
  if (!parent || parent.phase() !== "idle") return;
3055
3164
  const label = `subagent ${job.id}${job.description ? ` — ${job.description}` : ""}`;
@@ -3057,7 +3166,7 @@ var ChildSupervisor = class {
3057
3166
  parent.send([{
3058
3167
  type: "text",
3059
3168
  text: body
3060
- }]).catch(noop);
3169
+ }], job.metadata ? { metadata: job.metadata } : {}).catch(noop);
3061
3170
  }
3062
3171
  createChildAgentNode(childId, description) {
3063
3172
  return {
@@ -3090,13 +3199,15 @@ var ChildSupervisor = class {
3090
3199
  type: "text",
3091
3200
  text: `[subagent ${childId}${description ? ` — ${description}` : ""}] ${message}`
3092
3201
  }];
3202
+ const metadata = this.jobs.get(childId)?.metadata ?? null;
3203
+ const sendOptions = metadata ? { metadata } : {};
3093
3204
  if (parent.phase() !== "idle") {
3094
3205
  parent.steer(content).catch(() => {
3095
- parent.send(content).catch(noop);
3206
+ parent.send(content, sendOptions).catch(noop);
3096
3207
  });
3097
3208
  return;
3098
3209
  }
3099
- parent.send(content).catch(noop);
3210
+ parent.send(content, sendOptions).catch(noop);
3100
3211
  }
3101
3212
  async childEnvironment(job, ctx) {
3102
3213
  const resolved = await this.options.agent.host({
@@ -3306,6 +3417,7 @@ var ChildSupervisor = class {
3306
3417
  description: job.description,
3307
3418
  profile: job.profileName,
3308
3419
  phase: job.phase,
3420
+ metadata: job.metadata,
3309
3421
  ...result !== void 0 ? { result } : {}
3310
3422
  };
3311
3423
  }
@@ -3445,6 +3557,7 @@ var AgentServer = class {
3445
3557
  shellOptions;
3446
3558
  sessionOptions;
3447
3559
  prepareShell;
3560
+ notifyParentOnIdle;
3448
3561
  bindings = /* @__PURE__ */ new Set();
3449
3562
  sessionOwnership = new SessionOwnershipRegistry();
3450
3563
  constructor(options) {
@@ -3453,6 +3566,7 @@ var AgentServer = class {
3453
3566
  this.shellOptions = options.shell ?? {};
3454
3567
  this.sessionOptions = options.session ?? {};
3455
3568
  this.prepareShell = options.prepareShell ?? null;
3569
+ this.notifyParentOnIdle = options.subagents?.notifyParentOnIdle ?? true;
3456
3570
  }
3457
3571
  client() {
3458
3572
  const transports = createInProcessTransportPair();
@@ -3467,6 +3581,7 @@ var AgentServer = class {
3467
3581
  shell: this.shellOptions,
3468
3582
  session: this.sessionOptions,
3469
3583
  prepareShell: this.prepareShell,
3584
+ notifyParentOnIdle: this.notifyParentOnIdle,
3470
3585
  sessions: this.sessionOwnership
3471
3586
  });
3472
3587
  this.bindings.add(binding);
@@ -3516,6 +3631,7 @@ var AgentTransportBindingImpl = class {
3516
3631
  shellOptions;
3517
3632
  sessionOptions;
3518
3633
  prepareShell;
3634
+ notifyParentOnIdle;
3519
3635
  sessions;
3520
3636
  session = null;
3521
3637
  currentAgent = null;
@@ -3537,6 +3653,7 @@ var AgentTransportBindingImpl = class {
3537
3653
  this.shellOptions = options.shell ?? {};
3538
3654
  this.sessionOptions = options.session ?? {};
3539
3655
  this.prepareShell = options.prepareShell;
3656
+ this.notifyParentOnIdle = options.notifyParentOnIdle;
3540
3657
  this.sessions = options.sessions;
3541
3658
  this.unsubscribeTransport = this.transport.onFrame((frame) => {
3542
3659
  this.handleFrame(frame);
@@ -3731,10 +3848,11 @@ var AgentTransportBindingImpl = class {
3731
3848
  await this.sessions.claim(agentSessionId, this);
3732
3849
  this.currentSessionId = agentSessionId;
3733
3850
  const initialState = agent.initialState();
3734
- const store = new HostAgentSessionStore((await agent.host({
3851
+ const provisionalHost = await agent.host({
3735
3852
  state: initialState,
3736
3853
  cwd: frame.cwd
3737
- })).store, agentSessionId);
3854
+ });
3855
+ const store = new HostAgentSessionStore(provisionalHost.store, agentSessionId);
3738
3856
  const checkpoint = await store.loadCheckpoint();
3739
3857
  const restoring = checkpoint !== null && checkpoint.harnessName === agent.name;
3740
3858
  const state = restoring ? structuredClone(checkpoint.state) : initialState;
@@ -3752,6 +3870,8 @@ var AgentTransportBindingImpl = class {
3752
3870
  shellOptions: this.shellOptions,
3753
3871
  prepareShell: this.prepareShell,
3754
3872
  sessionOptions: this.sessionOptions,
3873
+ notifyParentOnIdle: this.notifyParentOnIdle,
3874
+ store: provisionalHost.store,
3755
3875
  emit: (subagentFrame) => this.send(subagentFrame)
3756
3876
  });
3757
3877
  const commands = injectSubagentCommand(harnessCommands, supervisor.rootCommandNode());
@@ -3822,6 +3942,7 @@ var AgentTransportBindingImpl = class {
3822
3942
  type: "queue",
3823
3943
  queue: this.session.queuedMessages()
3824
3944
  });
3945
+ if (restoring) await supervisor.restore();
3825
3946
  }
3826
3947
  sendTranscriptReset(session) {
3827
3948
  const transcript = session.transcript();
@@ -1,4 +1,4 @@
1
- import { n as AgentServerTransport, t as AgentClientTransport } from "./transport-BRqZoaeo.mjs";
1
+ import { n as AgentServerTransport, t as AgentClientTransport } from "./transport-CHrPzcrU.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;
@@ -398,6 +398,8 @@ interface SubagentJob {
398
398
  description: string;
399
399
  profile: string | null;
400
400
  phase: 'running' | 'completed' | 'aborted' | 'error';
401
+ /** Action metadata of the round that spawned the child. */
402
+ metadata: AgentMetadata | null;
401
403
  /** Present on `closed`: the child's last assistant text, at most 32 KiB. */
402
404
  result?: string;
403
405
  }
@@ -1,4 +1,4 @@
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";
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-CHrPzcrU.mjs";
2
2
  import { ProviderSelection } from "@demicodes/provider";
3
3
  import { Block, UserContentBlock } from "@demicodes/core";
4
4
  //#region src/client.d.ts
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.19.0",
4
+ "version": "0.19.2",
5
5
  "private": false,
6
6
  "type": "module",
7
7
  "exports": {
@@ -19,10 +19,10 @@
19
19
  }
20
20
  },
21
21
  "dependencies": {
22
- "@demicodes/core": "^0.19.0",
23
- "@demicodes/provider": "^0.19.0",
24
- "@demicodes/shell": "^0.19.0",
25
- "@demicodes/utils": "^0.19.0",
22
+ "@demicodes/core": "^0.19.2",
23
+ "@demicodes/provider": "^0.19.2",
24
+ "@demicodes/shell": "^0.19.2",
25
+ "@demicodes/utils": "^0.19.2",
26
26
  "zod": "^4.0.0"
27
27
  },
28
28
  "license": "Apache-2.0",