@borgee/agents-host 0.2.2 → 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 (76) 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 -141
  6. package/dist/chat/chat-control-plane.d.ts +13 -2
  7. package/dist/chat/sdk-chat-control-plane.d.ts +14 -3
  8. package/dist/chat/sdk-chat-control-plane.js +54 -2
  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 +1 -0
  49. package/dist/providers/codex/adapter.d.ts +11 -0
  50. package/dist/providers/codex/adapter.js +19 -0
  51. package/dist/providers/codex/cli-client.d.ts +103 -0
  52. package/dist/providers/codex/cli-client.js +1133 -0
  53. package/dist/providers/codex/project-doc.d.ts +3 -0
  54. package/dist/providers/codex/project-doc.js +66 -0
  55. package/dist/providers/codex/session-store.d.ts +38 -0
  56. package/dist/providers/codex/session-store.js +150 -0
  57. package/dist/providers/copilot/adapter.d.ts +3 -1
  58. package/dist/providers/copilot/adapter.js +8 -12
  59. package/dist/providers/copilot/cli-client.d.ts +20 -2
  60. package/dist/providers/copilot/cli-client.js +251 -71
  61. package/dist/providers/copilot/session-store.d.ts +1 -0
  62. package/dist/providers/create-provider.d.ts +11 -2
  63. package/dist/providers/create-provider.js +131 -12
  64. package/dist/run.d.ts +1 -0
  65. package/dist/run.js +5 -2
  66. package/dist/state-paths.d.ts +13 -1
  67. package/dist/state-paths.js +84 -3
  68. package/dist/task-thread-resolution.d.ts +10 -0
  69. package/dist/task-thread-resolution.js +48 -0
  70. package/dist/types.d.ts +174 -1
  71. package/dist/visible-mentions.d.ts +3 -0
  72. package/dist/visible-mentions.js +15 -0
  73. package/package.json +19 -17
  74. package/skills/borgee-agent/SKILL.md +33 -0
  75. package/skills/borgee-agent/borgee-agent.mjs +473 -0
  76. package/skills/borgee-agent/borgee-agent.py +409 -0
@@ -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
+ }
package/dist/types.d.ts CHANGED
@@ -1,7 +1,9 @@
1
- export type ProviderKind = 'claude' | 'copilot';
1
+ export type ProviderKind = 'claude' | 'codex' | 'copilot';
2
2
  export interface ProviderCommandConfig {
3
3
  claudeCommand: string;
4
4
  claudeArgs: string[];
5
+ codexCommand: string;
6
+ codexArgs: string[];
5
7
  copilotCommand: string;
6
8
  copilotArgs: string[];
7
9
  copilotSessionTtlMinutes: number;
@@ -83,6 +85,7 @@ export interface LocalConfigGenerateResult {
83
85
  }
84
86
  export interface ChannelMessageEvent {
85
87
  type?: string;
88
+ message_type?: string;
86
89
  channel_id: string;
87
90
  channel_type?: string;
88
91
  message_id?: string;
@@ -99,19 +102,189 @@ export interface MeResponseUser {
99
102
  display_name?: string;
100
103
  role?: string;
101
104
  }
105
+ export interface DirectoryUser {
106
+ id: string;
107
+ display_name?: string;
108
+ kind: 'user' | 'agent';
109
+ }
110
+ export interface ChannelHistoryEntry {
111
+ id: string;
112
+ channelId: string;
113
+ authorId: string;
114
+ body: string;
115
+ contentType?: string;
116
+ type?: string;
117
+ replyToId?: string;
118
+ createdAt: number;
119
+ editedAt?: number;
120
+ }
121
+ export interface ReadChannelHistoryInput {
122
+ channelId: string;
123
+ before?: number;
124
+ after?: number;
125
+ limit: number;
126
+ }
127
+ export interface ChannelSummary {
128
+ id: string;
129
+ name: string;
130
+ kind?: string;
131
+ }
132
+ export interface CollaborationDraftSnapshot {
133
+ draftId: string;
134
+ channelId: string;
135
+ turnExecutionId: string;
136
+ replyToId?: string;
137
+ body: string;
138
+ status: 'streaming' | 'finalized';
139
+ updatedAt: number;
140
+ finalMessageId?: string;
141
+ }
142
+ export type ProviderCollaborationTurnMode = 'ordinary' | 'silent-kickoff' | 'protocol-managed';
143
+ export interface ProviderCollaborationContext {
144
+ enabled: boolean;
145
+ turnExecutionId?: string;
146
+ sendRoutesAllowed?: boolean;
147
+ turnMode?: ProviderCollaborationTurnMode;
148
+ kickoff?: ProviderProtocolKickoffContext;
149
+ protocol?: ProviderProtocolContext;
150
+ grounding?: ProviderTurnGroundingContext;
151
+ }
102
152
  export interface ProviderInput {
103
153
  agentName: string;
104
154
  provider: ProviderKind;
105
155
  channelId: string;
106
156
  incomingAuthorId: string;
107
157
  incomingContent: string;
158
+ incomingEventKind?: string;
159
+ incomingMessageType?: string;
160
+ collaboration?: ProviderCollaborationContext;
161
+ }
162
+ export interface ProviderProtocolContext {
163
+ anchorMessageId: string;
164
+ anchorSummary: string;
165
+ issuerId: string;
166
+ role: 'issuer' | 'intermediate' | 'final';
167
+ targetPeerId: string;
168
+ turnIndex: number;
169
+ maxTurnCount: number;
170
+ }
171
+ export interface ProviderProtocolKickoffContext {
172
+ anchorMessageId: string;
173
+ participantIds: [string, string];
174
+ issuerId: string;
175
+ }
176
+ export interface ProviderTurnIdentity {
177
+ id: string;
178
+ displayName?: string;
179
+ kind: 'human' | 'agent' | 'unknown';
108
180
  }
181
+ export interface ProviderTurnGroundingContext {
182
+ self: ProviderTurnIdentity;
183
+ incomingAuthor: ProviderTurnIdentity;
184
+ peer?: ProviderTurnIdentity;
185
+ participants?: ProviderTurnIdentity[];
186
+ hostRecognizedKickoffCandidate?: boolean;
187
+ }
188
+ export interface SkillRuntimeBootstrapMetadata {
189
+ skillDirectoryPath: string;
190
+ skillMarkdownPath: string;
191
+ nodeCliPath: string;
192
+ pythonCliPath: string;
193
+ }
194
+ export interface LocalhostGatewayBootstrapMetadata {
195
+ baseUrl: string;
196
+ collaboration?: {
197
+ enabled: true;
198
+ };
199
+ }
200
+ export interface TaskAssignmentThreadContext {
201
+ active: true;
202
+ currentTaskId?: string;
203
+ }
204
+ export interface TaskWorkspaceContext {
205
+ currentTaskId: string;
206
+ rootPath: string;
207
+ }
208
+ export interface PreparedPromptContext {
209
+ channelContextPayloadPath?: string;
210
+ gatewayAuthPath?: string;
211
+ collaborationTurnExecutionId?: string;
212
+ collaborationTurnMode?: ProviderCollaborationTurnMode;
213
+ kickoff?: ProviderProtocolKickoffContext;
214
+ protocol?: ProviderProtocolContext;
215
+ grounding?: ProviderTurnGroundingContext;
216
+ skillRuntime?: SkillRuntimeBootstrapMetadata;
217
+ localhostGateway?: LocalhostGatewayBootstrapMetadata;
218
+ taskAssignmentContext?: TaskAssignmentThreadContext;
219
+ taskWorkspace?: TaskWorkspaceContext;
220
+ }
221
+ export interface PreparedProviderSessionRouting {
222
+ key: string;
223
+ persistence: 'persistent' | 'ephemeral';
224
+ }
225
+ export interface PreparedProviderTurnInput {
226
+ channelId: string;
227
+ incomingEventKind?: string;
228
+ incomingMessageType?: string;
229
+ prompt: string;
230
+ promptContext?: PreparedPromptContext;
231
+ providerSessionRouting?: PreparedProviderSessionRouting;
232
+ }
233
+ export interface ProviderAwaitingUser {
234
+ question: string;
235
+ reason?: string;
236
+ }
237
+ export type ProviderTurnControl = {
238
+ kind: 'continue-to-peer';
239
+ } | {
240
+ kind: 'conclude-locally';
241
+ } | {
242
+ kind: 'start-protocol';
243
+ rounds: number;
244
+ } | ({
245
+ kind: 'awaiting-user';
246
+ } & ProviderAwaitingUser);
109
247
  export interface ProviderReply {
110
248
  text: string;
249
+ awaitingUser?: ProviderAwaitingUser;
250
+ control?: ProviderTurnControl;
251
+ controlMalformed?: boolean;
111
252
  }
112
253
  export interface PostedMessage {
113
254
  messageId: string;
114
255
  }
256
+ export interface PostMessageInput {
257
+ channelId: string;
258
+ body: string;
259
+ replyToId?: string;
260
+ }
261
+ export interface Task {
262
+ id: string;
263
+ channelId: string;
264
+ guildId: string;
265
+ seq?: number;
266
+ title: string;
267
+ description: string;
268
+ status: string;
269
+ assigneeId: string | null;
270
+ createdBy: string;
271
+ messageId: string | null;
272
+ threadId: string | null;
273
+ createdAt: number;
274
+ updatedAt: number;
275
+ }
276
+ export interface CreateTaskInput {
277
+ channelId: string;
278
+ title: string;
279
+ description?: string;
280
+ assigneeId?: string;
281
+ }
282
+ export interface UpdateTaskInput {
283
+ taskId: string;
284
+ status?: string;
285
+ assigneeId?: string;
286
+ title?: string;
287
+ }
115
288
  export interface ProviderProgressUpdate {
116
289
  text: string;
117
290
  }
@@ -0,0 +1,3 @@
1
+ export declare function extractVisibleMentionIds(content: string): string[];
2
+ export declare function hasVisibleMention(content: string, participantId: string): boolean;
3
+ export declare function appendVisibleMention(content: string, participantId: string): string;
@@ -0,0 +1,15 @@
1
+ export function extractVisibleMentionIds(content) {
2
+ return [...content.matchAll(/<@([^>\s]+)>/gu)]
3
+ .map((match) => match[1]?.trim())
4
+ .filter((value) => Boolean(value));
5
+ }
6
+ export function hasVisibleMention(content, participantId) {
7
+ return extractVisibleMentionIds(content).includes(participantId);
8
+ }
9
+ export function appendVisibleMention(content, participantId) {
10
+ if (hasVisibleMention(content, participantId)) {
11
+ return content;
12
+ }
13
+ const separator = /\s$/u.test(content) ? '' : ' ';
14
+ return `${content}${separator}<@${participantId}>`;
15
+ }
package/package.json CHANGED
@@ -1,17 +1,18 @@
1
1
  {
2
2
  "name": "@borgee/agents-host",
3
- "version": "0.2.2",
3
+ "version": "0.2.26",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
8
8
  "type": "module",
9
- "description": "Minimal local agents host: connects a local Claude/Copilot CLI to a Borgee agent over @borgee/plugin-sdk",
9
+ "description": "Minimal local agents host: connects local Claude/Codex/Copilot runtimes to a Borgee agent over @borgee/plugin-sdk",
10
10
  "bin": {
11
- "agents-host": "dist/cli.js"
11
+ "agents-host": "./dist/cli.js"
12
12
  },
13
13
  "files": [
14
14
  "dist",
15
+ "skills",
15
16
  "README.md"
16
17
  ],
17
18
  "repository": {
@@ -20,6 +21,20 @@
20
21
  "directory": "packages/agents-host"
21
22
  },
22
23
  "license": "MIT",
24
+ "dependencies": {
25
+ "@agentclientprotocol/codex-acp": "1.1.7",
26
+ "@agentclientprotocol/sdk": "1.3.0",
27
+ "cross-spawn": "^7.0.6",
28
+ "yaml": "^2.8.1",
29
+ "@borgee/plugin-sdk": "0.2.4"
30
+ },
31
+ "devDependencies": {
32
+ "@types/cross-spawn": "^6.0.6",
33
+ "@types/node": "^24.3.0",
34
+ "tsx": "^4.20.5",
35
+ "typescript": "^5.9.3",
36
+ "vitest": "^4.1.5"
37
+ },
23
38
  "scripts": {
24
39
  "predev": "pnpm --filter @borgee/plugin-sdk build",
25
40
  "dev": "tsx src/index.ts",
@@ -33,18 +48,5 @@
33
48
  "pretest": "pnpm --filter @borgee/plugin-sdk build",
34
49
  "test": "vitest run --testTimeout=10000",
35
50
  "pretypecheck": "pnpm --filter @borgee/plugin-sdk build"
36
- },
37
- "dependencies": {
38
- "@agentclientprotocol/sdk": "^1.2.1",
39
- "@borgee/plugin-sdk": "0.1.5",
40
- "cross-spawn": "^7.0.6",
41
- "yaml": "^2.8.1"
42
- },
43
- "devDependencies": {
44
- "@types/cross-spawn": "^6.0.6",
45
- "@types/node": "^24.3.0",
46
- "tsx": "^4.20.5",
47
- "typescript": "^5.9.3",
48
- "vitest": "^4.1.5"
49
51
  }
50
- }
52
+ }