@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.
Files changed (42) hide show
  1. package/dist/agents-host.d.ts +2 -0
  2. package/dist/agents-host.js +111 -44
  3. package/dist/chat/sdk-chat-control-plane.js +3 -0
  4. package/dist/context/injection.d.ts +5 -3
  5. package/dist/context/injection.js +45 -29
  6. package/dist/context/prompt.js +23 -35
  7. package/dist/context/skill-manual.d.ts +13 -0
  8. package/dist/context/skill-manual.js +18 -0
  9. package/dist/context/turn-preparation.js +13 -4
  10. package/dist/gateway/localhost-gateway.js +16 -8
  11. package/dist/hosted-turn-content.d.ts +15 -0
  12. package/dist/hosted-turn-content.js +50 -0
  13. package/dist/managed-daemon.d.ts +3 -2
  14. package/dist/managed-daemon.js +77 -29
  15. package/dist/providers/claude/adapter.d.ts +3 -1
  16. package/dist/providers/claude/adapter.js +10 -0
  17. package/dist/providers/claude/cli-client.d.ts +11 -2
  18. package/dist/providers/claude/cli-client.js +98 -27
  19. package/dist/providers/codex/adapter.d.ts +3 -1
  20. package/dist/providers/codex/adapter.js +10 -0
  21. package/dist/providers/codex/cli-client.d.ts +10 -2
  22. package/dist/providers/codex/cli-client.js +85 -21
  23. package/dist/providers/codex/project-doc.js +13 -29
  24. package/dist/providers/copilot/adapter.d.ts +3 -1
  25. package/dist/providers/copilot/adapter.js +10 -0
  26. package/dist/providers/copilot/cli-client.d.ts +9 -1
  27. package/dist/providers/copilot/cli-client.js +71 -8
  28. package/dist/providers/create-provider.d.ts +1 -1
  29. package/dist/providers/create-provider.js +16 -5
  30. package/dist/providers/provider-adapter.d.ts +35 -0
  31. package/dist/providers/provider-adapter.js +44 -1
  32. package/dist/state-paths.d.ts +9 -1
  33. package/dist/state-paths.js +22 -3
  34. package/dist/types.d.ts +33 -2
  35. package/package.json +1 -1
  36. package/skills/borgee-agent/SKILL.md +119 -38
  37. package/skills/borgee-agent/references/errors.md +38 -0
  38. package/skills/borgee-agent/references/task-properties.md +30 -0
  39. package/skills/borgee-agent/scripts/borgee-agent.mjs +553 -0
  40. package/skills/borgee-agent/scripts/borgee-agent.py +547 -0
  41. package/skills/borgee-agent/borgee-agent.mjs +0 -562
  42. 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.channelContextPayloadPath) {
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
- function buildGatewayLines(context) {
22
- if (!context?.localhostGateway || !context.channelContextPayloadPath) {
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
- const lines = [
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 = buildGatewayLines(context);
66
+ const gatewayLines = buildGatewayCredentialLines(context);
83
67
  return gatewayLines.length > 0 ? ['', ...gatewayLines] : [];
84
68
  })(),
85
69
  ];
@@ -1,10 +1,12 @@
1
- import type { ProviderAdapter } from '../provider-adapter.js';
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, prompt, sessionPersistence, promptContext, options) {
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
- prompt,
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 preparedTurn = typeof channelIdOrTurn === 'string'
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.prompt, sessionRouting.persistence, preparedTurn.promptContext, options);
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
- reply = await this.runTurn(session, turn.prompt, turn.options);
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 { ProviderAdapter } from './provider-adapter.js';
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
- constructor(provider, turnPreparer) {
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
+ });
@@ -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 resolveManagedDaemonSocketPath(rootPath: string): string;
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;
@@ -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 resolveManagedDaemonSocketPath(rootPath) {
99
- return join(resolve(rootPath), MANAGED_DAEMON_SOCKET_FILENAME);
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
- gatewayAuthPath?: string;
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?: number;
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@borgee/agents-host",
3
- "version": "0.2.35",
3
+ "version": "0.2.44",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"