@borgee/agents-host 0.2.35 → 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 (74) hide show
  1. package/README.md +23 -27
  2. package/dist/agents-host.d.ts +28 -5
  3. package/dist/agents-host.js +274 -236
  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 +6 -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 +44 -9
  15. package/dist/context/injection.js +360 -53
  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 +475 -53
  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 +14 -0
  24. package/dist/context/skill-manual.js +21 -0
  25. package/dist/context/turn-preparation.d.ts +8 -2
  26. package/dist/context/turn-preparation.js +68 -17
  27. package/dist/gateway/localhost-gateway.js +18 -8
  28. package/dist/hosted-turn-content.d.ts +15 -0
  29. package/dist/hosted-turn-content.js +50 -0
  30. package/dist/managed-daemon.d.ts +3 -2
  31. package/dist/managed-daemon.js +198 -37
  32. package/dist/plugin-sdk.js +276 -359
  33. package/dist/plugin-sdk.js.map +4 -4
  34. package/dist/progress-to-activity.d.ts +16 -0
  35. package/dist/progress-to-activity.js +24 -0
  36. package/dist/projection-strategy-values.d.ts +4 -0
  37. package/dist/projection-strategy-values.js +28 -0
  38. package/dist/providers/acp-progress-collector.d.ts +44 -0
  39. package/dist/providers/acp-progress-collector.js +130 -0
  40. package/dist/providers/awaiting-user.d.ts +2 -3
  41. package/dist/providers/awaiting-user.js +5 -7
  42. package/dist/providers/claude/activity-metadata.d.ts +14 -0
  43. package/dist/providers/claude/activity-metadata.js +81 -0
  44. package/dist/providers/claude/adapter.d.ts +3 -1
  45. package/dist/providers/claude/adapter.js +10 -0
  46. package/dist/providers/claude/cli-client.d.ts +9 -1
  47. package/dist/providers/claude/cli-client.js +270 -126
  48. package/dist/providers/codex/adapter.d.ts +3 -1
  49. package/dist/providers/codex/adapter.js +10 -0
  50. package/dist/providers/codex/cli-client.d.ts +10 -2
  51. package/dist/providers/codex/cli-client.js +88 -104
  52. package/dist/providers/codex/project-doc.js +22 -37
  53. package/dist/providers/copilot/adapter.d.ts +3 -1
  54. package/dist/providers/copilot/adapter.js +10 -0
  55. package/dist/providers/copilot/cli-client.d.ts +9 -1
  56. package/dist/providers/copilot/cli-client.js +68 -85
  57. package/dist/providers/create-provider.d.ts +3 -1
  58. package/dist/providers/create-provider.js +36 -9
  59. package/dist/providers/provider-adapter.d.ts +35 -0
  60. package/dist/providers/provider-adapter.js +44 -1
  61. package/dist/state-paths.d.ts +10 -2
  62. package/dist/state-paths.js +25 -6
  63. package/dist/task-thread-resolution.d.ts +3 -2
  64. package/dist/types.d.ts +163 -8
  65. package/package.json +2 -2
  66. package/skills/borgee-agent/SKILL.md +127 -38
  67. package/skills/borgee-agent/references/errors.md +38 -0
  68. package/skills/borgee-agent/references/task-properties.md +33 -0
  69. package/skills/borgee-agent/scripts/borgee-agent.mjs +553 -0
  70. package/skills/borgee-agent/scripts/borgee-agent.py +547 -0
  71. package/dist/durable-cursor-store.d.ts +0 -5
  72. package/dist/durable-cursor-store.js +0 -7
  73. package/skills/borgee-agent/borgee-agent.mjs +0 -562
  74. package/skills/borgee-agent/borgee-agent.py +0 -469
@@ -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, resolveManagedDaemonLogPath, resolveManagedDaemonSocketPath, 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) {
@@ -599,8 +614,11 @@ async function canConnectToSocket(socketPath) {
599
614
  socket.once('error', () => finalize(false));
600
615
  });
601
616
  }
602
- async function removeStaleControlSocket(socketPath) {
603
- const stats = await fs.lstat(socketPath).catch((error) => {
617
+ async function removeStaleControlSocket(endpoint) {
618
+ if (endpoint.kind === 'named-pipe') {
619
+ return false;
620
+ }
621
+ const stats = await fs.lstat(endpoint.address).catch((error) => {
604
622
  if (isNotFoundError(error)) {
605
623
  return undefined;
606
624
  }
@@ -610,15 +628,15 @@ async function removeStaleControlSocket(socketPath) {
610
628
  return false;
611
629
  }
612
630
  if (stats.isDirectory() || stats.isSymbolicLink()) {
613
- throw new Error(`Managed control socket path has an unexpected value: ${socketPath}`);
631
+ throw new Error(`Managed control socket path has an unexpected value: ${endpoint.address}`);
614
632
  }
615
- if (await canConnectToSocket(socketPath)) {
633
+ if (await canConnectToSocket(endpoint.address)) {
616
634
  return false;
617
635
  }
618
636
  if (!stats.isSocket() && !stats.isFile()) {
619
- throw new Error(`Managed control socket path has an unexpected value: ${socketPath}`);
637
+ throw new Error(`Managed control socket path has an unexpected value: ${endpoint.address}`);
620
638
  }
621
- await fs.rm(socketPath, { force: true });
639
+ await fs.rm(endpoint.address, { force: true });
622
640
  return true;
623
641
  }
624
642
  function parseDaemonResponse(raw) {
@@ -655,12 +673,12 @@ function isManagedDaemonConnectionError(error) {
655
673
  isNodeErrorWithCode(error, 'ECONNRESET'));
656
674
  }
657
675
  export async function sendManagedDaemonRequest(rootPath, request) {
658
- const socketPath = resolveManagedDaemonSocketPath(rootPath);
676
+ const endpoint = resolveManagedDaemonEndpoint(rootPath);
659
677
  const requestTimeoutMs = request.type === 'upsertAgent' || request.type === 'applySpec'
660
678
  ? DAEMON_RECONCILE_WAIT_MS
661
679
  : DAEMON_READY_WAIT_MS;
662
680
  return new Promise((resolveRequest, rejectRequest) => {
663
- const socket = createConnection(socketPath);
681
+ const socket = createConnection(endpoint.address);
664
682
  let settled = false;
665
683
  let responseBuffer = '';
666
684
  const rejectOnce = (error) => {
@@ -681,7 +699,7 @@ export async function sendManagedDaemonRequest(rootPath, request) {
681
699
  };
682
700
  socket.setEncoding('utf8');
683
701
  socket.setTimeout(requestTimeoutMs);
684
- socket.once('timeout', () => rejectOnce(new Error(`Timed out talking to managed daemon: ${socketPath}`)));
702
+ socket.once('timeout', () => rejectOnce(new Error(`Timed out talking to managed daemon: ${endpoint.address}`)));
685
703
  socket.once('error', rejectOnce);
686
704
  socket.on('data', (chunk) => {
687
705
  responseBuffer += chunk;
@@ -790,9 +808,9 @@ async function readReadyManagedDaemonStatus(rootPath, sendRequest) {
790
808
  }
791
809
  async function waitForManagedDaemonShutdown(rootPath, canConnect) {
792
810
  const deadline = Date.now() + DAEMON_READY_WAIT_MS;
793
- const socketPath = resolveManagedDaemonSocketPath(rootPath);
811
+ const endpoint = resolveManagedDaemonEndpoint(rootPath);
794
812
  while (Date.now() < deadline) {
795
- const listening = await canConnect(socketPath).catch((error) => {
813
+ const listening = await canConnect(endpoint.address).catch((error) => {
796
814
  if (isNodeErrorWithCode(error, 'ENOENT')) {
797
815
  return false;
798
816
  }
@@ -816,6 +834,101 @@ async function shutdownManagedDaemon(rootPath, sendRequest, waitForShutdown) {
816
834
  }
817
835
  await waitForShutdown(rootPath);
818
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
+ }
819
932
  export async function cleanupManagedRuntime(options, deps = {}) {
820
933
  const sendRequest = deps.sendRequest ?? sendManagedDaemonRequest;
821
934
  const removeRuntimeRoot = deps.removeRuntimeRoot ??
@@ -852,6 +965,9 @@ export async function cleanupManagedRuntime(options, deps = {}) {
852
965
  await waitForShutdown(resolved.rootPath);
853
966
  }
854
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
+ }
855
971
  await removeRuntimeRoot(resolved.rootPath);
856
972
  }
857
973
  }
@@ -897,16 +1013,16 @@ export async function spawnManagedDaemonProcess(options, deps = {}) {
897
1013
  }
898
1014
  const thisPath = fileURLToPath(import.meta.url);
899
1015
  const cliEntrypointPath = join(dirname(thisPath), thisPath.endsWith('.ts') ? 'cli.ts' : 'cli.js');
1016
+ const baseEnv = {
1017
+ ...process.env,
1018
+ ...(options.processEnv ?? {}),
1019
+ };
900
1020
  return spawnDetachedProcess({
901
1021
  rootPath: options.rootPath,
902
1022
  command: process.execPath,
903
1023
  env: {
904
- ...buildManagedRuntimeSettingsEnv({
905
- ...process.env,
906
- ...(options.processEnv ?? {}),
907
- }, managedRuntimeSettingsSnapshot),
1024
+ ...buildManagedRuntimeSettingsEnv(baseEnv, managedRuntimeSettingsSnapshot),
908
1025
  AGENTS_HOST_MANAGED_DAEMON_LOG_PATH: resolveManagedDaemonLogPath(options.rootPath),
909
- AGENTS_HOST_INTERNAL_TASK_WORKSPACE_ROOT_DIR: options.rootPath,
910
1026
  },
911
1027
  args: [
912
1028
  ...process.execArgv,
@@ -1481,7 +1597,7 @@ export async function applyManagedSpec(options, deps = {}) {
1481
1597
  export class ManagedAgentsHostDaemon {
1482
1598
  rootPath;
1483
1599
  hostConfigPath;
1484
- socketPath;
1600
+ endpoint;
1485
1601
  createSupervisor;
1486
1602
  supervisor;
1487
1603
  loadSpec;
@@ -1493,14 +1609,14 @@ export class ManagedAgentsHostDaemon {
1493
1609
  requestQueue = Promise.resolve();
1494
1610
  started = false;
1495
1611
  ready = false;
1496
- ownsSocket = false;
1612
+ ownsEndpoint = false;
1497
1613
  shutdownRequested = false;
1498
1614
  ownedSocketInode = null;
1499
1615
  constructor(rootPath, debug = false, deps = {}) {
1500
1616
  const layout = resolveLocalConfigLayout(rootPath);
1501
1617
  this.rootPath = layout.root;
1502
1618
  this.hostConfigPath = layout.hostConfigPath;
1503
- this.socketPath = resolveManagedDaemonSocketPath(this.rootPath);
1619
+ this.endpoint = resolveManagedDaemonEndpoint(this.rootPath, deps.platform);
1504
1620
  this.createSupervisor =
1505
1621
  deps.createSupervisor ??
1506
1622
  ((configPath, daemonDebug) => new AgentsHostSupervisor(configPath, { debug: daemonDebug }));
@@ -1535,22 +1651,34 @@ export class ManagedAgentsHostDaemon {
1535
1651
  this.logger.log('[agents-host] managed daemon ready', {
1536
1652
  rootPath: this.rootPath,
1537
1653
  hostConfigPath: this.hostConfigPath,
1538
- socketPath: this.socketPath,
1654
+ socketPath: this.endpoint.address,
1539
1655
  });
1540
1656
  }
1541
1657
  catch (error) {
1542
1658
  this.started = false;
1543
1659
  this.ready = false;
1544
- await this.stop().catch(() => undefined);
1660
+ try {
1661
+ await this.stop();
1662
+ }
1663
+ catch (cleanupError) {
1664
+ this.logger.error('[agents-host] managed daemon startup cleanup failed', {
1665
+ rootPath: this.rootPath,
1666
+ error: cleanupError,
1667
+ });
1668
+ }
1545
1669
  throw error;
1546
1670
  }
1547
1671
  }
1548
1672
  async stop() {
1549
1673
  this.ready = false;
1674
+ let stopError;
1550
1675
  try {
1551
1676
  await this.supervisor.stop();
1552
1677
  }
1553
- finally {
1678
+ catch (error) {
1679
+ stopError = error;
1680
+ }
1681
+ try {
1554
1682
  await new Promise((resolveStop) => {
1555
1683
  if (!this.server.listening) {
1556
1684
  resolveStop();
@@ -1558,17 +1686,38 @@ export class ManagedAgentsHostDaemon {
1558
1686
  }
1559
1687
  this.server.close(() => resolveStop());
1560
1688
  });
1561
- if (this.ownsSocket) {
1562
- const socketStats = await fs.lstat(this.socketPath).catch(() => undefined);
1689
+ if (this.ownsEndpoint && this.endpoint.kind === 'unix-socket') {
1690
+ const socketStats = await fs.lstat(this.endpoint.address).catch((error) => {
1691
+ if (isNotFoundError(error)) {
1692
+ return undefined;
1693
+ }
1694
+ throw error;
1695
+ });
1563
1696
  if (socketStats && socketStats.ino === this.ownedSocketInode) {
1564
- await fs.rm(this.socketPath, { force: true }).catch(() => undefined);
1697
+ await fs.rm(this.endpoint.address, { force: true });
1565
1698
  }
1566
- this.ownsSocket = false;
1567
- this.ownedSocketInode = null;
1568
1699
  }
1700
+ }
1701
+ catch (cleanupError) {
1702
+ if (stopError) {
1703
+ this.logger.error('[agents-host] managed daemon control endpoint cleanup failed', {
1704
+ rootPath: this.rootPath,
1705
+ error: cleanupError,
1706
+ });
1707
+ }
1708
+ else {
1709
+ stopError = cleanupError;
1710
+ }
1711
+ }
1712
+ finally {
1713
+ this.ownsEndpoint = false;
1714
+ this.ownedSocketInode = null;
1569
1715
  this.started = false;
1570
1716
  this.shutdownRequested = false;
1571
1717
  }
1718
+ if (stopError) {
1719
+ throw stopError;
1720
+ }
1572
1721
  }
1573
1722
  async listen() {
1574
1723
  for (let attempt = 0; attempt < 2; attempt++) {
@@ -1584,20 +1733,32 @@ export class ManagedAgentsHostDaemon {
1584
1733
  };
1585
1734
  this.server.once('error', onError);
1586
1735
  this.server.once('listening', onListening);
1587
- this.server.listen(this.socketPath);
1736
+ this.server.listen(this.endpoint.address);
1588
1737
  });
1589
- await fs.chmod(this.socketPath, MANAGED_CONFIG_MODE);
1590
- this.ownedSocketInode = (await fs.lstat(this.socketPath)).ino;
1591
- this.ownsSocket = true;
1738
+ if (this.endpoint.kind === 'unix-socket') {
1739
+ await fs.chmod(this.endpoint.address, MANAGED_CONFIG_MODE);
1740
+ this.ownedSocketInode = (await fs.lstat(this.endpoint.address)).ino;
1741
+ }
1742
+ this.ownsEndpoint = true;
1592
1743
  return;
1593
1744
  }
1594
1745
  catch (error) {
1595
- if (!isSocketBusyError(error) || !(await removeStaleControlSocket(this.socketPath))) {
1746
+ if (!isSocketBusyError(error)) {
1747
+ throw error;
1748
+ }
1749
+ if (this.endpoint.kind === 'named-pipe') {
1750
+ if (await canConnectToSocket(this.endpoint.address)) {
1751
+ throw error;
1752
+ }
1753
+ await delay(DAEMON_READY_RETRY_MS);
1754
+ continue;
1755
+ }
1756
+ if (!(await removeStaleControlSocket(this.endpoint))) {
1596
1757
  throw error;
1597
1758
  }
1598
1759
  }
1599
1760
  }
1600
- throw new Error(`Unable to bind managed daemon control socket: ${this.socketPath}`);
1761
+ throw new Error(`Unable to bind managed daemon control endpoint: ${this.endpoint.address}`);
1601
1762
  }
1602
1763
  async handleSocket(socket) {
1603
1764
  let buffer = '';