@borgee/agents-host 0.2.35 → 0.2.56

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 (74) hide show
  1. package/README.md +23 -27
  2. package/dist/agents-host.d.ts +28 -5
  3. package/dist/agents-host.js +274 -236
  4. package/dist/chat/chat-control-plane.d.ts +3 -0
  5. package/dist/chat/sdk-chat-control-plane.d.ts +4 -3
  6. package/dist/chat/sdk-chat-control-plane.js +6 -0
  7. package/dist/cli.js +1 -5
  8. package/dist/compatibility-gates.d.ts +4 -0
  9. package/dist/compatibility-gates.js +18 -1
  10. package/dist/context/claude-file-brief.d.ts +2 -0
  11. package/dist/context/claude-file-brief.js +83 -0
  12. package/dist/context/compaction.d.ts +20 -0
  13. package/dist/context/compaction.js +59 -0
  14. package/dist/context/injection.d.ts +44 -9
  15. package/dist/context/injection.js +360 -53
  16. package/dist/context/main-session-delegation.d.ts +1 -1
  17. package/dist/context/projection-strategy.d.ts +24 -0
  18. package/dist/context/projection-strategy.js +90 -0
  19. package/dist/context/prompt.d.ts +16 -1
  20. package/dist/context/prompt.js +475 -53
  21. package/dist/context/resolved-workspace.d.ts +2 -0
  22. package/dist/context/resolved-workspace.js +64 -0
  23. package/dist/context/skill-manual.d.ts +14 -0
  24. package/dist/context/skill-manual.js +21 -0
  25. package/dist/context/turn-preparation.d.ts +8 -2
  26. package/dist/context/turn-preparation.js +68 -17
  27. package/dist/gateway/localhost-gateway.js +18 -8
  28. package/dist/hosted-turn-content.d.ts +15 -0
  29. package/dist/hosted-turn-content.js +50 -0
  30. package/dist/managed-daemon.d.ts +3 -2
  31. package/dist/managed-daemon.js +198 -37
  32. package/dist/plugin-sdk.js +276 -359
  33. package/dist/plugin-sdk.js.map +4 -4
  34. package/dist/progress-to-activity.d.ts +16 -0
  35. package/dist/progress-to-activity.js +24 -0
  36. package/dist/projection-strategy-values.d.ts +4 -0
  37. package/dist/projection-strategy-values.js +28 -0
  38. package/dist/providers/acp-progress-collector.d.ts +44 -0
  39. package/dist/providers/acp-progress-collector.js +130 -0
  40. package/dist/providers/awaiting-user.d.ts +2 -3
  41. package/dist/providers/awaiting-user.js +5 -7
  42. package/dist/providers/claude/activity-metadata.d.ts +14 -0
  43. package/dist/providers/claude/activity-metadata.js +81 -0
  44. package/dist/providers/claude/adapter.d.ts +3 -1
  45. package/dist/providers/claude/adapter.js +10 -0
  46. package/dist/providers/claude/cli-client.d.ts +9 -1
  47. package/dist/providers/claude/cli-client.js +270 -126
  48. package/dist/providers/codex/adapter.d.ts +3 -1
  49. package/dist/providers/codex/adapter.js +10 -0
  50. package/dist/providers/codex/cli-client.d.ts +10 -2
  51. package/dist/providers/codex/cli-client.js +88 -104
  52. package/dist/providers/codex/project-doc.js +22 -37
  53. package/dist/providers/copilot/adapter.d.ts +3 -1
  54. package/dist/providers/copilot/adapter.js +10 -0
  55. package/dist/providers/copilot/cli-client.d.ts +9 -1
  56. package/dist/providers/copilot/cli-client.js +68 -85
  57. package/dist/providers/create-provider.d.ts +3 -1
  58. package/dist/providers/create-provider.js +36 -9
  59. package/dist/providers/provider-adapter.d.ts +35 -0
  60. package/dist/providers/provider-adapter.js +44 -1
  61. package/dist/state-paths.d.ts +10 -2
  62. package/dist/state-paths.js +25 -6
  63. package/dist/task-thread-resolution.d.ts +3 -2
  64. package/dist/types.d.ts +163 -8
  65. package/package.json +2 -2
  66. package/skills/borgee-agent/SKILL.md +127 -38
  67. package/skills/borgee-agent/references/errors.md +38 -0
  68. package/skills/borgee-agent/references/task-properties.md +33 -0
  69. package/skills/borgee-agent/scripts/borgee-agent.mjs +553 -0
  70. package/skills/borgee-agent/scripts/borgee-agent.py +547 -0
  71. package/dist/durable-cursor-store.d.ts +0 -5
  72. package/dist/durable-cursor-store.js +0 -7
  73. package/skills/borgee-agent/borgee-agent.mjs +0 -562
  74. package/skills/borgee-agent/borgee-agent.py +0 -469
@@ -2,6 +2,7 @@ import { Readable, Writable } from 'node:stream';
2
2
  import spawn from 'cross-spawn';
3
3
  import { PROTOCOL_VERSION, client, methods, ndJsonStream, } from '@agentclientprotocol/sdk';
4
4
  import { HostLogger, summarizeChildStderr, summarizeError } from '../../debug.js';
5
+ import { AcpProgressCollector } from '../acp-progress-collector.js';
5
6
  import { resolveCopilotPermissionResponse } from '../../policy/copilot-permission.js';
6
7
  import { IDLE_BACKEND_SHUTDOWN_DISABLED_MS, IdleBackendShutdownScheduler, } from '../idle-backend-shutdown.js';
7
8
  const SESSION_TAINTED_ERRORS = new WeakSet();
@@ -30,93 +31,35 @@ const DEFAULT_RUNTIME = {
30
31
  idleShutdownGracePeriodMs: DEFAULT_IDLE_SHUTDOWN_GRACE_PERIOD_MS,
31
32
  shutdownGracePeriodMs: DEFAULT_SHUTDOWN_GRACE_PERIOD_MS,
32
33
  shutdownForceKillWaitMs: DEFAULT_SHUTDOWN_FORCE_KILL_WAIT_MS,
34
+ fetch: async (input, init) => fetch(input, init),
33
35
  };
34
36
  const DEFAULT_SESSION_CAPABILITIES = {
35
37
  loadSession: false,
36
38
  resumeSession: false,
37
39
  };
38
- function hasVisibleText(value) {
39
- return typeof value === 'string' && value.trim().length > 0;
40
+ function isAbsoluteHttpUrl(value) {
41
+ return value.startsWith('http://') || value.startsWith('https://');
40
42
  }
41
- function formatToolProgress(title, status) {
42
- const normalizedTitle = hasVisibleText(title) ? title.trim() : null;
43
- switch (status) {
44
- case 'completed':
45
- return normalizedTitle ? `Completed ${normalizedTitle}` : 'Completed tool call';
46
- case 'failed':
47
- return normalizedTitle ? `Failed ${normalizedTitle}` : 'Tool call failed';
48
- case 'pending':
49
- case 'in_progress':
50
- case undefined:
51
- case null:
52
- return normalizedTitle ? `Running ${normalizedTitle}…` : 'Running tool…';
53
- default:
54
- return normalizedTitle ? `${status} ${normalizedTitle}` : status;
43
+ function resolveHostedAttachmentUrl(rawUrl, borgeeBaseUrl) {
44
+ if (isAbsoluteHttpUrl(rawUrl)) {
45
+ return rawUrl;
55
46
  }
47
+ const base = borgeeBaseUrl.replace(/\/$/, '');
48
+ const path = rawUrl.startsWith('/') ? rawUrl : `/${rawUrl}`;
49
+ return `${base}${path}`;
56
50
  }
57
- function formatPlanProgress(entries) {
58
- const current = entries.find((entry) => entry.status === 'in_progress') ??
59
- entries.find((entry) => entry.status === 'pending') ??
60
- entries[0];
61
- return hasVisibleText(current?.content) ? `Plan: ${current.content.trim()}` : null;
62
- }
63
- class CopilotProgressCollector {
64
- onProgress;
65
- publicText = '';
66
- lastPublished = null;
67
- toolTitles = new Map();
68
- constructor(onProgress) {
69
- this.onProgress = onProgress;
70
- }
71
- consume(update) {
72
- switch (update.update.sessionUpdate) {
73
- case 'agent_message_chunk':
74
- if (update.update.content.type !== 'text') {
75
- return;
76
- }
77
- this.publicText += update.update.content.text;
78
- this.publish(this.publicText);
79
- return;
80
- case 'tool_call':
81
- this.toolTitles.set(update.update.toolCallId, update.update.title);
82
- this.publishFallback(formatToolProgress(update.update.title, update.update.status));
83
- return;
84
- case 'tool_call_update': {
85
- const nextTitle = update.update.title ?? this.toolTitles.get(update.update.toolCallId);
86
- if (hasVisibleText(nextTitle)) {
87
- this.toolTitles.set(update.update.toolCallId, nextTitle);
88
- }
89
- this.publishFallback(formatToolProgress(nextTitle, update.update.status));
90
- return;
91
- }
92
- case 'plan':
93
- this.publishFallback(formatPlanProgress(update.update.entries.map((entry) => ({
94
- content: entry.content,
95
- status: entry.status,
96
- }))));
97
- return;
98
- default:
99
- return;
100
- }
51
+ function isSameOrigin(left, right) {
52
+ try {
53
+ return new URL(left).origin === new URL(right).origin;
101
54
  }
102
- getFinalText() {
103
- return this.publicText.trim();
104
- }
105
- publishFallback(text) {
106
- if (hasVisibleText(this.publicText)) {
107
- return;
108
- }
109
- this.publish(text);
110
- }
111
- publish(text) {
112
- if (!this.onProgress || !hasVisibleText(text) || text === this.lastPublished) {
113
- return;
114
- }
115
- this.lastPublished = text;
116
- this.onProgress({ text });
55
+ catch {
56
+ return false;
117
57
  }
118
58
  }
119
- function createDeferredTurn(channelId, prompt, sessionPersistence, promptContext, options) {
59
+ function asImagePart(part) {
60
+ return part.type === 'image' ? part : null;
61
+ }
62
+ function createDeferredTurn(channelId, preparedTurn, sessionPersistence, options) {
120
63
  let settled = false;
121
64
  let resolvePromise;
122
65
  let rejectPromise;
@@ -126,9 +69,8 @@ function createDeferredTurn(channelId, prompt, sessionPersistence, promptContext
126
69
  });
127
70
  return {
128
71
  channelId,
129
- prompt,
72
+ preparedTurn,
130
73
  sessionPersistence,
131
- promptContext,
132
74
  options,
133
75
  promise,
134
76
  resolve(value) {
@@ -216,6 +158,7 @@ export class CopilotCliClient {
216
158
  resolveSessionStoreAgentId;
217
159
  logger;
218
160
  permissionPolicy;
161
+ imageInputConfig;
219
162
  runtime;
220
163
  channels = new Map();
221
164
  /**
@@ -253,12 +196,13 @@ export class CopilotCliClient {
253
196
  sessionStoreWriteQueue = Promise.resolve();
254
197
  sessionCapabilities = DEFAULT_SESSION_CAPABILITIES;
255
198
  idleBackendShutdown;
256
- constructor(command, _ignoredArgs = [], runtimeOverrides = {}, sessionStore, resolveSessionStoreAgentId = () => undefined, logger = new HostLogger(), permissionPolicy = {}) {
199
+ constructor(command, _ignoredArgs = [], runtimeOverrides = {}, sessionStore, resolveSessionStoreAgentId = () => undefined, logger = new HostLogger(), permissionPolicy = {}, imageInputConfig = {}) {
257
200
  this.command = command;
258
201
  this.sessionStore = sessionStore;
259
202
  this.resolveSessionStoreAgentId = resolveSessionStoreAgentId;
260
203
  this.logger = logger;
261
204
  this.permissionPolicy = permissionPolicy;
205
+ this.imageInputConfig = imageInputConfig;
262
206
  this.runtime = { ...DEFAULT_RUNTIME, ...runtimeOverrides };
263
207
  this.idleBackendShutdown = new IdleBackendShutdownScheduler({
264
208
  idleShutdownMs: this.runtime.idleBackendShutdownMs,
@@ -279,12 +223,19 @@ export class CopilotCliClient {
279
223
  if (this.fatalError) {
280
224
  throw this.fatalError;
281
225
  }
282
- const preparedTurn = typeof channelIdOrTurn === 'string'
226
+ const preparedTurnBase = typeof channelIdOrTurn === 'string'
283
227
  ? {
284
228
  channelId: channelIdOrTurn,
229
+ incomingContent: '',
230
+ incomingParts: [],
285
231
  prompt: typeof promptOrOptions === 'string' ? promptOrOptions : '',
286
232
  }
287
233
  : channelIdOrTurn;
234
+ const preparedTurn = {
235
+ ...preparedTurnBase,
236
+ incomingContent: preparedTurnBase.incomingContent ?? '',
237
+ incomingParts: preparedTurnBase.incomingParts ?? [],
238
+ };
288
239
  const options = typeof channelIdOrTurn === 'string'
289
240
  ? maybeOptions
290
241
  : promptOrOptions;
@@ -292,7 +243,7 @@ export class CopilotCliClient {
292
243
  const state = this.getOrCreateChannelState(sessionRouting.key, preparedTurn.channelId, sessionRouting.persistence);
293
244
  this.clearIdleTimer(state);
294
245
  this.idleBackendShutdown.cancel();
295
- const turn = createDeferredTurn(preparedTurn.channelId, preparedTurn.prompt, sessionRouting.persistence, preparedTurn.promptContext, options);
246
+ const turn = createDeferredTurn(preparedTurn.channelId, preparedTurn, sessionRouting.persistence, options);
296
247
  state.queue.push(turn);
297
248
  this.processChannelQueue(sessionRouting.key, state);
298
249
  return turn.promise;
@@ -455,13 +406,14 @@ export class CopilotCliClient {
455
406
  }
456
407
  state.activeTurn = turn;
457
408
  try {
458
- state.cwd = await this.resolveSessionCwd(turn.promptContext);
409
+ state.cwd = await this.resolveSessionCwd(turn.preparedTurn.promptContext);
459
410
  await this.ensureStarted();
460
411
  await this.recycleSessionIfCwdChanged(channelId, state);
461
412
  const session = await this.getOrCreateSession(channelId, state);
462
413
  let reply;
463
414
  try {
464
- reply = await this.runTurn(session, turn.prompt, turn.options);
415
+ const promptInput = await this.buildPromptInput(turn.preparedTurn);
416
+ reply = await this.runTurn(session, promptInput, turn.options);
465
417
  }
466
418
  catch (error) {
467
419
  if (isSessionTainted(error)) {
@@ -615,7 +567,38 @@ export class CopilotCliClient {
615
567
  }
616
568
  }
617
569
  async resolveSessionCwd(promptContext) {
618
- return promptContext?.taskWorkspace?.rootPath ?? this.runtime.cwd;
570
+ return promptContext?.resolvedWorkspace?.rootPath ?? this.runtime.cwd;
571
+ }
572
+ async buildPromptInput(turn) {
573
+ const imageParts = turn.incomingParts.map(asImagePart).filter((part) => part !== null);
574
+ if (imageParts.length === 0) {
575
+ return turn.prompt;
576
+ }
577
+ const imageBlocks = await Promise.all(imageParts.map(async (part) => this.fetchImageBlock(part.attachment.url, part.attachment.contentType)));
578
+ return [{ type: 'text', text: turn.prompt }, ...imageBlocks];
579
+ }
580
+ async fetchImageBlock(rawUrl, contentType) {
581
+ const baseUrl = this.imageInputConfig.borgeeBaseUrl?.trim();
582
+ const agentApiKey = this.imageInputConfig.agentApiKey?.trim();
583
+ if (!baseUrl || !agentApiKey) {
584
+ throw new Error('Copilot image input requires Borgee base URL and agent API key');
585
+ }
586
+ const resolvedUrl = resolveHostedAttachmentUrl(rawUrl, baseUrl);
587
+ if (!isSameOrigin(resolvedUrl, baseUrl)) {
588
+ throw new Error(`Copilot image input only supports Borgee-origin attachments: ${resolvedUrl}`);
589
+ }
590
+ const response = await this.runtime.fetch(resolvedUrl, {
591
+ headers: { Authorization: `Bearer ${agentApiKey}` },
592
+ });
593
+ if (!response.ok) {
594
+ throw new Error(`Copilot image fetch failed with ${response.status} ${response.statusText}`.trim());
595
+ }
596
+ return {
597
+ type: 'image',
598
+ mimeType: contentType,
599
+ data: Buffer.from(await response.arrayBuffer()).toString('base64'),
600
+ uri: response.url || resolvedUrl,
601
+ };
619
602
  }
620
603
  async recycleSessionIfCwdChanged(channelId, state) {
621
604
  const session = state.session;
@@ -644,7 +627,7 @@ export class CopilotCliClient {
644
627
  const promptFailure = new Promise((_, reject) => {
645
628
  void promptPromise.catch((error) => reject(markSessionTainted(error)));
646
629
  });
647
- const collector = new CopilotProgressCollector(options?.onProgress);
630
+ const collector = new AcpProgressCollector(options?.onProgress);
648
631
  for (;;) {
649
632
  let update;
650
633
  try {
@@ -1,12 +1,14 @@
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 { ChatControlPlane } from '../chat/chat-control-plane.js';
4
+ import { type ProviderAdapter } from './provider-adapter.js';
4
5
  import type { LocalhostGatewayContextPublisher } from '../context/injection.js';
5
6
  import type { AuthorizationAuditSinkLike } from '../policy/authorization-audit.js';
6
7
  interface CreateProviderOptions {
7
8
  compatibilityGates?: ReadonlySet<string>;
8
9
  localhostGateway?: LocalhostGatewayContextPublisher;
9
10
  authorizationAuditSink?: AuthorizationAuditSinkLike;
11
+ controlPlane?: Pick<ChatControlPlane, 'getTask' | 'listChannels' | 'listTasks'>;
10
12
  }
11
13
  export declare function createProvider(config: ProviderRuntimeConfig, debugLogger?: DebugLogger, options?: CreateProviderOptions): ProviderAdapter;
12
14
  export {};
@@ -1,7 +1,8 @@
1
1
  import { FileChannelContextStore } from '../context/injection.js';
2
+ import { ServerProjectionStrategyResolver } from '../context/projection-strategy.js';
2
3
  import { createProviderConnectionsSessionStore } from '../connections-state-store.js';
3
4
  import { ProviderTurnPreparer } from '../context/turn-preparation.js';
4
- import { 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';
5
+ import { 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, resolveInternalProjectionStrategy, SKILL_RUNTIME_COMPATIBILITY_GATE, } from '../compatibility-gates.js';
5
6
  import { resolveIdleBackendShutdownMs } from './idle-backend-shutdown.js';
6
7
  import { ClaudeCliClient } from './claude/cli-client.js';
7
8
  import { ClaudeProviderAdapter } from './claude/adapter.js';
@@ -10,16 +11,18 @@ import { CodexCliClient } from './codex/cli-client.js';
10
11
  import { CodexProviderAdapter } from './codex/adapter.js';
11
12
  import { FileCodexChannelSessionStore } from './codex/session-store.js';
12
13
  import { CopilotCliClient } from './copilot/cli-client.js';
13
- import { CopilotProviderAdapter } from './copilot/adapter.js';
14
+ import { COPILOT_HOSTED_PROVIDER_CAPABILITIES, CopilotProviderAdapter } from './copilot/adapter.js';
14
15
  import { FileCopilotChannelSessionStore } from './copilot/session-store.js';
15
16
  import { resolveClaudeSessionMapPath, resolveCodexSessionMapPath, resolveCopilotSessionMapPath, } from '../state-paths.js';
16
17
  import { createAwaitingUserProgressHandler, parseProviderReply } from './awaiting-user.js';
17
18
  class ProviderV2CompatibilityAdapter {
18
19
  provider;
19
20
  turnPreparer;
20
- constructor(provider, turnPreparer) {
21
+ capabilities;
22
+ constructor(provider, turnPreparer, capabilities) {
21
23
  this.provider = provider;
22
24
  this.turnPreparer = turnPreparer;
25
+ this.capabilities = capabilities;
23
26
  }
24
27
  async generateReply(input, options) {
25
28
  const preparedTurn = await this.turnPreparer.prepare(input);
@@ -50,7 +53,6 @@ class CliBackedProviderV2 {
50
53
  }
51
54
  class CopilotProviderV2 extends CliBackedProviderV2 {
52
55
  }
53
- const TASK_WORKSPACE_ROOT_DIR_ENV = 'AGENTS_HOST_INTERNAL_TASK_WORKSPACE_ROOT_DIR';
54
56
  const PROVIDER_V2_COMPATIBILITY_GATES = {
55
57
  copilot: COPILOT_PROVIDER_V2_COMPATIBILITY_GATE,
56
58
  };
@@ -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 = {}) {
@@ -120,13 +131,29 @@ export function createProvider(config, debugLogger, options = {}) {
120
131
  const channelContextStore = contextInjectionGateEnabled
121
132
  ? new FileChannelContextStore(config.stateRootDir, {
122
133
  skillRuntimeEnabled: skillRuntimeGateEnabled,
123
- taskWorkspaceRootDir: process.env[TASK_WORKSPACE_ROOT_DIR_ENV],
124
134
  localhostGateway: skillRuntimeGateEnabled && localhostGatewayGateEnabled
125
135
  ? options.localhostGateway
126
136
  : undefined,
137
+ taskReader: options.controlPlane,
127
138
  })
128
139
  : undefined;
129
- const turnPreparer = new ProviderTurnPreparer(channelContextStore, debugLogger);
140
+ const resolveInternalProjection = () => resolveInternalProjectionStrategy();
141
+ const projectionStrategyResolver = new ServerProjectionStrategyResolver({
142
+ baseUrl: config.borgeeBaseUrl,
143
+ agentApiKey: config.agentApiKey,
144
+ resolveStableAgentId: config.resolveStableAgentId,
145
+ fallback: resolveInternalProjection,
146
+ logger: debugLogger,
147
+ taskReader: options.controlPlane,
148
+ });
149
+ const turnPreparer = new ProviderTurnPreparer(channelContextStore, debugLogger, {
150
+ resolveProjectionStrategy: async (input) => {
151
+ if (input.provider !== 'claude') {
152
+ return resolveInternalProjection();
153
+ }
154
+ return projectionStrategyResolver.resolve(input.channelId);
155
+ },
156
+ });
130
157
  switch (config.provider) {
131
158
  case 'claude': {
132
159
  return createClaudeProvider(config, debugLogger, connectionsStateGateEnabled, turnPreparer, idleBackendShutdownMs);
@@ -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,16 +14,17 @@ 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;
14
22
  export declare function resolveLocalConfigAgentStateRoot(rootPath: string, agentKey: string): string;
15
- export declare function resolveAgentCursorPath(stateRootDir: string, agentId: string): string;
16
23
  export declare function resolveSharedProtocolKickoffDecisionPath(stateRootDir: string, channelId: string, anchorMessageId: string): string;
17
24
  export declare function resolveSharedProtocolStatusPath(stateRootDir: string, channelId: string, anchorMessageId: string): string;
18
25
  export declare function resolveConnectionsStatePath(stateRootDir: string): string;
19
26
  export declare function resolveAttentionStatePath(stateRootDir: string, channelId: string): string;
27
+ export declare function resolveCompactionStatePath(stateRootDir: string, channelId: string): string;
20
28
  export declare function resolveAuthorizationAuditPath(stateRootDir: string): string;
21
29
  export declare function resolvePreviousAuthorizationAuditPath(stateRootDir: string): string;
22
30
  export declare function resolveClaudeSessionMapPath(stateRootDir: string, agentId: 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);
@@ -110,9 +129,6 @@ export function resolveManagedBootstrapLockPath(rootPath) {
110
129
  export function resolveLocalConfigAgentStateRoot(rootPath, agentKey) {
111
130
  return join(resolveManagedStateRoot(rootPath), `${sanitizeStateRootLabel(agentKey)}-${hashStateRootKey(agentKey)}`);
112
131
  }
113
- export function resolveAgentCursorPath(stateRootDir, agentId) {
114
- return join(stateRootDir, `bpp-cursor-${encodeSegment(agentId)}.json`);
115
- }
116
132
  export function resolveSharedProtocolKickoffDecisionPath(stateRootDir, channelId, anchorMessageId) {
117
133
  return join(dirname(resolve(stateRootDir)), 'protocol-kickoff-decisions', `${encodeSegment(channelId)}--${encodeSegment(anchorMessageId)}.json`);
118
134
  }
@@ -125,6 +141,9 @@ export function resolveConnectionsStatePath(stateRootDir) {
125
141
  export function resolveAttentionStatePath(stateRootDir, channelId) {
126
142
  return join(stateRootDir, 'attention-state', `${encodeSegment(channelId)}.json`);
127
143
  }
144
+ export function resolveCompactionStatePath(stateRootDir, channelId) {
145
+ return join(stateRootDir, 'compaction-state', `${encodeSegment(channelId)}.json`);
146
+ }
128
147
  export function resolveAuthorizationAuditPath(stateRootDir) {
129
148
  return join(stateRootDir, 'authorization-audit.jsonl');
130
149
  }
@@ -1,10 +1,11 @@
1
1
  import type { ChatControlPlane } from './chat/chat-control-plane.js';
2
2
  import type { Task } from './types.js';
3
+ type TaskThreadResolutionControlPlane = Pick<ChatControlPlane, 'getTask' | 'listChannels' | 'listTasks'>;
3
4
  interface WaitForTaskForThreadOptions {
4
5
  preferredTaskId?: string;
5
6
  attempts?: number;
6
7
  delayMs?: number;
7
8
  }
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>;
9
+ export declare function findTaskForThread(controlPlane: TaskThreadResolutionControlPlane, threadId: string, preferredTaskId?: string): Promise<Task | null>;
10
+ export declare function waitForTaskForThread(controlPlane: TaskThreadResolutionControlPlane, threadId: string, options?: WaitForTaskForThreadOptions): Promise<Task | null>;
10
11
  export {};