@borgee/agents-host 0.2.28 → 0.2.31
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 +42 -28
- package/dist/agents-host.d.ts +1 -1
- package/dist/agents-host.js +45 -18
- package/dist/cli-args.d.ts +1 -1
- package/dist/cli-args.js +2 -2
- package/dist/compatibility-gates.d.ts +4 -1
- package/dist/compatibility-gates.js +42 -3
- package/dist/config.d.ts +8 -0
- package/dist/config.js +54 -16
- package/dist/gateway/localhost-gateway.js +5 -1
- package/dist/local-config.js +20 -1
- package/dist/managed-daemon.js +199 -49
- package/dist/providers/claude/cli-client.d.ts +55 -16
- package/dist/providers/claude/cli-client.js +811 -345
- package/dist/providers/create-provider.js +8 -13
- package/dist/types.d.ts +1 -0
- package/package.json +3 -2
- package/skills/borgee-agent/borgee-agent.mjs +2 -1
- package/skills/borgee-agent/borgee-agent.py +3 -1
package/dist/managed-daemon.js
CHANGED
|
@@ -5,8 +5,8 @@ import { createConnection, createServer } from 'node:net';
|
|
|
5
5
|
import { dirname, join, resolve } from 'node:path';
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
7
7
|
import { AgentsHostSupervisor } from './agents-host-supervisor.js';
|
|
8
|
-
import { COMPATIBILITY_GATES_ENV, createManagedRuntimeSettingsFingerprint, INTERNAL_POLICY_MODE_ENV, INTERNAL_PROVIDER_IMPLEMENTATIONS_ENV, MANAGED_RUNTIME_CONVERGENCE_COMPATIBILITY_GATE, MANAGED_RUNTIME_SETTINGS_SCHEMA_VERSION, resolveManagedRuntimeSettingsSnapshot, normalizeInternalProviderImplementationOverrides, } from './compatibility-gates.js';
|
|
9
|
-
import { loadConfigFromEnv } from './config.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';
|
|
9
|
+
import { DEFAULT_PROVIDER_COMMAND_CONFIG, hasLegacyClaudeOneShotArgs, loadConfigFromEnv, } from './config.js';
|
|
10
10
|
import { loadLocalConfigGenerateSpec, materializeLocalConfig, parseGenerateConfigSpec, resolveLocalConfigLayout, } from './local-config.js';
|
|
11
11
|
import { normalizeManagedRuntimeKey, resolveManagedBootstrapLockPath, resolveManagedDaemonLogPath, resolveManagedDaemonSocketPath, resolveManagedRuntimeRoot, resolveManagedRuntimeSettingsPath, } from './state-paths.js';
|
|
12
12
|
const MANAGED_ROOT_MODE = 0o700;
|
|
@@ -149,8 +149,9 @@ function resolveDesiredManagedRuntimeSettings(env = process.env) {
|
|
|
149
149
|
function normalizeManagedRuntimeSettingsSnapshot(snapshot) {
|
|
150
150
|
const compatibilityGates = [...new Set(snapshot.compatibilityGates.map((gate) => gate.trim()))]
|
|
151
151
|
.filter((gate) => gate.length > 0)
|
|
152
|
+
.filter((gate) => gate !== CLAUDE_PROVIDER_V2_COMPATIBILITY_GATE)
|
|
152
153
|
.sort((left, right) => left.localeCompare(right));
|
|
153
|
-
const providerImplementationOverrides = normalizeInternalProviderImplementationOverrides(snapshot.providerImplementationOverrides.join(','));
|
|
154
|
+
const providerImplementationOverrides = normalizeInternalProviderImplementationOverrides(snapshot.providerImplementationOverrides.join(',')).filter((assignment) => !assignment.startsWith('claude:'));
|
|
154
155
|
return {
|
|
155
156
|
schemaVersion: MANAGED_RUNTIME_SETTINGS_SCHEMA_VERSION,
|
|
156
157
|
compatibilityGates,
|
|
@@ -222,15 +223,45 @@ function buildManagedRuntimeSettingsEnv(baseEnv, snapshot) {
|
|
|
222
223
|
return {
|
|
223
224
|
...baseEnv,
|
|
224
225
|
[COMPATIBILITY_GATES_ENV]: snapshot.compatibilityGates.join(','),
|
|
226
|
+
[INTERNAL_DISABLED_COMPATIBILITY_GATES_ENV]: resolveDisabledDefaultCompatibilityGates(snapshot.compatibilityGates).join(','),
|
|
225
227
|
[INTERNAL_PROVIDER_IMPLEMENTATIONS_ENV]: snapshot.providerImplementationOverrides.join(','),
|
|
226
228
|
[INTERNAL_POLICY_MODE_ENV]: snapshot.internalPolicyMode,
|
|
227
229
|
};
|
|
228
230
|
}
|
|
229
|
-
function
|
|
230
|
-
|
|
231
|
-
|
|
231
|
+
function buildDesiredManagedRuntimeSettingsFromSnapshot(snapshot) {
|
|
232
|
+
return {
|
|
233
|
+
snapshot,
|
|
234
|
+
fingerprint: createManagedRuntimeSettingsFingerprint(snapshot),
|
|
235
|
+
convergenceEnabled: snapshot.compatibilityGates.includes(MANAGED_RUNTIME_CONVERGENCE_COMPATIBILITY_GATE),
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
async function getManagedRuntimeFingerprintMismatch(rootPath, status, desiredSettings) {
|
|
239
|
+
if (status.managedRuntimeFingerprint != null) {
|
|
240
|
+
return status.managedRuntimeFingerprint !== desiredSettings.fingerprint;
|
|
241
|
+
}
|
|
242
|
+
if (desiredSettings.convergenceEnabled) {
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
const persistedSettingsSnapshot = await loadManagedRuntimeSettingsSnapshot(rootPath);
|
|
246
|
+
if (!persistedSettingsSnapshot) {
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
249
|
+
return createManagedRuntimeSettingsFingerprint(persistedSettingsSnapshot) !== desiredSettings.fingerprint;
|
|
250
|
+
}
|
|
251
|
+
async function resolveAuthoritativeManagedRuntimeSettings(rootPath, effectiveProcessEnv) {
|
|
252
|
+
const persistedSettingsSnapshot = await loadManagedRuntimeSettingsSnapshot(rootPath);
|
|
253
|
+
if (!persistedSettingsSnapshot) {
|
|
254
|
+
return {
|
|
255
|
+
desiredSettings: resolveDesiredManagedRuntimeSettings(effectiveProcessEnv),
|
|
256
|
+
processEnv: effectiveProcessEnv,
|
|
257
|
+
persistedSettingsSnapshot: null,
|
|
258
|
+
};
|
|
232
259
|
}
|
|
233
|
-
return
|
|
260
|
+
return {
|
|
261
|
+
desiredSettings: buildDesiredManagedRuntimeSettingsFromSnapshot(persistedSettingsSnapshot),
|
|
262
|
+
processEnv: buildManagedRuntimeSettingsEnv(effectiveProcessEnv, persistedSettingsSnapshot),
|
|
263
|
+
persistedSettingsSnapshot,
|
|
264
|
+
};
|
|
234
265
|
}
|
|
235
266
|
function cloneLocalAgentConfig(agent) {
|
|
236
267
|
return {
|
|
@@ -311,6 +342,70 @@ export function buildManagedLocalAgentConfig(options) {
|
|
|
311
342
|
},
|
|
312
343
|
};
|
|
313
344
|
}
|
|
345
|
+
function mergeManagedLocalAgentConfig(hostDefaults, existingAgent, generatedAgent, envOverrides) {
|
|
346
|
+
const overrides = envOverrides ?? {};
|
|
347
|
+
const hasOverride = (key) => Object.prototype.hasOwnProperty.call(overrides, key);
|
|
348
|
+
const effectiveExistingClaudeCommand = existingAgent.claudeCommand ?? hostDefaults?.claudeCommand;
|
|
349
|
+
const effectiveExistingClaudeArgs = existingAgent.claudeArgs ?? hostDefaults?.claudeArgs ?? [];
|
|
350
|
+
const hasLegacyClaudeDefaults = effectiveExistingClaudeCommand === 'claude' && hasLegacyClaudeOneShotArgs(effectiveExistingClaudeArgs);
|
|
351
|
+
const shouldRefreshLegacyClaudeDefaults = hasLegacyClaudeDefaults
|
|
352
|
+
&& generatedAgent.claudeCommand === DEFAULT_PROVIDER_COMMAND_CONFIG.claudeCommand
|
|
353
|
+
&& JSON.stringify(generatedAgent.claudeArgs ?? []) === JSON.stringify(DEFAULT_PROVIDER_COMMAND_CONFIG.claudeArgs);
|
|
354
|
+
const shouldResetLegacyClaudeArgsForCommandOverride = hasLegacyClaudeDefaults
|
|
355
|
+
&& hasOverride('CLAUDE_COMMAND')
|
|
356
|
+
&& !hasOverride('CLAUDE_ARGS');
|
|
357
|
+
return {
|
|
358
|
+
...cloneLocalAgentConfig(existingAgent),
|
|
359
|
+
key: generatedAgent.key,
|
|
360
|
+
apiKey: generatedAgent.apiKey,
|
|
361
|
+
enabled: true,
|
|
362
|
+
name: hasOverride('BORGEE_AGENT_NAME') ? generatedAgent.name : existingAgent.name,
|
|
363
|
+
provider: hasOverride('RUNTIME_PROVIDER') ? generatedAgent.provider : existingAgent.provider,
|
|
364
|
+
claudeCommand: hasOverride('CLAUDE_COMMAND') || shouldRefreshLegacyClaudeDefaults
|
|
365
|
+
? generatedAgent.claudeCommand
|
|
366
|
+
: existingAgent.claudeCommand,
|
|
367
|
+
claudeArgs: hasOverride('CLAUDE_ARGS')
|
|
368
|
+
|| shouldRefreshLegacyClaudeDefaults
|
|
369
|
+
|| shouldResetLegacyClaudeArgsForCommandOverride
|
|
370
|
+
? [...(generatedAgent.claudeArgs ?? [])]
|
|
371
|
+
: [...(existingAgent.claudeArgs ?? [])],
|
|
372
|
+
codexCommand: hasOverride('CODEX_COMMAND') ? generatedAgent.codexCommand : existingAgent.codexCommand,
|
|
373
|
+
codexArgs: hasOverride('CODEX_ARGS')
|
|
374
|
+
? [...(generatedAgent.codexArgs ?? [])]
|
|
375
|
+
: [...(existingAgent.codexArgs ?? [])],
|
|
376
|
+
copilotCommand: hasOverride('COPILOT_COMMAND')
|
|
377
|
+
? generatedAgent.copilotCommand
|
|
378
|
+
: existingAgent.copilotCommand,
|
|
379
|
+
copilotArgs: hasOverride('COPILOT_ARGS')
|
|
380
|
+
? [...(generatedAgent.copilotArgs ?? [])]
|
|
381
|
+
: [...(existingAgent.copilotArgs ?? [])],
|
|
382
|
+
copilotSessionTtlMinutes: hasOverride('COPILOT_SESSION_TTL_MINUTES')
|
|
383
|
+
? generatedAgent.copilotSessionTtlMinutes
|
|
384
|
+
: existingAgent.copilotSessionTtlMinutes,
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
function collectExplicitManagedAgentEnvOverrides(processEnv, optionEnv) {
|
|
388
|
+
const explicitOverrides = {};
|
|
389
|
+
for (const key of [
|
|
390
|
+
'BORGEE_AGENT_NAME',
|
|
391
|
+
'RUNTIME_PROVIDER',
|
|
392
|
+
'CLAUDE_COMMAND',
|
|
393
|
+
'CLAUDE_ARGS',
|
|
394
|
+
'CODEX_COMMAND',
|
|
395
|
+
'CODEX_ARGS',
|
|
396
|
+
'COPILOT_COMMAND',
|
|
397
|
+
'COPILOT_ARGS',
|
|
398
|
+
'COPILOT_SESSION_TTL_MINUTES',
|
|
399
|
+
]) {
|
|
400
|
+
if (processEnv[key] !== undefined) {
|
|
401
|
+
explicitOverrides[key] = processEnv[key];
|
|
402
|
+
}
|
|
403
|
+
if (optionEnv && Object.prototype.hasOwnProperty.call(optionEnv, key)) {
|
|
404
|
+
explicitOverrides[key] = optionEnv[key];
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
return explicitOverrides;
|
|
408
|
+
}
|
|
314
409
|
function resolveManagedRuntimeLayoutForServerUrl(serverUrl, processEnv = process.env) {
|
|
315
410
|
const rootPath = resolveManagedRuntimeRoot(serverUrl, processEnv);
|
|
316
411
|
return {
|
|
@@ -884,8 +979,6 @@ export async function bootstrapManagedDaemonStart(options, deps = {}) {
|
|
|
884
979
|
...(options.processEnv ?? process.env),
|
|
885
980
|
...options.env,
|
|
886
981
|
};
|
|
887
|
-
const loadSpec = deps.loadSpec
|
|
888
|
-
?? ((hostConfigPath) => loadLocalConfigGenerateSpec(hostConfigPath, { env: effectiveProcessEnv }));
|
|
889
982
|
const materialize = deps.materialize ?? materializeLocalConfig;
|
|
890
983
|
const sendRequest = deps.sendRequest ?? sendManagedDaemonRequest;
|
|
891
984
|
const spawnDaemonProcess = deps.spawnDaemon ?? spawnManagedDaemonProcess;
|
|
@@ -895,19 +988,51 @@ export async function bootstrapManagedDaemonStart(options, deps = {}) {
|
|
|
895
988
|
?? (async (rootPath) => {
|
|
896
989
|
await waitForManagedDaemonShutdown(rootPath, canConnectToSocket);
|
|
897
990
|
});
|
|
898
|
-
const
|
|
899
|
-
const
|
|
900
|
-
const resolved = resolvedManagedAgent ??
|
|
901
|
-
resolveManagedRuntimeLayoutForServerUrl(options.serverUrl, options.processEnv);
|
|
991
|
+
const requestedSettings = resolveDesiredManagedRuntimeSettings(effectiveProcessEnv);
|
|
992
|
+
const resolved = resolveManagedRuntimeLayoutForServerUrl(options.serverUrl, options.processEnv);
|
|
902
993
|
const releaseBootstrapLock = await acquireLock(resolved.rootPath);
|
|
903
994
|
try {
|
|
995
|
+
const currentRuntimeSettings = await resolveAuthoritativeManagedRuntimeSettings(resolved.rootPath, effectiveProcessEnv);
|
|
996
|
+
const loadManagedSpecWithCurrentRuntimeEnv = (hostConfigPath) => deps.loadSpec
|
|
997
|
+
? deps.loadSpec(hostConfigPath)
|
|
998
|
+
: loadLocalConfigGenerateSpec(hostConfigPath, {
|
|
999
|
+
env: currentRuntimeSettings.processEnv,
|
|
1000
|
+
});
|
|
1001
|
+
if (options.apiKey !== undefined
|
|
1002
|
+
&& currentRuntimeSettings.persistedSettingsSnapshot == null
|
|
1003
|
+
&& await fs.access(resolved.hostConfigPath).then(() => true).catch(() => false)) {
|
|
1004
|
+
throw new Error(`Managed runtime root ${resolved.rootPath} predates managed-runtime-settings.json; run \`agents-host start-managed ${options.serverUrl}\` once to migrate it before updating an individual agent`);
|
|
1005
|
+
}
|
|
1006
|
+
const explicitManagedAgentOverrides = collectExplicitManagedAgentEnvOverrides(options.processEnv ?? process.env, options.env);
|
|
1007
|
+
const currentManagedSpec = options.apiKey === undefined
|
|
1008
|
+
? undefined
|
|
1009
|
+
: await loadManagedSpecOrUndefined(resolved.hostConfigPath, loadManagedSpecWithCurrentRuntimeEnv);
|
|
1010
|
+
const resolvedManagedAgent = options.apiKey === undefined
|
|
1011
|
+
? null
|
|
1012
|
+
: (() => {
|
|
1013
|
+
const generatedManagedAgent = buildManagedLocalAgentConfig({
|
|
1014
|
+
...options,
|
|
1015
|
+
processEnv: currentRuntimeSettings.processEnv,
|
|
1016
|
+
env: options.env,
|
|
1017
|
+
});
|
|
1018
|
+
const existingAgent = currentManagedSpec?.agents.find((agent) => agent.key === generatedManagedAgent.agent.key);
|
|
1019
|
+
return existingAgent
|
|
1020
|
+
? {
|
|
1021
|
+
...generatedManagedAgent,
|
|
1022
|
+
agent: mergeManagedLocalAgentConfig(currentManagedSpec?.host.defaults, existingAgent, generatedManagedAgent.agent, explicitManagedAgentOverrides),
|
|
1023
|
+
}
|
|
1024
|
+
: generatedManagedAgent;
|
|
1025
|
+
})();
|
|
1026
|
+
const desiredBootstrapSettings = options.apiKey === undefined ? requestedSettings : currentRuntimeSettings.desiredSettings;
|
|
1027
|
+
const desiredBootstrapProcessEnv = options.apiKey === undefined ? effectiveProcessEnv : currentRuntimeSettings.processEnv;
|
|
904
1028
|
let statusResponse = await readReadyManagedDaemonStatus(resolved.rootPath, sendRequest);
|
|
905
|
-
if (statusResponse
|
|
1029
|
+
if (statusResponse
|
|
1030
|
+
&& await getManagedRuntimeFingerprintMismatch(resolved.rootPath, statusResponse, desiredBootstrapSettings)) {
|
|
906
1031
|
await recycleManagedDaemonWithDesiredSettings({
|
|
907
1032
|
rootPath: resolved.rootPath,
|
|
908
1033
|
debug: options.debug === true,
|
|
909
|
-
processEnv:
|
|
910
|
-
desiredSettings,
|
|
1034
|
+
processEnv: desiredBootstrapProcessEnv,
|
|
1035
|
+
desiredSettings: desiredBootstrapSettings,
|
|
911
1036
|
sendRequest,
|
|
912
1037
|
waitForShutdown,
|
|
913
1038
|
spawnDaemonProcess,
|
|
@@ -917,11 +1042,17 @@ export async function bootstrapManagedDaemonStart(options, deps = {}) {
|
|
|
917
1042
|
}
|
|
918
1043
|
if (!statusResponse) {
|
|
919
1044
|
if (options.apiKey === undefined) {
|
|
920
|
-
await ensurePersistedManagedAgentsExist(options.serverUrl, resolved.hostConfigPath,
|
|
1045
|
+
await ensurePersistedManagedAgentsExist(options.serverUrl, resolved.hostConfigPath, (hostConfigPath) => options.apiKey === undefined
|
|
1046
|
+
? (deps.loadSpec
|
|
1047
|
+
? deps.loadSpec(hostConfigPath)
|
|
1048
|
+
: loadLocalConfigGenerateSpec(hostConfigPath, {
|
|
1049
|
+
env: desiredBootstrapProcessEnv,
|
|
1050
|
+
}))
|
|
1051
|
+
: loadManagedSpecWithCurrentRuntimeEnv(hostConfigPath));
|
|
921
1052
|
}
|
|
922
1053
|
else {
|
|
923
1054
|
try {
|
|
924
|
-
await
|
|
1055
|
+
await loadManagedSpecWithCurrentRuntimeEnv(resolved.hostConfigPath);
|
|
925
1056
|
}
|
|
926
1057
|
catch (error) {
|
|
927
1058
|
if (isNotFoundError(error)) {
|
|
@@ -938,8 +1069,8 @@ export async function bootstrapManagedDaemonStart(options, deps = {}) {
|
|
|
938
1069
|
await startManagedDaemonWithPersistedSettings({
|
|
939
1070
|
rootPath: resolved.rootPath,
|
|
940
1071
|
debug: options.debug === true,
|
|
941
|
-
processEnv:
|
|
942
|
-
desiredSettings,
|
|
1072
|
+
processEnv: desiredBootstrapProcessEnv,
|
|
1073
|
+
desiredSettings: desiredBootstrapSettings,
|
|
943
1074
|
spawnDaemonProcess,
|
|
944
1075
|
waitForDaemon,
|
|
945
1076
|
sendRequest,
|
|
@@ -948,7 +1079,11 @@ export async function bootstrapManagedDaemonStart(options, deps = {}) {
|
|
|
948
1079
|
}
|
|
949
1080
|
if (options.apiKey === undefined) {
|
|
950
1081
|
if (!statusResponse) {
|
|
951
|
-
await ensurePersistedManagedAgentsExist(options.serverUrl, resolved.hostConfigPath, loadSpec
|
|
1082
|
+
await ensurePersistedManagedAgentsExist(options.serverUrl, resolved.hostConfigPath, (hostConfigPath) => deps.loadSpec
|
|
1083
|
+
? deps.loadSpec(hostConfigPath)
|
|
1084
|
+
: loadLocalConfigGenerateSpec(hostConfigPath, {
|
|
1085
|
+
env: desiredBootstrapProcessEnv,
|
|
1086
|
+
}));
|
|
952
1087
|
}
|
|
953
1088
|
else {
|
|
954
1089
|
assertManagedRuntimeBinding(statusResponse.borgeeBaseUrl, options.serverUrl);
|
|
@@ -1079,10 +1214,11 @@ async function terminateChildProcessAndWait(child) {
|
|
|
1079
1214
|
}
|
|
1080
1215
|
await waitForChildExit(child, DAEMON_FORCE_KILL_WAIT_MS);
|
|
1081
1216
|
}
|
|
1082
|
-
async function rollbackManagedApplyFailure(rootPath, previousSpec, materialize, clearRuntimeImpl, applyError) {
|
|
1217
|
+
async function rollbackManagedApplyFailure(rootPath, previousSpec, previousSettingsSnapshot, materialize, clearRuntimeImpl, applyError) {
|
|
1083
1218
|
const original = toManagedErrorPayload(applyError, 'MANAGED_RELOAD_FAILED');
|
|
1084
1219
|
try {
|
|
1085
1220
|
if (previousSpec) {
|
|
1221
|
+
await restoreManagedRuntimeSettingsSnapshot(rootPath, previousSettingsSnapshot);
|
|
1086
1222
|
await materialize(rootPath, cloneLocalConfigGenerateSpec(previousSpec));
|
|
1087
1223
|
}
|
|
1088
1224
|
else {
|
|
@@ -1139,8 +1275,6 @@ function validateDaemonUpsertAgentInput(currentSpec, serverUrl, agent, env = pro
|
|
|
1139
1275
|
}
|
|
1140
1276
|
export async function describeManagedSpec(options, deps = {}) {
|
|
1141
1277
|
const effectiveProcessEnv = options.processEnv ?? process.env;
|
|
1142
|
-
const loadSpec = deps.loadSpec
|
|
1143
|
-
?? ((hostConfigPath) => loadLocalConfigGenerateSpec(hostConfigPath, { env: effectiveProcessEnv }));
|
|
1144
1278
|
const sendRequest = deps.sendRequest ?? sendManagedDaemonRequest;
|
|
1145
1279
|
const resolved = resolveManagedRuntimeLayoutForServerUrl(options.serverUrl, options.processEnv);
|
|
1146
1280
|
try {
|
|
@@ -1167,7 +1301,12 @@ export async function describeManagedSpec(options, deps = {}) {
|
|
|
1167
1301
|
}
|
|
1168
1302
|
}
|
|
1169
1303
|
try {
|
|
1170
|
-
const
|
|
1304
|
+
const currentRuntimeSettings = await resolveAuthoritativeManagedRuntimeSettings(resolved.rootPath, effectiveProcessEnv);
|
|
1305
|
+
const persistedSpec = await loadManagedSpecOrUndefined(resolved.hostConfigPath, (hostConfigPath) => deps.loadSpec
|
|
1306
|
+
? deps.loadSpec(hostConfigPath)
|
|
1307
|
+
: loadLocalConfigGenerateSpec(hostConfigPath, {
|
|
1308
|
+
env: currentRuntimeSettings.processEnv,
|
|
1309
|
+
}));
|
|
1171
1310
|
if (!persistedSpec) {
|
|
1172
1311
|
return { ok: true, present: false };
|
|
1173
1312
|
}
|
|
@@ -1207,27 +1346,13 @@ async function applySpecToRunningManagedDaemon(rootPath, borgeeBaseUrl, spec, se
|
|
|
1207
1346
|
}
|
|
1208
1347
|
export async function applyManagedSpec(options, deps = {}) {
|
|
1209
1348
|
const effectiveProcessEnv = options.processEnv ?? process.env;
|
|
1210
|
-
const
|
|
1211
|
-
?? ((hostConfigPath) => loadLocalConfigGenerateSpec(hostConfigPath, { env: effectiveProcessEnv }));
|
|
1212
|
-
const materialize = deps.materialize
|
|
1213
|
-
?? ((rootPath, spec) => materializeLocalConfig(rootPath, spec, { env: effectiveProcessEnv }));
|
|
1349
|
+
const desiredSettings = resolveDesiredManagedRuntimeSettings(effectiveProcessEnv);
|
|
1214
1350
|
const sendRequest = deps.sendRequest ?? sendManagedDaemonRequest;
|
|
1215
1351
|
const spawnDaemonProcess = deps.spawnDaemon ?? spawnManagedDaemonProcess;
|
|
1216
1352
|
const waitForDaemon = deps.waitForDaemonReady ?? waitForManagedDaemonReady;
|
|
1217
1353
|
const acquireLock = deps.acquireBootstrapLock ?? acquireBootstrapLock;
|
|
1218
1354
|
const clearManagedRuntimeImpl = deps.clearManagedRuntime ?? clearManagedRuntime;
|
|
1219
1355
|
const resolved = resolveManagedRuntimeLayoutForServerUrl(options.serverUrl, options.processEnv);
|
|
1220
|
-
let spec;
|
|
1221
|
-
try {
|
|
1222
|
-
spec = validateApplyManagedInput(options.spec, options.serverUrl, effectiveProcessEnv);
|
|
1223
|
-
}
|
|
1224
|
-
catch (error) {
|
|
1225
|
-
if (isManagedOperationError(error)) {
|
|
1226
|
-
return toManagedErrorResponse(error);
|
|
1227
|
-
}
|
|
1228
|
-
return toManagedErrorResponse(managedOperationError('MANAGED_SPEC_INVALID', `Invalid managed spec: ${toErrorMessage(error)}`));
|
|
1229
|
-
}
|
|
1230
|
-
const desiredSettings = resolveDesiredManagedRuntimeSettings(effectiveProcessEnv);
|
|
1231
1356
|
const waitForShutdown = deps.waitForShutdown
|
|
1232
1357
|
?? (async (rootPath) => {
|
|
1233
1358
|
await waitForManagedDaemonShutdown(rootPath, canConnectToSocket);
|
|
@@ -1240,6 +1365,32 @@ export async function applyManagedSpec(options, deps = {}) {
|
|
|
1240
1365
|
return toApplyManagedColdStartErrorResponse(error);
|
|
1241
1366
|
}
|
|
1242
1367
|
try {
|
|
1368
|
+
const currentRuntimeSettings = await resolveAuthoritativeManagedRuntimeSettings(resolved.rootPath, effectiveProcessEnv);
|
|
1369
|
+
const loadSpecWithCurrentRuntimeEnv = (hostConfigPath) => deps.loadSpec
|
|
1370
|
+
? deps.loadSpec(hostConfigPath)
|
|
1371
|
+
: loadLocalConfigGenerateSpec(hostConfigPath, {
|
|
1372
|
+
env: currentRuntimeSettings.processEnv,
|
|
1373
|
+
});
|
|
1374
|
+
const materializeWithDesiredEnv = (rootPath, nextSpec) => deps.materialize
|
|
1375
|
+
? deps.materialize(rootPath, nextSpec)
|
|
1376
|
+
: materializeLocalConfig(rootPath, nextSpec, {
|
|
1377
|
+
env: effectiveProcessEnv,
|
|
1378
|
+
});
|
|
1379
|
+
const materializeWithCurrentRuntimeEnv = (rootPath, nextSpec) => deps.materialize
|
|
1380
|
+
? deps.materialize(rootPath, nextSpec)
|
|
1381
|
+
: materializeLocalConfig(rootPath, nextSpec, {
|
|
1382
|
+
env: currentRuntimeSettings.processEnv,
|
|
1383
|
+
});
|
|
1384
|
+
let spec;
|
|
1385
|
+
try {
|
|
1386
|
+
spec = validateApplyManagedInput(options.spec, options.serverUrl, effectiveProcessEnv);
|
|
1387
|
+
}
|
|
1388
|
+
catch (error) {
|
|
1389
|
+
if (isManagedOperationError(error)) {
|
|
1390
|
+
return toManagedErrorResponse(error);
|
|
1391
|
+
}
|
|
1392
|
+
return toManagedErrorResponse(managedOperationError('MANAGED_SPEC_INVALID', `Invalid managed spec: ${toErrorMessage(error)}`));
|
|
1393
|
+
}
|
|
1243
1394
|
let statusResponse;
|
|
1244
1395
|
try {
|
|
1245
1396
|
statusResponse = await readReadyManagedDaemonStatus(resolved.rootPath, sendRequest);
|
|
@@ -1250,7 +1401,8 @@ export async function applyManagedSpec(options, deps = {}) {
|
|
|
1250
1401
|
}
|
|
1251
1402
|
throw error;
|
|
1252
1403
|
}
|
|
1253
|
-
if (statusResponse
|
|
1404
|
+
if (statusResponse
|
|
1405
|
+
&& await getManagedRuntimeFingerprintMismatch(resolved.rootPath, statusResponse, desiredSettings)) {
|
|
1254
1406
|
try {
|
|
1255
1407
|
await recycleManagedDaemonWithDesiredSettings({
|
|
1256
1408
|
rootPath: resolved.rootPath,
|
|
@@ -1273,7 +1425,7 @@ export async function applyManagedSpec(options, deps = {}) {
|
|
|
1273
1425
|
}
|
|
1274
1426
|
let previousSpec;
|
|
1275
1427
|
try {
|
|
1276
|
-
previousSpec = await loadManagedSpecOrUndefined(resolved.hostConfigPath,
|
|
1428
|
+
previousSpec = await loadManagedSpecOrUndefined(resolved.hostConfigPath, loadSpecWithCurrentRuntimeEnv);
|
|
1277
1429
|
if (previousSpec) {
|
|
1278
1430
|
assertManagedSpecBinding(previousSpec, options.serverUrl);
|
|
1279
1431
|
}
|
|
@@ -1282,10 +1434,10 @@ export async function applyManagedSpec(options, deps = {}) {
|
|
|
1282
1434
|
return toApplyManagedColdStartErrorResponse(error);
|
|
1283
1435
|
}
|
|
1284
1436
|
try {
|
|
1285
|
-
await
|
|
1437
|
+
await materializeWithDesiredEnv(resolved.rootPath, cloneLocalConfigGenerateSpec(spec));
|
|
1286
1438
|
}
|
|
1287
1439
|
catch (error) {
|
|
1288
|
-
return rollbackManagedApplyFailure(resolved.rootPath, previousSpec,
|
|
1440
|
+
return rollbackManagedApplyFailure(resolved.rootPath, previousSpec, currentRuntimeSettings.persistedSettingsSnapshot ?? undefined, materializeWithCurrentRuntimeEnv, clearManagedRuntimeImpl, error);
|
|
1289
1441
|
}
|
|
1290
1442
|
try {
|
|
1291
1443
|
await startManagedDaemonWithPersistedSettings({
|
|
@@ -1300,7 +1452,7 @@ export async function applyManagedSpec(options, deps = {}) {
|
|
|
1300
1452
|
const status = await sendRequest(resolved.rootPath, { type: 'status' });
|
|
1301
1453
|
if (!status.ok) {
|
|
1302
1454
|
const managedError = getManagedErrorPayload(status);
|
|
1303
|
-
return rollbackManagedApplyFailure(resolved.rootPath, previousSpec,
|
|
1455
|
+
return rollbackManagedApplyFailure(resolved.rootPath, previousSpec, currentRuntimeSettings.persistedSettingsSnapshot ?? undefined, materializeWithCurrentRuntimeEnv, clearManagedRuntimeImpl, managedOperationError(managedError.code, managedError.message));
|
|
1304
1456
|
}
|
|
1305
1457
|
if (status.type !== 'status') {
|
|
1306
1458
|
throw new Error('Unexpected managed daemon response for status');
|
|
@@ -1314,7 +1466,7 @@ export async function applyManagedSpec(options, deps = {}) {
|
|
|
1314
1466
|
};
|
|
1315
1467
|
}
|
|
1316
1468
|
catch (error) {
|
|
1317
|
-
return rollbackManagedApplyFailure(resolved.rootPath, previousSpec,
|
|
1469
|
+
return rollbackManagedApplyFailure(resolved.rootPath, previousSpec, currentRuntimeSettings.persistedSettingsSnapshot ?? undefined, materializeWithCurrentRuntimeEnv, clearManagedRuntimeImpl, error);
|
|
1318
1470
|
}
|
|
1319
1471
|
}
|
|
1320
1472
|
finally {
|
|
@@ -1350,9 +1502,7 @@ export class ManagedAgentsHostDaemon {
|
|
|
1350
1502
|
this.loadSpec = deps.loadSpec ?? loadLocalConfigGenerateSpec;
|
|
1351
1503
|
this.materialize = deps.materialize ?? materializeLocalConfig;
|
|
1352
1504
|
const managedRuntimeSettings = resolveDesiredManagedRuntimeSettings(process.env);
|
|
1353
|
-
this.managedRuntimeFingerprint = managedRuntimeSettings.
|
|
1354
|
-
? managedRuntimeSettings.fingerprint
|
|
1355
|
-
: null;
|
|
1505
|
+
this.managedRuntimeFingerprint = managedRuntimeSettings.fingerprint;
|
|
1356
1506
|
this.logger = deps.logger ?? console;
|
|
1357
1507
|
this.logPath = deps.logPath ?? null;
|
|
1358
1508
|
this.server = createServer((socket) => {
|
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
import spawn from 'cross-spawn';
|
|
2
|
+
import { PROTOCOL_VERSION, client, methods, ndJsonStream } from '@agentclientprotocol/sdk';
|
|
2
3
|
import { type DebugLogger } from '../../debug.js';
|
|
3
4
|
import type { PreparedProviderTurnInput, ProviderGenerateOptions } from '../../types.js';
|
|
4
5
|
import type { ClaudeChannelSessionStore } from './session-store.js';
|
|
5
|
-
interface
|
|
6
|
+
interface ClaudeAcpRuntime {
|
|
6
7
|
spawn: typeof spawn;
|
|
8
|
+
client: typeof client;
|
|
9
|
+
ndJsonStream: typeof ndJsonStream;
|
|
10
|
+
methods: typeof methods;
|
|
11
|
+
protocolVersion: typeof PROTOCOL_VERSION;
|
|
7
12
|
cwd: string;
|
|
13
|
+
shutdownGracePeriodMs: number;
|
|
14
|
+
shutdownForceKillWaitMs: number;
|
|
8
15
|
}
|
|
9
16
|
/**
|
|
10
|
-
*
|
|
11
|
-
* native provider-session continuity.
|
|
17
|
+
* Persistent ACP-backed client for the Claude ACP adapter.
|
|
12
18
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
19
|
+
* The public caller surface intentionally stays stable: callers still create a
|
|
20
|
+
* `ClaudeCliClient`, then call `generateReply()` and `dispose()`. Internally,
|
|
21
|
+
* the shipped runtime now runs one Claude ACP adapter process and reuses one
|
|
22
|
+
* ACP session per routed Borgee channel.
|
|
17
23
|
*/
|
|
18
24
|
export declare class ClaudeCliClient {
|
|
19
25
|
private readonly command;
|
|
@@ -24,28 +30,61 @@ export declare class ClaudeCliClient {
|
|
|
24
30
|
private readonly runtime;
|
|
25
31
|
private readonly channels;
|
|
26
32
|
private readonly persistedSessions;
|
|
33
|
+
private readonly closingSessions;
|
|
34
|
+
private readonly pendingSessionStarts;
|
|
35
|
+
private readonly pendingSessionCloses;
|
|
36
|
+
private readonly pendingSessionStoreOperations;
|
|
37
|
+
private readonly fatalPromise;
|
|
38
|
+
private rejectFatalPromise;
|
|
39
|
+
private child?;
|
|
40
|
+
private connection?;
|
|
41
|
+
private startPromise?;
|
|
42
|
+
private shutdownPromise?;
|
|
43
|
+
private childExitPromise?;
|
|
44
|
+
private resolveChildExit?;
|
|
45
|
+
private fatalError;
|
|
46
|
+
private disposing;
|
|
47
|
+
private backendClosed;
|
|
27
48
|
private loadedSessionStoreAgentId;
|
|
28
|
-
private stopped;
|
|
29
49
|
private sessionStoreLoadPromise;
|
|
30
50
|
private sessionStoreWriteQueue;
|
|
31
|
-
|
|
51
|
+
private sessionCapabilities;
|
|
52
|
+
private childStderr;
|
|
53
|
+
constructor(command: string, args?: string[], runtimeOverrides?: Partial<ClaudeAcpRuntime>, sessionStore?: ClaudeChannelSessionStore | undefined, resolveSessionStoreAgentId?: () => string | undefined, logger?: DebugLogger);
|
|
32
54
|
generateReply(turn: PreparedProviderTurnInput, options?: ProviderGenerateOptions): Promise<string>;
|
|
33
55
|
generateReply(channelId: string, prompt: string, options?: ProviderGenerateOptions): Promise<string>;
|
|
34
56
|
dispose(): Promise<void>;
|
|
57
|
+
private ensureStarted;
|
|
58
|
+
private startBackend;
|
|
35
59
|
private getOrCreateChannelState;
|
|
36
60
|
private processChannelQueue;
|
|
61
|
+
private getOrCreateSession;
|
|
62
|
+
private startFreshSession;
|
|
63
|
+
private restoreOrCreateSession;
|
|
64
|
+
private restoreSession;
|
|
65
|
+
private clearBufferedSessionReplay;
|
|
66
|
+
private resolveSessionCwd;
|
|
67
|
+
private recycleSessionIfScopeChanged;
|
|
37
68
|
private runTurn;
|
|
38
|
-
private
|
|
69
|
+
private raceWithFatal;
|
|
70
|
+
private failAll;
|
|
71
|
+
private handlePermissionRequest;
|
|
72
|
+
private invalidateSession;
|
|
73
|
+
private closeSession;
|
|
74
|
+
private rejectQueuedTurnsAfterSessionTaint;
|
|
75
|
+
private findChannelIdBySessionId;
|
|
39
76
|
private currentSessionStoreAgentId;
|
|
40
77
|
private ensureSessionStoreLoaded;
|
|
41
|
-
private
|
|
78
|
+
private readPersistedSessionId;
|
|
42
79
|
private persistSession;
|
|
43
80
|
private persistSessionBestEffort;
|
|
44
|
-
private
|
|
45
|
-
private
|
|
46
|
-
private resetSessionIfCwdChanged;
|
|
47
|
-
private resetPersistedSessionBestEffort;
|
|
81
|
+
private clearPersistedSession;
|
|
82
|
+
private clearPersistedSessionBestEffort;
|
|
48
83
|
private enqueueSessionStoreWrite;
|
|
49
|
-
private
|
|
84
|
+
private trackSessionStoreOperation;
|
|
85
|
+
private shutdownBackend;
|
|
86
|
+
private waitForPendingSessionStarts;
|
|
87
|
+
private waitForPendingSessionCloses;
|
|
88
|
+
private waitForChildExit;
|
|
50
89
|
}
|
|
51
90
|
export {};
|