@borgee/agents-host 0.2.35 → 0.2.44
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/dist/agents-host.d.ts +2 -0
- package/dist/agents-host.js +111 -44
- package/dist/chat/sdk-chat-control-plane.js +3 -0
- package/dist/context/injection.d.ts +5 -3
- package/dist/context/injection.js +45 -29
- package/dist/context/prompt.js +23 -35
- package/dist/context/skill-manual.d.ts +13 -0
- package/dist/context/skill-manual.js +18 -0
- package/dist/context/turn-preparation.js +13 -4
- package/dist/gateway/localhost-gateway.js +16 -8
- package/dist/hosted-turn-content.d.ts +15 -0
- package/dist/hosted-turn-content.js +50 -0
- package/dist/managed-daemon.d.ts +3 -2
- package/dist/managed-daemon.js +77 -29
- package/dist/providers/claude/adapter.d.ts +3 -1
- package/dist/providers/claude/adapter.js +10 -0
- package/dist/providers/claude/cli-client.d.ts +11 -2
- package/dist/providers/claude/cli-client.js +98 -27
- package/dist/providers/codex/adapter.d.ts +3 -1
- package/dist/providers/codex/adapter.js +10 -0
- package/dist/providers/codex/cli-client.d.ts +10 -2
- package/dist/providers/codex/cli-client.js +85 -21
- package/dist/providers/codex/project-doc.js +13 -29
- package/dist/providers/copilot/adapter.d.ts +3 -1
- package/dist/providers/copilot/adapter.js +10 -0
- package/dist/providers/copilot/cli-client.d.ts +9 -1
- package/dist/providers/copilot/cli-client.js +71 -8
- package/dist/providers/create-provider.d.ts +1 -1
- package/dist/providers/create-provider.js +16 -5
- package/dist/providers/provider-adapter.d.ts +35 -0
- package/dist/providers/provider-adapter.js +44 -1
- package/dist/state-paths.d.ts +9 -1
- package/dist/state-paths.js +22 -3
- package/dist/types.d.ts +33 -2
- package/package.json +1 -1
- package/skills/borgee-agent/SKILL.md +119 -38
- package/skills/borgee-agent/references/errors.md +38 -0
- package/skills/borgee-agent/references/task-properties.md +30 -0
- package/skills/borgee-agent/scripts/borgee-agent.mjs +553 -0
- package/skills/borgee-agent/scripts/borgee-agent.py +547 -0
- package/skills/borgee-agent/borgee-agent.mjs +0 -562
- package/skills/borgee-agent/borgee-agent.py +0 -469
|
@@ -1,36 +1,27 @@
|
|
|
1
|
-
import { basename } from 'node:path';
|
|
2
1
|
import { buildAttentionSummaryLines } from '../../context/attention.js';
|
|
3
2
|
import { buildCollaborationCapabilityDeclarationSummaryLines, buildMissedCollaborationDiagnosticSummaryLines, } from '../../context/collaboration-capabilities-diagnostics.js';
|
|
4
3
|
import { buildCollaborationOutcomeSummaryLines } from '../../context/collaboration-outcome.js';
|
|
5
4
|
import { MAIN_SESSION_DELEGATION_LINES } from '../../context/main-session-delegation.js';
|
|
5
|
+
import { buildSkillManualLines } from '../../context/skill-manual.js';
|
|
6
6
|
import { buildTaskThreadCollaborationSummaryLines } from '../../context/task-thread-collaboration.js';
|
|
7
7
|
const PROJECT_DOC_MAX_BYTES = 32 * 1024;
|
|
8
|
+
// Every command the CLI carries needs the gateway credential file, so the capability is only
|
|
9
|
+
// worth naming in a workspace that also names one; otherwise the model is handed an invocation
|
|
10
|
+
// template it cannot fill.
|
|
8
11
|
function buildSkillRuntimeLines(context) {
|
|
9
|
-
if (!context?.skillRuntime || !context.
|
|
12
|
+
if (!context?.skillRuntime || !context.gatewayCredentialPath) {
|
|
10
13
|
return [];
|
|
11
14
|
}
|
|
12
|
-
return
|
|
13
|
-
'Read-only local skill runtime bootstrap is available in this workspace.',
|
|
14
|
-
`Context payload: ${context.channelContextPayloadPath}`,
|
|
15
|
-
`Skill guide: ${context.skillRuntime.skillMarkdownPath}`,
|
|
16
|
-
`Node CLI: node ${context.skillRuntime.nodeCliPath} --context ${context.channelContextPayloadPath} --print-bootstrap`,
|
|
17
|
-
`Python CLI: python3 ${context.skillRuntime.pythonCliPath} --context ${context.channelContextPayloadPath} --print-bootstrap`,
|
|
18
|
-
'These CLIs are local-only, may only access the loopback gateway surfaced by the host, and must not mutate files or the local environment.',
|
|
19
|
-
];
|
|
15
|
+
return buildSkillManualLines(context.skillRuntime);
|
|
20
16
|
}
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
// The credential file is the whole handoff: it carries the gateway's base URL along with the
|
|
18
|
+
// channel id and the token, and the CLI reads all three out of it. Naming the base URL separately
|
|
19
|
+
// would only invite a hand-rolled request that carries the token off the CLI's one path.
|
|
20
|
+
function buildGatewayCredentialLines(context) {
|
|
21
|
+
if (!context?.localhostGateway || !context.gatewayCredentialPath) {
|
|
23
22
|
return [];
|
|
24
23
|
}
|
|
25
|
-
|
|
26
|
-
'A loopback-only localhost gateway may be available for this channel.',
|
|
27
|
-
`Gateway base URL: ${context.localhostGateway.baseUrl}`,
|
|
28
|
-
'Use the packaged local CLI with the context payload above for gateway access.',
|
|
29
|
-
];
|
|
30
|
-
if (context.gatewayAuthPath) {
|
|
31
|
-
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.');
|
|
32
|
-
}
|
|
33
|
-
return lines;
|
|
24
|
+
return [`Gateway credential file: ${context.gatewayCredentialPath}`];
|
|
34
25
|
}
|
|
35
26
|
export function buildCodexProjectDocument(context) {
|
|
36
27
|
const lines = [
|
|
@@ -40,13 +31,6 @@ export function buildCodexProjectDocument(context) {
|
|
|
40
31
|
'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.',
|
|
41
32
|
'',
|
|
42
33
|
...MAIN_SESSION_DELEGATION_LINES,
|
|
43
|
-
...(context?.channelContextPayloadPath
|
|
44
|
-
? [
|
|
45
|
-
'',
|
|
46
|
-
`Context payload file: ${context.channelContextPayloadPath}`,
|
|
47
|
-
`Workspace anchor: ${basename(context.channelContextPayloadPath)}`,
|
|
48
|
-
]
|
|
49
|
-
: []),
|
|
50
34
|
...(context?.taskWorkspace
|
|
51
35
|
? [
|
|
52
36
|
'',
|
|
@@ -79,7 +63,7 @@ export function buildCodexProjectDocument(context) {
|
|
|
79
63
|
return skillRuntimeLines.length > 0 ? ['', ...skillRuntimeLines] : [];
|
|
80
64
|
})(),
|
|
81
65
|
...(() => {
|
|
82
|
-
const gatewayLines =
|
|
66
|
+
const gatewayLines = buildGatewayCredentialLines(context);
|
|
83
67
|
return gatewayLines.length > 0 ? ['', ...gatewayLines] : [];
|
|
84
68
|
})(),
|
|
85
69
|
];
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ProviderAdapter } from '../provider-adapter.js';
|
|
2
2
|
import type { ProviderGenerateOptions, ProviderInput, ProviderReply } from '../../types.js';
|
|
3
3
|
import { ProviderTurnPreparer } from '../../context/turn-preparation.js';
|
|
4
4
|
import { CopilotCliClient } from './cli-client.js';
|
|
5
|
+
export declare const COPILOT_HOSTED_PROVIDER_CAPABILITIES: import("../provider-adapter.js").ProviderCapabilities;
|
|
5
6
|
export declare class CopilotProviderAdapter implements ProviderAdapter {
|
|
6
7
|
private readonly cli;
|
|
7
8
|
private readonly turnPreparer;
|
|
9
|
+
readonly capabilities: import("../provider-adapter.js").ProviderCapabilities;
|
|
8
10
|
constructor(cli: CopilotCliClient, turnPreparer: ProviderTurnPreparer);
|
|
9
11
|
generateReply(input: ProviderInput, options?: ProviderGenerateOptions): Promise<ProviderReply>;
|
|
10
12
|
dispose(): Promise<void>;
|
|
@@ -1,7 +1,17 @@
|
|
|
1
|
+
import { createHostedProviderCapabilities } from '../provider-adapter.js';
|
|
1
2
|
import { createAwaitingUserProgressHandler, parseProviderReply } from '../awaiting-user.js';
|
|
3
|
+
export const COPILOT_HOSTED_PROVIDER_CAPABILITIES = createHostedProviderCapabilities({
|
|
4
|
+
imageInputTransport: {
|
|
5
|
+
source: 'transport-derived',
|
|
6
|
+
support: 'supported',
|
|
7
|
+
reason: 'real-media-delivered',
|
|
8
|
+
delivery: ['blob'],
|
|
9
|
+
},
|
|
10
|
+
});
|
|
2
11
|
export class CopilotProviderAdapter {
|
|
3
12
|
cli;
|
|
4
13
|
turnPreparer;
|
|
14
|
+
capabilities = COPILOT_HOSTED_PROVIDER_CAPABILITIES;
|
|
5
15
|
constructor(cli, turnPreparer) {
|
|
6
16
|
this.cli = cli;
|
|
7
17
|
this.turnPreparer = turnPreparer;
|
|
@@ -17,12 +17,17 @@ interface CopilotAcpRuntime {
|
|
|
17
17
|
idleShutdownGracePeriodMs: number;
|
|
18
18
|
shutdownGracePeriodMs: number;
|
|
19
19
|
shutdownForceKillWaitMs: number;
|
|
20
|
+
fetch(input: string | URL, init?: RequestInit): Promise<Response>;
|
|
20
21
|
}
|
|
21
22
|
interface CopilotPermissionPolicyOptions {
|
|
22
23
|
gateEnabled?: boolean;
|
|
23
24
|
policyMode?: InternalPolicyMode;
|
|
24
25
|
auditSink?: AuthorizationAuditSinkLike;
|
|
25
26
|
}
|
|
27
|
+
interface CopilotHostedImageInputConfig {
|
|
28
|
+
borgeeBaseUrl?: string;
|
|
29
|
+
agentApiKey?: string;
|
|
30
|
+
}
|
|
26
31
|
/**
|
|
27
32
|
* Persistent ACP-backed client for the GitHub Copilot CLI (`copilot --acp`).
|
|
28
33
|
*
|
|
@@ -37,6 +42,7 @@ export declare class CopilotCliClient {
|
|
|
37
42
|
private readonly resolveSessionStoreAgentId;
|
|
38
43
|
private readonly logger;
|
|
39
44
|
private readonly permissionPolicy;
|
|
45
|
+
private readonly imageInputConfig;
|
|
40
46
|
private readonly runtime;
|
|
41
47
|
private readonly channels;
|
|
42
48
|
/**
|
|
@@ -72,7 +78,7 @@ export declare class CopilotCliClient {
|
|
|
72
78
|
private sessionStoreWriteQueue;
|
|
73
79
|
private sessionCapabilities;
|
|
74
80
|
private readonly idleBackendShutdown;
|
|
75
|
-
constructor(command: string, _ignoredArgs?: string[], runtimeOverrides?: Partial<CopilotAcpRuntime>, sessionStore?: CopilotChannelSessionStore | undefined, resolveSessionStoreAgentId?: () => string | undefined, logger?: DebugLogger, permissionPolicy?: CopilotPermissionPolicyOptions);
|
|
81
|
+
constructor(command: string, _ignoredArgs?: string[], runtimeOverrides?: Partial<CopilotAcpRuntime>, sessionStore?: CopilotChannelSessionStore | undefined, resolveSessionStoreAgentId?: () => string | undefined, logger?: DebugLogger, permissionPolicy?: CopilotPermissionPolicyOptions, imageInputConfig?: CopilotHostedImageInputConfig);
|
|
76
82
|
generateReply(turn: PreparedProviderTurnInput, options?: ProviderGenerateOptions): Promise<string>;
|
|
77
83
|
generateReply(channelId: string, prompt: string, options?: ProviderGenerateOptions): Promise<string>;
|
|
78
84
|
dispose(): Promise<void>;
|
|
@@ -86,6 +92,8 @@ export declare class CopilotCliClient {
|
|
|
86
92
|
private restoreSession;
|
|
87
93
|
private clearBufferedSessionReplay;
|
|
88
94
|
private resolveSessionCwd;
|
|
95
|
+
private buildPromptInput;
|
|
96
|
+
private fetchImageBlock;
|
|
89
97
|
private recycleSessionIfCwdChanged;
|
|
90
98
|
private runTurn;
|
|
91
99
|
private raceWithFatal;
|
|
@@ -30,6 +30,7 @@ const DEFAULT_RUNTIME = {
|
|
|
30
30
|
idleShutdownGracePeriodMs: DEFAULT_IDLE_SHUTDOWN_GRACE_PERIOD_MS,
|
|
31
31
|
shutdownGracePeriodMs: DEFAULT_SHUTDOWN_GRACE_PERIOD_MS,
|
|
32
32
|
shutdownForceKillWaitMs: DEFAULT_SHUTDOWN_FORCE_KILL_WAIT_MS,
|
|
33
|
+
fetch: async (input, init) => fetch(input, init),
|
|
33
34
|
};
|
|
34
35
|
const DEFAULT_SESSION_CAPABILITIES = {
|
|
35
36
|
loadSession: false,
|
|
@@ -38,6 +39,28 @@ const DEFAULT_SESSION_CAPABILITIES = {
|
|
|
38
39
|
function hasVisibleText(value) {
|
|
39
40
|
return typeof value === 'string' && value.trim().length > 0;
|
|
40
41
|
}
|
|
42
|
+
function isAbsoluteHttpUrl(value) {
|
|
43
|
+
return value.startsWith('http://') || value.startsWith('https://');
|
|
44
|
+
}
|
|
45
|
+
function resolveHostedAttachmentUrl(rawUrl, borgeeBaseUrl) {
|
|
46
|
+
if (isAbsoluteHttpUrl(rawUrl)) {
|
|
47
|
+
return rawUrl;
|
|
48
|
+
}
|
|
49
|
+
const base = borgeeBaseUrl.replace(/\/$/, '');
|
|
50
|
+
const path = rawUrl.startsWith('/') ? rawUrl : `/${rawUrl}`;
|
|
51
|
+
return `${base}${path}`;
|
|
52
|
+
}
|
|
53
|
+
function isSameOrigin(left, right) {
|
|
54
|
+
try {
|
|
55
|
+
return new URL(left).origin === new URL(right).origin;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function asImagePart(part) {
|
|
62
|
+
return part.type === 'image' ? part : null;
|
|
63
|
+
}
|
|
41
64
|
function formatToolProgress(title, status) {
|
|
42
65
|
const normalizedTitle = hasVisibleText(title) ? title.trim() : null;
|
|
43
66
|
switch (status) {
|
|
@@ -116,7 +139,7 @@ class CopilotProgressCollector {
|
|
|
116
139
|
this.onProgress({ text });
|
|
117
140
|
}
|
|
118
141
|
}
|
|
119
|
-
function createDeferredTurn(channelId,
|
|
142
|
+
function createDeferredTurn(channelId, preparedTurn, sessionPersistence, options) {
|
|
120
143
|
let settled = false;
|
|
121
144
|
let resolvePromise;
|
|
122
145
|
let rejectPromise;
|
|
@@ -126,9 +149,8 @@ function createDeferredTurn(channelId, prompt, sessionPersistence, promptContext
|
|
|
126
149
|
});
|
|
127
150
|
return {
|
|
128
151
|
channelId,
|
|
129
|
-
|
|
152
|
+
preparedTurn,
|
|
130
153
|
sessionPersistence,
|
|
131
|
-
promptContext,
|
|
132
154
|
options,
|
|
133
155
|
promise,
|
|
134
156
|
resolve(value) {
|
|
@@ -216,6 +238,7 @@ export class CopilotCliClient {
|
|
|
216
238
|
resolveSessionStoreAgentId;
|
|
217
239
|
logger;
|
|
218
240
|
permissionPolicy;
|
|
241
|
+
imageInputConfig;
|
|
219
242
|
runtime;
|
|
220
243
|
channels = new Map();
|
|
221
244
|
/**
|
|
@@ -253,12 +276,13 @@ export class CopilotCliClient {
|
|
|
253
276
|
sessionStoreWriteQueue = Promise.resolve();
|
|
254
277
|
sessionCapabilities = DEFAULT_SESSION_CAPABILITIES;
|
|
255
278
|
idleBackendShutdown;
|
|
256
|
-
constructor(command, _ignoredArgs = [], runtimeOverrides = {}, sessionStore, resolveSessionStoreAgentId = () => undefined, logger = new HostLogger(), permissionPolicy = {}) {
|
|
279
|
+
constructor(command, _ignoredArgs = [], runtimeOverrides = {}, sessionStore, resolveSessionStoreAgentId = () => undefined, logger = new HostLogger(), permissionPolicy = {}, imageInputConfig = {}) {
|
|
257
280
|
this.command = command;
|
|
258
281
|
this.sessionStore = sessionStore;
|
|
259
282
|
this.resolveSessionStoreAgentId = resolveSessionStoreAgentId;
|
|
260
283
|
this.logger = logger;
|
|
261
284
|
this.permissionPolicy = permissionPolicy;
|
|
285
|
+
this.imageInputConfig = imageInputConfig;
|
|
262
286
|
this.runtime = { ...DEFAULT_RUNTIME, ...runtimeOverrides };
|
|
263
287
|
this.idleBackendShutdown = new IdleBackendShutdownScheduler({
|
|
264
288
|
idleShutdownMs: this.runtime.idleBackendShutdownMs,
|
|
@@ -279,12 +303,19 @@ export class CopilotCliClient {
|
|
|
279
303
|
if (this.fatalError) {
|
|
280
304
|
throw this.fatalError;
|
|
281
305
|
}
|
|
282
|
-
const
|
|
306
|
+
const preparedTurnBase = typeof channelIdOrTurn === 'string'
|
|
283
307
|
? {
|
|
284
308
|
channelId: channelIdOrTurn,
|
|
309
|
+
incomingContent: '',
|
|
310
|
+
incomingParts: [],
|
|
285
311
|
prompt: typeof promptOrOptions === 'string' ? promptOrOptions : '',
|
|
286
312
|
}
|
|
287
313
|
: channelIdOrTurn;
|
|
314
|
+
const preparedTurn = {
|
|
315
|
+
...preparedTurnBase,
|
|
316
|
+
incomingContent: preparedTurnBase.incomingContent ?? '',
|
|
317
|
+
incomingParts: preparedTurnBase.incomingParts ?? [],
|
|
318
|
+
};
|
|
288
319
|
const options = typeof channelIdOrTurn === 'string'
|
|
289
320
|
? maybeOptions
|
|
290
321
|
: promptOrOptions;
|
|
@@ -292,7 +323,7 @@ export class CopilotCliClient {
|
|
|
292
323
|
const state = this.getOrCreateChannelState(sessionRouting.key, preparedTurn.channelId, sessionRouting.persistence);
|
|
293
324
|
this.clearIdleTimer(state);
|
|
294
325
|
this.idleBackendShutdown.cancel();
|
|
295
|
-
const turn = createDeferredTurn(preparedTurn.channelId, preparedTurn
|
|
326
|
+
const turn = createDeferredTurn(preparedTurn.channelId, preparedTurn, sessionRouting.persistence, options);
|
|
296
327
|
state.queue.push(turn);
|
|
297
328
|
this.processChannelQueue(sessionRouting.key, state);
|
|
298
329
|
return turn.promise;
|
|
@@ -455,13 +486,14 @@ export class CopilotCliClient {
|
|
|
455
486
|
}
|
|
456
487
|
state.activeTurn = turn;
|
|
457
488
|
try {
|
|
458
|
-
state.cwd = await this.resolveSessionCwd(turn.promptContext);
|
|
489
|
+
state.cwd = await this.resolveSessionCwd(turn.preparedTurn.promptContext);
|
|
459
490
|
await this.ensureStarted();
|
|
460
491
|
await this.recycleSessionIfCwdChanged(channelId, state);
|
|
461
492
|
const session = await this.getOrCreateSession(channelId, state);
|
|
462
493
|
let reply;
|
|
463
494
|
try {
|
|
464
|
-
|
|
495
|
+
const promptInput = await this.buildPromptInput(turn.preparedTurn);
|
|
496
|
+
reply = await this.runTurn(session, promptInput, turn.options);
|
|
465
497
|
}
|
|
466
498
|
catch (error) {
|
|
467
499
|
if (isSessionTainted(error)) {
|
|
@@ -617,6 +649,37 @@ export class CopilotCliClient {
|
|
|
617
649
|
async resolveSessionCwd(promptContext) {
|
|
618
650
|
return promptContext?.taskWorkspace?.rootPath ?? this.runtime.cwd;
|
|
619
651
|
}
|
|
652
|
+
async buildPromptInput(turn) {
|
|
653
|
+
const imageParts = turn.incomingParts.map(asImagePart).filter((part) => part !== null);
|
|
654
|
+
if (imageParts.length === 0) {
|
|
655
|
+
return turn.prompt;
|
|
656
|
+
}
|
|
657
|
+
const imageBlocks = await Promise.all(imageParts.map(async (part) => this.fetchImageBlock(part.attachment.url, part.attachment.contentType)));
|
|
658
|
+
return [{ type: 'text', text: turn.prompt }, ...imageBlocks];
|
|
659
|
+
}
|
|
660
|
+
async fetchImageBlock(rawUrl, contentType) {
|
|
661
|
+
const baseUrl = this.imageInputConfig.borgeeBaseUrl?.trim();
|
|
662
|
+
const agentApiKey = this.imageInputConfig.agentApiKey?.trim();
|
|
663
|
+
if (!baseUrl || !agentApiKey) {
|
|
664
|
+
throw new Error('Copilot image input requires Borgee base URL and agent API key');
|
|
665
|
+
}
|
|
666
|
+
const resolvedUrl = resolveHostedAttachmentUrl(rawUrl, baseUrl);
|
|
667
|
+
if (!isSameOrigin(resolvedUrl, baseUrl)) {
|
|
668
|
+
throw new Error(`Copilot image input only supports Borgee-origin attachments: ${resolvedUrl}`);
|
|
669
|
+
}
|
|
670
|
+
const response = await this.runtime.fetch(resolvedUrl, {
|
|
671
|
+
headers: { Authorization: `Bearer ${agentApiKey}` },
|
|
672
|
+
});
|
|
673
|
+
if (!response.ok) {
|
|
674
|
+
throw new Error(`Copilot image fetch failed with ${response.status} ${response.statusText}`.trim());
|
|
675
|
+
}
|
|
676
|
+
return {
|
|
677
|
+
type: 'image',
|
|
678
|
+
mimeType: contentType,
|
|
679
|
+
data: Buffer.from(await response.arrayBuffer()).toString('base64'),
|
|
680
|
+
uri: response.url || resolvedUrl,
|
|
681
|
+
};
|
|
682
|
+
}
|
|
620
683
|
async recycleSessionIfCwdChanged(channelId, state) {
|
|
621
684
|
const session = state.session;
|
|
622
685
|
if (!session) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { DebugLogger } from '../debug.js';
|
|
2
2
|
import type { ProviderRuntimeConfig } from '../types.js';
|
|
3
|
-
import type
|
|
3
|
+
import { type ProviderAdapter } from './provider-adapter.js';
|
|
4
4
|
import type { LocalhostGatewayContextPublisher } from '../context/injection.js';
|
|
5
5
|
import type { AuthorizationAuditSinkLike } from '../policy/authorization-audit.js';
|
|
6
6
|
interface CreateProviderOptions {
|
|
@@ -10,16 +10,18 @@ import { CodexCliClient } from './codex/cli-client.js';
|
|
|
10
10
|
import { CodexProviderAdapter } from './codex/adapter.js';
|
|
11
11
|
import { FileCodexChannelSessionStore } from './codex/session-store.js';
|
|
12
12
|
import { CopilotCliClient } from './copilot/cli-client.js';
|
|
13
|
-
import { CopilotProviderAdapter } from './copilot/adapter.js';
|
|
13
|
+
import { COPILOT_HOSTED_PROVIDER_CAPABILITIES, CopilotProviderAdapter } from './copilot/adapter.js';
|
|
14
14
|
import { FileCopilotChannelSessionStore } from './copilot/session-store.js';
|
|
15
15
|
import { resolveClaudeSessionMapPath, resolveCodexSessionMapPath, resolveCopilotSessionMapPath, } from '../state-paths.js';
|
|
16
16
|
import { createAwaitingUserProgressHandler, parseProviderReply } from './awaiting-user.js';
|
|
17
17
|
class ProviderV2CompatibilityAdapter {
|
|
18
18
|
provider;
|
|
19
19
|
turnPreparer;
|
|
20
|
-
|
|
20
|
+
capabilities;
|
|
21
|
+
constructor(provider, turnPreparer, capabilities) {
|
|
21
22
|
this.provider = provider;
|
|
22
23
|
this.turnPreparer = turnPreparer;
|
|
24
|
+
this.capabilities = capabilities;
|
|
23
25
|
}
|
|
24
26
|
async generateReply(input, options) {
|
|
25
27
|
const preparedTurn = await this.turnPreparer.prepare(input);
|
|
@@ -69,7 +71,10 @@ function createClaudeProvider(config, debugLogger, connectionsStateGateEnabled,
|
|
|
69
71
|
stateRootDir: config.stateRootDir,
|
|
70
72
|
gateEnabled: connectionsStateGateEnabled,
|
|
71
73
|
logger: debugLogger,
|
|
72
|
-
}), config.resolveStableAgentId, debugLogger
|
|
74
|
+
}), config.resolveStableAgentId, debugLogger, {
|
|
75
|
+
borgeeBaseUrl: config.borgeeBaseUrl,
|
|
76
|
+
agentApiKey: config.agentApiKey,
|
|
77
|
+
});
|
|
73
78
|
return new ClaudeProviderAdapter(cli, turnPreparer);
|
|
74
79
|
}
|
|
75
80
|
function createCopilotProvider(config, debugLogger, connectionsStateGateEnabled, implementation, turnPreparer, policyAuditGateEnabled, authorizationAuditSink, idleBackendShutdownMs) {
|
|
@@ -89,9 +94,12 @@ function createCopilotProvider(config, debugLogger, connectionsStateGateEnabled,
|
|
|
89
94
|
gateEnabled: policyAuditGateEnabled,
|
|
90
95
|
policyMode,
|
|
91
96
|
auditSink: authorizationAuditSink,
|
|
97
|
+
}, {
|
|
98
|
+
borgeeBaseUrl: config.borgeeBaseUrl,
|
|
99
|
+
agentApiKey: config.agentApiKey,
|
|
92
100
|
});
|
|
93
101
|
return implementation === 'v2'
|
|
94
|
-
? new ProviderV2CompatibilityAdapter(new CopilotProviderV2(cli), turnPreparer)
|
|
102
|
+
? new ProviderV2CompatibilityAdapter(new CopilotProviderV2(cli), turnPreparer, COPILOT_HOSTED_PROVIDER_CAPABILITIES)
|
|
95
103
|
: new CopilotProviderAdapter(cli, turnPreparer);
|
|
96
104
|
}
|
|
97
105
|
function createCodexProvider(config, debugLogger, connectionsStateGateEnabled, turnPreparer, idleBackendShutdownMs) {
|
|
@@ -103,7 +111,10 @@ function createCodexProvider(config, debugLogger, connectionsStateGateEnabled, t
|
|
|
103
111
|
stateRootDir: config.stateRootDir,
|
|
104
112
|
gateEnabled: connectionsStateGateEnabled,
|
|
105
113
|
logger: debugLogger,
|
|
106
|
-
}), config.resolveStableAgentId, debugLogger
|
|
114
|
+
}), config.resolveStableAgentId, debugLogger, {
|
|
115
|
+
borgeeBaseUrl: config.borgeeBaseUrl,
|
|
116
|
+
agentApiKey: config.agentApiKey,
|
|
117
|
+
});
|
|
107
118
|
return new CodexProviderAdapter(cli, turnPreparer);
|
|
108
119
|
}
|
|
109
120
|
export function createProvider(config, debugLogger, options = {}) {
|
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
import type { ProviderGenerateOptions, ProviderInput, ProviderReply } from '../types.js';
|
|
2
|
+
export type ProviderImageInputSupport = 'supported' | 'metadata-only' | 'not-supported';
|
|
3
|
+
export type ProviderImageTransportSupport = 'supported' | 'not-supported';
|
|
4
|
+
export type ProviderImageTransportSource = 'transport-derived' | 'runtime-derived';
|
|
5
|
+
export type ProviderImageTransportDelivery = 'host-materialized-local-path' | 'blob';
|
|
6
|
+
export type ProviderImageInputReason = 'real-media-delivered' | 'shared-host-attachment-metadata-fallback' | 'host-policy-blocked';
|
|
7
|
+
export type ProviderImageTransportReason = 'real-media-delivered' | 'transport-does-not-deliver-real-media' | 'runtime-capability-not-yet-resolved' | 'host-policy-blocked';
|
|
8
|
+
export interface SharedHostAttachmentMetadataFallbackCapability {
|
|
9
|
+
support: 'metadata-only';
|
|
10
|
+
delivery: 'prompt-text-attachment-metadata';
|
|
11
|
+
}
|
|
12
|
+
export interface ProviderImageTransportCapability {
|
|
13
|
+
source: ProviderImageTransportSource;
|
|
14
|
+
support: ProviderImageTransportSupport;
|
|
15
|
+
reason: ProviderImageTransportReason;
|
|
16
|
+
delivery: readonly ProviderImageTransportDelivery[];
|
|
17
|
+
maxImageCount?: number;
|
|
18
|
+
maxBytesPerImage?: number;
|
|
19
|
+
maxTotalBytes?: number;
|
|
20
|
+
}
|
|
21
|
+
export interface ProviderImageCapability {
|
|
22
|
+
support: ProviderImageInputSupport;
|
|
23
|
+
reason: ProviderImageInputReason;
|
|
24
|
+
sharedHostFallback: SharedHostAttachmentMetadataFallbackCapability;
|
|
25
|
+
transport: ProviderImageTransportCapability;
|
|
26
|
+
}
|
|
27
|
+
export interface ProviderCapabilities {
|
|
28
|
+
imageInput: ProviderImageCapability;
|
|
29
|
+
}
|
|
30
|
+
export declare const SHARED_HOST_ATTACHMENT_METADATA_FALLBACK: SharedHostAttachmentMetadataFallbackCapability;
|
|
31
|
+
export declare function createHostedProviderCapabilities(params: {
|
|
32
|
+
imageInputTransport: ProviderImageTransportCapability;
|
|
33
|
+
sharedHostFallback?: SharedHostAttachmentMetadataFallbackCapability;
|
|
34
|
+
}): ProviderCapabilities;
|
|
35
|
+
export declare const TEXT_ONLY_HOSTED_PROVIDER_CAPABILITIES: ProviderCapabilities;
|
|
2
36
|
export interface ProviderAdapter {
|
|
37
|
+
readonly capabilities: ProviderCapabilities;
|
|
3
38
|
generateReply(input: ProviderInput, options?: ProviderGenerateOptions): Promise<ProviderReply>;
|
|
4
39
|
dispose?(): Promise<void>;
|
|
5
40
|
}
|
|
@@ -1 +1,44 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export const SHARED_HOST_ATTACHMENT_METADATA_FALLBACK = Object.freeze({
|
|
2
|
+
support: 'metadata-only',
|
|
3
|
+
delivery: 'prompt-text-attachment-metadata',
|
|
4
|
+
});
|
|
5
|
+
function resolveImageInputSupport(transport, sharedHostFallback) {
|
|
6
|
+
if (transport.support === 'supported') {
|
|
7
|
+
return {
|
|
8
|
+
support: 'supported',
|
|
9
|
+
reason: 'real-media-delivered',
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
if (transport.reason === 'host-policy-blocked') {
|
|
13
|
+
return {
|
|
14
|
+
support: 'not-supported',
|
|
15
|
+
reason: 'host-policy-blocked',
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
support: sharedHostFallback.support,
|
|
20
|
+
reason: 'shared-host-attachment-metadata-fallback',
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export function createHostedProviderCapabilities(params) {
|
|
24
|
+
const sharedHostFallback = params.sharedHostFallback ?? SHARED_HOST_ATTACHMENT_METADATA_FALLBACK;
|
|
25
|
+
const transport = Object.freeze({
|
|
26
|
+
...params.imageInputTransport,
|
|
27
|
+
delivery: Object.freeze([...params.imageInputTransport.delivery]),
|
|
28
|
+
});
|
|
29
|
+
return Object.freeze({
|
|
30
|
+
imageInput: Object.freeze({
|
|
31
|
+
...resolveImageInputSupport(transport, sharedHostFallback),
|
|
32
|
+
sharedHostFallback,
|
|
33
|
+
transport,
|
|
34
|
+
}),
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
export const TEXT_ONLY_HOSTED_PROVIDER_CAPABILITIES = createHostedProviderCapabilities({
|
|
38
|
+
imageInputTransport: {
|
|
39
|
+
source: 'transport-derived',
|
|
40
|
+
support: 'not-supported',
|
|
41
|
+
reason: 'transport-does-not-deliver-real-media',
|
|
42
|
+
delivery: [],
|
|
43
|
+
},
|
|
44
|
+
});
|
package/dist/state-paths.d.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
export declare function normalizeManagedRuntimeKey(serverUrl: string): string;
|
|
2
|
+
export type ManagedDaemonEndpoint = {
|
|
3
|
+
kind: 'named-pipe';
|
|
4
|
+
address: string;
|
|
5
|
+
} | {
|
|
6
|
+
kind: 'unix-socket';
|
|
7
|
+
address: string;
|
|
8
|
+
};
|
|
2
9
|
export declare function resolveSingleAgentStateRoot(env?: NodeJS.ProcessEnv, resolvedHomeDir?: string, agentKey?: string): string;
|
|
3
10
|
export declare function resolveManagedRuntimeRoot(serverUrl: string, env?: NodeJS.ProcessEnv, resolvedHomeDir?: string): string;
|
|
4
11
|
/**
|
|
@@ -7,7 +14,8 @@ export declare function resolveManagedRuntimeRoot(serverUrl: string, env?: NodeJ
|
|
|
7
14
|
*/
|
|
8
15
|
export declare function resolveUpdateCheckCachePath(env?: NodeJS.ProcessEnv, resolvedHomeDir?: string): string;
|
|
9
16
|
export declare function resolveManagedStateRoot(rootPath: string): string;
|
|
10
|
-
export declare function
|
|
17
|
+
export declare function resolveManagedDaemonEndpoint(rootPath: string, platform?: NodeJS.Platform): ManagedDaemonEndpoint;
|
|
18
|
+
export declare function resolveManagedDaemonSocketPath(rootPath: string, platform?: NodeJS.Platform): string;
|
|
11
19
|
export declare function resolveManagedDaemonLogPath(rootPath: string): string;
|
|
12
20
|
export declare function resolveManagedRuntimeSettingsPath(rootPath: string): string;
|
|
13
21
|
export declare function resolveManagedBootstrapLockPath(rootPath: string): string;
|
package/dist/state-paths.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
|
-
import { dirname, join, resolve } from 'node:path';
|
|
3
|
+
import { dirname, join, posix, resolve, win32 } 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
7
|
const MANAGED_RUNTIME_NAMESPACE = 'managed';
|
|
8
8
|
const MANAGED_STATE_DIRNAME = '.state';
|
|
9
9
|
const MANAGED_DAEMON_SOCKET_FILENAME = 'ctl.sock';
|
|
10
|
+
const MANAGED_DAEMON_PIPE_PREFIX = '\\\\.\\pipe\\borgee-agents-host-managed-';
|
|
10
11
|
const MANAGED_DAEMON_LOG_FILENAME = 'daemon.log';
|
|
11
12
|
const MANAGED_RUNTIME_SETTINGS_FILENAME = 'managed-runtime-settings.json';
|
|
12
13
|
const MANAGED_BOOTSTRAP_LOCK_DIRNAME = '.bootstrap.lock';
|
|
@@ -95,8 +96,26 @@ export function resolveUpdateCheckCachePath(env = process.env, resolvedHomeDir =
|
|
|
95
96
|
export function resolveManagedStateRoot(rootPath) {
|
|
96
97
|
return join(resolve(rootPath), MANAGED_STATE_DIRNAME);
|
|
97
98
|
}
|
|
98
|
-
export function
|
|
99
|
-
|
|
99
|
+
export function resolveManagedDaemonEndpoint(rootPath, platform = process.platform) {
|
|
100
|
+
const pathApi = platform === 'win32' ? win32 : posix;
|
|
101
|
+
const resolvedRoot = pathApi.resolve(rootPath);
|
|
102
|
+
if (platform === 'win32') {
|
|
103
|
+
const rootHash = createHash('sha256')
|
|
104
|
+
.update(resolvedRoot.toLowerCase())
|
|
105
|
+
.digest('hex')
|
|
106
|
+
.slice(0, 24);
|
|
107
|
+
return {
|
|
108
|
+
kind: 'named-pipe',
|
|
109
|
+
address: `${MANAGED_DAEMON_PIPE_PREFIX}${rootHash}`,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
kind: 'unix-socket',
|
|
114
|
+
address: posix.join(resolvedRoot, MANAGED_DAEMON_SOCKET_FILENAME),
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
export function resolveManagedDaemonSocketPath(rootPath, platform = process.platform) {
|
|
118
|
+
return resolveManagedDaemonEndpoint(rootPath, platform).address;
|
|
100
119
|
}
|
|
101
120
|
export function resolveManagedDaemonLogPath(rootPath) {
|
|
102
121
|
return join(resolve(rootPath), MANAGED_DAEMON_LOG_FILENAME);
|
package/dist/types.d.ts
CHANGED
|
@@ -12,7 +12,9 @@ export interface ProviderCommandConfig {
|
|
|
12
12
|
}
|
|
13
13
|
export interface ProviderRuntimeConfig extends ProviderCommandConfig {
|
|
14
14
|
provider: ProviderKind;
|
|
15
|
+
borgeeBaseUrl: string;
|
|
15
16
|
stateRootDir: string;
|
|
17
|
+
agentApiKey: string;
|
|
16
18
|
resolveStableAgentId?: () => string | undefined;
|
|
17
19
|
}
|
|
18
20
|
export interface HostedAgentConfig {
|
|
@@ -96,6 +98,7 @@ export interface ChannelMessageEvent {
|
|
|
96
98
|
content?: string;
|
|
97
99
|
body?: string;
|
|
98
100
|
content_type?: string;
|
|
101
|
+
attachments?: HostedMessageAttachment[];
|
|
99
102
|
created_at?: number;
|
|
100
103
|
[key: string]: unknown;
|
|
101
104
|
}
|
|
@@ -115,11 +118,32 @@ export interface ChannelHistoryEntry {
|
|
|
115
118
|
authorId: string;
|
|
116
119
|
body: string;
|
|
117
120
|
contentType?: string;
|
|
121
|
+
attachments?: HostedMessageAttachment[];
|
|
118
122
|
type?: string;
|
|
119
123
|
replyToId?: string;
|
|
120
124
|
createdAt: number;
|
|
121
125
|
editedAt?: number;
|
|
122
126
|
}
|
|
127
|
+
export interface HostedMessageAttachment {
|
|
128
|
+
url: string;
|
|
129
|
+
filename?: string;
|
|
130
|
+
contentType: string;
|
|
131
|
+
kind?: 'image' | 'file';
|
|
132
|
+
sizeBytes?: number;
|
|
133
|
+
}
|
|
134
|
+
export interface HostedTextContentPart {
|
|
135
|
+
type: 'text';
|
|
136
|
+
text: string;
|
|
137
|
+
}
|
|
138
|
+
export interface HostedImageContentPart {
|
|
139
|
+
type: 'image';
|
|
140
|
+
attachment: HostedMessageAttachment;
|
|
141
|
+
}
|
|
142
|
+
export interface HostedFileContentPart {
|
|
143
|
+
type: 'file';
|
|
144
|
+
attachment: HostedMessageAttachment;
|
|
145
|
+
}
|
|
146
|
+
export type HostedTurnContentPart = HostedTextContentPart | HostedImageContentPart | HostedFileContentPart;
|
|
123
147
|
export interface ReadChannelHistoryInput {
|
|
124
148
|
channelId: string;
|
|
125
149
|
before?: number;
|
|
@@ -234,6 +258,7 @@ export interface ProviderInput {
|
|
|
234
258
|
channelId: string;
|
|
235
259
|
incomingAuthorId: string;
|
|
236
260
|
incomingContent: string;
|
|
261
|
+
incomingParts?: HostedTurnContentPart[];
|
|
237
262
|
incomingEventKind?: string;
|
|
238
263
|
incomingMessageType?: string;
|
|
239
264
|
collaboration?: ProviderCollaborationContext;
|
|
@@ -291,7 +316,7 @@ export interface TaskWorkspaceContext {
|
|
|
291
316
|
}
|
|
292
317
|
export interface PreparedPromptContext {
|
|
293
318
|
channelContextPayloadPath?: string;
|
|
294
|
-
|
|
319
|
+
gatewayCredentialPath?: string;
|
|
295
320
|
collaborationTurnExecutionId?: string;
|
|
296
321
|
collaborationTurnMode?: ProviderCollaborationTurnMode;
|
|
297
322
|
collaborationOutcome?: CollaborationOutcomeSnapshot;
|
|
@@ -313,6 +338,8 @@ export interface PreparedProviderSessionRouting {
|
|
|
313
338
|
}
|
|
314
339
|
export interface PreparedProviderTurnInput {
|
|
315
340
|
channelId: string;
|
|
341
|
+
incomingContent: string;
|
|
342
|
+
incomingParts: HostedTurnContentPart[];
|
|
316
343
|
incomingEventKind?: string;
|
|
317
344
|
incomingMessageType?: string;
|
|
318
345
|
prompt: string;
|
|
@@ -362,7 +389,7 @@ export interface Task {
|
|
|
362
389
|
id: string;
|
|
363
390
|
channelId: string;
|
|
364
391
|
guildId: string;
|
|
365
|
-
seq
|
|
392
|
+
seq: number;
|
|
366
393
|
title: string;
|
|
367
394
|
description: string;
|
|
368
395
|
status: string;
|
|
@@ -372,6 +399,10 @@ export interface Task {
|
|
|
372
399
|
threadId: string | null;
|
|
373
400
|
createdAt: number;
|
|
374
401
|
updatedAt: number;
|
|
402
|
+
heartbeatIntervalMs: number;
|
|
403
|
+
heartbeatPrompt: string;
|
|
404
|
+
participants: string[];
|
|
405
|
+
properties: Record<string, string>;
|
|
375
406
|
}
|
|
376
407
|
export interface CreateTaskInput {
|
|
377
408
|
channelId: string;
|