@deepseek-ai/dsh-subagent 0.1.6-alpha.2 → 0.1.7-alpha.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.
package/lib/index.js CHANGED
@@ -30,15 +30,13 @@ var SubagentError = class extends HarnessError {
30
30
  //#endregion
31
31
  //#region lib/types/control.js
32
32
  /**
33
- * Browser-facing subagent control assembly: the catalog view sampled against
34
- * the live Agent registry, one browser zone's validation, and the stable
35
- * failure codes the Remote surface answers with.
33
+ * Browser-facing subagent prompt and interrupt request validation plus the
34
+ * stable prompt failure codes returned by the Remote surface.
36
35
  *
37
36
  * @module @deepseek-ai/dsh-subagent
38
37
  */
39
38
  const SESSION_ID_SCHEMA = z$1.string().min(1);
40
39
  const CONTROL_ID_SCHEMAS = {
41
- "subagent.list": z$1.object({ parentSessionId: SESSION_ID_SCHEMA }),
42
40
  "subagent.prompt": z$1.object({
43
41
  parentSessionId: SESSION_ID_SCHEMA,
44
42
  childSessionId: SESSION_ID_SCHEMA,
@@ -63,39 +61,6 @@ function validateControlRequest(method, payload) {
63
61
  if (!parsed.success) throw new RemoteError("gateway/bad-request", `invalid payload for ${method}`, { issues: parsed.error.issues });
64
62
  }
65
63
  /**
66
- * Project one durable listing onto the catalog view, replacing each row's
67
- * store-derived activity with the live Agent driver's status and reporting
68
- * whether the exact parent Agent is live. Without an Agent registry no driver
69
- * runs at all, so every row is inactive and the parent is unavailable.
70
- * @param ctx - Host context that may carry the Agent registry.
71
- * @param parentSessionId - the listed parent.
72
- * @param entries - the durable direct-child listing.
73
- * @returns the catalog view answered to one browser.
74
- */
75
- function catalogView(ctx, parentSessionId, entries) {
76
- const agents = ctx.get("agents");
77
- return {
78
- entries: entries.map((entry) => entry.kind === "child" ? {
79
- ...entry,
80
- activity: agents?.get(entry.id)?.status === "running" ? "running" : "inactive"
81
- } : entry),
82
- parentAvailable: agents?.get(parentSessionId) !== void 0
83
- };
84
- }
85
- /**
86
- * Refuse one catalog read while preserving cancellation and a missing
87
- * projections registry as distinct failures.
88
- * @param error - the thrown value.
89
- * @param signal - the caller's cancellation.
90
- * @returns Never — the refusal is thrown.
91
- * @throws {RemoteError} always.
92
- */
93
- function rejectCatalogRead(error, signal) {
94
- if (isCancellation(error, signal)) throw new RemoteError("gateway/cancelled", "subagent catalog read was cancelled", {}, { cause: error });
95
- if (error instanceof SubagentError && error.code === "SUBAGENT_CONTROL_PROJECTIONS_UNAVAILABLE") throw new RemoteError("subagent/projections-unavailable", "subagent catalog is unavailable: this deployment does not mount the sessionProjections registry (load @deepseek-ai/dsh-session-projection)", {}, { cause: error });
96
- throw new RemoteError("gateway/internal", "subagent catalog read failed", {}, { cause: error });
97
- }
98
- /**
99
64
  * Refuse one continuation prompt without exposing provider detail: admission
100
65
  * failures the caller can act on keep their own code, everything else is
101
66
  * internal.
@@ -385,9 +350,10 @@ function epochStopReason(events) {
385
350
  case "blocked": return "refusal";
386
351
  case void 0:
387
352
  case "completed": return droppedUnrun ? "aborted" : "completed";
388
- /* v8 ignore next 3 -- `TurnEndReason` is merge-extensible, so this arm needs a
389
- * backend that adds a variant; treating an unnameable reason as success would
390
- * report failed work as completed. */
353
+ /* v8 ignore next 4 -- `forked` appears only in constructor seed history, while
354
+ * this function reads an epoch-owned suffix. `TurnEndReason` is merge-extensible,
355
+ * so a backend-added variant cannot be listed; treating an unnameable reason as
356
+ * success would report failed work as completed. */
391
357
  default: return "error";
392
358
  }
393
359
  }
@@ -1472,47 +1438,66 @@ function foldSubagentDescriptor(events) {
1472
1438
  }
1473
1439
  const sessionIdSchema = z$1.string();
1474
1440
  const oneShotCatalogSchema = z$1.object({
1475
- version: z$1.literal(0),
1441
+ version: z$1.union([z$1.literal(0), z$1.literal(1)]),
1476
1442
  childId: sessionIdSchema,
1477
1443
  childCreatedAt: z$1.number().int().nonnegative(),
1478
1444
  mode: z$1.literal("one-shot"),
1479
1445
  label: z$1.string().optional()
1480
1446
  }).strict();
1481
1447
  const continuableCatalogSchema = z$1.object({
1482
- version: z$1.literal(0),
1448
+ version: z$1.union([z$1.literal(0), z$1.literal(1)]),
1483
1449
  childId: sessionIdSchema,
1484
1450
  childCreatedAt: z$1.number().int().nonnegative(),
1485
1451
  mode: z$1.literal("continuable"),
1486
1452
  label: z$1.string()
1487
1453
  }).strict();
1488
- const eventDataSchema = z$1.union([oneShotCatalogSchema, continuableCatalogSchema]);
1489
- const viewSchema = z$1.array(z$1.union([oneShotCatalogSchema.omit({
1490
- version: true,
1491
- childId: true,
1492
- childCreatedAt: true
1493
- }).extend({
1494
- id: sessionIdSchema,
1495
- createdAt: oneShotCatalogSchema.shape.childCreatedAt
1496
- }), continuableCatalogSchema.omit({
1497
- version: true,
1498
- childId: true,
1499
- childCreatedAt: true
1500
- }).extend({
1501
- id: sessionIdSchema,
1502
- createdAt: continuableCatalogSchema.shape.childCreatedAt
1503
- })]));
1454
+ const unknownCatalogSchema = oneShotCatalogSchema.extend({
1455
+ version: z$1.literal(1),
1456
+ mode: z$1.literal("unknown")
1457
+ });
1458
+ const eventDataSchema = z$1.union([
1459
+ oneShotCatalogSchema,
1460
+ continuableCatalogSchema,
1461
+ unknownCatalogSchema
1462
+ ]);
1463
+ const viewSchema = z$1.array(z$1.union([
1464
+ oneShotCatalogSchema.omit({
1465
+ version: true,
1466
+ childId: true,
1467
+ childCreatedAt: true
1468
+ }).extend({
1469
+ id: sessionIdSchema,
1470
+ createdAt: oneShotCatalogSchema.shape.childCreatedAt
1471
+ }),
1472
+ continuableCatalogSchema.omit({
1473
+ version: true,
1474
+ childId: true,
1475
+ childCreatedAt: true
1476
+ }).extend({
1477
+ id: sessionIdSchema,
1478
+ createdAt: continuableCatalogSchema.shape.childCreatedAt
1479
+ }),
1480
+ unknownCatalogSchema.omit({
1481
+ version: true,
1482
+ childId: true,
1483
+ childCreatedAt: true
1484
+ }).extend({
1485
+ id: sessionIdSchema,
1486
+ createdAt: unknownCatalogSchema.shape.childCreatedAt
1487
+ })
1488
+ ]));
1504
1489
  const stateSchema = z$1.object({
1505
1490
  inheritedEventCount: z$1.number().int().nonnegative(),
1506
1491
  head: chunkedListSchema(eventDataSchema).optional()
1507
1492
  }).strict();
1508
1493
  /**
1509
- * Materialize direct children from their parent's successful creation facts.
1494
+ * Materialize complete and unknown-mode child identities from parent catalog events.
1510
1495
  * @param state - parent catalog fold state.
1511
1496
  * @returns current direct-child rows in parent catalog event order.
1512
1497
  */
1513
1498
  function subagentCatalogEntries(state) {
1514
1499
  const entries = [];
1515
- for (const data of iterateChunkedList(state.head)) entries.push(data.mode === "one-shot" ? {
1500
+ for (const data of iterateChunkedList(state.head)) entries.push(data.mode !== "continuable" ? {
1516
1501
  id: data.childId,
1517
1502
  createdAt: data.childCreatedAt,
1518
1503
  mode: data.mode,
@@ -1537,7 +1522,7 @@ const subagentCatalogProjectionDefinition = {
1537
1522
  head: appendChunkedList(state.head, eventDataSchema.parse(event.data))
1538
1523
  };
1539
1524
  },
1540
- stateVersion: 2,
1525
+ stateVersion: 3,
1541
1526
  wire: {
1542
1527
  viewSchema,
1543
1528
  view: subagentCatalogEntries
@@ -1603,7 +1588,7 @@ const deliverSubagentPrompt = Symbol.for("dsh.subagent.deliverPrompt");
1603
1588
  *
1604
1589
  * @module @deepseek-ai/dsh-subagent
1605
1590
  */
1606
- var __addDisposableResource$1 = function(env, value, async) {
1591
+ var __addDisposableResource$2 = function(env, value, async) {
1607
1592
  if (value !== null && value !== void 0) {
1608
1593
  if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
1609
1594
  var dispose, inner;
@@ -1632,7 +1617,7 @@ var __addDisposableResource$1 = function(env, value, async) {
1632
1617
  } else if (async) env.stack.push({ async: true });
1633
1618
  return value;
1634
1619
  };
1635
- var __disposeResources$1 = (function(SuppressedError) {
1620
+ var __disposeResources$2 = (function(SuppressedError) {
1636
1621
  return function(env) {
1637
1622
  function fail(e) {
1638
1623
  env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
@@ -1925,7 +1910,7 @@ var SubagentContinuationManager = class {
1925
1910
  options.signal.throwIfAborted();
1926
1911
  throw new SubagentError(`subagent "${childId}" is unavailable`, "NOT_RESUMABLE", { cause: error });
1927
1912
  }
1928
- const source = __addDisposableResource$1(env_1, observation, false);
1913
+ const source = __addDisposableResource$2(env_1, observation, false);
1929
1914
  this.activations.assertAdmitting(parent);
1930
1915
  this.activations.authorizeLineage(parent, childId, source.header.parentSession);
1931
1916
  const descriptor = foldSubagentDescriptor(source.events.slice(source.inheritedEventCount));
@@ -1957,7 +1942,7 @@ var SubagentContinuationManager = class {
1957
1942
  env_1.error = e_1;
1958
1943
  env_1.hasError = true;
1959
1944
  } finally {
1960
- __disposeResources$1(env_1);
1945
+ __disposeResources$2(env_1);
1961
1946
  }
1962
1947
  }
1963
1948
  /** Admit a materialized child, commit its creation fact, and release it on failure. */
@@ -2014,24 +1999,15 @@ var SubagentContinuationManager = class {
2014
1999
  //#endregion
2015
2000
  //#region lib/types/list-children.js
2016
2001
  /**
2017
- * Read-only enumeration of durable subagent children and descendant trees
2018
- * through the Session query service. Candidates come from one live-preferred
2019
- * corpus; each child's mode/label is the registered `subagent` projection
2020
- * unit's value, resolved
2021
- * down a three-rung ladder: the registry's watermark cache for a live child,
2022
- * an unseeded durable projection-cache row, and one shared Session observation
2023
- * otherwise. A seeded header deliberately lacks its exact inherited cut, so
2024
- * it takes the body-bearing observation path before classifying an identity.
2025
- * The projection fold is the single classification
2026
- * authority — this module parses no descriptor
2027
- * itself. Absent persistence, enumeration is live-only: a cold child is
2028
- * unreachable for resume anyway, so its absence is capability absence, not an
2029
- * error. The module owns no catalog state and does not consult Activation,
2030
- * Agent-registry, continuation-manager, or provider state.
2002
+ * Direct-child discovery from a parent-owned `subagent/catalog` projection,
2003
+ * plus complete descendant-tree enumeration from the Session corpus. A direct
2004
+ * listing owns one parent observation and reads no child log. Descendant
2005
+ * enumeration retains the complete corpus and the child identity projection
2006
+ * because ordinary Sessions and one-shot children remain traversal nodes.
2031
2007
  *
2032
2008
  * @module @deepseek-ai/dsh-subagent
2033
2009
  */
2034
- var __addDisposableResource = function(env, value, async) {
2010
+ var __addDisposableResource$1 = function(env, value, async) {
2035
2011
  if (value !== null && value !== void 0) {
2036
2012
  if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
2037
2013
  var dispose, inner;
@@ -2060,7 +2036,7 @@ var __addDisposableResource = function(env, value, async) {
2060
2036
  } else if (async) env.stack.push({ async: true });
2061
2037
  return value;
2062
2038
  };
2063
- var __disposeResources = (function(SuppressedError) {
2039
+ var __disposeResources$1 = (function(SuppressedError) {
2064
2040
  return function(env) {
2065
2041
  function fail(e) {
2066
2042
  env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
@@ -2096,37 +2072,43 @@ var __disposeResources = (function(SuppressedError) {
2096
2072
  */
2097
2073
  const COLD_READ_CONCURRENCY = 4;
2098
2074
  /**
2099
- * Enumerate one parent's origin-classified direct children from the
2100
- * live-preferred merge of `ctx.sessions` and optional session persistence,
2101
- * serving each identity from the `subagent` projection unit: the registry's
2102
- * watermark snapshot for a live child; for a cold one, a durable
2103
- * projection-cache read for an unseeded lifecycle, else one bounded-concurrency
2104
- * shared Session observation carrying the exact inherited cut.
2105
- * @see SubagentRuntime.listChildren for the public cancellation and failure contract.
2106
- * @param ctx - context carrying the session store, the projection registry,
2107
- * optional persistence, and the optional projection cache.
2108
- * @param parentSessionId - parent session whose direct children are listed.
2109
- * @param signal - caller-owned cancellation observed around every persistence read.
2110
- * @returns children and per-child diagnostics ordered by `createdAt`, then id.
2111
- * @throws {@link SubagentError} when the projection registry or the session
2112
- * store is not mounted, or the caller cancels the listing.
2075
+ * Read one parent's durable catalog through a live-preferred Session observation.
2076
+ * @param ctx - context carrying the Session query service.
2077
+ * @param parentSessionId - parent whose direct children are requested.
2078
+ * @param signal - cancellation forwarded to the Session observation.
2079
+ * @returns direct-child rows in parent catalog event order.
2080
+ * @throws {@link SubagentError} when query or catalog projection is unavailable.
2113
2081
  */
2114
2082
  async function listChildren(ctx, parentSessionId, signal) {
2115
- const listing = await prepareListing(ctx, signal);
2116
- return (await resolveCandidateRows([...listing.corpus.values()].filter((record) => record.header.parentSession === parentSessionId && record.header.origin === "subagent").sort(compareCorpusRecords), listing, signal)).filter((row) => row !== void 0);
2083
+ const env_1 = {
2084
+ stack: [],
2085
+ error: void 0,
2086
+ hasError: false
2087
+ };
2088
+ try {
2089
+ const query = ctx.get("sessionQuery");
2090
+ if (query === void 0) throw new SubagentError("listing subagents requires the sessionQuery service (load @deepseek-ai/dsh-session-query)", "SUBAGENT_CONTROL_QUERY_UNAVAILABLE");
2091
+ const entries = __addDisposableResource$1(env_1, await query.observeSession(parentSessionId, { ...signal === void 0 ? {} : { signal } }), false).projections?.values.subagentCatalog;
2092
+ if (entries === void 0) throw new SubagentError("listing subagents requires the registered subagentCatalog projection", "SUBAGENT_CONTROL_PROJECTIONS_UNAVAILABLE");
2093
+ return entries;
2094
+ } catch (e_1) {
2095
+ env_1.error = e_1;
2096
+ env_1.hasError = true;
2097
+ } finally {
2098
+ __disposeResources$1(env_1);
2099
+ }
2117
2100
  }
2118
2101
  /**
2119
2102
  * Enumerate every session-backed subagent below one root in stable pre-order.
2120
2103
  * Ordinary sessions and one-shot children remain traversal nodes, so a
2121
2104
  * continuable child below either is still discovered. Classification uses the
2122
- * same projection-backed runtime as {@link listChildren}; no Agent is loaded or
2123
- * resumed.
2105
+ * registered child identity projection; no Agent is loaded or resumed.
2124
2106
  * @see SubagentRuntime.listDescendants for the public cancellation and failure contract.
2125
2107
  * @param ctx - context carrying the session store, projection registry, and optional persistence/cache.
2126
2108
  * @param rootSessionId - session whose complete descendant tree is listed.
2127
2109
  * @param signal - caller-owned cancellation observed around every persistence read.
2128
2110
  * @returns interpreted subagents with durable direct-parent and root-relative depth.
2129
- * @throws {@link SubagentError} under the same conditions as {@link listChildren}.
2111
+ * @throws {@link SubagentError} when listing dependencies are unavailable or the caller cancels.
2130
2112
  */
2131
2113
  async function listDescendants(ctx, rootSessionId, signal) {
2132
2114
  const listing = await prepareListing(ctx, signal);
@@ -2262,7 +2244,7 @@ function compareCorpusRecords(a, b) {
2262
2244
  * throw — are final, so they report `corrupt`.
2263
2245
  */
2264
2246
  async function resolveColdIdentity(query, cache, header, hasChildren, signal) {
2265
- const env_1 = {
2247
+ const env_2 = {
2266
2248
  stack: [],
2267
2249
  error: void 0,
2268
2250
  hasError: false
@@ -2272,7 +2254,7 @@ async function resolveColdIdentity(query, cache, header, hasChildren, signal) {
2272
2254
  if (cache !== void 0 && !header.isSeeded) {
2273
2255
  let cached;
2274
2256
  try {
2275
- cached = cache.cachedSnapshot(header, SessionLogOffset(0), ["subagent"])?.values.subagent;
2257
+ cached = cache.cachedSnapshot(header, ["subagent"])?.values.subagent;
2276
2258
  } catch {
2277
2259
  cached = void 0;
2278
2260
  }
@@ -2290,7 +2272,7 @@ async function resolveColdIdentity(query, cache, header, hasChildren, signal) {
2290
2272
  reason: sessionQueryCode(error) === "SESSION_QUERY_CORRUPT_SESSION" || sessionQueryCode(error) === "SESSION_QUERY_SOURCE_CONFLICT" ? "corrupt" : "unavailable"
2291
2273
  };
2292
2274
  }
2293
- const ownedObservation = __addDisposableResource(env_1, observation, false);
2275
+ const ownedObservation = __addDisposableResource$1(env_2, observation, false);
2294
2276
  assertListingNotCancelled(signal);
2295
2277
  if (!sameLifecycle(ownedObservation.header, header)) return {
2296
2278
  kind: "diagnostic",
@@ -2304,11 +2286,11 @@ async function resolveColdIdentity(query, cache, header, hasChildren, signal) {
2304
2286
  reason: "corrupt"
2305
2287
  };
2306
2288
  return childRow(childId, identity, "inactive", hasChildren);
2307
- } catch (e_1) {
2308
- env_1.error = e_1;
2309
- env_1.hasError = true;
2289
+ } catch (e_2) {
2290
+ env_2.error = e_2;
2291
+ env_2.hasError = true;
2310
2292
  } finally {
2311
- __disposeResources(env_1);
2293
+ __disposeResources$1(env_2);
2312
2294
  }
2313
2295
  }
2314
2296
  /** Materialize one served identity as its child row. */
@@ -2353,6 +2335,159 @@ function sessionQueryCode(error) {
2353
2335
  return error instanceof Error && "code" in error ? error.code : void 0;
2354
2336
  }
2355
2337
  //#endregion
2338
+ //#region lib/types/archive-admission.js
2339
+ /**
2340
+ * The `subagent` family of the Workspace registry's archive admission: which
2341
+ * subagent descendants of a Session are still inside a turn, and how they
2342
+ * stop when the Session is archived with its work.
2343
+ *
2344
+ * @module @deepseek-ai/dsh-subagent
2345
+ */
2346
+ var __addDisposableResource = function(env, value, async) {
2347
+ if (value !== null && value !== void 0) {
2348
+ if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
2349
+ var dispose, inner;
2350
+ if (async) {
2351
+ if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
2352
+ dispose = value[Symbol.asyncDispose];
2353
+ }
2354
+ if (dispose === void 0) {
2355
+ if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
2356
+ dispose = value[Symbol.dispose];
2357
+ if (async) inner = dispose;
2358
+ }
2359
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
2360
+ if (inner) dispose = function() {
2361
+ try {
2362
+ inner.call(this);
2363
+ } catch (e) {
2364
+ return Promise.reject(e);
2365
+ }
2366
+ };
2367
+ env.stack.push({
2368
+ value,
2369
+ dispose,
2370
+ async
2371
+ });
2372
+ } else if (async) env.stack.push({ async: true });
2373
+ return value;
2374
+ };
2375
+ var __disposeResources = (function(SuppressedError) {
2376
+ return function(env) {
2377
+ function fail(e) {
2378
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
2379
+ env.hasError = true;
2380
+ }
2381
+ var r, s = 0;
2382
+ function next() {
2383
+ while (r = env.stack.pop()) try {
2384
+ if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
2385
+ if (r.dispose) {
2386
+ var result = r.dispose.call(r.value);
2387
+ if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) {
2388
+ fail(e);
2389
+ return next();
2390
+ });
2391
+ } else s |= 1;
2392
+ } catch (e) {
2393
+ fail(e);
2394
+ }
2395
+ if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
2396
+ if (env.hasError) throw env.error;
2397
+ }
2398
+ return next();
2399
+ };
2400
+ })(typeof SuppressedError === "function" ? SuppressedError : function(error, suppressed, message) {
2401
+ var e = new Error(message);
2402
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
2403
+ });
2404
+ /**
2405
+ * Answer `workspace/session-activity` with the running subagent descendants of
2406
+ * the asked Session, and `workspace/session-stop` by cancelling each of them
2407
+ * as their parent would. Both listeners live as long as `ctx`'s fiber.
2408
+ * @param ctx - context carrying the Agent registry; the Session query service is optional and only supplies labels.
2409
+ */
2410
+ function installSubagentArchiveAdmission(ctx) {
2411
+ ctx.on("workspace/session-activity", async ({ sessionId }, next) => {
2412
+ const running = runningDescendants(ctx, sessionId);
2413
+ const rest = await next();
2414
+ if (running.length === 0) return rest;
2415
+ return [{
2416
+ kind: "subagent",
2417
+ items: await Promise.all(running.map((child) => describe(ctx, child)))
2418
+ }, ...rest];
2419
+ });
2420
+ ctx.on("workspace/session-stop", ({ sessionId }) => {
2421
+ for (const child of runningDescendants(ctx, sessionId)) try {
2422
+ child.cancel({ kind: "parent" });
2423
+ } catch (error) {
2424
+ ctx.logger.warn(`subagent: cancelling "${child.id}" for an archived Session failed: ${String(error)}`);
2425
+ }
2426
+ });
2427
+ }
2428
+ /**
2429
+ * Live subagent descendants inside a turn, by durable lineage: a child whose
2430
+ * header names its parent and carries the subagent origin this package
2431
+ * records, at any depth. A fork shares the lineage field without the origin
2432
+ * and is an independent conversation, so it never holds its source. Lineage
2433
+ * is read as data, so a damaged header chain that loops is visited once.
2434
+ */
2435
+ function runningDescendants(ctx, rootId) {
2436
+ const childrenOf = /* @__PURE__ */ new Map();
2437
+ for (const agent of ctx.agents.list()) {
2438
+ const { parentSession, origin } = agent.session.header;
2439
+ if (parentSession === void 0 || origin !== "subagent") continue;
2440
+ const siblings = childrenOf.get(parentSession) ?? [];
2441
+ siblings.push(agent);
2442
+ childrenOf.set(parentSession, siblings);
2443
+ }
2444
+ const running = [];
2445
+ const pending = [rootId];
2446
+ const visited = /* @__PURE__ */ new Set();
2447
+ while (pending.length > 0) {
2448
+ const parentId = pending.shift();
2449
+ if (visited.has(parentId)) continue;
2450
+ visited.add(parentId);
2451
+ for (const child of childrenOf.get(parentId) ?? []) {
2452
+ if (child.status === "running") running.push(child);
2453
+ pending.push(child.id);
2454
+ }
2455
+ }
2456
+ return running;
2457
+ }
2458
+ /**
2459
+ * The child's activity item: its id, plus the durable creation label its
2460
+ * descriptor carries, read through a live Session observation. Without the
2461
+ * Session query service, or with a descriptor that is absent or unreadable,
2462
+ * the item names the child by id alone.
2463
+ */
2464
+ async function describe(ctx, child) {
2465
+ const query = ctx.get("sessionQuery");
2466
+ if (query === void 0) return { id: child.id };
2467
+ try {
2468
+ const env_1 = {
2469
+ stack: [],
2470
+ error: void 0,
2471
+ hasError: false
2472
+ };
2473
+ try {
2474
+ const observation = __addDisposableResource(env_1, await query.observeSession(child.id, { projectionMode: "none" }), false);
2475
+ const label = foldSubagentDescriptor(observation.events.slice(observation.inheritedEventCount))?.label;
2476
+ return label === void 0 ? { id: child.id } : {
2477
+ id: child.id,
2478
+ label
2479
+ };
2480
+ } catch (e_1) {
2481
+ env_1.error = e_1;
2482
+ env_1.hasError = true;
2483
+ } finally {
2484
+ __disposeResources(env_1);
2485
+ }
2486
+ } catch {
2487
+ return { id: child.id };
2488
+ }
2489
+ }
2490
+ //#endregion
2356
2491
  //#region lib/types/projection.js
2357
2492
  /**
2358
2493
  * Pure session projections for subagent identity (mode/label) and active-turn
@@ -2366,10 +2501,12 @@ const activeIntervalSchema = z$1.object({
2366
2501
  }).strict();
2367
2502
  const projectionSchema = z$1.object({
2368
2503
  settledMs: z$1.number().int().nonnegative(),
2369
- active: activeIntervalSchema.optional()
2370
- }).strict().transform(({ settledMs, active }) => ({
2504
+ active: activeIntervalSchema.optional(),
2505
+ lastTurnCompleted: z$1.boolean().optional()
2506
+ }).strict().transform(({ settledMs, active, lastTurnCompleted }) => ({
2371
2507
  settledMs,
2372
- ...active === void 0 ? {} : { active }
2508
+ ...active === void 0 ? {} : { active },
2509
+ ...lastTurnCompleted === void 0 ? {} : { lastTurnCompleted }
2373
2510
  }));
2374
2511
  /**
2375
2512
  * Fold turn boundaries around the child's own durable descriptor.
@@ -2385,23 +2522,27 @@ const subagentTimingProjectionDefinition = {
2385
2522
  settledMs: z$1.number().int().nonnegative(),
2386
2523
  active: activeIntervalSchema.optional(),
2387
2524
  pendingTurnStart: z$1.number().int().nonnegative().optional(),
2388
- descriptorSeen: z$1.boolean()
2525
+ descriptorSeen: z$1.boolean(),
2526
+ lastTurnCompleted: z$1.boolean().optional()
2389
2527
  }).strict(),
2390
2528
  init: () => ({
2391
2529
  descriptorSeen: false,
2392
2530
  settledMs: 0
2393
2531
  }),
2394
2532
  apply: (state, event) => {
2395
- if (event.type === "turn/start") return state.descriptorSeen ? {
2396
- ...state,
2397
- active: {
2398
- since: event.time,
2399
- through: event.time
2400
- }
2401
- } : {
2402
- ...state,
2403
- pendingTurnStart: event.time
2404
- };
2533
+ if (event.type === "turn/start") {
2534
+ const { lastTurnCompleted: _closed, ...openState } = state;
2535
+ return state.descriptorSeen ? {
2536
+ ...openState,
2537
+ active: {
2538
+ since: event.time,
2539
+ through: event.time
2540
+ }
2541
+ } : {
2542
+ ...openState,
2543
+ pendingTurnStart: event.time
2544
+ };
2545
+ }
2405
2546
  if (event.type === "subagent/descriptor") {
2406
2547
  const activeSince = state.active?.since ?? state.pendingTurnStart;
2407
2548
  return {
@@ -2423,7 +2564,8 @@ const subagentTimingProjectionDefinition = {
2423
2564
  const { active, ...rest } = state;
2424
2565
  return {
2425
2566
  ...rest,
2426
- settledMs: state.settledMs + Math.max(0, event.time - active.since)
2567
+ settledMs: state.settledMs + Math.max(0, event.time - active.since),
2568
+ lastTurnCompleted: event.data.reason.kind === "completed"
2427
2569
  };
2428
2570
  }
2429
2571
  if (state.active === void 0) return state;
@@ -2439,10 +2581,11 @@ const subagentTimingProjectionDefinition = {
2439
2581
  viewSchema: projectionSchema,
2440
2582
  view: (state) => ({
2441
2583
  settledMs: state.settledMs,
2442
- ...state.active === void 0 ? {} : { active: state.active }
2584
+ ...state.active === void 0 ? {} : { active: state.active },
2585
+ ...state.lastTurnCompleted === void 0 ? {} : { lastTurnCompleted: state.lastTurnCompleted }
2443
2586
  })
2444
2587
  },
2445
- stateVersion: 2
2588
+ stateVersion: 3
2446
2589
  };
2447
2590
  const identityValueSchema = z$1.discriminatedUnion("mode", [z$1.object({
2448
2591
  mode: z$1.literal("one-shot"),
@@ -2717,7 +2860,7 @@ function runOutcome(result) {
2717
2860
  switch (result.stopReason) {
2718
2861
  case "completed": return {
2719
2862
  status: "completed",
2720
- output: finalText(result.output)
2863
+ result: finalText(result.output)
2721
2864
  };
2722
2865
  case "aborted": return result.diagnostic === void 0 ? { status: "killed" } : {
2723
2866
  status: "failed",
@@ -2763,36 +2906,6 @@ async function settleRun(run) {
2763
2906
  }
2764
2907
  //#endregion
2765
2908
  //#region lib/types/index.js
2766
- /**
2767
- * Service Definition for the subagent capability seam (`ctx.subagents`): a named-provider registry plus a
2768
- * capability-validating asynchronous start API. Providers establish a
2769
- * child before returning its run, so fulfillment is the single publication and
2770
- * ownership-transfer boundary.
2771
- *
2772
- * Multiple providers coexist: each registers under a unique name and callers
2773
- * select one by name.
2774
- *
2775
- * This package owns the Service Definition role of the capability seam. Service Providers
2776
- * (`@deepseek-ai/dsh-subagent-spawn-in-process`, `-fork`, `-acp`) and the model-facing
2777
- * consumer (`@deepseek-ai/dsh-tool-subagent`) are separate packages.
2778
- *
2779
- * Public operations express caller intent: `start` returns one published owned
2780
- * one-shot run, `startContinuable` establishes a durable continuable child, and
2781
- * `sendMessage` steers between adjacent Agents without exposing whether a child
2782
- * is resident. Continuable children never become a {@link SubagentRun}: the
2783
- * continuation manager holds their `AgentHandle` directly and orders every turn
2784
- * through the child's own inbox, so providers contribute only the detached
2785
- * creation spec and see no handle, turn, or teardown. Child and descendant
2786
- * discovery read the live session store and optional session persistence
2787
- * directly and do not require that continuation runtime.
2788
- *
2789
- * Same-process providers are trusted typed collaborators. Requests, provider
2790
- * descriptors, results, and lifecycle payloads are borrowed immutable values;
2791
- * serialization and hostile-input validation belong at real process, worker,
2792
- * persistence, and model boundaries.
2793
- *
2794
- * @module @deepseek-ai/dsh-subagent
2795
- */
2796
2909
  var __runInitializers = function(thisArg, initializers, value) {
2797
2910
  var useValue = arguments.length > 2;
2798
2911
  for (var i = 0; i < initializers.length; i++) value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
@@ -2835,26 +2948,13 @@ var __esDecorate = function(ctor, descriptorIn, decorators, contextIn, initializ
2835
2948
  let SubagentRuntime = (() => {
2836
2949
  let _classSuper = TypertRemoteService;
2837
2950
  let _instanceExtraInitializers = [];
2838
- let _remoteExportList_decorators;
2839
2951
  let _prompt_decorators;
2840
2952
  let _interruptByParent_decorators;
2841
2953
  return class SubagentRuntime extends _classSuper {
2842
2954
  static {
2843
2955
  const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
2844
- _remoteExportList_decorators = [Remote("list")];
2845
2956
  _prompt_decorators = [Remote("prompt")];
2846
2957
  _interruptByParent_decorators = [Remote("interruptByParent")];
2847
- __esDecorate(this, null, _remoteExportList_decorators, {
2848
- kind: "method",
2849
- name: "remoteExportList",
2850
- static: false,
2851
- private: false,
2852
- access: {
2853
- has: (obj) => "remoteExportList" in obj,
2854
- get: (obj) => obj.remoteExportList
2855
- },
2856
- metadata: _metadata
2857
- }, null, _instanceExtraInitializers);
2858
2958
  __esDecorate(this, null, _prompt_decorators, {
2859
2959
  kind: "method",
2860
2960
  name: "prompt",
@@ -2884,11 +2984,11 @@ let SubagentRuntime = (() => {
2884
2984
  value: _metadata
2885
2985
  });
2886
2986
  }
2987
+ config = __runInitializers(this, _instanceExtraInitializers);
2887
2988
  static Config = z.object({
2888
- maxDepth: z.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER).default(1),
2889
- maxActiveSubagents: z.number().step(1).min(1).max(Number.MAX_SAFE_INTEGER).default(8)
2989
+ maxDepth: z.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER).default(1).volatile(),
2990
+ maxActiveSubagents: z.number().step(1).min(1).max(Number.MAX_SAFE_INTEGER).default(8).volatile()
2890
2991
  });
2891
- settingsSource = __runInitializers(this, _instanceExtraInitializers);
2892
2992
  providers = /* @__PURE__ */ new Map();
2893
2993
  continuations;
2894
2994
  /**
@@ -2899,25 +2999,13 @@ let SubagentRuntime = (() => {
2899
2999
  emitLifecycle;
2900
3000
  constructor(ctx, config) {
2901
3001
  super(ctx, "subagents");
2902
- assertSubagentMaxDepth(config.maxDepth);
2903
- this.settingsSource = () => config;
2904
- ctx.inject(["settings"], (settingsCtx) => {
2905
- settingsCtx.settings.installSection(ctx, "subagent", SubagentRuntime.Config, config, {
2906
- validate: (value) => {
2907
- assertSubagentMaxDepth(value.maxDepth);
2908
- },
2909
- setSource: (source) => {
2910
- this.settingsSource = source;
2911
- },
2912
- onChange: () => {}
2913
- });
2914
- });
3002
+ this.config = config;
2915
3003
  this.emitLifecycle = createLifecycleEmitter(this.ctx, (parent) => scopeTarget(this, parent));
2916
3004
  ctx.inject(["agents"], (childCtx) => {
2917
3005
  const manager = new SubagentContinuationManager(childCtx, {
2918
3006
  prepareContinuable: (name, request) => this.prepareContinuable(name, request),
2919
3007
  observeActivation: (provider, childId, parent) => this.observeActivation(provider, childId, parent)
2920
- }, () => this.settingsSource().maxActiveSubagents);
3008
+ }, () => this.config.maxActiveSubagents.get());
2921
3009
  this.continuations = manager;
2922
3010
  childCtx.effect(() => () => {
2923
3011
  /* v8 ignore else -- one injected binding owns the slot until its fiber disposes. */
@@ -2925,9 +3013,13 @@ let SubagentRuntime = (() => {
2925
3013
  }, "subagents.continuationBinding()");
2926
3014
  });
2927
3015
  ctx.inject(["sessionProjections"], (projectionCtx) => {
2928
- projectionCtx.sessionProjections.register(subagentCatalogProjectionDefinition);
2929
- projectionCtx.sessionProjections.register(subagentTimingProjectionDefinition);
2930
- projectionCtx.sessionProjections.register(subagentIdentityProjectionDefinition);
3016
+ const projections = projectionCtx.sessionProjections;
3017
+ projections.register(subagentCatalogProjectionDefinition);
3018
+ projections.register(subagentTimingProjectionDefinition);
3019
+ projections.register(subagentIdentityProjectionDefinition);
3020
+ });
3021
+ ctx.inject(["agents"], (agentsCtx) => {
3022
+ installSubagentArchiveAdmission(agentsCtx);
2931
3023
  });
2932
3024
  }
2933
3025
  /**
@@ -2937,7 +3029,10 @@ let SubagentRuntime = (() => {
2937
3029
  */
2938
3030
  resolveMaxDepth(configured) {
2939
3031
  if (configured === "provider-managed") return void 0;
2940
- return configured ?? this.settingsSource().maxDepth;
3032
+ if (configured !== void 0) return configured;
3033
+ const depth = this.config.maxDepth.get();
3034
+ assertSubagentMaxDepth(depth);
3035
+ return depth;
2941
3036
  }
2942
3037
  /**
2943
3038
  * Establish one durable continuable child and deliver its initial prompt.
@@ -3032,21 +3127,13 @@ let SubagentRuntime = (() => {
3032
3127
  await manager.drainChildren(parent, childIds);
3033
3128
  }
3034
3129
  /**
3035
- * Enumerate the parent's direct session-backed subagents without loading or
3036
- * resuming an Agent. The Session query service supplies one live-preferred
3037
- * corpus and shared point observations; the projection cache supplies
3038
- * immutable descriptor hits without opening cold logs. The registered
3039
- * `subagent` projection remains the sole mode/label classifier.
3040
- *
3041
- * Every query receives `signal`, and the listing rechecks cancellation
3042
- * around each await. Read rejections that settle
3043
- * after an abort become a stable `SubagentError` with code `CANCELLED`.
3044
- * @param parentSessionId - parent session whose direct children are listed.
3045
- * @param signal - caller-owned cancellation forwarded to Session queries
3046
- * and observed around every read await.
3047
- * @returns children and per-child diagnostics ordered by `createdAt`, then id.
3048
- * @throws {@link SubagentError} when the projection registry or the session
3049
- * store is not mounted, or the caller cancels the listing.
3130
+ * Read the parent's durable direct-child catalog without loading or resuming an Agent.
3131
+ * The service owns and releases the live-preferred Session observation.
3132
+ * @param parentSessionId - parent whose direct children are requested.
3133
+ * @param signal - cancellation forwarded to the Session query.
3134
+ * @returns catalog children in parent event order.
3135
+ * @throws {@link SubagentError} when query or catalog projection is unavailable.
3136
+ * @throws SessionQueryError when the parent cannot be read or the query is cancelled.
3050
3137
  */
3051
3138
  listChildren(parentSessionId, signal) {
3052
3139
  return listChildren(this.ctx, parentSessionId, signal);
@@ -3057,40 +3144,19 @@ let SubagentRuntime = (() => {
3057
3144
  * Agent. Ordinary sessions and one-shot children remain traversal nodes so
3058
3145
  * continuable descendants below them are discovered; each returned entry
3059
3146
  * adds its durable `parentId` and root-relative `depth`. Identity resolution,
3060
- * diagnostics, optional persistence, and cancellation follow the same
3061
- * projection-backed contract as {@link listChildren}.
3147
+ * diagnostics, optional persistence, and cancellation use the registered
3148
+ * child identity projection and complete Session corpus.
3062
3149
  * @param rootSessionId - session whose complete descendant tree is listed.
3063
3150
  * @param signal - caller-owned cancellation forwarded to persistence reads
3064
3151
  * and observed around every read await.
3065
3152
  * @returns children and per-candidate diagnostics with tree position, in
3066
3153
  * stable pre-order.
3067
- * @throws {@link SubagentError} under the same conditions as {@link listChildren}.
3154
+ * @throws {@link SubagentError} when listing dependencies are unavailable or the caller cancels.
3068
3155
  */
3069
3156
  listDescendants(rootSessionId, signal) {
3070
3157
  return listDescendants(this.ctx, rootSessionId, signal);
3071
3158
  }
3072
3159
  /**
3073
- * Remote face of {@link listChildren} for one browser: the durable listing
3074
- * plus live Agent activity and the delivery-time parent availability hint.
3075
- * Parent availability is a hint; {@link prompt} performs the authoritative
3076
- * check. Named apart from the provider-name {@link list}, which owns the
3077
- * member.
3078
- * @param parentSessionId - parent session whose direct children are listed.
3079
- * @param signal - carrier cancellation forwarded to Session queries.
3080
- * @returns the catalog view for that parent.
3081
- * @throws {RemoteError} `gateway/bad-request` for an empty parent id,
3082
- * `gateway/cancelled` for an aborted read, `subagent/projections-unavailable` when
3083
- * the deployment has no projection registry, otherwise `gateway/internal`.
3084
- */
3085
- async remoteExportList(parentSessionId, signal) {
3086
- validateControlRequest("subagent.list", { parentSessionId });
3087
- try {
3088
- return catalogView(this.ctx, parentSessionId, await this.listChildren(parentSessionId, signal));
3089
- } catch (error) {
3090
- return rejectCatalogRead(error, signal);
3091
- }
3092
- }
3093
- /**
3094
3160
  * Deliver one browser-authored message to a continuable child through the
3095
3161
  * exact live direct parent, retaining the caller-minted request identity and
3096
3162
  * validated browser zone on the accepted message. Success identifies the