@borgee/agents-host 0.2.101 → 0.2.110

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +77 -5
  2. package/dist/agents-host.d.ts +8 -0
  3. package/dist/agents-host.js +355 -23
  4. package/dist/catalog-worker.js +21 -0
  5. package/dist/catalog-worker.js.map +7 -0
  6. package/dist/chat/chat-control-plane.d.ts +7 -2
  7. package/dist/chat/normalized-input.d.ts +9 -0
  8. package/dist/chat/normalized-input.js +24 -0
  9. package/dist/chat/sdk-chat-control-plane.d.ts +18 -3
  10. package/dist/chat/sdk-chat-control-plane.js +44 -9
  11. package/dist/cli-args.d.ts +1 -1
  12. package/dist/cli-args.js +13 -4
  13. package/dist/config.d.ts +3 -1
  14. package/dist/config.js +25 -0
  15. package/dist/context/injection.d.ts +16 -0
  16. package/dist/context/injection.js +37 -21
  17. package/dist/context/prompt.d.ts +12 -0
  18. package/dist/context/prompt.js +25 -0
  19. package/dist/context/turn-preparation.d.ts +2 -1
  20. package/dist/context/turn-preparation.js +15 -0
  21. package/dist/gateway/localhost-gateway.js +33 -8
  22. package/dist/hosted-turn-content.js +3 -2
  23. package/dist/local-config.js +34 -1
  24. package/dist/managed-daemon.js +15 -0
  25. package/dist/native-environment.d.ts +2 -0
  26. package/dist/native-environment.js +9 -0
  27. package/dist/native-protocol-types/codec.d.ts +49 -0
  28. package/dist/native-protocol-types/commands.d.ts +71 -0
  29. package/dist/native-protocol-types/compatibility.d.ts +5 -0
  30. package/dist/native-protocol-types/cursor.d.ts +7 -0
  31. package/dist/native-protocol-types/envelope.d.ts +1105 -0
  32. package/dist/native-protocol-types/file-changes.d.ts +20 -0
  33. package/dist/native-protocol-types/history.d.ts +487 -0
  34. package/dist/native-protocol-types/index.d.ts +18 -0
  35. package/dist/native-protocol-types/interactions.d.ts +619 -0
  36. package/dist/native-protocol-types/messages.d.ts +2482 -0
  37. package/dist/native-protocol-types/public-validation.d.ts +10 -0
  38. package/dist/native-protocol-types/remote-host-uplink.d.ts +67 -0
  39. package/dist/native-protocol-types/resources.d.ts +126 -0
  40. package/dist/native-protocol-types/session-settings.d.ts +22 -0
  41. package/dist/native-protocol-types/snapshot.d.ts +575 -0
  42. package/dist/native-protocol-types/timeline.d.ts +1503 -0
  43. package/dist/native-protocol-types/tool-result.d.ts +26 -0
  44. package/dist/native-protocol-types/uplink.d.ts +56 -0
  45. package/dist/native-protocol-types/version.d.ts +5 -0
  46. package/dist/native-provider-types/commands.d.ts +51 -0
  47. package/dist/native-provider-types/control.d.ts +192 -0
  48. package/dist/native-provider-types/file-changes.d.ts +9 -0
  49. package/dist/native-provider-types/index.d.ts +8 -0
  50. package/dist/native-provider-types/interactions.d.ts +7 -0
  51. package/dist/native-provider-types/observation.d.ts +209 -0
  52. package/dist/native-provider-types/provider.d.ts +124 -0
  53. package/dist/native-provider-types/session-settings.d.ts +17 -0
  54. package/dist/native-provider-types/testing.d.ts +27 -0
  55. package/dist/native-provider-types/tool-result.d.ts +21 -0
  56. package/dist/native-providers.d.ts +21 -0
  57. package/dist/native-providers.js +18402 -0
  58. package/dist/native-providers.js.map +7 -0
  59. package/dist/plugin-sdk.js +10640 -225
  60. package/dist/plugin-sdk.js.map +4 -4
  61. package/dist/policy/copilot-permission.js +1 -8
  62. package/dist/providers/copilot/sdk-session.js +8 -1
  63. package/dist/providers/create-provider.js +20 -0
  64. package/dist/providers/normalized/adapter.d.ts +107 -0
  65. package/dist/providers/normalized/adapter.js +1650 -0
  66. package/dist/providers/normalized/control-policy.d.ts +5 -0
  67. package/dist/providers/normalized/control-policy.js +12 -0
  68. package/dist/providers/normalized/interactions.d.ts +3 -0
  69. package/dist/providers/normalized/interactions.js +39 -0
  70. package/dist/providers/normalized/legacy-input-ownership.d.ts +34 -0
  71. package/dist/providers/normalized/legacy-input-ownership.js +123 -0
  72. package/dist/providers/normalized/session-management.d.ts +67 -0
  73. package/dist/providers/normalized/session-management.js +334 -0
  74. package/dist/providers/normalized/session-ownership.d.ts +3 -0
  75. package/dist/providers/normalized/session-ownership.js +90 -0
  76. package/dist/providers/provider-adapter.d.ts +43 -1
  77. package/dist/providers/provider-adapter.js +8 -0
  78. package/dist/types.d.ts +34 -9
  79. package/dist/vendor/agent-provider-codex/LICENSE +211 -0
  80. package/dist/vendor/agent-provider-codex/NOTICE +13 -0
  81. package/package.json +22 -13
  82. package/skills/borgee-agent/SKILL.md +1 -1
  83. package/skills/borgee-agent/scripts/borgee-agent.mjs +24 -3
  84. package/skills/borgee-agent/scripts/borgee-agent.py +28 -2
@@ -0,0 +1,9 @@
1
+ import type { ChannelMessageEvent } from '../types.js';
2
+ import type { ChatControlPlane } from './chat-control-plane.js';
3
+ /** Plugin bodies may include server-only instructions; only the durable row is public input. */
4
+ export declare function readNormalizedInputText(controlPlane: Pick<ChatControlPlane, 'readChannelHistory'>, message: ChannelMessageEvent, timeoutMs?: number): Promise<string | null>;
5
+ export declare function readDurableInputText(controlPlane: Pick<ChatControlPlane, 'readChannelHistory'>, inputRef: {
6
+ channelId: string;
7
+ inputMessageId?: string;
8
+ createdAt?: number;
9
+ }, timeoutMs?: number): Promise<string | null>;
@@ -0,0 +1,24 @@
1
+ /** Plugin bodies may include server-only instructions; only the durable row is public input. */
2
+ export async function readNormalizedInputText(controlPlane, message, timeoutMs = 2000) {
3
+ return readDurableInputText(controlPlane, { channelId: message.channel_id, inputMessageId: message.message_id, createdAt: message.created_at }, timeoutMs);
4
+ }
5
+ export async function readDurableInputText(controlPlane, inputRef, timeoutMs = 2000) {
6
+ if (!inputRef.inputMessageId)
7
+ return null;
8
+ let timer;
9
+ try {
10
+ const history = await Promise.race([
11
+ controlPlane.readChannelHistory({ channelId: inputRef.channelId, before: inputRef.createdAt == null ? undefined : inputRef.createdAt + 1, limit: 200 }),
12
+ new Promise(resolve => { timer = setTimeout(() => resolve(null), timeoutMs); }),
13
+ ]);
14
+ const input = history?.find(entry => entry.id === inputRef.inputMessageId && entry.channelId === inputRef.channelId);
15
+ return typeof input?.body === 'string' ? input.body : null;
16
+ }
17
+ catch {
18
+ return null;
19
+ }
20
+ finally {
21
+ if (timer)
22
+ clearTimeout(timer);
23
+ }
24
+ }
@@ -1,10 +1,20 @@
1
- import { type BorgeePluginClient, type BorgeePluginOptions, type ExecutionTelemetryFrame, type InboundMessageEvent, type ReportTaskFinishedInput, type ReportTurnActivityInput, type StopTurnHandler, type StopBackgroundRunHandler, type TurnActivityReporter } from '../plugin-sdk.js';
1
+ import { type BorgeePluginClient, type BorgeePluginOptions, type ExecutionTelemetryFrame, type InboundMessageEvent, type ReportTaskFinishedInput, type ReportTurnActivityInput, type StopTurnHandler, type ServerRequestHandler, type StopBackgroundRunHandler, type TurnActivityReporter } from '../plugin-sdk.js';
2
2
  import { type ExecutionTelemetryOutboxPolicy } from '../execution-telemetry.js';
3
3
  import type { ChannelSummary, ChannelFileEntry, ChannelFileContent, PublishChannelFileInput, ChannelHistoryEntry, ChannelMessageEvent, CreateTaskInput, DirectoryUser, MeResponseUser, PostMessageInput, PostedMessage, ReadChannelHistoryInput, Task, UpdateTaskInput } from '../types.js';
4
4
  import type { ChatControlPlane } from './chat-control-plane.js';
5
- type PluginClientLike = Pick<BorgeePluginClient, 'agentId' | 'close' | 'connect' | 'createTask' | 'deleteMessage' | 'editMessage' | 'getMe' | 'getTask' | 'listChannelFiles' | 'readChannelFile' | 'publishChannelFile' | 'listChannels' | 'listTasks' | 'listUsers' | 'on' | 'onStopTurn' | 'onStopBackgroundRun' | 'reportActivity' | 'readHistory' | 'reportTurnActivity' | 'reportTyping' | 'sendMessage' | 'updateTask' | 'setTaskProperty' | 'deleteTaskProperty'> & Partial<Pick<BorgeePluginClient, 'connectionState' | 'reportExecutionTelemetry' | 'reportTaskFinished'>>;
5
+ type PluginClientLike = Pick<BorgeePluginClient, 'agentId' | 'close' | 'connect' | 'createTask' | 'deleteMessage' | 'editMessage' | 'getMe' | 'getTask' | 'listChannelFiles' | 'readChannelFile' | 'publishChannelFile' | 'listChannels' | 'listTasks' | 'listUsers' | 'on' | 'onStopTurn' | 'onStopBackgroundRun' | 'reportActivity' | 'readHistory' | 'reportTurnActivity' | 'reportTyping' | 'sendMessage' | 'updateTask' | 'setTaskProperty' | 'deleteTaskProperty'> & Partial<Pick<BorgeePluginClient, 'connectionState' | 'reportExecutionTelemetry' | 'reportTaskFinished' | 'onServerRequest' | 'sessionBindingsSupported'>>;
6
6
  type PluginClientFactory = (options: BorgeePluginOptions) => PluginClientLike;
7
7
  type SdkChatControlPlaneOptions = Pick<BorgeePluginOptions, 'pluginId'> & {
8
+ providerOutputMode?: () => 'normalized' | 'legacy';
9
+ normalizedActivitySync?: () => boolean;
10
+ sessionControl?: () => boolean;
11
+ sessionBindings?: () => boolean;
12
+ sessionDirectory?: () => boolean;
13
+ sessionManagement?: () => boolean;
14
+ sessionRelations?: () => boolean;
15
+ sessionObservation?: () => boolean;
16
+ connectionLiveness?: () => BorgeePluginOptions['connectionLiveness'];
17
+ sessionInputOperations?: () => readonly ('start_turn' | 'steer' | 'next_turn')[];
8
18
  executionTelemetryCapacityWaitMs?: number;
9
19
  executionTelemetryOutboxPath?: string;
10
20
  executionTelemetryOutboxPolicy?: Partial<ExecutionTelemetryOutboxPolicy>;
@@ -30,12 +40,17 @@ export declare class SdkChatControlPlane implements ChatControlPlane {
30
40
  private readonly executionTelemetryCapacityWaitMs;
31
41
  private me;
32
42
  constructor(baseUrl: string, apiKey: string, createClient?: PluginClientFactory, options?: SdkChatControlPlaneOptions);
43
+ reportExecutionTelemetryFrame(frame: ExecutionTelemetryFrame): void;
44
+ onExecutionTelemetryAck(handler: (ack: import('../plugin-sdk.js').ExecutionTelemetryAck) => void): () => void;
45
+ onExecutionTransportReady(handler: () => void): () => void;
46
+ onServerRequest(handler: ServerRequestHandler | undefined): void;
33
47
  onStopTurn(handler: StopTurnHandler | undefined): void;
34
48
  onStopBackgroundRun(handler: StopBackgroundRunHandler | undefined): void;
35
49
  onActivityTransportReady(handler: (() => void) | undefined): void;
36
50
  connect(onMessage: (message: ChannelMessageEvent) => void): Promise<void>;
37
51
  close(): Promise<void>;
38
- postMessage(input: PostMessageInput): Promise<PostedMessage>;
52
+ postMessage(input: PostMessageInput, source?: Pick<import('../plugin-sdk.js').ServerRequestContext, 'isConnectionCurrent'>): Promise<PostedMessage>;
53
+ get sessionBindingsSupported(): boolean;
39
54
  reportActivity(input: Parameters<BorgeePluginClient['reportActivity']>[0]): void;
40
55
  reportTaskFinished(input: ReportTaskFinishedInput): void;
41
56
  editMessage(messageId: string, content: string): Promise<void>;
@@ -21,11 +21,29 @@ export class SdkChatControlPlane {
21
21
  executionTelemetryCapacityWaitMs;
22
22
  me = null;
23
23
  constructor(baseUrl, apiKey, createClient = createBorgeePlugin, options = {}) {
24
- this.client = createClient({
25
- baseUrl,
26
- apiKey,
27
- ...(options.pluginId ? { pluginId: options.pluginId } : {}),
28
- });
24
+ const clientOptions = { baseUrl, apiKey, pluginId: options.pluginId };
25
+ // The adapter is selected after its control plane; read its policy at connect.
26
+ if (options.providerOutputMode)
27
+ Object.defineProperty(clientOptions, 'providerOutputMode', { enumerable: true, get: options.providerOutputMode });
28
+ if (options.normalizedActivitySync)
29
+ Object.defineProperty(clientOptions, 'normalizedActivitySync', { enumerable: true, get: options.normalizedActivitySync });
30
+ if (options.sessionControl)
31
+ Object.defineProperty(clientOptions, 'sessionControl', { enumerable: true, get: options.sessionControl });
32
+ if (options.sessionBindings)
33
+ Object.defineProperty(clientOptions, 'sessionBindings', { enumerable: true, get: options.sessionBindings });
34
+ if (options.sessionManagement)
35
+ Object.defineProperty(clientOptions, 'sessionManagement', { enumerable: true, get: options.sessionManagement });
36
+ if (options.sessionDirectory)
37
+ Object.defineProperty(clientOptions, 'sessionDirectory', { enumerable: true, get: options.sessionDirectory });
38
+ if (options.sessionRelations)
39
+ Object.defineProperty(clientOptions, 'sessionRelations', { enumerable: true, get: options.sessionRelations });
40
+ if (options.sessionObservation)
41
+ Object.defineProperty(clientOptions, 'sessionObservation', { enumerable: true, get: options.sessionObservation });
42
+ if (options.connectionLiveness)
43
+ Object.defineProperty(clientOptions, 'connectionLiveness', { enumerable: true, get: options.connectionLiveness });
44
+ if (options.sessionInputOperations)
45
+ clientOptions.sessionInputOperations = options.sessionInputOperations;
46
+ this.client = createClient(clientOptions);
29
47
  this.onExecutionTelemetryError =
30
48
  options.onExecutionTelemetryError ??
31
49
  (() => console.error('[agents-host] execution telemetry persistence failed'));
@@ -38,6 +56,19 @@ export class SdkChatControlPlane {
38
56
  policy: options.executionTelemetryOutboxPolicy,
39
57
  });
40
58
  }
59
+ reportExecutionTelemetryFrame(frame) {
60
+ this.client.reportExecutionTelemetry?.(frame);
61
+ }
62
+ onExecutionTelemetryAck(handler) {
63
+ return this.client.on('executionTelemetryAck', handler);
64
+ }
65
+ onExecutionTransportReady(handler) {
66
+ return this.client.on('connectionState', state => { if (state.status === 'online')
67
+ handler(); });
68
+ }
69
+ onServerRequest(handler) {
70
+ this.client.onServerRequest?.(handler);
71
+ }
41
72
  onStopTurn(handler) {
42
73
  this.client.onStopTurn(handler);
43
74
  }
@@ -117,14 +148,16 @@ export class SdkChatControlPlane {
117
148
  await this.executionTelemetryOutbox.settle();
118
149
  await this.client.close();
119
150
  }
120
- async postMessage(input) {
121
- const sent = await this.client.sendMessage({
151
+ async postMessage(input, source) {
152
+ const message = {
122
153
  channelId: input.channelId,
123
154
  body: input.body,
124
155
  replyToId: input.replyToId,
125
- });
156
+ };
157
+ const sent = source ? await this.client.sendMessage(message, source) : await this.client.sendMessage(message);
126
158
  return { messageId: sent.messageId };
127
159
  }
160
+ get sessionBindingsSupported() { return this.client.sessionBindingsSupported ?? false; }
128
161
  reportActivity(input) {
129
162
  this.client.reportActivity(input);
130
163
  }
@@ -294,6 +327,9 @@ export class SdkChatControlPlane {
294
327
  export function mapInboundToChannelMessage(event) {
295
328
  return {
296
329
  type: event.kind,
330
+ ...(event.executionGrant ? { execution_grant: { ...event.executionGrant } } : {}),
331
+ ...(event.sessionTarget ? { sessionTarget: { ...event.sessionTarget } } : {}),
332
+ ...(event.sourceCurrent ? { sourceCurrent: event.sourceCurrent } : {}),
297
333
  ...(event.message?.type != null ? { message_type: event.message.type } : {}),
298
334
  channel_id: event.channelId,
299
335
  channel_type: event.channelType,
@@ -305,6 +341,5 @@ export function mapInboundToChannelMessage(event) {
305
341
  content_type: event.message?.contentType,
306
342
  attachments: event.message?.attachments?.map(normalizeHostedMessageAttachment),
307
343
  created_at: event.createdAt,
308
- ...(event.executionGrant ? { execution_grant: event.executionGrant } : {}),
309
344
  };
310
345
  }
@@ -85,4 +85,4 @@ export declare function parseCleanupManagedArgs(argv: string[]): ParsedCleanupMa
85
85
  export declare function parseApplyManagedArgs(argv: string[]): ParsedApplyManagedArgs;
86
86
  /** `update` always installs the latest published release; it takes no options. */
87
87
  export declare function assertNoUpdateArgs(argv: string[]): void;
88
- export declare const USAGE = "Usage:\n agents-host start <serverUrl> <apiKey> [options]\n agents-host start-managed <serverUrl>\n agents-host start-managed <serverUrl> <apiKey> [agent-options]\n agents-host describe-managed <serverUrl>\n agents-host cleanup-managed <serverUrl> [--purge]\n agents-host apply-managed <serverUrl> --stdin\n agents-host apply-managed <serverUrl> --spec-json <json>\n agents-host log <serverUrl> [--lines <n>] [--follow]\n agents-host log <serverUrl> --path\n agents-host start --config <path-to-host-config> [--debug]\n agents-host validate --config <path-to-host-config>\n agents-host describe --config <path-to-host-config>\n agents-host print-layout --root <dir>\n agents-host generate-config --root <dir> --stdin\n agents-host generate-config --root <dir> --spec-json <json>\n agents-host update\n\nForeground start options:\n --debug Enable host/provider debug logs (or set AGENTS_HOST_DEBUG=1)\n\nSingle-agent agent options:\n --name <name> Display name (default: Assistant)\n --provider <claude|codex|copilot>\n Runtime provider (default: claude)\n --claude-command <cmd> Local Claude ACP adapter command (default: claude-agent-acp)\n --claude-args <args> Local Claude ACP adapter args (empty on the shipped bundled path)\n --codex-command <cmd> Local Codex ACP adapter command (default: codex-acp)\n --codex-args <args> Local Codex ACP adapter args\n --copilot-command <cmd> Local Copilot CLI command (default: copilot)\n --copilot-args <args> Ignored by the Copilot ACP prototype\n --copilot-session-ttl-minutes <minutes>\n Idle session TTL for Copilot ACP sessions (default: 2880)\n --provider-idle-shutdown-minutes <minutes>\n Shut the shared ACP adapter process down after this much idle\n time and re-spawn it on the next turn; 0 keeps it resident\n (default: 10)\n\nCommand summary:\n start <serverUrl> <apiKey> Start one foreground hosted agent (legacy-compatible behavior)\n start --config <path> Start agents + supervisor + watchers from a host config file\n start-managed <serverUrl> Restart or resume an existing per-serverUrl managed daemon\n start-managed <serverUrl> <apiKey>\n Upsert one agent into the per-serverUrl local daemon and exit after reconcile\n describe-managed <serverUrl> Print managed-runtime spec JSON for machine callers\n cleanup-managed <serverUrl> Stop one managed-runtime daemon locally; --purge also removes its runtime root\n apply-managed <serverUrl> Apply one managed-runtime full-set spec for machine callers\n log <serverUrl> Print the managed daemon log tail for one server runtime\n validate --config <path> Validate local-config files without starting agents or watchers\n describe --config <path> Print the current managed full-set spec as JSON\n print-layout --root <dir> Print the canonical default local-config layout as JSON\n generate-config --root <dir> --stdin Materialize canonical local-config files from stdin\n generate-config --root <dir> --spec-json <json>\n Compatibility input; JSON is exposed in process arguments\n update Update the globally installed @borgee/agents-host to the latest release\n\nStartup update check:\n Startup commands print an advisory notice on stderr when a newer release is published.\n Set AGENTS_HOST_DISABLE_UPDATE_CHECK=1 to turn that check off.\n\nExamples:\n agents-host start https://borgee.example.com bgr_xxxxxxxx --provider copilot --debug\n agents-host start-managed https://borgee.example.com\n agents-host start-managed https://borgee.example.com bgr_xxxxxxxx --provider copilot\n agents-host describe-managed https://borgee.example.com\n agents-host cleanup-managed https://borgee.example.com --purge\n printf '%s' '{\"host\":{\"borgeeBaseUrl\":\"https://borgee.example.com\"},\"agents\":[{\"key\":\"cp1\",\"name\":\"Copilot\",\"apiKey\":\"bgr_xxx\",\"provider\":\"copilot\"}]}' | agents-host apply-managed https://borgee.example.com --stdin\n agents-host log https://borgee.example.com --lines 200 --follow\n agents-host log https://borgee.example.com --path\n agents-host start --config ./agents-host.yaml --debug\n agents-host validate --config ./agents-host.yaml\n agents-host describe --config ./agents-host.yaml\n agents-host print-layout --root ./runtime-root\n printf '%s' '{\"host\":{\"borgeeBaseUrl\":\"https://borgee.example.com\"},\"agents\":[{\"key\":\"cp1\",\"name\":\"Copilot\",\"apiKey\":\"bgr_xxx\",\"provider\":\"copilot\"}]}' | agents-host generate-config --root ./runtime-root --stdin\n agents-host update\n";
88
+ export declare const USAGE = "Usage:\n agents-host start <serverUrl> <apiKey> [options]\n agents-host start-managed <serverUrl>\n agents-host start-managed <serverUrl> <apiKey> [agent-options]\n agents-host describe-managed <serverUrl>\n agents-host cleanup-managed <serverUrl> [--purge]\n agents-host apply-managed <serverUrl> --stdin\n agents-host apply-managed <serverUrl> --spec-json <json>\n agents-host log <serverUrl> [--lines <n>] [--follow]\n agents-host log <serverUrl> --path\n agents-host start --config <path-to-host-config> [--debug]\n agents-host validate --config <path-to-host-config>\n agents-host describe --config <path-to-host-config>\n agents-host print-layout --root <dir>\n agents-host generate-config --root <dir> --stdin\n agents-host generate-config --root <dir> --spec-json <json>\n agents-host update\n\nForeground start options:\n --debug Enable host/provider debug logs (or set AGENTS_HOST_DEBUG=1)\n\nSingle-agent agent options:\n --name <name> Display name (default: Assistant)\n --provider <claude|codex|copilot>\n Runtime provider (default: claude)\n --provider-output-mode <legacy|normalized>\n Output adapter (default: legacy; overrides PROVIDER_OUTPUT_MODE)\n --native-provider-command <path>\n Native executable for normalized output (overrides NATIVE_PROVIDER_COMMAND)\n --codex-connection-mode <shared|stdio>\n Normalized Codex transport (default: stdio; overrides CODEX_CONNECTION_MODE)\n --claude-command <cmd> Local Claude ACP adapter command (default: claude-agent-acp)\n --claude-args <args> Local Claude ACP adapter args (empty on the shipped bundled path)\n --codex-command <cmd> Local Codex ACP adapter command (default: codex-acp)\n --codex-args <args> Local Codex ACP adapter args\n --copilot-command <cmd> Legacy Copilot CLI command (default: copilot)\n --copilot-args <args> Parsed but ignored by the legacy Copilot SDK adapter\n --copilot-session-ttl-minutes <minutes>\n Idle session TTL for legacy Copilot SDK sessions (default: 2880)\n --provider-idle-shutdown-minutes <minutes>\n Shut the legacy provider process down after this much idle\n time and re-spawn it on the next turn; 0 keeps it resident\n (default: 10)\n\nCommand summary:\n start <serverUrl> <apiKey> Start one foreground hosted agent (legacy-compatible behavior)\n start --config <path> Start agents + supervisor + watchers from a host config file\n start-managed <serverUrl> Restart or resume an existing per-serverUrl managed daemon\n start-managed <serverUrl> <apiKey>\n Upsert one agent into the per-serverUrl local daemon and exit after reconcile\n describe-managed <serverUrl> Print managed-runtime spec JSON for machine callers\n cleanup-managed <serverUrl> Stop one managed-runtime daemon locally; --purge also removes its runtime root\n apply-managed <serverUrl> Apply one managed-runtime full-set spec for machine callers\n log <serverUrl> Print the managed daemon log tail for one server runtime\n validate --config <path> Validate local-config files without starting agents or watchers\n describe --config <path> Print the current managed full-set spec as JSON\n print-layout --root <dir> Print the canonical default local-config layout as JSON\n generate-config --root <dir> --stdin Materialize canonical local-config files from stdin\n generate-config --root <dir> --spec-json <json>\n Compatibility input; JSON is exposed in process arguments\n update Update the globally installed @borgee/agents-host to the latest release\n\nStartup update check:\n Startup commands print an advisory notice on stderr when a newer release is published.\n Set AGENTS_HOST_DISABLE_UPDATE_CHECK=1 to turn that check off.\n\nExamples:\n agents-host start https://borgee.example.com bgr_xxxxxxxx --provider copilot --debug\n agents-host start-managed https://borgee.example.com\n agents-host start-managed https://borgee.example.com bgr_xxxxxxxx --provider copilot\n agents-host describe-managed https://borgee.example.com\n agents-host cleanup-managed https://borgee.example.com --purge\n printf '%s' '{\"host\":{\"borgeeBaseUrl\":\"https://borgee.example.com\"},\"agents\":[{\"key\":\"cp1\",\"name\":\"Copilot\",\"apiKey\":\"bgr_xxx\",\"provider\":\"copilot\"}]}' | agents-host apply-managed https://borgee.example.com --stdin\n agents-host log https://borgee.example.com --lines 200 --follow\n agents-host log https://borgee.example.com --path\n agents-host start --config ./agents-host.yaml --debug\n agents-host validate --config ./agents-host.yaml\n agents-host describe --config ./agents-host.yaml\n agents-host print-layout --root ./runtime-root\n printf '%s' '{\"host\":{\"borgeeBaseUrl\":\"https://borgee.example.com\"},\"agents\":[{\"key\":\"cp1\",\"name\":\"Copilot\",\"apiKey\":\"bgr_xxx\",\"provider\":\"copilot\"}]}' | agents-host generate-config --root ./runtime-root --stdin\n agents-host update\n";
package/dist/cli-args.js CHANGED
@@ -8,6 +8,9 @@
8
8
  export const CLI_FLAG_TO_ENV = {
9
9
  name: 'BORGEE_AGENT_NAME',
10
10
  provider: 'RUNTIME_PROVIDER',
11
+ 'provider-output-mode': 'PROVIDER_OUTPUT_MODE',
12
+ 'native-provider-command': 'NATIVE_PROVIDER_COMMAND',
13
+ 'codex-connection-mode': 'CODEX_CONNECTION_MODE',
11
14
  'claude-command': 'CLAUDE_COMMAND',
12
15
  'claude-args': 'CLAUDE_ARGS',
13
16
  'codex-command': 'CODEX_COMMAND',
@@ -505,16 +508,22 @@ Single-agent agent options:
505
508
  --name <name> Display name (default: Assistant)
506
509
  --provider <claude|codex|copilot>
507
510
  Runtime provider (default: claude)
511
+ --provider-output-mode <legacy|normalized>
512
+ Output adapter (default: legacy; overrides PROVIDER_OUTPUT_MODE)
513
+ --native-provider-command <path>
514
+ Native executable for normalized output (overrides NATIVE_PROVIDER_COMMAND)
515
+ --codex-connection-mode <shared|stdio>
516
+ Normalized Codex transport (default: stdio; overrides CODEX_CONNECTION_MODE)
508
517
  --claude-command <cmd> Local Claude ACP adapter command (default: claude-agent-acp)
509
518
  --claude-args <args> Local Claude ACP adapter args (empty on the shipped bundled path)
510
519
  --codex-command <cmd> Local Codex ACP adapter command (default: codex-acp)
511
520
  --codex-args <args> Local Codex ACP adapter args
512
- --copilot-command <cmd> Local Copilot CLI command (default: copilot)
513
- --copilot-args <args> Ignored by the Copilot ACP prototype
521
+ --copilot-command <cmd> Legacy Copilot CLI command (default: copilot)
522
+ --copilot-args <args> Parsed but ignored by the legacy Copilot SDK adapter
514
523
  --copilot-session-ttl-minutes <minutes>
515
- Idle session TTL for Copilot ACP sessions (default: 2880)
524
+ Idle session TTL for legacy Copilot SDK sessions (default: 2880)
516
525
  --provider-idle-shutdown-minutes <minutes>
517
- Shut the shared ACP adapter process down after this much idle
526
+ Shut the legacy provider process down after this much idle
518
527
  time and re-spawn it on the next turn; 0 keeps it resident
519
528
  (default: 10)
520
529
 
package/dist/config.d.ts CHANGED
@@ -21,6 +21,8 @@ export declare function isLegacyClaudeCompatibilityAlias(command: string, args:
21
21
  export declare function isLegacyClaudeBinaryCommand(command: string): boolean;
22
22
  export declare function hasLegacyClaudeOneShotArgs(args: string[]): boolean;
23
23
  export declare function assertClaudeCommandCompatibility(command: string, args: string[], sourceLabel: string): void;
24
- export declare function assertProviderCommandCompatibility(provider: ProviderKind, config: Pick<ProviderCommandConfig, 'claudeCommand' | 'claudeArgs'>, sourceLabel: string): void;
24
+ export declare function assertProviderCommandCompatibility(provider: ProviderKind, config: Pick<ProviderCommandConfig, 'claudeCommand' | 'claudeArgs'> & Partial<Pick<ProviderCommandConfig, 'providerOutputMode' | 'codexConnectionMode' | 'codexSharedSocketPath'>>, sourceLabel: string): void;
25
+ export declare function parseCodexConnectionMode(value: unknown): 'shared' | 'stdio';
26
+ export declare function parseProviderOutputMode(value: unknown): 'legacy' | 'normalized';
25
27
  export declare function resolveProviderCommandConfig(overrides?: Partial<ProviderCommandConfig>): ProviderCommandConfig;
26
28
  export declare function loadConfigFromEnv(env?: NodeJS.ProcessEnv): AgentsHostConfig;
package/dist/config.js CHANGED
@@ -137,9 +137,28 @@ export function assertProviderCommandCompatibility(provider, config, sourceLabel
137
137
  if (provider === 'claude') {
138
138
  assertClaudeCommandCompatibility(config.claudeCommand, config.claudeArgs, sourceLabel);
139
139
  }
140
+ if (provider === 'codex' && config.providerOutputMode === 'normalized' && (config.codexConnectionMode ?? 'stdio') === 'stdio' && config.codexSharedSocketPath !== undefined) {
141
+ throw new Error(`${sourceLabel}: a Codex shared socket cannot be configured in stdio mode. Set codexConnectionMode: shared (CODEX_CONNECTION_MODE=shared) explicitly to use a shared daemon.`);
142
+ }
143
+ }
144
+ export function parseCodexConnectionMode(value) {
145
+ if (value !== 'shared' && value !== 'stdio')
146
+ throw new Error('codexConnectionMode must be shared or stdio.');
147
+ return value;
148
+ }
149
+ export function parseProviderOutputMode(value) {
150
+ if (value !== 'legacy' && value !== 'normalized')
151
+ throw new Error('providerOutputMode must be legacy or normalized.');
152
+ return value;
140
153
  }
141
154
  export function resolveProviderCommandConfig(overrides = {}) {
142
155
  return {
156
+ ...(overrides.providerOutputMode !== undefined ? { providerOutputMode: parseProviderOutputMode(overrides.providerOutputMode) } : {}),
157
+ ...(overrides.sessionWorkspaceRoot !== undefined ? { sessionWorkspaceRoot: overrides.sessionWorkspaceRoot } : {}),
158
+ ...(overrides.nativeProviderCommand !== undefined ? { nativeProviderCommand: overrides.nativeProviderCommand } : {}),
159
+ ...(overrides.codexConnectionMode !== undefined ? { codexConnectionMode: parseCodexConnectionMode(overrides.codexConnectionMode) } : {}),
160
+ ...(overrides.codexSharedSocketPath !== undefined ? { codexSharedSocketPath: overrides.codexSharedSocketPath } : {}),
161
+ ...(overrides.codexTrustShared !== undefined ? { codexTrustShared: parseBooleanValue(overrides.codexTrustShared, 'codexTrustShared', 'provider configuration') } : {}),
143
162
  claudeCommand: overrides.claudeCommand ?? DEFAULT_PROVIDER_COMMAND_CONFIG.claudeCommand,
144
163
  claudeArgs: [...(overrides.claudeArgs ?? DEFAULT_PROVIDER_COMMAND_CONFIG.claudeArgs)],
145
164
  codexCommand: overrides.codexCommand ?? DEFAULT_PROVIDER_COMMAND_CONFIG.codexCommand,
@@ -158,8 +177,14 @@ export function loadConfigFromEnv(env = process.env) {
158
177
  assertProviderCompatibility(provider, 'Unsupported RUNTIME_PROVIDER', env);
159
178
  const agentApiKey = requireEnv('BORGEE_AGENT_API_KEY', env);
160
179
  const providerConfig = resolveProviderCommandConfig({
180
+ ...(env.PROVIDER_OUTPUT_MODE ? { providerOutputMode: parseProviderOutputMode(env.PROVIDER_OUTPUT_MODE) } : {}),
181
+ ...(env.SESSION_WORKSPACE_ROOT ? { sessionWorkspaceRoot: requireNonEmptyString(env.SESSION_WORKSPACE_ROOT, 'SESSION_WORKSPACE_ROOT') } : {}),
182
+ ...(env.NATIVE_PROVIDER_COMMAND ? { nativeProviderCommand: env.NATIVE_PROVIDER_COMMAND } : {}),
161
183
  claudeCommand: envOr('CLAUDE_COMMAND', DEFAULT_PROVIDER_COMMAND_CONFIG.claudeCommand, env),
162
184
  claudeArgs: parseArgs(envOr('CLAUDE_ARGS', DEFAULT_PROVIDER_COMMAND_CONFIG.claudeArgs.join(' '), env)),
185
+ ...(env.CODEX_CONNECTION_MODE !== undefined ? { codexConnectionMode: parseCodexConnectionMode(env.CODEX_CONNECTION_MODE) } : {}),
186
+ ...(env.CODEX_SHARED_SOCKET_PATH ? { codexSharedSocketPath: requireNonEmptyString(env.CODEX_SHARED_SOCKET_PATH, 'CODEX_SHARED_SOCKET_PATH') } : {}),
187
+ ...(env.CODEX_TRUST_SHARED !== undefined ? { codexTrustShared: parseBooleanValue(env.CODEX_TRUST_SHARED, 'CODEX_TRUST_SHARED', 'environment') } : {}),
163
188
  codexCommand: envOr('CODEX_COMMAND', DEFAULT_PROVIDER_COMMAND_CONFIG.codexCommand, env),
164
189
  codexArgs: parseArgs(envOr('CODEX_ARGS', DEFAULT_PROVIDER_COMMAND_CONFIG.codexArgs.join(' '), env)),
165
190
  copilotCommand: envOr('COPILOT_COMMAND', DEFAULT_PROVIDER_COMMAND_CONFIG.copilotCommand, env),
@@ -57,6 +57,10 @@ export interface ChannelContextStore {
57
57
  incomingContent?: string;
58
58
  taskAssignmentContextOverride?: TaskAssignmentThreadContext;
59
59
  taskAssignmentContextOverridePersistence?: 'persist' | 'ephemeral';
60
+ independentSessionWorkspace?: {
61
+ bindingId: string;
62
+ cwd: string;
63
+ };
60
64
  }): Promise<PreparedChannelContext>;
61
65
  prepare(channelId: string, options?: {
62
66
  provider?: ProviderKind;
@@ -72,6 +76,10 @@ export interface ChannelContextStore {
72
76
  incomingContent?: string;
73
77
  taskAssignmentContextOverride?: TaskAssignmentThreadContext;
74
78
  taskAssignmentContextOverridePersistence?: 'persist' | 'ephemeral';
79
+ independentSessionWorkspace?: {
80
+ bindingId: string;
81
+ cwd: string;
82
+ };
75
83
  }): Promise<PreparedChannelContext>;
76
84
  }
77
85
  export interface SkillAssetResolver {
@@ -173,6 +181,10 @@ export declare class FileChannelContextStore implements ChannelContextStore {
173
181
  incomingContent?: string;
174
182
  taskAssignmentContextOverride?: TaskAssignmentThreadContext;
175
183
  taskAssignmentContextOverridePersistence?: 'persist' | 'ephemeral';
184
+ independentSessionWorkspace?: {
185
+ bindingId: string;
186
+ cwd: string;
187
+ };
176
188
  } | string, options?: {
177
189
  provider?: ProviderKind;
178
190
  projectionStrategy?: ProjectionStrategy;
@@ -187,6 +199,10 @@ export declare class FileChannelContextStore implements ChannelContextStore {
187
199
  incomingContent?: string;
188
200
  taskAssignmentContextOverride?: TaskAssignmentThreadContext;
189
201
  taskAssignmentContextOverridePersistence?: 'persist' | 'ephemeral';
202
+ independentSessionWorkspace?: {
203
+ bindingId: string;
204
+ cwd: string;
205
+ };
190
206
  }): Promise<PreparedChannelContext>;
191
207
  private resolveSkillRuntimeBestEffort;
192
208
  }
@@ -216,9 +216,14 @@ function sanitizeTaskAssignmentContext(value) {
216
216
  const currentTaskId = typeof value.currentTaskId === 'string'
217
217
  ? value.currentTaskId.trim()
218
218
  : '';
219
- return currentTaskId && isUsableTaskId(currentTaskId)
220
- ? { active: true, currentTaskId }
221
- : { active: true };
219
+ const taskChannelId = typeof value.taskChannelId === 'string'
220
+ ? value.taskChannelId.trim()
221
+ : '';
222
+ return {
223
+ active: true,
224
+ ...(currentTaskId && isUsableTaskId(currentTaskId) ? { currentTaskId } : {}),
225
+ ...(taskChannelId.length > 0 ? { taskChannelId } : {}),
226
+ };
222
227
  }
223
228
  export function extractTaskIdFromTaskAssignmentContent(incomingContent) {
224
229
  if (typeof incomingContent !== 'string') {
@@ -232,7 +237,11 @@ export function extractTaskIdFromTaskAssignmentContent(incomingContent) {
232
237
  function buildTaskAssignmentContextForTurn(options, existingContext) {
233
238
  if (options?.incomingMessageType === 'task_assignment') {
234
239
  const currentTaskId = extractTaskIdFromTaskAssignmentContent(options.incomingContent);
235
- return currentTaskId ? { active: true, currentTaskId } : { active: true };
240
+ return {
241
+ active: true,
242
+ ...(existingContext?.taskChannelId ? { taskChannelId: existingContext.taskChannelId } : {}),
243
+ ...(currentTaskId ? { currentTaskId } : {}),
244
+ };
236
245
  }
237
246
  return existingContext;
238
247
  }
@@ -352,7 +361,10 @@ async function resolveTaskThreadWorkingFolderContext(params) {
352
361
  };
353
362
  }
354
363
  return {
355
- taskAssignmentContext,
364
+ taskAssignmentContext: {
365
+ ...taskAssignmentContext,
366
+ taskChannelId: task.channelId,
367
+ },
356
368
  resolvedWorkingFolder: {
357
369
  authority: 'task-execution-target',
358
370
  taskId: task.id,
@@ -535,9 +547,11 @@ export class FileChannelContextStore {
535
547
  const collaborationCommandsEnabled = input.collaboration?.enabled === true && input.collaboration.sendRoutesAllowed === true;
536
548
  const collaborationRoutesAllowedForTurnMode = turnMode === 'ordinary';
537
549
  const auxiliaryCollaborationEnabled = collaborationCommandsEnabled && collaborationRoutesAllowedForTurnMode;
538
- const directoryPath = resolveChannelContextDirectory(this.stateRootDir, input.channelId);
539
- const payloadPath = resolveChannelContextPayloadPath(this.stateRootDir, input.channelId);
540
- const taskAssignmentStatePath = resolveTaskAssignmentStatePath(this.stateRootDir, input.channelId);
550
+ const contextRoot = input.independentSessionWorkspace
551
+ ? join(this.stateRootDir, 'session-context', encodeURIComponent(input.independentSessionWorkspace.bindingId)) : this.stateRootDir;
552
+ const directoryPath = resolveChannelContextDirectory(contextRoot, input.channelId);
553
+ const payloadPath = resolveChannelContextPayloadPath(contextRoot, input.channelId);
554
+ const taskAssignmentStatePath = resolveTaskAssignmentStatePath(contextRoot, input.channelId);
541
555
  const claudeProjectedBriefPath = resolveClaudeProjectedBriefPathFromPayloadPath(payloadPath);
542
556
  const gatewayCredentialPath = resolveGatewayCredentialPathFromPayloadPath(payloadPath);
543
557
  const existingTaskAssignmentContext = await readExistingTaskAssignmentContext(taskAssignmentStatePath, this.fileSystem);
@@ -571,16 +585,18 @@ export class FileChannelContextStore {
571
585
  incomingMessageType: input.incomingMessageType,
572
586
  incomingContent: input.incomingContent,
573
587
  }, existingTaskAssignmentContext);
574
- const workingFolderResolution = await resolveTaskThreadWorkingFolderContext({
575
- channelId: input.channelId,
576
- workingFolderCollectionRootDir: this.workingFolderCollectionRootDir,
577
- taskAssignmentContext,
578
- taskReader: this.taskReader,
579
- taskResolutionTimeoutMs: this.taskResolutionTimeoutMs,
580
- allowCrossAgentIndependentWorkspaceHandoff: this.allowCrossAgentIndependentWorkspaceHandoff,
581
- resolveStableAgentId: this.resolveStableAgentId,
582
- fileSystem: this.fileSystem,
583
- });
588
+ const workingFolderResolution = input.independentSessionWorkspace
589
+ ? { resolvedWorkingFolder: { authority: 'channel', owningChannelId: input.channelId, rootPath: input.independentSessionWorkspace.cwd }, taskAssignmentContext }
590
+ : await resolveTaskThreadWorkingFolderContext({
591
+ channelId: input.channelId,
592
+ workingFolderCollectionRootDir: this.workingFolderCollectionRootDir,
593
+ taskAssignmentContext,
594
+ taskReader: this.taskReader,
595
+ taskResolutionTimeoutMs: this.taskResolutionTimeoutMs,
596
+ allowCrossAgentIndependentWorkspaceHandoff: this.allowCrossAgentIndependentWorkspaceHandoff,
597
+ resolveStableAgentId: this.resolveStableAgentId,
598
+ fileSystem: this.fileSystem,
599
+ });
584
600
  const resolvedWorkingFolder = workingFolderResolution.resolvedWorkingFolder;
585
601
  const runtimeTaskAssignmentContext = workingFolderResolution.taskAssignmentContext ?? taskAssignmentContext;
586
602
  const persistedTaskAssignmentContext = input.taskAssignmentContextOverridePersistence === 'ephemeral'
@@ -625,8 +641,8 @@ export class FileChannelContextStore {
625
641
  }
626
642
  try {
627
643
  await this.fileSystem.mkdir(directoryPath, { recursive: true, mode: 0o700 });
628
- await this.fileSystem.mkdir(resolveTaskAssignmentStateDirectory(this.stateRootDir, input.channelId), { recursive: true, mode: 0o700 });
629
- if (resolvedWorkingFolder.authority !== 'task-execution-target') {
644
+ await this.fileSystem.mkdir(resolveTaskAssignmentStateDirectory(contextRoot, input.channelId), { recursive: true, mode: 0o700 });
645
+ if (!input.independentSessionWorkspace && resolvedWorkingFolder.authority !== 'task-execution-target') {
630
646
  await this.fileSystem.mkdir(resolvedWorkingFolder.rootPath, { recursive: true, mode: 0o700 });
631
647
  }
632
648
  await pruneGatewayCredentialSidecars(this.fileSystem, directoryPath, gatewayCredential ? CHANNEL_GATEWAY_CREDENTIAL_FILENAME : undefined);
@@ -636,7 +652,7 @@ export class FileChannelContextStore {
636
652
  await this.fileSystem.writeFile(payloadPath, `${JSON.stringify(payload, null, 2)}\n`, { encoding: 'utf8', mode: 0o600 });
637
653
  payloadWritten = true;
638
654
  if (persistedTaskAssignmentContext) {
639
- const stagingTaskAssignmentStatePath = join(resolveTaskAssignmentStateDirectory(this.stateRootDir, input.channelId), `.task-assignment-state.${randomUUID()}.json`);
655
+ const stagingTaskAssignmentStatePath = join(resolveTaskAssignmentStateDirectory(contextRoot, input.channelId), `.task-assignment-state.${randomUUID()}.json`);
640
656
  await this.fileSystem.writeFile(stagingTaskAssignmentStatePath, `${JSON.stringify({ taskAssignmentContext: persistedTaskAssignmentContext }, null, 2)}\n`, { encoding: 'utf8', mode: 0o600 });
641
657
  await this.fileSystem.rename(stagingTaskAssignmentStatePath, taskAssignmentStatePath);
642
658
  }
@@ -31,3 +31,15 @@ export declare function buildClaudeTurnPrompt(params: {
31
31
  promptContext?: PreparedPromptContext;
32
32
  promptStrategy?: ProjectionStrategy;
33
33
  }): string;
34
+ /** Native context keys are stable; Codex owns deduplication and conversation history. */
35
+ export declare function buildCodexTurnContext(params: {
36
+ agentName: string;
37
+ channelId: string;
38
+ incomingAuthorId: string;
39
+ incomingEventKind?: string;
40
+ incomingMessageType?: string;
41
+ promptContext?: PreparedPromptContext;
42
+ }): Record<string, {
43
+ kind: 'application' | 'untrusted';
44
+ value: string;
45
+ }>;
@@ -681,3 +681,28 @@ export function buildClaudeTurnPrompt(params) {
681
681
  }),
682
682
  ].join('\n');
683
683
  }
684
+ /** Native context keys are stable; Codex owns deduplication and conversation history. */
685
+ export function buildCodexTurnContext(params) {
686
+ const context = params.promptContext;
687
+ const provider = 'codex';
688
+ // Dynamic context can contain peer/user-authored names and summaries. Keep its original user trust level.
689
+ const blocks = {
690
+ borgee_identity: { kind: 'application', value: [
691
+ ...buildPromptHeaderLines({ agentName: params.agentName, provider }),
692
+ ...buildSessionDelegationPromptLines(provider, context),
693
+ buildProviderIdentityReminderLine(provider),
694
+ ...(context?.skillRuntime ? buildSkillManualLines(context.skillRuntime) : []),
695
+ ].join('\n') },
696
+ borgee_control: { kind: 'untrusted', value: buildTurnControlPromptLines(context).join('\n') },
697
+ borgee_metadata: { kind: 'untrusted', value: [
698
+ `Channel: ${params.channelId}`, `Incoming author: ${params.incomingAuthorId}`,
699
+ ...buildInboundMetadataLines({ ...params, provider }),
700
+ ].join('\n') },
701
+ borgee_outcome: { kind: 'untrusted', value: buildCollaborationOutcomeSummaryLines(context?.collaborationOutcome).join('\n') },
702
+ borgee_attention: { kind: 'untrusted', value: buildAttentionSummaryLines(context?.attentionSnapshot).join('\n') },
703
+ borgee_capabilities: { kind: 'untrusted', value: buildCollaborationCapabilityDeclarationSummaryLines(context?.collaborationCapabilities).join('\n') },
704
+ borgee_diagnostic: { kind: 'untrusted', value: buildMissedCollaborationDiagnosticSummaryLines(context?.missedCollaborationDiagnostic).join('\n') },
705
+ borgee_gateway: { kind: 'untrusted', value: buildLocalhostGatewayPromptLines(context).join('\n') },
706
+ };
707
+ return blocks;
708
+ }
@@ -1,5 +1,5 @@
1
1
  import { type DebugLogger } from '../debug.js';
2
- import type { ProjectionStrategy, PreparedProviderTurnInput, ProviderInput } from '../types.js';
2
+ import type { ProjectionStrategy, PreparedPromptContext, PreparedProviderTurnInput, ProviderInput } from '../types.js';
3
3
  import { type ChannelContextStore } from './injection.js';
4
4
  type ResolveProjectionStrategy = (input: ProviderInput) => Promise<ProjectionStrategy>;
5
5
  export declare class ProviderTurnPreparer {
@@ -10,6 +10,7 @@ export declare class ProviderTurnPreparer {
10
10
  projectionStrategy?: ProjectionStrategy;
11
11
  resolveProjectionStrategy?: ResolveProjectionStrategy;
12
12
  });
13
+ prepareSession(channelId: string, provider: ProviderInput['provider']): Promise<PreparedPromptContext | undefined>;
13
14
  prepare(input: ProviderInput): Promise<PreparedProviderTurnInput>;
14
15
  }
15
16
  export {};
@@ -89,6 +89,17 @@ export class ProviderTurnPreparer {
89
89
  this.resolveProjectionStrategy = options.resolveProjectionStrategy
90
90
  ?? (async () => options.projectionStrategy ?? DEFAULT_PROJECTION_STRATEGY);
91
91
  }
92
+ async prepareSession(channelId, provider) {
93
+ const context = await this.channelContextStore?.prepare({ channelId, provider });
94
+ if (!context)
95
+ return undefined;
96
+ return {
97
+ channelContextPayloadPath: context.payloadPath,
98
+ claudeProjectedBriefPath: context.claudeProjectedBriefPath,
99
+ gatewayCredentialPath: context.gatewayCredentialPath,
100
+ resolvedWorkingFolder: context.resolvedWorkingFolder,
101
+ };
102
+ }
92
103
  async prepare(input) {
93
104
  const incomingParts = input.incomingParts ?? buildHostedTurnContentParts({
94
105
  text: input.incomingContent,
@@ -100,10 +111,12 @@ export class ProviderTurnPreparer {
100
111
  ? await this.resolveProjectionStrategy(input)
101
112
  : undefined;
102
113
  let channelContext;
114
+ let contextPreparationFailed = false;
103
115
  if (this.channelContextStore) {
104
116
  try {
105
117
  const prepareInput = {
106
118
  channelId: input.channelId,
119
+ ...(input.independentSessionWorkspace ? { independentSessionWorkspace: input.independentSessionWorkspace } : {}),
107
120
  provider: input.provider,
108
121
  projectionStrategy,
109
122
  collaboration: input.collaboration
@@ -139,6 +152,7 @@ export class ProviderTurnPreparer {
139
152
  channelContext = await this.channelContextStore.prepare(prepareInput);
140
153
  }
141
154
  catch (error) {
155
+ contextPreparationFailed = true;
142
156
  if (error instanceof ChannelContextPreparationError) {
143
157
  channelContext = error.partialContext;
144
158
  }
@@ -154,6 +168,7 @@ export class ProviderTurnPreparer {
154
168
  channelId: input.channelId,
155
169
  incomingContent,
156
170
  incomingParts,
171
+ ...(contextPreparationFailed ? { contextPreparationFailed: true } : {}),
157
172
  incomingEventKind: input.incomingEventKind,
158
173
  incomingMessageType: input.incomingMessageType,
159
174
  prompt: input.provider === 'claude'