@agimon-ai/po-team 0.0.0-alpha.2 → 0.0.0-alpha.21

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.
Files changed (120) hide show
  1. package/LICENSE +166 -5
  2. package/README.md +11 -12
  3. package/dist/{agentDiscovery-Whwzc4dP.cjs → agentDiscovery-DTTlM_LT.cjs} +246 -350
  4. package/dist/agentDiscovery-DTTlM_LT.cjs.map +1 -0
  5. package/dist/{agentDiscovery-ZQlFkKvB.mjs → agentDiscovery-ZNG8iZXV.mjs} +210 -279
  6. package/dist/agentDiscovery-ZNG8iZXV.mjs.map +1 -0
  7. package/dist/apiContracts.cjs +10 -3
  8. package/dist/apiContracts.cjs.map +1 -1
  9. package/dist/apiContracts.d.mts.map +1 -1
  10. package/dist/apiContracts.mjs +10 -3
  11. package/dist/apiContracts.mjs.map +1 -1
  12. package/dist/{externalProcessIpc-ojE0pvw0.cjs → atomicJson-DRT03Ql5.cjs} +155 -21
  13. package/dist/atomicJson-DRT03Ql5.cjs.map +1 -0
  14. package/dist/{externalProcessIpc-C-15lJrB.mjs → atomicJson-VVM1iAPu.mjs} +140 -12
  15. package/dist/atomicJson-VVM1iAPu.mjs.map +1 -0
  16. package/dist/{capabilityCeiling-CF7kAS18.mjs → capabilityCeiling-BaHT9v9S.mjs} +3 -3
  17. package/dist/capabilityCeiling-BaHT9v9S.mjs.map +1 -0
  18. package/dist/{capabilityCeiling-C-Y7H8oC.cjs → capabilityCeiling-CO3ZjYmY.cjs} +3 -3
  19. package/dist/capabilityCeiling-CO3ZjYmY.cjs.map +1 -0
  20. package/dist/capabilityCeiling.cjs +2 -2
  21. package/dist/capabilityCeiling.d.mts +2 -2
  22. package/dist/capabilityCeiling.mjs +2 -2
  23. package/dist/delegation.cjs +1 -1
  24. package/dist/delegation.cjs.map +1 -1
  25. package/dist/delegation.d.mts +1 -1
  26. package/dist/delegation.mjs +1 -1
  27. package/dist/delegation.mjs.map +1 -1
  28. package/dist/delegationApi.cjs +30 -30
  29. package/dist/delegationApi.d.mts +2 -2
  30. package/dist/delegationApi.mjs +1 -2
  31. package/dist/env.cjs +1 -1
  32. package/dist/env.d.mts +1 -1
  33. package/dist/env.mjs +1 -2
  34. package/dist/{environment-DrqhrxG_.mjs → environment-BtvJI2S-.mjs} +3 -3
  35. package/dist/environment-BtvJI2S-.mjs.map +1 -0
  36. package/dist/{environment-ma5oQ7uM.d.mts → environment-Bz1OlWIT.d.mts} +2 -2
  37. package/dist/{environment-ma5oQ7uM.d.mts.map → environment-Bz1OlWIT.d.mts.map} +1 -1
  38. package/dist/{environment-CaCW3SgT.cjs → environment-Cqs7vHJL.cjs} +3 -3
  39. package/dist/environment-Cqs7vHJL.cjs.map +1 -0
  40. package/dist/extensions/native.mjs +34 -12
  41. package/dist/extensions/native.mjs.map +1 -1
  42. package/dist/extensions/server.cjs +990 -786
  43. package/dist/extensions/server.cjs.map +1 -1
  44. package/dist/extensions/server.d.mts +2 -2
  45. package/dist/extensions/server.d.mts.map +1 -1
  46. package/dist/extensions/server.mjs +975 -772
  47. package/dist/extensions/server.mjs.map +1 -1
  48. package/dist/extensions/web.mjs +994 -807
  49. package/dist/extensions/web.mjs.map +1 -1
  50. package/dist/fablePlan.cjs +3 -3
  51. package/dist/fablePlan.d.mts +2 -2
  52. package/dist/fablePlan.mjs +1 -2
  53. package/dist/{fablePlanApi-CZ3uUcMi.d.mts → fablePlanApi-BTNKjLni.d.mts} +6 -6
  54. package/dist/{fablePlanApi-CZ3uUcMi.d.mts.map → fablePlanApi-BTNKjLni.d.mts.map} +1 -1
  55. package/dist/{fablePlanApi-wBu9bRXU.cjs → fablePlanApi-BhI4jnDP.cjs} +12 -12
  56. package/dist/fablePlanApi-BhI4jnDP.cjs.map +1 -0
  57. package/dist/{fablePlanApi-BsHhgVFV.mjs → fablePlanApi-DuMg43Hu.mjs} +5 -5
  58. package/dist/fablePlanApi-DuMg43Hu.mjs.map +1 -0
  59. package/dist/fablePlanApi.cjs +3 -3
  60. package/dist/fablePlanApi.d.mts +2 -2
  61. package/dist/fablePlanApi.mjs +1 -2
  62. package/dist/index.cjs +17 -8
  63. package/dist/index.cjs.map +1 -1
  64. package/dist/index.d.mts +5 -1
  65. package/dist/index.d.mts.map +1 -1
  66. package/dist/index.mjs +15 -5
  67. package/dist/index.mjs.map +1 -1
  68. package/dist/rolldown-runtime-VH7oDXx4.cjs +28 -0
  69. package/dist/runs/background/cliRunnerEntry.cjs +26 -25
  70. package/dist/runs/background/cliRunnerEntry.cjs.map +1 -1
  71. package/dist/runs/background/cliRunnerEntry.mjs +14 -14
  72. package/dist/runs/background/cliRunnerEntry.mjs.map +1 -1
  73. package/dist/runtimeSubagentPolicy.cjs +3 -3
  74. package/dist/runtimeSubagentPolicy.d.mts +2 -2
  75. package/dist/runtimeSubagentPolicy.mjs +1 -2
  76. package/dist/runtimeSubagentTool.cjs +7 -2
  77. package/dist/runtimeSubagentTool.d.mts +1 -1
  78. package/dist/runtimeSubagentTool.mjs +1 -2
  79. package/dist/{subagentPolicy-ALJam4GN.d.mts → subagentPolicy-BE-OZoa-.d.mts} +6 -6
  80. package/dist/subagentPolicy-BE-OZoa-.d.mts.map +1 -0
  81. package/dist/{subagentPolicy-DQRyKDH-.cjs → subagentPolicy-DIpaKHO8.cjs} +8 -8
  82. package/dist/subagentPolicy-DIpaKHO8.cjs.map +1 -0
  83. package/dist/{subagentPolicy-DxjsKeyj.mjs → subagentPolicy-LfcXuqO5.mjs} +5 -5
  84. package/dist/subagentPolicy-LfcXuqO5.mjs.map +1 -0
  85. package/dist/subagentPolicy.cjs +3 -3
  86. package/dist/subagentPolicy.d.mts +2 -2
  87. package/dist/subagentPolicy.mjs +1 -2
  88. package/dist/{subagentTool-Ca2zQXKw.cjs → subagentTool-BueNPGHo.cjs} +5 -16
  89. package/dist/subagentTool-BueNPGHo.cjs.map +1 -0
  90. package/dist/{subagentTool-BTssGLw8.d.mts → subagentTool-UdB3s6Mn.d.mts} +2 -11
  91. package/dist/subagentTool-UdB3s6Mn.d.mts.map +1 -0
  92. package/dist/{subagentTool-WOYrjxgT.mjs → subagentTool-a2oh2v5B.mjs} +5 -16
  93. package/dist/subagentTool-a2oh2v5B.mjs.map +1 -0
  94. package/dist/subagentTool.cjs +7 -2
  95. package/dist/subagentTool.d.mts +1 -1
  96. package/dist/subagentTool.mjs +1 -2
  97. package/dist/teamSnapshot.d.mts +1 -0
  98. package/dist/teamSnapshot.d.mts.map +1 -1
  99. package/dist/teamSnapshot.mjs +1 -2
  100. package/package.json +25 -24
  101. package/dist/agentDiscovery-Whwzc4dP.cjs.map +0 -1
  102. package/dist/agentDiscovery-ZQlFkKvB.mjs.map +0 -1
  103. package/dist/atomicJson-D0346bt5.cjs +0 -178
  104. package/dist/atomicJson-D0346bt5.cjs.map +0 -1
  105. package/dist/atomicJson-D2t0CDh9.mjs +0 -137
  106. package/dist/atomicJson-D2t0CDh9.mjs.map +0 -1
  107. package/dist/capabilityCeiling-C-Y7H8oC.cjs.map +0 -1
  108. package/dist/capabilityCeiling-CF7kAS18.mjs.map +0 -1
  109. package/dist/environment-CaCW3SgT.cjs.map +0 -1
  110. package/dist/environment-DrqhrxG_.mjs.map +0 -1
  111. package/dist/externalProcessIpc-C-15lJrB.mjs.map +0 -1
  112. package/dist/externalProcessIpc-ojE0pvw0.cjs.map +0 -1
  113. package/dist/fablePlanApi-BsHhgVFV.mjs.map +0 -1
  114. package/dist/fablePlanApi-wBu9bRXU.cjs.map +0 -1
  115. package/dist/subagentPolicy-ALJam4GN.d.mts.map +0 -1
  116. package/dist/subagentPolicy-DQRyKDH-.cjs.map +0 -1
  117. package/dist/subagentPolicy-DxjsKeyj.mjs.map +0 -1
  118. package/dist/subagentTool-BTssGLw8.d.mts.map +0 -1
  119. package/dist/subagentTool-Ca2zQXKw.cjs.map +0 -1
  120. package/dist/subagentTool-WOYrjxgT.mjs.map +0 -1
@@ -1,34 +1,36 @@
1
- import { I as SUBAGENT_RUN_ID_ENV, O as SUBAGENT_PARENT_DEPTH_ENV, W as runtimeBinaryEnvVar } from "../environment-DrqhrxG_.mjs";
2
- import { n as SubagentCapabilityPolicyStore } from "../capabilityCeiling-CF7kAS18.mjs";
3
- import { a as FABLE_PLAN_RUNTIME, f as FablePlanStartSchema, n as FABLE_PLAN_MODEL, o as FablePlanCancelSchema, r as FABLE_PLAN_PROFILE, t as DOOM_FABLE_PLAN_SERVICE } from "../fablePlanApi-BsHhgVFV.mjs";
4
- import { a as agentHasWriteTools, c as getProjectConfigDir, i as LruCache, n as resolveActiveTeamModelSpecs, o as canonicalizeDiscoveryCwd, r as resolveActiveTeamPackageConfig, s as getAgentDir, t as AgentDiscoveryService } from "../agentDiscovery-ZQlFkKvB.mjs";
5
- import { t as writeAtomicJson } from "../atomicJson-D2t0CDh9.mjs";
6
- import { n as SubagentPolicySchema, t as DOOM_SUBAGENT_POLICY_SERVICE } from "../subagentPolicy-DxjsKeyj.mjs";
7
- import { a as SubagentToolSchema, n as SUBAGENT_ACTIONS, r as SUBAGENT_ACTION_FIELDS, s as normalizeSubagentToolParams } from "../subagentTool-WOYrjxgT.mjs";
1
+ import { I as SUBAGENT_RUN_ID_ENV, O as SUBAGENT_PARENT_DEPTH_ENV, W as runtimeBinaryEnvVar } from "../environment-BtvJI2S-.mjs";
2
+ import { n as SubagentCapabilityPolicyStore } from "../capabilityCeiling-BaHT9v9S.mjs";
3
+ import { a as FablePlanCancelSchema, d as FablePlanStartSchema, i as FABLE_PLAN_RUNTIME, m as PO_FABLE_PLAN_SERVICE, n as FABLE_PLAN_PROFILE, t as FABLE_PLAN_MODEL } from "../fablePlanApi-DuMg43Hu.mjs";
4
+ import { a as getAgentDir, i as canonicalizeDiscoveryCwd, n as LruCache, o as getConfigDirName, r as agentHasWriteTools, s as getProjectConfigDir, t as AgentDiscoveryService } from "../agentDiscovery-ZNG8iZXV.mjs";
5
+ import { n as SubagentPolicySchema, t as PO_SUBAGENT_POLICY_SERVICE } from "../subagentPolicy-LfcXuqO5.mjs";
6
+ import { a as SubagentToolSchema, n as SUBAGENT_ACTIONS, r as SUBAGENT_ACTION_FIELDS, s as normalizeSubagentToolParams } from "../subagentTool-a2oh2v5B.mjs";
8
7
  import { n as readActiveTeamSnapshot, t as formatTeamContextSnapshot } from "../teamSnapshot-DgaANhBs.mjs";
9
- import { _ as sessionScopeEnvironment, a as parseExternalRunnerMessage, c as cleanupClaudeFableLaunch, f as createSessionScope, g as sessionScopeDir, h as scopeSuspendedDir, m as scopeRunsDir, n as ExternalProcessIpc, p as scopeResultsDir, r as childProcessEndpoint, s as CLAUDE_FABLE_PROFILE, u as prepareClaudeFableLaunch } from "../externalProcessIpc-C-15lJrB.mjs";
8
+ import { _ as sessionScopeDir, a as childProcessEndpoint, f as prepareClaudeFableLaunch, g as scopeRunsDir, h as scopeResultsDir, i as ExternalProcessIpc, l as CLAUDE_FABLE_PROFILE, m as createSessionScope, s as parseExternalRunnerMessage, t as writeAtomicJson, u as cleanupClaudeFableLaunch, v as sessionScopeEnvironment } from "../atomicJson-VVM1iAPu.mjs";
10
9
  import { Buffer as Buffer$1 } from "node:buffer";
11
10
  import { resolveRootSessionId } from "@agimon-ai/po-core/childProcess";
12
- import { DOOM_DELEGATION_ACCEPTED_EVENT, DOOM_DELEGATION_CANCELLED_EVENT, DOOM_DELEGATION_FINISHED_EVENT, DOOM_DELEGATION_REQUESTED_EVENT, DOOM_DELEGATION_SERVICE, DOOM_DELEGATION_STARTED_EVENT, DOOM_DELEGATION_UPDATED_EVENT, DelegationCancelSchema, DelegationRequestSchema } from "@agimon-ai/po-core/delegation";
11
+ import { DelegationCancelSchema, DelegationRequestSchema, PO_DELEGATION_ACCEPTED_EVENT, PO_DELEGATION_CANCELLED_EVENT, PO_DELEGATION_FINISHED_EVENT, PO_DELEGATION_REQUESTED_EVENT, PO_DELEGATION_SERVICE, PO_DELEGATION_STARTED_EVENT, PO_DELEGATION_UPDATED_EVENT } from "@agimon-ai/po-core/delegation";
13
12
  import { Type } from "typebox";
14
- import * as fs from "node:fs";
15
13
  import * as path from "node:path";
14
+ import { BACKGROUND_CONTEXT, nodeHost, requirePoFs } from "@agimon-ai/po-core/platform";
15
+ import * as fs from "node:fs";
16
16
  import * as os from "node:os";
17
- import { composeServerProviderOverrides, defineServerPlugin } from "@agimon-ai/po-core/serverFacet";
18
- import { composeRootHooks, defineChannel, defineResource, defineRoot, defineRoute, defineServerTool } from "@agimon-ai/po-core/extensionFile";
19
- import { readDoomBackgroundWorkService } from "@agimon-ai/po-core/backgroundWork";
20
- import { readDoomChildSessionService } from "@agimon-ai/po-core/childSession";
21
- import { DOOM_CONTEXT_CONTRIBUTIONS_SERVICE, requireDoomContextContributions } from "@agimon-ai/po-core/contextContributions";
22
- import { DOOM_MCP_TOOL_RESOLVER_SERVICE, requireDoomMcpToolResolver } from "@agimon-ai/po-core/mcpToolResolver";
23
- import { createDoomReadinessCoordinator, readDoomReadinessCoordinator } from "@agimon-ai/po-core/readiness";
24
- import { provideBackgroundWorkService, readDoomSessionDelivery } from "@agimon-ai/po-session";
25
- import { createDoomTelemetry } from "@agimon-ai/po-telemetry";
17
+ import { composeServerProviderOverrides, defineServerPlugin } from "@agimon-ai/po-core/serverPlugin";
18
+ import { composeRootHooks, composeRootInjects, defineChannel, defineHook, defineResource, defineRoot, defineRoute, defineServerTool, runCleanups } from "@agimon-ai/po-core/extensionFile";
19
+ import { PO_CONFIG_SERVICE } from "@agimon-ai/po-config";
20
+ import { readPoBackgroundWorkService } from "@agimon-ai/po-core/backgroundWork";
21
+ import { PO_CHILD_SESSION_LABELS_SERVICE, readPoChildSessionService } from "@agimon-ai/po-core/childSession";
22
+ import { PO_CONTEXT_CONTRIBUTIONS_SERVICE, requirePoContextContributions } from "@agimon-ai/po-core/contextContributions";
23
+ import { PO_MCP_TOOL_RESOLVER_SERVICE, requirePoMcpToolResolver } from "@agimon-ai/po-core/mcpToolResolver";
24
+ import { provideBackgroundWorkService, readPoSessionDelivery } from "@agimon-ai/po-session";
25
+ import { createPoTelemetry } from "@agimon-ai/po-telemetry";
26
26
  import { Check } from "typebox/value";
27
27
  import { createHash, randomBytes, randomUUID } from "node:crypto";
28
28
  import { spawn } from "node:child_process";
29
29
  import { fileURLToPath } from "node:url";
30
- import { toDoomHeadlessToolResult } from "@agimon-ai/po-core/headless";
30
+ import { toPoHeadlessToolResult } from "@agimon-ai/po-core/headless";
31
+ import { readMajorModesConfig, readPackageConfigurations } from "@agimon-ai/po-config/majorModes";
31
32
  import "@agimon-ai/po-core/cordisHost";
33
+ import { HOME_ENTITY_LIMIT } from "@agimon-ai/po-core/hubChannel";
32
34
  //#region src/services/config/index.ts
33
35
  /**
34
36
  * `subagent` tool config file: read/write, and the scoped `ExtensionConfig`
@@ -170,13 +172,13 @@ var AdmissionGate = class {
170
172
  }
171
173
  if (!waited) {
172
174
  waited = true;
173
- request.report?.("doom_team.admission_wait", {
175
+ request.report?.("po_team.admission_wait", {
174
176
  "team.live_runs": live,
175
177
  "team.max_live_runs": request.maxLiveRuns
176
178
  });
177
179
  }
178
180
  if (this.now() >= deadline) {
179
- request.report?.("doom_team.admission_timeout", {
181
+ request.report?.("po_team.admission_timeout", {
180
182
  "team.live_runs": live,
181
183
  "team.max_live_runs": request.maxLiveRuns,
182
184
  duration_ms: this.now() - waitStartedAt
@@ -508,26 +510,6 @@ function claimAgentIdentity(scope, input) {
508
510
  }
509
511
  return degraded;
510
512
  }
511
- /**
512
- * Point an existing identity at a new run id.
513
- *
514
- * Restore mints a fresh run id for what is conceptually the same agent, so the
515
- * identity has to be carried across rather than derived again. This is a
516
- * single-writer rewrite of one claim, so the lost-update hazard that rules out
517
- * a shared registry file does not apply.
518
- */
519
- function adoptAgentIdentity(scope, identity, runId) {
520
- const file = path.join(agentIdentitiesDir(scope), `${identity}.json`);
521
- const existing = readClaim(file);
522
- if (!existing) return;
523
- try {
524
- fs.writeFileSync(file, `${JSON.stringify({
525
- ...existing,
526
- runId,
527
- claimedAt: Date.now()
528
- })}\n`, { mode: 384 });
529
- } catch {}
530
- }
531
513
  /** Render an identity for display, marking the inline case that has no agent file on disk. */
532
514
  function formatAgentIdentity(identity, inline) {
533
515
  if (!identity) return void 0;
@@ -1063,7 +1045,7 @@ function preflightSubagentDepth(depth, config) {
1063
1045
  }
1064
1046
  //#endregion
1065
1047
  //#region src/services/errors/index.ts
1066
- var DoomTeamExpectedError = class extends Error {
1048
+ var PoTeamExpectedError = class extends Error {
1067
1049
  code;
1068
1050
  retryable;
1069
1051
  recovery;
@@ -1072,11 +1054,11 @@ var DoomTeamExpectedError = class extends Error {
1072
1054
  this.code = code;
1073
1055
  this.retryable = retryable;
1074
1056
  this.recovery = recovery;
1075
- this.name = "DoomTeamExpectedError";
1057
+ this.name = "PoTeamExpectedError";
1076
1058
  }
1077
1059
  };
1078
1060
  function invalidRequest(message, recovery) {
1079
- return new DoomTeamExpectedError("invalid_request", message, false, recovery);
1061
+ return new PoTeamExpectedError("invalid_request", message, false, recovery);
1080
1062
  }
1081
1063
  //#endregion
1082
1064
  //#region src/services/modelInfo/index.ts
@@ -1116,6 +1098,12 @@ function toModelInfo(model) {
1116
1098
  thinkingLevelMap: model.thinkingLevelMap
1117
1099
  };
1118
1100
  }
1101
+ /** The parent's authenticated models as the host lists them now; only the parent model when it cannot list. */
1102
+ async function availableModelInfos(execution) {
1103
+ const models = await execution.getAvailableModels?.();
1104
+ if (models) return models.map(toModelInfo);
1105
+ return execution.model ? [toModelInfo(execution.model)] : [];
1106
+ }
1119
1107
  function splitKnownThinkingSuffix(model) {
1120
1108
  const colonIdx = model.lastIndexOf(THINKING_SUFFIX_SEPARATOR);
1121
1109
  if (colonIdx === -1) return {
@@ -1399,6 +1387,205 @@ function selectAvailableModel(primaryModel, fallbackModels, availableModels, pre
1399
1387
  }
1400
1388
  }
1401
1389
  //#endregion
1390
+ //#region src/services/nativeRunCoordinator/index.ts
1391
+ /**
1392
+ * The root session's agent. Every Team launcher except the browser's is one of
1393
+ * its tools, so it is who sent a task or steer that arrives through them.
1394
+ */
1395
+ const MAIN_AGENT_SENDER = {
1396
+ kind: "agent",
1397
+ id: "main",
1398
+ name: "Main agent"
1399
+ };
1400
+ /** The person at the browser, who launches and guides runs from the Agents tab. */
1401
+ const HUMAN_SENDER = {
1402
+ kind: "human",
1403
+ name: "You"
1404
+ };
1405
+ function terminalState(event) {
1406
+ return event.state === "completed" || event.state === "failed" || event.state === "stopped" ? event.state : void 0;
1407
+ }
1408
+ function entryKey(sessionId, runId) {
1409
+ return JSON.stringify([sessionId, runId]);
1410
+ }
1411
+ /** A stored run's identity, as `start` put it in the request's `meta`. */
1412
+ function storedRunIdentity(run) {
1413
+ const meta = run.request.meta;
1414
+ if (typeof meta?.identity !== "string") return void 0;
1415
+ return {
1416
+ identity: meta.identity,
1417
+ inline: meta.inline === true,
1418
+ ...typeof meta.agentDescription === "string" ? { agentDescription: meta.agentDescription } : {},
1419
+ ...typeof meta.agentFilePath === "string" ? { agentFilePath: meta.agentFilePath } : {}
1420
+ };
1421
+ }
1422
+ var NativeRunCoordinator = class {
1423
+ childSessions;
1424
+ tracker;
1425
+ notifier;
1426
+ projection;
1427
+ entries = /* @__PURE__ */ new Map();
1428
+ closed = false;
1429
+ closing;
1430
+ constructor(childSessions, tracker, notifier, projection) {
1431
+ this.childSessions = childSessions;
1432
+ this.tracker = tracker;
1433
+ this.notifier = notifier;
1434
+ this.projection = projection;
1435
+ }
1436
+ service() {
1437
+ if (this.closed) throw new Error("The native Team run coordinator is closed.");
1438
+ const service = this.childSessions?.get();
1439
+ if (!service) throw new Error("The in-process Pi child-session service is unavailable.");
1440
+ return service;
1441
+ }
1442
+ async start(sessionId, request, identity) {
1443
+ if (!sessionId.trim()) throw new Error("Native child runs require a session identity.");
1444
+ const handle = await this.service().start({
1445
+ ...request,
1446
+ ...identity ? { meta: {
1447
+ ...request.meta,
1448
+ ...JSON.parse(JSON.stringify(identity))
1449
+ } } : {}
1450
+ });
1451
+ return this.adopt(sessionId, request.scope, request, handle, identity);
1452
+ }
1453
+ async restore(sessionId, run, intercom) {
1454
+ const service = this.service();
1455
+ if (!service.restore) throw new Error("This child-session service cannot restore runs.");
1456
+ const handle = await service.restore(run.request.runId, intercom ? { intercom } : void 0);
1457
+ return this.adopt(sessionId, run.request.scope, run.request, handle, storedRunIdentity(run));
1458
+ }
1459
+ async adopt(sessionId, scope, request, handle, identity) {
1460
+ if (this.closed) {
1461
+ await handle.dispose();
1462
+ throw new Error("The native Team run coordinator closed during child startup.");
1463
+ }
1464
+ const entry = {
1465
+ sessionId,
1466
+ scope,
1467
+ request,
1468
+ ...identity ? { identity } : {},
1469
+ handle,
1470
+ terminal: false
1471
+ };
1472
+ const key = entryKey(sessionId, request.runId);
1473
+ this.entries.set(key, entry);
1474
+ try {
1475
+ const unsubscribe = handle.subscribe((event) => this.onEvent(entry, event));
1476
+ if (entry.terminal) unsubscribe();
1477
+ else entry.unsubscribe = unsubscribe;
1478
+ return handle;
1479
+ } catch (error) {
1480
+ this.entries.delete(key);
1481
+ await handle.dispose().catch(() => void 0);
1482
+ throw error;
1483
+ }
1484
+ }
1485
+ get(sessionId, runId) {
1486
+ return this.entries.get(entryKey(sessionId, runId))?.handle;
1487
+ }
1488
+ /**
1489
+ * The newest entries of a run this session's child service stored, from its durable conversation, also once the
1490
+ * run finished or the session restarted; undefined for a run it cannot read, such as an external one.
1491
+ */
1492
+ async readTranscript(sessionId, runId, limit) {
1493
+ const service = this.service();
1494
+ if (!service.readTranscriptPage) return void 0;
1495
+ try {
1496
+ return (await service.readTranscriptPage(runId, { limit })).entries;
1497
+ } catch (error) {
1498
+ if (this.entries.has(entryKey(sessionId, runId))) throw error;
1499
+ return;
1500
+ }
1501
+ }
1502
+ status(sessionId, runId) {
1503
+ return this.tracker.getNative(sessionId, runId);
1504
+ }
1505
+ async reported(runId) {
1506
+ return this.service().reported?.(runId);
1507
+ }
1508
+ async steer(sessionId, runId, message, signal) {
1509
+ const handle = this.get(sessionId, runId);
1510
+ if (!handle) throw new Error(`No active native run matches '${runId}'.`);
1511
+ await handle.steer(message, signal);
1512
+ }
1513
+ async stop(sessionId, runId, reason) {
1514
+ const handle = this.get(sessionId, runId);
1515
+ if (!handle) throw new Error(`No active native run matches '${runId}'.`);
1516
+ await handle.stop(reason);
1517
+ }
1518
+ async close() {
1519
+ this.closed = true;
1520
+ this.closing ??= (async () => {
1521
+ const active = [...this.entries.values()];
1522
+ for (const entry of active) {
1523
+ entry.unsubscribe?.();
1524
+ this.entries.delete(entryKey(entry.sessionId, entry.request.runId));
1525
+ }
1526
+ const failures = (await Promise.allSettled(active.map((entry) => entry.handle.dispose()))).filter((outcome) => outcome.status === "rejected").map((outcome) => outcome.reason);
1527
+ if (failures.length) throw new AggregateError(failures, "Native Team run shutdown failed.");
1528
+ })();
1529
+ return this.closing;
1530
+ }
1531
+ onEvent(entry, event) {
1532
+ entry.startedAt ??= event.timestamp;
1533
+ const status = terminalState(event);
1534
+ const projection = {
1535
+ runId: event.runId,
1536
+ agent: entry.request.agent,
1537
+ ...entry.identity ? {
1538
+ identity: entry.identity.identity,
1539
+ inline: entry.identity.inline
1540
+ } : {},
1541
+ ...entry.identity?.agentDescription ? { agentDescription: entry.identity.agentDescription } : {},
1542
+ ...entry.identity?.agentFilePath ? { agentFilePath: entry.identity.agentFilePath } : {},
1543
+ task: entry.request.task,
1544
+ cwd: entry.request.cwd,
1545
+ runtime: "pi",
1546
+ status: event.state === "starting" ? "queued" : event.state === "running" ? "running" : event.state,
1547
+ startedAt: entry.startedAt,
1548
+ updatedAt: event.timestamp,
1549
+ ...event.cost === void 0 ? {} : { cost: event.cost },
1550
+ ...event.tokens === void 0 ? {} : { tokens: event.tokens },
1551
+ ...event.toolCount === void 0 ? {} : { toolCount: event.toolCount },
1552
+ ...event.model === void 0 ? {} : { model: event.model },
1553
+ ...event.thinking === void 0 ? {} : { thinking: event.thinking },
1554
+ ...event.message ? {
1555
+ error: status === "failed" || status === "stopped" ? event.message : void 0,
1556
+ summary: event.message
1557
+ } : {}
1558
+ };
1559
+ this.tracker.upsertNative(entry.sessionId, entry.scope, projection);
1560
+ this.projection?.publish(entry.sessionId, projection);
1561
+ if (!status || entry.terminal) return;
1562
+ entry.terminal = true;
1563
+ entry.unsubscribe?.();
1564
+ this.entries.delete(entryKey(entry.sessionId, entry.request.runId));
1565
+ const result = {
1566
+ runId: entry.request.runId,
1567
+ agent: entry.request.agent,
1568
+ ...entry.identity ? {
1569
+ identity: entry.identity.identity,
1570
+ inline: entry.identity.inline
1571
+ } : {},
1572
+ task: entry.request.task,
1573
+ cwd: entry.request.cwd,
1574
+ runtime: "pi",
1575
+ state: status,
1576
+ success: status === "completed",
1577
+ summary: event.message ?? (status === "completed" ? "Native child run completed." : `Native child run ${status}.`),
1578
+ durationMs: Math.max(0, event.timestamp - (this.tracker.getNative(entry.sessionId, entry.request.runId)?.startedAt ?? event.timestamp)),
1579
+ sessionId: entry.sessionId
1580
+ };
1581
+ if (this.notifier) this.notifier.deliver(result).then(async (delivered) => {
1582
+ if (!delivered) return;
1583
+ this.tracker.acknowledgeHandoff(entry.sessionId, entry.request.runId);
1584
+ await this.childSessions?.get()?.markReported?.(entry.request.runId);
1585
+ }).catch(() => void 0);
1586
+ }
1587
+ };
1588
+ //#endregion
1402
1589
  //#region src/services/runtimeRegistry/index.ts
1403
1590
  /**
1404
1591
  * Which agent runtime executes a run, and how to invoke it.
@@ -1515,12 +1702,12 @@ async function runWithConcurrency(factories, concurrency, report) {
1515
1702
  }
1516
1703
  }
1517
1704
  }
1518
- report?.("doom_team.concurrency_started", {
1705
+ report?.("po_team.concurrency_started", {
1519
1706
  "team.task_count": factories.length,
1520
1707
  "team.concurrency": effectiveConcurrency
1521
1708
  });
1522
1709
  await Promise.all(Array.from({ length: effectiveConcurrency }, () => worker()));
1523
- report?.("doom_team.concurrency_finished", {
1710
+ report?.("po_team.concurrency_finished", {
1524
1711
  "team.task_count": factories.length,
1525
1712
  "team.concurrency": effectiveConcurrency,
1526
1713
  "team.failure_count": results.filter((result) => result?.status === "rejected").length,
@@ -1567,8 +1754,8 @@ async function runWithConcurrency(factories, concurrency, report) {
1567
1754
  * be worse than not checking at all (false confidence). Wiring this needs
1568
1755
  * a real session-scoped store owned by a lifecycle-bound service - a
1569
1756
  * follow-up, not guessed here.
1570
- * - Fork context is resolved here and requires a captured persisted Pi source;
1571
- * unavailable or non-Pi fork requests fail closed rather than becoming fresh
1757
+ * - Fork context is resolved here and requires the parent session's branch;
1758
+ * unavailable fork requests fail closed rather than becoming fresh
1572
1759
  * launches.
1573
1760
  * - Per-task overrides with no direct `BuildPiArgsInput` field confirmed yet
1574
1761
  * (`skill`, `toolBudget`, `turnBudget`, `outputSchema`/structured output,
@@ -1604,9 +1791,8 @@ function describeForkFailure(reason) {
1604
1791
  /**
1605
1792
  * Map a captured parent branch onto the spawn request's fork fields.
1606
1793
  *
1607
- * `parentSessionFile` and `parentLeafId` describe a terminal Pi capture only. A
1608
- * v4 source already carries its own file and branch, so flattening it into
1609
- * those fields would describe it in the wrong vocabulary and lose the branch.
1794
+ * A headless host's branch forks a native child inside its own Harness. A
1795
+ * terminal Pi capture describes a branch for an external runtime only.
1610
1796
  */
1611
1797
  function forkRequestFields(captured) {
1612
1798
  if (!captured) return { parentForkFailure: "no-capture-installed" };
@@ -1614,7 +1800,6 @@ function forkRequestFields(captured) {
1614
1800
  if (!captured.ok) return { parentForkFailure: captured.reason };
1615
1801
  const { source } = captured;
1616
1802
  return {
1617
- parentForkSource: source.terminalSource,
1618
1803
  ...source.sessionFile ? { parentSessionFile: source.sessionFile } : {},
1619
1804
  parentLeafId: source.leafId
1620
1805
  };
@@ -1636,34 +1821,25 @@ function captureSessionForkSource(manager, mode) {
1636
1821
  ok: false,
1637
1822
  reason: "unsupported-version"
1638
1823
  };
1639
- const branch = manager.getBranch(leafId);
1640
- if (branch.at(-1)?.id !== leafId) return {
1824
+ if (manager.getBranch(leafId).at(-1)?.id !== leafId) return {
1641
1825
  ok: false,
1642
1826
  reason: "branch-mismatch"
1643
1827
  };
1644
- const sourceSessionId = manager.getSessionId();
1645
- if (!sourceSessionId.trim()) return {
1828
+ if (!manager.getSessionId().trim()) return {
1646
1829
  ok: false,
1647
1830
  reason: "no-session-id"
1648
1831
  };
1649
- const terminalSource = Object.freeze({
1650
- kind: "terminal-pi-fork",
1651
- sourceSessionId,
1652
- sourceLeafId: leafId,
1653
- snapshotJsonl: `${[header, ...branch].map((record) => JSON.stringify(record)).join("\n")}\n`
1654
- });
1655
1832
  const sessionFile = manager.getSessionFile();
1656
1833
  return {
1657
1834
  ok: true,
1658
1835
  source: {
1659
1836
  leafId,
1660
- terminalSource,
1661
1837
  ...readableSessionFile(sessionFile) ? { sessionFile } : {}
1662
1838
  }
1663
1839
  };
1664
1840
  }
1665
1841
  function resolveEffectiveContext(taskInput, agent) {
1666
- return taskInput.sessionFile ? CONTEXT_FRESH : taskInput.context ?? agent.defaultContext ?? CONTEXT_FRESH;
1842
+ return taskInput.context ?? agent.defaultContext ?? CONTEXT_FRESH;
1667
1843
  }
1668
1844
  const DEFAULT_PARALLEL_CONCURRENCY = 4;
1669
1845
  /**
@@ -1673,14 +1849,13 @@ const DEFAULT_PARALLEL_CONCURRENCY = 4;
1673
1849
  * soon as its child confirms it started, and the child then runs detached. So
1674
1850
  * `runWithConcurrency` throttles how fast children are STARTED, not how many
1675
1851
  * are running - `{tasks: [8], concurrency: 4}` ends up with eight live model
1676
- * processes, not four. 8 matches the sibling implementation's
1677
- * `PI_TEAM_MATE_MAX_PARALLEL`.
1852
+ * processes, not four. 30 lets one call fan a whole swarm out.
1678
1853
  *
1679
1854
  * This is a per-call declaration limit and nothing more. What actually bounds
1680
1855
  * live children across concurrent calls is `AdmissionGate`
1681
1856
  * (`runs/shared/admissionGate.ts`), which every spawn passes through.
1682
1857
  */
1683
- const DEFAULT_PARALLEL_MAX_TASKS = 8;
1858
+ const DEFAULT_PARALLEL_MAX_TASKS = 30;
1684
1859
  /**
1685
1860
  * Process-wide ceiling on children ALIVE at once.
1686
1861
  *
@@ -1711,14 +1886,14 @@ function bestEffortRuntimeWarnings(runtime, agentConfig, excludeTools) {
1711
1886
  ...agentConfig.skills?.length || agentConfig.skillPath?.length ? ["skills"] : [],
1712
1887
  ...agentConfig.extensions !== void 0 || agentConfig.subagentOnlyExtensions?.length ? ["extensions"] : []
1713
1888
  ];
1714
- if (unsupportedResources.length > 0) warnings.push(`Doom Team cannot project or enforce configured Pi ${unsupportedResources.join(", ")} on runtime '${runtime}'.`);
1889
+ if (unsupportedResources.length > 0) warnings.push(`Po Team cannot project or enforce configured Pi ${unsupportedResources.join(", ")} on runtime '${runtime}'.`);
1715
1890
  if (agentConfig.skills?.length) warnings.push(`Configured skills were not injected: ${agentConfig.skills.join(", ")}.`);
1716
1891
  if (excludeTools?.length) warnings.push(`Runtime '${runtime}' cannot enforce Team package tool exclusions; launched best effort for: ${excludeTools.join(", ")}.`);
1717
1892
  return warnings;
1718
1893
  }
1719
1894
  function readWorkflowPreferences(payload) {
1720
1895
  if (payload === void 0) return {};
1721
- const invalid = (detail) => new DoomTeamExpectedError(ERROR_CODE_INVALID_REQUEST, `WORKFLOW_RUN_CONFIG ${detail}`, false, "Provide a JSON object with optional nonblank subagentModel and recognized subagentThinking.");
1896
+ const invalid = (detail) => new PoTeamExpectedError(ERROR_CODE_INVALID_REQUEST, `WORKFLOW_RUN_CONFIG ${detail}`, false, "Provide a JSON object with optional nonblank subagentModel and recognized subagentThinking.");
1722
1897
  let value;
1723
1898
  try {
1724
1899
  value = JSON.parse(payload);
@@ -1736,29 +1911,40 @@ function readWorkflowPreferences(payload) {
1736
1911
  if (preferences.subagentThinking && !THINKING_LEVELS.some((level) => level === preferences.subagentThinking)) throw invalid(`subagentThinking must be one of: ${THINKING_LEVELS.join(", ")}.`);
1737
1912
  return preferences;
1738
1913
  }
1739
- function resolvedModelSelection(taskInput, agentConfig, runtime, parentModel, availableModels, teamPackageModels, preferences) {
1914
+ function resolvedModelSelection(taskInput, agentConfig, runtime, parentModel, availableModels, teamPackageModels, preferences, planning = {}, teamThinking) {
1740
1915
  const parentModelId = isPiRuntime(runtime) && parentModel ? `${parentModel.provider}/${parentModel.id}` : void 0;
1741
1916
  const agentModels = [...agentConfig.modelSource?.scope === "package" ? [] : [agentConfig.model], ...agentConfig.fallbackModels ?? []];
1742
1917
  const candidates = (isPiRuntime(runtime) ? [
1743
1918
  taskInput.model,
1744
1919
  preferences.subagentModel,
1920
+ planning.subagentModel,
1745
1921
  ...agentModels,
1746
1922
  ...teamPackageModels ?? [],
1747
1923
  parentModelId
1748
1924
  ] : [
1749
1925
  taskInput.model,
1750
1926
  preferences.subagentModel,
1927
+ planning.subagentModel,
1751
1928
  ...agentModels,
1752
1929
  ...teamPackageModels ?? []
1753
1930
  ]).filter((candidate) => Boolean(candidate?.trim()));
1754
1931
  const [primaryModel, ...fallbackModels] = candidates;
1755
1932
  const agentThinking = typeof agentConfig.thinking === "string" ? agentConfig.thinking : void 0;
1756
- if (!preferences.subagentModel && !preferences.subagentThinking) return {
1757
- primaryModel,
1758
- fallbackModels,
1759
- model: selectAvailableModel(primaryModel, fallbackModels, availableModels),
1760
- thinking: agentThinking
1933
+ const teamThinkingFor = (model) => {
1934
+ if (agentConfig.thinking === false || !teamThinking) return void 0;
1935
+ const info = findModelInfo(model, availableModels);
1936
+ return info && !getSupportedThinkingLevels(info).some((level) => level === teamThinking) ? void 0 : teamThinking;
1761
1937
  };
1938
+ if (!preferences.subagentModel && !preferences.subagentThinking && !planning.subagentModel && !planning.subagentThinking) {
1939
+ const model = selectAvailableModel(primaryModel, fallbackModels, availableModels);
1940
+ const selectedSuffix = model ? splitKnownThinkingSuffix(model).thinkingSuffix.slice(1) : void 0;
1941
+ return {
1942
+ primaryModel,
1943
+ fallbackModels,
1944
+ model,
1945
+ thinking: agentThinking ?? (selectedSuffix || void 0) ?? teamThinkingFor(model)
1946
+ };
1947
+ }
1762
1948
  let model;
1763
1949
  let selectedCandidate;
1764
1950
  for (const candidate of candidates) {
@@ -1770,11 +1956,13 @@ function resolvedModelSelection(taskInput, agentConfig, runtime, parentModel, av
1770
1956
  }
1771
1957
  const selectedSuffix = model ? splitKnownThinkingSuffix(model).thinkingSuffix.slice(1) : void 0;
1772
1958
  const explicitThinking = selectedCandidate === taskInput.model ? selectedSuffix : void 0;
1773
- const thinking = explicitThinking || preferences.subagentThinking || selectedSuffix || agentThinking;
1774
- const workflowThinking = !explicitThinking && (preferences.subagentThinking || selectedCandidate === preferences.subagentModel && selectedSuffix);
1959
+ const planningThinking = !planning.subagentModel || selectedCandidate === planning.subagentModel ? planning.subagentThinking : void 0;
1960
+ const preferredThinking = (!preferences.subagentModel || selectedCandidate === preferences.subagentModel || selectedCandidate === taskInput.model ? preferences.subagentThinking : void 0) || planningThinking;
1961
+ const thinking = explicitThinking || preferredThinking || selectedSuffix || agentThinking || teamThinkingFor(model);
1962
+ const workflowThinking = !explicitThinking && (preferredThinking || selectedCandidate === preferences.subagentModel && selectedSuffix);
1775
1963
  const modelInfo = findModelInfo(model, availableModels);
1776
- if (isPiRuntime(runtime) && workflowThinking && modelInfo && !getSupportedThinkingLevels(modelInfo).some((level) => level === thinking)) throw new DoomTeamExpectedError(ERROR_CODE_INVALID_REQUEST, `WORKFLOW_RUN_CONFIG thinking '${thinking}' is unsupported by model '${modelInfo.fullId}'.`, false, `Choose a supported thinking level: ${getSupportedThinkingLevels(modelInfo).join(", ")}.`);
1777
- if (model && selectedSuffix && !explicitThinking && preferences.subagentThinking) model = splitKnownThinkingSuffix(model).baseModel;
1964
+ if (isPiRuntime(runtime) && workflowThinking && modelInfo && !getSupportedThinkingLevels(modelInfo).some((level) => level === thinking)) throw new PoTeamExpectedError(ERROR_CODE_INVALID_REQUEST, `Subagent thinking '${thinking}' is unsupported by model '${modelInfo.fullId}'.`, false, `Choose a supported thinking level: ${getSupportedThinkingLevels(modelInfo).join(", ")}.`);
1965
+ if (model && selectedSuffix && !explicitThinking && preferredThinking) model = splitKnownThinkingSuffix(model).baseModel;
1778
1966
  return {
1779
1967
  primaryModel,
1780
1968
  fallbackModels,
@@ -1800,7 +1988,8 @@ var SpawnPlanner = class {
1800
1988
  nativeRuns;
1801
1989
  teamChannel;
1802
1990
  environment;
1803
- constructor(agents, spawner, policies = new SubagentCapabilityPolicyStore(), skills, reportConcurrencyEvent, _mcpToolResolver, admission, childSessions, nativeRuns, teamChannel, environment = {}) {
1991
+ agentConfig;
1992
+ constructor(agents, spawner, policies = new SubagentCapabilityPolicyStore(), skills, reportConcurrencyEvent, _mcpToolResolver, admission, childSessions, nativeRuns, teamChannel, environment = {}, agentConfig) {
1804
1993
  this.agents = agents;
1805
1994
  this.spawner = spawner;
1806
1995
  this.policies = policies;
@@ -1811,6 +2000,7 @@ var SpawnPlanner = class {
1811
2000
  this.nativeRuns = nativeRuns;
1812
2001
  this.teamChannel = teamChannel;
1813
2002
  this.environment = environment;
2003
+ this.agentConfig = agentConfig;
1814
2004
  }
1815
2005
  generateRunId() {
1816
2006
  return crypto.randomUUID();
@@ -1820,15 +2010,9 @@ var SpawnPlanner = class {
1820
2010
  try {
1821
2011
  stat = fs.statSync(cwd);
1822
2012
  } catch {
1823
- throw new DoomTeamExpectedError(ERROR_CODE_INVALID_REQUEST, `Working directory '${cwd}' does not exist.`, false, "Retry with an existing directory.");
2013
+ throw new PoTeamExpectedError(ERROR_CODE_INVALID_REQUEST, `Working directory '${cwd}' does not exist.`, false, "Retry with an existing directory.");
1824
2014
  }
1825
- if (!stat.isDirectory()) throw new DoomTeamExpectedError(ERROR_CODE_INVALID_REQUEST, `Working directory '${cwd}' is not a directory.`, false, "Retry with an existing directory.");
1826
- }
1827
- resolveTeamPackageExcludeTools() {
1828
- return resolveActiveTeamPackageConfig()?.config.excludeTools;
1829
- }
1830
- resolveTeamPackageModels() {
1831
- return resolveActiveTeamModelSpecs();
2015
+ if (!stat.isDirectory()) throw new PoTeamExpectedError(ERROR_CODE_INVALID_REQUEST, `Working directory '${cwd}' is not a directory.`, false, "Retry with an existing directory.");
1832
2016
  }
1833
2017
  executableAvailable(command) {
1834
2018
  if (path.isAbsolute(command) || command.includes(path.sep)) try {
@@ -1848,17 +2032,17 @@ var SpawnPlanner = class {
1848
2032
  }
1849
2033
  resolveTaskList(request) {
1850
2034
  const hasSingle = request.single !== void 0;
1851
- if (hasSingle === (request.tasks !== void 0)) throw new DoomTeamExpectedError(ERROR_CODE_INVALID_REQUEST, "A run requires exactly one of internal single or tasks representations.", false, "Submit a non-empty requests array.");
2035
+ if (hasSingle === (request.tasks !== void 0)) throw new PoTeamExpectedError(ERROR_CODE_INVALID_REQUEST, "A run requires exactly one of internal single or tasks representations.", false, "Submit a non-empty requests array.");
1852
2036
  if (hasSingle) return [request.single];
1853
- if (!request.tasks || request.tasks.length === 0) throw new DoomTeamExpectedError(ERROR_CODE_INVALID_REQUEST, "A run requires at least one entry in its requests collection.", false, "Submit a non-empty requests array.");
2037
+ if (!request.tasks || request.tasks.length === 0) throw new PoTeamExpectedError(ERROR_CODE_INVALID_REQUEST, "A run requires at least one entry in its requests collection.", false, "Submit a non-empty requests array.");
1854
2038
  return request.tasks;
1855
2039
  }
1856
- resolveAgentOrThrow(name, cwd, scope) {
1857
- const resolved = this.agents.find(cwd, scope, name);
1858
- if (!resolved) throw new DoomTeamExpectedError(ERROR_CODE_AGENT_NOT_FOUND, `Unknown agent '${name}'.`, false, "Call subagent({\"action\":\"agents\"}) and retry with an exact name.");
2040
+ async resolveAgentOrThrow(name, cwd, scope) {
2041
+ const resolved = await this.agents.find(cwd, scope, name);
2042
+ if (!resolved) throw new PoTeamExpectedError(ERROR_CODE_AGENT_NOT_FOUND, `Unknown agent '${name}'.`, false, "Call subagent({\"action\":\"agents\"}) and retry with an exact name.");
1859
2043
  return resolved;
1860
2044
  }
1861
- resolveExecutableAgent(taskInput, cwd, scope) {
2045
+ async resolveExecutableAgent(taskInput, cwd, scope) {
1862
2046
  if (!taskInput.inlineAgent) return this.resolveAgentOrThrow(taskInput.agent, cwd, scope);
1863
2047
  return {
1864
2048
  name: taskInput.agent.trim(),
@@ -1952,12 +2136,12 @@ var SpawnPlanner = class {
1952
2136
  * the module header.
1953
2137
  */
1954
2138
  async spawnOneChild(input) {
1955
- const { runId, operationId, agentConfig, identity, capabilityCeiling, excludeTools, teamPackageModels, workflowPreferences, taskInput, childIndex, fanout, fallbackCwd, parentSessionId, parentForkSource, sessionScope, environment, parentSessionFile, maxLiveRuns, admissionTimeoutMs, handshakeTimeoutMs, artifacts, artifactDir, availableModels, parentModel, runtime, runtimes, skillProjectionCache } = input;
2139
+ const { runId, operationId, agentConfig, identity, capabilityCeiling, excludeTools, teamPackageModels, teamThinking, workflowPreferences, planningPreferences, taskInput, childIndex, fanout, fallbackCwd, parentSessionId, parentForkSource, sessionScope, environment, parentSessionFile, maxLiveRuns, admissionTimeoutMs, handshakeTimeoutMs, artifacts, artifactDir, availableModels, parentModel, runtime, runtimes, skillProjectionCache, sender } = input;
1956
2140
  const cwd = taskInput.cwd ?? fallbackCwd;
1957
2141
  const task = taskInput.task ?? "";
1958
2142
  const effectiveContext = resolveEffectiveContext(taskInput, agentConfig);
1959
2143
  const effectiveRuntime = taskInput.runtime ?? runtime ?? agentConfig.runtime ?? "pi";
1960
- const modelSelection = resolvedModelSelection(taskInput, agentConfig, effectiveRuntime, parentModel, availableModels, teamPackageModels, workflowPreferences);
2144
+ const modelSelection = resolvedModelSelection(taskInput, agentConfig, effectiveRuntime, parentModel, availableModels, teamPackageModels, workflowPreferences, planningPreferences, teamThinking);
1961
2145
  if (modelSelection.primaryModel && availableModels !== void 0 && !modelSelection.model) return {
1962
2146
  agent: agentConfig.name,
1963
2147
  identity: identity.identity,
@@ -1979,7 +2163,11 @@ var SpawnPlanner = class {
1979
2163
  error: error instanceof Error ? error.message : String(error)
1980
2164
  };
1981
2165
  }
1982
- const warnings = [...skillProjection.warnings, ...bestEffortRuntimeWarnings(effectiveRuntime, agentConfig, excludeTools)];
2166
+ const warnings = [
2167
+ ...modelSelection.primaryModel && modelSelection.model && !selectAvailableModel(modelSelection.primaryModel, [], availableModels) ? [`[fallback] ${modelSelection.primaryModel} is unavailable. Using ${modelSelection.model}.`] : [],
2168
+ ...skillProjection.warnings,
2169
+ ...bestEffortRuntimeWarnings(effectiveRuntime, agentConfig, excludeTools)
2170
+ ];
1983
2171
  const warning = warnings.length > 0 ? warnings.join(" ") : void 0;
1984
2172
  const spawnInput = {
1985
2173
  runId,
@@ -1994,7 +2182,6 @@ var SpawnPlanner = class {
1994
2182
  sessionScope,
1995
2183
  ...parentSessionFile ? { parentSessionFile } : {},
1996
2184
  piArgs: {
1997
- ...taskInput.sessionFile ? { sessionFile: taskInput.sessionFile } : {},
1998
2185
  ...modelSelection.model ? { model: modelSelection.model } : {},
1999
2186
  ...capabilityCeiling ? { capabilityCeiling } : {},
2000
2187
  ...effectiveContext === CONTEXT_FORK && parentSessionId ? { parentSessionId } : {}
@@ -2006,7 +2193,7 @@ var SpawnPlanner = class {
2006
2193
  runtimes
2007
2194
  };
2008
2195
  const admission = this.admission;
2009
- if (!admission) throw new Error("Doom Team spawn admission is not configured.");
2196
+ if (!admission) throw new Error("Po Team spawn admission is not configured.");
2010
2197
  let ticket;
2011
2198
  try {
2012
2199
  ticket = await admission.admit({
@@ -2028,11 +2215,8 @@ var SpawnPlanner = class {
2028
2215
  if (isPiRuntime(effectiveRuntime)) {
2029
2216
  if (!this.nativeRuns || !this.childSessions?.get()) throw new Error("The native Team run coordinator is unavailable.");
2030
2217
  const scope = sessionScope;
2031
- const source = taskInput.sessionFile ? {
2032
- kind: "v4-restore",
2033
- sessionFile: taskInput.sessionFile
2034
- } : effectiveContext === CONTEXT_FORK ? parentForkSource ?? (() => {
2035
- throw new Error("Native fork input requires an immutable terminal Pi snapshot.");
2218
+ const source = effectiveContext === CONTEXT_FORK ? parentForkSource ?? (() => {
2219
+ throw new Error("Native fork input requires the parent session's branch.");
2036
2220
  })() : { kind: "fresh" };
2037
2221
  const intercom = this.teamChannel?.createNativeChildIntercom({
2038
2222
  rootSessionId: scope.rootSessionId,
@@ -2055,6 +2239,8 @@ var SpawnPlanner = class {
2055
2239
  source,
2056
2240
  agent: agentConfig.name,
2057
2241
  task,
2242
+ ...warning ? { meta: { warning } } : {},
2243
+ sender: sender ?? MAIN_AGENT_SENDER,
2058
2244
  cwd,
2059
2245
  ...modelSelection.model ? { model: modelSelection.model } : {},
2060
2246
  ...typeof modelSelection.thinking === "string" ? { thinking: modelSelection.thinking } : {},
@@ -2071,7 +2257,9 @@ var SpawnPlanner = class {
2071
2257
  environment
2072
2258
  }, {
2073
2259
  identity: identity.identity,
2074
- inline: identity.inline
2260
+ inline: identity.inline,
2261
+ ...agentConfig.description ? { agentDescription: agentConfig.description } : {},
2262
+ ...agentConfig.filePath ? { agentFilePath: agentConfig.filePath } : {}
2075
2263
  });
2076
2264
  } catch (error) {
2077
2265
  intercom?.dispose?.();
@@ -2113,71 +2301,67 @@ var SpawnPlanner = class {
2113
2301
  }
2114
2302
  }
2115
2303
  async spawn(request, config) {
2304
+ if (!this.agentConfig) throw new PoTeamExpectedError("runtime_unavailable", "Team agent configuration is not loaded yet.", true, "Retry after the session configuration is ready.");
2305
+ const team = await this.agentConfig.current();
2116
2306
  const tasks = this.resolveTaskList(request);
2117
2307
  const workflowPreferences = readWorkflowPreferences(request.environment?.WORKFLOW_RUN_CONFIG ?? this.environment.WORKFLOW_RUN_CONFIG);
2308
+ const planningConfig = request.parentSelection?.state?.["minor-mode"]?.includes("plan") ? team.planningSubagents : void 0;
2309
+ const planningPreferences = {
2310
+ subagentModel: planningConfig?.model,
2311
+ subagentThinking: planningConfig?.thinking
2312
+ };
2118
2313
  const fanout = tasks.length > 1;
2119
- const teamPackageExcludeTools = this.resolveTeamPackageExcludeTools();
2120
- const teamPackageModels = this.resolveTeamPackageModels();
2314
+ const teamPackageExcludeTools = team.excludeTools ? [...team.excludeTools] : void 0;
2315
+ const teamPackageModels = team.models;
2121
2316
  const capabilityCeiling = this.policies.resolve();
2122
2317
  const depthCheck = preflightSubagentDepth(request.currentDepth ?? resolveCurrentSubagentDepth(), config);
2123
- if (depthCheck.error) throw new DoomTeamExpectedError(ERROR_CODE_UNSUPPORTED_OPERATION, depthCheck.error, false, "Give each child a self-contained task within the Team package policy.");
2318
+ if (depthCheck.error) throw new PoTeamExpectedError(ERROR_CODE_UNSUPPORTED_OPERATION, depthCheck.error, false, "Give each child a self-contained task within the Team package policy.");
2124
2319
  const maxTasks = config.parallel?.maxTasks ?? DEFAULT_PARALLEL_MAX_TASKS;
2125
- if (tasks.length > maxTasks) throw new DoomTeamExpectedError(ERROR_CODE_INVALID_REQUEST, `Run requested ${tasks.length} tasks, but at most ${maxTasks} may be declared in one call. Concurrency throttles how fast they start, not how many run.`, false, "Send at most that many requests in this call and wait for them, or raise parallel.maxTasks in the subagent config. Splitting the same width across extra calls does not raise the live-child ceiling.");
2320
+ if (tasks.length > maxTasks) throw new PoTeamExpectedError(ERROR_CODE_INVALID_REQUEST, `Run requested ${tasks.length} tasks, but at most ${maxTasks} may be declared in one call. Concurrency throttles how fast they start, not how many run.`, false, "Send at most that many requests in this call and wait for them, or raise parallel.maxTasks in the subagent config. Splitting the same width across extra calls does not raise the live-child ceiling.");
2126
2321
  for (const taskInput of tasks) {
2127
- if (!taskInput.task?.trim()) throw new DoomTeamExpectedError(ERROR_CODE_INVALID_REQUEST, `Run request for '${taskInput.agent}' requires a nonblank task.`, false, "Provide a self-contained nonblank task.");
2128
- if (taskInput.inlineAgent && !taskInput.inlineAgent.systemPrompt.trim()) throw new DoomTeamExpectedError(ERROR_CODE_INVALID_REQUEST, `Inline agent '${taskInput.agent}' requires a nonblank system prompt.`, false, "Provide a focused read-only exploration role.");
2322
+ if (!taskInput.task?.trim()) throw new PoTeamExpectedError(ERROR_CODE_INVALID_REQUEST, `Run request for '${taskInput.agent}' requires a nonblank task.`, false, "Provide a self-contained nonblank task.");
2323
+ if (taskInput.inlineAgent && !taskInput.inlineAgent.systemPrompt.trim()) throw new PoTeamExpectedError(ERROR_CODE_INVALID_REQUEST, `Inline agent '${taskInput.agent}' requires a nonblank system prompt.`, false, "Provide a focused read-only exploration role.");
2129
2324
  }
2130
2325
  const resolvedAgentCache = /* @__PURE__ */ new Map();
2131
- const resolvedAgents = tasks.map((taskInput) => {
2132
- if (taskInput.inlineAgent) return this.resolveExecutableAgent(taskInput, taskInput.cwd ?? request.cwd, request.agentScope);
2326
+ const resolvedAgents = [];
2327
+ for (const taskInput of tasks) {
2133
2328
  const cacheKey = [
2134
2329
  request.agentScope,
2135
2330
  canonicalizeDiscoveryCwd(taskInput.cwd ?? request.cwd),
2136
2331
  taskInput.agent
2137
2332
  ].join("\0");
2138
- const cached = resolvedAgentCache.get(cacheKey);
2139
- if (cached) return cached;
2140
- const resolved = this.resolveExecutableAgent(taskInput, taskInput.cwd ?? request.cwd, request.agentScope);
2141
- resolvedAgentCache.set(cacheKey, resolved);
2142
- return resolved;
2143
- });
2333
+ let resolved = taskInput.inlineAgent ? void 0 : resolvedAgentCache.get(cacheKey);
2334
+ if (!resolved) {
2335
+ resolved = await this.resolveExecutableAgent(taskInput, taskInput.cwd ?? request.cwd, request.agentScope);
2336
+ if (!taskInput.inlineAgent) resolvedAgentCache.set(cacheKey, resolved);
2337
+ }
2338
+ resolvedAgents.push(resolved);
2339
+ }
2144
2340
  for (const [index, taskInput] of tasks.entries()) {
2145
2341
  const effectiveContext = resolveEffectiveContext(taskInput, resolvedAgents[index]);
2146
2342
  const runtime = taskInput.runtime ?? request.runtime ?? resolvedAgents[index].runtime ?? "pi";
2147
2343
  if (effectiveContext === CONTEXT_FORK) {
2148
- if (!isPiRuntime(runtime)) throw new DoomTeamExpectedError(ERROR_CODE_UNSUPPORTED_CONTEXT, `Fork context requires ${PI_RUNTIME_REQUIREMENT} for '${taskInput.agent}'.`, false, "Use a Pi agent for fork context or explicitly request a fresh run.");
2149
- if (!request.parentForkSource) throw new DoomTeamExpectedError(ERROR_CODE_UNSUPPORTED_CONTEXT, `Fork context is unavailable for '${taskInput.agent}': ${describeForkFailure(request.parentForkFailure)}.`, false, "Use an active Pi session with a completed parent turn or explicitly request a fresh run.");
2344
+ if (!isPiRuntime(runtime)) throw new PoTeamExpectedError(ERROR_CODE_UNSUPPORTED_CONTEXT, `Fork context requires ${PI_RUNTIME_REQUIREMENT} for '${taskInput.agent}'.`, false, "Use a Pi agent for fork context or explicitly request a fresh run.");
2345
+ if (!request.parentForkSource) throw new PoTeamExpectedError(ERROR_CODE_UNSUPPORTED_CONTEXT, `Fork context is unavailable for '${taskInput.agent}': ${describeForkFailure(request.parentForkFailure)}.`, false, "Use an active Pi session with a completed parent turn or explicitly request a fresh run.");
2150
2346
  }
2151
2347
  }
2152
- if (request.preallocatedRunIds && request.preallocatedRunIds.length !== tasks.length) throw new DoomTeamExpectedError(ERROR_CODE_OPERATION_CONFLICT, "The operation journal run-id count does not match the request count.", false, "Submit the run as a new tool call.");
2348
+ if (request.preallocatedRunIds && request.preallocatedRunIds.length !== tasks.length) throw new PoTeamExpectedError(ERROR_CODE_OPERATION_CONFLICT, "The operation journal run-id count does not match the request count.", false, "Submit the run as a new tool call.");
2153
2349
  const runIds = request.preallocatedRunIds ?? tasks.map(() => this.generateRunId());
2154
- const identities = tasks.map((taskInput, index) => {
2155
- const inline = taskInput.inlineAgent !== void 0;
2156
- if (!taskInput.identity) return claimAgentIdentity(request.sessionScope, {
2157
- agent: resolvedAgents[index].name,
2158
- inline,
2159
- runId: runIds[index]
2160
- });
2161
- adoptAgentIdentity(request.sessionScope, taskInput.identity, runIds[index]);
2162
- return {
2163
- identity: taskInput.identity,
2164
- name: "",
2165
- role: roleFromAgent(resolvedAgents[index].name),
2166
- number: 0,
2167
- inline,
2168
- persisted: true
2169
- };
2170
- });
2350
+ const identities = tasks.map((taskInput, index) => claimAgentIdentity(request.sessionScope, {
2351
+ agent: resolvedAgents[index].name,
2352
+ inline: taskInput.inlineAgent !== void 0,
2353
+ runId: runIds[index]
2354
+ }));
2171
2355
  const runtimes = resolveRuntimeTable(config.runtimes);
2172
2356
  for (const [index, taskInput] of tasks.entries()) {
2173
2357
  const agent = resolvedAgents[index];
2174
2358
  const cwd = taskInput.cwd ?? request.cwd;
2175
2359
  this.validateCwd(cwd);
2176
2360
  const runtime = taskInput.runtime ?? request.runtime ?? agent.runtime ?? "pi";
2177
- const modelSelection = resolvedModelSelection(taskInput, agent, runtime, request.parentModel, request.availableModels, teamPackageModels, workflowPreferences);
2178
- if (modelSelection.primaryModel && request.availableModels !== void 0 && !modelSelection.model) throw new DoomTeamExpectedError(ERROR_CODE_MODEL_UNAVAILABLE, `No authenticated model is available for '${agent.name}'.`, false, "Authenticate the requested model or choose an available model.");
2179
- if (taskInput.inlineAgent && !isPiRuntime(runtime)) throw new DoomTeamExpectedError(ERROR_CODE_UNSUPPORTED_OPERATION, `Inline agent '${taskInput.agent}' requires ${PI_RUNTIME_REQUIREMENT} to enforce its read-only tools.`, false, "Remove the runtime override or use a discovered external-runtime agent without inlineAgent.");
2180
- if (capabilityCeiling && !isPiRuntime(runtime)) throw new DoomTeamExpectedError(ERROR_CODE_UNSUPPORTED_OPERATION, `Runtime '${runtime}' cannot enforce the active capability ceiling.`, false, `Use ${PI_RUNTIME_REQUIREMENT} while plan mode or another capability ceiling is active.`);
2361
+ const modelSelection = resolvedModelSelection(taskInput, agent, runtime, request.parentModel, request.availableModels, teamPackageModels, workflowPreferences, planningPreferences, team.thinking);
2362
+ if (modelSelection.primaryModel && request.availableModels !== void 0 && !modelSelection.model) throw new PoTeamExpectedError(ERROR_CODE_MODEL_UNAVAILABLE, `No authenticated model is available for '${agent.name}'.`, false, "Authenticate the requested model or choose an available model.");
2363
+ if (taskInput.inlineAgent && !isPiRuntime(runtime)) throw new PoTeamExpectedError(ERROR_CODE_UNSUPPORTED_OPERATION, `Inline agent '${taskInput.agent}' requires ${PI_RUNTIME_REQUIREMENT} to enforce its read-only tools.`, false, "Remove the runtime override or use a discovered external-runtime agent without inlineAgent.");
2364
+ if (capabilityCeiling && !isPiRuntime(runtime)) throw new PoTeamExpectedError(ERROR_CODE_UNSUPPORTED_OPERATION, `Runtime '${runtime}' cannot enforce the active capability ceiling.`, false, `Use ${PI_RUNTIME_REQUIREMENT} while plan mode or another capability ceiling is active.`);
2181
2365
  if (!isPiRuntime(runtime)) {
2182
2366
  let command;
2183
2367
  try {
@@ -2186,9 +2370,9 @@ var SpawnPlanner = class {
2186
2370
  cwd
2187
2371
  }).command;
2188
2372
  } catch (error) {
2189
- throw new DoomTeamExpectedError(ERROR_CODE_RUNTIME_UNAVAILABLE, error instanceof Error ? error.message : String(error), false, `Configure a valid external runtime or use ${PI_RUNTIME_REQUIREMENT}.`);
2373
+ throw new PoTeamExpectedError(ERROR_CODE_RUNTIME_UNAVAILABLE, error instanceof Error ? error.message : String(error), false, `Configure a valid external runtime or use ${PI_RUNTIME_REQUIREMENT}.`);
2190
2374
  }
2191
- if (!this.executableAvailable(command)) throw new DoomTeamExpectedError(ERROR_CODE_RUNTIME_UNAVAILABLE, `Executable '${command}' for runtime '${runtime}' is unavailable.`, false, `Install the executable, configure its absolute path, or use ${PI_RUNTIME_REQUIREMENT}.`);
2375
+ if (!this.executableAvailable(command)) throw new PoTeamExpectedError(ERROR_CODE_RUNTIME_UNAVAILABLE, `Executable '${command}' for runtime '${runtime}' is unavailable.`, false, `Install the executable, configure its absolute path, or use ${PI_RUNTIME_REQUIREMENT}.`);
2192
2376
  }
2193
2377
  }
2194
2378
  const concurrency = fanout ? request.concurrency ?? config.parallel?.concurrency ?? DEFAULT_PARALLEL_CONCURRENCY : 1;
@@ -2202,7 +2386,9 @@ var SpawnPlanner = class {
2202
2386
  identity: identities[childIndex],
2203
2387
  excludeTools: teamPackageExcludeTools,
2204
2388
  teamPackageModels,
2389
+ teamThinking: team.thinking,
2205
2390
  workflowPreferences,
2391
+ planningPreferences,
2206
2392
  capabilityCeiling,
2207
2393
  taskInput,
2208
2394
  childIndex,
@@ -2211,6 +2397,7 @@ var SpawnPlanner = class {
2211
2397
  parentSessionId: request.parentSessionId,
2212
2398
  parentForkSource: request.parentForkSource,
2213
2399
  sessionScope: request.sessionScope,
2400
+ ...request.sender ? { sender: request.sender } : {},
2214
2401
  environment: request.environment ?? {},
2215
2402
  parentSessionFile: request.parentSessionFile,
2216
2403
  maxLiveRuns,
@@ -2236,7 +2423,7 @@ var SpawnPlanner = class {
2236
2423
  //#endregion
2237
2424
  //#region src/services/delegationBridge/index.ts
2238
2425
  /**
2239
- * Serves `doom-task` through Team's session-bound delegation service: turn a
2426
+ * Serves `po-task` through Team's session-bound delegation service: turn a
2240
2427
  * request into a spawned run, report its progress, and settle it exactly once.
2241
2428
  *
2242
2429
  * WHY THIS IS NOT IN `pi.ts`:
@@ -2266,7 +2453,7 @@ const DELEGATION_PROGRESS_INTERVAL_MS = 250;
2266
2453
  const SETTLED_DELEGATION_WINDOW = 256;
2267
2454
  /**
2268
2455
  * Terminal states, named because they are an external contract with
2269
- * `doom-task`'s `DelegationManager`, which branches on the exact strings.
2456
+ * `po-task`'s `DelegationManager`, which branches on the exact strings.
2270
2457
  */
2271
2458
  const STATUS_COMPLETED = "completed";
2272
2459
  const STATUS_CANCELLED = "cancelled";
@@ -2288,7 +2475,7 @@ function createDelegationBridge(deps) {
2288
2475
  entry.disposeProgress?.();
2289
2476
  active.delete(entry.requestId);
2290
2477
  rememberSettled(entry.requestId);
2291
- ctx.emit(DOOM_DELEGATION_FINISHED_EVENT, result);
2478
+ ctx.emit(PO_DELEGATION_FINISHED_EVENT, result);
2292
2479
  };
2293
2480
  /** Ask the run to stop. Returns the failure message when the request itself failed. */
2294
2481
  const stop = (entry, reason) => {
@@ -2319,7 +2506,13 @@ function createDelegationBridge(deps) {
2319
2506
  timeoutWarningSent = true;
2320
2507
  const secondsRemaining = Math.max(1, Math.ceil(warningLeadMs / 1e3));
2321
2508
  const secondsLabel = `${secondsRemaining} second${secondsRemaining === 1 ? "" : "s"}`;
2322
- deps.management.steer(runId, `This delegation will time out in about ${secondsLabel}. Stop exploring now, complete the highest-value remaining work, and return a concise result before the deadline. If you cannot finish, return verified findings and blockers immediately.`).catch(() => void 0);
2509
+ deps.management.steer(runId, {
2510
+ text: `This delegation will time out in about ${secondsLabel}. Stop exploring now, complete the highest-value remaining work, and return a concise result before the deadline. If you cannot finish, return verified findings and blockers immediately.`,
2511
+ sender: {
2512
+ kind: "system",
2513
+ name: "Po"
2514
+ }
2515
+ }).catch(() => void 0);
2323
2516
  worked = true;
2324
2517
  }
2325
2518
  const progressKey = [
@@ -2332,7 +2525,7 @@ function createDelegationBridge(deps) {
2332
2525
  ].map((value) => String(value)).join(":");
2333
2526
  if (progressKey === lastProgress) return worked;
2334
2527
  lastProgress = progressKey;
2335
- ctx.emit(DOOM_DELEGATION_UPDATED_EVENT, {
2528
+ ctx.emit(PO_DELEGATION_UPDATED_EVENT, {
2336
2529
  requestId: entry.requestId,
2337
2530
  runId,
2338
2531
  ...job.status !== void 0 ? { status: job.status } : {},
@@ -2407,7 +2600,7 @@ function createDelegationBridge(deps) {
2407
2600
  settled: false
2408
2601
  };
2409
2602
  active.set(request.requestId, entry);
2410
- ctx.emit(DOOM_DELEGATION_ACCEPTED_EVENT, { requestId: request.requestId });
2603
+ ctx.emit(PO_DELEGATION_ACCEPTED_EVENT, { requestId: request.requestId });
2411
2604
  try {
2412
2605
  const forkSource = await session.captureForkSource?.();
2413
2606
  const outcome = (await deps.planner.spawn({
@@ -2422,8 +2615,10 @@ function createDelegationBridge(deps) {
2422
2615
  agentScope: "both",
2423
2616
  sessionScope: session.sessionScope,
2424
2617
  parentSessionId: session.sessionId,
2618
+ parentSelection: session.parentSelection,
2619
+ environment: session.environment,
2425
2620
  ...forkRequestFields(forkSource),
2426
- availableModels: session.availableModels,
2621
+ availableModels: await session.availableModels(),
2427
2622
  ...session.parentModel ? { parentModel: session.parentModel } : {}
2428
2623
  }, deps.loadConfig())).outcomes[0];
2429
2624
  if (!outcome?.runId) {
@@ -2446,10 +2641,15 @@ function createDelegationBridge(deps) {
2446
2641
  return;
2447
2642
  }
2448
2643
  entry.runtimeStartedAt = Date.now();
2449
- ctx.emit(DOOM_DELEGATION_STARTED_EVENT, {
2644
+ ctx.emit(PO_DELEGATION_STARTED_EVENT, {
2450
2645
  requestId: request.requestId,
2451
2646
  runId: outcome.runId
2452
2647
  });
2648
+ if (outcome.warning) try {
2649
+ await session.reportWarning?.(outcome.warning);
2650
+ } catch (error) {
2651
+ process.emitWarning(`Delegation warning could not be delivered: ${error instanceof Error ? error.message : String(error)}`);
2652
+ }
2453
2653
  await monitor(entry, request, ctx, jobs, session.sessionId, session.sessionScope);
2454
2654
  } catch (error) {
2455
2655
  finish(entry, {
@@ -2477,15 +2677,15 @@ function createDelegationBridge(deps) {
2477
2677
  createService(ctx, session) {
2478
2678
  const service = {
2479
2679
  sessionId: session.sessionId,
2480
- generation: `doom-delegation:${crypto.randomUUID()}`,
2680
+ generation: `po-delegation:${crypto.randomUUID()}`,
2481
2681
  async request(request) {
2482
2682
  if (!Check(DelegationRequestSchema, request)) throw new TypeError("Invalid delegation request.");
2483
- ctx.emit(DOOM_DELEGATION_REQUESTED_EVENT, request);
2683
+ ctx.emit(PO_DELEGATION_REQUESTED_EVENT, request);
2484
2684
  await onRequested(request, ctx, session);
2485
2685
  },
2486
2686
  cancel(request) {
2487
2687
  if (!Check(DelegationCancelSchema, request)) throw new TypeError("Invalid delegation cancel request.");
2488
- ctx.emit(DOOM_DELEGATION_CANCELLED_EVENT, request);
2688
+ ctx.emit(PO_DELEGATION_CANCELLED_EVENT, request);
2489
2689
  onCancelled(request.requestId, request.reason, ctx);
2490
2690
  }
2491
2691
  };
@@ -2588,10 +2788,10 @@ function resolveRunnerEntry(builtRelativePath, sourceRelativePath, stem) {
2588
2788
  if (fs.existsSync(builtCandidate)) return builtCandidate;
2589
2789
  const sourceCandidate = path.join(directory, "src", `${sourceRelativePath}.ts`);
2590
2790
  if (fs.existsSync(sourceCandidate)) return sourceCandidate;
2591
- throw new Error(`Doom Team runner '${stem}' is unavailable at '${builtCandidate}' or '${sourceCandidate}'.`);
2791
+ throw new Error(`Po Team runner '${stem}' is unavailable at '${builtCandidate}' or '${sourceCandidate}'.`);
2592
2792
  }
2593
2793
  const parent = path.dirname(directory);
2594
- if (parent === directory) throw new Error(`Could not locate the Doom Team package root for runner '${stem}'.`);
2794
+ if (parent === directory) throw new Error(`Could not locate the Po Team package root for runner '${stem}'.`);
2595
2795
  directory = parent;
2596
2796
  }
2597
2797
  }
@@ -2788,8 +2988,8 @@ const FABLE_STAGE = {
2788
2988
  interrupted: "interrupted"
2789
2989
  };
2790
2990
  const FABLE_EVENT = {
2791
- stageStarted: "doom_team.fable_stage_started",
2792
- finished: "doom_team.fable_finished"
2991
+ stageStarted: "po_team.fable_stage_started",
2992
+ finished: "po_team.fable_finished"
2793
2993
  };
2794
2994
  const SESSION_ENDED_REASON = "Fable bridge session ended.";
2795
2995
  const FABLE_ERROR = {
@@ -2979,7 +3179,7 @@ function createFablePlanBridge(deps) {
2979
3179
  return Object.freeze({
2980
3180
  ...provider,
2981
3181
  sessionId: session.sessionId,
2982
- generation: `doom-fable-plan:${crypto.randomUUID()}`
3182
+ generation: `po-fable-plan:${crypto.randomUUID()}`
2983
3183
  });
2984
3184
  },
2985
3185
  abandonAll() {
@@ -3167,7 +3367,7 @@ function parseTeamToolParams(raw) {
3167
3367
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) throw invalidRequest("Intercom params must be an object.", "Use one documented intercom action shape.");
3168
3368
  const params = raw;
3169
3369
  const action = TEAM_ACTIONS.find((candidate) => candidate === params.action);
3170
- if (!action) throw new DoomTeamExpectedError("unsupported_operation", `Unsupported intercom action: ${String(params.action)}.`, false, "Use members, send, ask, pending, or reply.");
3370
+ if (!action) throw new PoTeamExpectedError("unsupported_operation", `Unsupported intercom action: ${String(params.action)}.`, false, "Use members, send, ask, pending, or reply.");
3171
3371
  if (!isOptionalString(params.to) || !isOptionalString(params.message) || !isOptionalString(params.requestId)) throw invalidRequest("Intercom to, message and requestId values must be strings.", "Correct the action fields and retry.");
3172
3372
  const allowed = new Set(action === "members" || action === "pending" ? ["action"] : action === "reply" ? [
3173
3373
  "action",
@@ -3280,7 +3480,14 @@ var NativeTeamDirectChannel = class {
3280
3480
  bindRuntime: (runtime) => {
3281
3481
  const member = this.members.get(memberId);
3282
3482
  if (!member || disposed) throw new Error(`Native team member '${memberId}' is no longer active.`);
3283
- member.deliver = (message) => runtime.steer(message.content);
3483
+ member.deliver = ({ content, details: { from } }) => runtime.steer({
3484
+ text: content,
3485
+ sender: from.role === "main" ? MAIN_AGENT_SENDER : {
3486
+ kind: "agent",
3487
+ id: from.runId ?? from.name,
3488
+ name: from.name
3489
+ }
3490
+ });
3284
3491
  return {
3285
3492
  name: NATIVE_TEAM_TOOL_NAME,
3286
3493
  description: "Communicate with active native agents in this root session.",
@@ -3378,7 +3585,7 @@ var NativeTeamDirectChannel = class {
3378
3585
  if (params.action === "reply") {
3379
3586
  const request = this.asks.get(params.requestId ?? "");
3380
3587
  const asker = request && this.members.get(request.fromMemberId);
3381
- if (!request || request.toMemberId !== memberId || !asker) throw new DoomTeamExpectedError("recipient_not_found", `No open intercom ask matches requestId '${params.requestId}'. It may already be answered, or its asker has finished.`, false, "Call intercom({\"action\":\"pending\"}) and retry with an exact requestId, or continue without replying.");
3588
+ if (!request || request.toMemberId !== memberId || !asker) throw new PoTeamExpectedError("recipient_not_found", `No open intercom ask matches requestId '${params.requestId}'. It may already be answered, or its asker has finished.`, false, "Call intercom({\"action\":\"pending\"}) and retry with an exact requestId, or continue without replying.");
3382
3589
  await this.deliver(asker, formatDirectMessage(member.context, "reply", request.id, message));
3383
3590
  this.closeAsk(request);
3384
3591
  return {
@@ -3393,7 +3600,7 @@ var NativeTeamDirectChannel = class {
3393
3600
  };
3394
3601
  }
3395
3602
  const target = this.findMember(params.to);
3396
- if (!target) throw new DoomTeamExpectedError("recipient_not_found", `Active intercom member '${params.to}' was not found.`, true, "Call intercom({\"action\":\"members\"}) and retry with an active member id.");
3603
+ if (!target) throw new PoTeamExpectedError("recipient_not_found", `Active intercom member '${params.to}' was not found.`, true, "Call intercom({\"action\":\"members\"}) and retry with an active member id.");
3397
3604
  const messageId = operationMessageId(memberId, operationId);
3398
3605
  const isAsk = params.action === "ask";
3399
3606
  const result = () => ({
@@ -3463,15 +3670,15 @@ var NativeTeamDirectChannel = class {
3463
3670
  }
3464
3671
  async deliver(target, message) {
3465
3672
  const id = target.context.memberId;
3466
- if (this.members.get(id) !== target) throw new DoomTeamExpectedError("communication_unavailable", `Intercom member '${id}' has finished.`, false, "Call intercom({\"action\":\"members\"}) to see who is active.");
3467
- if (!target.deliver) throw new DoomTeamExpectedError("communication_unavailable", `Intercom member '${id}' is not ready.`, true, "Retry after the child session starts.");
3673
+ if (this.members.get(id) !== target) throw new PoTeamExpectedError("communication_unavailable", `Intercom member '${id}' has finished.`, false, "Call intercom({\"action\":\"members\"}) to see who is active.");
3674
+ if (!target.deliver) throw new PoTeamExpectedError("communication_unavailable", `Intercom member '${id}' is not ready.`, true, "Retry after the child session starts.");
3468
3675
  target.inFlight += 1;
3469
3676
  try {
3470
3677
  await target.deliver(message);
3471
3678
  target.delivered = true;
3472
3679
  } catch (error) {
3473
- if (error instanceof DoomTeamExpectedError) throw error;
3474
- throw new DoomTeamExpectedError("communication_unavailable", `Intercom member '${id}' could not receive the message: ${error instanceof Error ? error.message : String(error)}`, true, "Retry shortly, or continue without it.");
3680
+ if (error instanceof PoTeamExpectedError) throw error;
3681
+ throw new PoTeamExpectedError("communication_unavailable", `Intercom member '${id}' could not receive the message: ${error instanceof Error ? error.message : String(error)}`, true, "Retry shortly, or continue without it.");
3475
3682
  } finally {
3476
3683
  target.inFlight -= 1;
3477
3684
  target.release?.(true);
@@ -3498,7 +3705,7 @@ var TeamChannelRuntime = class {
3498
3705
  }
3499
3706
  execute(operationId, rawParams) {
3500
3707
  const context = this.context;
3501
- if (!context || !this.directChannel) throw new DoomTeamExpectedError("communication_unavailable", "Intercom is not active for this session.", true, "Wait for session startup or reload Doom Team.");
3708
+ if (!context || !this.directChannel) throw new PoTeamExpectedError("communication_unavailable", "Intercom is not active for this session.", true, "Wait for session startup or reload Po Team.");
3502
3709
  return this.directChannel.execute(context.memberId, operationId, rawParams);
3503
3710
  }
3504
3711
  bindMainSession(rootSessionId) {
@@ -3555,51 +3762,80 @@ var NativeTeamChannelService = class {
3555
3762
  };
3556
3763
  //#endregion
3557
3764
  //#region src/services/headlessIntercomTool/index.ts
3765
+ /**
3766
+ * Finds the peer a `to` value names: its session ID first, then its session
3767
+ * name, ignoring case, so an agent can use the name a person typed. A name two
3768
+ * peers share is refused with their IDs rather than guessed.
3769
+ */
3770
+ function resolvePeer(peers, to) {
3771
+ const byId = peers.find((peer) => peer.sessionId === to);
3772
+ if (byId !== void 0) return byId;
3773
+ const wanted = to.toLowerCase();
3774
+ const named = peers.filter((peer) => peer.name.trim().toLowerCase() === wanted);
3775
+ if (named.length > 1) throw invalidRequest(`${String(named.length)} sessions are named "${to}".`, `Send to one session ID: ${named.map((peer) => peer.sessionId).join(", ")}.`);
3776
+ return named[0];
3777
+ }
3778
+ function describePeerLine(peer) {
3779
+ const details = [
3780
+ `session ${peer.sessionId}`,
3781
+ ...peer.profile === void 0 ? [] : [`profile ${peer.profile}`],
3782
+ ...peer.sharedGroups.length === 0 ? [] : [`shared groups: ${peer.sharedGroups.join(", ")}`]
3783
+ ];
3784
+ return `- ${peer.name.trim() === "" ? peer.sessionId : peer.name} (${details.join("; ")}): use send; it can reply to you the same way`;
3785
+ }
3558
3786
  function createHeadlessIntercomTool(channel, sessions) {
3559
3787
  return {
3560
3788
  name: "intercom",
3561
3789
  label: "Intercom",
3562
3790
  description: "Communicate with active agents and related live sessions.",
3563
3791
  parameters: TeamToolParamsSchema,
3564
- promptGuidelines: ["Send only new progress, blockers, or decisions. Do not repeat task briefs or delegation instructions.", "Use members to find related sessions, then send to their session ID to delegate work such as workflow recovery. Session peers use send, not ask/reply."],
3792
+ promptGuidelines: ["Send only new progress, blockers, or decisions. Do not repeat task briefs or delegation instructions.", "Use members to find related sessions by the names people give them, then send to that name or its session ID. Session peers use send, not ask/reply, and can send back."],
3565
3793
  executionMode: "serial",
3566
3794
  async execute(toolCallId, parameters, signal, onUpdate) {
3567
3795
  const params = parseTeamToolParams(parameters);
3568
3796
  const peers = channel.current() === void 0 ? [] : sessions?.peers() ?? [];
3569
3797
  const to = params.to?.trim();
3570
- if (sessions !== void 0 && to !== void 0 && peers.includes(to)) {
3571
- if (params.action !== "send") throw invalidRequest("Session peers support send only.", "Use send with the session ID.");
3798
+ const knownId = channel.current() !== void 0 && to !== void 0 && sessions?.canCommunicate(to) === true;
3799
+ const teamMember = to !== void 0 && channel.snapshot().members.some((member) => member.name === to) && !knownId && !peers.some((peer) => peer.sessionId === to);
3800
+ const peer = sessions === void 0 || to === void 0 || teamMember ? void 0 : knownId ? peers.find((candidate) => candidate.sessionId === to) : resolvePeer(peers, to);
3801
+ const recipientId = knownId ? to : peer?.sessionId;
3802
+ if (sessions !== void 0 && recipientId !== void 0) {
3803
+ if (params.action !== "send") throw invalidRequest("Session peers support send only.", "Use send with the session name or ID.");
3572
3804
  const message = validateMessage(params.message);
3573
3805
  const delivery = sessions.delivery();
3574
3806
  if (delivery === void 0) throw new Error("Session delivery is unavailable.");
3807
+ const senderName = sessions.name?.()?.trim() || sessions.sessionId;
3808
+ const toName = peer?.name.trim() || recipientId;
3575
3809
  const receipt = await delivery.deliver({
3576
3810
  deliveryId: `intercom:${sessions.sessionId}:${toolCallId}`,
3577
- recipientKey: to,
3811
+ recipientKey: recipientId,
3578
3812
  kind: "intercom_message",
3579
- prompt: `Message from session ${sessions.sessionId} via intercom.\n\n${message}\n\nReply using intercom({ action: "send", to: "${sessions.sessionId}", message: "..." }).`,
3813
+ prompt: `Message from session ${senderName} via intercom.\n\n${message}\n\nReply using intercom({ action: "send", to: ${JSON.stringify(senderName)}, message: "..." }).`,
3580
3814
  delivery: "steer"
3581
3815
  });
3582
3816
  return {
3583
3817
  content: [{
3584
3818
  type: "text",
3585
- text: `Message queued for session ${to}.`
3819
+ text: `Message queued for session ${toName}.`
3586
3820
  }],
3587
3821
  details: {
3588
3822
  ...receipt,
3589
- to
3823
+ to: recipientId,
3824
+ toName
3590
3825
  }
3591
3826
  };
3592
3827
  }
3593
- const result = toDoomHeadlessToolResult(await channel.execute(toolCallId, parameters, signal, onUpdate && ((partial) => onUpdate(toDoomHeadlessToolResult(partial)))));
3828
+ const result = toPoHeadlessToolResult(await channel.execute(toolCallId, parameters, signal, onUpdate && ((partial) => onUpdate(toPoHeadlessToolResult(partial)))));
3594
3829
  if (params.action !== "members" || peers.length === 0) return result;
3595
3830
  return {
3596
3831
  ...result,
3597
3832
  content: [...result.content, {
3598
3833
  type: "text",
3599
- text: peers.map((id) => `- ${id}: related session (send only)`).join("\n")
3834
+ text: peers.map(describePeerLine).join("\n")
3600
3835
  }],
3601
- details: { members: [...channel.snapshot().members, ...peers.map((id) => ({
3602
- name: id,
3836
+ details: { members: [...channel.snapshot().members, ...peers.map((peer) => ({
3837
+ name: peer.name || peer.sessionId,
3838
+ sessionId: peer.sessionId,
3603
3839
  role: "session"
3604
3840
  }))] }
3605
3841
  };
@@ -3721,6 +3957,7 @@ var AsyncJobTracker = class {
3721
3957
  startedAt: projection.startedAt,
3722
3958
  updatedAt: projection.updatedAt,
3723
3959
  ...projection.error === void 0 ? {} : { error: projection.error },
3960
+ ...projection.promptWithheld ? { promptWithheld: true } : {},
3724
3961
  ...projection.summary === void 0 ? {} : { summary: projection.summary },
3725
3962
  ...projection.tokens === void 0 ? {} : { tokens: projection.tokens },
3726
3963
  ...projection.cost === void 0 ? {} : { cost: projection.cost },
@@ -3932,7 +4169,7 @@ function formatDuration(ms) {
3932
4169
  */
3933
4170
  const MAX_RETAINED_EVENTS = 1e3;
3934
4171
  const NEWLINE_BYTE = 10;
3935
- function isRecord$1(value) {
4172
+ function isRecord$2(value) {
3936
4173
  return typeof value === "object" && value !== null && !Array.isArray(value);
3937
4174
  }
3938
4175
  function parseLine(line) {
@@ -3940,7 +4177,7 @@ function parseLine(line) {
3940
4177
  if (!trimmed) return void 0;
3941
4178
  try {
3942
4179
  const parsed = JSON.parse(trimmed);
3943
- return isRecord$1(parsed) ? parsed : void 0;
4180
+ return isRecord$2(parsed) ? parsed : void 0;
3944
4181
  } catch {
3945
4182
  return;
3946
4183
  }
@@ -3975,7 +4212,7 @@ function parseArgsPayload(payload) {
3975
4212
  if (!payload) return void 0;
3976
4213
  try {
3977
4214
  const parsed = JSON.parse(payload);
3978
- return isRecord$1(parsed) ? parsed : void 0;
4215
+ return isRecord$2(parsed) ? parsed : void 0;
3979
4216
  } catch {
3980
4217
  return;
3981
4218
  }
@@ -4170,125 +4407,63 @@ function readFleetTranscript(transcriptPath) {
4170
4407
  warning: tail.warning
4171
4408
  } : { events: tail.events };
4172
4409
  }
4173
- //#endregion
4174
- //#region src/services/suspendedRuns/index.ts
4410
+ function contentText(content) {
4411
+ if (typeof content === "string") return content;
4412
+ if (!Array.isArray(content)) return "";
4413
+ return content.flatMap((part) => isRecord$2(part) && part.type === "text" && typeof part.text === "string" ? [part.text] : []).join("\n");
4414
+ }
4175
4415
  /**
4176
- * Runs stopped by a session shutdown, and enough about each to start it again.
4177
- *
4178
- * WHY EVERY SHUTDOWN SUSPENDS, WITH NO SPECIAL CASE FOR `reload`:
4179
- * Pi emits `session_shutdown` for `quit`, `reload`, `new`, `resume` and `fork`.
4180
- * An earlier design kept children alive across `reload` alone, on the grounds
4181
- * that `/domains`, `/major-mode` and `/profile` reload the extension rather than
4182
- * change the session. That is true, and it still makes `reload` the one case
4183
- * behaving differently from the other four - which is a rule someone has to
4184
- * remember, and a branch that silently does the wrong thing if Pi ever adds a
4185
- * sixth reason. Suspending uniformly costs a respawn on reload and removes the
4186
- * special case; restore is cheap because the child continues its own transcript
4187
- * rather than starting over.
4188
- *
4189
- * WHY RESTORE IS OFFERED AND NEVER AUTOMATIC:
4190
- * Reopening a session would otherwise spawn N processes and start spending
4191
- * tokens on tasks that may be days stale. `session_start` reports what is
4192
- * suspended; something explicit has to ask for it back.
4193
- *
4194
- * AVOID:
4195
- * - Auto-restoring from any lifecycle event
4196
- * - Recording a suspended run without its `sessionFile`; without it a restore
4197
- * restarts the work instead of continuing it
4416
+ * Events of a native run's durable transcript page, so a native run renders through the same tail as a file
4417
+ * transcript. Entries are projected messages: `{ type: 'message', timestamp, message }` with Pi message roles.
4198
4418
  */
4199
- const SUSPENDED_RUN_VERSION = 1;
4200
- const JSON_SUFFIX = ".json";
4201
- function suspendedPath(scope, runId) {
4202
- return path.join(scopeSuspendedDir(scope), `${runId}${JSON_SUFFIX}`);
4203
- }
4204
- function suspendRun(scope, record) {
4205
- writeAtomicJson(suspendedPath(scope, record.runId), {
4206
- version: SUSPENDED_RUN_VERSION,
4207
- ...record
4208
- });
4209
- }
4210
- function parseSuspendedRun(raw) {
4211
- let parsed;
4212
- try {
4213
- parsed = JSON.parse(raw);
4214
- } catch {
4215
- return;
4216
- }
4217
- const result = parseVersioned(parsed, [SUSPENDED_RUN_VERSION]);
4218
- return result.ok && typeof result.value.runId === "string" ? result.value : void 0;
4219
- }
4220
- function listSuspendedRuns(scope) {
4221
- let entries;
4222
- try {
4223
- entries = fs.readdirSync(scopeSuspendedDir(scope));
4224
- } catch {
4225
- return [];
4226
- }
4227
- const runs = [];
4419
+ function fleetEventsFromEntries(entries) {
4420
+ const events = [];
4421
+ const calls = /* @__PURE__ */ new Map();
4228
4422
  for (const entry of entries) {
4229
- if (!entry.endsWith(JSON_SUFFIX)) continue;
4230
- let run;
4231
- try {
4232
- run = parseSuspendedRun(fs.readFileSync(path.join(scopeSuspendedDir(scope), entry), "utf-8"));
4233
- } catch {
4234
- continue;
4423
+ if (!isRecord$2(entry) || entry.type !== "message" || !isRecord$2(entry.message)) continue;
4424
+ const message = entry.message;
4425
+ const at = typeof entry.timestamp === "number" ? entry.timestamp : 0;
4426
+ if (message.role === "user") {
4427
+ const text = contentText(message.content);
4428
+ if (text.trim()) events.push({
4429
+ kind: "user",
4430
+ at,
4431
+ text
4432
+ });
4433
+ } else if (message.role === "assistant") {
4434
+ const parts = splitAssistantContent(message.content);
4435
+ for (const thinking of parts?.thinking ?? []) events.push({
4436
+ kind: "thinking",
4437
+ at,
4438
+ text: thinking
4439
+ });
4440
+ if (parts?.text.trim()) events.push({
4441
+ kind: "assistant",
4442
+ at,
4443
+ text: parts.text,
4444
+ ...typeof message.model === "string" ? { model: message.model } : {}
4445
+ });
4446
+ for (const part of Array.isArray(message.content) ? message.content : []) {
4447
+ if (!isRecord$2(part) || part.type !== "toolCall" || typeof part.id !== "string") continue;
4448
+ const event = {
4449
+ kind: "tool",
4450
+ at,
4451
+ text: "",
4452
+ ...typeof part.name === "string" ? { name: part.name } : {},
4453
+ status: "running",
4454
+ ...isRecord$2(part.arguments) ? { args: part.arguments } : {}
4455
+ };
4456
+ calls.set(part.id, event);
4457
+ events.push(event);
4458
+ }
4459
+ } else if (message.role === "toolResult" && typeof message.toolCallId === "string") {
4460
+ const call = calls.get(message.toolCallId);
4461
+ if (!call) continue;
4462
+ call.result = contentText(message.content);
4463
+ call.status = message.isError === true ? "error" : "ok";
4235
4464
  }
4236
- if (run) runs.push(run);
4237
- }
4238
- return runs.sort((left, right) => left.suspendedAt - right.suspendedAt);
4239
- }
4240
- async function listSuspendedRunsAsync(scope) {
4241
- let entries;
4242
- try {
4243
- entries = await fs.promises.readdir(scopeSuspendedDir(scope));
4244
- } catch {
4245
- return [];
4246
- }
4247
- const runs = [];
4248
- for (const entry of entries) {
4249
- if (!entry.endsWith(JSON_SUFFIX)) continue;
4250
- try {
4251
- const run = parseSuspendedRun(await fs.promises.readFile(path.join(scopeSuspendedDir(scope), entry), "utf-8"));
4252
- if (run) runs.push(run);
4253
- } catch {}
4254
- }
4255
- return runs.sort((left, right) => left.suspendedAt - right.suspendedAt);
4256
- }
4257
- /** Drop a record once its run has been restored, or explicitly discarded. */
4258
- function clearSuspendedRun(scope, runId) {
4259
- try {
4260
- fs.rmSync(suspendedPath(scope, runId), { force: true });
4261
- } catch {}
4262
- }
4263
- /** Whether an explicit restore can safely continue this exact run. */
4264
- function isSuspendedRunResumable(run) {
4265
- if (run.runtime !== "pi" || !run.agent.trim() || !run.task.trim() || !run.cwd.trim() || !run.sessionFile) return false;
4266
- try {
4267
- fs.accessSync(run.sessionFile, fs.constants.R_OK);
4268
- return true;
4269
- } catch {
4270
- return false;
4271
4465
  }
4272
- }
4273
- /** The identity prefix for a suspended line, empty for a record written before identities existed. */
4274
- function suspendedLabel(run) {
4275
- const label = formatAgentIdentity(run.identity, run.inline);
4276
- return label ? `${label}, ` : "";
4277
- }
4278
- /** One line per suspended run, for the report `session_start` prints. */
4279
- function formatSuspendedRuns(runs) {
4280
- if (runs.length === 0) return "";
4281
- const lines = runs.map((run) => {
4282
- const continues = isSuspendedRunResumable(run) ? "resumable" : "not resumable";
4283
- return `- ${run.runId} (${suspendedLabel(run)}${run.agent}, ${run.runtime}, ${continues}): ${run.task.split("\n")[0]}`;
4284
- });
4285
- const noun = runs.length === 1 ? "subagent" : "subagents";
4286
- return [
4287
- `${runs.length} suspended ${noun} from this session:`,
4288
- ...lines,
4289
- "",
4290
- "They are stopped. Restore one with subagent {\"action\":\"restore\",\"id\":\"<run-id>\"}, or leave them."
4291
- ].join("\n");
4466
+ return events;
4292
4467
  }
4293
4468
  function age(job, now) {
4294
4469
  return job.startedAt ? formatDuration(Math.max(0, now - job.startedAt)) : "unknown";
@@ -4299,8 +4474,8 @@ function age(job, now) {
4299
4474
  * Takes `now` rather than calling `Date.now()` so a test asserting rendered
4300
4475
  * ages does not have to freeze global time.
4301
4476
  */
4302
- function formatFleetView(jobs, now = Date.now(), suspended = []) {
4303
- if (jobs.length === 0 && suspended.length === 0) return "No runs tracked in this session. A run appears here once it has been launched from this session.";
4477
+ function formatFleetView(jobs, now = Date.now()) {
4478
+ if (jobs.length === 0) return "No runs tracked in this session. A run appears here once it has been launched from this session.";
4304
4479
  const lines = [`${jobs.length} active run${jobs.length === 1 ? "" : "s"}:`];
4305
4480
  for (const job of jobs) {
4306
4481
  const parts = [`- ${job.runId}`];
@@ -4311,18 +4486,8 @@ function formatFleetView(jobs, now = Date.now(), suspended = []) {
4311
4486
  if (job.error) parts.push(`error: ${job.error}`);
4312
4487
  lines.push(parts.join(" · "));
4313
4488
  }
4314
- if (suspended.length > 0) {
4315
- lines.push("", `${suspended.length} suspended run${suspended.length === 1 ? "" : "s"}:`);
4316
- for (const run of suspended) {
4317
- const recovery = isSuspendedRunResumable(run) ? `restore with { action: "restore", id: "${run.runId}" }` : "not resumable; submit a new explicit run";
4318
- const label = formatAgentIdentity(run.identity, run.inline);
4319
- lines.push(`- ${run.runId} (${label ? `${label}, ` : ""}${run.agent}, ${run.runtime}): ${recovery}`);
4320
- }
4321
- }
4322
- if (jobs.length > 0) {
4323
- lines.push("");
4324
- lines.push(`Inspect one with { action: "status", id: "<run id>", transcriptLines: 80 }.`);
4325
- }
4489
+ lines.push("");
4490
+ lines.push(`Inspect one with { action: "status", id: "<run id>", transcriptLines: 80 }.`);
4326
4491
  return lines.join("\n");
4327
4492
  }
4328
4493
  /** Tool output shown to the model, bounded so one long result cannot crowd out the rest of the tail. */
@@ -4355,11 +4520,25 @@ function formatEvent(event) {
4355
4520
  */
4356
4521
  function formatRunTranscript(status, maxLines) {
4357
4522
  const transcriptPath = status?.transcriptPath;
4358
- if (!transcriptPath) return status ? "This run has no transcript. Artifacts were disabled for it (artifacts: false), so nothing was recorded." : "No status found for this run, so there is no transcript to read.";
4523
+ if (!transcriptPath) return status ? "This run has no transcript to read." : "No status found for this run, so there is no transcript to read.";
4359
4524
  const transcript = readFleetTranscript(transcriptPath);
4360
4525
  if (transcript.warning) return `Transcript at '${transcriptPath}' could not be read: ${transcript.warning}`;
4361
4526
  if (transcript.events.length === 0) return `Transcript at '${transcriptPath}' is empty. The run has not written anything yet.`;
4362
- const rendered = transcript.events.flatMap((event) => formatEvent(event).split("\n"));
4527
+ return formatTail(transcript.events, maxLines);
4528
+ }
4529
+ /**
4530
+ * A native run's transcript tail, read from its durable conversation rather than a file.
4531
+ *
4532
+ * ponytail: `entries` is one page of the newest entries, so the "of N lines" count covers that page, not the
4533
+ * whole run; read older pages first if a caller needs the full count.
4534
+ */
4535
+ function formatNativeRunTranscript(entries, maxLines) {
4536
+ const events = fleetEventsFromEntries(entries);
4537
+ if (events.length === 0) return "The run has no transcript entries yet.";
4538
+ return formatTail(events, maxLines);
4539
+ }
4540
+ function formatTail(events, maxLines) {
4541
+ const rendered = events.flatMap((event) => formatEvent(event).split("\n"));
4363
4542
  const kept = rendered.slice(-maxLines);
4364
4543
  const dropped = rendered.length - kept.length;
4365
4544
  return [
@@ -4407,7 +4586,7 @@ function startOperation(scope, operationId, args, runIds) {
4407
4586
  } catch (error) {
4408
4587
  if (!(error instanceof Error && "code" in error && error.code === "EEXIST")) throw error;
4409
4588
  const existing = readRecord(file);
4410
- if (existing.argumentHash !== argumentHash) throw new DoomTeamExpectedError("operation_conflict", `Operation '${operationId}' was already used with different arguments.`, false, "Submit the operation as a new tool call.");
4589
+ if (existing.argumentHash !== argumentHash) throw new PoTeamExpectedError("operation_conflict", `Operation '${operationId}' was already used with different arguments.`, false, "Submit the operation as a new tool call.");
4411
4590
  return {
4412
4591
  kind: "replay",
4413
4592
  record: existing
@@ -4444,7 +4623,6 @@ function progressMessage(params) {
4444
4623
  case SUBAGENT_ACTIONS.run: return `Starting ${params.requests.length} subagent${params.requests.length === 1 ? "" : "s"}...`;
4445
4624
  case SUBAGENT_ACTIONS.steer: return `Steering subagent ${params.id}...`;
4446
4625
  case SUBAGENT_ACTIONS.stop: return `Requesting stop for subagent ${params.id}...`;
4447
- case SUBAGENT_ACTIONS.restore: return `Restoring subagent ${params.id}...`;
4448
4626
  default: return;
4449
4627
  }
4450
4628
  }
@@ -4472,12 +4650,6 @@ function publicAgent(agent) {
4472
4650
  ...tools.length ? { tools } : {}
4473
4651
  };
4474
4652
  }
4475
- function publicSuspendedRun(run) {
4476
- return {
4477
- ...run,
4478
- resumable: isSuspendedRunResumable(run)
4479
- };
4480
- }
4481
4653
  /** One agent in full, as the model reads it. Shared with the headless tool. */
4482
4654
  function formatAgentDetail(agent) {
4483
4655
  const detail = publicAgent(agent);
@@ -4507,7 +4679,7 @@ function validateParams(input) {
4507
4679
  requireNonblank(record, "id", action);
4508
4680
  requireNonblank(record, "message", action);
4509
4681
  }
4510
- if (action === SUBAGENT_ACTIONS.stop || action === SUBAGENT_ACTIONS.restore) requireNonblank(record, "id", action);
4682
+ if (action === SUBAGENT_ACTIONS.stop) requireNonblank(record, "id", action);
4511
4683
  return raw;
4512
4684
  }
4513
4685
  var SubagentToolService = class {
@@ -4572,7 +4744,7 @@ var SubagentToolService = class {
4572
4744
  const operation = startOperation(scope, operationId, params, params.requests.map(() => randomUUID()));
4573
4745
  if (operation.kind === "replay") {
4574
4746
  if (operation.record.state === "completed" && operation.record.result) return operation.record.result;
4575
- throw new DoomTeamExpectedError("delivery_unconfirmed", `Operation '${operationId}' is already pending for runs: ${operation.record.runIds.join(", ")}.`, true, "Call subagent({\"action\":\"status\"}) and do not submit the run again.");
4747
+ throw new PoTeamExpectedError("delivery_unconfirmed", `Operation '${operationId}' is already pending for runs: ${operation.record.runIds.join(", ")}.`, true, "Call subagent({\"action\":\"status\"}) and do not submit the run again.");
4576
4748
  }
4577
4749
  const result = this.trackOutcomes(await this.planner.spawn(this.buildSpawnRequest(operationId, params, ctx, operation.record.runIds), loadConfig().config), ctx);
4578
4750
  const started = result.outcomes.filter((outcome) => outcome.runId);
@@ -4580,7 +4752,7 @@ var SubagentToolService = class {
4580
4752
  const outcomeLines = result.outcomes.map((outcome) => {
4581
4753
  return `- ${formatAgentIdentity(outcome.identity, outcome.inline) ?? outcome.agent}: ${outcome.runId ? outcome.runId : `failed: ${outcome.error ?? "unknown error"}`}${outcome.warning ? `\n Warning: ${outcome.warning}` : ""}`;
4582
4754
  });
4583
- if (started.length === 0 && failed.length > 0) throw new DoomTeamExpectedError("runtime_unavailable", `No requested subagent started.\n${outcomeLines.join("\n")}`, true, "Correct the reported launch failures. If an agent name is uncertain, call subagent({\"action\":\"agents\"}) before retrying only corrected requests.");
4755
+ if (started.length === 0 && failed.length > 0) throw new PoTeamExpectedError("runtime_unavailable", `No requested subagent started.\n${outcomeLines.join("\n")}`, true, "Correct the reported launch failures. If an agent name is uncertain, call subagent({\"action\":\"agents\"}) before retrying only corrected requests.");
4584
4756
  const subagentLabel = result.outcomes.length === 1 ? "subagent" : "subagents";
4585
4757
  const summary = failed.length === 0 ? `Started ${started.length} ${subagentLabel}:` : `Started ${started.length}/${result.outcomes.length} ${subagentLabel}; ${failed.length} failed:`;
4586
4758
  const next = failed.length === 0 ? "Completion will arrive asynchronously. Continue only non-overlapping work, or end your turn." : "Completion will arrive asynchronously for started runs. Do not resubmit them; retry only corrected failed entries.";
@@ -4597,44 +4769,35 @@ var SubagentToolService = class {
4597
4769
  completeOperation(scope, operationId, operation.record, response);
4598
4770
  return response;
4599
4771
  }
4600
- executeAgents(params, ctx) {
4772
+ async executeAgents(params, ctx) {
4601
4773
  const cwd = params.cwd ?? ctx.cwd;
4602
4774
  const scope = scopeOrDefault(params.scope);
4603
4775
  if (!params.name) {
4604
- const result = this.discovery.discover(cwd, scope);
4776
+ const result = await this.discovery.discover(cwd, scope);
4605
4777
  return textResult(formatAgentList(result.agents), { agents: result.agents.map(publicAgent) });
4606
4778
  }
4607
- const agent = this.discovery.find(cwd, scope, params.name);
4779
+ const agent = await this.discovery.find(cwd, scope, params.name);
4608
4780
  if (!agent) {
4609
- const candidates = this.discovery.discover(cwd, scope).agents.map((candidate) => candidate.name).toSorted();
4610
- throw new DoomTeamExpectedError("agent_not_found", `No executable agent matches '${params.name}'.${candidates.length ? ` Available: ${candidates.join(", ")}.` : ""}`, false, "Call subagent({\"action\":\"agents\"}) and retry with an exact name.");
4781
+ const candidates = (await this.discovery.discover(cwd, scope)).agents.map((candidate) => candidate.name).toSorted();
4782
+ throw new PoTeamExpectedError("agent_not_found", `No executable agent matches '${params.name}'.${candidates.length ? ` Available: ${candidates.join(", ")}.` : ""}`, false, "Call subagent({\"action\":\"agents\"}) and retry with an exact name.");
4611
4783
  }
4612
4784
  return textResult(formatAgentDetail(agent), { agent: publicAgent(agent) });
4613
4785
  }
4614
- async executeManagement(params, ctx, signal, preallocatedRunIds, operationId) {
4786
+ async executeManagement(params, ctx, signal) {
4615
4787
  const scope = this.sessionScope(ctx);
4616
4788
  this.management.bindSessionScope(scope);
4617
4789
  const jobs = this.tracker.forSession(ctx.sessionManager.getSessionId(), scope);
4618
4790
  switch (params.action) {
4619
4791
  case SUBAGENT_ACTIONS.status: {
4620
- const suspendedRuns = listSuspendedRuns(scope);
4621
4792
  if (!("id" in params)) {
4622
4793
  if ("transcriptLines" in params && params.transcriptLines !== void 0) throw invalidRequest("transcriptLines requires a run id.", "Add id or omit transcriptLines for fleet status.");
4623
4794
  const runs = jobs.list();
4624
- return textResult(formatFleetView(runs, Date.now(), suspendedRuns), {
4625
- fleet: runs,
4626
- suspended: suspendedRuns.map(publicSuspendedRun)
4627
- });
4795
+ return textResult(formatFleetView(runs, Date.now()), { fleet: runs });
4628
4796
  }
4629
4797
  const runId = params.id;
4630
4798
  if (!runId) throw invalidRequest("transcriptLines requires a run id.", "Add id or omit transcriptLines for fleet status.");
4631
- const suspended = suspendedRuns.find((run) => run.runId === runId);
4632
- if (suspended && params.transcriptLines === void 0) {
4633
- const recovery = isSuspendedRunResumable(suspended) ? `Restore it with { action: "restore", id: "${suspended.runId}" }.` : "It is not resumable; submit a new explicit run.";
4634
- return textResult(`Run '${suspended.runId}' is suspended. ${recovery}`, { suspended: publicSuspendedRun(suspended) });
4635
- }
4636
4799
  const result = this.management.status(runId);
4637
- if (!result.status && !result.runDir && !result.resultPath && !suspended) throw new DoomTeamExpectedError("run_not_found", `No run matches '${runId}'.`, false, "Call subagent({\"action\":\"status\"}) and retry with an exact id.");
4800
+ if (!result.status && !result.runDir && !result.resultPath) throw new PoTeamExpectedError("run_not_found", `No run matches '${runId}'.`, false, "Call subagent({\"action\":\"status\"}) and retry with an exact id.");
4638
4801
  if (params.transcriptLines !== void 0) return textResult(formatRunTranscript(result.status, params.transcriptLines ?? 80), { transcript: { runId: result.runId } });
4639
4802
  return textResult(result.status ? `Run '${result.runId}': ${result.status.state}` : `Run '${result.runId}' has no status yet.`, { status: result });
4640
4803
  }
@@ -4646,44 +4809,13 @@ var SubagentToolService = class {
4646
4809
  case SUBAGENT_ACTIONS.steer: {
4647
4810
  const id = resolveTrackedRunId(jobs, params.id);
4648
4811
  const runtime = jobs.get(id)?.runtime;
4649
- if (runtime && !isPiRuntime(runtime)) throw new DoomTeamExpectedError("unsupported_operation", `Run '${id}' uses '${runtime}', which does not support steering.`, false, `Use subagent({"action":"stop","id":"${id}"}) to terminate it.`);
4650
- const result = await this.management.steer(id, params.message, void 0, signal);
4812
+ if (runtime && !isPiRuntime(runtime)) throw new PoTeamExpectedError("unsupported_operation", `Run '${id}' uses '${runtime}', which does not support steering.`, false, `Use subagent({"action":"stop","id":"${id}"}) to terminate it.`);
4813
+ const result = await this.management.steer(id, {
4814
+ text: params.message,
4815
+ sender: MAIN_AGENT_SENDER
4816
+ }, void 0, signal);
4651
4817
  return textResult(`Steer request '${result.requestId}' for '${id}' is ${result.state}: ${result.message}`, { steer: result });
4652
4818
  }
4653
- case SUBAGENT_ACTIONS.suspended: {
4654
- const runs = listSuspendedRuns(scope);
4655
- return textResult(runs.length ? formatSuspendedRuns(runs) : "No suspended subagents in this session.", { suspended: runs.map(publicSuspendedRun) });
4656
- }
4657
- case SUBAGENT_ACTIONS.restore: {
4658
- const record = listSuspendedRuns(scope).find((run) => run.runId === params.id);
4659
- if (!record) throw new DoomTeamExpectedError("run_not_found", `No suspended run matches '${params.id}'.`, false, "Call subagent({\"action\":\"suspended\"}) and retry with an exact id.");
4660
- if (!isSuspendedRunResumable(record)) throw new DoomTeamExpectedError("not_resumable", `Suspended run '${params.id}' does not have complete, readable Pi recovery state.`, false, "Submit a new explicit run request instead of restoring this record.");
4661
- const outcome = this.trackOutcomes(await this.planner.spawn({
4662
- single: {
4663
- agent: record.agent,
4664
- ...record.inlineAgent ? { inlineAgent: record.inlineAgent } : {},
4665
- task: record.task,
4666
- cwd: record.cwd,
4667
- ...record.model ? { model: record.model } : {},
4668
- sessionFile: record.sessionFile,
4669
- ...record.identity ? { identity: record.identity } : {}
4670
- },
4671
- cwd: record.cwd,
4672
- agentScope: "both",
4673
- sessionScope: scope,
4674
- parentSessionId: ctx.sessionManager.getSessionId(),
4675
- availableModels: authenticatedModelInfos(ctx.modelRegistry),
4676
- ...operationId ? { operationId } : {},
4677
- ...preallocatedRunIds ? { preallocatedRunIds } : {}
4678
- }, loadConfig().config), ctx).outcomes[0];
4679
- if (!outcome?.runId) throw new DoomTeamExpectedError("not_resumable", `Could not restore '${params.id}': ${outcome?.error ?? "the spawn produced no run."}`, true, "Inspect suspended state, then submit a new explicit run if recovery is not possible.");
4680
- clearSuspendedRun(scope, params.id);
4681
- const restoredAs = formatAgentIdentity(outcome.identity, outcome.inline) ?? outcome.runId;
4682
- return textResult(`Restored '${params.id}' as '${restoredAs}' (${outcome.runId}), continuing its transcript.`, { restore: {
4683
- ...outcome,
4684
- restoredFrom: params.id
4685
- } });
4686
- }
4687
4819
  }
4688
4820
  }
4689
4821
  async execute(id, rawParams, signal, onUpdate, ctx) {
@@ -4692,22 +4824,22 @@ var SubagentToolService = class {
4692
4824
  if (progress) onUpdate?.(progressResult(progress, params.action));
4693
4825
  if (params.action === SUBAGENT_ACTIONS.run) return this.executeRun(id, params, ctx);
4694
4826
  if (params.action === SUBAGENT_ACTIONS.agents) return this.executeAgents(params, ctx);
4695
- if (!(params.action === SUBAGENT_ACTIONS.steer || params.action === SUBAGENT_ACTIONS.stop || params.action === SUBAGENT_ACTIONS.restore)) return this.executeManagement(params, ctx, signal);
4827
+ if (!(params.action === SUBAGENT_ACTIONS.steer || params.action === SUBAGENT_ACTIONS.stop)) return this.executeManagement(params, ctx, signal);
4696
4828
  const scope = this.sessionScope(ctx);
4697
4829
  this.management.bindSessionScope(scope);
4698
- const operation = startOperation(scope, id, params, params.action === SUBAGENT_ACTIONS.restore ? [randomUUID()] : []);
4830
+ const operation = startOperation(scope, id, params, []);
4699
4831
  if (operation.kind === "replay") {
4700
4832
  if (operation.record.state === "completed" && operation.record.result) return operation.record.result;
4701
- throw new DoomTeamExpectedError("delivery_unconfirmed", `Operation '${id}' is already pending.`, true, "Inspect run status before retrying this control operation.");
4833
+ throw new PoTeamExpectedError("delivery_unconfirmed", `Operation '${id}' is already pending.`, true, "Inspect run status before retrying this control operation.");
4702
4834
  }
4703
- const response = await this.executeManagement(params, ctx, signal, operation.record.runIds, id);
4835
+ const response = await this.executeManagement(params, ctx, signal);
4704
4836
  completeOperation(scope, id, operation.record, response);
4705
4837
  return response;
4706
4838
  }
4707
4839
  };
4708
4840
  //#endregion
4709
4841
  //#region src/services/headlessSubagentTool/index.ts
4710
- const SUBAGENT_DESCRIPTION = "Discover agents, start and manage persistent background subagent runs, and inspect suspended work.";
4842
+ const SUBAGENT_DESCRIPTION = "Discover agents, start and manage persistent background subagent runs.";
4711
4843
  /**
4712
4844
  * Every result carries the text the MODEL reads and the structured details the
4713
4845
  * cockpit card reads. The text is always given explicitly: a serialized detail
@@ -4750,18 +4882,17 @@ function createHeadlessSubagentTool(runtime, initialExecution) {
4750
4882
  label: "Subagent",
4751
4883
  description: SUBAGENT_DESCRIPTION,
4752
4884
  parameters: SubagentToolSchema,
4753
- promptSnippet: "Discover, run, inspect, steer, stop, and restore persistent subagents",
4754
- promptGuidelines: ["Runs are persistent background work. Do not resubmit a run that already returned an id.", "Use status or suspended before retrying a failed or interrupted operation."],
4885
+ promptSnippet: "Discover, run, inspect, steer, and stop persistent subagents",
4886
+ promptGuidelines: ["Runs are persistent background work. Do not resubmit a run that already returned an id.", "Use status before retrying a failed or interrupted operation."],
4755
4887
  executionMode: "serial",
4756
4888
  async execute(_toolCallId, parameters, signal, onUpdate, execution = initialExecution) {
4757
4889
  try {
4758
4890
  const params = validateParams(parameters);
4759
- const availableModels = execution.getAvailableModels?.().map(toModelInfo) ?? (execution.model ? [toModelInfo(execution.model)] : []);
4760
4891
  if (params.action === SUBAGENT_ACTIONS.agents) {
4761
4892
  const scope = scopeOf(params.scope);
4762
- const agents = runtime.discovery.discover(params.cwd ?? execution.cwd, scope).agents;
4763
- const selected = params.name ? runtime.discovery.find(params.cwd ?? execution.cwd, scope, params.name) : void 0;
4764
- if (params.name && !selected) throw new DoomTeamExpectedError("agent_not_found", `No executable agent matches '${params.name}'.`, false, "Call subagent({\"action\":\"agents\"}) and retry with an exact agent name.");
4893
+ const agents = (await runtime.discovery.discover(params.cwd ?? execution.cwd, scope)).agents;
4894
+ const selected = params.name ? await runtime.discovery.find(params.cwd ?? execution.cwd, scope, params.name) : void 0;
4895
+ if (params.name && !selected) throw new PoTeamExpectedError("agent_not_found", `No executable agent matches '${params.name}'.`, false, "Call subagent({\"action\":\"agents\"}) and retry with an exact agent name.");
4765
4896
  if (selected) return result(formatAgentDetail(selected), { agents: [publicAgent(selected)] });
4766
4897
  return result(formatAgentList(agents), { agents: agents.map((agent) => ({
4767
4898
  name: agent.name,
@@ -4790,9 +4921,10 @@ function createHeadlessSubagentTool(runtime, initialExecution) {
4790
4921
  sessionScope: createSessionScope(execution.sessionId),
4791
4922
  environment: execution.environment,
4792
4923
  parentSessionId: execution.sessionId,
4924
+ parentSelection: execution.selection,
4793
4925
  ...params.concurrency === void 0 ? {} : { concurrency: params.concurrency },
4794
4926
  ...params.artifacts === void 0 ? {} : { artifacts: params.artifacts },
4795
- availableModels,
4927
+ availableModels: await availableModelInfos(execution),
4796
4928
  ...execution.model ? { parentModel: execution.model } : {},
4797
4929
  ...parentForkSource ? { parentForkSource } : {}
4798
4930
  }, loadConfig().config);
@@ -4808,19 +4940,13 @@ function createHeadlessSubagentTool(runtime, initialExecution) {
4808
4940
  return result(formatFleetView(runs), { runs });
4809
4941
  }
4810
4942
  const status = runtime.management.statusAsync ? await runtime.management.statusAsync(params.id) : runtime.management.status(params.id);
4811
- return result([status.status ? [`Run '${status.runId}': ${status.status.state}`, status.status.summary ? `Summary: ${status.status.summary}` : void 0].filter((line) => line !== void 0).join("\n") : `Run '${status.runId}' has no status yet.`, params.transcriptLines === void 0 ? void 0 : formatRunTranscript(status.status, params.transcriptLines)].filter((text) => text !== void 0).join("\n\n"), {
4943
+ const statusText = status.status ? [`Run '${status.runId}': ${status.status.state}`, status.status.summary ? `Summary: ${status.status.summary}` : void 0].filter((line) => line !== void 0).join("\n") : `Run '${status.runId}' has no status yet.`;
4944
+ const nativeEntries = params.transcriptLines === void 0 || status.status?.transcriptPath !== void 0 ? void 0 : await runtime.nativeRuns.readTranscript(execution.sessionId, status.runId, params.transcriptLines);
4945
+ return result([statusText, params.transcriptLines === void 0 ? void 0 : nativeEntries !== void 0 ? formatNativeRunTranscript(nativeEntries, params.transcriptLines) : formatRunTranscript(status.status, params.transcriptLines)].filter((text) => text !== void 0).join("\n\n"), {
4812
4946
  runId: status.runId,
4813
4947
  status: status.status
4814
4948
  });
4815
4949
  }
4816
- if (params.action === SUBAGENT_ACTIONS.suspended) {
4817
- const suspended = listSuspendedRuns(createSessionScope(execution.sessionId));
4818
- const text = suspended.length ? formatSuspendedRuns(suspended) : "No suspended subagents in this session.";
4819
- return result(text, {
4820
- suspended,
4821
- text
4822
- });
4823
- }
4824
4950
  const id = resolveTrackedRunId(jobs, params.id);
4825
4951
  if (params.action === SUBAGENT_ACTIONS.stop) {
4826
4952
  const control = await runtime.management.stop(id, params.reason);
@@ -4829,40 +4955,14 @@ function createHeadlessSubagentTool(runtime, initialExecution) {
4829
4955
  control
4830
4956
  });
4831
4957
  }
4832
- if (params.action === SUBAGENT_ACTIONS.steer) {
4833
- const steer = await runtime.management.steer(id, params.message, void 0, signal);
4834
- return result(`Steer request '${steer.requestId}' for '${id}' is ${steer.state}: ${steer.message}`, {
4835
- runId: id,
4836
- steer
4837
- });
4838
- }
4839
- const suspended = listSuspendedRuns(createSessionScope(execution.sessionId)).find((item) => item.runId === params.id);
4840
- if (!suspended) throw new Error(`No suspended run matches '${params.id}'.`);
4841
- if (!isSuspendedRunResumable(suspended)) throw new Error(`Suspended run '${params.id}' is not resumable.`);
4842
- const outcome = (await runtime.spawnPlanner.spawn({
4843
- single: {
4844
- agent: suspended.agent,
4845
- ...suspended.inlineAgent ? { inlineAgent: suspended.inlineAgent } : {},
4846
- task: suspended.task,
4847
- cwd: suspended.cwd,
4848
- ...suspended.model ? { model: suspended.model } : {},
4849
- sessionFile: suspended.sessionFile,
4850
- ...suspended.identity ? { identity: suspended.identity } : {}
4851
- },
4852
- cwd: suspended.cwd,
4853
- agentScope: "both",
4854
- sessionScope: createSessionScope(execution.sessionId),
4855
- environment: execution.environment,
4856
- runtime: "pi",
4857
- parentSessionId: execution.sessionId,
4858
- availableModels
4859
- }, loadConfig().config)).outcomes[0];
4860
- if (!outcome?.runId) throw new Error(`Could not restore '${params.id}': ${outcome?.error ?? "the spawn produced no run."}`);
4861
- clearSuspendedRun(createSessionScope(execution.sessionId), params.id);
4862
- return result(`Restored '${params.id}' as '${outcome.runId}', continuing its transcript.`, { restore: {
4863
- ...outcome,
4864
- restoredFrom: params.id
4865
- } });
4958
+ const steer = await runtime.management.steer(id, {
4959
+ text: params.message,
4960
+ sender: MAIN_AGENT_SENDER
4961
+ }, void 0, signal);
4962
+ return result(`Steer request '${steer.requestId}' for '${id}' is ${steer.state}: ${steer.message}`, {
4963
+ runId: id,
4964
+ steer
4965
+ });
4866
4966
  } catch (error) {
4867
4967
  const message = error instanceof Error ? error.message : String(error);
4868
4968
  return result(message, message, true);
@@ -4872,7 +4972,7 @@ function createHeadlessSubagentTool(runtime, initialExecution) {
4872
4972
  }
4873
4973
  //#endregion
4874
4974
  //#region src/services/mcpTools/index.ts
4875
- const TEAM_MCP_TOOLS_SERVICE = "doom/team-mcp-tools";
4975
+ const TEAM_MCP_TOOLS_SERVICE = "po/team-mcp-tools";
4876
4976
  function mountMcpTools(tools) {
4877
4977
  return (context) => {
4878
4978
  context.plugin((providerContext) => {
@@ -4922,7 +5022,7 @@ function subscribeNativeRunProjection(sessionId, listener) {
4922
5022
  if (closed) return;
4923
5023
  closed = true;
4924
5024
  projection.listeners.delete(listener);
4925
- if (projection.listeners.size === 0 && projection.runs.size === 0) sessions.delete(sessionId);
5025
+ if (projection.listeners.size === 0 && projection.runs.size === 0 && sessions.get(sessionId) === projection) sessions.delete(sessionId);
4926
5026
  };
4927
5027
  }
4928
5028
  //#endregion
@@ -5138,7 +5238,7 @@ function formatGroupedCompletion(details) {
5138
5238
  *
5139
5239
  * Separate from `formatSingleCompletion` because the two have different
5140
5240
  * readers. That one is written for the model and embeds the run's full result
5141
- * preview; this one goes to `DoomHeadlessClient.notify`, which collapses every
5241
+ * preview; this one goes to `PoHeadlessClient.notify`, which collapses every
5142
5242
  * newline to a space and truncates at 4096 characters
5143
5243
  * (`packages/core/po-core/src/schemas/notification.ts`), and on the web
5144
5244
  * additionally drives a browser notification. Feeding the model-facing text to
@@ -5211,7 +5311,7 @@ function buildCompletionDetails(result) {
5211
5311
  * The custom-message type the parent renders completions under. The literal is
5212
5312
  * shared by the producer here and the TUI renderer registered for it
5213
5313
  * (`(frontend)/message/_lib/subagent-notify.cli.ts`, which draws through
5214
- * `completionNotice.ts`), and by the headless facet, which persists the same
5314
+ * `completionNotice.ts`), and by the server plugin, which persists the same
5215
5315
  * type as a session entry so a cockpit transcript records what was delivered.
5216
5316
  */
5217
5317
  const SUBAGENT_NOTIFY_MESSAGE_TYPE = "subagent-notify";
@@ -5337,41 +5437,149 @@ function active(job) {
5337
5437
  "stopped"
5338
5438
  ])).has(job.status);
5339
5439
  }
5340
- function suspendedRecord(job, reason, suspendedAt) {
5440
+ /**
5441
+ * Stops the live external runs of a session that ends. A native run is a conversation of the session's own
5442
+ * Harness and resumes with it, so it is left alone.
5443
+ */
5444
+ async function stopScopeRuns(options) {
5445
+ const jobs = options.jobs.filter((job) => !job.native && active(job));
5446
+ const outcomes = await Promise.allSettled(jobs.map((job) => options.stop(job.runId, options.reason)));
5341
5447
  return {
5342
- runId: job.runId,
5343
- agent: job.agent ?? "unknown",
5344
- ...job.identity === void 0 ? {} : { identity: job.identity },
5345
- ...job.inline === void 0 ? {} : { inline: job.inline },
5346
- runtime: job.runtime ?? (job.native ? "pi" : "external"),
5347
- task: job.task ?? "",
5348
- cwd: job.cwd ?? "",
5349
- ...job.status === void 0 ? {} : { lastStatus: job.status },
5350
- ...job.sessionFile === void 0 ? {} : { sessionFile: job.sessionFile },
5351
- suspendedAt,
5352
- reason
5448
+ stopped: jobs.map((job) => job.runId),
5449
+ unstoppable: jobs.filter((_, index) => outcomes[index]?.status === "rejected").map((job) => job.runId)
5353
5450
  };
5354
5451
  }
5355
- /** Persist resumable metadata, then stop every live run through its typed owner. */
5356
- async function suspendScopeRuns(options) {
5357
- const jobs = options.jobs.filter(active);
5358
- const suspendedAt = options.now?.() ?? Date.now();
5359
- for (const job of jobs) suspendRun(options.scope, suspendedRecord(job, options.reason, suspendedAt));
5360
- const outcomes = await Promise.allSettled(jobs.map((job) => options.stop(job.runId, options.reason)));
5452
+ //#endregion
5453
+ //#region src/schemas/team/packageConfig.ts
5454
+ const PO_TEAM_PACKAGE = "@agimon-ai/po-team";
5455
+ const TEAM_CONFIG_KEYS = ["models", "excludeTools"];
5456
+ const TEAM_MODEL_KEYS = ["model", "thinking"];
5457
+ const THINKING_VALUES = /* @__PURE__ */ new Set([
5458
+ "off",
5459
+ "minimal",
5460
+ "low",
5461
+ "medium",
5462
+ "high",
5463
+ "xhigh",
5464
+ "max"
5465
+ ]);
5466
+ function isRecord$1(value) {
5467
+ return typeof value === "object" && value !== null && !Array.isArray(value);
5468
+ }
5469
+ function assertKnownKeys(value, keys, location) {
5470
+ const unsupported = Object.keys(value).filter((key) => !keys.includes(key));
5471
+ if (unsupported.length > 0) throw new Error(`${location} has unsupported field(s): ${unsupported.join(", ")}`);
5472
+ }
5473
+ /** Validate and normalize one Team-owned package configuration mapping. */
5474
+ function parseTeamPackageConfig(value, location) {
5475
+ if (!isRecord$1(value)) throw new Error(`${location} must be a mapping`);
5476
+ assertKnownKeys(value, TEAM_CONFIG_KEYS, location);
5477
+ const hasModels = Object.hasOwn(value, "models");
5478
+ const hasExcludeTools = Object.hasOwn(value, "excludeTools");
5479
+ if (!hasModels && !hasExcludeTools) throw new Error(`${location} must define models or excludeTools`);
5480
+ let models;
5481
+ if (hasModels) {
5482
+ if (!Array.isArray(value.models) || value.models.length === 0) throw new Error(`${location}.models must be a non-empty array`);
5483
+ models = value.models.map((entry, index) => {
5484
+ const entryLocation = `${location}.models[${index}]`;
5485
+ if (!isRecord$1(entry)) throw new Error(`${entryLocation} must be a mapping`);
5486
+ assertKnownKeys(entry, TEAM_MODEL_KEYS, entryLocation);
5487
+ if (typeof entry.model !== "string" || !entry.model.trim()) throw new Error(`${entryLocation}.model must be a non-empty string`);
5488
+ if (entry.thinking !== void 0 && (typeof entry.thinking !== "string" || !THINKING_VALUES.has(entry.thinking))) throw new Error(`${entryLocation}.thinking must be one of: ${[...THINKING_VALUES].join(", ")}`);
5489
+ return {
5490
+ model: entry.model.trim(),
5491
+ ...entry.thinking ? { thinking: entry.thinking } : {}
5492
+ };
5493
+ });
5494
+ }
5495
+ let excludeTools;
5496
+ if (hasExcludeTools) {
5497
+ if (!Array.isArray(value.excludeTools) || value.excludeTools.length === 0) throw new Error(`${location}.excludeTools must be a non-empty array`);
5498
+ excludeTools = [...new Set(value.excludeTools.map((entry, index) => {
5499
+ if (typeof entry !== "string" || !entry.trim()) throw new Error(`${location}.excludeTools[${index}] must be a non-empty string`);
5500
+ return entry.trim();
5501
+ }))];
5502
+ }
5361
5503
  return {
5362
- suspended: jobs.map((job) => job.runId),
5363
- unstoppable: jobs.filter((_, index) => outcomes[index]?.status === "rejected").map((job) => job.runId)
5504
+ ...models ? { models } : {},
5505
+ ...excludeTools ? { excludeTools } : {}
5364
5506
  };
5365
5507
  }
5366
- /** Async form used by session startup. */
5367
- async function openScopeAsync(scope) {
5508
+ /** Merge Team configurations contributed by selected layers in selection order. */
5509
+ function mergeTeamPackageConfigurations(entries) {
5510
+ let models;
5511
+ const excludeTools = /* @__PURE__ */ new Set();
5512
+ let hasExcludeTools = false;
5513
+ for (const entry of entries) {
5514
+ const location = `Package "${entry.specifier}" config in layer "${entry.layer}" of .po/modes.yaml`;
5515
+ const next = parseTeamPackageConfig(entry.config, location);
5516
+ if (next.models) models = next.models.map((model) => ({ ...model }));
5517
+ if (next.excludeTools) {
5518
+ hasExcludeTools = true;
5519
+ for (const tool of next.excludeTools) excludeTools.add(tool);
5520
+ }
5521
+ }
5522
+ if (!models && !hasExcludeTools) return void 0;
5368
5523
  return {
5369
- pruned: [],
5370
- reaped: 0,
5371
- suspended: await listSuspendedRunsAsync(scope)
5524
+ ...models ? { models } : {},
5525
+ ...hasExcludeTools ? { excludeTools: [...excludeTools] } : {}
5372
5526
  };
5373
5527
  }
5374
5528
  //#endregion
5529
+ //#region src/services/teamAgentConfig/index.ts
5530
+ const PO_TEAM_AGENT_CONFIG_SERVICE = "po/team-agent-config";
5531
+ /** Normalize Team's ordered models without duplicating a known thinking suffix. */
5532
+ function teamModelSpecs(config) {
5533
+ return config?.models?.map(({ model, thinking }) => thinking ? `${model.replace(/:(?:off|minimal|low|medium|high|xhigh|max)$/, "")}:${thinking}` : model);
5534
+ }
5535
+ /** Session-local Team policy, loaded once for each injected config snapshot. */
5536
+ var TeamAgentConfigService = class {
5537
+ io;
5538
+ snapshot;
5539
+ loads = /* @__PURE__ */ new WeakMap();
5540
+ constructor(io) {
5541
+ this.io = io;
5542
+ }
5543
+ bind(snapshot) {
5544
+ this.snapshot = snapshot;
5545
+ return () => {
5546
+ if (this.snapshot === snapshot) this.snapshot = void 0;
5547
+ };
5548
+ }
5549
+ current() {
5550
+ if (!this.snapshot) return Promise.reject(new PoTeamExpectedError("runtime_unavailable", "Team agent configuration is not loaded yet.", true, "Retry after the session configuration is ready."));
5551
+ const snapshot = this.snapshot();
5552
+ const cached = this.loads.get(snapshot);
5553
+ if (cached) return cached;
5554
+ const load = this.load(snapshot).catch((error) => {
5555
+ this.loads.delete(snapshot);
5556
+ throw error;
5557
+ });
5558
+ this.loads.set(snapshot, load);
5559
+ return load;
5560
+ }
5561
+ currentOrUndefined() {
5562
+ return this.snapshot ? this.current() : Promise.resolve(void 0);
5563
+ }
5564
+ async load(snapshot) {
5565
+ const { fs, homeDirectory, environment } = this.io;
5566
+ if (!snapshot.harness.root) return { planningSubagents: snapshot.settings.modes?.planning?.subagents };
5567
+ const modes = await readMajorModesConfig(fs, snapshot.harness.root, BACKGROUND_CONTEXT, {
5568
+ homeDirectory,
5569
+ environment
5570
+ });
5571
+ const configurations = await readPackageConfigurations(fs, modes, [...snapshot.harness.layers], PO_TEAM_PACKAGE, BACKGROUND_CONTEXT);
5572
+ const config = mergeTeamPackageConfigurations(configurations);
5573
+ return {
5574
+ models: teamModelSpecs(config),
5575
+ thinking: config?.models?.[0]?.thinking,
5576
+ excludeTools: config?.excludeTools,
5577
+ planningSubagents: snapshot.settings.modes?.planning?.subagents,
5578
+ ...config ? { path: configurations.at(-1)?.filePath ?? path.join(snapshot.harness.root, ".po", "modes.yaml") } : {}
5579
+ };
5580
+ }
5581
+ };
5582
+ //#endregion
5375
5583
  //#region src/services/directRunBackgroundWork/index.ts
5376
5584
  const PROVIDER_NAME = "team-direct-runs";
5377
5585
  /** Publish direct Team runs owned by exactly one Pi session. */
@@ -5399,7 +5607,7 @@ function assertPolicy(policy) {
5399
5607
  /** Publishes Team's capability intersection store through a Cordis service. */
5400
5608
  function createSubagentPolicyService(store) {
5401
5609
  const service = {
5402
- generation: `doom-subagent-policy:${crypto.randomUUID()}`,
5610
+ generation: `po-subagent-policy:${crypto.randomUUID()}`,
5403
5611
  register(initialPolicy) {
5404
5612
  assertPolicy(initialPolicy);
5405
5613
  const owner = initialPolicy.owner;
@@ -5431,7 +5639,7 @@ function createSubagentPolicyService(store) {
5431
5639
  //#region src/services/teamCollaboration/index.ts
5432
5640
  /** Mount every Team-owned collaboration capability in one session fiber. */
5433
5641
  function teamCollaborationPlugin(ctx, config) {
5434
- const backgroundWork = readDoomBackgroundWorkService(ctx);
5642
+ const backgroundWork = readPoBackgroundWorkService(ctx);
5435
5643
  if (backgroundWork === void 0) throw new Error("Team requires the Po Session foundation.");
5436
5644
  const subagentPolicy = createSubagentPolicyService(config.policies);
5437
5645
  const delegation = config.delegation.createService(ctx, config.session);
@@ -5441,28 +5649,28 @@ function teamCollaborationPlugin(ctx, config) {
5441
5649
  cwd: config.session.cwd,
5442
5650
  environment: config.session.environment
5443
5651
  });
5444
- ctx.provide(DOOM_SUBAGENT_POLICY_SERVICE, subagentPolicy);
5445
- ctx.provide(DOOM_DELEGATION_SERVICE, delegation);
5446
- ctx.provide(DOOM_FABLE_PLAN_SERVICE, fablePlan);
5652
+ ctx.provide(PO_SUBAGENT_POLICY_SERVICE, subagentPolicy);
5653
+ ctx.provide(PO_DELEGATION_SERVICE, delegation);
5654
+ ctx.provide(PO_FABLE_PLAN_SERVICE, fablePlan);
5447
5655
  registerDirectRunBackgroundWork(ctx, backgroundWork, config.session.sessionId, config.directRunTracker);
5448
5656
  if (config.observeDelegation) {
5449
- ctx.on(DOOM_DELEGATION_REQUESTED_EVENT, (event) => config.observeDelegation?.({
5657
+ ctx.on(PO_DELEGATION_REQUESTED_EVENT, (event) => config.observeDelegation?.({
5450
5658
  kind: "requested",
5451
5659
  event
5452
5660
  }));
5453
- ctx.on(DOOM_DELEGATION_STARTED_EVENT, (event) => config.observeDelegation?.({
5661
+ ctx.on(PO_DELEGATION_STARTED_EVENT, (event) => config.observeDelegation?.({
5454
5662
  kind: "started",
5455
5663
  event
5456
5664
  }));
5457
- ctx.on(DOOM_DELEGATION_UPDATED_EVENT, (event) => config.observeDelegation?.({
5665
+ ctx.on(PO_DELEGATION_UPDATED_EVENT, (event) => config.observeDelegation?.({
5458
5666
  kind: "updated",
5459
5667
  event
5460
5668
  }));
5461
- ctx.on(DOOM_DELEGATION_FINISHED_EVENT, (event) => config.observeDelegation?.({
5669
+ ctx.on(PO_DELEGATION_FINISHED_EVENT, (event) => config.observeDelegation?.({
5462
5670
  kind: "finished",
5463
5671
  event
5464
5672
  }));
5465
- ctx.on(DOOM_DELEGATION_CANCELLED_EVENT, (event) => config.observeDelegation?.({
5673
+ ctx.on(PO_DELEGATION_CANCELLED_EVENT, (event) => config.observeDelegation?.({
5466
5674
  kind: "cancelled",
5467
5675
  event
5468
5676
  }));
@@ -5533,11 +5741,11 @@ var ManagementActions = class {
5533
5741
  if (exact) return exact;
5534
5742
  const matches = runs.filter((run) => run.runId.startsWith(id));
5535
5743
  if (matches.length === 1) return matches[0];
5536
- if (matches.length > 1) throw new DoomTeamExpectedError("run_not_found", `Multiple current-session runs match '${id}'.`, false, "Retry with a longer run id.");
5744
+ if (matches.length > 1) throw new PoTeamExpectedError("run_not_found", `Multiple current-session runs match '${id}'.`, false, "Retry with a longer run id.");
5537
5745
  }
5538
5746
  requireActiveJob(id) {
5539
5747
  const job = this.resolveJob(id);
5540
- if (!job) throw new DoomTeamExpectedError("run_not_found", `No active run matches '${id}'.`, false, "Call subagent({\"action\":\"status\"}) and retry with an exact run id.");
5748
+ if (!job) throw new PoTeamExpectedError("run_not_found", `No active run matches '${id}'.`, false, "Call subagent({\"action\":\"status\"}) and retry with an exact run id.");
5541
5749
  return job;
5542
5750
  }
5543
5751
  status(id) {
@@ -5551,7 +5759,20 @@ var ManagementActions = class {
5551
5759
  };
5552
5760
  }
5553
5761
  async statusAsync(id) {
5554
- return this.status(id);
5762
+ const live = this.status(id);
5763
+ if (live.status || !this.nativeRuns?.reported) return live;
5764
+ const run = await this.nativeRuns.reported(id);
5765
+ if (!run?.state) return live;
5766
+ return {
5767
+ ...live,
5768
+ status: statusProjection({
5769
+ runId: id,
5770
+ agent: run.agent,
5771
+ task: run.task,
5772
+ status: run.state,
5773
+ native: true
5774
+ })
5775
+ };
5555
5776
  }
5556
5777
  list() {
5557
5778
  return { runs: this.sessionJobs().list() };
@@ -5567,11 +5788,11 @@ var ManagementActions = class {
5567
5788
  const job = this.requireActiveJob(id);
5568
5789
  const requestId = randomUUID();
5569
5790
  if (job.native) {
5570
- if (!this.nativeRuns?.get(scope.rootSessionId, job.runId)) throw new DoomTeamExpectedError("run_not_found", `Native run '${job.runId}' is no longer active.`, false, "List active runs and retry with a current run id.");
5791
+ if (!this.nativeRuns?.get(scope.rootSessionId, job.runId)) throw new PoTeamExpectedError("run_not_found", `Native run '${job.runId}' is no longer active.`, false, "List active runs and retry with a current run id.");
5571
5792
  await this.nativeRuns.stop(scope.rootSessionId, job.runId, reason);
5572
5793
  return { requestId };
5573
5794
  }
5574
- if (!this.externalProcesses?.has(scope, job.runId)) throw new DoomTeamExpectedError("run_not_found", `External run '${job.runId}' is no longer active.`, false, "List active runs and retry with a current run id.");
5795
+ if (!this.externalProcesses?.has(scope, job.runId)) throw new PoTeamExpectedError("run_not_found", `External run '${job.runId}' is no longer active.`, false, "List active runs and retry with a current run id.");
5575
5796
  await this.externalProcesses.control(scope, job.runId, {
5576
5797
  command,
5577
5798
  requestId,
@@ -5585,7 +5806,7 @@ var ManagementActions = class {
5585
5806
  const requestId = randomUUID();
5586
5807
  const index = targetIndex ?? 0;
5587
5808
  if (job.native) {
5588
- if (!this.nativeRuns?.get(scope.rootSessionId, job.runId)) throw new DoomTeamExpectedError("run_not_found", `Native run '${job.runId}' is no longer active.`, false, "List active runs and retry with a current run id.");
5809
+ if (!this.nativeRuns?.get(scope.rootSessionId, job.runId)) throw new PoTeamExpectedError("run_not_found", `Native run '${job.runId}' is no longer active.`, false, "List active runs and retry with a current run id.");
5589
5810
  await this.nativeRuns.steer(scope.rootSessionId, job.runId, message, signal);
5590
5811
  return {
5591
5812
  requestId,
@@ -5594,11 +5815,11 @@ var ManagementActions = class {
5594
5815
  message: "Native child accepted the steer request."
5595
5816
  };
5596
5817
  }
5597
- if (!this.externalProcesses?.has(scope, job.runId)) throw new DoomTeamExpectedError("run_not_found", `External run '${job.runId}' is no longer active.`, false, "List active runs and retry with a current run id.");
5818
+ if (!this.externalProcesses?.has(scope, job.runId)) throw new PoTeamExpectedError("run_not_found", `External run '${job.runId}' is no longer active.`, false, "List active runs and retry with a current run id.");
5598
5819
  const acknowledgement = await this.externalProcesses.control(scope, job.runId, {
5599
5820
  command: "steer",
5600
5821
  requestId,
5601
- message,
5822
+ message: typeof message === "string" ? message : message.text,
5602
5823
  targetIndex: index
5603
5824
  });
5604
5825
  if (!acknowledgement) return {
@@ -5645,140 +5866,6 @@ var McpDirectToolResolverBinding = class {
5645
5866
  }
5646
5867
  };
5647
5868
  //#endregion
5648
- //#region src/services/nativeRunCoordinator/index.ts
5649
- function terminalState(event) {
5650
- return event.state === "completed" || event.state === "failed" || event.state === "stopped" ? event.state : void 0;
5651
- }
5652
- function entryKey(sessionId, runId) {
5653
- return JSON.stringify([sessionId, runId]);
5654
- }
5655
- var NativeRunCoordinator = class {
5656
- childSessions;
5657
- tracker;
5658
- notifier;
5659
- projection;
5660
- entries = /* @__PURE__ */ new Map();
5661
- closed = false;
5662
- closing;
5663
- constructor(childSessions, tracker, notifier, projection) {
5664
- this.childSessions = childSessions;
5665
- this.tracker = tracker;
5666
- this.notifier = notifier;
5667
- this.projection = projection;
5668
- }
5669
- async start(sessionId, request, identity) {
5670
- if (this.closed) throw new Error("The native Team run coordinator is closed.");
5671
- if (!sessionId.trim()) throw new Error("Native child runs require a session identity.");
5672
- const service = this.childSessions?.get();
5673
- if (!service) throw new Error("The in-process Pi child-session service is unavailable.");
5674
- const handle = await service.start(request);
5675
- if (this.closed) {
5676
- await handle.dispose();
5677
- throw new Error("The native Team run coordinator closed during child startup.");
5678
- }
5679
- const entry = {
5680
- sessionId,
5681
- scope: request.scope,
5682
- request,
5683
- ...identity ? { identity } : {},
5684
- handle,
5685
- terminal: false
5686
- };
5687
- const key = entryKey(sessionId, request.runId);
5688
- this.entries.set(key, entry);
5689
- try {
5690
- const unsubscribe = handle.subscribe((event) => this.onEvent(entry, event));
5691
- if (entry.terminal) unsubscribe();
5692
- else entry.unsubscribe = unsubscribe;
5693
- return handle;
5694
- } catch (error) {
5695
- this.entries.delete(key);
5696
- await handle.dispose().catch(() => void 0);
5697
- throw error;
5698
- }
5699
- }
5700
- get(sessionId, runId) {
5701
- return this.entries.get(entryKey(sessionId, runId))?.handle;
5702
- }
5703
- status(sessionId, runId) {
5704
- return this.tracker.getNative(sessionId, runId);
5705
- }
5706
- async steer(sessionId, runId, message, signal) {
5707
- const handle = this.get(sessionId, runId);
5708
- if (!handle) throw new Error(`No active native run matches '${runId}'.`);
5709
- await handle.steer(message, signal);
5710
- }
5711
- async stop(sessionId, runId, reason) {
5712
- const handle = this.get(sessionId, runId);
5713
- if (!handle) throw new Error(`No active native run matches '${runId}'.`);
5714
- await handle.stop(reason);
5715
- }
5716
- async close() {
5717
- this.closed = true;
5718
- this.closing ??= (async () => {
5719
- const active = [...this.entries.values()];
5720
- const outcomes = await Promise.allSettled(active.map((entry) => entry.handle.dispose()));
5721
- for (const entry of active) {
5722
- entry.unsubscribe?.();
5723
- this.entries.delete(entryKey(entry.sessionId, entry.request.runId));
5724
- }
5725
- const failures = outcomes.filter((outcome) => outcome.status === "rejected").map((outcome) => outcome.reason);
5726
- if (failures.length) throw new AggregateError(failures, "Native Team run shutdown failed.");
5727
- })();
5728
- return this.closing;
5729
- }
5730
- onEvent(entry, event) {
5731
- entry.startedAt ??= event.timestamp;
5732
- const status = terminalState(event);
5733
- const projection = {
5734
- runId: event.runId,
5735
- agent: entry.request.agent,
5736
- ...entry.identity ? {
5737
- identity: entry.identity.identity,
5738
- inline: entry.identity.inline
5739
- } : {},
5740
- task: entry.request.task,
5741
- cwd: entry.request.cwd,
5742
- runtime: "pi",
5743
- status: event.state === "starting" ? "queued" : event.state === "running" ? "running" : event.state,
5744
- startedAt: entry.startedAt,
5745
- updatedAt: event.timestamp,
5746
- ...event.cost === void 0 ? {} : { cost: event.cost },
5747
- ...event.sessionFile ? { sessionFile: event.sessionFile } : {},
5748
- ...event.message ? {
5749
- error: status === "failed" || status === "stopped" ? event.message : void 0,
5750
- summary: event.message
5751
- } : {}
5752
- };
5753
- this.tracker.upsertNative(entry.sessionId, entry.scope, projection);
5754
- this.projection?.publish(entry.sessionId, projection);
5755
- if (!status || entry.terminal) return;
5756
- entry.terminal = true;
5757
- entry.unsubscribe?.();
5758
- this.entries.delete(entryKey(entry.sessionId, entry.request.runId));
5759
- const result = {
5760
- runId: entry.request.runId,
5761
- agent: entry.request.agent,
5762
- ...entry.identity ? {
5763
- identity: entry.identity.identity,
5764
- inline: entry.identity.inline
5765
- } : {},
5766
- task: entry.request.task,
5767
- cwd: entry.request.cwd,
5768
- runtime: "pi",
5769
- state: status,
5770
- success: status === "completed",
5771
- summary: event.message ?? (status === "completed" ? "Native child run completed." : `Native child run ${status}.`),
5772
- durationMs: Math.max(0, event.timestamp - (this.tracker.getNative(entry.sessionId, entry.request.runId)?.startedAt ?? event.timestamp)),
5773
- sessionId: entry.sessionId,
5774
- ...event.sessionFile ? { sessionFile: event.sessionFile } : {}
5775
- };
5776
- if (this.notifier) this.notifier.deliver(result).then((delivered) => {
5777
- if (delivered) this.tracker.acknowledgeHandoff(entry.sessionId, entry.request.runId);
5778
- }).catch(() => void 0);
5779
- }
5780
- };
5781
- //#endregion
5782
5869
  //#region src/services/pollScheduler/index.ts
5783
5870
  /**
5784
5871
  * One master tick that drives every background subscriber in the runner
@@ -6094,14 +6181,14 @@ function createTeamExtensionRuntime(reportConcurrencyEvent, options) {
6094
6181
  const subagentWaiter = new SubagentWaiter(asyncJobTracker);
6095
6182
  const externalProcesses = new ExternalProcessIpc();
6096
6183
  const asyncSubagentSpawner = new AsyncSubagentSpawner(externalProcesses);
6097
- const discovery = new AgentDiscoveryService(options.environment);
6184
+ const discovery = new AgentDiscoveryService(options.discoveryIo, options.agentConfig);
6098
6185
  const skills = new SkillDiscoveryService();
6099
6186
  const capabilityPolicies = new SubagentCapabilityPolicyStore();
6100
6187
  const mcpToolResolver = new McpDirectToolResolverBinding();
6101
6188
  const completionNotifier = new CompletionNotifier();
6102
6189
  const nativeRuns = new NativeRunCoordinator(options.childSessions, asyncJobTracker, completionNotifier, options.nativeRunProjection);
6103
6190
  const teamChannel = new NativeTeamChannelService();
6104
- const spawnPlanner = new SpawnPlanner(discovery, asyncSubagentSpawner, capabilityPolicies, skills, report, mcpToolResolver, admission, options.childSessions, nativeRuns, teamChannel, options.environment);
6191
+ const spawnPlanner = new SpawnPlanner(discovery, asyncSubagentSpawner, capabilityPolicies, skills, report, mcpToolResolver, admission, options.childSessions, nativeRuns, teamChannel, options.environment, options.agentConfig);
6105
6192
  const management = new ManagementActions(asyncJobTracker, nativeRuns, externalProcesses);
6106
6193
  const onExternalEvent = (event) => {
6107
6194
  const sessionId = event.scope.rootSessionId;
@@ -6158,6 +6245,7 @@ function createTeamExtensionRuntime(reportConcurrencyEvent, options) {
6158
6245
  subagentWaiter,
6159
6246
  asyncSubagentSpawner,
6160
6247
  discovery,
6248
+ agentConfig: options.agentConfig,
6161
6249
  skills,
6162
6250
  spawnPlanner,
6163
6251
  completionNotifier,
@@ -6239,12 +6327,16 @@ function presentTrackedRun(run) {
6239
6327
  state: runState(run.status),
6240
6328
  rawState: run.status,
6241
6329
  task: run.task ?? "",
6330
+ ...run.promptWithheld ? { promptWithheld: true } : {},
6242
6331
  cwd: run.cwd ?? "",
6243
6332
  startedAt: run.startedAt,
6244
6333
  ...TERMINAL_RUN_STATES.has(run.status) ? { endedAt: lastUpdate } : {},
6245
6334
  lastUpdate,
6246
6335
  tail: [],
6247
- ...run.sessionFile === void 0 ? {} : { sessionFile: run.sessionFile },
6336
+ canSteer: false,
6337
+ ...run.tokens === void 0 ? {} : { tokens: run.tokens },
6338
+ ...run.toolCount === void 0 ? {} : { toolCount: run.toolCount },
6339
+ ...run.cost === void 0 ? {} : { cost: run.cost },
6248
6340
  ...run.summary === void 0 ? {} : { summary: run.summary },
6249
6341
  ...run.error === void 0 ? {} : { error: run.error }
6250
6342
  };
@@ -6255,6 +6347,8 @@ function presentNativeRun(run) {
6255
6347
  agent: run.agent,
6256
6348
  ...run.identity === void 0 ? {} : { identity: run.identity },
6257
6349
  ...run.inline === void 0 ? {} : { inline: run.inline },
6350
+ ...run.agentDescription === void 0 ? {} : { agentDescription: run.agentDescription },
6351
+ ...run.agentFilePath === void 0 ? {} : { agentFilePath: run.agentFilePath },
6258
6352
  state: runState(run.status),
6259
6353
  rawState: run.status,
6260
6354
  task: run.task,
@@ -6263,7 +6357,14 @@ function presentNativeRun(run) {
6263
6357
  ...TERMINAL_RUN_STATES.has(run.status) ? { endedAt: run.updatedAt } : {},
6264
6358
  lastUpdate: run.updatedAt,
6265
6359
  tail: [],
6266
- ...run.sessionFile === void 0 ? {} : { sessionFile: run.sessionFile },
6360
+ ...run.model === void 0 ? {} : {
6361
+ model: run.model,
6362
+ modelSource: "runtime"
6363
+ },
6364
+ ...run.thinking === void 0 ? {} : { thinking: run.thinking },
6365
+ ...run.tokens === void 0 ? {} : { tokens: run.tokens },
6366
+ ...run.toolCount === void 0 ? {} : { toolCount: run.toolCount },
6367
+ ...run.cost === void 0 ? {} : { cost: run.cost },
6267
6368
  ...run.summary === void 0 ? {} : { summary: run.summary },
6268
6369
  ...run.error === void 0 ? {} : { error: run.error }
6269
6370
  };
@@ -6273,7 +6374,7 @@ function publishRuns(directEvents, sessionId, jobs, nativeRuns) {
6273
6374
  runs.push(...[...nativeRuns.values()].map(presentNativeRun));
6274
6375
  directEvents.publish(SUBAGENT_RUNS_TYPE, sessionId, { runs });
6275
6376
  }
6276
- const TEAM_COST_STATUS = "doom-team-cost";
6377
+ const TEAM_COST_STATUS = "po-team-cost";
6277
6378
  const COST_DECIMALS = 6;
6278
6379
  function updateTeamCost(client, jobs, nativeRuns, costs, previousText, attached, force = false) {
6279
6380
  const trackedCosts = /* @__PURE__ */ new Map();
@@ -6293,20 +6394,20 @@ function updateTeamCost(client, jobs, nativeRuns, costs, previousText, attached,
6293
6394
  function recordDelegationObservation(telemetry, observation) {
6294
6395
  if (observation.kind === "requested") {
6295
6396
  const { event } = observation;
6296
- telemetry.recordEvent("doom_team.delegation_requested", {
6397
+ telemetry.recordEvent("po_team.delegation_requested", {
6297
6398
  "agent.name": event.agent,
6298
6399
  "task.id": event.taskId,
6299
6400
  mode: event.runMode ?? "foreground"
6300
6401
  });
6301
6402
  } else if (observation.kind === "started") {
6302
6403
  const { event } = observation;
6303
- telemetry.recordEvent("doom_team.child_started", {
6404
+ telemetry.recordEvent("po_team.child_started", {
6304
6405
  "run.id": event.runId,
6305
6406
  outcome: "started"
6306
6407
  });
6307
6408
  } else if (observation.kind === "updated") {
6308
6409
  const { event } = observation;
6309
- telemetry.recordEvent("doom_team.delegation_updated", {
6410
+ telemetry.recordEvent("po_team.delegation_updated", {
6310
6411
  "run.id": event.runId,
6311
6412
  "team.tool_count": event.toolCount ?? 0,
6312
6413
  "team.duration_ms": event.durationMs ?? 0,
@@ -6315,40 +6416,58 @@ function recordDelegationObservation(telemetry, observation) {
6315
6416
  });
6316
6417
  } else if (observation.kind === "finished") {
6317
6418
  const { event } = observation;
6318
- telemetry.recordEvent("doom_team.delegation_finished", {
6419
+ telemetry.recordEvent("po_team.delegation_finished", {
6319
6420
  "run.id": event.runId,
6320
6421
  "team.tool_count": event.toolCount ?? 0,
6321
6422
  "team.duration_ms": event.durationMs ?? 0,
6322
6423
  outcome: event.status
6323
6424
  });
6324
- } else telemetry.recordEvent("doom_team.delegation_cancelled", { outcome: "cancelled" });
6425
+ } else telemetry.recordEvent("po_team.delegation_cancelled", { outcome: "cancelled" });
6325
6426
  }
6326
6427
  const root = defineRoot(({ context, host: serverHost, agent: host }) => {
6327
6428
  if (!host) throw new Error("Team session requires the headless host.");
6328
- if (serverHost.context.environment === void 0) throw new Error("Team headless facet requires an admitted session environment.");
6429
+ if (serverHost.context.environment === void 0) throw new Error("Team server plugin requires an admitted session environment.");
6329
6430
  const directEvents = serverHost.context.directEvents;
6330
- if (directEvents === void 0) throw new Error("Team headless facet requires host-owned direct events.");
6431
+ if (directEvents === void 0) throw new Error("Team server plugin requires host-owned direct events.");
6331
6432
  const execution = {
6332
6433
  ...host.context,
6333
6434
  get model() {
6334
6435
  return host.context.model;
6436
+ },
6437
+ get selection() {
6438
+ return host.context.selection;
6335
6439
  }
6336
6440
  };
6337
6441
  const sessionPeers = /* @__PURE__ */ new Set();
6338
6442
  const stopPeerDiscovery = serverHost.context.sessionCommunication?.onPeerReady((id) => sessionPeers.add(id));
6339
- const telemetry = createDoomTelemetry({
6340
- serviceName: "doom-team",
6443
+ const telemetry = createPoTelemetry({
6444
+ serviceName: "po-team",
6341
6445
  packageName: "@agimon-ai/po-team",
6342
6446
  cwd: execution.cwd,
6343
6447
  env: serverHost.context.environment,
6344
6448
  enableLogs: true,
6345
6449
  enableTraces: true
6346
6450
  });
6451
+ const environment = serverHost.context.environment;
6452
+ const fs = requirePoFs(context);
6453
+ const homeDirectory = environment.HOME ?? nodeHost.homeDirectory();
6347
6454
  const runtime = createTeamExtensionRuntime((event, attributes) => {
6348
6455
  telemetry.recordEvent(event, attributes);
6349
6456
  }, {
6350
- environment: serverHost.context.environment,
6351
- childSessions: { get: () => readDoomChildSessionService(context) },
6457
+ environment,
6458
+ discoveryIo: {
6459
+ fs,
6460
+ context: BACKGROUND_CONTEXT,
6461
+ environment,
6462
+ homeDirectory,
6463
+ configDirName: getConfigDirName(environment)
6464
+ },
6465
+ agentConfig: new TeamAgentConfigService({
6466
+ fs,
6467
+ environment,
6468
+ homeDirectory
6469
+ }),
6470
+ childSessions: { get: () => readPoChildSessionService(context) },
6352
6471
  nativeRunProjection
6353
6472
  });
6354
6473
  const scope = createSessionScope(execution.sessionId);
@@ -6371,9 +6490,6 @@ const root = defineRoot(({ context, host: serverHost, agent: host }) => {
6371
6490
  telemetry.recordEvent(event, attributes);
6372
6491
  }
6373
6492
  });
6374
- let ownedReadiness;
6375
- let ready;
6376
- const startupAbort = new AbortController();
6377
6493
  const nativeRuns = /* @__PURE__ */ new Map();
6378
6494
  const childCosts = /* @__PURE__ */ new Map();
6379
6495
  let costText;
@@ -6411,7 +6527,8 @@ const root = defineRoot(({ context, host: serverHost, agent: host }) => {
6411
6527
  });
6412
6528
  if (options?.triggerTurn === false) return;
6413
6529
  if (!execution.session.admitPrompt) throw new Error("The session cannot admit a completion prompt.");
6414
- await execution.session.admitPrompt(message.content, "steer");
6530
+ const runIds = details.map((detail) => detail.runId).toSorted();
6531
+ await execution.session.admitPrompt(message.content, "steer", void 0, runIds.length ? `subagent-report:${runIds.join(",")}` : void 0);
6415
6532
  });
6416
6533
  deliveries = delivery.catch(() => void 0);
6417
6534
  return delivery;
@@ -6424,16 +6541,26 @@ const root = defineRoot(({ context, host: serverHost, agent: host }) => {
6424
6541
  });
6425
6542
  if (options?.triggerTurn === false) return;
6426
6543
  const delivery = deliveries.then(async () => {
6427
- if (!execution.session.admitPrompt) throw new Error("The session cannot admit an intercom message.");
6428
- await execution.session.admitPrompt(text, "steer");
6544
+ if (!execution.session.admitMessage) throw new Error("The session cannot admit an intercom message.");
6545
+ await execution.session.admitMessage({
6546
+ type: message.customType,
6547
+ text,
6548
+ display: message.display,
6549
+ details: message.details
6550
+ }, "steer");
6429
6551
  });
6430
6552
  deliveries = delivery.catch(() => void 0);
6431
6553
  return delivery;
6432
6554
  } });
6433
6555
  const intercom = createHeadlessIntercomTool(channel, {
6434
6556
  sessionId: execution.sessionId,
6435
- peers: () => [...sessionPeers].filter((id) => serverHost.context.sessionService?.canCommunicate?.(execution.sessionId, id) === true),
6436
- delivery: () => readDoomSessionDelivery(context)
6557
+ name: () => serverHost.context.sessionCommunication?.sessionName,
6558
+ peers: () => [...sessionPeers].filter((id) => serverHost.context.sessionService?.canCommunicate?.(execution.sessionId, id) === true).flatMap((id) => {
6559
+ const peer = serverHost.context.sessionCommunication?.describePeer?.(id);
6560
+ return peer === void 0 ? [] : [peer];
6561
+ }),
6562
+ canCommunicate: (id) => serverHost.context.sessionService?.canCommunicate?.(execution.sessionId, id) === true,
6563
+ delivery: () => readPoSessionDelivery(context)
6437
6564
  });
6438
6565
  const serverService = (ctx) => {
6439
6566
  ctx.plugin((providerContext) => {
@@ -6443,12 +6570,18 @@ const root = defineRoot(({ context, host: serverHost, agent: host }) => {
6443
6570
  sessionScope: scope,
6444
6571
  cwd: execution.cwd,
6445
6572
  environment: serverHost.context.environment,
6446
- get availableModels() {
6447
- return execution.getAvailableModels?.().map(toModelInfo) ?? (execution.model ? [toModelInfo(execution.model)] : []);
6573
+ availableModels: () => availableModelInfos(execution),
6574
+ repoRoot: execution.repoRoot,
6575
+ get parentSelection() {
6576
+ return execution.selection;
6448
6577
  },
6449
6578
  get parentModel() {
6450
6579
  return execution.model;
6451
6580
  },
6581
+ reportWarning: (body) => execution.client.notify({
6582
+ body,
6583
+ level: "warning"
6584
+ }),
6452
6585
  captureForkSource: () => execution.session.forkSource?.()
6453
6586
  },
6454
6587
  directRunTracker: runtime.asyncJobTracker,
@@ -6457,9 +6590,16 @@ const root = defineRoot(({ context, host: serverHost, agent: host }) => {
6457
6590
  policies: runtime.capabilityPolicies,
6458
6591
  observeDelegation: (observation) => recordDelegationObservation(telemetry, observation)
6459
6592
  });
6460
- providerContext.inject([DOOM_MCP_TOOL_RESOLVER_SERVICE], (resolverContext) => runtime.mcpToolResolver.bind(requireDoomMcpToolResolver(resolverContext)));
6461
- providerContext.inject([DOOM_CONTEXT_CONTRIBUTIONS_SERVICE], (contributionContext) => {
6462
- const registration = requireDoomContextContributions(contributionContext).register({
6593
+ providerContext.inject([PO_CONFIG_SERVICE], (configContext) => {
6594
+ const config = configContext.get(PO_CONFIG_SERVICE);
6595
+ if (!config) throw new Error("Team requires the session Po config service.");
6596
+ const unbind = runtime.agentConfig.bind(() => config.getSnapshot());
6597
+ publishCatalog();
6598
+ return unbind;
6599
+ });
6600
+ providerContext.inject([PO_MCP_TOOL_RESOLVER_SERVICE], (resolverContext) => runtime.mcpToolResolver.bind(requirePoMcpToolResolver(resolverContext)));
6601
+ providerContext.inject([PO_CONTEXT_CONTRIBUTIONS_SERVICE], (contributionContext) => {
6602
+ const registration = requirePoContextContributions(contributionContext).register({
6463
6603
  source: "@agimon-ai/po-team",
6464
6604
  id: "runtime",
6465
6605
  label: "Team",
@@ -6468,30 +6608,96 @@ const root = defineRoot(({ context, host: serverHost, agent: host }) => {
6468
6608
  });
6469
6609
  return () => registration.dispose();
6470
6610
  });
6471
- ready = (readDoomReadinessCoordinator(providerContext) ?? (ownedReadiness = createDoomReadinessCoordinator({ notify: (notification) => execution.client.notify({
6472
- level: "warning",
6473
- body: notification.error?.message ?? notification.diagnostics.join("; ")
6474
- }) }))).start("@agimon-ai/po-team", `team:${execution.sessionId}:${crypto.randomUUID()}`, async (signal) => {
6475
- signal.throwIfAborted();
6476
- startupAbort.signal.throwIfAborted();
6477
- const opened = await openScopeAsync(scope);
6478
- signal.throwIfAborted();
6479
- startupAbort.signal.throwIfAborted();
6480
- if (opened.suspended.length) await execution.client.notify({
6481
- body: formatSuspendedRuns(opened.suspended),
6482
- level: "info"
6483
- });
6484
- telemetry.recordEvent("doom_team.runs_restored", {
6485
- "team.suspended_count": opened.suspended.length,
6486
- "team.pruned_count": opened.pruned.length,
6487
- "team.reaped_count": opened.reaped,
6488
- outcome: "reported"
6489
- });
6490
- return { value: void 0 };
6611
+ });
6612
+ };
6613
+ const publishCatalog = async () => {
6614
+ if (!activityAttached) return;
6615
+ try {
6616
+ const discovered = (await runtime.discovery.discover(execution.cwd, "both")).agents;
6617
+ const models = (await runtime.agentConfig.currentOrUndefined())?.models ?? [];
6618
+ if (!activityAttached) return;
6619
+ directEvents.publish(SUBAGENT_CATALOG_TYPE, execution.sessionId, {
6620
+ cwd: execution.cwd,
6621
+ agents: presentCatalog(discovered),
6622
+ models: catalogModels(discovered, [...models])
6491
6623
  });
6624
+ } catch (error) {
6625
+ if (!activityAttached) return;
6626
+ directEvents.publish(SUBAGENT_CATALOG_TYPE, execution.sessionId, {
6627
+ cwd: execution.cwd,
6628
+ agents: [],
6629
+ models: [],
6630
+ warning: error instanceof Error ? error.message : String(error)
6631
+ });
6632
+ }
6633
+ };
6634
+ const activity = {
6635
+ name: "@agimon-ai/po-team",
6636
+ async start(activityContext) {
6637
+ activityAttached = true;
6638
+ publishCost(true);
6639
+ publishRunSnapshot();
6640
+ await publishCatalog();
6641
+ try {
6642
+ channel.bindMainSession(activityContext.sessionId);
6643
+ return () => {
6644
+ if (!activityAttached) return;
6645
+ activityAttached = false;
6646
+ channel.dispose();
6647
+ execution.client.setStatus(TEAM_COST_STATUS, void 0);
6648
+ };
6649
+ } catch (error) {
6650
+ activityAttached = false;
6651
+ execution.client.setStatus(TEAM_COST_STATUS, void 0);
6652
+ throw error;
6653
+ }
6654
+ }
6655
+ };
6656
+ /**
6657
+ * Re-attaches the runs this session stored before a restart. Called from `session_start`, once the session can
6658
+ * take a completion and before its Harness resumes: a running child gets its lane and intercom back first, and
6659
+ * a finished one reports its completion now.
6660
+ */
6661
+ const restoreRuns = async () => {
6662
+ const report = (subject, error) => execution.client.notify({
6663
+ body: `${subject}: ${error instanceof Error ? error.message : String(error)}`,
6664
+ level: "warning"
6665
+ });
6666
+ let stored = [];
6667
+ try {
6668
+ stored = await readPoChildSessionService(context)?.stored?.() ?? [];
6669
+ } catch (error) {
6670
+ await report("Stored subagent runs could not be read", error);
6671
+ }
6672
+ let restored = 0;
6673
+ for (const run of stored) {
6674
+ const identity = storedRunIdentity(run);
6675
+ const intercom = run.settled ? void 0 : runtime.teamChannel.createNativeChildIntercom({
6676
+ rootSessionId: run.request.scope.rootSessionId,
6677
+ agent: run.request.agent,
6678
+ ...identity ? {
6679
+ identity: identity.identity,
6680
+ inline: identity.inline
6681
+ } : {},
6682
+ runId: run.request.runId,
6683
+ task: {
6684
+ id: run.request.runId,
6685
+ subject: run.request.task
6686
+ }
6687
+ });
6688
+ try {
6689
+ await runtime.nativeRuns.restore(run.request.parentSessionId, run, intercom);
6690
+ restored += 1;
6691
+ } catch (error) {
6692
+ intercom?.dispose?.();
6693
+ await report(`Subagent run ${run.request.runId} could not resume`, error);
6694
+ }
6695
+ }
6696
+ telemetry.recordEvent("po_team.runs_restored", {
6697
+ "team.restored_count": restored,
6698
+ outcome: "restored"
6492
6699
  });
6493
6700
  };
6494
- const environment = serverHost.context.environment;
6495
6701
  return {
6496
6702
  value: {
6497
6703
  runtime,
@@ -6502,96 +6708,63 @@ const root = defineRoot(({ context, host: serverHost, agent: host }) => {
6502
6708
  channel,
6503
6709
  intercom,
6504
6710
  directEvents,
6505
- environment: serverHost.context.environment
6711
+ environment: serverHost.context.environment,
6712
+ restoreRuns
6506
6713
  },
6507
6714
  services: [
6508
6715
  (context) => {
6509
- if (readDoomBackgroundWorkService(context) === void 0) provideBackgroundWorkService(context);
6716
+ if (readPoBackgroundWorkService(context) === void 0) provideBackgroundWorkService(context);
6510
6717
  },
6511
6718
  serverService,
6719
+ (context) => context.provide(PO_TEAM_AGENT_CONFIG_SERVICE, runtime.agentConfig),
6720
+ (context) => context.provide(PO_CHILD_SESSION_LABELS_SERVICE, { labelFor: (runId) => nativeRuns.get(runId)?.identity }),
6512
6721
  mountMcpTools([intercom, createHeadlessSubagentTool(runtime, execution)])
6513
6722
  ],
6514
- activities: [{
6515
- name: "@agimon-ai/po-team",
6516
- async start(activityContext) {
6517
- activityAttached = true;
6518
- publishCost(true);
6519
- publishRunSnapshot();
6520
- try {
6521
- const discovered = runtime.discovery.discover(execution.cwd, "both").agents;
6522
- directEvents.publish(SUBAGENT_CATALOG_TYPE, execution.sessionId, {
6523
- cwd: execution.cwd,
6524
- agents: presentCatalog(discovered),
6525
- models: catalogModels(discovered, resolveActiveTeamModelSpecs(environment) ?? [])
6526
- });
6527
- } catch (error) {
6528
- directEvents.publish(SUBAGENT_CATALOG_TYPE, execution.sessionId, {
6529
- cwd: execution.cwd,
6530
- agents: [],
6531
- models: [],
6532
- warning: error instanceof Error ? error.message : String(error)
6533
- });
6534
- }
6535
- try {
6536
- channel.bindMainSession(activityContext.sessionId);
6537
- return () => {
6538
- if (!activityAttached) return;
6539
- activityAttached = false;
6540
- channel.dispose();
6541
- execution.client.setStatus(TEAM_COST_STATUS, void 0);
6542
- };
6543
- } catch (error) {
6544
- activityAttached = false;
6545
- execution.client.setStatus(TEAM_COST_STATUS, void 0);
6546
- throw error;
6547
- }
6548
- }
6549
- }],
6723
+ activities: [activity],
6550
6724
  async onStart() {
6551
6725
  runtime.pollScheduler.start();
6552
- telemetry.recordEvent("doom_team.session_started", { "team.root_session": resolveRootSessionId(execution.sessionId, environment) === execution.sessionId });
6553
- if (!ready) throw new Error("Team readiness was not installed.");
6554
- await Promise.allSettled([ready.wait()]);
6726
+ telemetry.recordEvent("po_team.session_started", { "team.root_session": resolveRootSessionId(execution.sessionId, environment) === execution.sessionId });
6555
6727
  },
6556
6728
  async onDispose() {
6557
6729
  disposing = true;
6558
- startupAbort.abort(/* @__PURE__ */ new Error("Team session ended."));
6559
- await Promise.allSettled([...ready ? [ready.wait()] : [], ...ownedReadiness ? [ownedReadiness.dispose()] : []]);
6560
6730
  activityAttached = false;
6561
- stopPeerDiscovery?.();
6562
- sessionPeers.clear();
6563
- execution.client.setStatus(TEAM_COST_STATUS, void 0);
6564
- channel.dispose();
6565
- runtime.completionNotifier.dispose();
6566
- const suspension = suspendScopeRuns({
6567
- scope,
6568
- reason: "headless session ended",
6569
- jobs: jobs.list(),
6570
- stop: (runId, reason) => runtime.management.stop(runId, reason)
6571
- });
6572
- const nativeShutdown = runtime.nativeRuns.close().finally(() => nativeRunProjection.dispose(execution.sessionId));
6573
- const outcomes = await Promise.allSettled([suspension, nativeShutdown]);
6574
- for (const outcome of outcomes) if (outcome.status === "rejected") process.emitWarning(`Could not shut down headless Team runs: ${String(outcome.reason)}`);
6575
- unsubscribeCostTracker();
6576
- unsubscribeCostNative();
6577
- nativeRuns.clear();
6578
- childCosts.clear();
6579
- costText = void 0;
6580
- bridge.abandonAll();
6581
- fablePlan.abandonAll();
6582
- runtime.capabilityPolicies.clear();
6583
- runtime.management.releaseSessionScope(scope);
6584
- runtime.dispose();
6585
- runtime.asyncJobTracker.stop();
6586
- runtime.pollScheduler.stop();
6587
- const suspended = outcomes[0];
6588
- await telemetry.recordEvent("doom_team.session_finished", {
6589
- reason: "headless session ended",
6590
- "team.suspended_count": suspended?.status === "fulfilled" ? suspended.value?.suspended.length ?? 0 : 0,
6591
- "team.unstoppable_count": suspended?.status === "fulfilled" ? suspended.value?.unstoppable.length ?? 0 : 0,
6592
- outcome: suspended?.status === "fulfilled" && !suspended.value?.unstoppable.length ? "suspended" : "degraded"
6593
- });
6594
- await telemetry.shutdown();
6731
+ let stoppedRuns;
6732
+ await runCleanups([
6733
+ () => stopPeerDiscovery?.(),
6734
+ () => sessionPeers.clear(),
6735
+ () => execution.client.setStatus(TEAM_COST_STATUS, void 0),
6736
+ () => channel.dispose(),
6737
+ () => runtime.completionNotifier.dispose(),
6738
+ async () => {
6739
+ stoppedRuns = await stopScopeRuns({
6740
+ reason: "headless session ended",
6741
+ jobs: jobs.list(),
6742
+ stop: (runId, reason) => runtime.management.stop(runId, reason)
6743
+ });
6744
+ },
6745
+ () => runCleanups([() => runtime.nativeRuns.close(), () => nativeRunProjection.dispose(execution.sessionId)], "Native Team runs cleanup failed."),
6746
+ unsubscribeCostTracker,
6747
+ unsubscribeCostNative,
6748
+ () => nativeRuns.clear(),
6749
+ () => childCosts.clear(),
6750
+ () => {
6751
+ costText = void 0;
6752
+ },
6753
+ () => bridge.abandonAll(),
6754
+ () => fablePlan.abandonAll(),
6755
+ () => runtime.capabilityPolicies.clear(),
6756
+ () => runtime.management.releaseSessionScope(scope),
6757
+ () => runtime.dispose(),
6758
+ () => runtime.asyncJobTracker.stop(),
6759
+ () => runtime.pollScheduler.stop(),
6760
+ () => telemetry.recordEvent("po_team.session_finished", {
6761
+ reason: "headless session ended",
6762
+ "team.stopped_count": stoppedRuns?.stopped.length ?? 0,
6763
+ "team.unstoppable_count": stoppedRuns?.unstoppable.length ?? 0,
6764
+ outcome: stoppedRuns && !stoppedRuns.unstoppable.length ? "stopped" : "degraded"
6765
+ }),
6766
+ () => telemetry.shutdown()
6767
+ ], "Team cleanup failed.");
6595
6768
  }
6596
6769
  };
6597
6770
  });
@@ -6657,13 +6830,9 @@ var subagent_catalog_default = defineChannel(createSubagentCatalogChannel);
6657
6830
  var subagent_catalog_server_default = defineChannel(subagent_catalog_default);
6658
6831
  //#endregion
6659
6832
  //#region src/extensions/(backend)/channel/_lib/subagent-runs.ts
6660
- const RUN_ID_PATTERN = /^[\w.-]+$/;
6661
6833
  function isSubagentRunsPayload(value) {
6662
6834
  return typeof value === "object" && value !== null && Array.isArray(value.runs);
6663
6835
  }
6664
- function publicPayload(payload) {
6665
- return { runs: payload.runs.map(({ sessionFile: _sessionFile, ...run }) => run) };
6666
- }
6667
6836
  /**
6668
6837
  * The built-in subagents data channel consumes current state from the
6669
6838
  * host-owned direct event bus. Its bounded latest replay seeds late subscribers.
@@ -6678,16 +6847,34 @@ function createSubagentsChannel() {
6678
6847
  const publish = (scope, payload) => {
6679
6848
  if (scopes.get(scope.sessionId) !== scope) return;
6680
6849
  latest.set(scope.sessionId, payload);
6681
- host.publish(scope.sessionId, publicPayload(payload));
6850
+ host.publish(scope.sessionId, payload);
6682
6851
  };
6683
6852
  return {
6684
- payloadFor(scope) {
6685
- const payload = scopes.has(scope.sessionId) ? latest.get(scope.sessionId) : void 0;
6686
- return payload === void 0 ? void 0 : publicPayload(payload);
6853
+ homeFor(scope) {
6854
+ const payload = latest.get(scope.sessionId);
6855
+ if (!payload) return {
6856
+ coverage: "unavailable",
6857
+ entities: []
6858
+ };
6859
+ return {
6860
+ coverage: payload.runs.length > HOME_ENTITY_LIMIT ? "partial" : "ready",
6861
+ entities: payload.runs.slice(0, HOME_ENTITY_LIMIT).map((run) => ({
6862
+ id: createHash("sha256").update(JSON.stringify([
6863
+ scope.workspaceId ?? scope.cwd,
6864
+ scope.sessionId,
6865
+ run.runId,
6866
+ run.startedAt
6867
+ ])).digest("hex"),
6868
+ kind: run.state === "failed" ? "failure" : run.state === "done" ? "completed" : run.state === "stopped" ? "paused" : run.state,
6869
+ title: (run.taskRef || run.task || run.agent).slice(0, 160),
6870
+ ...(run.error || run.currentTool)?.trim() ? { detail: (run.error || run.currentTool).slice(0, 320) } : {},
6871
+ updatedAt: new Date(run.lastUpdate).toISOString(),
6872
+ sessionId: scope.sessionId
6873
+ }))
6874
+ };
6687
6875
  },
6688
- threadJournal(scope, runId) {
6689
- if (!RUN_ID_PATTERN.test(runId)) return void 0;
6690
- return (latest.get(scope.sessionId)?.runs.find((run) => run.runId === runId))?.sessionFile;
6876
+ payloadFor(scope) {
6877
+ return scopes.has(scope.sessionId) ? latest.get(scope.sessionId) : void 0;
6691
6878
  },
6692
6879
  sessionAdded(scope) {
6693
6880
  subscriptions.get(scope.sessionId)?.();
@@ -6743,16 +6930,11 @@ function isStopRequest(value) {
6743
6930
  }
6744
6931
  /** The typed error's text carries a recovery hint written for the model, so a missing run gets a sentence for people. */
6745
6932
  function controlFailure(error) {
6746
- if (error instanceof DoomTeamExpectedError && error.code === "run_not_found") return Response.json({ error: "This run is not active." }, { status: 404 });
6933
+ if (error instanceof PoTeamExpectedError && error.code === "run_not_found") return Response.json({ error: "This run is not active." }, { status: 404 });
6747
6934
  return Response.json({ error: error instanceof Error ? error.message : String(error) }, { status: 409 });
6748
6935
  }
6749
6936
  /** Serves catalog discovery, run launch, and run steer and stop from the selected session process, never through a parent prompt. */
6750
6937
  function createTeamApiHandler(options) {
6751
- const discovery = new AgentDiscoveryService(options.environment);
6752
- const read = options.read ?? ((cwd) => ({
6753
- agents: discovery.discover(cwd, "both").agents,
6754
- models: resolveActiveTeamModelSpecs(options.environment) ?? []
6755
- }));
6756
6938
  return {
6757
6939
  async fetch(request) {
6758
6940
  const url = new URL(request.url);
@@ -6803,8 +6985,8 @@ function createTeamApiHandler(options) {
6803
6985
  }
6804
6986
  if (request.method !== "GET" || url.pathname !== "/catalog") return Response.json({ error: "Not found." }, { status: 404 });
6805
6987
  try {
6806
- discovery.invalidate();
6807
- const snapshot = read(options.cwd);
6988
+ options.invalidate?.();
6989
+ const snapshot = await options.read(options.cwd);
6808
6990
  return Response.json({
6809
6991
  cwd: options.cwd,
6810
6992
  agents: presentCatalog(snapshot.agents),
@@ -6837,9 +7019,11 @@ function createTeamSessionApi(runtime, execution) {
6837
7019
  sessionScope: scope,
6838
7020
  environment: execution.environment,
6839
7021
  parentSessionId: execution.sessionId,
7022
+ parentSelection: execution.selection,
6840
7023
  ...parentForkSource ? { parentForkSource } : {},
6841
- availableModels: execution.model ? [toModelInfo(execution.model)] : [],
6842
- ...execution.model ? { parentModel: execution.model } : {}
7024
+ availableModels: await availableModelInfos(execution),
7025
+ ...execution.model ? { parentModel: execution.model } : {},
7026
+ sender: HUMAN_SENDER
6843
7027
  }, loadConfig().config)).outcomes[0];
6844
7028
  if (!outcome?.runId) throw new Error(outcome?.error ?? "The agent did not start.");
6845
7029
  jobs.track(outcome.runId, outcome.identity ? {
@@ -6852,9 +7036,16 @@ function createTeamSessionApi(runtime, execution) {
6852
7036
  basePath: TEAM_API_BASE_PATH,
6853
7037
  start: () => createTeamApiHandler({
6854
7038
  cwd: execution.cwd,
6855
- environment: execution.environment,
7039
+ read: async (cwd) => ({
7040
+ agents: (await runtime.discovery.discover(cwd, "both")).agents,
7041
+ models: [...(await runtime.agentConfig.currentOrUndefined())?.models ?? []]
7042
+ }),
7043
+ invalidate: () => runtime.discovery.invalidate(),
6856
7044
  launch,
6857
- steer: (request, signal) => runtime.management.steer(request.runId, request.message, void 0, signal),
7045
+ steer: (request, signal) => runtime.management.steer(request.runId, {
7046
+ text: request.message,
7047
+ sender: HUMAN_SENDER
7048
+ }, void 0, signal),
6858
7049
  stop: (request) => runtime.management.stop(request.runId)
6859
7050
  })
6860
7051
  };
@@ -6864,6 +7055,12 @@ var route_server_default$1 = (context) => createTeamSessionApi(context.root.runt
6864
7055
  //#region src/extensions/workspaces/sessions/(backend)/api/route.server.ts
6865
7056
  var route_server_default = defineRoute(route_server_default$1);
6866
7057
  //#endregion
7058
+ //#region src/extensions/workspaces/sessions/(backend)/hook/sessionStart.server.ts
7059
+ var sessionStart_server_default = defineHook(({ root }) => ({
7060
+ event: "session_start",
7061
+ handle: () => root.restoreRuns()
7062
+ }));
7063
+ //#endregion
6867
7064
  //#region src/extensions/workspaces/sessions/(backend)/resource/_lib/team.server.ts
6868
7065
  /**
6869
7066
  * The roster the model is allowed to see.
@@ -6872,7 +7069,7 @@ var route_server_default = defineRoute(route_server_default$1);
6872
7069
  * `TeamMemberContext`, whose `token` is a session capability documented as never
6873
7070
  * leaving the direct context. A context resource is pasted into the system prompt,
6874
7071
  * sent to the provider and persisted, so the raw struct must not be serialized here.
6875
- * `formatTeamContextSnapshot` is the same projection the Pi facet renders.
7072
+ * `formatTeamContextSnapshot` is the same projection the Pi extension renders.
6876
7073
  */
6877
7074
  var team_server_default$1 = (context) => ({
6878
7075
  name: "po/team",
@@ -6894,21 +7091,24 @@ var subagent_server_default = defineServerTool((context) => createHeadlessSubage
6894
7091
  //#endregion
6895
7092
  //#region generated/server.ts
6896
7093
  const at = (value, context) => typeof value === "function" ? value(context) : value;
7094
+ /* v8 ignore next -- generated normalization branch */
7095
+ const defined = (values) => values.filter((value) => value !== void 0);
6897
7096
  const via = (identity, value) => value === void 0 ? void 0 : {
6898
7097
  ...identity,
6899
7098
  ...value
6900
7099
  };
6901
- const facet = defineServerPlugin({
7100
+ const serverPlugin = defineServerPlugin({
6902
7101
  name: "@agimon-ai/po-team",
6903
7102
  providerOverrides: composeServerProviderOverrides(root_server_default),
7103
+ injects: composeRootInjects({ session: [root_server_default] }),
6904
7104
  global: () => ({ get channels() {
6905
7105
  return [() => via({ frameType: "subagent_catalog" }, subagent_catalog_server_default()), () => via({ frameType: "subagent_runs" }, subagent_runs_server_default())];
6906
7106
  } }),
6907
- workspace: () => ({ get channels() {
6908
- return [() => via({ frameType: "subagent_catalog" }, subagent_catalog_server_default()), () => via({ frameType: "subagent_runs" }, subagent_runs_server_default())];
7107
+ workspace: (context) => ({ get channels() {
7108
+ return defined([context.hosted("global") ? void 0 : () => via({ frameType: "subagent_catalog" }, subagent_catalog_server_default()), context.hosted("global") ? void 0 : () => via({ frameType: "subagent_runs" }, subagent_runs_server_default())]);
6909
7109
  } }),
6910
7110
  session: async (context) => {
6911
- const scopeSession = await root_server_default(context);
7111
+ const scopeSession = await context.scopeRoot("session", root_server_default, context);
6912
7112
  const contextSession = {
6913
7113
  ...context,
6914
7114
  root: scopeSession.value
@@ -6919,7 +7119,10 @@ const facet = defineServerPlugin({
6919
7119
  return [at(route_server_default, contextSession)];
6920
7120
  },
6921
7121
  get channels() {
6922
- return [() => via({ frameType: "subagent_catalog" }, subagent_catalog_server_default()), () => via({ frameType: "subagent_runs" }, subagent_runs_server_default())];
7122
+ return defined([context.hosted("global") ? void 0 : () => via({ frameType: "subagent_catalog" }, subagent_catalog_server_default()), context.hosted("global") ? void 0 : () => via({ frameType: "subagent_runs" }, subagent_runs_server_default())]);
7123
+ },
7124
+ get hooks() {
7125
+ return [via({ event: "session_start" }, at(sessionStart_server_default, contextSession))];
6923
7126
  },
6924
7127
  get resources() {
6925
7128
  return [at(team_server_default, contextSession)];
@@ -6935,6 +7138,6 @@ const facet = defineServerPlugin({
6935
7138
  }
6936
7139
  });
6937
7140
  //#endregion
6938
- export { facet as default, facet };
7141
+ export { serverPlugin as default, serverPlugin };
6939
7142
 
6940
7143
  //# sourceMappingURL=server.mjs.map