@borgee/agents-host 0.2.44 → 0.2.56

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 (56) hide show
  1. package/README.md +23 -27
  2. package/dist/agents-host.d.ts +26 -5
  3. package/dist/agents-host.js +163 -192
  4. package/dist/chat/chat-control-plane.d.ts +3 -0
  5. package/dist/chat/sdk-chat-control-plane.d.ts +4 -3
  6. package/dist/chat/sdk-chat-control-plane.js +3 -0
  7. package/dist/cli.js +1 -5
  8. package/dist/compatibility-gates.d.ts +4 -0
  9. package/dist/compatibility-gates.js +18 -1
  10. package/dist/context/claude-file-brief.d.ts +2 -0
  11. package/dist/context/claude-file-brief.js +83 -0
  12. package/dist/context/compaction.d.ts +20 -0
  13. package/dist/context/compaction.js +59 -0
  14. package/dist/context/injection.d.ts +39 -6
  15. package/dist/context/injection.js +317 -26
  16. package/dist/context/main-session-delegation.d.ts +1 -1
  17. package/dist/context/projection-strategy.d.ts +24 -0
  18. package/dist/context/projection-strategy.js +90 -0
  19. package/dist/context/prompt.d.ts +16 -1
  20. package/dist/context/prompt.js +456 -22
  21. package/dist/context/resolved-workspace.d.ts +2 -0
  22. package/dist/context/resolved-workspace.js +64 -0
  23. package/dist/context/skill-manual.d.ts +1 -0
  24. package/dist/context/skill-manual.js +4 -1
  25. package/dist/context/turn-preparation.d.ts +8 -2
  26. package/dist/context/turn-preparation.js +56 -14
  27. package/dist/gateway/localhost-gateway.js +2 -0
  28. package/dist/managed-daemon.js +122 -9
  29. package/dist/plugin-sdk.js +276 -359
  30. package/dist/plugin-sdk.js.map +4 -4
  31. package/dist/progress-to-activity.d.ts +16 -0
  32. package/dist/progress-to-activity.js +24 -0
  33. package/dist/projection-strategy-values.d.ts +4 -0
  34. package/dist/projection-strategy-values.js +28 -0
  35. package/dist/providers/acp-progress-collector.d.ts +44 -0
  36. package/dist/providers/acp-progress-collector.js +130 -0
  37. package/dist/providers/awaiting-user.d.ts +2 -3
  38. package/dist/providers/awaiting-user.js +5 -7
  39. package/dist/providers/claude/activity-metadata.d.ts +14 -0
  40. package/dist/providers/claude/activity-metadata.js +81 -0
  41. package/dist/providers/claude/cli-client.d.ts +1 -2
  42. package/dist/providers/claude/cli-client.js +190 -117
  43. package/dist/providers/codex/cli-client.js +3 -83
  44. package/dist/providers/codex/project-doc.js +12 -11
  45. package/dist/providers/copilot/cli-client.js +3 -83
  46. package/dist/providers/create-provider.d.ts +2 -0
  47. package/dist/providers/create-provider.js +20 -4
  48. package/dist/state-paths.d.ts +1 -1
  49. package/dist/state-paths.js +3 -3
  50. package/dist/task-thread-resolution.d.ts +3 -2
  51. package/dist/types.d.ts +130 -6
  52. package/package.json +2 -2
  53. package/skills/borgee-agent/SKILL.md +9 -1
  54. package/skills/borgee-agent/references/task-properties.md +5 -2
  55. package/dist/durable-cursor-store.d.ts +0 -5
  56. package/dist/durable-cursor-store.js +0 -7
@@ -1,9 +1,15 @@
1
1
  import { type DebugLogger } from '../debug.js';
2
- import type { PreparedProviderTurnInput, ProviderInput } from '../types.js';
2
+ import type { ProjectionStrategy, PreparedProviderTurnInput, ProviderInput } from '../types.js';
3
3
  import { type ChannelContextStore } from './injection.js';
4
+ type ResolveProjectionStrategy = (input: ProviderInput) => Promise<ProjectionStrategy>;
4
5
  export declare class ProviderTurnPreparer {
5
6
  private readonly channelContextStore?;
6
7
  private readonly logger;
7
- constructor(channelContextStore?: ChannelContextStore | undefined, logger?: DebugLogger);
8
+ private readonly resolveProjectionStrategy;
9
+ constructor(channelContextStore?: ChannelContextStore | undefined, logger?: DebugLogger, options?: {
10
+ projectionStrategy?: ProjectionStrategy;
11
+ resolveProjectionStrategy?: ResolveProjectionStrategy;
12
+ });
8
13
  prepare(input: ProviderInput): Promise<PreparedProviderTurnInput>;
9
14
  }
15
+ export {};
@@ -1,7 +1,8 @@
1
1
  import { HostLogger, summarizeError } from '../debug.js';
2
+ import { DEFAULT_PROJECTION_STRATEGY } from '../projection-strategy-values.js';
2
3
  import { buildHostedIncomingContentText, buildHostedTurnContentParts } from '../hosted-turn-content.js';
3
- import { ChannelContextPreparationError, } from './injection.js';
4
- import { buildPrompt } from './prompt.js';
4
+ import { buildRuntimeSurfaceForTurn, ChannelContextPreparationError, } from './injection.js';
5
+ import { buildClaudeSessionPromptAppend, buildClaudeTurnPrompt, buildPrompt } from './prompt.js';
5
6
  function providerLabel(provider) {
6
7
  if (provider === 'copilot') {
7
8
  return 'Copilot';
@@ -16,26 +17,36 @@ function toPromptContext(channelContext, input) {
16
17
  && !input.collaboration
17
18
  && !input.collaborationOutcome
18
19
  && !input.attentionSnapshot
20
+ && !input.compactionSnapshot
19
21
  && !input.collaborationCapabilities
20
22
  && !input.missedCollaborationDiagnostic
21
23
  && !input.taskThreadCollaborationContract) {
22
24
  return undefined;
23
25
  }
26
+ const runtimeSurface = channelContext?.payload.runtimeSurface ?? buildRuntimeSurfaceForTurn({
27
+ localhostGateway: channelContext?.payload.localhostGateway ?? channelContext?.localhostGateway,
28
+ collaboration: input.collaboration,
29
+ taskAssignmentContext: channelContext?.payload.taskAssignmentContext,
30
+ });
24
31
  return {
25
32
  ...(channelContext
26
33
  ? {
27
34
  channelContextPayloadPath: channelContext.payloadPath,
35
+ claudeProjectedBriefPath: channelContext.claudeProjectedBriefPath,
36
+ claudeProjectedBriefHash: channelContext.claudeProjectedBriefHash,
28
37
  gatewayCredentialPath: channelContext.gatewayCredentialPath,
29
38
  skillRuntime: channelContext.skillRuntime,
30
39
  localhostGateway: channelContext.localhostGateway,
31
40
  taskAssignmentContext: channelContext.payload.taskAssignmentContext,
32
- taskWorkspace: channelContext.taskWorkspace,
41
+ resolvedWorkspace: channelContext.resolvedWorkspace,
33
42
  }
34
43
  : {}),
44
+ runtimeSurface,
35
45
  collaborationTurnExecutionId: input.collaboration?.turnExecutionId,
36
46
  collaborationTurnMode: input.collaboration?.turnMode,
37
47
  collaborationOutcome: input.collaborationOutcome ?? channelContext?.payload.collaborationOutcome,
38
48
  attentionSnapshot: input.attentionSnapshot ?? channelContext?.payload.attentionSnapshot,
49
+ compactionSnapshot: input.compactionSnapshot ?? channelContext?.payload.compactionSnapshot,
39
50
  ...(() => {
40
51
  const collaborationCapabilities = input.collaborationCapabilities ?? channelContext?.payload.collaborationCapabilities;
41
52
  return collaborationCapabilities ? { collaborationCapabilities } : {};
@@ -71,9 +82,12 @@ function resolveProviderSessionRouting(input) {
71
82
  export class ProviderTurnPreparer {
72
83
  channelContextStore;
73
84
  logger;
74
- constructor(channelContextStore, logger = new HostLogger()) {
85
+ resolveProjectionStrategy;
86
+ constructor(channelContextStore, logger = new HostLogger(), options = {}) {
75
87
  this.channelContextStore = channelContextStore;
76
88
  this.logger = logger;
89
+ this.resolveProjectionStrategy = options.resolveProjectionStrategy
90
+ ?? (async () => options.projectionStrategy ?? DEFAULT_PROJECTION_STRATEGY);
77
91
  }
78
92
  async prepare(input) {
79
93
  const incomingParts = input.incomingParts ?? buildHostedTurnContentParts({
@@ -82,11 +96,16 @@ export class ProviderTurnPreparer {
82
96
  const incomingContent = incomingParts.length > 0
83
97
  ? buildHostedIncomingContentText(incomingParts)
84
98
  : input.incomingContent;
99
+ const projectionStrategy = input.provider === 'claude'
100
+ ? await this.resolveProjectionStrategy(input)
101
+ : undefined;
85
102
  let channelContext;
86
103
  if (this.channelContextStore) {
87
104
  try {
88
105
  const prepareInput = {
89
106
  channelId: input.channelId,
107
+ provider: input.provider,
108
+ projectionStrategy,
90
109
  collaboration: input.collaboration
91
110
  ? {
92
111
  ...input.collaboration,
@@ -96,6 +115,7 @@ export class ProviderTurnPreparer {
96
115
  : undefined,
97
116
  collaborationOutcome: input.collaborationOutcome,
98
117
  attentionSnapshot: input.attentionSnapshot,
118
+ compactionSnapshot: input.compactionSnapshot,
99
119
  collaborationCapabilities: input.collaborationCapabilities,
100
120
  missedCollaborationDiagnostic: input.missedCollaborationDiagnostic,
101
121
  ...(input.taskThreadCollaborationContract
@@ -122,21 +142,43 @@ export class ProviderTurnPreparer {
122
142
  }
123
143
  const promptContext = toPromptContext(channelContext, input);
124
144
  return {
145
+ agentName: input.agentName,
125
146
  channelId: input.channelId,
126
147
  incomingContent,
127
148
  incomingParts,
128
149
  incomingEventKind: input.incomingEventKind,
129
150
  incomingMessageType: input.incomingMessageType,
130
- prompt: buildPrompt({
131
- agentName: input.agentName,
132
- provider: input.provider,
133
- channelId: input.channelId,
134
- incomingAuthorId: input.incomingAuthorId,
135
- incomingContent,
136
- incomingEventKind: input.incomingEventKind,
137
- incomingMessageType: input.incomingMessageType,
138
- promptContext,
139
- }),
151
+ prompt: input.provider === 'claude'
152
+ ? buildClaudeTurnPrompt({
153
+ agentName: input.agentName,
154
+ channelId: input.channelId,
155
+ incomingAuthorId: input.incomingAuthorId,
156
+ incomingContent,
157
+ incomingEventKind: input.incomingEventKind,
158
+ incomingMessageType: input.incomingMessageType,
159
+ promptContext,
160
+ promptStrategy: projectionStrategy,
161
+ })
162
+ : buildPrompt({
163
+ agentName: input.agentName,
164
+ provider: input.provider,
165
+ channelId: input.channelId,
166
+ incomingAuthorId: input.incomingAuthorId,
167
+ incomingContent,
168
+ incomingEventKind: input.incomingEventKind,
169
+ incomingMessageType: input.incomingMessageType,
170
+ promptContext,
171
+ }),
172
+ ...(input.provider === 'claude'
173
+ ? {
174
+ claudeSessionPromptAppend: buildClaudeSessionPromptAppend({
175
+ agentName: input.agentName,
176
+ promptContext,
177
+ promptStrategy: projectionStrategy,
178
+ }),
179
+ projectionStrategy,
180
+ }
181
+ : {}),
140
182
  promptContext,
141
183
  providerSessionRouting: resolveProviderSessionRouting(input),
142
184
  };
@@ -910,6 +910,8 @@ function mapGatewayControlPlaneError(error, fallbackReason) {
910
910
  // is broken, and it would retry the same bad call instead of correcting it.
911
911
  case 'bpp.task_property_key_unknown':
912
912
  return new GatewayHttpError(400, { error: 'unknown_property_key' }, 'bad-request');
913
+ case 'bpp.task_property_value_invalid':
914
+ return new GatewayHttpError(400, { error: 'invalid_property_value' }, 'bad-request');
913
915
  case 'bpp.task_property_value_too_long':
914
916
  return new GatewayHttpError(400, { error: 'property_value_too_long' }, 'bad-request');
915
917
  case 'bpp.task_invalid_status':
@@ -2,13 +2,15 @@ import { createHash, randomUUID } from 'node:crypto';
2
2
  import { promises as fs } from 'node:fs';
3
3
  import { spawn } from 'node:child_process';
4
4
  import { createConnection, createServer } from 'node:net';
5
- import { dirname, join, resolve } from 'node:path';
5
+ import { dirname, isAbsolute, join, resolve } from 'node:path';
6
6
  import { fileURLToPath } from 'node:url';
7
7
  import { AgentsHostSupervisor } from './agents-host-supervisor.js';
8
- import { CLAUDE_PROVIDER_V2_COMPATIBILITY_GATE, COMPATIBILITY_GATES_ENV, createManagedRuntimeSettingsFingerprint, INTERNAL_DISABLED_COMPATIBILITY_GATES_ENV, INTERNAL_POLICY_MODE_ENV, INTERNAL_PROVIDER_IMPLEMENTATIONS_ENV, MANAGED_RUNTIME_CONVERGENCE_COMPATIBILITY_GATE, MANAGED_RUNTIME_SETTINGS_SCHEMA_VERSION, resolveDisabledDefaultCompatibilityGates, resolveManagedRuntimeSettingsSnapshot, normalizeInternalProviderImplementationOverrides, } from './compatibility-gates.js';
8
+ import { DEFAULT_PROJECTION_STRATEGY, normalizeProjectionStrategy, } from './projection-strategy-values.js';
9
+ import { resolveChannelWorkspaceDirectory, resolveChannelWorkspaceRootDirectory, resolveManagedWorkspaceCollectionRoot, resolveTaskWorkspaceDirectory, } from './context/injection.js';
10
+ import { CLAUDE_PROVIDER_V2_COMPATIBILITY_GATE, COMPATIBILITY_GATES_ENV, createManagedRuntimeSettingsFingerprint, INTERNAL_CLAUDE_PROMPT_STRATEGY_ENV, INTERNAL_DISABLED_COMPATIBILITY_GATES_ENV, INTERNAL_POLICY_MODE_ENV, INTERNAL_PROVIDER_IMPLEMENTATIONS_ENV, MANAGED_RUNTIME_CONVERGENCE_COMPATIBILITY_GATE, MANAGED_RUNTIME_SETTINGS_SCHEMA_VERSION, resolveDisabledDefaultCompatibilityGates, resolveManagedRuntimeSettingsSnapshot, serializeManagedRuntimeSettingsSnapshot, normalizeInternalProviderImplementationOverrides, } from './compatibility-gates.js';
9
11
  import { DEFAULT_PROVIDER_COMMAND_CONFIG, hasLegacyClaudeOneShotArgs, loadConfigFromEnv, } from './config.js';
10
12
  import { loadLocalConfigGenerateSpec, materializeLocalConfig, parseGenerateConfigSpec, resolveLocalConfigLayout, } from './local-config.js';
11
- import { normalizeManagedRuntimeKey, resolveManagedBootstrapLockPath, resolveManagedDaemonEndpoint, resolveManagedDaemonLogPath, resolveManagedRuntimeRoot, resolveManagedRuntimeSettingsPath, } from './state-paths.js';
13
+ import { normalizeManagedRuntimeKey, resolveManagedBootstrapLockPath, resolveManagedDaemonEndpoint, resolveManagedDaemonLogPath, resolveManagedRuntimeRoot, resolveManagedStateRoot, resolveManagedRuntimeSettingsPath, } from './state-paths.js';
12
14
  const MANAGED_ROOT_MODE = 0o700;
13
15
  const MANAGED_CONFIG_MODE = 0o600;
14
16
  const BOOTSTRAP_LOCK_WAIT_MS = 10_000;
@@ -48,6 +50,9 @@ function isAlreadyExistsError(error) {
48
50
  function isSocketBusyError(error) {
49
51
  return isNodeErrorWithCode(error, 'EADDRINUSE');
50
52
  }
53
+ function isRecord(value) {
54
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
55
+ }
51
56
  function delay(milliseconds) {
52
57
  return new Promise((resolveDelay) => {
53
58
  setTimeout(resolveDelay, milliseconds);
@@ -157,6 +162,7 @@ function normalizeManagedRuntimeSettingsSnapshot(snapshot) {
157
162
  compatibilityGates,
158
163
  providerImplementationOverrides,
159
164
  internalPolicyMode: snapshot.internalPolicyMode,
165
+ projectionStrategy: snapshot.projectionStrategy ?? DEFAULT_PROJECTION_STRATEGY,
160
166
  };
161
167
  }
162
168
  function parseManagedRuntimeSettingsSnapshotRecord(value, sourceLabel) {
@@ -178,11 +184,19 @@ function parseManagedRuntimeSettingsSnapshotRecord(value, sourceLabel) {
178
184
  if (record.internalPolicyMode !== 'audit-only' && record.internalPolicyMode !== 'enforce') {
179
185
  throw new Error(`Invalid ${sourceLabel}: expected internalPolicyMode to be "audit-only" or "enforce"`);
180
186
  }
187
+ const rawProjectionStrategy = typeof record.projectionStrategy === 'string'
188
+ ? record.projectionStrategy
189
+ : record.claudePromptStrategy;
190
+ const projectionStrategy = normalizeProjectionStrategy(rawProjectionStrategy);
191
+ if (rawProjectionStrategy !== undefined && !projectionStrategy) {
192
+ throw new Error(`Invalid ${sourceLabel}: expected projectionStrategy to be "session-brief", "turn-full", "message-only", "turn-thin", or "file-brief"`);
193
+ }
181
194
  return normalizeManagedRuntimeSettingsSnapshot({
182
195
  schemaVersion: MANAGED_RUNTIME_SETTINGS_SCHEMA_VERSION,
183
196
  compatibilityGates: [...record.compatibilityGates],
184
197
  providerImplementationOverrides: [...record.providerImplementationOverrides],
185
198
  internalPolicyMode: record.internalPolicyMode,
199
+ projectionStrategy: projectionStrategy ?? DEFAULT_PROJECTION_STRATEGY,
186
200
  });
187
201
  }
188
202
  async function loadManagedRuntimeSettingsSnapshot(rootPath) {
@@ -205,7 +219,7 @@ async function writeManagedRuntimeSettingsSnapshot(rootPath, snapshot) {
205
219
  await ensureDirectory(rootPath, MANAGED_ROOT_MODE);
206
220
  const settingsPath = resolveManagedRuntimeSettingsPath(rootPath);
207
221
  const normalizedSnapshot = normalizeManagedRuntimeSettingsSnapshot(snapshot);
208
- await fs.writeFile(settingsPath, `${JSON.stringify(normalizedSnapshot)}\n`, {
222
+ await fs.writeFile(settingsPath, `${serializeManagedRuntimeSettingsSnapshot(normalizedSnapshot)}\n`, {
209
223
  encoding: 'utf8',
210
224
  mode: MANAGED_CONFIG_MODE,
211
225
  });
@@ -226,6 +240,7 @@ function buildManagedRuntimeSettingsEnv(baseEnv, snapshot) {
226
240
  [INTERNAL_DISABLED_COMPATIBILITY_GATES_ENV]: resolveDisabledDefaultCompatibilityGates(snapshot.compatibilityGates).join(','),
227
241
  [INTERNAL_PROVIDER_IMPLEMENTATIONS_ENV]: snapshot.providerImplementationOverrides.join(','),
228
242
  [INTERNAL_POLICY_MODE_ENV]: snapshot.internalPolicyMode,
243
+ [INTERNAL_CLAUDE_PROMPT_STRATEGY_ENV]: snapshot.projectionStrategy,
229
244
  };
230
245
  }
231
246
  function buildDesiredManagedRuntimeSettingsFromSnapshot(snapshot) {
@@ -819,6 +834,101 @@ async function shutdownManagedDaemon(rootPath, sendRequest, waitForShutdown) {
819
834
  }
820
835
  await waitForShutdown(rootPath);
821
836
  }
837
+ function resolveTrustedPersistedChannelWorkspaceRoot(workspaceCollectionRoot, owningChannelId, rootPath) {
838
+ if (!owningChannelId || !isAbsolute(rootPath)) {
839
+ return null;
840
+ }
841
+ const expectedWorkspacePath = resolveChannelWorkspaceDirectory(workspaceCollectionRoot, owningChannelId);
842
+ if (rootPath !== expectedWorkspacePath) {
843
+ return null;
844
+ }
845
+ return resolveChannelWorkspaceRootDirectory(workspaceCollectionRoot, owningChannelId);
846
+ }
847
+ async function discoverManagedWorkspaceRootsForPurge(rootPath, processEnv) {
848
+ const trustedWorkspaceCollectionRoots = new Set([
849
+ resolveManagedWorkspaceCollectionRoot(undefined, processEnv),
850
+ ]);
851
+ const managedStateRoot = resolveManagedStateRoot(rootPath);
852
+ const agentStateEntries = await fs.readdir(managedStateRoot, { withFileTypes: true }).catch((error) => {
853
+ if (isNotFoundError(error)) {
854
+ return [];
855
+ }
856
+ throw error;
857
+ });
858
+ const workspaceRoots = new Set();
859
+ for (const agentStateEntry of agentStateEntries) {
860
+ if (!agentStateEntry.isDirectory()) {
861
+ continue;
862
+ }
863
+ const channelContextRoot = join(managedStateRoot, agentStateEntry.name, 'channel-context');
864
+ const channelEntries = await fs.readdir(channelContextRoot, { withFileTypes: true }).catch((error) => {
865
+ if (isNotFoundError(error)) {
866
+ return [];
867
+ }
868
+ throw error;
869
+ });
870
+ for (const channelEntry of channelEntries) {
871
+ if (!channelEntry.isDirectory()) {
872
+ continue;
873
+ }
874
+ const payloadPath = join(channelContextRoot, channelEntry.name, 'context.json');
875
+ const payload = await fs.readFile(payloadPath, 'utf8').catch((error) => {
876
+ if (isNotFoundError(error)) {
877
+ return undefined;
878
+ }
879
+ throw error;
880
+ });
881
+ if (!payload) {
882
+ continue;
883
+ }
884
+ let parsed;
885
+ try {
886
+ parsed = JSON.parse(payload);
887
+ }
888
+ catch {
889
+ continue;
890
+ }
891
+ if (!isRecord(parsed) || typeof parsed.channelId !== 'string' || !isRecord(parsed.resolvedWorkspace)) {
892
+ continue;
893
+ }
894
+ const resolvedWorkspace = parsed.resolvedWorkspace;
895
+ if ((resolvedWorkspace.authority !== 'channel' && resolvedWorkspace.authority !== 'task')
896
+ || typeof resolvedWorkspace.rootPath !== 'string'
897
+ || !isAbsolute(resolvedWorkspace.rootPath)) {
898
+ continue;
899
+ }
900
+ for (const workspaceCollectionRoot of trustedWorkspaceCollectionRoots) {
901
+ if (resolvedWorkspace.authority === 'channel') {
902
+ if (typeof resolvedWorkspace.owningChannelId !== 'string'
903
+ || resolvedWorkspace.owningChannelId.length === 0) {
904
+ continue;
905
+ }
906
+ const trustedChannelWorkspaceRoot = resolveTrustedPersistedChannelWorkspaceRoot(workspaceCollectionRoot, resolvedWorkspace.owningChannelId, resolvedWorkspace.rootPath);
907
+ if (!trustedChannelWorkspaceRoot) {
908
+ continue;
909
+ }
910
+ workspaceRoots.add(trustedChannelWorkspaceRoot);
911
+ break;
912
+ }
913
+ const expectedWorkspacePath = resolvedWorkspace.authority === 'task'
914
+ ? typeof resolvedWorkspace.taskId === 'string' && resolvedWorkspace.taskId.length > 0
915
+ ? resolveTaskWorkspaceDirectory(workspaceCollectionRoot, parsed.channelId, resolvedWorkspace.taskId)
916
+ : null
917
+ : null;
918
+ if (!expectedWorkspacePath) {
919
+ continue;
920
+ }
921
+ if (resolvedWorkspace.rootPath !== expectedWorkspacePath) {
922
+ continue;
923
+ }
924
+ workspaceRoots.add(dirname(expectedWorkspacePath));
925
+ workspaceRoots.add(resolveChannelWorkspaceRootDirectory(workspaceCollectionRoot, parsed.channelId));
926
+ break;
927
+ }
928
+ }
929
+ }
930
+ return [...workspaceRoots].sort((left, right) => left.localeCompare(right));
931
+ }
822
932
  export async function cleanupManagedRuntime(options, deps = {}) {
823
933
  const sendRequest = deps.sendRequest ?? sendManagedDaemonRequest;
824
934
  const removeRuntimeRoot = deps.removeRuntimeRoot ??
@@ -855,6 +965,9 @@ export async function cleanupManagedRuntime(options, deps = {}) {
855
965
  await waitForShutdown(resolved.rootPath);
856
966
  }
857
967
  if (options.purge === true) {
968
+ for (const workspaceRoot of await discoverManagedWorkspaceRootsForPurge(resolved.rootPath, options.processEnv ?? process.env)) {
969
+ await fs.rm(workspaceRoot, { recursive: true, force: true });
970
+ }
858
971
  await removeRuntimeRoot(resolved.rootPath);
859
972
  }
860
973
  }
@@ -900,16 +1013,16 @@ export async function spawnManagedDaemonProcess(options, deps = {}) {
900
1013
  }
901
1014
  const thisPath = fileURLToPath(import.meta.url);
902
1015
  const cliEntrypointPath = join(dirname(thisPath), thisPath.endsWith('.ts') ? 'cli.ts' : 'cli.js');
1016
+ const baseEnv = {
1017
+ ...process.env,
1018
+ ...(options.processEnv ?? {}),
1019
+ };
903
1020
  return spawnDetachedProcess({
904
1021
  rootPath: options.rootPath,
905
1022
  command: process.execPath,
906
1023
  env: {
907
- ...buildManagedRuntimeSettingsEnv({
908
- ...process.env,
909
- ...(options.processEnv ?? {}),
910
- }, managedRuntimeSettingsSnapshot),
1024
+ ...buildManagedRuntimeSettingsEnv(baseEnv, managedRuntimeSettingsSnapshot),
911
1025
  AGENTS_HOST_MANAGED_DAEMON_LOG_PATH: resolveManagedDaemonLogPath(options.rootPath),
912
- AGENTS_HOST_INTERNAL_TASK_WORKSPACE_ROOT_DIR: options.rootPath,
913
1026
  },
914
1027
  args: [
915
1028
  ...process.execArgv,