pilotswarm-sdk 0.5.64 → 0.5.65

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 (231) hide show
  1. package/dist/activity-routing.d.ts +21 -0
  2. package/dist/activity-routing.d.ts.map +1 -0
  3. package/dist/activity-routing.js +32 -0
  4. package/dist/activity-routing.js.map +1 -0
  5. package/dist/agent-discovery.d.ts +35 -0
  6. package/dist/agent-discovery.d.ts.map +1 -0
  7. package/dist/agent-discovery.js +107 -0
  8. package/dist/agent-discovery.js.map +1 -0
  9. package/dist/client.d.ts +9 -1
  10. package/dist/client.d.ts.map +1 -1
  11. package/dist/client.js +94 -12
  12. package/dist/client.js.map +1 -1
  13. package/dist/managed-session.d.ts.map +1 -1
  14. package/dist/managed-session.js +21 -13
  15. package/dist/managed-session.js.map +1 -1
  16. package/dist/native-subagents.d.ts +1 -1
  17. package/dist/native-subagents.d.ts.map +1 -1
  18. package/dist/native-subagents.js +11 -6
  19. package/dist/native-subagents.js.map +1 -1
  20. package/dist/orchestration/agents.d.ts +8 -3
  21. package/dist/orchestration/agents.d.ts.map +1 -1
  22. package/dist/orchestration/agents.js +87 -36
  23. package/dist/orchestration/agents.js.map +1 -1
  24. package/dist/orchestration/index.d.ts +2 -2
  25. package/dist/orchestration/index.js +1 -1
  26. package/dist/orchestration/lifecycle.js +1 -1
  27. package/dist/orchestration/lifecycle.js.map +1 -1
  28. package/dist/orchestration/runtime.d.ts +1 -1
  29. package/dist/orchestration/runtime.d.ts.map +1 -1
  30. package/dist/orchestration/runtime.js +18 -4
  31. package/dist/orchestration/runtime.js.map +1 -1
  32. package/dist/orchestration/turn.d.ts.map +1 -1
  33. package/dist/orchestration/turn.js +3 -5
  34. package/dist/orchestration/turn.js.map +1 -1
  35. package/dist/orchestration-registry.d.ts.map +1 -1
  36. package/dist/orchestration-registry.js +12 -2
  37. package/dist/orchestration-registry.js.map +1 -1
  38. package/dist/orchestration-version.d.ts +1 -1
  39. package/dist/orchestration-version.js +1 -1
  40. package/dist/orchestration.d.ts +2 -2
  41. package/dist/orchestration.js +1 -1
  42. package/dist/orchestration_1_0_46.d.ts +1 -1
  43. package/dist/orchestration_1_0_47.d.ts +1 -1
  44. package/dist/orchestration_1_0_48.d.ts +1 -1
  45. package/dist/orchestration_1_0_49.d.ts +1 -1
  46. package/dist/orchestration_1_0_50.d.ts +1 -1
  47. package/dist/orchestration_1_0_73/agents.d.ts +61 -0
  48. package/dist/orchestration_1_0_73/agents.d.ts.map +1 -0
  49. package/dist/orchestration_1_0_73/agents.js +843 -0
  50. package/dist/orchestration_1_0_73/agents.js.map +1 -0
  51. package/dist/orchestration_1_0_73/index.d.ts +24 -0
  52. package/dist/orchestration_1_0_73/index.d.ts.map +1 -0
  53. package/dist/orchestration_1_0_73/index.js +13 -0
  54. package/dist/orchestration_1_0_73/index.js.map +1 -0
  55. package/dist/orchestration_1_0_73/lifecycle.d.ts +51 -0
  56. package/dist/orchestration_1_0_73/lifecycle.d.ts.map +1 -0
  57. package/dist/orchestration_1_0_73/lifecycle.js +981 -0
  58. package/dist/orchestration_1_0_73/lifecycle.js.map +1 -0
  59. package/dist/orchestration_1_0_73/queue.d.ts +20 -0
  60. package/dist/orchestration_1_0_73/queue.d.ts.map +1 -0
  61. package/dist/orchestration_1_0_73/queue.js +894 -0
  62. package/dist/orchestration_1_0_73/queue.js.map +1 -0
  63. package/dist/orchestration_1_0_73/runtime.d.ts +31 -0
  64. package/dist/orchestration_1_0_73/runtime.d.ts.map +1 -0
  65. package/dist/orchestration_1_0_73/runtime.js +230 -0
  66. package/dist/orchestration_1_0_73/runtime.js.map +1 -0
  67. package/dist/orchestration_1_0_73/state.d.ts +214 -0
  68. package/dist/orchestration_1_0_73/state.d.ts.map +1 -0
  69. package/dist/orchestration_1_0_73/state.js +178 -0
  70. package/dist/orchestration_1_0_73/state.js.map +1 -0
  71. package/dist/orchestration_1_0_73/turn.d.ts +34 -0
  72. package/dist/orchestration_1_0_73/turn.d.ts.map +1 -0
  73. package/dist/orchestration_1_0_73/turn.js +1435 -0
  74. package/dist/orchestration_1_0_73/turn.js.map +1 -0
  75. package/dist/orchestration_1_0_73/utils.d.ts +39 -0
  76. package/dist/orchestration_1_0_73/utils.d.ts.map +1 -0
  77. package/dist/orchestration_1_0_73/utils.js +322 -0
  78. package/dist/orchestration_1_0_73/utils.js.map +1 -0
  79. package/dist/orchestration_1_0_74/agents.d.ts +61 -0
  80. package/dist/orchestration_1_0_74/agents.d.ts.map +1 -0
  81. package/dist/orchestration_1_0_74/agents.js +889 -0
  82. package/dist/orchestration_1_0_74/agents.js.map +1 -0
  83. package/dist/orchestration_1_0_74/index.d.ts +24 -0
  84. package/dist/orchestration_1_0_74/index.d.ts.map +1 -0
  85. package/dist/orchestration_1_0_74/index.js +13 -0
  86. package/dist/orchestration_1_0_74/index.js.map +1 -0
  87. package/dist/orchestration_1_0_74/lifecycle.d.ts +51 -0
  88. package/dist/orchestration_1_0_74/lifecycle.d.ts.map +1 -0
  89. package/dist/orchestration_1_0_74/lifecycle.js +981 -0
  90. package/dist/orchestration_1_0_74/lifecycle.js.map +1 -0
  91. package/dist/orchestration_1_0_74/queue.d.ts +20 -0
  92. package/dist/orchestration_1_0_74/queue.d.ts.map +1 -0
  93. package/dist/orchestration_1_0_74/queue.js +894 -0
  94. package/dist/orchestration_1_0_74/queue.js.map +1 -0
  95. package/dist/orchestration_1_0_74/runtime.d.ts +31 -0
  96. package/dist/orchestration_1_0_74/runtime.d.ts.map +1 -0
  97. package/dist/orchestration_1_0_74/runtime.js +232 -0
  98. package/dist/orchestration_1_0_74/runtime.js.map +1 -0
  99. package/dist/orchestration_1_0_74/state.d.ts +214 -0
  100. package/dist/orchestration_1_0_74/state.d.ts.map +1 -0
  101. package/dist/orchestration_1_0_74/state.js +178 -0
  102. package/dist/orchestration_1_0_74/state.js.map +1 -0
  103. package/dist/orchestration_1_0_74/turn.d.ts +34 -0
  104. package/dist/orchestration_1_0_74/turn.d.ts.map +1 -0
  105. package/dist/orchestration_1_0_74/turn.js +1435 -0
  106. package/dist/orchestration_1_0_74/turn.js.map +1 -0
  107. package/dist/orchestration_1_0_74/utils.d.ts +39 -0
  108. package/dist/orchestration_1_0_74/utils.d.ts.map +1 -0
  109. package/dist/orchestration_1_0_74/utils.js +322 -0
  110. package/dist/orchestration_1_0_74/utils.js.map +1 -0
  111. package/dist/orchestration_1_0_75/agents.d.ts +61 -0
  112. package/dist/orchestration_1_0_75/agents.d.ts.map +1 -0
  113. package/dist/orchestration_1_0_75/agents.js +887 -0
  114. package/dist/orchestration_1_0_75/agents.js.map +1 -0
  115. package/dist/orchestration_1_0_75/index.d.ts +24 -0
  116. package/dist/orchestration_1_0_75/index.d.ts.map +1 -0
  117. package/dist/orchestration_1_0_75/index.js +13 -0
  118. package/dist/orchestration_1_0_75/index.js.map +1 -0
  119. package/dist/orchestration_1_0_75/lifecycle.d.ts +51 -0
  120. package/dist/orchestration_1_0_75/lifecycle.d.ts.map +1 -0
  121. package/dist/orchestration_1_0_75/lifecycle.js +981 -0
  122. package/dist/orchestration_1_0_75/lifecycle.js.map +1 -0
  123. package/dist/orchestration_1_0_75/queue.d.ts +20 -0
  124. package/dist/orchestration_1_0_75/queue.d.ts.map +1 -0
  125. package/dist/orchestration_1_0_75/queue.js +894 -0
  126. package/dist/orchestration_1_0_75/queue.js.map +1 -0
  127. package/dist/orchestration_1_0_75/runtime.d.ts +31 -0
  128. package/dist/orchestration_1_0_75/runtime.d.ts.map +1 -0
  129. package/dist/orchestration_1_0_75/runtime.js +239 -0
  130. package/dist/orchestration_1_0_75/runtime.js.map +1 -0
  131. package/dist/orchestration_1_0_75/state.d.ts +214 -0
  132. package/dist/orchestration_1_0_75/state.d.ts.map +1 -0
  133. package/dist/orchestration_1_0_75/state.js +178 -0
  134. package/dist/orchestration_1_0_75/state.js.map +1 -0
  135. package/dist/orchestration_1_0_75/turn.d.ts +34 -0
  136. package/dist/orchestration_1_0_75/turn.d.ts.map +1 -0
  137. package/dist/orchestration_1_0_75/turn.js +1435 -0
  138. package/dist/orchestration_1_0_75/turn.js.map +1 -0
  139. package/dist/orchestration_1_0_75/utils.d.ts +39 -0
  140. package/dist/orchestration_1_0_75/utils.d.ts.map +1 -0
  141. package/dist/orchestration_1_0_75/utils.js +322 -0
  142. package/dist/orchestration_1_0_75/utils.js.map +1 -0
  143. package/dist/orchestration_1_0_76/agents.d.ts +61 -0
  144. package/dist/orchestration_1_0_76/agents.d.ts.map +1 -0
  145. package/dist/orchestration_1_0_76/agents.js +865 -0
  146. package/dist/orchestration_1_0_76/agents.js.map +1 -0
  147. package/dist/orchestration_1_0_76/index.d.ts +24 -0
  148. package/dist/orchestration_1_0_76/index.d.ts.map +1 -0
  149. package/dist/orchestration_1_0_76/index.js +13 -0
  150. package/dist/orchestration_1_0_76/index.js.map +1 -0
  151. package/dist/orchestration_1_0_76/lifecycle.d.ts +51 -0
  152. package/dist/orchestration_1_0_76/lifecycle.d.ts.map +1 -0
  153. package/dist/orchestration_1_0_76/lifecycle.js +981 -0
  154. package/dist/orchestration_1_0_76/lifecycle.js.map +1 -0
  155. package/dist/orchestration_1_0_76/queue.d.ts +20 -0
  156. package/dist/orchestration_1_0_76/queue.d.ts.map +1 -0
  157. package/dist/orchestration_1_0_76/queue.js +894 -0
  158. package/dist/orchestration_1_0_76/queue.js.map +1 -0
  159. package/dist/orchestration_1_0_76/runtime.d.ts +31 -0
  160. package/dist/orchestration_1_0_76/runtime.d.ts.map +1 -0
  161. package/dist/orchestration_1_0_76/runtime.js +239 -0
  162. package/dist/orchestration_1_0_76/runtime.js.map +1 -0
  163. package/dist/orchestration_1_0_76/state.d.ts +214 -0
  164. package/dist/orchestration_1_0_76/state.d.ts.map +1 -0
  165. package/dist/orchestration_1_0_76/state.js +178 -0
  166. package/dist/orchestration_1_0_76/state.js.map +1 -0
  167. package/dist/orchestration_1_0_76/turn.d.ts +34 -0
  168. package/dist/orchestration_1_0_76/turn.d.ts.map +1 -0
  169. package/dist/orchestration_1_0_76/turn.js +1435 -0
  170. package/dist/orchestration_1_0_76/turn.js.map +1 -0
  171. package/dist/orchestration_1_0_76/utils.d.ts +39 -0
  172. package/dist/orchestration_1_0_76/utils.d.ts.map +1 -0
  173. package/dist/orchestration_1_0_76/utils.js +322 -0
  174. package/dist/orchestration_1_0_76/utils.js.map +1 -0
  175. package/dist/orchestration_1_0_77/agents.d.ts +66 -0
  176. package/dist/orchestration_1_0_77/agents.d.ts.map +1 -0
  177. package/dist/orchestration_1_0_77/agents.js +895 -0
  178. package/dist/orchestration_1_0_77/agents.js.map +1 -0
  179. package/dist/orchestration_1_0_77/index.d.ts +24 -0
  180. package/dist/orchestration_1_0_77/index.d.ts.map +1 -0
  181. package/dist/orchestration_1_0_77/index.js +13 -0
  182. package/dist/orchestration_1_0_77/index.js.map +1 -0
  183. package/dist/orchestration_1_0_77/lifecycle.d.ts +51 -0
  184. package/dist/orchestration_1_0_77/lifecycle.d.ts.map +1 -0
  185. package/dist/orchestration_1_0_77/lifecycle.js +981 -0
  186. package/dist/orchestration_1_0_77/lifecycle.js.map +1 -0
  187. package/dist/orchestration_1_0_77/queue.d.ts +20 -0
  188. package/dist/orchestration_1_0_77/queue.d.ts.map +1 -0
  189. package/dist/orchestration_1_0_77/queue.js +894 -0
  190. package/dist/orchestration_1_0_77/queue.js.map +1 -0
  191. package/dist/orchestration_1_0_77/runtime.d.ts +31 -0
  192. package/dist/orchestration_1_0_77/runtime.d.ts.map +1 -0
  193. package/dist/orchestration_1_0_77/runtime.js +239 -0
  194. package/dist/orchestration_1_0_77/runtime.js.map +1 -0
  195. package/dist/orchestration_1_0_77/state.d.ts +214 -0
  196. package/dist/orchestration_1_0_77/state.d.ts.map +1 -0
  197. package/dist/orchestration_1_0_77/state.js +178 -0
  198. package/dist/orchestration_1_0_77/state.js.map +1 -0
  199. package/dist/orchestration_1_0_77/turn.d.ts +34 -0
  200. package/dist/orchestration_1_0_77/turn.d.ts.map +1 -0
  201. package/dist/orchestration_1_0_77/turn.js +1433 -0
  202. package/dist/orchestration_1_0_77/turn.js.map +1 -0
  203. package/dist/orchestration_1_0_77/utils.d.ts +39 -0
  204. package/dist/orchestration_1_0_77/utils.d.ts.map +1 -0
  205. package/dist/orchestration_1_0_77/utils.js +322 -0
  206. package/dist/orchestration_1_0_77/utils.js.map +1 -0
  207. package/dist/reserved-tool-names.d.ts +5 -0
  208. package/dist/reserved-tool-names.d.ts.map +1 -0
  209. package/dist/reserved-tool-names.js +102 -0
  210. package/dist/reserved-tool-names.js.map +1 -0
  211. package/dist/session-manager.d.ts +25 -1
  212. package/dist/session-manager.d.ts.map +1 -1
  213. package/dist/session-manager.js +275 -93
  214. package/dist/session-manager.js.map +1 -1
  215. package/dist/session-proxy.d.ts +28 -3
  216. package/dist/session-proxy.d.ts.map +1 -1
  217. package/dist/session-proxy.js +174 -70
  218. package/dist/session-proxy.js.map +1 -1
  219. package/dist/session-store.d.ts +2 -0
  220. package/dist/session-store.d.ts.map +1 -1
  221. package/dist/session-store.js +18 -2
  222. package/dist/session-store.js.map +1 -1
  223. package/dist/types.d.ts +17 -3
  224. package/dist/types.d.ts.map +1 -1
  225. package/dist/types.js.map +1 -1
  226. package/dist/worker.d.ts +9 -3
  227. package/dist/worker.d.ts.map +1 -1
  228. package/dist/worker.js +110 -99
  229. package/dist/worker.js.map +1 -1
  230. package/package.json +4 -3
  231. package/plugins/system/agents/default.agent.md +4 -3
@@ -1,6 +1,7 @@
1
+ import { HANDOFF_ACTIVITY_NAMES, routeHandoffActivity, routedActivityName, registerHandoffActivity } from "./activity-routing.js";
1
2
  import nodeCrypto from "node:crypto";
2
3
  import { createCopilotClient } from "./copilot-client.js";
3
- import { isSessionLockAcquireTimeoutError } from "./session-manager.js";
4
+ import { BoundAgentPackageUnavailableError, isSessionLockAcquireTimeoutError, PackageToolBindingError, } from "./session-manager.js";
4
5
  import { extractCanvasAppManifest, canvasAppCard, normalizeCanvasResponseContract } from "./canvas-app-manifest.js";
5
6
  import { readCanvasKv, writeCanvasKv } from "./canvas-kv.js";
6
7
  import { publishCanvasApp, findCanvasApp } from "./canvas-app-catalog.js";
@@ -37,6 +38,13 @@ import { attemptStoreRecovery, runTurnCommit, runTurnPreamble } from "./session-
37
38
  import { supportsVersionedSnapshots, writeTurnSentinel } from "./snapshot-protocol.js";
38
39
  import { LatestValuePublisher } from "./live-turn.js";
39
40
  const SYSTEM_AGENT_IDS = new Set(["pilotswarm", "sweeper", "resourcemgr", "facts-manager"]);
41
+ /** Same lineage principal for inline and durable agent selection. */
42
+ async function resolveCallerAgentOwnerKey(catalog, sessionId) {
43
+ const owner = catalog && sessionId
44
+ ? await resolveEffectiveSpawnOwner((id) => catalog.getSession(id), sessionId).catch(() => null)
45
+ : null;
46
+ return owner?.provider && owner?.subject ? `${owner.provider}\u0001${owner.subject}` : null;
47
+ }
40
48
  /**
41
49
  * THE agent-name resolver: FQN parsing, fuzzy matching, package privacy, and
42
50
  * owner shadowing in one place.
@@ -107,8 +115,10 @@ export async function resolveAgentDefinitionForCaller(opts) {
107
115
  const ownerKeyOf = (owner) => owner?.provider && owner?.subject ? `${owner.provider}\u0001${owner.subject}` : null;
108
116
  const visibleToCaller = async (agent) => {
109
117
  const agentOwnerKey = ownerKeyOf(agent?.packageOwner);
110
- if (agent?.packageScope !== "user" || !agentOwnerKey)
118
+ if (agent?.packageScope !== "user")
111
119
  return true;
120
+ if (!agentOwnerKey)
121
+ return false;
112
122
  const key = await callerOwnerKeyOnce();
113
123
  return key !== null && key === agentOwnerKey;
114
124
  };
@@ -172,6 +182,36 @@ export async function resolveAgentDefinitionForCaller(opts) {
172
182
  packageScope: agent.packageScope ?? undefined,
173
183
  };
174
184
  }
185
+ /** Legacy activity compatibility for frozen orchestrations. Not a spawn_agent selector. */
186
+ export async function resolveAgentDefinitionForRequiredToolForCaller(opts) {
187
+ const requiredTool = String(opts.requiredTool || "").trim();
188
+ if (!requiredTool)
189
+ return { status: "not_found", candidates: [] };
190
+ const declaresTool = (agent) => agent?.tools?.includes(requiredTool)
191
+ || agent?.copies?.some((copy) => copy?.tools?.includes(requiredTool));
192
+ const logicalNames = [...new Set((opts.userAgents ?? [])
193
+ .filter(declaresTool)
194
+ .map((agent) => String(agent?.name || "").trim())
195
+ .filter(Boolean))].sort((left, right) => left.localeCompare(right));
196
+ const resolved = new Map();
197
+ for (const agentName of logicalNames) {
198
+ const agent = await resolveAgentDefinitionForCaller({
199
+ agentName,
200
+ userAgents: opts.userAgents,
201
+ systemAgents: opts.systemAgents,
202
+ getCallerOwnerKey: opts.getCallerOwnerKey,
203
+ });
204
+ if (!agent || agent.creatable === false || !agent.tools?.includes(requiredTool))
205
+ continue;
206
+ resolved.set(agent.name, agent);
207
+ }
208
+ const candidates = [...resolved.keys()].sort((left, right) => left.localeCompare(right));
209
+ if (candidates.length === 0)
210
+ return { status: "not_found", candidates: [] };
211
+ if (candidates.length > 1)
212
+ return { status: "ambiguous", candidates };
213
+ return { status: "resolved", agent: resolved.get(candidates[0]), candidates };
214
+ }
175
215
  // The canvas helpers (filenames, slot normalization, revision derivation)
176
216
  // live in canvas-support.ts, shared with the app catalog. Re-exported so
177
217
  // existing importers keep their path.
@@ -521,15 +561,15 @@ function normalizeDistillerEffort(value) {
521
561
  function normalizeDistillerTier(value) {
522
562
  return value && DISTILLER_TIERS.has(value) ? value : undefined;
523
563
  }
524
- export function createSessionProxy(ctx, sessionId, affinityKey, config) {
564
+ export function createSessionProxy(ctx, sessionId, affinityKey, config, routingContract) {
525
565
  return {
526
566
  runTurn(prompt, bootstrap, turnIndex, turnMeta) {
527
- return ctx.scheduleActivityOnSession(
567
+ return routeHandoffActivity(ctx.scheduleActivityOnSession(
528
568
  // The epoch-start turn is a distinct activity name (runTurn2):
529
- // pre-1.0.67 workers don't register it, so a rolling deploy can
530
- // never hand the fresh-epoch create to a worker that would
531
- // silently resume the dead transcript instead.
532
- turnMeta?.epochStart ? "runTurn2" : "runTurn", {
569
+ // with an explicit contract since 1.0.67. New handoffs ALSO
570
+ // require a capability tag: activity names alone do not stop
571
+ // old workers from dequeuing work they cannot execute.
572
+ routedActivityName(turnMeta?.epochStart ? "runTurn2" : "runTurn", routingContract), {
533
573
  sessionId,
534
574
  prompt,
535
575
  config,
@@ -566,7 +606,7 @@ export function createSessionProxy(ctx, sessionId, affinityKey, config) {
566
606
  ...(turnMeta?.stashedPrompts && turnMeta.stashedPrompts.length > 0
567
607
  ? { stashedPrompts: turnMeta.stashedPrompts }
568
608
  : {}),
569
- }, affinityKey);
609
+ }, affinityKey), routingContract);
570
610
  },
571
611
  dehydrate(reason, eventData) {
572
612
  return ctx.scheduleActivityOnSession("dehydrateSession", {
@@ -734,7 +774,7 @@ export function bootstrapTurnOptions(requiredTool) {
734
774
  // ─── SessionManagerProxy ─────────────────────────────────────────
735
775
  // The orchestration's view of the SessionManager singleton.
736
776
  // Operations that don't require session affinity.
737
- export function createSessionManagerProxy(ctx) {
777
+ export function createSessionManagerProxy(ctx, routingContract, options) {
738
778
  return {
739
779
  listModels() {
740
780
  return ctx.scheduleActivity("listModels", {});
@@ -744,10 +784,10 @@ export function createSessionManagerProxy(ctx) {
744
784
  },
745
785
  /** Spawn a child session via the PilotSwarmClient SDK. Returns the generated child session ID. */
746
786
  spawnChildSession(parentSessionId, config, task, nestingLevel, isSystem, title, agentId, splash, titleIsExplicit, requiredTool) {
747
- return ctx.scheduleActivity("spawnChildSession", {
787
+ return routeHandoffActivity(ctx.scheduleActivity(routedActivityName("spawnChildSession", routingContract), {
748
788
  parentSessionId, config, task, nestingLevel, isSystem, title, agentId, splash, titleIsExplicit,
749
789
  ...(requiredTool ? { requiredTool } : {}),
750
- });
790
+ }), routingContract);
751
791
  },
752
792
  /**
753
793
  * Resolve a loaded agent config by name. Returns null if not found.
@@ -758,8 +798,18 @@ export function createSessionManagerProxy(ctx) {
758
798
  * the orchestration generator keeps the yield sequence byte-identical, so
759
799
  * this is not an orchestration version change.
760
800
  */
761
- resolveAgentConfig(agentName) {
762
- return ctx.scheduleActivity("resolveAgentConfig", { agentName, callerSessionId: ctx.instanceId });
801
+ resolveAgentConfig(agentName, callerSessionId, binding) {
802
+ return routeHandoffActivity(ctx.scheduleActivity(routedActivityName("resolveAgentConfig", routingContract), {
803
+ agentName,
804
+ callerSessionId: callerSessionId ?? ctx.instanceId,
805
+ ...(binding ? { binding } : {}),
806
+ }), routingContract);
807
+ },
808
+ resolveAgentForRequiredTool(requiredTool, callerSessionId) {
809
+ return routeHandoffActivity(ctx.scheduleActivity(routedActivityName("resolveAgentForRequiredTool", routingContract), {
810
+ requiredTool,
811
+ callerSessionId: callerSessionId ?? ctx.instanceId,
812
+ }), routingContract);
763
813
  },
764
814
  /** Send a message to a session via the PilotSwarmClient SDK. */
765
815
  sendToSession(sessionId, message) {
@@ -771,6 +821,9 @@ export function createSessionManagerProxy(ctx) {
771
821
  },
772
822
  /** Get the status of a session via the PilotSwarmClient SDK. */
773
823
  getSessionStatus(sessionId) {
824
+ if (options?.childResultProvenance) {
825
+ return routeHandoffActivity(ctx.scheduleActivity(HANDOFF_ACTIVITY_NAMES.getSessionStatus, { sessionId }), routingContract);
826
+ }
774
827
  return ctx.scheduleActivity("getSessionStatus", { sessionId });
775
828
  },
776
829
  /** Get orchestration runtime stats for a session. */
@@ -783,6 +836,9 @@ export function createSessionManagerProxy(ctx) {
783
836
  },
784
837
  /** List direct child sessions of a session. */
785
838
  listChildSessions(parentSessionId) {
839
+ if (options?.childResultProvenance) {
840
+ return routeHandoffActivity(ctx.scheduleActivity(HANDOFF_ACTIVITY_NAMES.listChildSessions, { parentSessionId }), routingContract);
841
+ }
786
842
  return ctx.scheduleActivity("listChildSessions", { parentSessionId });
787
843
  },
788
844
  /** @deprecated Send a child_updates event to a parent orchestration. Use sendToSession instead. */
@@ -1367,18 +1423,12 @@ artifactStore) {
1367
1423
  // previous inline copy had none of that: it could hand another user's
1368
1424
  // private agent to this session and could not address the shared copy
1369
1425
  // of a shadowed name.
1426
+ const getCallerOwnerKeyInline = () => resolveCallerAgentOwnerKey(catalog, input.sessionId);
1370
1427
  const resolveAgentConfigInline = (agentName) => resolveAgentDefinitionForCaller({
1371
1428
  agentName,
1372
1429
  userAgents,
1373
1430
  systemAgents,
1374
- getCallerOwnerKey: async () => {
1375
- const owner = catalog
1376
- ? await resolveEffectiveSpawnOwner((id) => catalog.getSession(id), input.sessionId).catch(() => null)
1377
- : null;
1378
- return owner?.provider && owner?.subject
1379
- ? `${owner.provider}\u0001${owner.subject}`
1380
- : null;
1381
- },
1431
+ getCallerOwnerKey: getCallerOwnerKeyInline,
1382
1432
  });
1383
1433
  const loadDirectChildSessions = async () => {
1384
1434
  const sdkClient = await getInlineClient();
@@ -1664,15 +1714,18 @@ artifactStore) {
1664
1714
  nestingLevel: 0,
1665
1715
  ...(normalizedModel ? { model: normalizedModel } : {}),
1666
1716
  ...(args.reasoning_effort ? { reasoningEffort: args.reasoning_effort } : {}),
1667
- boundAgentName: agentDef.name,
1717
+ boundAgentName: !agentDef.packageId && agentDef.namespace ? `${agentDef.namespace}:${agentDef.name}` : agentDef.name,
1718
+ ...(agentDef.packageId ? { boundAgentPackageId: agentDef.packageId } : {}),
1719
+ ...(!agentDef.packageId ? { boundAgentSource: "deployment" } : {}),
1720
+ namedAgentToolAdditions: [],
1668
1721
  promptLayering: { kind: "app-agent" },
1669
1722
  ...(agentDef.tools ? { toolNames: agentDef.tools } : {}),
1670
- agentId: agentDef.id ?? agentName,
1723
+ agentId: agentDef.id ?? agentDef.name,
1671
1724
  ...(owner ? { owner } : {}),
1672
1725
  });
1673
1726
  if (catalog) {
1674
1727
  const meta = {
1675
- agentId: agentDef.id ?? agentName,
1728
+ agentId: agentDef.id ?? agentDef.name,
1676
1729
  title: typeof args.title === "string" && args.title.trim()
1677
1730
  ? args.title.trim()
1678
1731
  : `${agentDef.title || agentName}: ${newSessionId.slice(0, 8)}`,
@@ -1716,6 +1769,9 @@ artifactStore) {
1716
1769
  },
1717
1770
  spawnAgent: async (args) => {
1718
1771
  try {
1772
+ if (Object.hasOwn(args, "required_tool") || Object.hasOwn(args, "requiredTool")) {
1773
+ return `[SYSTEM: spawn_agent failed — required_tool is no longer supported by spawn_agent. Use ps_list_agents to find a suitable named agent, then pass its exact agent_name and your assignment in task.]`;
1774
+ }
1719
1775
  const childNestingLevel = (input.nestingLevel ?? 0) + 1;
1720
1776
  if (childNestingLevel > MAX_NESTING_LEVEL) {
1721
1777
  return `[SYSTEM: spawn_agent failed — you are already at nesting level ${input.nestingLevel ?? 0} (max ${MAX_NESTING_LEVEL}). ` +
@@ -1741,24 +1797,24 @@ artifactStore) {
1741
1797
  let agentSplashMobile;
1742
1798
  let bootstrapRequiredTool;
1743
1799
  let boundAgentName;
1800
+ let boundAgentPackageId;
1744
1801
  let promptLayeringKind;
1745
1802
  let resolvedAgentName = args.agent_name;
1746
- const applyAgentDef = (agentDef, useDefinitionDefaults = false) => {
1747
- agentTask = useDefinitionDefaults
1748
- ? (agentDef.initialPrompt || `You are the ${agentDef.name} agent. Begin your work.`)
1749
- : (args.task || agentDef.initialPrompt || `You are the ${agentDef.name} agent. Begin your work.`);
1750
- agentSystemMessage = useDefinitionDefaults ? undefined : args.system_message;
1751
- agentToolNames = useDefinitionDefaults
1752
- ? (agentDef.tools ?? undefined)
1753
- : (args.tool_names ?? agentDef.tools ?? undefined);
1803
+ const applyAgentDef = (agentDef) => {
1804
+ agentTask = args.task || agentDef.initialPrompt || `You are the ${agentDef.name} agent. Begin your work.`;
1805
+ agentSystemMessage = undefined;
1806
+ // Match durable spawning: no parent package tools when
1807
+ // the selected definition declares no extra tools.
1808
+ agentToolNames = agentDef.tools ?? [];
1754
1809
  agentIsSystem = agentDef.system ?? false;
1755
1810
  if (!agentTitleIsExplicit)
1756
1811
  agentTitle = agentDef.title;
1757
- agentId = agentDef.id ?? resolvedAgentName;
1812
+ agentId = agentDef.id ?? agentDef.name;
1758
1813
  agentSplash = agentDef.splash;
1759
1814
  agentSplashMobile = agentDef.splashMobile;
1760
1815
  bootstrapRequiredTool = agentDef.initialRequiredTool;
1761
- boundAgentName = agentDef.name;
1816
+ boundAgentName = !agentDef.packageId && agentDef.namespace ? `${agentDef.namespace}:${agentDef.name}` : agentDef.name;
1817
+ boundAgentPackageId = agentDef.packageId;
1762
1818
  promptLayeringKind = agentDef.promptLayerKind
1763
1819
  ?? (agentDef.system
1764
1820
  ? ((agentDef.namespace || "pilotswarm") === "pilotswarm"
@@ -1766,16 +1822,26 @@ artifactStore) {
1766
1822
  : "app-system-agent")
1767
1823
  : "app-agent");
1768
1824
  };
1825
+ let agentDef = null;
1769
1826
  if (resolvedAgentName) {
1770
- const agentDef = await resolveAgentConfigInline(resolvedAgentName);
1827
+ agentDef = await resolveAgentConfigInline(resolvedAgentName);
1771
1828
  if (!agentDef) {
1772
1829
  return `[SYSTEM: spawn_agent failed — agent "${resolvedAgentName}" not found. Use ps_list_agents to see available agents.]`;
1773
1830
  }
1831
+ }
1832
+ if (agentDef) {
1774
1833
  if (agentDef.system && agentDef.creatable === false) {
1775
1834
  return `[SYSTEM: spawn_agent failed — agent "${resolvedAgentName}" is a worker-managed system agent and cannot be spawned from a session. ` +
1776
1835
  `If it is missing, the workers likely need to be restarted.]`;
1777
1836
  }
1778
- applyAgentDef(agentDef, resolvedAgentName !== args.agent_name);
1837
+ if (args.tool_names !== undefined) {
1838
+ return `[SYSTEM: spawn_agent failed — tool_names cannot override a bound named-agent definition. Use a custom task without agent_name, or remove tool_names.]`;
1839
+ }
1840
+ if (args.system_message !== undefined) {
1841
+ return `[SYSTEM: spawn_agent failed — system_message cannot override a bound named-agent definition. Put the bounded assignment in task instead.]`;
1842
+ }
1843
+ // The named definition owns its startup requirement.
1844
+ applyAgentDef(agentDef);
1779
1845
  }
1780
1846
  // Spawned children inherit the parent lineage's EFFECTIVE
1781
1847
  // owner instead of any system flag: the nearest owned
@@ -1803,14 +1869,18 @@ artifactStore) {
1803
1869
  // still apply. The parent is responsible for closing
1804
1870
  // each instance with complete_agent / cancel_agent /
1805
1871
  // delete_agent when it no longer needs the child.
1806
- const { boundAgentName: _parentBoundAgentName, promptLayering: _parentPromptLayering, isCrawler: _parentIsCrawler, isHarvester: _parentIsHarvester, ...parentConfig } = input.config;
1872
+ const { boundAgentName: _parentBoundAgentName, boundAgentPackageId: _parentBoundAgentPackageId, boundAgentSource: _parentBoundAgentSource, namedAgentToolAdditions: _parentNamedAgentToolAdditions, childContract: _parentChildContract, detachedPackageToolPolicy: _parentDetachedPackageToolPolicy, promptLayering: _parentPromptLayering, agentIdentity: _parentAgentIdentity, isCrawler: _parentIsCrawler, isHarvester: _parentIsHarvester, ...parentConfig } = input.config;
1807
1873
  const childConfig = {
1808
1874
  ...parentConfig,
1875
+ ...(boundAgentName ? { systemMessage: undefined } : {}),
1809
1876
  ...(agentModel ? { model: agentModel } : {}),
1810
1877
  ...(agentReasoningEffort ? { reasoningEffort: agentReasoningEffort } : {}),
1811
1878
  ...(args.context_tier !== undefined ? { contextTier: args.context_tier } : {}),
1812
1879
  ...(agentSystemMessage ? { systemMessage: agentSystemMessage } : {}),
1813
1880
  ...(boundAgentName ? { boundAgentName } : {}),
1881
+ ...(boundAgentPackageId ? { boundAgentPackageId } : {}),
1882
+ ...(boundAgentName && !boundAgentPackageId ? { boundAgentSource: "deployment" } : {}),
1883
+ detachedPackageToolPolicy: boundAgentName || args.tool_names?.length ? "reject" : "drop",
1814
1884
  ...(promptLayeringKind ? { promptLayering: { kind: promptLayeringKind } } : {}),
1815
1885
  ...(agentToolNames ? { toolNames: agentToolNames } : {}),
1816
1886
  ...(args.contract ? { childContract: args.contract } : {}),
@@ -1850,6 +1920,9 @@ artifactStore) {
1850
1920
  ...childModelCreationOptions(childConfig),
1851
1921
  systemMessage: childConfig.systemMessage,
1852
1922
  boundAgentName: childConfig.boundAgentName,
1923
+ boundAgentPackageId: childConfig.boundAgentPackageId,
1924
+ boundAgentSource: childConfig.boundAgentSource,
1925
+ detachedPackageToolPolicy: childConfig.detachedPackageToolPolicy,
1853
1926
  promptLayering: childConfig.promptLayering,
1854
1927
  toolNames: childConfig.toolNames,
1855
1928
  waitThreshold: childConfig.waitThreshold,
@@ -2225,7 +2298,7 @@ artifactStore) {
2225
2298
  }
2226
2299
  const sdkClient = await getInlineClient();
2227
2300
  const cmdId = `done-inline-${Date.now()}`;
2228
- await sdkClient._getDuroxideClient().enqueueEvent(child.orchId, "messages", JSON.stringify({ type: "cmd", cmd: "done", id: cmdId, args: { reason: "Completed by parent" } }));
2301
+ await sdkClient._getDuroxideClient().enqueueEvent(child.orchId, "messages", JSON.stringify({ type: "cmd", cmd: "done", id: cmdId, args: { reason: "Completed by parent" }, requestedBy: input.sessionId }));
2229
2302
  return `[SYSTEM: Graceful completion requested for sub-agent ${child.orchId}. ` +
2230
2303
  `Use check_agents or wait_for_agents to observe final completion.]`;
2231
2304
  }
@@ -2256,7 +2329,7 @@ artifactStore) {
2256
2329
  }
2257
2330
  const sdkClient = await getInlineClient();
2258
2331
  const cmdId = `cancel-inline-${Date.now()}`;
2259
- await sdkClient._getDuroxideClient().enqueueEvent(child.orchId, "messages", JSON.stringify({ type: "cmd", cmd: "cancel", id: cmdId, args: { reason: args.reason ?? "Cancelled by parent" } }));
2332
+ await sdkClient._getDuroxideClient().enqueueEvent(child.orchId, "messages", JSON.stringify({ type: "cmd", cmd: "cancel", id: cmdId, args: { reason: args.reason ?? "Cancelled by parent" }, requestedBy: input.sessionId }));
2260
2333
  return `[SYSTEM: Graceful cancellation requested for sub-agent ${child.orchId}. ` +
2261
2334
  `Use check_agents or wait_for_agents to observe final termination.${args.reason ? ` Reason: ${args.reason}` : ""}]`;
2262
2335
  }
@@ -2273,7 +2346,7 @@ artifactStore) {
2273
2346
  return `[SYSTEM: Sub-agent ${child.orchId} has been deleted.${args.reason ? ` Reason: ${args.reason}` : ""}]`;
2274
2347
  }
2275
2348
  const cmdId = `delete-inline-${Date.now()}`;
2276
- await sdkClient._getDuroxideClient().enqueueEvent(child.orchId, "messages", JSON.stringify({ type: "cmd", cmd: "delete", id: cmdId, args: { reason: args.reason ?? "Deleted by parent" } }));
2349
+ await sdkClient._getDuroxideClient().enqueueEvent(child.orchId, "messages", JSON.stringify({ type: "cmd", cmd: "delete", id: cmdId, args: { reason: args.reason ?? "Deleted by parent" }, requestedBy: input.sessionId }));
2277
2350
  return `[SYSTEM: Graceful deletion requested for sub-agent ${child.orchId}. ` +
2278
2351
  `It will cancel its descendants first and then delete itself.${args.reason ? ` Reason: ${args.reason}` : ""}]`;
2279
2352
  }
@@ -3371,6 +3444,12 @@ artifactStore) {
3371
3444
  return finalTurnResult;
3372
3445
  }
3373
3446
  catch (err) {
3447
+ if (err instanceof PackageToolBindingError || err instanceof BoundAgentPackageUnavailableError) {
3448
+ const message = err.message || String(err);
3449
+ activityCtx.traceInfo(`[runTurn] deterministic package-tool binding failure: ${message}`);
3450
+ finalTurnResult = { type: "error", message, retryable: false };
3451
+ return finalTurnResult;
3452
+ }
3374
3453
  if (isSessionLockAcquireTimeoutError(err)) {
3375
3454
  const message = err.message || String(err);
3376
3455
  activityCtx.traceInfo(`[runTurn] ${message}`);
@@ -3427,11 +3506,10 @@ artifactStore) {
3427
3506
  }
3428
3507
  }
3429
3508
  };
3430
- runtime.registerActivity("runTurn", runTurnHandler);
3431
- // Session regeneration: the epoch-start turn dispatches under a NEW
3432
- // activity name so pre-1.0.67 workers — which would silently resume the
3433
- // dead transcript — structurally cannot receive it (deployment gate §4).
3434
- runtime.registerActivity("runTurn2", runTurnHandler);
3509
+ registerHandoffActivity(runtime, "runTurn", runTurnHandler);
3510
+ // Keep the historical epoch activity for replay. 1.0.75 uses a renamed,
3511
+ // capability-tagged alias; the tag filter performs actual worker routing.
3512
+ registerHandoffActivity(runtime, "runTurn2", runTurnHandler);
3435
3513
  // ── abortTurn ────────────────────────────────────────────
3436
3514
  // Stop-turn fast-path interrupt. Routed on the session affinity key so it
3437
3515
  // lands on the worker owning the warm ManagedSession and runs CONCURRENTLY
@@ -3819,11 +3897,16 @@ artifactStore) {
3819
3897
  // resolveAgentDefinitionForCaller — shared with the control bridge so
3820
3898
  // spawn_agent/create_agent_session and this activity can never disagree
3821
3899
  // about which agent a name means.
3822
- runtime.registerActivity("resolveAgentConfig", async (_activityCtx, input) => {
3900
+ registerHandoffActivity(runtime, "resolveAgentConfig", async (_activityCtx, input) => {
3901
+ // Top-level verification sessions already selected a package/deployment
3902
+ // copy at creation. Keep that selection through the startup resolver.
3903
+ // An unavailable pin yields no definition; it never falls back by name.
3904
+ const pinnedCandidates = (agents) => agents?.filter(agent => (!input.binding?.packageId || agent.packageId === input.binding.packageId)
3905
+ && (input.binding?.source !== "deployment" || (!agent.packageId && agent.packageScope == null)));
3823
3906
  return resolveAgentDefinitionForCaller({
3824
3907
  agentName: input.agentName,
3825
- userAgents,
3826
- systemAgents,
3908
+ userAgents: pinnedCandidates(userAgents),
3909
+ systemAgents: pinnedCandidates(systemAgents),
3827
3910
  // PACKAGE PRIVACY, enforced at RESOLUTION rather than at creation.
3828
3911
  //
3829
3912
  // Workers install every enabled package, including other users'
@@ -3840,15 +3923,15 @@ artifactStore) {
3840
3923
  //
3841
3924
  // Deployment-owned plugin agents carry no package owner and stay
3842
3925
  // public, exactly as before.
3843
- getCallerOwnerKey: async () => {
3844
- const row = input.callerSessionId
3845
- ? await catalog?.getSession(input.callerSessionId)
3846
- : null;
3847
- const owner = row?.owner;
3848
- return owner?.provider && owner?.subject
3849
- ? `${owner.provider}\u0001${owner.subject}`
3850
- : null;
3851
- },
3926
+ getCallerOwnerKey: () => resolveCallerAgentOwnerKey(catalog, input.callerSessionId),
3927
+ });
3928
+ });
3929
+ registerHandoffActivity(runtime, "resolveAgentForRequiredTool", async (_activityCtx, input) => {
3930
+ return resolveAgentDefinitionForRequiredToolForCaller({
3931
+ requiredTool: input.requiredTool,
3932
+ userAgents,
3933
+ systemAgents,
3934
+ getCallerOwnerKey: () => resolveCallerAgentOwnerKey(catalog, input.callerSessionId),
3852
3935
  });
3853
3936
  });
3854
3937
  // ── spawnChildSession ─────────────────────────────────────
@@ -3856,7 +3939,7 @@ artifactStore) {
3856
3939
  // System child agents with a stable agentId use a deterministic UUID.
3857
3940
  // Other child sessions use a random UUID.
3858
3941
  // Goes through the full SDK path: CMS registration + orchestration startup.
3859
- runtime.registerActivity("spawnChildSession", async (activityCtx, input) => {
3942
+ registerHandoffActivity(runtime, "spawnChildSession", async (activityCtx, input) => {
3860
3943
  const startedAt = Date.now();
3861
3944
  const trace = (message) => {
3862
3945
  activityCtx.traceInfo(`[spawnChildSession] +${Date.now() - startedAt}ms ${message}`);
@@ -3927,6 +4010,9 @@ artifactStore) {
3927
4010
  ...childModelCreationOptions(input.config),
3928
4011
  systemMessage: input.config.systemMessage,
3929
4012
  boundAgentName: input.config.boundAgentName,
4013
+ boundAgentPackageId: input.config.boundAgentPackageId,
4014
+ boundAgentSource: input.config.boundAgentSource,
4015
+ detachedPackageToolPolicy: input.config.detachedPackageToolPolicy,
3930
4016
  promptLayering: input.config.promptLayering,
3931
4017
  toolNames: input.config.toolNames,
3932
4018
  waitThreshold: input.config.waitThreshold,
@@ -3950,12 +4036,23 @@ artifactStore) {
3950
4036
  meta.agentId = input.agentId;
3951
4037
  if (input.splash)
3952
4038
  meta.splash = input.splash;
3953
- // Frozen orchestration versions don't thread splashMobile through
3954
- // the spawn input, so resolve it from the loaded agent definition.
4039
+ // Resolve mobile metadata through the same package pin and caller
4040
+ // visibility rules as the prompt/tools, including for older inputs
4041
+ // that carry only an agent id. A bare first match can expose another
4042
+ // user's copy when private and shared packages have the same name.
3955
4043
  if (input.agentId) {
3956
- const normalizeId = (value) => (value || "").toLowerCase().replace(/[^a-z0-9]+/g, "");
3957
- const lookup = normalizeId(input.agentId);
3958
- const agentDef = [...(userAgents ?? []), ...(systemAgents ?? [])].find((a) => [a.id, a.name].map(normalizeId).filter(Boolean).includes(lookup));
4044
+ const packageId = input.config.boundAgentPackageId;
4045
+ const fromBoundCopy = (agents) => packageId
4046
+ ? agents?.filter(a => a.packageId === packageId)
4047
+ : input.config.boundAgentSource === "deployment"
4048
+ ? agents?.filter(a => !a.packageId && a.packageScope == null)
4049
+ : agents;
4050
+ const agentDef = await resolveAgentDefinitionForCaller({
4051
+ agentName: input.config.boundAgentName ?? input.agentId,
4052
+ userAgents: fromBoundCopy(userAgents),
4053
+ systemAgents: fromBoundCopy(systemAgents),
4054
+ getCallerOwnerKey: () => resolveCallerAgentOwnerKey(catalog, input.parentSessionId),
4055
+ });
3959
4056
  if (agentDef?.splashMobile)
3960
4057
  meta.splashMobile = agentDef.splashMobile;
3961
4058
  }
@@ -4038,27 +4135,29 @@ artifactStore) {
4038
4135
  });
4039
4136
  // ── getSessionStatus ────────────────────────────────────
4040
4137
  // Gets the status of a session via the PilotSwarmClient SDK.
4041
- runtime.registerActivity("getSessionStatus", async (activityCtx, input) => {
4138
+ const getSessionStatus = async (activityCtx, input, includeResultSource = false) => {
4042
4139
  activityCtx.traceInfo(`[getSessionStatus] session=${input.sessionId}`);
4043
4140
  if (!storeUrl)
4044
4141
  throw new Error("No storeUrl — cannot create PilotSwarmClient");
4045
4142
  const sdkClient = new PilotSwarmClient(internalClientConfig());
4046
4143
  try {
4047
4144
  await sdkClient.start();
4048
- const info = await sdkClient._getSessionInfo(input.sessionId);
4145
+ const info = await sdkClient._getSessionInfo(input.sessionId, includeResultSource ? { includeResultSource: true } : undefined);
4049
4146
  return JSON.stringify({
4050
4147
  sessionId: info.sessionId,
4051
4148
  status: info.status,
4052
4149
  title: info.title,
4053
4150
  iterations: info.iterations,
4054
4151
  result: info.result,
4152
+ ...(includeResultSource && info.resultSource ? { resultSource: info.resultSource } : {}),
4055
4153
  error: info.error,
4056
4154
  });
4057
4155
  }
4058
4156
  finally {
4059
4157
  await sdkClient.stop();
4060
4158
  }
4061
- });
4159
+ };
4160
+ registerHandoffActivity(runtime, "getSessionStatus", getSessionStatus, (ctx, input) => getSessionStatus(ctx, input, true));
4062
4161
  // ── getOrchestrationStats ───────────────────────────────
4063
4162
  // Gets duroxide orchestration runtime stats for a session.
4064
4163
  runtime.registerActivity("getOrchestrationStats", async (activityCtx, input) => {
@@ -4101,7 +4200,7 @@ artifactStore) {
4101
4200
  });
4102
4201
  // ── listChildSessions ───────────────────────────────────
4103
4202
  // Lists direct child sessions of a parent with merged live status.
4104
- runtime.registerActivity("listChildSessions", async (activityCtx, input) => {
4203
+ const listChildSessions = async (activityCtx, input, includeResultSource = false) => {
4105
4204
  activityCtx.traceInfo(`[listChildSessions] parent=${input.parentSessionId}`);
4106
4205
  if (!storeUrl)
4107
4206
  throw new Error("No storeUrl — cannot create PilotSwarmClient");
@@ -4111,7 +4210,7 @@ artifactStore) {
4111
4210
  const sessions = await sdkClient.listSessions();
4112
4211
  const directChildren = sessions.filter(s => s.parentSessionId === input.parentSessionId);
4113
4212
  const enriched = await Promise.all(directChildren.map(async (child) => {
4114
- const info = await sdkClient._getSessionInfo(child.sessionId);
4213
+ const info = await sdkClient._getSessionInfo(child.sessionId, includeResultSource ? { includeResultSource: true } : undefined);
4115
4214
  const outcome = catalog ? await catalog.getChildOutcome(child.sessionId).catch(() => null) : null;
4116
4215
  const outcomeResult = normalizeJsonObject(outcome?.resultJson?.current);
4117
4216
  const contractCurrent = normalizeJsonObject(outcome?.contractJson?.current);
@@ -4125,6 +4224,10 @@ artifactStore) {
4125
4224
  isSystem: child.isSystem ?? info.isSystem ?? false,
4126
4225
  agentId: child.agentId ?? info.agentId,
4127
4226
  result: outcome?.summary ?? (typeof outcomeResult?.summary === "string" ? outcomeResult.summary : info.result),
4227
+ ...(includeResultSource ? {
4228
+ resultSource: outcome?.summary != null || typeof outcomeResult?.summary === "string"
4229
+ ? "child_outcome" : info.resultSource,
4230
+ } : {}),
4128
4231
  contract: contractCurrent ?? undefined,
4129
4232
  contractStatus: outcome?.contractJson ? "contracted" : undefined,
4130
4233
  verdict: outcome?.verdict ?? undefined,
@@ -4137,7 +4240,8 @@ artifactStore) {
4137
4240
  finally {
4138
4241
  await sdkClient.stop();
4139
4242
  }
4140
- });
4243
+ };
4244
+ registerHandoffActivity(runtime, "listChildSessions", listChildSessions, (ctx, input) => listChildSessions(ctx, input, true));
4141
4245
  // ── notifyParent ────────────────────────────────────────
4142
4246
  // Sends a child_updates event to the parent orchestration so it can
4143
4247
  // wake up from durable sleep and process the child's result.