pilotswarm-sdk 0.5.63 → 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.
- package/api/src/admin-diagnostics.d.ts +12 -0
- package/api/src/admin-diagnostics.js +38 -1
- package/api/src/http-api-transport.js +26 -0
- package/api/src/protocol.js +15 -0
- package/dist/activity-routing.d.ts +21 -0
- package/dist/activity-routing.d.ts.map +1 -0
- package/dist/activity-routing.js +32 -0
- package/dist/activity-routing.js.map +1 -0
- package/dist/agent-discovery.d.ts +35 -0
- package/dist/agent-discovery.d.ts.map +1 -0
- package/dist/agent-discovery.js +107 -0
- package/dist/agent-discovery.js.map +1 -0
- package/dist/client.d.ts +9 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +94 -12
- package/dist/client.js.map +1 -1
- package/dist/cms-migrations.d.ts.map +1 -1
- package/dist/cms-migrations.js +4 -0
- package/dist/cms-migrations.js.map +1 -1
- package/dist/cms.d.ts +3 -0
- package/dist/cms.d.ts.map +1 -1
- package/dist/cms.js +7 -0
- package/dist/cms.js.map +1 -1
- package/dist/feature-flag-cache.d.ts +40 -0
- package/dist/feature-flag-cache.d.ts.map +1 -0
- package/dist/feature-flag-cache.js +125 -0
- package/dist/feature-flag-cache.js.map +1 -0
- package/dist/feature-flags.d.ts +68 -0
- package/dist/feature-flags.d.ts.map +1 -0
- package/dist/feature-flags.js +45 -0
- package/dist/feature-flags.js.map +1 -0
- package/dist/feature-store.d.ts +55 -0
- package/dist/feature-store.d.ts.map +1 -0
- package/dist/feature-store.js +106 -0
- package/dist/feature-store.js.map +1 -0
- package/dist/feature-tools.d.ts +94 -0
- package/dist/feature-tools.d.ts.map +1 -0
- package/dist/feature-tools.js +60 -0
- package/dist/feature-tools.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/managed-session.d.ts +4 -0
- package/dist/managed-session.d.ts.map +1 -1
- package/dist/managed-session.js +120 -23
- package/dist/managed-session.js.map +1 -1
- package/dist/management-client.d.ts +18 -0
- package/dist/management-client.d.ts.map +1 -1
- package/dist/management-client.js +19 -0
- package/dist/management-client.js.map +1 -1
- package/dist/migrations/feature-flags-0077.d.ts +3 -0
- package/dist/migrations/feature-flags-0077.d.ts.map +1 -0
- package/dist/migrations/feature-flags-0077.js +271 -0
- package/dist/migrations/feature-flags-0077.js.map +1 -0
- package/dist/migrations/native-tasks-default-policy-0078.d.ts +3 -0
- package/dist/migrations/native-tasks-default-policy-0078.d.ts.map +1 -0
- package/dist/migrations/native-tasks-default-policy-0078.js +21 -0
- package/dist/migrations/native-tasks-default-policy-0078.js.map +1 -0
- package/dist/native-subagents.d.ts +25 -0
- package/dist/native-subagents.d.ts.map +1 -0
- package/dist/native-subagents.js +191 -0
- package/dist/native-subagents.js.map +1 -0
- package/dist/native-task-observer.d.ts +87 -0
- package/dist/native-task-observer.d.ts.map +1 -0
- package/dist/native-task-observer.js +268 -0
- package/dist/native-task-observer.js.map +1 -0
- package/dist/orchestration/agents.d.ts +8 -3
- package/dist/orchestration/agents.d.ts.map +1 -1
- package/dist/orchestration/agents.js +87 -36
- package/dist/orchestration/agents.js.map +1 -1
- package/dist/orchestration/index.d.ts +2 -2
- package/dist/orchestration/index.js +1 -1
- package/dist/orchestration/lifecycle.js +1 -1
- package/dist/orchestration/lifecycle.js.map +1 -1
- package/dist/orchestration/runtime.d.ts +1 -1
- package/dist/orchestration/runtime.d.ts.map +1 -1
- package/dist/orchestration/runtime.js +18 -4
- package/dist/orchestration/runtime.js.map +1 -1
- package/dist/orchestration/turn.d.ts.map +1 -1
- package/dist/orchestration/turn.js +3 -5
- package/dist/orchestration/turn.js.map +1 -1
- package/dist/orchestration-registry.d.ts.map +1 -1
- package/dist/orchestration-registry.js +12 -2
- package/dist/orchestration-registry.js.map +1 -1
- package/dist/orchestration-version.d.ts +1 -1
- package/dist/orchestration-version.js +1 -1
- package/dist/orchestration.d.ts +2 -2
- package/dist/orchestration.js +1 -1
- package/dist/orchestration_1_0_46.d.ts +1 -1
- package/dist/orchestration_1_0_47.d.ts +1 -1
- package/dist/orchestration_1_0_48.d.ts +1 -1
- package/dist/orchestration_1_0_49.d.ts +1 -1
- package/dist/orchestration_1_0_50.d.ts +1 -1
- package/dist/orchestration_1_0_73/agents.d.ts +61 -0
- package/dist/orchestration_1_0_73/agents.d.ts.map +1 -0
- package/dist/orchestration_1_0_73/agents.js +843 -0
- package/dist/orchestration_1_0_73/agents.js.map +1 -0
- package/dist/orchestration_1_0_73/index.d.ts +24 -0
- package/dist/orchestration_1_0_73/index.d.ts.map +1 -0
- package/dist/orchestration_1_0_73/index.js +13 -0
- package/dist/orchestration_1_0_73/index.js.map +1 -0
- package/dist/orchestration_1_0_73/lifecycle.d.ts +51 -0
- package/dist/orchestration_1_0_73/lifecycle.d.ts.map +1 -0
- package/dist/orchestration_1_0_73/lifecycle.js +981 -0
- package/dist/orchestration_1_0_73/lifecycle.js.map +1 -0
- package/dist/orchestration_1_0_73/queue.d.ts +20 -0
- package/dist/orchestration_1_0_73/queue.d.ts.map +1 -0
- package/dist/orchestration_1_0_73/queue.js +894 -0
- package/dist/orchestration_1_0_73/queue.js.map +1 -0
- package/dist/orchestration_1_0_73/runtime.d.ts +31 -0
- package/dist/orchestration_1_0_73/runtime.d.ts.map +1 -0
- package/dist/orchestration_1_0_73/runtime.js +230 -0
- package/dist/orchestration_1_0_73/runtime.js.map +1 -0
- package/dist/orchestration_1_0_73/state.d.ts +214 -0
- package/dist/orchestration_1_0_73/state.d.ts.map +1 -0
- package/dist/orchestration_1_0_73/state.js +178 -0
- package/dist/orchestration_1_0_73/state.js.map +1 -0
- package/dist/orchestration_1_0_73/turn.d.ts +34 -0
- package/dist/orchestration_1_0_73/turn.d.ts.map +1 -0
- package/dist/orchestration_1_0_73/turn.js +1435 -0
- package/dist/orchestration_1_0_73/turn.js.map +1 -0
- package/dist/orchestration_1_0_73/utils.d.ts +39 -0
- package/dist/orchestration_1_0_73/utils.d.ts.map +1 -0
- package/dist/orchestration_1_0_73/utils.js +322 -0
- package/dist/orchestration_1_0_73/utils.js.map +1 -0
- package/dist/orchestration_1_0_74/agents.d.ts +61 -0
- package/dist/orchestration_1_0_74/agents.d.ts.map +1 -0
- package/dist/orchestration_1_0_74/agents.js +889 -0
- package/dist/orchestration_1_0_74/agents.js.map +1 -0
- package/dist/orchestration_1_0_74/index.d.ts +24 -0
- package/dist/orchestration_1_0_74/index.d.ts.map +1 -0
- package/dist/orchestration_1_0_74/index.js +13 -0
- package/dist/orchestration_1_0_74/index.js.map +1 -0
- package/dist/orchestration_1_0_74/lifecycle.d.ts +51 -0
- package/dist/orchestration_1_0_74/lifecycle.d.ts.map +1 -0
- package/dist/orchestration_1_0_74/lifecycle.js +981 -0
- package/dist/orchestration_1_0_74/lifecycle.js.map +1 -0
- package/dist/orchestration_1_0_74/queue.d.ts +20 -0
- package/dist/orchestration_1_0_74/queue.d.ts.map +1 -0
- package/dist/orchestration_1_0_74/queue.js +894 -0
- package/dist/orchestration_1_0_74/queue.js.map +1 -0
- package/dist/orchestration_1_0_74/runtime.d.ts +31 -0
- package/dist/orchestration_1_0_74/runtime.d.ts.map +1 -0
- package/dist/orchestration_1_0_74/runtime.js +232 -0
- package/dist/orchestration_1_0_74/runtime.js.map +1 -0
- package/dist/orchestration_1_0_74/state.d.ts +214 -0
- package/dist/orchestration_1_0_74/state.d.ts.map +1 -0
- package/dist/orchestration_1_0_74/state.js +178 -0
- package/dist/orchestration_1_0_74/state.js.map +1 -0
- package/dist/orchestration_1_0_74/turn.d.ts +34 -0
- package/dist/orchestration_1_0_74/turn.d.ts.map +1 -0
- package/dist/orchestration_1_0_74/turn.js +1435 -0
- package/dist/orchestration_1_0_74/turn.js.map +1 -0
- package/dist/orchestration_1_0_74/utils.d.ts +39 -0
- package/dist/orchestration_1_0_74/utils.d.ts.map +1 -0
- package/dist/orchestration_1_0_74/utils.js +322 -0
- package/dist/orchestration_1_0_74/utils.js.map +1 -0
- package/dist/orchestration_1_0_75/agents.d.ts +61 -0
- package/dist/orchestration_1_0_75/agents.d.ts.map +1 -0
- package/dist/orchestration_1_0_75/agents.js +887 -0
- package/dist/orchestration_1_0_75/agents.js.map +1 -0
- package/dist/orchestration_1_0_75/index.d.ts +24 -0
- package/dist/orchestration_1_0_75/index.d.ts.map +1 -0
- package/dist/orchestration_1_0_75/index.js +13 -0
- package/dist/orchestration_1_0_75/index.js.map +1 -0
- package/dist/orchestration_1_0_75/lifecycle.d.ts +51 -0
- package/dist/orchestration_1_0_75/lifecycle.d.ts.map +1 -0
- package/dist/orchestration_1_0_75/lifecycle.js +981 -0
- package/dist/orchestration_1_0_75/lifecycle.js.map +1 -0
- package/dist/orchestration_1_0_75/queue.d.ts +20 -0
- package/dist/orchestration_1_0_75/queue.d.ts.map +1 -0
- package/dist/orchestration_1_0_75/queue.js +894 -0
- package/dist/orchestration_1_0_75/queue.js.map +1 -0
- package/dist/orchestration_1_0_75/runtime.d.ts +31 -0
- package/dist/orchestration_1_0_75/runtime.d.ts.map +1 -0
- package/dist/orchestration_1_0_75/runtime.js +239 -0
- package/dist/orchestration_1_0_75/runtime.js.map +1 -0
- package/dist/orchestration_1_0_75/state.d.ts +214 -0
- package/dist/orchestration_1_0_75/state.d.ts.map +1 -0
- package/dist/orchestration_1_0_75/state.js +178 -0
- package/dist/orchestration_1_0_75/state.js.map +1 -0
- package/dist/orchestration_1_0_75/turn.d.ts +34 -0
- package/dist/orchestration_1_0_75/turn.d.ts.map +1 -0
- package/dist/orchestration_1_0_75/turn.js +1435 -0
- package/dist/orchestration_1_0_75/turn.js.map +1 -0
- package/dist/orchestration_1_0_75/utils.d.ts +39 -0
- package/dist/orchestration_1_0_75/utils.d.ts.map +1 -0
- package/dist/orchestration_1_0_75/utils.js +322 -0
- package/dist/orchestration_1_0_75/utils.js.map +1 -0
- package/dist/orchestration_1_0_76/agents.d.ts +61 -0
- package/dist/orchestration_1_0_76/agents.d.ts.map +1 -0
- package/dist/orchestration_1_0_76/agents.js +865 -0
- package/dist/orchestration_1_0_76/agents.js.map +1 -0
- package/dist/orchestration_1_0_76/index.d.ts +24 -0
- package/dist/orchestration_1_0_76/index.d.ts.map +1 -0
- package/dist/orchestration_1_0_76/index.js +13 -0
- package/dist/orchestration_1_0_76/index.js.map +1 -0
- package/dist/orchestration_1_0_76/lifecycle.d.ts +51 -0
- package/dist/orchestration_1_0_76/lifecycle.d.ts.map +1 -0
- package/dist/orchestration_1_0_76/lifecycle.js +981 -0
- package/dist/orchestration_1_0_76/lifecycle.js.map +1 -0
- package/dist/orchestration_1_0_76/queue.d.ts +20 -0
- package/dist/orchestration_1_0_76/queue.d.ts.map +1 -0
- package/dist/orchestration_1_0_76/queue.js +894 -0
- package/dist/orchestration_1_0_76/queue.js.map +1 -0
- package/dist/orchestration_1_0_76/runtime.d.ts +31 -0
- package/dist/orchestration_1_0_76/runtime.d.ts.map +1 -0
- package/dist/orchestration_1_0_76/runtime.js +239 -0
- package/dist/orchestration_1_0_76/runtime.js.map +1 -0
- package/dist/orchestration_1_0_76/state.d.ts +214 -0
- package/dist/orchestration_1_0_76/state.d.ts.map +1 -0
- package/dist/orchestration_1_0_76/state.js +178 -0
- package/dist/orchestration_1_0_76/state.js.map +1 -0
- package/dist/orchestration_1_0_76/turn.d.ts +34 -0
- package/dist/orchestration_1_0_76/turn.d.ts.map +1 -0
- package/dist/orchestration_1_0_76/turn.js +1435 -0
- package/dist/orchestration_1_0_76/turn.js.map +1 -0
- package/dist/orchestration_1_0_76/utils.d.ts +39 -0
- package/dist/orchestration_1_0_76/utils.d.ts.map +1 -0
- package/dist/orchestration_1_0_76/utils.js +322 -0
- package/dist/orchestration_1_0_76/utils.js.map +1 -0
- package/dist/orchestration_1_0_77/agents.d.ts +66 -0
- package/dist/orchestration_1_0_77/agents.d.ts.map +1 -0
- package/dist/orchestration_1_0_77/agents.js +895 -0
- package/dist/orchestration_1_0_77/agents.js.map +1 -0
- package/dist/orchestration_1_0_77/index.d.ts +24 -0
- package/dist/orchestration_1_0_77/index.d.ts.map +1 -0
- package/dist/orchestration_1_0_77/index.js +13 -0
- package/dist/orchestration_1_0_77/index.js.map +1 -0
- package/dist/orchestration_1_0_77/lifecycle.d.ts +51 -0
- package/dist/orchestration_1_0_77/lifecycle.d.ts.map +1 -0
- package/dist/orchestration_1_0_77/lifecycle.js +981 -0
- package/dist/orchestration_1_0_77/lifecycle.js.map +1 -0
- package/dist/orchestration_1_0_77/queue.d.ts +20 -0
- package/dist/orchestration_1_0_77/queue.d.ts.map +1 -0
- package/dist/orchestration_1_0_77/queue.js +894 -0
- package/dist/orchestration_1_0_77/queue.js.map +1 -0
- package/dist/orchestration_1_0_77/runtime.d.ts +31 -0
- package/dist/orchestration_1_0_77/runtime.d.ts.map +1 -0
- package/dist/orchestration_1_0_77/runtime.js +239 -0
- package/dist/orchestration_1_0_77/runtime.js.map +1 -0
- package/dist/orchestration_1_0_77/state.d.ts +214 -0
- package/dist/orchestration_1_0_77/state.d.ts.map +1 -0
- package/dist/orchestration_1_0_77/state.js +178 -0
- package/dist/orchestration_1_0_77/state.js.map +1 -0
- package/dist/orchestration_1_0_77/turn.d.ts +34 -0
- package/dist/orchestration_1_0_77/turn.d.ts.map +1 -0
- package/dist/orchestration_1_0_77/turn.js +1433 -0
- package/dist/orchestration_1_0_77/turn.js.map +1 -0
- package/dist/orchestration_1_0_77/utils.d.ts +39 -0
- package/dist/orchestration_1_0_77/utils.d.ts.map +1 -0
- package/dist/orchestration_1_0_77/utils.js +322 -0
- package/dist/orchestration_1_0_77/utils.js.map +1 -0
- package/dist/reserved-tool-names.d.ts +5 -0
- package/dist/reserved-tool-names.d.ts.map +1 -0
- package/dist/reserved-tool-names.js +102 -0
- package/dist/reserved-tool-names.js.map +1 -0
- package/dist/session-manager.d.ts +31 -1
- package/dist/session-manager.d.ts.map +1 -1
- package/dist/session-manager.js +343 -102
- package/dist/session-manager.js.map +1 -1
- package/dist/session-proxy.d.ts +28 -3
- package/dist/session-proxy.d.ts.map +1 -1
- package/dist/session-proxy.js +192 -71
- package/dist/session-proxy.js.map +1 -1
- package/dist/session-store.d.ts +2 -0
- package/dist/session-store.d.ts.map +1 -1
- package/dist/session-store.js +18 -2
- package/dist/session-store.js.map +1 -1
- package/dist/types.d.ts +25 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/web/generated-op-methods.d.ts +96 -0
- package/dist/web/generated-op-methods.d.ts.map +1 -1
- package/dist/web/generated-op-methods.js +24 -0
- package/dist/web/generated-op-methods.js.map +1 -1
- package/dist/web/web-management-client.d.ts +13 -0
- package/dist/web/web-management-client.d.ts.map +1 -1
- package/dist/web/web-management-client.js +12 -0
- package/dist/web/web-management-client.js.map +1 -1
- package/dist/worker.d.ts +13 -3
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +150 -110
- package/dist/worker.js.map +1 -1
- package/package.json +4 -3
- package/plugins/mgmt/agents/pilotswarm.agent.md +18 -1
- package/plugins/mgmt/agents/resourcemgr.agent.md +18 -1
- package/plugins/system/agents/default.agent.md +16 -7
package/dist/session-manager.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { NATIVE_BUILTIN_AGENTS, NATIVE_EXCLUDED_TOOLS, nativeSubagentGuidance, nativeSubagentDefinitions, nativeSubagentHooks, guardNativeExternalTools } from "./native-subagents.js";
|
|
2
|
+
import { createFeatureTools, FEATURE_OPERATION_SPECS } from "./feature-tools.js";
|
|
3
|
+
import { FeatureFlagError } from "./feature-flags.js";
|
|
1
4
|
import { BYOK_CLIENT_PREFIX, createCopilotClient, needsByokRequestCompatibility } from "./copilot-client.js";
|
|
2
5
|
import { ManagedSession } from "./managed-session.js";
|
|
3
6
|
import { SESSION_STATE_MISSING_PREFIX } from "./types.js";
|
|
@@ -35,6 +38,7 @@ const DEHYDRATE_STORE_MAX_RETRIES = 1;
|
|
|
35
38
|
const DEHYDRATE_STORE_RETRY_BASE_DELAY_MS = 0;
|
|
36
39
|
const SESSION_LOCK_BACKOFF_MS = [5_000, 10_000, 20_000];
|
|
37
40
|
const SESSION_LOCK_MAX_WAIT_MS = 120_000;
|
|
41
|
+
const COPILOT_CLIENT_SHUTDOWN_TIMEOUT_MS = 10_000;
|
|
38
42
|
export const SESSION_LOCK_ACQUIRE_TIMEOUT_CODE = "PILOTSWARM_SESSION_LOCK_ACQUIRE_TIMEOUT";
|
|
39
43
|
export class SessionLockAcquireTimeoutError extends Error {
|
|
40
44
|
code = SESSION_LOCK_ACQUIRE_TIMEOUT_CODE;
|
|
@@ -76,6 +80,24 @@ function isMissingDehydrateSnapshotError(error) {
|
|
|
76
80
|
const message = error instanceof Error ? error.message : String(error ?? "");
|
|
77
81
|
return /Session state directory not ready during dehydrate/i.test(message);
|
|
78
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
|
+
}
|
|
79
101
|
/** Key under which a package copy's per-agent config (MCP) is registered. */
|
|
80
102
|
export function packageAgentKey(packageId, agentName) {
|
|
81
103
|
return `pkg\u0001${packageId}\u0001${agentName}`;
|
|
@@ -120,7 +142,58 @@ export function pickAgentCopyForOwner(entry, ownerKey) {
|
|
|
120
142
|
?? copies.find((copy) => copy.packageScope === "shared")
|
|
121
143
|
?? undefined;
|
|
122
144
|
}
|
|
123
|
-
|
|
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) {
|
|
124
197
|
const boundAgentName = config.boundAgentName;
|
|
125
198
|
const layerKind = config.promptLayering?.kind ?? (boundAgentName ? "app-agent" : undefined);
|
|
126
199
|
const isPilotSwarmSystemAgent = layerKind === "pilotswarm-system-agent";
|
|
@@ -130,9 +203,8 @@ function buildEffectivePromptLayers(workerDefaults, config, sessionOwnerKey = nu
|
|
|
130
203
|
if (!isPilotSwarmSystemAgent && workerDefaults.appDefaultDescriptor)
|
|
131
204
|
layers.push(workerDefaults.appDefaultDescriptor);
|
|
132
205
|
if (boundAgentName) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
layers.push(copy.descriptor);
|
|
206
|
+
if (boundAgentCopy?.descriptor)
|
|
207
|
+
layers.push(boundAgentCopy.descriptor);
|
|
136
208
|
}
|
|
137
209
|
return layers;
|
|
138
210
|
}
|
|
@@ -287,6 +359,8 @@ export class SessionManager {
|
|
|
287
359
|
}
|
|
288
360
|
}
|
|
289
361
|
sessions = new Map();
|
|
362
|
+
featureFlags = null;
|
|
363
|
+
unsubscribeFeatureFlags = null;
|
|
290
364
|
/** Live in-memory session count — worker-registry health reporting. */
|
|
291
365
|
get activeSessionCount() {
|
|
292
366
|
return this.sessions.size;
|
|
@@ -304,10 +378,18 @@ export class SessionManager {
|
|
|
304
378
|
sessionStore = null;
|
|
305
379
|
/** In-memory configs with non-serializable fields (tools, hooks). */
|
|
306
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();
|
|
307
387
|
/** Worker-level tool registry — shared reference from PilotSwarmWorker. */
|
|
308
388
|
toolRegistry = new Map();
|
|
309
389
|
/** Per-package tool maps, so a session prefers ITS package's handler on a name collision. */
|
|
310
390
|
packageToolRegistry = null;
|
|
391
|
+
/** Package-owned names cannot be attached without the matching agent package binding. */
|
|
392
|
+
packageToolNames = new Set();
|
|
311
393
|
/** Names registered by deployment code — a package tool must never shadow these. */
|
|
312
394
|
staticToolNames = null;
|
|
313
395
|
/** Worker-level defaults for building blocks. */
|
|
@@ -363,6 +445,10 @@ export class SessionManager {
|
|
|
363
445
|
/** Store full config (with tools/hooks) for a session. Called by PilotSwarmClient. */
|
|
364
446
|
setConfig(sessionId, config) {
|
|
365
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);
|
|
366
452
|
}
|
|
367
453
|
async _allowedModelProviderIds(sessionId) {
|
|
368
454
|
const registry = this.workerDefaults.modelProviders;
|
|
@@ -596,6 +682,12 @@ export class SessionManager {
|
|
|
596
682
|
this.toolRegistry = registry;
|
|
597
683
|
this.packageToolRegistry = opts?.byPackage ?? null;
|
|
598
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
|
+
}
|
|
599
691
|
}
|
|
600
692
|
/** Set the cluster facts store for always-on facts tools. */
|
|
601
693
|
setFactStore(factStore) {
|
|
@@ -609,6 +701,16 @@ export class SessionManager {
|
|
|
609
701
|
setSessionCatalog(catalog) {
|
|
610
702
|
this.sessionCatalog = catalog;
|
|
611
703
|
}
|
|
704
|
+
setFeatureFlagCache(cache) {
|
|
705
|
+
this.unsubscribeFeatureFlags?.();
|
|
706
|
+
this.featureFlags = cache;
|
|
707
|
+
this.unsubscribeFeatureFlags = cache?.onChange(() => {
|
|
708
|
+
for (const managed of this.sessions.values())
|
|
709
|
+
managed.refreshNativeFeaturePolicy();
|
|
710
|
+
}) ?? null;
|
|
711
|
+
for (const managed of this.sessions.values())
|
|
712
|
+
managed.refreshNativeFeaturePolicy();
|
|
713
|
+
}
|
|
612
714
|
/**
|
|
613
715
|
* Hot-swap the model-provider registry after a config-file change on
|
|
614
716
|
* disk (ConfigMap update). Applies to all subsequent model resolution;
|
|
@@ -830,7 +932,7 @@ export class SessionManager {
|
|
|
830
932
|
await existing.destroy();
|
|
831
933
|
}
|
|
832
934
|
catch { }
|
|
833
|
-
this.
|
|
935
|
+
this._forgetWarmSession(sessionId);
|
|
834
936
|
}
|
|
835
937
|
try {
|
|
836
938
|
const client = await this._ensureClientForSession(sessionId);
|
|
@@ -866,7 +968,7 @@ export class SessionManager {
|
|
|
866
968
|
await existing.destroy();
|
|
867
969
|
}
|
|
868
970
|
catch { }
|
|
869
|
-
this.
|
|
971
|
+
this._forgetWarmSession(sessionId);
|
|
870
972
|
}
|
|
871
973
|
try {
|
|
872
974
|
const client = await this._ensureClientForSession(sessionId);
|
|
@@ -983,7 +1085,7 @@ export class SessionManager {
|
|
|
983
1085
|
await existing.destroy();
|
|
984
1086
|
}
|
|
985
1087
|
catch { }
|
|
986
|
-
this.
|
|
1088
|
+
this._forgetWarmSession(sessionId);
|
|
987
1089
|
}
|
|
988
1090
|
if (!dirExists) {
|
|
989
1091
|
this.sessionLastTouchedAt.delete(sessionId);
|
|
@@ -1128,7 +1230,7 @@ export class SessionManager {
|
|
|
1128
1230
|
...(this.workerDefaults.appDefaultToolNames ?? []),
|
|
1129
1231
|
...(serializableConfig.toolNames ?? []),
|
|
1130
1232
|
]));
|
|
1131
|
-
|
|
1233
|
+
let effectiveSerializableConfig = inheritedToolNames.length > 0
|
|
1132
1234
|
? { ...serializableConfig, toolNames: inheritedToolNames }
|
|
1133
1235
|
: serializableConfig;
|
|
1134
1236
|
// Which copy of the bound agent does this session get? Owner-aware:
|
|
@@ -1136,20 +1238,69 @@ export class SessionManager {
|
|
|
1136
1238
|
// one, the same rule the registry resolver applies at create time.
|
|
1137
1239
|
// Resolved once here; prompt, descriptor, MCP, and tool-handler picks
|
|
1138
1240
|
// below all follow the same copy so a session can never mix copies.
|
|
1139
|
-
const boundAgentEntry = effectiveSerializableConfig.boundAgentName
|
|
1140
|
-
? this.workerDefaults.agentPromptLookup?.[effectiveSerializableConfig.boundAgentName]
|
|
1141
|
-
: undefined;
|
|
1142
|
-
// A PACKAGE agent (as opposed to a deployment/inline agent) carries a
|
|
1143
|
-
// packageId on its entry or on any of its copies. This is load-bearing
|
|
1144
|
-
// for MCP resolution: a package agent must never fall back to the
|
|
1145
|
-
// bare-name MCP key, which another copy of a shadowed name also wrote.
|
|
1146
1241
|
const sessionOwnerKey = effectiveSerializableConfig.boundAgentName
|
|
1147
1242
|
? await this._sessionAgentOwnerKey(sessionId)
|
|
1148
1243
|
: null;
|
|
1149
|
-
const boundAgentCopy = pickAgentCopyForOwner(boundAgentEntry, sessionOwnerKey);
|
|
1150
|
-
// Resolve tools: merge per-session (setConfig) + registry (toolNames)
|
|
1151
1244
|
const storedConfig = this.sessionConfigs.get(sessionId);
|
|
1152
|
-
|
|
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
|
+
};
|
|
1153
1304
|
const config = {
|
|
1154
1305
|
...storedConfig,
|
|
1155
1306
|
...effectiveSerializableConfig,
|
|
@@ -1274,7 +1425,7 @@ export class SessionManager {
|
|
|
1274
1425
|
await existingWarm.destroy();
|
|
1275
1426
|
}
|
|
1276
1427
|
catch { }
|
|
1277
|
-
this.
|
|
1428
|
+
this._forgetWarmSession(sessionId);
|
|
1278
1429
|
}
|
|
1279
1430
|
}
|
|
1280
1431
|
const client = await this.ensureClient(userGithubToken, byokOpenAi);
|
|
@@ -1451,16 +1602,36 @@ export class SessionManager {
|
|
|
1451
1602
|
// sub-agent, fact, inspect, or graph tools. Mirrors the per-turn gate
|
|
1452
1603
|
// in managed-session.ts runTurn — hard exclusion beats instructions.
|
|
1453
1604
|
const isServiceSession = effectiveSerializableConfig.agentIdentity === "regen-distiller";
|
|
1605
|
+
const featureTools = this.sessionCatalog?.features
|
|
1606
|
+
&& ["resourcemgr", "pilotswarm", "agent-manager"].includes(effectiveSerializableConfig.agentIdentity ?? "")
|
|
1607
|
+
&& (await this._resolveFeatureViewer(sessionId)).isAdmin
|
|
1608
|
+
? createFeatureTools(this.sessionCatalog.features, () => this._resolveFeatureViewer(sessionId)) : [];
|
|
1609
|
+
// Handler refresh alone cannot change the CLI's tool declarations.
|
|
1610
|
+
// Role transitions must recreate the warm SDK handle at the next turn.
|
|
1611
|
+
config.featureToolFingerprint = createHash("sha256")
|
|
1612
|
+
.update(JSON.stringify(featureTools.map(({ name, description, parameters }) => ({ name, description, parameters }))))
|
|
1613
|
+
.digest("hex");
|
|
1614
|
+
const nativeOwner = catalogRow?.owner ?? null;
|
|
1615
|
+
// An unreadable owner must not bypass a user-level OFF by resolving
|
|
1616
|
+
// cluster policy. Ownerless resolution is only for standalone managers.
|
|
1617
|
+
const nativeOwnerKnown = !this.sessionCatalog || Boolean(nativeOwner?.provider && nativeOwner?.subject);
|
|
1618
|
+
config.nativeFeatureAllowed = () => nativeOwnerKnown
|
|
1619
|
+
&& (this.featureFlags?.resolve("copilot.native_tasks", nativeOwner, { fallback: false }).enabled ?? false);
|
|
1620
|
+
const nativeEnabled = this.workerDefaults.nativeSubagents === "sync" && config.nativeFeatureAllowed()
|
|
1621
|
+
&& !catalogRow?.isSystem && !isServiceSession && !isTunerSession
|
|
1622
|
+
&& config.promptLayering?.kind !== "pilotswarm-system-agent";
|
|
1623
|
+
config.nativeSubagents = nativeEnabled ? "sync" : "off";
|
|
1454
1624
|
const unlessService = (tools) => (isServiceSession ? [] : tools);
|
|
1455
1625
|
const SYSTEM_TOOL_NAMES = new Set([
|
|
1456
1626
|
...systemTools, ...subAgentTools, ...factTools, ...inspectTools, ...graphTools,
|
|
1457
|
-
].map((t) => t.name));
|
|
1627
|
+
].map((t) => t.name).concat(FEATURE_OPERATION_SPECS.map(spec => spec.name)));
|
|
1458
1628
|
const persistentSessionTools = [
|
|
1459
1629
|
...userTools.filter((t) => !SYSTEM_TOOL_NAMES.has(t.name)),
|
|
1460
1630
|
...unlessService(factTools),
|
|
1461
1631
|
...unlessService(inspectTools),
|
|
1462
1632
|
...unlessService(graphTools),
|
|
1463
1633
|
...unlessService(providerTools),
|
|
1634
|
+
...unlessService(featureTools),
|
|
1464
1635
|
];
|
|
1465
1636
|
const allTools = [
|
|
1466
1637
|
...persistentSessionTools.filter((t) => !SYSTEM_TOOL_NAMES.has(t.name)),
|
|
@@ -1470,38 +1641,23 @@ export class SessionManager {
|
|
|
1470
1641
|
...unlessService(inspectTools),
|
|
1471
1642
|
...unlessService(graphTools),
|
|
1472
1643
|
...unlessService(providerTools),
|
|
1644
|
+
...unlessService(featureTools),
|
|
1473
1645
|
];
|
|
1474
1646
|
config.tools = persistentSessionTools;
|
|
1475
1647
|
// Build system message: worker base + client override
|
|
1476
|
-
const systemMessage = this._buildSystemMessage(sessionId, config, sessionOwnerKey);
|
|
1477
|
-
//
|
|
1478
|
-
//
|
|
1479
|
-
//
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
// • deployment/inline copy → the bare name (its own MCP);
|
|
1490
|
-
// • no copy resolved (a foreign-private-only name) → no grants.
|
|
1491
|
-
// Keying off `boundAgentCopy.packageId` rather than "is any copy a
|
|
1492
|
-
// package" is load-bearing: when a deployment agent and a package
|
|
1493
|
-
// share a name and this session resolved the DEPLOYMENT copy, it must
|
|
1494
|
-
// still get the deployment agent's bare-key MCP, not an empty
|
|
1495
|
-
// qualified lookup.
|
|
1496
|
-
const boundAgentMcpServers = !effectiveSerializableConfig.boundAgentName || !boundAgentCopy
|
|
1497
|
-
? undefined
|
|
1498
|
-
: boundAgentCopy.packageId
|
|
1499
|
-
? this.workerDefaults.agentMcpServers?.[packageAgentKey(boundAgentCopy.packageId, effectiveSerializableConfig.boundAgentName)]
|
|
1500
|
-
: this.workerDefaults.agentMcpServers?.[effectiveSerializableConfig.boundAgentName];
|
|
1501
|
-
const effectiveMcpServers = {
|
|
1502
|
-
...(this.workerDefaults.baseMcpServers ?? {}),
|
|
1503
|
-
...(boundAgentMcpServers ?? {}),
|
|
1504
|
-
};
|
|
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;
|
|
1505
1661
|
const sessionConfig = {
|
|
1506
1662
|
sessionId,
|
|
1507
1663
|
// Sole chokepoint where tool DECLARATIONS reach the CLI (create and
|
|
@@ -1509,7 +1665,7 @@ export class SessionManager {
|
|
|
1509
1665
|
// refreshes the client-side handler map). Pinned so tool search
|
|
1510
1666
|
// cannot defer PilotSwarm tools out of the prompt — see
|
|
1511
1667
|
// tool-pinning.ts for the why and the phase-2 opt-out path.
|
|
1512
|
-
tools: pinToolsNeverDefer(allTools),
|
|
1668
|
+
tools: pinToolsNeverDefer(nativeEnabled ? guardNativeExternalTools(allTools, sessionId) : allTools),
|
|
1513
1669
|
model: sdkModelName,
|
|
1514
1670
|
// Tell the runtime what a BYOK model can do.
|
|
1515
1671
|
//
|
|
@@ -1542,7 +1698,7 @@ export class SessionManager {
|
|
|
1542
1698
|
// state placement (verified against @github/copilot 1.0.36). State location is
|
|
1543
1699
|
// controlled exclusively via COPILOT_HOME, set on the spawned CLI in ensureClient().
|
|
1544
1700
|
workingDirectory: config.workingDirectory,
|
|
1545
|
-
hooks: config.hooks,
|
|
1701
|
+
hooks: nativeEnabled ? nativeSubagentHooks(sdkModelName, config.hooks, () => this.sessions.get(sessionId)?.canAdmitNativeTask() ?? false) : config.hooks,
|
|
1546
1702
|
onPermissionRequest: config.onPermissionRequest ?? approvePermissionForSession,
|
|
1547
1703
|
infiniteSessions: { enabled: true },
|
|
1548
1704
|
// Enable token-level streaming so the catch-all event handler in
|
|
@@ -1555,17 +1711,20 @@ export class SessionManager {
|
|
|
1555
1711
|
// Suppress sub-agent streaming events — we never want the parent
|
|
1556
1712
|
// session's event log polluted with grandchild deltas.
|
|
1557
1713
|
includeSubAgentStreamingEvents: false,
|
|
1558
|
-
//
|
|
1559
|
-
//
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1714
|
+
// Native workers are explicitly scoped: built-ins inherit external
|
|
1715
|
+
// tools, and loaded PilotSwarm agents expect durable child contracts.
|
|
1716
|
+
excludedTools: nativeEnabled ? NATIVE_EXCLUDED_TOOLS : ["task"],
|
|
1717
|
+
...(nativeEnabled ? {
|
|
1718
|
+
customAgents: nativeSubagentDefinitions(sdkModelName),
|
|
1719
|
+
customAgentsLocalOnly: true,
|
|
1720
|
+
excludedBuiltinAgents: NATIVE_BUILTIN_AGENTS,
|
|
1721
|
+
} : {}),
|
|
1563
1722
|
// Custom LLM provider — resolve from registry or legacy single provider
|
|
1564
1723
|
...resolvedProviderConfig,
|
|
1565
1724
|
// Pass loaded skills and agents from worker defaults; MCP servers
|
|
1566
1725
|
// are the bound agent's own resolved map (see above).
|
|
1567
1726
|
...(this.workerDefaults.skillDirectories?.length && { skillDirectories: this.workerDefaults.skillDirectories }),
|
|
1568
|
-
...(this.workerDefaults.customAgents?.length && { customAgents: this.workerDefaults.customAgents }),
|
|
1727
|
+
...(!nativeEnabled && this.workerDefaults.customAgents?.length && { customAgents: this.workerDefaults.customAgents }),
|
|
1569
1728
|
...(Object.keys(effectiveMcpServers).length > 0 && { mcpServers: effectiveMcpServers }),
|
|
1570
1729
|
};
|
|
1571
1730
|
let copilotSession;
|
|
@@ -1582,15 +1741,16 @@ export class SessionManager {
|
|
|
1582
1741
|
console.warn(`[SessionManager] epoch-start for ${sessionId} (epoch ${transcriptEpoch}); ` +
|
|
1583
1742
|
`discarding the warm Copilot session of the previous epoch.`);
|
|
1584
1743
|
await existing.destroy();
|
|
1585
|
-
this.
|
|
1744
|
+
this._forgetWarmSession(sessionId);
|
|
1586
1745
|
}
|
|
1587
|
-
else if (existing.requiresModelRebind(config)) {
|
|
1588
|
-
console.warn(`[SessionManager] model
|
|
1589
|
-
`disconnecting warm Copilot session so it can resume with the
|
|
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.`);
|
|
1590
1749
|
await existing.destroy();
|
|
1591
|
-
this.
|
|
1750
|
+
this._forgetWarmSession(sessionId);
|
|
1592
1751
|
}
|
|
1593
1752
|
else {
|
|
1753
|
+
this.sessionAgentCopies.set(sessionId, boundAgentCopy);
|
|
1594
1754
|
existing.updateConfig(config);
|
|
1595
1755
|
return existing;
|
|
1596
1756
|
}
|
|
@@ -1698,7 +1858,9 @@ export class SessionManager {
|
|
|
1698
1858
|
agentId: effectiveSerializableConfig.agentIdentity ?? null,
|
|
1699
1859
|
}));
|
|
1700
1860
|
this.sessions.set(sessionId, managed);
|
|
1701
|
-
|
|
1861
|
+
this.sessionAgentCopies.set(sessionId, boundAgentCopy);
|
|
1862
|
+
this.sessionBindingFingerprints.set(sessionId, bindingFingerprint);
|
|
1863
|
+
const promptLayers = buildEffectivePromptLayers(this.workerDefaults, config, sessionOwnerKey, boundAgentCopy ?? null);
|
|
1702
1864
|
if (promptLayers.length > 0 && this.sessionCatalog) {
|
|
1703
1865
|
void this.sessionCatalog.recordEvents(sessionId, [{
|
|
1704
1866
|
eventType: "session.prompt_layers",
|
|
@@ -1715,6 +1877,12 @@ export class SessionManager {
|
|
|
1715
1877
|
getSessionStateDir() {
|
|
1716
1878
|
return this.sessionStateDir;
|
|
1717
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
|
+
}
|
|
1718
1886
|
/**
|
|
1719
1887
|
* Destroy the in-memory ManagedSession only — disk state untouched.
|
|
1720
1888
|
* Used by the lifecycle preamble before overwriting local files with a
|
|
@@ -1723,13 +1891,13 @@ export class SessionManager {
|
|
|
1723
1891
|
*/
|
|
1724
1892
|
async dropWarmSession(sessionId) {
|
|
1725
1893
|
const existing = this.sessions.get(sessionId);
|
|
1726
|
-
if (
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1894
|
+
if (existing) {
|
|
1895
|
+
try {
|
|
1896
|
+
await existing.destroy();
|
|
1897
|
+
}
|
|
1898
|
+
catch { }
|
|
1730
1899
|
}
|
|
1731
|
-
|
|
1732
|
-
this.sessions.delete(sessionId);
|
|
1900
|
+
this._forgetWarmSession(sessionId);
|
|
1733
1901
|
}
|
|
1734
1902
|
/**
|
|
1735
1903
|
* Dehydrate a session: snapshot to the session store, release in-memory state.
|
|
@@ -1785,13 +1953,13 @@ export class SessionManager {
|
|
|
1785
1953
|
try {
|
|
1786
1954
|
emitSessionManagerTrace(sessionId, `destroy attempt ${attempt}/${DESTROY_MAX_RETRIES}`, { trace });
|
|
1787
1955
|
await session.destroy();
|
|
1788
|
-
this.
|
|
1956
|
+
this._forgetWarmSession(sessionId);
|
|
1789
1957
|
emitSessionManagerTrace(sessionId, `destroy complete on attempt ${attempt}/${DESTROY_MAX_RETRIES}`, { trace });
|
|
1790
1958
|
break; // Success
|
|
1791
1959
|
}
|
|
1792
1960
|
catch (err) {
|
|
1793
1961
|
lastDestroyError = normalizeError(err);
|
|
1794
|
-
this.
|
|
1962
|
+
this._forgetWarmSession(sessionId); // Remove broken session from map
|
|
1795
1963
|
emitSessionManagerTrace(sessionId, `destroy failed on attempt ${attempt}/${DESTROY_MAX_RETRIES} error=${lastDestroyError.message}`, { trace, level: "warn" });
|
|
1796
1964
|
if (attempt < DESTROY_MAX_RETRIES) {
|
|
1797
1965
|
// Re-create the session from local files so we can try destroy again.
|
|
@@ -1952,10 +2120,11 @@ export class SessionManager {
|
|
|
1952
2120
|
return this._withSessionLock(sessionId, "destroySession", () => this.destroySession(sessionId, { lockHeld: true }));
|
|
1953
2121
|
}
|
|
1954
2122
|
const session = this.sessions.get(sessionId);
|
|
1955
|
-
if (session)
|
|
2123
|
+
if (session)
|
|
1956
2124
|
await session.destroy();
|
|
1957
|
-
|
|
1958
|
-
|
|
2125
|
+
this._forgetWarmSession(sessionId);
|
|
2126
|
+
this.sessionApplicationTools.delete(sessionId);
|
|
2127
|
+
this.sessionConfigs.delete(sessionId);
|
|
1959
2128
|
}
|
|
1960
2129
|
/**
|
|
1961
2130
|
* Drop the warm in-memory session handle without deleting any persisted
|
|
@@ -1973,7 +2142,7 @@ export class SessionManager {
|
|
|
1973
2142
|
await session.destroy();
|
|
1974
2143
|
}
|
|
1975
2144
|
catch { }
|
|
1976
|
-
this.
|
|
2145
|
+
this._forgetWarmSession(sessionId);
|
|
1977
2146
|
}
|
|
1978
2147
|
/**
|
|
1979
2148
|
* Fully reset a session's live and persisted Copilot state.
|
|
@@ -2002,23 +2171,51 @@ export class SessionManager {
|
|
|
2002
2171
|
activeSessionIds() {
|
|
2003
2172
|
return [...this.sessions.keys()];
|
|
2004
2173
|
}
|
|
2005
|
-
/**
|
|
2174
|
+
/** Final worker cleanup, after the durable runtime has drained. */
|
|
2006
2175
|
async shutdown() {
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2176
|
+
this.unsubscribeFeatureFlags?.();
|
|
2177
|
+
this.unsubscribeFeatureFlags = null;
|
|
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();
|
|
2013
2184
|
this.sessions.clear();
|
|
2185
|
+
this.sessionAgentCopies.clear();
|
|
2186
|
+
this.sessionBindingFingerprints.clear();
|
|
2187
|
+
this.sessionApplicationTools.clear();
|
|
2188
|
+
this.sessionConfigs.clear();
|
|
2014
2189
|
this.sessionClientKeys.clear();
|
|
2015
|
-
|
|
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;
|
|
2016
2195
|
try {
|
|
2017
|
-
await
|
|
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];
|
|
2018
2204
|
}
|
|
2019
|
-
catch {
|
|
2020
|
-
|
|
2021
|
-
|
|
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
|
+
}));
|
|
2022
2219
|
}
|
|
2023
2220
|
/**
|
|
2024
2221
|
* Resolve tools from per-session config + worker-level registry.
|
|
@@ -2037,15 +2234,42 @@ export class SessionManager {
|
|
|
2037
2234
|
: undefined;
|
|
2038
2235
|
if (serializableConfig.toolNames?.length) {
|
|
2039
2236
|
for (const name of serializableConfig.toolNames) {
|
|
2040
|
-
const
|
|
2041
|
-
|
|
2042
|
-
|
|
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);
|
|
2043
2249
|
if (tool)
|
|
2044
2250
|
registryTools.push(tool);
|
|
2045
2251
|
}
|
|
2046
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
|
+
});
|
|
2047
2271
|
const combined = [
|
|
2048
|
-
...
|
|
2272
|
+
...storedTools,
|
|
2049
2273
|
...registryTools,
|
|
2050
2274
|
];
|
|
2051
2275
|
// Deduplicate by name — per-session tools take precedence
|
|
@@ -2177,18 +2401,17 @@ export class SessionManager {
|
|
|
2177
2401
|
},
|
|
2178
2402
|
};
|
|
2179
2403
|
}
|
|
2180
|
-
_buildLastInstructionsSection(sessionId, initialConfig) {
|
|
2404
|
+
_buildLastInstructionsSection(sessionId, initialConfig, initialAgentCopy) {
|
|
2181
2405
|
return {
|
|
2182
2406
|
action: async (currentContent) => {
|
|
2183
2407
|
const latest = this.sessionConfigs.get(sessionId) ?? initialConfig;
|
|
2184
2408
|
const runtimeContext = extractPromptContent(latest.systemMessage);
|
|
2185
|
-
//
|
|
2186
|
-
//
|
|
2187
|
-
//
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
: 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;
|
|
2192
2415
|
// Orchestration ≥1.0.71 delivers the per-turn note inside the
|
|
2193
2416
|
// user turn (see prompt-system-context.ts) and flags it. For
|
|
2194
2417
|
// those turns the note must NOT land here: this section is
|
|
@@ -2209,6 +2432,10 @@ export class SessionManager {
|
|
|
2209
2432
|
runtimeContext,
|
|
2210
2433
|
ownSkillsIndex,
|
|
2211
2434
|
latest.systemContextInPrompt ? undefined : latest.turnSystemPrompt,
|
|
2435
|
+
// The SDK ignores sibling `content` when `action` is a
|
|
2436
|
+
// transform callback. Include worker guidance in the actual
|
|
2437
|
+
// rendered section, using the current session policy.
|
|
2438
|
+
latest.nativeSubagents === "sync" ? nativeSubagentGuidance() : undefined,
|
|
2212
2439
|
]);
|
|
2213
2440
|
return this._notePromptSection(sessionId, "last_instructions", mergePromptSections([currentContent, overlay]) ?? currentContent);
|
|
2214
2441
|
},
|
|
@@ -2365,6 +2592,20 @@ export class SessionManager {
|
|
|
2365
2592
|
return false;
|
|
2366
2593
|
}
|
|
2367
2594
|
}
|
|
2595
|
+
async _resolveFeatureViewer(sessionId) {
|
|
2596
|
+
// Never use the inspect-viewer TTL for feature mutations: demotion must
|
|
2597
|
+
// apply on the next invocation, including an already-hydrated session.
|
|
2598
|
+
const row = await this.sessionCatalog?.getSession(sessionId);
|
|
2599
|
+
// CMS deliberately leaves worker-provisioned system sessions ownerless.
|
|
2600
|
+
// Their persisted service classification supplies the same identity used
|
|
2601
|
+
// by credential and inspect resolution; an agent name grants nothing.
|
|
2602
|
+
const principal = row?.owner ?? (row?.isSystem === true ? SYSTEM_USER_PRINCIPAL : null);
|
|
2603
|
+
if (!principal?.provider || !principal.subject)
|
|
2604
|
+
throw new FeatureFlagError("FEATURE_FORBIDDEN", "Feature tools require an authenticated session owner", 403);
|
|
2605
|
+
const system = row?.isSystem === true && principal.provider === SYSTEM_USER_PRINCIPAL.provider
|
|
2606
|
+
&& principal.subject === SYSTEM_USER_PRINCIPAL.subject;
|
|
2607
|
+
return { principal, isAdmin: system || await this._resolveOwnerIsAdmin(principal) };
|
|
2608
|
+
}
|
|
2368
2609
|
/**
|
|
2369
2610
|
* A one-line-per-skill index of the private skills this session's owner
|
|
2370
2611
|
* published, or undefined when there are none. Mirrors the wording of the
|
|
@@ -2425,17 +2666,17 @@ export class SessionManager {
|
|
|
2425
2666
|
const names = new Set(own.map((s) => s.name));
|
|
2426
2667
|
return [...own, ...shared.filter((s) => !names.has(s.name))];
|
|
2427
2668
|
}
|
|
2428
|
-
_buildSystemMessage(sessionId, config, sessionOwnerKey = null) {
|
|
2669
|
+
_buildSystemMessage(sessionId, config, sessionOwnerKey = null, boundAgentCopy = resolveBoundAgentCopy(this.workerDefaults, config, sessionOwnerKey) ?? null) {
|
|
2429
2670
|
const frameworkBase = this.workerDefaults.frameworkBasePrompt ?? this.workerDefaults.systemMessage;
|
|
2430
2671
|
const boundAgentName = config.boundAgentName;
|
|
2431
2672
|
const layerKind = config.promptLayering?.kind ?? (boundAgentName ? "app-agent" : undefined);
|
|
2432
2673
|
const knowledgeToolInstructions = this._buildKnowledgeToolInstructionsSection(sessionId, config.agentIdentity);
|
|
2433
|
-
const lastInstructions = this._buildLastInstructionsSection(sessionId, config);
|
|
2674
|
+
const lastInstructions = this._buildLastInstructionsSection(sessionId, config, boundAgentCopy);
|
|
2434
2675
|
const additionalSections = knowledgeToolInstructions
|
|
2435
2676
|
? { tool_instructions: knowledgeToolInstructions, last_instructions: lastInstructions }
|
|
2436
2677
|
: { last_instructions: lastInstructions };
|
|
2437
2678
|
const isPilotSwarmSystemAgent = layerKind === "pilotswarm-system-agent";
|
|
2438
|
-
const layerManifest = buildEffectivePromptLayers(this.workerDefaults, config, sessionOwnerKey);
|
|
2679
|
+
const layerManifest = buildEffectivePromptLayers(this.workerDefaults, config, sessionOwnerKey, boundAgentCopy);
|
|
2439
2680
|
return composeStructuredSystemMessage({
|
|
2440
2681
|
frameworkBase,
|
|
2441
2682
|
appDefault: isPilotSwarmSystemAgent
|