@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.
- package/README.md +23 -27
- package/dist/agents-host.d.ts +28 -5
- package/dist/agents-host.js +274 -236
- package/dist/chat/chat-control-plane.d.ts +3 -0
- package/dist/chat/sdk-chat-control-plane.d.ts +4 -3
- package/dist/chat/sdk-chat-control-plane.js +6 -0
- package/dist/cli.js +1 -5
- package/dist/compatibility-gates.d.ts +4 -0
- package/dist/compatibility-gates.js +18 -1
- package/dist/context/claude-file-brief.d.ts +2 -0
- package/dist/context/claude-file-brief.js +83 -0
- package/dist/context/compaction.d.ts +20 -0
- package/dist/context/compaction.js +59 -0
- package/dist/context/injection.d.ts +44 -9
- package/dist/context/injection.js +360 -53
- package/dist/context/main-session-delegation.d.ts +1 -1
- package/dist/context/projection-strategy.d.ts +24 -0
- package/dist/context/projection-strategy.js +90 -0
- package/dist/context/prompt.d.ts +16 -1
- package/dist/context/prompt.js +475 -53
- package/dist/context/resolved-workspace.d.ts +2 -0
- package/dist/context/resolved-workspace.js +64 -0
- package/dist/context/skill-manual.d.ts +14 -0
- package/dist/context/skill-manual.js +21 -0
- package/dist/context/turn-preparation.d.ts +8 -2
- package/dist/context/turn-preparation.js +68 -17
- package/dist/gateway/localhost-gateway.js +18 -8
- package/dist/hosted-turn-content.d.ts +15 -0
- package/dist/hosted-turn-content.js +50 -0
- package/dist/managed-daemon.d.ts +3 -2
- package/dist/managed-daemon.js +198 -37
- package/dist/plugin-sdk.js +276 -359
- package/dist/plugin-sdk.js.map +4 -4
- package/dist/progress-to-activity.d.ts +16 -0
- package/dist/progress-to-activity.js +24 -0
- package/dist/projection-strategy-values.d.ts +4 -0
- package/dist/projection-strategy-values.js +28 -0
- package/dist/providers/acp-progress-collector.d.ts +44 -0
- package/dist/providers/acp-progress-collector.js +130 -0
- package/dist/providers/awaiting-user.d.ts +2 -3
- package/dist/providers/awaiting-user.js +5 -7
- package/dist/providers/claude/activity-metadata.d.ts +14 -0
- package/dist/providers/claude/activity-metadata.js +81 -0
- package/dist/providers/claude/adapter.d.ts +3 -1
- package/dist/providers/claude/adapter.js +10 -0
- package/dist/providers/claude/cli-client.d.ts +9 -1
- package/dist/providers/claude/cli-client.js +270 -126
- package/dist/providers/codex/adapter.d.ts +3 -1
- package/dist/providers/codex/adapter.js +10 -0
- package/dist/providers/codex/cli-client.d.ts +10 -2
- package/dist/providers/codex/cli-client.js +88 -104
- package/dist/providers/codex/project-doc.js +22 -37
- package/dist/providers/copilot/adapter.d.ts +3 -1
- package/dist/providers/copilot/adapter.js +10 -0
- package/dist/providers/copilot/cli-client.d.ts +9 -1
- package/dist/providers/copilot/cli-client.js +68 -85
- package/dist/providers/create-provider.d.ts +3 -1
- package/dist/providers/create-provider.js +36 -9
- package/dist/providers/provider-adapter.d.ts +35 -0
- package/dist/providers/provider-adapter.js +44 -1
- package/dist/state-paths.d.ts +10 -2
- package/dist/state-paths.js +25 -6
- package/dist/task-thread-resolution.d.ts +3 -2
- package/dist/types.d.ts +163 -8
- package/package.json +2 -2
- package/skills/borgee-agent/SKILL.md +127 -38
- package/skills/borgee-agent/references/errors.md +38 -0
- package/skills/borgee-agent/references/task-properties.md +33 -0
- package/skills/borgee-agent/scripts/borgee-agent.mjs +553 -0
- package/skills/borgee-agent/scripts/borgee-agent.py +547 -0
- package/dist/durable-cursor-store.d.ts +0 -5
- package/dist/durable-cursor-store.js +0 -7
- package/skills/borgee-agent/borgee-agent.mjs +0 -562
- package/skills/borgee-agent/borgee-agent.py +0 -469
package/dist/managed-daemon.js
CHANGED
|
@@ -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 {
|
|
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,
|
|
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, `${
|
|
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(
|
|
603
|
-
|
|
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: ${
|
|
631
|
+
throw new Error(`Managed control socket path has an unexpected value: ${endpoint.address}`);
|
|
614
632
|
}
|
|
615
|
-
if (await canConnectToSocket(
|
|
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: ${
|
|
637
|
+
throw new Error(`Managed control socket path has an unexpected value: ${endpoint.address}`);
|
|
620
638
|
}
|
|
621
|
-
await fs.rm(
|
|
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
|
|
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(
|
|
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: ${
|
|
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
|
|
811
|
+
const endpoint = resolveManagedDaemonEndpoint(rootPath);
|
|
794
812
|
while (Date.now() < deadline) {
|
|
795
|
-
const listening = await canConnect(
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
1654
|
+
socketPath: this.endpoint.address,
|
|
1539
1655
|
});
|
|
1540
1656
|
}
|
|
1541
1657
|
catch (error) {
|
|
1542
1658
|
this.started = false;
|
|
1543
1659
|
this.ready = false;
|
|
1544
|
-
|
|
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
|
-
|
|
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.
|
|
1562
|
-
const socketStats = await fs.lstat(this.
|
|
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.
|
|
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.
|
|
1736
|
+
this.server.listen(this.endpoint.address);
|
|
1588
1737
|
});
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
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)
|
|
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
|
|
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 = '';
|