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
@@ -38,6 +38,7 @@ const DEHYDRATE_STORE_MAX_RETRIES = 1;
38
38
  const DEHYDRATE_STORE_RETRY_BASE_DELAY_MS = 0;
39
39
  const SESSION_LOCK_BACKOFF_MS = [5_000, 10_000, 20_000];
40
40
  const SESSION_LOCK_MAX_WAIT_MS = 120_000;
41
+ const COPILOT_CLIENT_SHUTDOWN_TIMEOUT_MS = 10_000;
41
42
  export const SESSION_LOCK_ACQUIRE_TIMEOUT_CODE = "PILOTSWARM_SESSION_LOCK_ACQUIRE_TIMEOUT";
42
43
  export class SessionLockAcquireTimeoutError extends Error {
43
44
  code = SESSION_LOCK_ACQUIRE_TIMEOUT_CODE;
@@ -79,6 +80,24 @@ function isMissingDehydrateSnapshotError(error) {
79
80
  const message = error instanceof Error ? error.message : String(error ?? "");
80
81
  return /Session state directory not ready during dehydrate/i.test(message);
81
82
  }
83
+ export class PackageToolBindingError extends Error {
84
+ toolName;
85
+ code = "PACKAGE_TOOL_REQUIRES_BOUND_AGENT";
86
+ constructor(toolName) {
87
+ super(`Package tool "${toolName}" requires its owning named-agent definition. Use ps_list_agents to find its named agent, then call spawn_agent with the exact agent_name and your assignment in task.`);
88
+ this.toolName = toolName;
89
+ this.name = "PackageToolBindingError";
90
+ }
91
+ }
92
+ export class BoundAgentPackageUnavailableError extends Error {
93
+ packageId;
94
+ code = "BOUND_AGENT_PACKAGE_UNAVAILABLE";
95
+ constructor(packageId, message = `The bound agent package "${packageId}" is no longer available to this session.`) {
96
+ super(message);
97
+ this.packageId = packageId;
98
+ this.name = "BoundAgentPackageUnavailableError";
99
+ }
100
+ }
82
101
  /** Key under which a package copy's per-agent config (MCP) is registered. */
83
102
  export function packageAgentKey(packageId, agentName) {
84
103
  return `pkg\u0001${packageId}\u0001${agentName}`;
@@ -123,7 +142,58 @@ export function pickAgentCopyForOwner(entry, ownerKey) {
123
142
  ?? copies.find((copy) => copy.packageScope === "shared")
124
143
  ?? undefined;
125
144
  }
126
- function buildEffectivePromptLayers(workerDefaults, config, sessionOwnerKey = null) {
145
+ /** Resolve an already-authorized exact package copy while rechecking owner visibility. */
146
+ export function pickAgentCopyByPackageIdForOwner(entry, packageId, ownerKey) {
147
+ if (!entry || !packageId)
148
+ return undefined;
149
+ const copies = entry.copies?.length ? entry.copies : [entry];
150
+ const copy = copies.find((candidate) => candidate.packageId === packageId);
151
+ if (!copy)
152
+ return undefined;
153
+ if (copy.packageScope !== "user")
154
+ return copy;
155
+ return agentOwnerKey(copy.packageOwner) === ownerKey && ownerKey !== null
156
+ ? copy
157
+ : undefined;
158
+ }
159
+ /** Resolve every part of a bound agent using the same authorized package copy. */
160
+ function resolveBoundAgentCopy(workerDefaults, config, sessionOwnerKey) {
161
+ const entry = config.boundAgentName
162
+ ? workerDefaults.agentPromptLookup?.[config.boundAgentName]
163
+ : undefined;
164
+ if (config.boundAgentSource === "deployment") {
165
+ const copies = entry?.copies?.length ? entry.copies : entry ? [entry] : [];
166
+ const deployment = copies.find(copy => !copy.packageId && copy.packageScope == null);
167
+ if (config.boundAgentPackageId || !deployment) {
168
+ throw new BoundAgentPackageUnavailableError(`deployment:${config.boundAgentName ?? ""}`, `The bound deployment agent "${config.boundAgentName ?? ""}" is no longer available or has a conflicting package binding.`);
169
+ }
170
+ return deployment;
171
+ }
172
+ const copy = config.boundAgentPackageId
173
+ ? pickAgentCopyByPackageIdForOwner(entry, config.boundAgentPackageId, sessionOwnerKey)
174
+ : pickAgentCopyForOwner(entry, sessionOwnerKey);
175
+ if (config.boundAgentPackageId && !copy) {
176
+ throw new BoundAgentPackageUnavailableError(config.boundAgentPackageId);
177
+ }
178
+ return copy;
179
+ }
180
+ /** Match the SDK wire declaration, including schemas supplied through Zod. */
181
+ function toolDeclarationForFingerprint(tool) {
182
+ const parameters = tool.parameters;
183
+ return {
184
+ name: tool.name,
185
+ description: tool.description,
186
+ parameters: parameters != null && typeof parameters === "object" && typeof parameters.toJSONSchema === "function"
187
+ ? parameters.toJSONSchema()
188
+ : parameters,
189
+ overridesBuiltInTool: tool.overridesBuiltInTool,
190
+ skipPermission: tool.skipPermission,
191
+ defer: tool.defer ?? "never",
192
+ metadata: tool.metadata,
193
+ isTerminal: tool.isTerminal,
194
+ };
195
+ }
196
+ function buildEffectivePromptLayers(workerDefaults, config, sessionOwnerKey = null, boundAgentCopy = resolveBoundAgentCopy(workerDefaults, config, sessionOwnerKey) ?? null) {
127
197
  const boundAgentName = config.boundAgentName;
128
198
  const layerKind = config.promptLayering?.kind ?? (boundAgentName ? "app-agent" : undefined);
129
199
  const isPilotSwarmSystemAgent = layerKind === "pilotswarm-system-agent";
@@ -133,9 +203,8 @@ function buildEffectivePromptLayers(workerDefaults, config, sessionOwnerKey = nu
133
203
  if (!isPilotSwarmSystemAgent && workerDefaults.appDefaultDescriptor)
134
204
  layers.push(workerDefaults.appDefaultDescriptor);
135
205
  if (boundAgentName) {
136
- const copy = pickAgentCopyForOwner(workerDefaults.agentPromptLookup?.[boundAgentName], sessionOwnerKey);
137
- if (copy?.descriptor)
138
- layers.push(copy.descriptor);
206
+ if (boundAgentCopy?.descriptor)
207
+ layers.push(boundAgentCopy.descriptor);
139
208
  }
140
209
  return layers;
141
210
  }
@@ -309,10 +378,18 @@ export class SessionManager {
309
378
  sessionStore = null;
310
379
  /** In-memory configs with non-serializable fields (tools, hooks). */
311
380
  sessionConfigs = new Map();
381
+ /** Only application-supplied tools; resolved registry/platform handlers are never cached as inputs. */
382
+ sessionApplicationTools = new Map();
383
+ /** One authorized agent snapshot per turn, also used by dynamic prompt callbacks. */
384
+ sessionAgentCopies = new Map();
385
+ /** CLI declarations and MCP grants require a new SDK handle when they change. */
386
+ sessionBindingFingerprints = new Map();
312
387
  /** Worker-level tool registry — shared reference from PilotSwarmWorker. */
313
388
  toolRegistry = new Map();
314
389
  /** Per-package tool maps, so a session prefers ITS package's handler on a name collision. */
315
390
  packageToolRegistry = null;
391
+ /** Package-owned names cannot be attached without the matching agent package binding. */
392
+ packageToolNames = new Set();
316
393
  /** Names registered by deployment code — a package tool must never shadow these. */
317
394
  staticToolNames = null;
318
395
  /** Worker-level defaults for building blocks. */
@@ -368,6 +445,10 @@ export class SessionManager {
368
445
  /** Store full config (with tools/hooks) for a session. Called by PilotSwarmClient. */
369
446
  setConfig(sessionId, config) {
370
447
  this.sessionConfigs.set(sessionId, config);
448
+ if (config.tools?.length)
449
+ this.sessionApplicationTools.set(sessionId, [...config.tools]);
450
+ else
451
+ this.sessionApplicationTools.delete(sessionId);
371
452
  }
372
453
  async _allowedModelProviderIds(sessionId) {
373
454
  const registry = this.workerDefaults.modelProviders;
@@ -601,6 +682,12 @@ export class SessionManager {
601
682
  this.toolRegistry = registry;
602
683
  this.packageToolRegistry = opts?.byPackage ?? null;
603
684
  this.staticToolNames = opts?.staticNames ?? null;
685
+ // Remember ownership after removal as well: a stale application-supplied
686
+ // handler must not become an ordinary tool when its package disappears.
687
+ for (const tools of this.packageToolRegistry?.values() ?? []) {
688
+ for (const name of tools.keys())
689
+ this.packageToolNames.add(name);
690
+ }
604
691
  }
605
692
  /** Set the cluster facts store for always-on facts tools. */
606
693
  setFactStore(factStore) {
@@ -845,7 +932,7 @@ export class SessionManager {
845
932
  await existing.destroy();
846
933
  }
847
934
  catch { }
848
- this.sessions.delete(sessionId);
935
+ this._forgetWarmSession(sessionId);
849
936
  }
850
937
  try {
851
938
  const client = await this._ensureClientForSession(sessionId);
@@ -881,7 +968,7 @@ export class SessionManager {
881
968
  await existing.destroy();
882
969
  }
883
970
  catch { }
884
- this.sessions.delete(sessionId);
971
+ this._forgetWarmSession(sessionId);
885
972
  }
886
973
  try {
887
974
  const client = await this._ensureClientForSession(sessionId);
@@ -998,7 +1085,7 @@ export class SessionManager {
998
1085
  await existing.destroy();
999
1086
  }
1000
1087
  catch { }
1001
- this.sessions.delete(sessionId);
1088
+ this._forgetWarmSession(sessionId);
1002
1089
  }
1003
1090
  if (!dirExists) {
1004
1091
  this.sessionLastTouchedAt.delete(sessionId);
@@ -1143,7 +1230,7 @@ export class SessionManager {
1143
1230
  ...(this.workerDefaults.appDefaultToolNames ?? []),
1144
1231
  ...(serializableConfig.toolNames ?? []),
1145
1232
  ]));
1146
- const effectiveSerializableConfig = inheritedToolNames.length > 0
1233
+ let effectiveSerializableConfig = inheritedToolNames.length > 0
1147
1234
  ? { ...serializableConfig, toolNames: inheritedToolNames }
1148
1235
  : serializableConfig;
1149
1236
  // Which copy of the bound agent does this session get? Owner-aware:
@@ -1151,20 +1238,69 @@ export class SessionManager {
1151
1238
  // one, the same rule the registry resolver applies at create time.
1152
1239
  // Resolved once here; prompt, descriptor, MCP, and tool-handler picks
1153
1240
  // below all follow the same copy so a session can never mix copies.
1154
- const boundAgentEntry = effectiveSerializableConfig.boundAgentName
1155
- ? this.workerDefaults.agentPromptLookup?.[effectiveSerializableConfig.boundAgentName]
1156
- : undefined;
1157
- // A PACKAGE agent (as opposed to a deployment/inline agent) carries a
1158
- // packageId on its entry or on any of its copies. This is load-bearing
1159
- // for MCP resolution: a package agent must never fall back to the
1160
- // bare-name MCP key, which another copy of a shadowed name also wrote.
1161
1241
  const sessionOwnerKey = effectiveSerializableConfig.boundAgentName
1162
1242
  ? await this._sessionAgentOwnerKey(sessionId)
1163
1243
  : null;
1164
- const boundAgentCopy = pickAgentCopyForOwner(boundAgentEntry, sessionOwnerKey);
1165
- // Resolve tools: merge per-session (setConfig) + registry (toolNames)
1166
1244
  const storedConfig = this.sessionConfigs.get(sessionId);
1167
- const resolvedTools = this._resolveTools(storedConfig, effectiveSerializableConfig, boundAgentCopy?.packageId);
1245
+ let boundAgentCopy;
1246
+ let resolvedTools;
1247
+ try {
1248
+ boundAgentCopy = resolveBoundAgentCopy(this.workerDefaults, effectiveSerializableConfig, sessionOwnerKey);
1249
+ // Snapshot the selected prompt and descriptor. A registry refresh in the
1250
+ // middle of a turn must not change instructions while handlers still
1251
+ // belong to the previously selected version.
1252
+ if (boundAgentCopy)
1253
+ boundAgentCopy = {
1254
+ ...boundAgentCopy,
1255
+ ...(boundAgentCopy.toolNames ? { toolNames: [...boundAgentCopy.toolNames] } : {}),
1256
+ ...(boundAgentCopy.descriptor ? { descriptor: { ...boundAgentCopy.descriptor } } : {}),
1257
+ };
1258
+ if (boundAgentCopy?.toolNames && effectiveSerializableConfig.boundAgentName
1259
+ && (effectiveSerializableConfig.boundAgentPackageId || effectiveSerializableConfig.detachedPackageToolPolicy
1260
+ || effectiveSerializableConfig.namedAgentToolAdditions !== undefined)) {
1261
+ // Named definitions refresh as a unit. Roots may add ordinary
1262
+ // application tools; protected children receive only defaults and
1263
+ // their definition. Legacy package roots have no provenance, so
1264
+ // retain their ordinary names but never stale package capabilities.
1265
+ const additions = effectiveSerializableConfig.detachedPackageToolPolicy
1266
+ ? []
1267
+ : (effectiveSerializableConfig.namedAgentToolAdditions ?? effectiveSerializableConfig.toolNames ?? [])
1268
+ .filter(name => this.staticToolNames?.has(name) || !this.packageToolNames.has(name));
1269
+ effectiveSerializableConfig = {
1270
+ ...effectiveSerializableConfig,
1271
+ toolNames: [...new Set([
1272
+ ...(this.workerDefaults.frameworkBaseToolNames ?? []),
1273
+ ...(this.workerDefaults.appDefaultToolNames ?? []),
1274
+ ...boundAgentCopy.toolNames,
1275
+ ...additions,
1276
+ ])],
1277
+ };
1278
+ }
1279
+ if (effectiveSerializableConfig.boundAgentSource === "deployment") {
1280
+ // A deployment definition is bound, but owns no registry package.
1281
+ // It cannot borrow a shared/private package's exported handlers.
1282
+ effectiveSerializableConfig = { ...effectiveSerializableConfig, detachedPackageToolPolicy: "reject" };
1283
+ }
1284
+ resolvedTools = this._resolveTools({ tools: this.sessionApplicationTools.get(sessionId) }, effectiveSerializableConfig, boundAgentCopy?.packageId);
1285
+ }
1286
+ catch (error) {
1287
+ // Failed authorization/removal must not leave a reusable handle with
1288
+ // the previous package's handlers or MCP grants.
1289
+ await this.dropWarmSession(sessionId);
1290
+ this.sessionAgentCopies.delete(sessionId);
1291
+ throw error;
1292
+ }
1293
+ // Capture MCP grants with the same registry snapshot, before asynchronous
1294
+ // provider/catalog work below can allow a package reload to intervene.
1295
+ const boundAgentMcpServers = !effectiveSerializableConfig.boundAgentName || !boundAgentCopy
1296
+ ? undefined
1297
+ : boundAgentCopy.packageId
1298
+ ? this.workerDefaults.agentMcpServers?.[packageAgentKey(boundAgentCopy.packageId, effectiveSerializableConfig.boundAgentName)]
1299
+ : this.workerDefaults.agentMcpServers?.[effectiveSerializableConfig.boundAgentName];
1300
+ const effectiveMcpServers = {
1301
+ ...(this.workerDefaults.baseMcpServers ?? {}),
1302
+ ...(boundAgentMcpServers ?? {}),
1303
+ };
1168
1304
  const config = {
1169
1305
  ...storedConfig,
1170
1306
  ...effectiveSerializableConfig,
@@ -1289,7 +1425,7 @@ export class SessionManager {
1289
1425
  await existingWarm.destroy();
1290
1426
  }
1291
1427
  catch { }
1292
- this.sessions.delete(sessionId);
1428
+ this._forgetWarmSession(sessionId);
1293
1429
  }
1294
1430
  }
1295
1431
  const client = await this.ensureClient(userGithubToken, byokOpenAi);
@@ -1509,35 +1645,19 @@ export class SessionManager {
1509
1645
  ];
1510
1646
  config.tools = persistentSessionTools;
1511
1647
  // Build system message: worker base + client override
1512
- const systemMessage = this._buildSystemMessage(sessionId, config, sessionOwnerKey);
1513
- // Per-agent MCP (capability-profiles Phase 1): a session gets the
1514
- // base map (base-agent opt-ins + direct worker-config servers) plus
1515
- // its bound agent's resolved server map — resolved worker-side at the
1516
- // same chokepoint as the agent prompt. The deployment catalog is
1517
- // never applied wholesale.
1518
- //
1519
- // Read the MCP map of the copy THIS session actually resolved to.
1520
- // The worker registers a package agent's MCP under a package-qualified
1521
- // key only, and reserves the bare name for deployment/inline agents —
1522
- // so the resolved copy's own packageId is the discriminator:
1523
- // • package copy resolved → its qualified key (never the bare name,
1524
- // which another copy of a shadowed name could have written);
1525
- // • deployment/inline copy → the bare name (its own MCP);
1526
- // • no copy resolved (a foreign-private-only name) → no grants.
1527
- // Keying off `boundAgentCopy.packageId` rather than "is any copy a
1528
- // package" is load-bearing: when a deployment agent and a package
1529
- // share a name and this session resolved the DEPLOYMENT copy, it must
1530
- // still get the deployment agent's bare-key MCP, not an empty
1531
- // qualified lookup.
1532
- const boundAgentMcpServers = !effectiveSerializableConfig.boundAgentName || !boundAgentCopy
1533
- ? undefined
1534
- : boundAgentCopy.packageId
1535
- ? this.workerDefaults.agentMcpServers?.[packageAgentKey(boundAgentCopy.packageId, effectiveSerializableConfig.boundAgentName)]
1536
- : this.workerDefaults.agentMcpServers?.[effectiveSerializableConfig.boundAgentName];
1537
- const effectiveMcpServers = {
1538
- ...(this.workerDefaults.baseMcpServers ?? {}),
1539
- ...(boundAgentMcpServers ?? {}),
1540
- };
1648
+ const systemMessage = this._buildSystemMessage(sessionId, config, sessionOwnerKey, boundAgentCopy ?? null);
1649
+ // Handler changes use updateConfig; declaration, MCP and authored prompt
1650
+ // changes need a fresh CLI handle at this turn boundary. Do not include
1651
+ // per-turn runtime context or newly allocated handler function identity.
1652
+ const bindingFingerprint = createHash("sha256").update(JSON.stringify({
1653
+ boundAgentName: config.boundAgentName,
1654
+ boundAgentSource: config.boundAgentSource,
1655
+ boundAgentCopy,
1656
+ mcpServers: effectiveMcpServers,
1657
+ tools: allTools.map(toolDeclarationForFingerprint),
1658
+ })).digest("hex");
1659
+ const bindingChanged = this.sessionBindingFingerprints.has(sessionId)
1660
+ && this.sessionBindingFingerprints.get(sessionId) !== bindingFingerprint;
1541
1661
  const sessionConfig = {
1542
1662
  sessionId,
1543
1663
  // Sole chokepoint where tool DECLARATIONS reach the CLI (create and
@@ -1621,15 +1741,16 @@ export class SessionManager {
1621
1741
  console.warn(`[SessionManager] epoch-start for ${sessionId} (epoch ${transcriptEpoch}); ` +
1622
1742
  `discarding the warm Copilot session of the previous epoch.`);
1623
1743
  await existing.destroy();
1624
- this.sessions.delete(sessionId);
1744
+ this._forgetWarmSession(sessionId);
1625
1745
  }
1626
- else if (existing.requiresModelRebind(config)) {
1627
- console.warn(`[SessionManager] model config changed for ${sessionId}; ` +
1628
- `disconnecting warm Copilot session so it can resume with the new model config.`);
1746
+ else if (bindingChanged || existing.requiresModelRebind(config)) {
1747
+ console.warn(`[SessionManager] model or agent configuration changed for ${sessionId}; ` +
1748
+ `disconnecting warm Copilot session so it can resume with the updated configuration.`);
1629
1749
  await existing.destroy();
1630
- this.sessions.delete(sessionId);
1750
+ this._forgetWarmSession(sessionId);
1631
1751
  }
1632
1752
  else {
1753
+ this.sessionAgentCopies.set(sessionId, boundAgentCopy);
1633
1754
  existing.updateConfig(config);
1634
1755
  return existing;
1635
1756
  }
@@ -1737,7 +1858,9 @@ export class SessionManager {
1737
1858
  agentId: effectiveSerializableConfig.agentIdentity ?? null,
1738
1859
  }));
1739
1860
  this.sessions.set(sessionId, managed);
1740
- const promptLayers = buildEffectivePromptLayers(this.workerDefaults, config);
1861
+ this.sessionAgentCopies.set(sessionId, boundAgentCopy);
1862
+ this.sessionBindingFingerprints.set(sessionId, bindingFingerprint);
1863
+ const promptLayers = buildEffectivePromptLayers(this.workerDefaults, config, sessionOwnerKey, boundAgentCopy ?? null);
1741
1864
  if (promptLayers.length > 0 && this.sessionCatalog) {
1742
1865
  void this.sessionCatalog.recordEvents(sessionId, [{
1743
1866
  eventType: "session.prompt_layers",
@@ -1754,6 +1877,12 @@ export class SessionManager {
1754
1877
  getSessionStateDir() {
1755
1878
  return this.sessionStateDir;
1756
1879
  }
1880
+ /** Release snapshots with their SDK handle; application tools survive ordinary hydration. */
1881
+ _forgetWarmSession(sessionId) {
1882
+ this.sessions.delete(sessionId);
1883
+ this.sessionAgentCopies.delete(sessionId);
1884
+ this.sessionBindingFingerprints.delete(sessionId);
1885
+ }
1757
1886
  /**
1758
1887
  * Destroy the in-memory ManagedSession only — disk state untouched.
1759
1888
  * Used by the lifecycle preamble before overwriting local files with a
@@ -1762,13 +1891,13 @@ export class SessionManager {
1762
1891
  */
1763
1892
  async dropWarmSession(sessionId) {
1764
1893
  const existing = this.sessions.get(sessionId);
1765
- if (!existing)
1766
- return;
1767
- try {
1768
- await existing.destroy();
1894
+ if (existing) {
1895
+ try {
1896
+ await existing.destroy();
1897
+ }
1898
+ catch { }
1769
1899
  }
1770
- catch { }
1771
- this.sessions.delete(sessionId);
1900
+ this._forgetWarmSession(sessionId);
1772
1901
  }
1773
1902
  /**
1774
1903
  * Dehydrate a session: snapshot to the session store, release in-memory state.
@@ -1824,13 +1953,13 @@ export class SessionManager {
1824
1953
  try {
1825
1954
  emitSessionManagerTrace(sessionId, `destroy attempt ${attempt}/${DESTROY_MAX_RETRIES}`, { trace });
1826
1955
  await session.destroy();
1827
- this.sessions.delete(sessionId);
1956
+ this._forgetWarmSession(sessionId);
1828
1957
  emitSessionManagerTrace(sessionId, `destroy complete on attempt ${attempt}/${DESTROY_MAX_RETRIES}`, { trace });
1829
1958
  break; // Success
1830
1959
  }
1831
1960
  catch (err) {
1832
1961
  lastDestroyError = normalizeError(err);
1833
- this.sessions.delete(sessionId); // Remove broken session from map
1962
+ this._forgetWarmSession(sessionId); // Remove broken session from map
1834
1963
  emitSessionManagerTrace(sessionId, `destroy failed on attempt ${attempt}/${DESTROY_MAX_RETRIES} error=${lastDestroyError.message}`, { trace, level: "warn" });
1835
1964
  if (attempt < DESTROY_MAX_RETRIES) {
1836
1965
  // Re-create the session from local files so we can try destroy again.
@@ -1991,10 +2120,11 @@ export class SessionManager {
1991
2120
  return this._withSessionLock(sessionId, "destroySession", () => this.destroySession(sessionId, { lockHeld: true }));
1992
2121
  }
1993
2122
  const session = this.sessions.get(sessionId);
1994
- if (session) {
2123
+ if (session)
1995
2124
  await session.destroy();
1996
- this.sessions.delete(sessionId);
1997
- }
2125
+ this._forgetWarmSession(sessionId);
2126
+ this.sessionApplicationTools.delete(sessionId);
2127
+ this.sessionConfigs.delete(sessionId);
1998
2128
  }
1999
2129
  /**
2000
2130
  * Drop the warm in-memory session handle without deleting any persisted
@@ -2012,7 +2142,7 @@ export class SessionManager {
2012
2142
  await session.destroy();
2013
2143
  }
2014
2144
  catch { }
2015
- this.sessions.delete(sessionId);
2145
+ this._forgetWarmSession(sessionId);
2016
2146
  }
2017
2147
  /**
2018
2148
  * Fully reset a session's live and persisted Copilot state.
@@ -2041,25 +2171,51 @@ export class SessionManager {
2041
2171
  activeSessionIds() {
2042
2172
  return [...this.sessions.keys()];
2043
2173
  }
2044
- /** Shutdown: destroy all sessions, stop CopilotClient. */
2174
+ /** Final worker cleanup, after the durable runtime has drained. */
2045
2175
  async shutdown() {
2046
2176
  this.unsubscribeFeatureFlags?.();
2047
2177
  this.unsubscribeFeatureFlags = null;
2048
- for (const [_, session] of this.sessions) {
2049
- try {
2050
- await session.destroy();
2051
- }
2052
- catch { }
2053
- }
2178
+ // CopilotClient.stop owns session detachment. Detaching each session
2179
+ // first can hang forever in the SDK's unbounded session.detach RPC.
2180
+ // Snapshot and release this pool before awaiting: late SDK cleanup
2181
+ // must never clear clients/configuration installed by a later start.
2182
+ const clients = [...new Set(this.clients.values())];
2183
+ this.clients.clear();
2054
2184
  this.sessions.clear();
2185
+ this.sessionAgentCopies.clear();
2186
+ this.sessionBindingFingerprints.clear();
2187
+ this.sessionApplicationTools.clear();
2188
+ this.sessionConfigs.clear();
2055
2189
  this.sessionClientKeys.clear();
2056
- for (const [, client] of this.clients) {
2190
+ // Each process gets the same deadline concurrently; one stuck client
2191
+ // cannot block another or multiply the worker's termination budget.
2192
+ // This is final cleanup only, not the individual-session eviction path.
2193
+ await Promise.all(clients.map(async (client) => {
2194
+ let timer;
2057
2195
  try {
2058
- await client.stop();
2196
+ const errors = await Promise.race([
2197
+ client.stop(),
2198
+ new Promise((_, reject) => {
2199
+ timer = setTimeout(() => reject(new Error("Copilot shutdown timed out")), COPILOT_CLIENT_SHUTDOWN_TIMEOUT_MS);
2200
+ }),
2201
+ ]);
2202
+ if (errors?.length)
2203
+ throw errors[0];
2059
2204
  }
2060
- catch { }
2061
- }
2062
- this.clients.clear();
2205
+ catch {
2206
+ console.warn("[SessionManager] Copilot did not shut down cleanly; forcing its local process to stop.");
2207
+ try {
2208
+ await client.forceStop();
2209
+ }
2210
+ catch {
2211
+ console.warn("[SessionManager] Copilot force-stop failed.");
2212
+ }
2213
+ }
2214
+ finally {
2215
+ if (timer)
2216
+ clearTimeout(timer);
2217
+ }
2218
+ }));
2063
2219
  }
2064
2220
  /**
2065
2221
  * Resolve tools from per-session config + worker-level registry.
@@ -2078,15 +2234,42 @@ export class SessionManager {
2078
2234
  : undefined;
2079
2235
  if (serializableConfig.toolNames?.length) {
2080
2236
  for (const name of serializableConfig.toolNames) {
2081
- const tool = (this.staticToolNames?.has(name) ? this.toolRegistry.get(name) : undefined)
2082
- ?? packageTools?.get(name)
2083
- ?? this.toolRegistry.get(name);
2237
+ const staticTool = this.staticToolNames?.has(name)
2238
+ ? this.toolRegistry.get(name)
2239
+ : undefined;
2240
+ const packageTool = packageTools?.get(name);
2241
+ if (!staticTool && !packageTool && this.packageToolNames.has(name)) {
2242
+ if (serializableConfig.detachedPackageToolPolicy === "drop")
2243
+ continue;
2244
+ if (serializableConfig.detachedPackageToolPolicy === "reject" || preferredPackageId) {
2245
+ throw new PackageToolBindingError(name);
2246
+ }
2247
+ }
2248
+ const tool = staticTool ?? packageTool ?? this.toolRegistry.get(name);
2084
2249
  if (tool)
2085
2250
  registryTools.push(tool);
2086
2251
  }
2087
2252
  }
2253
+ const storedTools = (storedConfig?.tools ?? []).filter((tool) => {
2254
+ const name = String(tool?.name || "");
2255
+ if (!name || this.staticToolNames?.has(name) || !this.packageToolNames.has(name))
2256
+ return true;
2257
+ // An explicitly supplied package handler follows the current package
2258
+ // copy, never a stale object retained by the caller across a reload.
2259
+ if (packageTools?.has(name) && serializableConfig.toolNames?.includes(name))
2260
+ return true;
2261
+ if (serializableConfig.detachedPackageToolPolicy === "drop")
2262
+ return false;
2263
+ if (serializableConfig.detachedPackageToolPolicy === "reject" || preferredPackageId) {
2264
+ throw new PackageToolBindingError(name);
2265
+ }
2266
+ return true;
2267
+ }).map((tool) => {
2268
+ const name = String(tool?.name || "");
2269
+ return this.staticToolNames?.has(name) ? tool : packageTools?.get(name) ?? tool;
2270
+ });
2088
2271
  const combined = [
2089
- ...(storedConfig?.tools ?? []),
2272
+ ...storedTools,
2090
2273
  ...registryTools,
2091
2274
  ];
2092
2275
  // Deduplicate by name — per-session tools take precedence
@@ -2218,18 +2401,17 @@ export class SessionManager {
2218
2401
  },
2219
2402
  };
2220
2403
  }
2221
- _buildLastInstructionsSection(sessionId, initialConfig) {
2404
+ _buildLastInstructionsSection(sessionId, initialConfig, initialAgentCopy) {
2222
2405
  return {
2223
2406
  action: async (currentContent) => {
2224
2407
  const latest = this.sessionConfigs.get(sessionId) ?? initialConfig;
2225
2408
  const runtimeContext = extractPromptContent(latest.systemMessage);
2226
- // Owner-aware copy pick, re-resolved here because this action
2227
- // runs per compose: with scope shadowing one agent name can be
2228
- // served by several packages, and this session must get the
2229
- // copy its OWNER resolves to — not whichever loaded last.
2230
- const activeAgentPrompt = latest.boundAgentName
2231
- ? pickAgentCopyForOwner(this.workerDefaults.agentPromptLookup?.[latest.boundAgentName], await this._sessionAgentOwnerKey(sessionId))?.prompt
2232
- : undefined;
2409
+ // Follow the same per-turn snapshot as tools/MCP/descriptor.
2410
+ // Re-selecting by bare name here could both defeat an explicit
2411
+ // shared-package pin and adopt refreshed instructions mid-turn.
2412
+ const activeAgentPrompt = (this.sessionAgentCopies.has(sessionId)
2413
+ ? this.sessionAgentCopies.get(sessionId)
2414
+ : initialAgentCopy)?.prompt;
2233
2415
  // Orchestration ≥1.0.71 delivers the per-turn note inside the
2234
2416
  // user turn (see prompt-system-context.ts) and flags it. For
2235
2417
  // those turns the note must NOT land here: this section is
@@ -2484,17 +2666,17 @@ export class SessionManager {
2484
2666
  const names = new Set(own.map((s) => s.name));
2485
2667
  return [...own, ...shared.filter((s) => !names.has(s.name))];
2486
2668
  }
2487
- _buildSystemMessage(sessionId, config, sessionOwnerKey = null) {
2669
+ _buildSystemMessage(sessionId, config, sessionOwnerKey = null, boundAgentCopy = resolveBoundAgentCopy(this.workerDefaults, config, sessionOwnerKey) ?? null) {
2488
2670
  const frameworkBase = this.workerDefaults.frameworkBasePrompt ?? this.workerDefaults.systemMessage;
2489
2671
  const boundAgentName = config.boundAgentName;
2490
2672
  const layerKind = config.promptLayering?.kind ?? (boundAgentName ? "app-agent" : undefined);
2491
2673
  const knowledgeToolInstructions = this._buildKnowledgeToolInstructionsSection(sessionId, config.agentIdentity);
2492
- const lastInstructions = this._buildLastInstructionsSection(sessionId, config);
2674
+ const lastInstructions = this._buildLastInstructionsSection(sessionId, config, boundAgentCopy);
2493
2675
  const additionalSections = knowledgeToolInstructions
2494
2676
  ? { tool_instructions: knowledgeToolInstructions, last_instructions: lastInstructions }
2495
2677
  : { last_instructions: lastInstructions };
2496
2678
  const isPilotSwarmSystemAgent = layerKind === "pilotswarm-system-agent";
2497
- const layerManifest = buildEffectivePromptLayers(this.workerDefaults, config, sessionOwnerKey);
2679
+ const layerManifest = buildEffectivePromptLayers(this.workerDefaults, config, sessionOwnerKey, boundAgentCopy);
2498
2680
  return composeStructuredSystemMessage({
2499
2681
  frameworkBase,
2500
2682
  appDefault: isPilotSwarmSystemAgent