@borgee/agents-host 0.2.1 → 0.2.26

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 (78) hide show
  1. package/README.md +184 -21
  2. package/dist/agents-host-supervisor.d.ts +7 -5
  3. package/dist/agents-host-supervisor.js +24 -4
  4. package/dist/agents-host.d.ts +89 -15
  5. package/dist/agents-host.js +2099 -142
  6. package/dist/chat/chat-control-plane.d.ts +14 -3
  7. package/dist/chat/sdk-chat-control-plane.d.ts +16 -4
  8. package/dist/chat/sdk-chat-control-plane.js +69 -9
  9. package/dist/cli-args.d.ts +46 -5
  10. package/dist/cli-args.js +313 -32
  11. package/dist/cli.d.ts +9 -0
  12. package/dist/cli.js +112 -5
  13. package/dist/compatibility-gates.d.ts +35 -0
  14. package/dist/compatibility-gates.js +127 -0
  15. package/dist/config.d.ts +1 -0
  16. package/dist/config.js +23 -5
  17. package/dist/connections-state-store.d.ts +81 -0
  18. package/dist/connections-state-store.js +228 -0
  19. package/dist/context/injection.d.ts +109 -0
  20. package/dist/context/injection.js +350 -0
  21. package/dist/context/prompt.d.ts +4 -1
  22. package/dist/context/prompt.js +170 -1
  23. package/dist/context/turn-preparation.d.ts +9 -0
  24. package/dist/context/turn-preparation.js +106 -0
  25. package/dist/debug.d.ts +44 -0
  26. package/dist/debug.js +135 -0
  27. package/dist/gateway/localhost-gateway.d.ts +52 -0
  28. package/dist/gateway/localhost-gateway.js +857 -0
  29. package/dist/index.js +7 -5
  30. package/dist/local-config.d.ts +4 -1
  31. package/dist/local-config.js +24 -7
  32. package/dist/managed-daemon-log.d.ts +34 -0
  33. package/dist/managed-daemon-log.js +261 -0
  34. package/dist/managed-daemon.d.ts +220 -0
  35. package/dist/managed-daemon.js +1601 -0
  36. package/dist/policy/authorization-audit.d.ts +63 -0
  37. package/dist/policy/authorization-audit.js +94 -0
  38. package/dist/policy/copilot-permission.d.ts +15 -0
  39. package/dist/policy/copilot-permission.js +193 -0
  40. package/dist/policy/gateway-authorization.d.ts +42 -0
  41. package/dist/policy/gateway-authorization.js +162 -0
  42. package/dist/providers/awaiting-user.d.ts +12 -0
  43. package/dist/providers/awaiting-user.js +151 -0
  44. package/dist/providers/claude/adapter.d.ts +3 -1
  45. package/dist/providers/claude/adapter.js +8 -12
  46. package/dist/providers/claude/cli-client.d.ts +12 -5
  47. package/dist/providers/claude/cli-client.js +184 -37
  48. package/dist/providers/claude/session-store.d.ts +24 -0
  49. package/dist/providers/claude/session-store.js +65 -12
  50. package/dist/providers/codex/adapter.d.ts +11 -0
  51. package/dist/providers/codex/adapter.js +19 -0
  52. package/dist/providers/codex/cli-client.d.ts +103 -0
  53. package/dist/providers/codex/cli-client.js +1133 -0
  54. package/dist/providers/codex/project-doc.d.ts +3 -0
  55. package/dist/providers/codex/project-doc.js +66 -0
  56. package/dist/providers/codex/session-store.d.ts +38 -0
  57. package/dist/providers/codex/session-store.js +150 -0
  58. package/dist/providers/copilot/adapter.d.ts +3 -1
  59. package/dist/providers/copilot/adapter.js +8 -12
  60. package/dist/providers/copilot/cli-client.d.ts +20 -2
  61. package/dist/providers/copilot/cli-client.js +251 -71
  62. package/dist/providers/copilot/session-store.d.ts +24 -0
  63. package/dist/providers/copilot/session-store.js +65 -12
  64. package/dist/providers/create-provider.d.ts +11 -2
  65. package/dist/providers/create-provider.js +131 -12
  66. package/dist/run.d.ts +1 -0
  67. package/dist/run.js +5 -2
  68. package/dist/state-paths.d.ts +13 -1
  69. package/dist/state-paths.js +84 -3
  70. package/dist/task-thread-resolution.d.ts +10 -0
  71. package/dist/task-thread-resolution.js +48 -0
  72. package/dist/types.d.ts +174 -1
  73. package/dist/visible-mentions.d.ts +3 -0
  74. package/dist/visible-mentions.js +15 -0
  75. package/package.json +19 -17
  76. package/skills/borgee-agent/SKILL.md +33 -0
  77. package/skills/borgee-agent/borgee-agent.mjs +473 -0
  78. package/skills/borgee-agent/borgee-agent.py +409 -0
@@ -1,12 +1,63 @@
1
1
  import { randomUUID } from 'node:crypto';
2
2
  import { mkdir, open, readFile, rename, unlink } from 'node:fs/promises';
3
3
  import { dirname } from 'node:path';
4
- async function syncDirectory(path) {
5
- const directoryHandle = await open(path, 'r');
4
+ const nodeFileSystem = {
5
+ async mkdir(path, options) {
6
+ await mkdir(path, options);
7
+ },
8
+ async openDirectory(path) {
9
+ return open(path, 'r');
10
+ },
11
+ async openFile(path, flags, mode) {
12
+ return open(path, flags, mode);
13
+ },
14
+ async readFile(path, encoding) {
15
+ return readFile(path, encoding);
16
+ },
17
+ async rename(from, to) {
18
+ await rename(from, to);
19
+ },
20
+ async unlink(path) {
21
+ await unlink(path);
22
+ },
23
+ };
24
+ function shouldIgnoreDirectorySyncError(platform, error) {
25
+ const code = error.code;
26
+ return platform === 'win32' && (code === 'EPERM' || code === 'EINVAL');
27
+ }
28
+ async function syncDirectory(path, fileSystem, platform) {
29
+ let directoryHandle;
6
30
  try {
7
- await directoryHandle.sync();
31
+ try {
32
+ directoryHandle = await fileSystem.openDirectory(path);
33
+ }
34
+ catch (error) {
35
+ if (!shouldIgnoreDirectorySyncError(platform, error)) {
36
+ throw error;
37
+ }
38
+ return;
39
+ }
40
+ try {
41
+ await directoryHandle.sync();
42
+ }
43
+ catch (error) {
44
+ if (!shouldIgnoreDirectorySyncError(platform, error)) {
45
+ throw error;
46
+ }
47
+ }
48
+ }
49
+ catch (error) {
50
+ if (directoryHandle) {
51
+ try {
52
+ await directoryHandle.close();
53
+ }
54
+ catch {
55
+ // Preserve the original directory durability failure.
56
+ }
57
+ }
58
+ throw error;
8
59
  }
9
- finally {
60
+ if (directoryHandle) {
10
61
  await directoryHandle.close();
11
62
  }
12
63
  }
@@ -30,7 +81,7 @@ export class FileCopilotChannelSessionStore {
30
81
  this.options = options;
31
82
  }
32
83
  async loadFromPath(filePath) {
33
- const raw = await readFile(filePath, 'utf8');
84
+ const raw = await (this.options.fileSystem ?? nodeFileSystem).readFile(filePath, 'utf8');
34
85
  return normalizePersistedSessions(JSON.parse(raw), filePath);
35
86
  }
36
87
  async load(agentId) {
@@ -48,11 +99,13 @@ export class FileCopilotChannelSessionStore {
48
99
  async save(agentId, sessions) {
49
100
  const filePath = this.options.resolvePath(agentId);
50
101
  const parentPath = dirname(filePath);
51
- await mkdir(parentPath, { recursive: true, mode: 0o700 });
102
+ const fileSystem = this.options.fileSystem ?? nodeFileSystem;
103
+ const platform = this.options.platform ?? process.platform;
104
+ await fileSystem.mkdir(parentPath, { recursive: true, mode: 0o700 });
52
105
  const entries = Object.entries(sessions).sort(([left], [right]) => left.localeCompare(right));
53
106
  if (entries.length === 0) {
54
107
  try {
55
- await unlink(filePath);
108
+ await fileSystem.unlink(filePath);
56
109
  }
57
110
  catch (error) {
58
111
  if (error.code !== 'ENOENT') {
@@ -60,21 +113,21 @@ export class FileCopilotChannelSessionStore {
60
113
  }
61
114
  return;
62
115
  }
63
- await syncDirectory(parentPath);
116
+ await syncDirectory(parentPath, fileSystem, platform);
64
117
  return;
65
118
  }
66
119
  const temporaryPath = `${filePath}.${process.pid}.${randomUUID()}.tmp`;
67
120
  let temporaryHandle;
68
121
  try {
69
- temporaryHandle = await open(temporaryPath, 'wx', 0o600);
122
+ temporaryHandle = await fileSystem.openFile(temporaryPath, 'wx', 0o600);
70
123
  await temporaryHandle.writeFile(JSON.stringify(Object.fromEntries(entries)), {
71
124
  encoding: 'utf8',
72
125
  });
73
126
  await temporaryHandle.sync();
74
127
  await temporaryHandle.close();
75
128
  temporaryHandle = undefined;
76
- await rename(temporaryPath, filePath);
77
- await syncDirectory(parentPath);
129
+ await fileSystem.rename(temporaryPath, filePath);
130
+ await syncDirectory(parentPath, fileSystem, platform);
78
131
  }
79
132
  catch (error) {
80
133
  if (temporaryHandle) {
@@ -86,7 +139,7 @@ export class FileCopilotChannelSessionStore {
86
139
  }
87
140
  }
88
141
  try {
89
- await unlink(temporaryPath);
142
+ await fileSystem.unlink(temporaryPath);
90
143
  }
91
144
  catch {
92
145
  // Cleanup is best effort.
@@ -1,3 +1,12 @@
1
- import type { ProviderAdapter } from './provider-adapter.js';
1
+ import type { DebugLogger } from '../debug.js';
2
2
  import type { ProviderRuntimeConfig } from '../types.js';
3
- export declare function createProvider(config: ProviderRuntimeConfig): ProviderAdapter;
3
+ import type { ProviderAdapter } from './provider-adapter.js';
4
+ import type { LocalhostGatewayContextPublisher } from '../context/injection.js';
5
+ import type { AuthorizationAuditSinkLike } from '../policy/authorization-audit.js';
6
+ interface CreateProviderOptions {
7
+ compatibilityGates?: ReadonlySet<string>;
8
+ localhostGateway?: LocalhostGatewayContextPublisher;
9
+ authorizationAuditSink?: AuthorizationAuditSinkLike;
10
+ }
11
+ export declare function createProvider(config: ProviderRuntimeConfig, debugLogger?: DebugLogger, options?: CreateProviderOptions): ProviderAdapter;
12
+ export {};
@@ -1,25 +1,144 @@
1
+ import { FileChannelContextStore } from '../context/injection.js';
2
+ import { createProviderConnectionsSessionStore } from '../connections-state-store.js';
3
+ import { ProviderTurnPreparer } from '../context/turn-preparation.js';
4
+ import { CLAUDE_PROVIDER_V2_COMPATIBILITY_GATE, CONNECTIONS_STATE_LAYER_COMPATIBILITY_GATE, CONTEXT_INJECTION_COMPATIBILITY_GATE, CODEX_PROVIDER_COMPATIBILITY_GATE, COPILOT_PROVIDER_V2_COMPATIBILITY_GATE, INTERNAL_PROVIDER_IMPLEMENTATIONS_ENV, LOCALHOST_GATEWAY_COMPATIBILITY_GATE, parseInternalProviderImplementationOverrides, POLICY_AUDIT_ENFORCEMENT_COMPATIBILITY_GATE, resolveInternalPolicyMode, resolveInternalCompatibilityGates, SKILL_RUNTIME_COMPATIBILITY_GATE, } from '../compatibility-gates.js';
1
5
  import { ClaudeCliClient } from './claude/cli-client.js';
2
6
  import { ClaudeProviderAdapter } from './claude/adapter.js';
3
7
  import { FileClaudeChannelSessionStore } from './claude/session-store.js';
8
+ import { CodexCliClient } from './codex/cli-client.js';
9
+ import { CodexProviderAdapter } from './codex/adapter.js';
10
+ import { FileCodexChannelSessionStore } from './codex/session-store.js';
4
11
  import { CopilotCliClient } from './copilot/cli-client.js';
5
12
  import { CopilotProviderAdapter } from './copilot/adapter.js';
6
13
  import { FileCopilotChannelSessionStore } from './copilot/session-store.js';
7
- import { resolveClaudeSessionMapPath, resolveCopilotSessionMapPath } from '../state-paths.js';
8
- export function createProvider(config) {
14
+ import { resolveClaudeSessionMapPath, resolveCodexSessionMapPath, resolveCopilotSessionMapPath, } from '../state-paths.js';
15
+ import { createAwaitingUserProgressHandler, parseProviderReply } from './awaiting-user.js';
16
+ class ProviderV2CompatibilityAdapter {
17
+ provider;
18
+ turnPreparer;
19
+ constructor(provider, turnPreparer) {
20
+ this.provider = provider;
21
+ this.turnPreparer = turnPreparer;
22
+ }
23
+ async generateReply(input, options) {
24
+ const preparedTurn = await this.turnPreparer.prepare(input);
25
+ return this.provider.generateReply({
26
+ turn: preparedTurn,
27
+ }, options);
28
+ }
29
+ async dispose() {
30
+ await this.provider.shutdown?.();
31
+ }
32
+ }
33
+ class CliBackedProviderV2 {
34
+ cli;
35
+ constructor(cli) {
36
+ this.cli = cli;
37
+ }
38
+ async generateReply(input, options) {
39
+ const text = await this.cli.generateReply(input.turn, {
40
+ onProgress: createAwaitingUserProgressHandler(options?.onProgress),
41
+ });
42
+ return parseProviderReply(text);
43
+ }
44
+ async shutdown() {
45
+ await this.cli.dispose();
46
+ }
47
+ }
48
+ class ClaudeProviderV2 extends CliBackedProviderV2 {
49
+ }
50
+ class CopilotProviderV2 extends CliBackedProviderV2 {
51
+ }
52
+ const TASK_WORKSPACE_ROOT_DIR_ENV = 'AGENTS_HOST_INTERNAL_TASK_WORKSPACE_ROOT_DIR';
53
+ const PROVIDER_V2_COMPATIBILITY_GATES = {
54
+ claude: CLAUDE_PROVIDER_V2_COMPATIBILITY_GATE,
55
+ copilot: COPILOT_PROVIDER_V2_COMPATIBILITY_GATE,
56
+ };
57
+ function resolveProviderImplementation(provider, routingConfig) {
58
+ if (!routingConfig.compatibilityGates.has(PROVIDER_V2_COMPATIBILITY_GATES[provider])) {
59
+ return 'v1';
60
+ }
61
+ return routingConfig.implementationOverrides[provider] ?? 'v2';
62
+ }
63
+ function createClaudeProvider(config, debugLogger, connectionsStateGateEnabled, implementation, turnPreparer) {
64
+ const cli = new ClaudeCliClient(config.claudeCommand, config.claudeArgs, {}, createProviderConnectionsSessionStore({
65
+ provider: 'claude',
66
+ legacyStore: new FileClaudeChannelSessionStore({
67
+ resolvePath: (agentId) => resolveClaudeSessionMapPath(config.stateRootDir, agentId),
68
+ }),
69
+ stateRootDir: config.stateRootDir,
70
+ gateEnabled: connectionsStateGateEnabled,
71
+ logger: debugLogger,
72
+ }), config.resolveStableAgentId, debugLogger);
73
+ return implementation === 'v2'
74
+ ? new ProviderV2CompatibilityAdapter(new ClaudeProviderV2(cli), turnPreparer)
75
+ : new ClaudeProviderAdapter(cli, turnPreparer);
76
+ }
77
+ function createCopilotProvider(config, debugLogger, connectionsStateGateEnabled, implementation, turnPreparer, policyAuditGateEnabled, authorizationAuditSink) {
78
+ const policyMode = resolveInternalPolicyMode(policyAuditGateEnabled);
79
+ const cli = new CopilotCliClient(config.copilotCommand, config.copilotArgs, {
80
+ idleSessionTtlMs: config.copilotSessionTtlMinutes * 60 * 1000,
81
+ }, createProviderConnectionsSessionStore({
82
+ provider: 'copilot',
83
+ legacyStore: new FileCopilotChannelSessionStore({
84
+ resolvePath: (agentId) => resolveCopilotSessionMapPath(config.stateRootDir, agentId),
85
+ }),
86
+ stateRootDir: config.stateRootDir,
87
+ gateEnabled: connectionsStateGateEnabled,
88
+ logger: debugLogger,
89
+ }), config.resolveStableAgentId, debugLogger, {
90
+ gateEnabled: policyAuditGateEnabled,
91
+ policyMode,
92
+ auditSink: authorizationAuditSink,
93
+ });
94
+ return implementation === 'v2'
95
+ ? new ProviderV2CompatibilityAdapter(new CopilotProviderV2(cli), turnPreparer)
96
+ : new CopilotProviderAdapter(cli, turnPreparer);
97
+ }
98
+ function createCodexProvider(config, debugLogger, connectionsStateGateEnabled, turnPreparer) {
99
+ const cli = new CodexCliClient(config.codexCommand, config.codexArgs, {}, createProviderConnectionsSessionStore({
100
+ provider: 'codex',
101
+ legacyStore: new FileCodexChannelSessionStore({
102
+ resolvePath: (agentId) => resolveCodexSessionMapPath(config.stateRootDir, agentId),
103
+ }),
104
+ stateRootDir: config.stateRootDir,
105
+ gateEnabled: connectionsStateGateEnabled,
106
+ logger: debugLogger,
107
+ }), config.resolveStableAgentId, debugLogger);
108
+ return new CodexProviderAdapter(cli, turnPreparer);
109
+ }
110
+ export function createProvider(config, debugLogger, options = {}) {
111
+ const routingConfig = {
112
+ compatibilityGates: options.compatibilityGates ?? resolveInternalCompatibilityGates(),
113
+ implementationOverrides: parseInternalProviderImplementationOverrides(process.env[INTERNAL_PROVIDER_IMPLEMENTATIONS_ENV]),
114
+ };
115
+ const connectionsStateGateEnabled = routingConfig.compatibilityGates.has(CONNECTIONS_STATE_LAYER_COMPATIBILITY_GATE);
116
+ const contextInjectionGateEnabled = routingConfig.compatibilityGates.has(CONTEXT_INJECTION_COMPATIBILITY_GATE);
117
+ const skillRuntimeGateEnabled = routingConfig.compatibilityGates.has(SKILL_RUNTIME_COMPATIBILITY_GATE);
118
+ const localhostGatewayGateEnabled = routingConfig.compatibilityGates.has(LOCALHOST_GATEWAY_COMPATIBILITY_GATE);
119
+ const policyAuditGateEnabled = routingConfig.compatibilityGates.has(POLICY_AUDIT_ENFORCEMENT_COMPATIBILITY_GATE);
120
+ const channelContextStore = contextInjectionGateEnabled
121
+ ? new FileChannelContextStore(config.stateRootDir, {
122
+ skillRuntimeEnabled: skillRuntimeGateEnabled,
123
+ taskWorkspaceRootDir: process.env[TASK_WORKSPACE_ROOT_DIR_ENV],
124
+ localhostGateway: skillRuntimeGateEnabled && localhostGatewayGateEnabled
125
+ ? options.localhostGateway
126
+ : undefined,
127
+ })
128
+ : undefined;
129
+ const turnPreparer = new ProviderTurnPreparer(channelContextStore, debugLogger);
9
130
  switch (config.provider) {
10
131
  case 'claude': {
11
- const cli = new ClaudeCliClient(config.claudeCommand, config.claudeArgs, {}, new FileClaudeChannelSessionStore({
12
- resolvePath: (agentId) => resolveClaudeSessionMapPath(config.stateRootDir, agentId),
13
- }), config.resolveStableAgentId);
14
- return new ClaudeProviderAdapter(cli);
132
+ return createClaudeProvider(config, debugLogger, connectionsStateGateEnabled, resolveProviderImplementation('claude', routingConfig), turnPreparer);
133
+ }
134
+ case 'codex': {
135
+ if (!routingConfig.compatibilityGates.has(CODEX_PROVIDER_COMPATIBILITY_GATE)) {
136
+ throw new Error(`Unsupported provider: ${String(config.provider)} (enable ${CODEX_PROVIDER_COMPATIBILITY_GATE})`);
137
+ }
138
+ return createCodexProvider(config, debugLogger, connectionsStateGateEnabled, turnPreparer);
15
139
  }
16
140
  case 'copilot': {
17
- const cli = new CopilotCliClient(config.copilotCommand, config.copilotArgs, {
18
- idleSessionTtlMs: config.copilotSessionTtlMinutes * 60 * 1000,
19
- }, new FileCopilotChannelSessionStore({
20
- resolvePath: (agentId) => resolveCopilotSessionMapPath(config.stateRootDir, agentId),
21
- }), config.resolveStableAgentId);
22
- return new CopilotProviderAdapter(cli);
141
+ return createCopilotProvider(config, debugLogger, connectionsStateGateEnabled, resolveProviderImplementation('copilot', routingConfig), turnPreparer, policyAuditGateEnabled, options.authorizationAuditSink);
23
142
  }
24
143
  default:
25
144
  throw new Error(`Unsupported provider: ${String(config.provider)}`);
package/dist/run.d.ts CHANGED
@@ -2,6 +2,7 @@ import { parseGenerateConfigSpec, resolveLocalConfigLayout } from './local-confi
2
2
  import type { LocalConfigGenerateResult, LocalConfigGenerateSpec, LocalConfigSnapshot } from './types.js';
3
3
  export interface RunMainOptions {
4
4
  configPath?: string;
5
+ debug?: boolean;
5
6
  }
6
7
  export interface ValidateLocalConfigDeps {
7
8
  loadSnapshot?: (configPath: string) => Promise<LocalConfigSnapshot>;
package/dist/run.js CHANGED
@@ -43,9 +43,12 @@ export async function generateLocalConfig(rootPath, specJson, sourceLabelOrDeps
43
43
  writer.log(JSON.stringify(result, null, 2));
44
44
  }
45
45
  export async function runMain(options = {}) {
46
+ const runtimeOptions = {
47
+ debug: options.debug,
48
+ };
46
49
  const host = options.configPath
47
- ? new AgentsHostSupervisor(options.configPath)
48
- : new AgentsHost(loadConfigFromEnv());
50
+ ? new AgentsHostSupervisor(options.configPath, runtimeOptions)
51
+ : new AgentsHost(loadConfigFromEnv(), { runtimeOptions });
49
52
  const shutdown = (signal) => {
50
53
  console.log(`[agents-host] received ${signal}, shutting down`);
51
54
  host
@@ -1,6 +1,18 @@
1
- export declare function resolveSingleAgentStateRoot(env?: NodeJS.ProcessEnv, resolvedHomeDir?: string): string;
1
+ export declare function normalizeManagedRuntimeKey(serverUrl: string): string;
2
+ export declare function resolveSingleAgentStateRoot(env?: NodeJS.ProcessEnv, resolvedHomeDir?: string, agentKey?: string): string;
3
+ export declare function resolveManagedRuntimeRoot(serverUrl: string, env?: NodeJS.ProcessEnv, resolvedHomeDir?: string): string;
2
4
  export declare function resolveManagedStateRoot(rootPath: string): string;
5
+ export declare function resolveManagedDaemonSocketPath(rootPath: string): string;
6
+ export declare function resolveManagedDaemonLogPath(rootPath: string): string;
7
+ export declare function resolveManagedRuntimeSettingsPath(rootPath: string): string;
8
+ export declare function resolveManagedBootstrapLockPath(rootPath: string): string;
3
9
  export declare function resolveLocalConfigAgentStateRoot(rootPath: string, agentKey: string): string;
4
10
  export declare function resolveAgentCursorPath(stateRootDir: string, agentId: string): string;
11
+ export declare function resolveSharedProtocolKickoffDecisionPath(stateRootDir: string, channelId: string, anchorMessageId: string): string;
12
+ export declare function resolveSharedProtocolStatusPath(stateRootDir: string, channelId: string, anchorMessageId: string): string;
13
+ export declare function resolveConnectionsStatePath(stateRootDir: string): string;
14
+ export declare function resolveAuthorizationAuditPath(stateRootDir: string): string;
15
+ export declare function resolvePreviousAuthorizationAuditPath(stateRootDir: string): string;
5
16
  export declare function resolveClaudeSessionMapPath(stateRootDir: string, agentId: string): string;
17
+ export declare function resolveCodexSessionMapPath(stateRootDir: string, agentId: string): string;
6
18
  export declare function resolveCopilotSessionMapPath(stateRootDir: string, agentId: string): string;
@@ -1,11 +1,17 @@
1
1
  import { createHash } from 'node:crypto';
2
2
  import { homedir } from 'node:os';
3
- import { join, resolve } from 'node:path';
3
+ import { dirname, join, resolve } from 'node:path';
4
4
  const SINGLE_AGENT_HOME_ROOT = '.borgee';
5
5
  const AGENTS_HOST_ROOT = 'agents-host';
6
6
  const SINGLE_AGENT_NAMESPACE = 'single-agent';
7
+ const MANAGED_RUNTIME_NAMESPACE = 'managed';
7
8
  const MANAGED_STATE_DIRNAME = '.state';
9
+ const MANAGED_DAEMON_SOCKET_FILENAME = 'ctl.sock';
10
+ const MANAGED_DAEMON_LOG_FILENAME = 'daemon.log';
11
+ const MANAGED_RUNTIME_SETTINGS_FILENAME = 'managed-runtime-settings.json';
12
+ const MANAGED_BOOTSTRAP_LOCK_DIRNAME = '.bootstrap.lock';
8
13
  const STATE_ROOT_LABEL_MAX_LENGTH = 48;
14
+ const MANAGED_RUNTIME_LABEL_MAX_LENGTH = 24;
9
15
  function encodeSegment(value) {
10
16
  return encodeURIComponent(value);
11
17
  }
@@ -22,25 +28,100 @@ function sanitizeStateRootLabel(agentKey) {
22
28
  function hashStateRootKey(agentKey) {
23
29
  return createHash('sha256').update(agentKey).digest('hex').slice(0, 12);
24
30
  }
25
- export function resolveSingleAgentStateRoot(env = process.env, resolvedHomeDir = homedir()) {
31
+ export function normalizeManagedRuntimeKey(serverUrl) {
32
+ const trimmed = serverUrl.trim();
33
+ try {
34
+ const parsed = new URL(trimmed);
35
+ parsed.pathname = parsed.pathname.replace(/\/+$/u, '') || '/';
36
+ return parsed.toString();
37
+ }
38
+ catch {
39
+ return trimmed.replace(/\/+$/u, '');
40
+ }
41
+ }
42
+ function sanitizeManagedRuntimeLabel(serverUrl) {
43
+ const normalized = normalizeManagedRuntimeKey(serverUrl);
44
+ let labelSource = normalized;
45
+ try {
46
+ const parsed = new URL(normalized);
47
+ labelSource = `${parsed.hostname}${parsed.pathname === '/' ? '' : parsed.pathname}`;
48
+ }
49
+ catch {
50
+ labelSource = normalized;
51
+ }
52
+ const sanitized = encodeSegment(labelSource)
53
+ .toLowerCase()
54
+ .replace(/%/g, '-')
55
+ .replace(/[^a-z0-9._-]+/g, '-')
56
+ .replace(/-+/g, '-')
57
+ .replace(/^-|-$/g, '')
58
+ .slice(0, MANAGED_RUNTIME_LABEL_MAX_LENGTH);
59
+ return sanitized.length > 0 ? sanitized : 'server';
60
+ }
61
+ function hashManagedRuntimeKey(serverUrl) {
62
+ return createHash('sha256').update(normalizeManagedRuntimeKey(serverUrl)).digest('hex').slice(0, 12);
63
+ }
64
+ export function resolveSingleAgentStateRoot(env = process.env, resolvedHomeDir = homedir(), agentKey) {
65
+ const home = env.HOME?.trim() || resolvedHomeDir.trim();
66
+ if (!home) {
67
+ throw new Error('Unable to resolve a user home directory for agents-host state');
68
+ }
69
+ const baseRoot = join(home, SINGLE_AGENT_HOME_ROOT, AGENTS_HOST_ROOT, SINGLE_AGENT_NAMESPACE);
70
+ const normalizedAgentKey = agentKey?.trim();
71
+ if (!normalizedAgentKey) {
72
+ return baseRoot;
73
+ }
74
+ return join(baseRoot, `agent-${hashStateRootKey(normalizedAgentKey)}`);
75
+ }
76
+ export function resolveManagedRuntimeRoot(serverUrl, env = process.env, resolvedHomeDir = homedir()) {
26
77
  const home = env.HOME?.trim() || resolvedHomeDir.trim();
27
78
  if (!home) {
28
79
  throw new Error('Unable to resolve a user home directory for agents-host state');
29
80
  }
30
- return join(home, SINGLE_AGENT_HOME_ROOT, AGENTS_HOST_ROOT, SINGLE_AGENT_NAMESPACE);
81
+ return join(home, SINGLE_AGENT_HOME_ROOT, AGENTS_HOST_ROOT, MANAGED_RUNTIME_NAMESPACE, `${sanitizeManagedRuntimeLabel(serverUrl)}-${hashManagedRuntimeKey(serverUrl)}`);
31
82
  }
32
83
  export function resolveManagedStateRoot(rootPath) {
33
84
  return join(resolve(rootPath), MANAGED_STATE_DIRNAME);
34
85
  }
86
+ export function resolveManagedDaemonSocketPath(rootPath) {
87
+ return join(resolve(rootPath), MANAGED_DAEMON_SOCKET_FILENAME);
88
+ }
89
+ export function resolveManagedDaemonLogPath(rootPath) {
90
+ return join(resolve(rootPath), MANAGED_DAEMON_LOG_FILENAME);
91
+ }
92
+ export function resolveManagedRuntimeSettingsPath(rootPath) {
93
+ return join(resolve(rootPath), MANAGED_RUNTIME_SETTINGS_FILENAME);
94
+ }
95
+ export function resolveManagedBootstrapLockPath(rootPath) {
96
+ return join(resolve(rootPath), MANAGED_BOOTSTRAP_LOCK_DIRNAME);
97
+ }
35
98
  export function resolveLocalConfigAgentStateRoot(rootPath, agentKey) {
36
99
  return join(resolveManagedStateRoot(rootPath), `${sanitizeStateRootLabel(agentKey)}-${hashStateRootKey(agentKey)}`);
37
100
  }
38
101
  export function resolveAgentCursorPath(stateRootDir, agentId) {
39
102
  return join(stateRootDir, `bpp-cursor-${encodeSegment(agentId)}.json`);
40
103
  }
104
+ export function resolveSharedProtocolKickoffDecisionPath(stateRootDir, channelId, anchorMessageId) {
105
+ return join(dirname(resolve(stateRootDir)), 'protocol-kickoff-decisions', `${encodeSegment(channelId)}--${encodeSegment(anchorMessageId)}.json`);
106
+ }
107
+ export function resolveSharedProtocolStatusPath(stateRootDir, channelId, anchorMessageId) {
108
+ return join(dirname(resolve(stateRootDir)), 'protocol-status', `${encodeSegment(channelId)}--${encodeSegment(anchorMessageId)}.json`);
109
+ }
110
+ export function resolveConnectionsStatePath(stateRootDir) {
111
+ return join(stateRootDir, 'connections-state.sqlite');
112
+ }
113
+ export function resolveAuthorizationAuditPath(stateRootDir) {
114
+ return join(stateRootDir, 'authorization-audit.jsonl');
115
+ }
116
+ export function resolvePreviousAuthorizationAuditPath(stateRootDir) {
117
+ return join(stateRootDir, 'authorization-audit.previous.jsonl');
118
+ }
41
119
  export function resolveClaudeSessionMapPath(stateRootDir, agentId) {
42
120
  return join(stateRootDir, `claude-channel-sessions-${encodeSegment(agentId)}.json`);
43
121
  }
122
+ export function resolveCodexSessionMapPath(stateRootDir, agentId) {
123
+ return join(stateRootDir, `codex-channel-sessions-${encodeSegment(agentId)}.json`);
124
+ }
44
125
  export function resolveCopilotSessionMapPath(stateRootDir, agentId) {
45
126
  return join(stateRootDir, `copilot-channel-sessions-${encodeSegment(agentId)}.json`);
46
127
  }
@@ -0,0 +1,10 @@
1
+ import type { ChatControlPlane } from './chat/chat-control-plane.js';
2
+ import type { Task } from './types.js';
3
+ interface WaitForTaskForThreadOptions {
4
+ preferredTaskId?: string;
5
+ attempts?: number;
6
+ delayMs?: number;
7
+ }
8
+ export declare function findTaskForThread(controlPlane: ChatControlPlane, threadId: string, preferredTaskId?: string): Promise<Task | null>;
9
+ export declare function waitForTaskForThread(controlPlane: ChatControlPlane, threadId: string, options?: WaitForTaskForThreadOptions): Promise<Task | null>;
10
+ export {};
@@ -0,0 +1,48 @@
1
+ export async function findTaskForThread(controlPlane, threadId, preferredTaskId) {
2
+ if (preferredTaskId) {
3
+ try {
4
+ const task = await controlPlane.getTask({ taskId: preferredTaskId });
5
+ if (task.threadId === threadId) {
6
+ return task;
7
+ }
8
+ }
9
+ catch {
10
+ // Fall through to the visibility-based compatibility scan.
11
+ }
12
+ }
13
+ const channels = await controlPlane.listChannels();
14
+ let matchedTask = null;
15
+ for (const channel of channels) {
16
+ let tasks;
17
+ try {
18
+ tasks = await controlPlane.listTasks({ channelId: channel.id });
19
+ }
20
+ catch {
21
+ continue;
22
+ }
23
+ for (const task of tasks) {
24
+ if (task.threadId !== threadId) {
25
+ continue;
26
+ }
27
+ if (matchedTask && matchedTask.id !== task.id) {
28
+ throw new Error(`multiple tasks matched thread ${threadId}`);
29
+ }
30
+ matchedTask = task;
31
+ }
32
+ }
33
+ return matchedTask;
34
+ }
35
+ export async function waitForTaskForThread(controlPlane, threadId, options = {}) {
36
+ const attempts = Math.max(1, options.attempts ?? 1);
37
+ const delayMs = Math.max(0, options.delayMs ?? 0);
38
+ for (let attempt = 0; attempt < attempts; attempt += 1) {
39
+ const task = await findTaskForThread(controlPlane, threadId, options.preferredTaskId);
40
+ if (task) {
41
+ return task;
42
+ }
43
+ if (attempt < attempts - 1 && delayMs > 0) {
44
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
45
+ }
46
+ }
47
+ return null;
48
+ }