@borgee/agents-host 0.2.2 → 0.2.28

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 (84) 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 +114 -15
  5. package/dist/agents-host.js +2712 -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 +39 -0
  14. package/dist/compatibility-gates.js +131 -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/attention.d.ts +12 -0
  20. package/dist/context/attention.js +137 -0
  21. package/dist/context/collaboration-capabilities-diagnostics.d.ts +3 -0
  22. package/dist/context/collaboration-capabilities-diagnostics.js +18 -0
  23. package/dist/context/collaboration-outcome.d.ts +2 -0
  24. package/dist/context/collaboration-outcome.js +26 -0
  25. package/dist/context/injection.d.ts +134 -0
  26. package/dist/context/injection.js +355 -0
  27. package/dist/context/prompt.d.ts +4 -1
  28. package/dist/context/prompt.js +231 -1
  29. package/dist/context/task-thread-collaboration.d.ts +6 -0
  30. package/dist/context/task-thread-collaboration.js +31 -0
  31. package/dist/context/turn-preparation.d.ts +9 -0
  32. package/dist/context/turn-preparation.js +135 -0
  33. package/dist/debug.d.ts +44 -0
  34. package/dist/debug.js +135 -0
  35. package/dist/gateway/localhost-gateway.d.ts +52 -0
  36. package/dist/gateway/localhost-gateway.js +857 -0
  37. package/dist/index.js +7 -5
  38. package/dist/local-config.d.ts +4 -1
  39. package/dist/local-config.js +24 -7
  40. package/dist/managed-daemon-log.d.ts +34 -0
  41. package/dist/managed-daemon-log.js +261 -0
  42. package/dist/managed-daemon.d.ts +220 -0
  43. package/dist/managed-daemon.js +1601 -0
  44. package/dist/policy/authorization-audit.d.ts +63 -0
  45. package/dist/policy/authorization-audit.js +94 -0
  46. package/dist/policy/copilot-permission.d.ts +15 -0
  47. package/dist/policy/copilot-permission.js +193 -0
  48. package/dist/policy/gateway-authorization.d.ts +42 -0
  49. package/dist/policy/gateway-authorization.js +162 -0
  50. package/dist/providers/awaiting-user.d.ts +12 -0
  51. package/dist/providers/awaiting-user.js +192 -0
  52. package/dist/providers/claude/adapter.d.ts +3 -1
  53. package/dist/providers/claude/adapter.js +8 -12
  54. package/dist/providers/claude/cli-client.d.ts +12 -5
  55. package/dist/providers/claude/cli-client.js +184 -37
  56. package/dist/providers/claude/session-store.d.ts +1 -0
  57. package/dist/providers/codex/adapter.d.ts +11 -0
  58. package/dist/providers/codex/adapter.js +19 -0
  59. package/dist/providers/codex/cli-client.d.ts +103 -0
  60. package/dist/providers/codex/cli-client.js +1133 -0
  61. package/dist/providers/codex/project-doc.d.ts +3 -0
  62. package/dist/providers/codex/project-doc.js +90 -0
  63. package/dist/providers/codex/session-store.d.ts +38 -0
  64. package/dist/providers/codex/session-store.js +150 -0
  65. package/dist/providers/copilot/adapter.d.ts +3 -1
  66. package/dist/providers/copilot/adapter.js +8 -12
  67. package/dist/providers/copilot/cli-client.d.ts +20 -2
  68. package/dist/providers/copilot/cli-client.js +251 -71
  69. package/dist/providers/copilot/session-store.d.ts +1 -0
  70. package/dist/providers/create-provider.d.ts +11 -2
  71. package/dist/providers/create-provider.js +131 -12
  72. package/dist/run.d.ts +1 -0
  73. package/dist/run.js +5 -2
  74. package/dist/state-paths.d.ts +14 -1
  75. package/dist/state-paths.js +87 -3
  76. package/dist/task-thread-resolution.d.ts +10 -0
  77. package/dist/task-thread-resolution.js +48 -0
  78. package/dist/types.d.ts +267 -1
  79. package/dist/visible-mentions.d.ts +3 -0
  80. package/dist/visible-mentions.js +15 -0
  81. package/package.json +19 -17
  82. package/skills/borgee-agent/SKILL.md +33 -0
  83. package/skills/borgee-agent/borgee-agent.mjs +473 -0
  84. package/skills/borgee-agent/borgee-agent.py +409 -0
@@ -0,0 +1,3 @@
1
+ import type { PreparedPromptContext } from '../../types.js';
2
+ export declare function buildCodexProjectDocument(context: PreparedPromptContext | undefined): string;
3
+ export declare function assertCodexProjectDocumentSize(content: string): void;
@@ -0,0 +1,90 @@
1
+ import { basename } from 'node:path';
2
+ import { buildAttentionSummaryLines } from '../../context/attention.js';
3
+ import { buildCollaborationCapabilityDeclarationSummaryLines, buildMissedCollaborationDiagnosticSummaryLines, } from '../../context/collaboration-capabilities-diagnostics.js';
4
+ import { buildCollaborationOutcomeSummaryLines } from '../../context/collaboration-outcome.js';
5
+ import { buildTaskThreadCollaborationSummaryLines } from '../../context/task-thread-collaboration.js';
6
+ const PROJECT_DOC_MAX_BYTES = 32 * 1024;
7
+ function buildSkillRuntimeLines(context) {
8
+ if (!context?.skillRuntime || !context.channelContextPayloadPath) {
9
+ return [];
10
+ }
11
+ return [
12
+ 'Read-only local skill runtime bootstrap is available in this workspace.',
13
+ `Context payload: ${context.channelContextPayloadPath}`,
14
+ `Skill guide: ${context.skillRuntime.skillMarkdownPath}`,
15
+ `Node CLI: node ${context.skillRuntime.nodeCliPath} --context ${context.channelContextPayloadPath} --print-bootstrap`,
16
+ `Python CLI: python3 ${context.skillRuntime.pythonCliPath} --context ${context.channelContextPayloadPath} --print-bootstrap`,
17
+ 'These CLIs are local-only, may only access the loopback gateway surfaced by the host, and must not mutate files or the local environment.',
18
+ ];
19
+ }
20
+ function buildGatewayLines(context) {
21
+ if (!context?.localhostGateway || !context.channelContextPayloadPath) {
22
+ return [];
23
+ }
24
+ const lines = [
25
+ 'A loopback-only localhost gateway may be available for this channel.',
26
+ `Gateway base URL: ${context.localhostGateway.baseUrl}`,
27
+ 'Use the packaged local CLI with the context payload above for gateway access.',
28
+ ];
29
+ if (context.gatewayAuthPath) {
30
+ lines.push(`Per-turn gateway auth sidecar: ${context.gatewayAuthPath}`, 'If a later turn prompt advertises a different auth sidecar path, prefer the prompt-visible path for that turn.');
31
+ }
32
+ return lines;
33
+ }
34
+ export function buildCodexProjectDocument(context) {
35
+ const lines = [
36
+ '# Borgee channel workspace',
37
+ '',
38
+ 'This workspace belongs to one Borgee channel session hosted by agents-host.',
39
+ 'Keep visible replies concise, do not claim actions you did not perform, and prefer the per-turn prompt when it provides newer turn-local details.',
40
+ ...(context?.channelContextPayloadPath
41
+ ? [
42
+ '',
43
+ `Context payload file: ${context.channelContextPayloadPath}`,
44
+ `Workspace anchor: ${basename(context.channelContextPayloadPath)}`,
45
+ ]
46
+ : []),
47
+ ...(context?.taskWorkspace
48
+ ? [
49
+ '',
50
+ `Writable task workspace root: ${context.taskWorkspace.rootPath}`,
51
+ 'That writable task workspace is local to agents-host for the current task thread and does not imply that the target repository has already been checked out there.',
52
+ ]
53
+ : []),
54
+ ...(() => {
55
+ const collaborationOutcomeLines = buildCollaborationOutcomeSummaryLines(context?.collaborationOutcome);
56
+ return collaborationOutcomeLines.length > 0 ? ['', ...collaborationOutcomeLines] : [];
57
+ })(),
58
+ ...(() => {
59
+ const attentionLines = buildAttentionSummaryLines(context?.attentionSnapshot);
60
+ return attentionLines.length > 0 ? ['', ...attentionLines] : [];
61
+ })(),
62
+ ...(() => {
63
+ const capabilityLines = buildCollaborationCapabilityDeclarationSummaryLines(context?.collaborationCapabilities);
64
+ return capabilityLines.length > 0 ? ['', ...capabilityLines] : [];
65
+ })(),
66
+ ...(() => {
67
+ const diagnosticLines = buildMissedCollaborationDiagnosticSummaryLines(context?.missedCollaborationDiagnostic);
68
+ return diagnosticLines.length > 0 ? ['', ...diagnosticLines] : [];
69
+ })(),
70
+ ...(() => {
71
+ const taskThreadLines = buildTaskThreadCollaborationSummaryLines(context?.taskThreadCollaborationContract);
72
+ return taskThreadLines.length > 0 ? ['', ...taskThreadLines] : [];
73
+ })(),
74
+ ...(() => {
75
+ const skillRuntimeLines = buildSkillRuntimeLines(context);
76
+ return skillRuntimeLines.length > 0 ? ['', ...skillRuntimeLines] : [];
77
+ })(),
78
+ ...(() => {
79
+ const gatewayLines = buildGatewayLines(context);
80
+ return gatewayLines.length > 0 ? ['', ...gatewayLines] : [];
81
+ })(),
82
+ ];
83
+ return `${lines.join('\n').trimEnd()}\n`;
84
+ }
85
+ export function assertCodexProjectDocumentSize(content) {
86
+ const size = Buffer.byteLength(content, 'utf8');
87
+ if (size > PROJECT_DOC_MAX_BYTES) {
88
+ throw new Error(`Codex project document exceeds ${PROJECT_DOC_MAX_BYTES} bytes (${size} bytes)`);
89
+ }
90
+ }
@@ -0,0 +1,38 @@
1
+ export interface CodexChannelSessionStore {
2
+ load(agentId: string): Promise<Record<string, string>>;
3
+ save(agentId: string, sessions: Record<string, string>): Promise<void>;
4
+ close?(): Promise<void> | void;
5
+ }
6
+ export interface FileCodexChannelSessionStoreOptions {
7
+ resolvePath(agentId: string): string;
8
+ fileSystem?: CodexSessionStoreFileSystem;
9
+ platform?: NodeJS.Platform;
10
+ }
11
+ interface CodexSessionStoreDirectoryHandle {
12
+ sync(): Promise<void>;
13
+ close(): Promise<void>;
14
+ }
15
+ interface CodexSessionStoreFileHandle extends CodexSessionStoreDirectoryHandle {
16
+ writeFile(data: string, options: {
17
+ encoding: BufferEncoding;
18
+ }): Promise<void>;
19
+ }
20
+ interface CodexSessionStoreFileSystem {
21
+ mkdir(path: string, options: {
22
+ recursive: true;
23
+ mode: number;
24
+ }): Promise<void>;
25
+ openDirectory(path: string): Promise<CodexSessionStoreDirectoryHandle>;
26
+ openFile(path: string, flags: string, mode: number): Promise<CodexSessionStoreFileHandle>;
27
+ readFile(path: string, encoding: BufferEncoding): Promise<string>;
28
+ rename(from: string, to: string): Promise<void>;
29
+ unlink(path: string): Promise<void>;
30
+ }
31
+ export declare class FileCodexChannelSessionStore implements CodexChannelSessionStore {
32
+ private readonly options;
33
+ constructor(options: FileCodexChannelSessionStoreOptions);
34
+ private loadFromPath;
35
+ load(agentId: string): Promise<Record<string, string>>;
36
+ save(agentId: string, sessions: Record<string, string>): Promise<void>;
37
+ }
38
+ export {};
@@ -0,0 +1,150 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { mkdir, open, readFile, rename, unlink } from 'node:fs/promises';
3
+ import { dirname } from 'node:path';
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;
30
+ try {
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;
59
+ }
60
+ if (directoryHandle) {
61
+ await directoryHandle.close();
62
+ }
63
+ }
64
+ function normalizePersistedSessions(raw, filePath) {
65
+ if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {
66
+ throw new Error(`invalid Codex session file: ${filePath}`);
67
+ }
68
+ const entries = Object.entries(raw);
69
+ const sessions = {};
70
+ for (const [channelId, sessionId] of entries) {
71
+ if (typeof sessionId !== 'string' || sessionId.trim().length === 0) {
72
+ throw new Error(`invalid Codex session file: ${filePath}`);
73
+ }
74
+ sessions[channelId] = sessionId;
75
+ }
76
+ return sessions;
77
+ }
78
+ export class FileCodexChannelSessionStore {
79
+ options;
80
+ constructor(options) {
81
+ this.options = options;
82
+ }
83
+ async loadFromPath(filePath) {
84
+ const raw = await (this.options.fileSystem ?? nodeFileSystem).readFile(filePath, 'utf8');
85
+ return normalizePersistedSessions(JSON.parse(raw), filePath);
86
+ }
87
+ async load(agentId) {
88
+ const filePath = this.options.resolvePath(agentId);
89
+ try {
90
+ return await this.loadFromPath(filePath);
91
+ }
92
+ catch (error) {
93
+ if (error.code !== 'ENOENT') {
94
+ throw error;
95
+ }
96
+ return {};
97
+ }
98
+ }
99
+ async save(agentId, sessions) {
100
+ const filePath = this.options.resolvePath(agentId);
101
+ const parentPath = dirname(filePath);
102
+ const fileSystem = this.options.fileSystem ?? nodeFileSystem;
103
+ const platform = this.options.platform ?? process.platform;
104
+ await fileSystem.mkdir(parentPath, { recursive: true, mode: 0o700 });
105
+ const entries = Object.entries(sessions).sort(([left], [right]) => left.localeCompare(right));
106
+ if (entries.length === 0) {
107
+ try {
108
+ await fileSystem.unlink(filePath);
109
+ }
110
+ catch (error) {
111
+ if (error.code !== 'ENOENT') {
112
+ throw error;
113
+ }
114
+ return;
115
+ }
116
+ await syncDirectory(parentPath, fileSystem, platform);
117
+ return;
118
+ }
119
+ const temporaryPath = `${filePath}.${process.pid}.${randomUUID()}.tmp`;
120
+ let temporaryHandle;
121
+ try {
122
+ temporaryHandle = await fileSystem.openFile(temporaryPath, 'wx', 0o600);
123
+ await temporaryHandle.writeFile(JSON.stringify(Object.fromEntries(entries)), {
124
+ encoding: 'utf8',
125
+ });
126
+ await temporaryHandle.sync();
127
+ await temporaryHandle.close();
128
+ temporaryHandle = undefined;
129
+ await fileSystem.rename(temporaryPath, filePath);
130
+ await syncDirectory(parentPath, fileSystem, platform);
131
+ }
132
+ catch (error) {
133
+ if (temporaryHandle) {
134
+ try {
135
+ await temporaryHandle.close();
136
+ }
137
+ catch {
138
+ // Preserve the original durability failure.
139
+ }
140
+ }
141
+ try {
142
+ await fileSystem.unlink(temporaryPath);
143
+ }
144
+ catch {
145
+ // Cleanup is best effort.
146
+ }
147
+ throw error;
148
+ }
149
+ }
150
+ }
@@ -1,9 +1,11 @@
1
1
  import type { ProviderAdapter } from '../provider-adapter.js';
2
2
  import type { ProviderGenerateOptions, ProviderInput, ProviderReply } from '../../types.js';
3
+ import { ProviderTurnPreparer } from '../../context/turn-preparation.js';
3
4
  import { CopilotCliClient } from './cli-client.js';
4
5
  export declare class CopilotProviderAdapter implements ProviderAdapter {
5
6
  private readonly cli;
6
- constructor(cli: CopilotCliClient);
7
+ private readonly turnPreparer;
8
+ constructor(cli: CopilotCliClient, turnPreparer: ProviderTurnPreparer);
7
9
  generateReply(input: ProviderInput, options?: ProviderGenerateOptions): Promise<ProviderReply>;
8
10
  dispose(): Promise<void>;
9
11
  }
@@ -1,21 +1,17 @@
1
- import { buildPrompt } from '../../context/prompt.js';
1
+ import { createAwaitingUserProgressHandler, parseProviderReply } from '../awaiting-user.js';
2
2
  export class CopilotProviderAdapter {
3
3
  cli;
4
- constructor(cli) {
4
+ turnPreparer;
5
+ constructor(cli, turnPreparer) {
5
6
  this.cli = cli;
7
+ this.turnPreparer = turnPreparer;
6
8
  }
7
9
  async generateReply(input, options) {
8
- const prompt = buildPrompt({
9
- agentName: input.agentName,
10
- provider: input.provider,
11
- channelId: input.channelId,
12
- incomingAuthorId: input.incomingAuthorId,
13
- incomingContent: input.incomingContent,
10
+ const preparedTurn = await this.turnPreparer.prepare(input);
11
+ const text = await this.cli.generateReply(preparedTurn, {
12
+ onProgress: createAwaitingUserProgressHandler(options?.onProgress),
14
13
  });
15
- const text = await this.cli.generateReply(input.channelId, prompt, {
16
- onProgress: options?.onProgress,
17
- });
18
- return { text };
14
+ return parseProviderReply(text);
19
15
  }
20
16
  async dispose() {
21
17
  await this.cli.dispose();
@@ -1,6 +1,9 @@
1
1
  import spawn from 'cross-spawn';
2
2
  import { PROTOCOL_VERSION, client, methods, ndJsonStream } from '@agentclientprotocol/sdk';
3
- import type { ProviderGenerateOptions } from '../../types.js';
3
+ import { type DebugLogger } from '../../debug.js';
4
+ import type { InternalPolicyMode } from '../../compatibility-gates.js';
5
+ import type { AuthorizationAuditSinkLike } from '../../policy/authorization-audit.js';
6
+ import type { PreparedProviderTurnInput, ProviderGenerateOptions } from '../../types.js';
4
7
  import type { CopilotChannelSessionStore } from './session-store.js';
5
8
  interface CopilotAcpRuntime {
6
9
  spawn: typeof spawn;
@@ -13,6 +16,11 @@ interface CopilotAcpRuntime {
13
16
  shutdownGracePeriodMs: number;
14
17
  shutdownForceKillWaitMs: number;
15
18
  }
19
+ interface CopilotPermissionPolicyOptions {
20
+ gateEnabled?: boolean;
21
+ policyMode?: InternalPolicyMode;
22
+ auditSink?: AuthorizationAuditSinkLike;
23
+ }
16
24
  /**
17
25
  * Persistent ACP-backed client for the GitHub Copilot CLI (`copilot --acp`).
18
26
  *
@@ -25,12 +33,15 @@ export declare class CopilotCliClient {
25
33
  private readonly command;
26
34
  private readonly sessionStore?;
27
35
  private readonly resolveSessionStoreAgentId;
36
+ private readonly logger;
37
+ private readonly permissionPolicy;
28
38
  private readonly runtime;
29
39
  private readonly channels;
30
40
  private readonly persistedSessions;
31
41
  private readonly closingSessions;
32
42
  private readonly pendingSessionStarts;
33
43
  private readonly pendingSessionCloses;
44
+ private readonly pendingSessionStoreOperations;
34
45
  private readonly fatalPromise;
35
46
  private rejectFatalPromise;
36
47
  private child?;
@@ -46,7 +57,8 @@ export declare class CopilotCliClient {
46
57
  private sessionStoreLoadPromise;
47
58
  private sessionStoreWriteQueue;
48
59
  private sessionCapabilities;
49
- constructor(command: string, _ignoredArgs?: string[], runtimeOverrides?: Partial<CopilotAcpRuntime>, sessionStore?: CopilotChannelSessionStore | undefined, resolveSessionStoreAgentId?: () => string | undefined);
60
+ constructor(command: string, _ignoredArgs?: string[], runtimeOverrides?: Partial<CopilotAcpRuntime>, sessionStore?: CopilotChannelSessionStore | undefined, resolveSessionStoreAgentId?: () => string | undefined, logger?: DebugLogger, permissionPolicy?: CopilotPermissionPolicyOptions);
61
+ generateReply(turn: PreparedProviderTurnInput, options?: ProviderGenerateOptions): Promise<string>;
50
62
  generateReply(channelId: string, prompt: string, options?: ProviderGenerateOptions): Promise<string>;
51
63
  dispose(): Promise<void>;
52
64
  private ensureStarted;
@@ -58,20 +70,26 @@ export declare class CopilotCliClient {
58
70
  private restoreOrCreateSession;
59
71
  private restoreSession;
60
72
  private clearBufferedSessionReplay;
73
+ private resolveSessionCwd;
74
+ private recycleSessionIfCwdChanged;
61
75
  private runTurn;
62
76
  private raceWithFatal;
77
+ private handlePermissionRequest;
63
78
  private failAll;
64
79
  private invalidateSession;
65
80
  private closeSession;
66
81
  private rejectQueuedTurnsAfterSessionTaint;
67
82
  private clearIdleTimer;
83
+ private findChannelIdBySessionId;
68
84
  private reconcileIdleChannelState;
69
85
  private ensureSessionStoreLoaded;
86
+ private readPersistedSessionId;
70
87
  private persistSession;
71
88
  private persistSessionBestEffort;
72
89
  private clearPersistedSession;
73
90
  private clearPersistedSessionBestEffort;
74
91
  private flushSessionStore;
92
+ private trackSessionStoreOperation;
75
93
  private evictIdleChannel;
76
94
  private shutdownBackend;
77
95
  private waitForPendingSessionStarts;