@borgee/agents-host 0.2.97 → 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.
- package/README.md +77 -5
- package/dist/agents-host.d.ts +16 -0
- package/dist/agents-host.js +608 -82
- package/dist/catalog-worker.js +21 -0
- package/dist/catalog-worker.js.map +7 -0
- package/dist/chat/chat-control-plane.d.ts +19 -4
- package/dist/chat/normalized-input.d.ts +9 -0
- package/dist/chat/normalized-input.js +24 -0
- package/dist/chat/sdk-chat-control-plane.d.ts +46 -6
- package/dist/chat/sdk-chat-control-plane.js +169 -6
- package/dist/cli-args.d.ts +1 -1
- package/dist/cli-args.js +13 -4
- package/dist/config.d.ts +3 -1
- package/dist/config.js +25 -0
- package/dist/context/injection.d.ts +16 -0
- package/dist/context/injection.js +37 -21
- package/dist/context/prompt.d.ts +12 -0
- package/dist/context/prompt.js +25 -0
- package/dist/context/skill-manual.js +1 -1
- package/dist/context/turn-preparation.d.ts +2 -1
- package/dist/context/turn-preparation.js +15 -0
- package/dist/execution-telemetry.d.ts +96 -0
- package/dist/execution-telemetry.js +583 -0
- package/dist/gateway/channel-file-workspace.d.ts +15 -0
- package/dist/gateway/channel-file-workspace.js +130 -0
- package/dist/gateway/localhost-gateway.js +167 -2
- package/dist/hosted-turn-content.js +3 -2
- package/dist/local-config.js +34 -1
- package/dist/managed-daemon.js +15 -0
- package/dist/native-environment.d.ts +2 -0
- package/dist/native-environment.js +9 -0
- package/dist/native-protocol-types/codec.d.ts +49 -0
- package/dist/native-protocol-types/commands.d.ts +71 -0
- package/dist/native-protocol-types/compatibility.d.ts +5 -0
- package/dist/native-protocol-types/cursor.d.ts +7 -0
- package/dist/native-protocol-types/envelope.d.ts +1105 -0
- package/dist/native-protocol-types/file-changes.d.ts +20 -0
- package/dist/native-protocol-types/history.d.ts +487 -0
- package/dist/native-protocol-types/index.d.ts +18 -0
- package/dist/native-protocol-types/interactions.d.ts +619 -0
- package/dist/native-protocol-types/messages.d.ts +2482 -0
- package/dist/native-protocol-types/public-validation.d.ts +10 -0
- package/dist/native-protocol-types/remote-host-uplink.d.ts +67 -0
- package/dist/native-protocol-types/resources.d.ts +126 -0
- package/dist/native-protocol-types/session-settings.d.ts +22 -0
- package/dist/native-protocol-types/snapshot.d.ts +575 -0
- package/dist/native-protocol-types/timeline.d.ts +1503 -0
- package/dist/native-protocol-types/tool-result.d.ts +26 -0
- package/dist/native-protocol-types/uplink.d.ts +56 -0
- package/dist/native-protocol-types/version.d.ts +5 -0
- package/dist/native-provider-types/commands.d.ts +51 -0
- package/dist/native-provider-types/control.d.ts +192 -0
- package/dist/native-provider-types/file-changes.d.ts +9 -0
- package/dist/native-provider-types/index.d.ts +8 -0
- package/dist/native-provider-types/interactions.d.ts +7 -0
- package/dist/native-provider-types/observation.d.ts +209 -0
- package/dist/native-provider-types/provider.d.ts +124 -0
- package/dist/native-provider-types/session-settings.d.ts +17 -0
- package/dist/native-provider-types/testing.d.ts +27 -0
- package/dist/native-provider-types/tool-result.d.ts +21 -0
- package/dist/native-providers.d.ts +21 -0
- package/dist/native-providers.js +18402 -0
- package/dist/native-providers.js.map +7 -0
- package/dist/plugin-sdk.js +10713 -229
- package/dist/plugin-sdk.js.map +4 -4
- package/dist/policy/copilot-permission.js +1 -8
- package/dist/policy/gateway-authorization.d.ts +1 -1
- package/dist/policy/gateway-authorization.js +22 -2
- package/dist/providers/claude/adapter.js +1 -0
- package/dist/providers/claude/cli-client.js +4 -0
- package/dist/providers/codex/adapter.js +1 -0
- package/dist/providers/codex/cli-client.js +5 -0
- package/dist/providers/copilot/adapter.js +4 -0
- package/dist/providers/copilot/cli-client.js +11 -1
- package/dist/providers/copilot/sdk-session.d.ts +12 -3
- package/dist/providers/copilot/sdk-session.js +230 -6
- package/dist/providers/create-provider.js +24 -0
- package/dist/providers/normalized/adapter.d.ts +107 -0
- package/dist/providers/normalized/adapter.js +1650 -0
- package/dist/providers/normalized/control-policy.d.ts +5 -0
- package/dist/providers/normalized/control-policy.js +12 -0
- package/dist/providers/normalized/interactions.d.ts +3 -0
- package/dist/providers/normalized/interactions.js +39 -0
- package/dist/providers/normalized/legacy-input-ownership.d.ts +34 -0
- package/dist/providers/normalized/legacy-input-ownership.js +123 -0
- package/dist/providers/normalized/session-management.d.ts +67 -0
- package/dist/providers/normalized/session-management.js +334 -0
- package/dist/providers/normalized/session-ownership.d.ts +3 -0
- package/dist/providers/normalized/session-ownership.js +90 -0
- package/dist/providers/prompt-usage.d.ts +8 -0
- package/dist/providers/prompt-usage.js +52 -0
- package/dist/providers/provider-adapter.d.ts +43 -1
- package/dist/providers/provider-adapter.js +8 -0
- package/dist/state-paths.d.ts +2 -0
- package/dist/state-paths.js +6 -0
- package/dist/types.d.ts +66 -0
- package/dist/typing-lease.d.ts +14 -0
- package/dist/typing-lease.js +31 -0
- package/dist/vendor/agent-provider-codex/LICENSE +211 -0
- package/dist/vendor/agent-provider-codex/NOTICE +13 -0
- package/package.json +22 -13
- package/skills/borgee-agent/SKILL.md +12 -4
- package/skills/borgee-agent/scripts/borgee-agent.mjs +110 -0
- package/skills/borgee-agent/scripts/borgee-agent.py +116 -0
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { summarizeError } from '../debug.js';
|
|
2
2
|
const LEGACY_MISSING_ALLOW_OPTION_MESSAGE = 'Copilot ACP requested permission but did not offer an allow option';
|
|
3
|
-
const SDK_APPROVE_FOR_SESSION_OPTION_ID = 'sdk-approve-for-session';
|
|
4
3
|
const SDK_APPROVE_ONCE_OPTION_ID = 'sdk-approve-once';
|
|
5
4
|
const SDK_REJECT_OPTION_ID = 'sdk-reject';
|
|
6
5
|
function legacySelectPermissionOption(options) {
|
|
@@ -279,12 +278,8 @@ export function resolveCopilotSdkPermissionResponse(context) {
|
|
|
279
278
|
feedback: 'Managed policy requires interactive approval, which this hosted runtime cannot provide.',
|
|
280
279
|
};
|
|
281
280
|
}
|
|
281
|
+
// Session grants bypass subsequent host policy checks; approve each SDK request independently.
|
|
282
282
|
const options = [
|
|
283
|
-
{
|
|
284
|
-
optionId: SDK_APPROVE_FOR_SESSION_OPTION_ID,
|
|
285
|
-
name: 'Allow for this session',
|
|
286
|
-
kind: 'allow_always',
|
|
287
|
-
},
|
|
288
283
|
{
|
|
289
284
|
optionId: SDK_APPROVE_ONCE_OPTION_ID,
|
|
290
285
|
name: 'Allow once',
|
|
@@ -312,8 +307,6 @@ export function resolveCopilotSdkPermissionResponse(context) {
|
|
|
312
307
|
return { kind: 'reject' };
|
|
313
308
|
}
|
|
314
309
|
switch (response.outcome.optionId) {
|
|
315
|
-
case SDK_APPROVE_FOR_SESSION_OPTION_ID:
|
|
316
|
-
return { kind: 'approve-for-session' };
|
|
317
310
|
case SDK_APPROVE_ONCE_OPTION_ID:
|
|
318
311
|
return { kind: 'approve-once' };
|
|
319
312
|
default:
|
|
@@ -2,7 +2,7 @@ import type { IncomingMessage } from 'node:http';
|
|
|
2
2
|
export type GatewayDecisionReason = 'browser-origin-not-allowed' | 'method-not-allowed' | 'missing-or-invalid-token' | 'invalid-token' | 'channel-mismatch' | 'not-found' | 'bootstrap-unavailable' | 'authorized';
|
|
3
3
|
export interface GatewayChannelRoute {
|
|
4
4
|
channelId: string;
|
|
5
|
-
resource: 'bootstrap' | 'me' | 'history' | 'draft' | 'users' | 'messages' | 'tasks' | 'current-task';
|
|
5
|
+
resource: 'bootstrap' | 'me' | 'history' | 'files' | 'file-content' | 'file-download' | 'file-sync' | 'file-publish' | 'draft' | 'users' | 'messages' | 'tasks' | 'current-task';
|
|
6
6
|
}
|
|
7
7
|
export interface GatewayTaskRoute {
|
|
8
8
|
taskId: string;
|
|
@@ -31,9 +31,23 @@ function decodePathSegment(segment) {
|
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
export function matchChannelRoute(pathname, allowCollaborationRoutes = false) {
|
|
34
|
+
const fileOperation = /^\/v1\/channels\/([^/]+)\/files\/(content|download|sync|publish)$/.exec(pathname);
|
|
35
|
+
if (fileOperation) {
|
|
36
|
+
const channelId = decodePathSegment(fileOperation[1]);
|
|
37
|
+
if (channelId == null) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
const resources = {
|
|
41
|
+
content: 'file-content',
|
|
42
|
+
download: 'file-download',
|
|
43
|
+
sync: 'file-sync',
|
|
44
|
+
publish: 'file-publish',
|
|
45
|
+
};
|
|
46
|
+
return { channelId, resource: resources[fileOperation[2]] };
|
|
47
|
+
}
|
|
34
48
|
const resources = allowCollaborationRoutes
|
|
35
|
-
? '(bootstrap|me|history|draft|users|messages|tasks|current-task)'
|
|
36
|
-
: '(bootstrap|me|history|users|tasks|current-task)';
|
|
49
|
+
? '(bootstrap|me|history|files|draft|users|messages|tasks|current-task)'
|
|
50
|
+
: '(bootstrap|me|history|files|users|tasks|current-task)';
|
|
37
51
|
const match = new RegExp(`^/v1/channels/([^/]+)/${resources}$`).exec(pathname);
|
|
38
52
|
if (!match) {
|
|
39
53
|
return null;
|
|
@@ -91,10 +105,16 @@ function allowedMethodsForRoute(route, collaborationRoutesEnabled) {
|
|
|
91
105
|
case 'bootstrap':
|
|
92
106
|
case 'me':
|
|
93
107
|
case 'history':
|
|
108
|
+
case 'files':
|
|
109
|
+
case 'file-content':
|
|
94
110
|
case 'draft':
|
|
95
111
|
case 'users':
|
|
96
112
|
case 'task-history':
|
|
97
113
|
return ['GET'];
|
|
114
|
+
case 'file-download':
|
|
115
|
+
case 'file-sync':
|
|
116
|
+
case 'file-publish':
|
|
117
|
+
return ['POST'];
|
|
98
118
|
case 'messages':
|
|
99
119
|
return collaborationRoutesEnabled ? ['POST'] : [];
|
|
100
120
|
case 'tasks':
|
|
@@ -55,6 +55,7 @@ export class ClaudeProviderAdapter {
|
|
|
55
55
|
const preparedTurn = await this.turnPreparer.prepare(input);
|
|
56
56
|
const text = await this.cli.generateReply(preparedTurn, {
|
|
57
57
|
onProgress: createAwaitingUserProgressHandler(options?.onProgress),
|
|
58
|
+
...(options?.onUsage ? { onUsage: options.onUsage } : {}),
|
|
58
59
|
});
|
|
59
60
|
// Read the session AFTER the turn: a first turn on a channel opens the
|
|
60
61
|
// session as it runs, so before this point there is nothing to report.
|
|
@@ -9,6 +9,7 @@ import { assertClaudeCommandCompatibility, DEFAULT_CLAUDE_COMMAND, isLegacyClaud
|
|
|
9
9
|
import { PROTOCOL_VERSION, client, methods, ndJsonStream, } from '@agentclientprotocol/sdk';
|
|
10
10
|
import { HostLogger, summarizeChildStderr, summarizeError } from '../../debug.js';
|
|
11
11
|
import { AcpProgressCollector } from '../acp-progress-collector.js';
|
|
12
|
+
import { normalizeAcpPromptUsage } from '../prompt-usage.js';
|
|
12
13
|
import { ProviderTurnCancelledError } from '../provider-adapter.js';
|
|
13
14
|
import { readClaudeActivityMetadata } from './activity-metadata.js';
|
|
14
15
|
import { ClaudeBackgroundRunObserver } from './background-run-observer.js';
|
|
@@ -1506,6 +1507,9 @@ export class ClaudeCliClient {
|
|
|
1506
1507
|
catch (error) {
|
|
1507
1508
|
throw markSessionTainted(error);
|
|
1508
1509
|
}
|
|
1510
|
+
const usage = normalizeAcpPromptUsage(response.usage);
|
|
1511
|
+
if (usage)
|
|
1512
|
+
options?.onUsage?.(usage);
|
|
1509
1513
|
const output = collector.getFinalText();
|
|
1510
1514
|
if (response.stopReason === 'cancelled') {
|
|
1511
1515
|
throw new ProviderTurnCancelledError('Claude ACP turn cancelled', {
|
|
@@ -27,6 +27,7 @@ export class CodexProviderAdapter {
|
|
|
27
27
|
const preparedTurn = await this.turnPreparer.prepare(input);
|
|
28
28
|
const text = await this.cli.generateReply(preparedTurn, {
|
|
29
29
|
onProgress: createAwaitingUserProgressHandler(options?.onProgress),
|
|
30
|
+
...(options?.onUsage ? { onUsage: options.onUsage } : {}),
|
|
30
31
|
});
|
|
31
32
|
// Read the session AFTER the turn: a first turn on a channel opens the
|
|
32
33
|
// session as it runs, so before this point there is nothing to report.
|
|
@@ -6,6 +6,7 @@ import spawn from 'cross-spawn';
|
|
|
6
6
|
import { PROTOCOL_VERSION, client, methods, ndJsonStream, } from '@agentclientprotocol/sdk';
|
|
7
7
|
import { HostLogger, summarizeChildStderr, summarizeError } from '../../debug.js';
|
|
8
8
|
import { AcpProgressCollector } from '../acp-progress-collector.js';
|
|
9
|
+
import { normalizeAcpPromptUsage } from '../prompt-usage.js';
|
|
9
10
|
import { ProviderTurnCancelledError } from '../provider-adapter.js';
|
|
10
11
|
import { assertCodexProjectDocumentSize, buildCodexProjectDocument } from './project-doc.js';
|
|
11
12
|
import { isGatewayCredentialSidecarBasename } from '../../context/injection.js';
|
|
@@ -850,6 +851,10 @@ export class CodexCliClient {
|
|
|
850
851
|
catch (error) {
|
|
851
852
|
throw markSessionTainted(error);
|
|
852
853
|
}
|
|
854
|
+
const usage = normalizeAcpPromptUsage(response.usage);
|
|
855
|
+
if (usage) {
|
|
856
|
+
options?.onUsage?.(usage);
|
|
857
|
+
}
|
|
853
858
|
const output = collector.getFinalText();
|
|
854
859
|
if (response.stopReason === 'cancelled') {
|
|
855
860
|
throw new ProviderTurnCancelledError('Codex ACP turn cancelled', {
|
|
@@ -27,6 +27,10 @@ export class CopilotProviderAdapter {
|
|
|
27
27
|
const preparedTurn = await this.turnPreparer.prepare(input);
|
|
28
28
|
const text = await this.cli.generateReply(preparedTurn, {
|
|
29
29
|
onProgress: createAwaitingUserProgressHandler(options?.onProgress),
|
|
30
|
+
...(options?.onUsage ? { onUsage: options.onUsage } : {}),
|
|
31
|
+
...(options?.onExecutionContinuation
|
|
32
|
+
? { onExecutionContinuation: options.onExecutionContinuation }
|
|
33
|
+
: {}),
|
|
30
34
|
});
|
|
31
35
|
// Read the session AFTER the turn: a first turn on a channel opens the
|
|
32
36
|
// session as it runs, so before this point there is nothing to report.
|
|
@@ -4,6 +4,7 @@ import { PROTOCOL_VERSION, client, methods, ndJsonStream, } from '@agentclientpr
|
|
|
4
4
|
import { toCopilotBackgroundActivity } from './activity-metadata.js';
|
|
5
5
|
import { HostLogger, summarizeChildStderr, summarizeError } from '../../debug.js';
|
|
6
6
|
import { AcpProgressCollector, } from '../acp-progress-collector.js';
|
|
7
|
+
import { normalizeAcpPromptUsage } from '../prompt-usage.js';
|
|
7
8
|
import { ProviderTurnCancelledError } from '../provider-adapter.js';
|
|
8
9
|
import { resolveCopilotPermissionResponse, resolveCopilotSdkPermissionResponse, } from '../../policy/copilot-permission.js';
|
|
9
10
|
import { isDiscussionOnlyResolvedWorkingFolder } from '../../context/resolved-working-folder.js';
|
|
@@ -1001,7 +1002,12 @@ export class CopilotCliClient {
|
|
|
1001
1002
|
this.closeSession(session);
|
|
1002
1003
|
}
|
|
1003
1004
|
async runTurn(session, prompt, options, onBackgroundAgentStarted) {
|
|
1004
|
-
const promptPromise = this.raceWithFatal(session
|
|
1005
|
+
const promptPromise = this.raceWithFatal(isCopilotSdkSession(session)
|
|
1006
|
+
? session.prompt(prompt, {
|
|
1007
|
+
onUsage: options?.onUsage,
|
|
1008
|
+
onExecutionContinuation: options?.onExecutionContinuation,
|
|
1009
|
+
})
|
|
1010
|
+
: session.prompt(prompt));
|
|
1005
1011
|
const promptFailure = new Promise((_, reject) => {
|
|
1006
1012
|
void promptPromise.catch((error) => reject(markSessionTainted(error)));
|
|
1007
1013
|
});
|
|
@@ -1042,6 +1048,10 @@ export class CopilotCliClient {
|
|
|
1042
1048
|
catch (error) {
|
|
1043
1049
|
throw markSessionTainted(error);
|
|
1044
1050
|
}
|
|
1051
|
+
const usage = normalizeAcpPromptUsage(response.usage);
|
|
1052
|
+
if (usage) {
|
|
1053
|
+
options?.onUsage?.(usage);
|
|
1054
|
+
}
|
|
1045
1055
|
if (response.stopReason === 'cancelled') {
|
|
1046
1056
|
throw new ProviderTurnCancelledError('Copilot turn cancelled');
|
|
1047
1057
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ActiveSession, ContentBlock } from '@agentclientprotocol/sdk';
|
|
2
2
|
import { type MessageOptions, type PermissionHandler, type ResumeSessionConfig, type SessionConfig, type SessionEventHandler } from '@github/copilot-sdk';
|
|
3
|
-
import type { ProviderBackgroundRunProgress } from '../../types.js';
|
|
3
|
+
import type { ProviderBackgroundRunProgress, ProviderGenerateOptions } from '../../types.js';
|
|
4
4
|
type CopilotSessionUpdate = Awaited<ReturnType<ActiveSession['nextUpdate']>>;
|
|
5
5
|
type CopilotPromptResponse = Awaited<ReturnType<ActiveSession['prompt']>>;
|
|
6
6
|
interface CopilotSdkTaskInfo {
|
|
@@ -77,6 +77,7 @@ export declare class CopilotSdkSessionAdapter {
|
|
|
77
77
|
private readonly onAutonomousReply;
|
|
78
78
|
private readonly onAutonomousTurnPresenceChanged;
|
|
79
79
|
private readonly onBackgroundTaskExecutionsChanged;
|
|
80
|
+
private readonly backgroundUsageOwnerTimeoutMs;
|
|
80
81
|
readonly transport = "sdk";
|
|
81
82
|
private readonly pendingUpdates;
|
|
82
83
|
private readonly updateWaiters;
|
|
@@ -98,11 +99,12 @@ export declare class CopilotSdkSessionAdapter {
|
|
|
98
99
|
private readonly backgroundTaskExecutions;
|
|
99
100
|
private backgroundTaskExecutionsDirty;
|
|
100
101
|
private readonly backgroundWaits;
|
|
102
|
+
private readonly backgroundUsageOwners;
|
|
101
103
|
private lastSyntheticExecutionStartedAt;
|
|
102
104
|
private closePromise?;
|
|
103
|
-
constructor(session: CopilotSdkSessionLike, onBackgroundRun: (run: ProviderBackgroundRunProgress) => void, onBackgroundAgentPresenceChanged: (active: boolean) => void, onBackgroundRunError: (error: Error) => void, onAutonomousReply: (reply: CopilotAutonomousReply) => void, onAutonomousTurnPresenceChanged: (active: boolean) => void, initialBackgroundTaskExecutions?: Readonly<Record<string, CopilotBackgroundExecutionState>>, onBackgroundTaskExecutionsChanged?: (executions: Readonly<Record<string, CopilotBackgroundExecutionState>>) => Promise<void
|
|
105
|
+
constructor(session: CopilotSdkSessionLike, onBackgroundRun: (run: ProviderBackgroundRunProgress) => void, onBackgroundAgentPresenceChanged: (active: boolean) => void, onBackgroundRunError: (error: Error) => void, onAutonomousReply: (reply: CopilotAutonomousReply) => void, onAutonomousTurnPresenceChanged: (active: boolean) => void, initialBackgroundTaskExecutions?: Readonly<Record<string, CopilotBackgroundExecutionState>>, onBackgroundTaskExecutionsChanged?: (executions: Readonly<Record<string, CopilotBackgroundExecutionState>>) => Promise<void>, backgroundUsageOwnerTimeoutMs?: number);
|
|
104
106
|
get sessionId(): string;
|
|
105
|
-
prompt(input: string | ContentBlock[]): Promise<CopilotPromptResponse>;
|
|
107
|
+
prompt(input: string | ContentBlock[], telemetry?: Pick<ProviderGenerateOptions, 'onExecutionContinuation' | 'onUsage'>): Promise<CopilotPromptResponse>;
|
|
106
108
|
nextUpdate(): Promise<CopilotSessionUpdate>;
|
|
107
109
|
interruptMainTurn(): Promise<boolean>;
|
|
108
110
|
cancelAllBackgroundWork(): Promise<boolean>;
|
|
@@ -139,6 +141,13 @@ export declare class CopilotSdkSessionAdapter {
|
|
|
139
141
|
private resolveBackgroundWait;
|
|
140
142
|
private backgroundWaitFor;
|
|
141
143
|
private handleOwnedTurnEvent;
|
|
144
|
+
private recordTurnUsage;
|
|
145
|
+
private deliverAccumulatedTurnUsage;
|
|
146
|
+
private recordBackgroundUsage;
|
|
147
|
+
private settleBackgroundUsageOwner;
|
|
148
|
+
private settleBackgroundUsageGroup;
|
|
149
|
+
private updateBackgroundUsageRegistryState;
|
|
150
|
+
private refreshMissingBackgroundTerminalGrace;
|
|
142
151
|
private interruptAttachedTurn;
|
|
143
152
|
private finishActiveTurn;
|
|
144
153
|
private resolveEventTurnId;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { accessSync, constants as fsConstants, existsSync, statSync } from 'node:fs';
|
|
2
2
|
import { delimiter, extname, isAbsolute, resolve } from 'node:path';
|
|
3
3
|
import { CopilotClient, RuntimeConnection, } from '@github/copilot-sdk';
|
|
4
|
+
import { normalizeProviderCallUsage } from '../prompt-usage.js';
|
|
4
5
|
function isExecutableFile(path) {
|
|
5
6
|
if (!existsSync(path) || !statSync(path).isFile()) {
|
|
6
7
|
return false;
|
|
@@ -79,6 +80,15 @@ export function createCopilotSdkSessionConfig(cwd, onPermissionRequest) {
|
|
|
79
80
|
onPermissionRequest,
|
|
80
81
|
};
|
|
81
82
|
}
|
|
83
|
+
const DEFAULT_BACKGROUND_USAGE_OWNER_TIMEOUT_MS = 30 * 60 * 1_000;
|
|
84
|
+
function addUsage(current, next) {
|
|
85
|
+
return {
|
|
86
|
+
inputTokens: (current?.inputTokens ?? 0) + next.inputTokens,
|
|
87
|
+
outputTokens: (current?.outputTokens ?? 0) + next.outputTokens,
|
|
88
|
+
cacheReadTokens: (current?.cacheReadTokens ?? 0) + next.cacheReadTokens,
|
|
89
|
+
cacheWriteTokens: (current?.cacheWriteTokens ?? 0) + next.cacheWriteTokens,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
82
92
|
const BACKGROUND_COMPLETION_SIGNAL_TTL_MS = 30_000;
|
|
83
93
|
const MAX_BACKGROUND_COMPLETION_SIGNALS = 32;
|
|
84
94
|
function normalizeError(error) {
|
|
@@ -193,6 +203,7 @@ export class CopilotSdkSessionAdapter {
|
|
|
193
203
|
onAutonomousReply;
|
|
194
204
|
onAutonomousTurnPresenceChanged;
|
|
195
205
|
onBackgroundTaskExecutionsChanged;
|
|
206
|
+
backgroundUsageOwnerTimeoutMs;
|
|
196
207
|
transport = 'sdk';
|
|
197
208
|
pendingUpdates = [];
|
|
198
209
|
updateWaiters = [];
|
|
@@ -214,9 +225,10 @@ export class CopilotSdkSessionAdapter {
|
|
|
214
225
|
backgroundTaskExecutions;
|
|
215
226
|
backgroundTaskExecutionsDirty = false;
|
|
216
227
|
backgroundWaits = new Map();
|
|
228
|
+
backgroundUsageOwners = new Map();
|
|
217
229
|
lastSyntheticExecutionStartedAt = 0;
|
|
218
230
|
closePromise;
|
|
219
|
-
constructor(session, onBackgroundRun, onBackgroundAgentPresenceChanged, onBackgroundRunError, onAutonomousReply, onAutonomousTurnPresenceChanged, initialBackgroundTaskExecutions = {}, onBackgroundTaskExecutionsChanged = async () => { }) {
|
|
231
|
+
constructor(session, onBackgroundRun, onBackgroundAgentPresenceChanged, onBackgroundRunError, onAutonomousReply, onAutonomousTurnPresenceChanged, initialBackgroundTaskExecutions = {}, onBackgroundTaskExecutionsChanged = async () => { }, backgroundUsageOwnerTimeoutMs = DEFAULT_BACKGROUND_USAGE_OWNER_TIMEOUT_MS) {
|
|
220
232
|
this.session = session;
|
|
221
233
|
this.onBackgroundRun = onBackgroundRun;
|
|
222
234
|
this.onBackgroundAgentPresenceChanged = onBackgroundAgentPresenceChanged;
|
|
@@ -224,6 +236,7 @@ export class CopilotSdkSessionAdapter {
|
|
|
224
236
|
this.onAutonomousReply = onAutonomousReply;
|
|
225
237
|
this.onAutonomousTurnPresenceChanged = onAutonomousTurnPresenceChanged;
|
|
226
238
|
this.onBackgroundTaskExecutionsChanged = onBackgroundTaskExecutionsChanged;
|
|
239
|
+
this.backgroundUsageOwnerTimeoutMs = backgroundUsageOwnerTimeoutMs;
|
|
227
240
|
this.backgroundTaskExecutions = new Map(Object.entries(initialBackgroundTaskExecutions).map(([taskId, execution]) => [
|
|
228
241
|
taskId,
|
|
229
242
|
{ ...execution },
|
|
@@ -233,7 +246,7 @@ export class CopilotSdkSessionAdapter {
|
|
|
233
246
|
get sessionId() {
|
|
234
247
|
return this.session.sessionId;
|
|
235
248
|
}
|
|
236
|
-
async prompt(input) {
|
|
249
|
+
async prompt(input, telemetry = {}) {
|
|
237
250
|
if (this.closePromise) {
|
|
238
251
|
throw new Error('Copilot SDK session is closed');
|
|
239
252
|
}
|
|
@@ -249,7 +262,13 @@ export class CopilotSdkSessionAdapter {
|
|
|
249
262
|
const turn = {
|
|
250
263
|
messageIdsWithDeltas: new Set(),
|
|
251
264
|
ownedTurnIds: new Set(),
|
|
265
|
+
usageKeys: new Set(),
|
|
266
|
+
backgroundAgentIds: new Set(),
|
|
267
|
+
backgroundSucceeded: true,
|
|
268
|
+
onUsage: telemetry.onUsage,
|
|
269
|
+
onExecutionContinuation: telemetry.onExecutionContinuation,
|
|
252
270
|
userMessageObserved: false,
|
|
271
|
+
usageDelivered: false,
|
|
253
272
|
aborted: false,
|
|
254
273
|
cancelRequested: false,
|
|
255
274
|
resolve: resolveTurn,
|
|
@@ -263,6 +282,7 @@ export class CopilotSdkSessionAdapter {
|
|
|
263
282
|
if (this.activeTurn === turn) {
|
|
264
283
|
this.activeTurn = undefined;
|
|
265
284
|
}
|
|
285
|
+
this.deliverAccumulatedTurnUsage(turn);
|
|
266
286
|
turn.reject(normalizeError(error));
|
|
267
287
|
}
|
|
268
288
|
return completion;
|
|
@@ -338,9 +358,16 @@ export class CopilotSdkSessionAdapter {
|
|
|
338
358
|
this.autonomousTurnIdByRuntimeTurnId.clear();
|
|
339
359
|
this.autonomousTurnIdByInteractionId.clear();
|
|
340
360
|
this.refreshAutonomousTurnPresence();
|
|
361
|
+
for (const owner of new Set(this.backgroundUsageOwners.values())) {
|
|
362
|
+
this.settleBackgroundUsageGroup(owner, false);
|
|
363
|
+
}
|
|
364
|
+
this.backgroundUsageOwners.clear();
|
|
341
365
|
this.closePromise = (async () => {
|
|
342
366
|
this.unsubscribe();
|
|
343
|
-
this.activeTurn
|
|
367
|
+
if (this.activeTurn) {
|
|
368
|
+
this.deliverAccumulatedTurnUsage(this.activeTurn);
|
|
369
|
+
this.activeTurn.reject(reason ?? new Error('Copilot SDK session closed'));
|
|
370
|
+
}
|
|
344
371
|
this.activeTurn = undefined;
|
|
345
372
|
for (const waiter of this.updateWaiters.splice(0)) {
|
|
346
373
|
waiter.reject(reason ?? new Error('Copilot SDK session closed'));
|
|
@@ -398,10 +425,11 @@ export class CopilotSdkSessionAdapter {
|
|
|
398
425
|
}
|
|
399
426
|
const fingerprint = JSON.stringify(run);
|
|
400
427
|
const previous = this.reportedBackgroundTasks.get(run.providerRunId);
|
|
428
|
+
this.reportedBackgroundTasks.set(run.providerRunId, run);
|
|
429
|
+
this.updateBackgroundUsageRegistryState(run.providerRunId, run.state === 'running' || run.state === 'waiting');
|
|
401
430
|
if (previous && JSON.stringify(previous) === fingerprint) {
|
|
402
431
|
continue;
|
|
403
432
|
}
|
|
404
|
-
this.reportedBackgroundTasks.set(run.providerRunId, run);
|
|
405
433
|
this.onBackgroundRun(run);
|
|
406
434
|
}
|
|
407
435
|
for (const [providerRunId, previous] of this.reportedBackgroundTasks) {
|
|
@@ -411,6 +439,10 @@ export class CopilotSdkSessionAdapter {
|
|
|
411
439
|
this.backgroundTaskExecutionsDirty = true;
|
|
412
440
|
}
|
|
413
441
|
this.backgroundWaits.delete(providerRunId);
|
|
442
|
+
if (this.activeTurn?.backgroundAgentIds.delete(providerRunId)) {
|
|
443
|
+
this.activeTurn.backgroundSucceeded = false;
|
|
444
|
+
}
|
|
445
|
+
this.refreshMissingBackgroundTerminalGrace(providerRunId);
|
|
414
446
|
if (previous.state === 'running' || previous.state === 'waiting') {
|
|
415
447
|
this.onBackgroundRun({
|
|
416
448
|
...previous,
|
|
@@ -666,7 +698,46 @@ export class CopilotSdkSessionAdapter {
|
|
|
666
698
|
this.noteBackgroundCompletion(event);
|
|
667
699
|
return;
|
|
668
700
|
}
|
|
701
|
+
if (event.type === 'session.error') {
|
|
702
|
+
if (event.agentId) {
|
|
703
|
+
if (this.activeTurn?.backgroundAgentIds.delete(event.agentId)) {
|
|
704
|
+
this.activeTurn.backgroundSucceeded = false;
|
|
705
|
+
}
|
|
706
|
+
this.settleBackgroundUsageOwner(event.agentId, false);
|
|
707
|
+
}
|
|
708
|
+
else {
|
|
709
|
+
for (const owner of new Set(this.backgroundUsageOwners.values())) {
|
|
710
|
+
this.settleBackgroundUsageGroup(owner, false);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}
|
|
669
714
|
if (event.agentId) {
|
|
715
|
+
if (event.type === 'subagent.started' && this.activeTurn) {
|
|
716
|
+
this.activeTurn.backgroundAgentIds.add(event.agentId);
|
|
717
|
+
}
|
|
718
|
+
else if (event.type === 'assistant.usage') {
|
|
719
|
+
if (this.activeTurn &&
|
|
720
|
+
this.activeTurn.backgroundAgentIds.has(event.agentId)) {
|
|
721
|
+
this.recordTurnUsage(this.activeTurn, event);
|
|
722
|
+
}
|
|
723
|
+
else {
|
|
724
|
+
this.recordBackgroundUsage(event.agentId, event);
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
else if (event.type === 'subagent.completed' ||
|
|
728
|
+
event.type === 'subagent.failed') {
|
|
729
|
+
const succeeded = event.type === 'subagent.completed' && event.data.cancelled !== true;
|
|
730
|
+
if (this.activeTurn?.backgroundAgentIds.delete(event.agentId) && !succeeded) {
|
|
731
|
+
this.activeTurn.backgroundSucceeded = false;
|
|
732
|
+
}
|
|
733
|
+
this.settleBackgroundUsageOwner(event.agentId, succeeded);
|
|
734
|
+
}
|
|
735
|
+
else if (event.type === 'abort') {
|
|
736
|
+
if (this.activeTurn?.backgroundAgentIds.delete(event.agentId)) {
|
|
737
|
+
this.activeTurn.backgroundSucceeded = false;
|
|
738
|
+
}
|
|
739
|
+
this.settleBackgroundUsageOwner(event.agentId, false);
|
|
740
|
+
}
|
|
670
741
|
this.handleBackgroundAgentEvent(event, event.agentId);
|
|
671
742
|
return;
|
|
672
743
|
}
|
|
@@ -765,7 +836,7 @@ export class CopilotSdkSessionAdapter {
|
|
|
765
836
|
this.discardAutonomousTurn(turn);
|
|
766
837
|
return;
|
|
767
838
|
}
|
|
768
|
-
if (event.type === 'assistant.idle') {
|
|
839
|
+
if (event.type === 'assistant.idle' || event.type === 'session.idle') {
|
|
769
840
|
this.finishAutonomousTurn(turn);
|
|
770
841
|
}
|
|
771
842
|
}
|
|
@@ -906,15 +977,25 @@ export class CopilotSdkSessionAdapter {
|
|
|
906
977
|
rawOutput: event.data.result ?? event.data.error,
|
|
907
978
|
}));
|
|
908
979
|
return;
|
|
980
|
+
case 'assistant.usage': {
|
|
981
|
+
this.recordTurnUsage(this.activeTurn, event);
|
|
982
|
+
return;
|
|
983
|
+
}
|
|
909
984
|
case 'abort':
|
|
985
|
+
this.deliverAccumulatedTurnUsage(this.activeTurn);
|
|
910
986
|
this.activeTurn.aborted = true;
|
|
911
987
|
return;
|
|
912
988
|
case 'session.error': {
|
|
913
989
|
const turn = this.activeTurn;
|
|
914
990
|
this.activeTurn = undefined;
|
|
991
|
+
this.deliverAccumulatedTurnUsage(turn);
|
|
915
992
|
turn.reject(new Error(event.data.message));
|
|
916
993
|
return;
|
|
917
994
|
}
|
|
995
|
+
case 'session.idle': {
|
|
996
|
+
this.finishActiveTurn(false);
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
918
999
|
case 'assistant.idle': {
|
|
919
1000
|
this.finishActiveTurn(event.data.aborted === true);
|
|
920
1001
|
return;
|
|
@@ -923,6 +1004,93 @@ export class CopilotSdkSessionAdapter {
|
|
|
923
1004
|
return;
|
|
924
1005
|
}
|
|
925
1006
|
}
|
|
1007
|
+
recordTurnUsage(turn, event) {
|
|
1008
|
+
const usageKey = event.data.apiCallId ?? event.id;
|
|
1009
|
+
if (turn.usageKeys.has(usageKey))
|
|
1010
|
+
return;
|
|
1011
|
+
const usage = normalizeProviderCallUsage(event.data);
|
|
1012
|
+
if (!usage)
|
|
1013
|
+
return;
|
|
1014
|
+
turn.usageKeys.add(usageKey);
|
|
1015
|
+
turn.usage = addUsage(turn.usage, usage);
|
|
1016
|
+
}
|
|
1017
|
+
deliverAccumulatedTurnUsage(turn) {
|
|
1018
|
+
if (!turn.usage || turn.usageDelivered)
|
|
1019
|
+
return;
|
|
1020
|
+
turn.usageDelivered = true;
|
|
1021
|
+
turn.onUsage?.({ ...turn.usage });
|
|
1022
|
+
}
|
|
1023
|
+
recordBackgroundUsage(agentId, event) {
|
|
1024
|
+
const owner = this.backgroundUsageOwners.get(agentId);
|
|
1025
|
+
if (!owner)
|
|
1026
|
+
return;
|
|
1027
|
+
const usageKey = event.data.apiCallId ?? event.id;
|
|
1028
|
+
if (owner.usageKeys.has(usageKey))
|
|
1029
|
+
return;
|
|
1030
|
+
const usage = normalizeProviderCallUsage(event.data);
|
|
1031
|
+
if (!usage)
|
|
1032
|
+
return;
|
|
1033
|
+
owner.usageKeys.add(usageKey);
|
|
1034
|
+
owner.onUsage?.(usage);
|
|
1035
|
+
this.refreshMissingBackgroundTerminalGrace(agentId);
|
|
1036
|
+
}
|
|
1037
|
+
settleBackgroundUsageOwner(agentId, succeeded) {
|
|
1038
|
+
const owner = this.backgroundUsageOwners.get(agentId);
|
|
1039
|
+
if (!owner)
|
|
1040
|
+
return;
|
|
1041
|
+
const timer = owner.missingTerminalTimers.get(agentId);
|
|
1042
|
+
if (timer)
|
|
1043
|
+
clearTimeout(timer);
|
|
1044
|
+
owner.missingTerminalTimers.delete(agentId);
|
|
1045
|
+
owner.succeeded &&= succeeded;
|
|
1046
|
+
owner.agentIds.delete(agentId);
|
|
1047
|
+
this.backgroundUsageOwners.delete(agentId);
|
|
1048
|
+
if (owner.agentIds.size === 0) {
|
|
1049
|
+
this.settleBackgroundUsageGroup(owner, owner.succeeded);
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
settleBackgroundUsageGroup(owner, succeeded) {
|
|
1053
|
+
for (const timer of owner.missingTerminalTimers.values()) {
|
|
1054
|
+
clearTimeout(timer);
|
|
1055
|
+
}
|
|
1056
|
+
owner.missingTerminalTimers.clear();
|
|
1057
|
+
for (const agentId of owner.agentIds) {
|
|
1058
|
+
if (this.backgroundUsageOwners.get(agentId) === owner) {
|
|
1059
|
+
this.backgroundUsageOwners.delete(agentId);
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
owner.agentIds.clear();
|
|
1063
|
+
owner.resolve(succeeded && owner.succeeded);
|
|
1064
|
+
}
|
|
1065
|
+
updateBackgroundUsageRegistryState(agentId, active) {
|
|
1066
|
+
const owner = this.backgroundUsageOwners.get(agentId);
|
|
1067
|
+
if (!owner)
|
|
1068
|
+
return;
|
|
1069
|
+
const existing = owner.missingTerminalTimers.get(agentId);
|
|
1070
|
+
if (active) {
|
|
1071
|
+
if (existing)
|
|
1072
|
+
clearTimeout(existing);
|
|
1073
|
+
owner.missingTerminalTimers.delete(agentId);
|
|
1074
|
+
return;
|
|
1075
|
+
}
|
|
1076
|
+
this.refreshMissingBackgroundTerminalGrace(agentId);
|
|
1077
|
+
}
|
|
1078
|
+
refreshMissingBackgroundTerminalGrace(agentId) {
|
|
1079
|
+
const owner = this.backgroundUsageOwners.get(agentId);
|
|
1080
|
+
if (!owner)
|
|
1081
|
+
return;
|
|
1082
|
+
const registryRun = this.reportedBackgroundTasks.get(agentId);
|
|
1083
|
+
if (registryRun?.state === 'running' || registryRun?.state === 'waiting') {
|
|
1084
|
+
this.updateBackgroundUsageRegistryState(agentId, true);
|
|
1085
|
+
return;
|
|
1086
|
+
}
|
|
1087
|
+
const existing = owner.missingTerminalTimers.get(agentId);
|
|
1088
|
+
if (existing)
|
|
1089
|
+
clearTimeout(existing);
|
|
1090
|
+
owner.missingTerminalTimers.set(agentId, setTimeout(() => {
|
|
1091
|
+
this.settleBackgroundUsageOwner(agentId, false);
|
|
1092
|
+
}, this.backgroundUsageOwnerTimeoutMs));
|
|
1093
|
+
}
|
|
926
1094
|
async interruptAttachedTurn(turn) {
|
|
927
1095
|
try {
|
|
928
1096
|
const result = await this.session.rpc.interruptMainTurn({ flushQueued: false });
|
|
@@ -935,6 +1103,7 @@ export class CopilotSdkSessionAdapter {
|
|
|
935
1103
|
return;
|
|
936
1104
|
}
|
|
937
1105
|
this.activeTurn = undefined;
|
|
1106
|
+
this.deliverAccumulatedTurnUsage(turn);
|
|
938
1107
|
turn.reject(normalizeError(error));
|
|
939
1108
|
}
|
|
940
1109
|
}
|
|
@@ -946,7 +1115,58 @@ export class CopilotSdkSessionAdapter {
|
|
|
946
1115
|
this.activeTurn = undefined;
|
|
947
1116
|
const stopReason = aborted || turn.aborted ? 'cancelled' : 'end_turn';
|
|
948
1117
|
this.emitUpdate(stopUpdate(stopReason));
|
|
949
|
-
turn.
|
|
1118
|
+
if (!turn.backgroundSucceeded) {
|
|
1119
|
+
turn.onExecutionContinuation?.(Promise.resolve(false));
|
|
1120
|
+
}
|
|
1121
|
+
if (turn.backgroundAgentIds.size > 0 &&
|
|
1122
|
+
turn.onExecutionContinuation) {
|
|
1123
|
+
let resolveContinuation;
|
|
1124
|
+
const completion = new Promise((resolve) => {
|
|
1125
|
+
resolveContinuation = resolve;
|
|
1126
|
+
});
|
|
1127
|
+
const owner = {
|
|
1128
|
+
agentIds: new Set(turn.backgroundAgentIds),
|
|
1129
|
+
usageKeys: new Set(turn.usageKeys),
|
|
1130
|
+
onUsage: turn.onUsage,
|
|
1131
|
+
succeeded: true,
|
|
1132
|
+
missingTerminalTimers: new Map(),
|
|
1133
|
+
resolve: resolveContinuation,
|
|
1134
|
+
};
|
|
1135
|
+
for (const agentId of owner.agentIds) {
|
|
1136
|
+
const previous = this.backgroundUsageOwners.get(agentId);
|
|
1137
|
+
if (previous) {
|
|
1138
|
+
const previousTimer = previous.missingTerminalTimers.get(agentId);
|
|
1139
|
+
if (previousTimer)
|
|
1140
|
+
clearTimeout(previousTimer);
|
|
1141
|
+
previous.missingTerminalTimers.delete(agentId);
|
|
1142
|
+
previous.agentIds.delete(agentId);
|
|
1143
|
+
previous.succeeded = false;
|
|
1144
|
+
if (previous.agentIds.size === 0) {
|
|
1145
|
+
this.settleBackgroundUsageGroup(previous, false);
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
this.backgroundUsageOwners.set(agentId, owner);
|
|
1149
|
+
this.refreshMissingBackgroundTerminalGrace(agentId);
|
|
1150
|
+
}
|
|
1151
|
+
turn.onExecutionContinuation(completion);
|
|
1152
|
+
}
|
|
1153
|
+
turn.resolve({
|
|
1154
|
+
stopReason,
|
|
1155
|
+
...(turn.usage && !turn.usageDelivered
|
|
1156
|
+
? {
|
|
1157
|
+
usage: {
|
|
1158
|
+
inputTokens: turn.usage.inputTokens,
|
|
1159
|
+
outputTokens: turn.usage.outputTokens,
|
|
1160
|
+
totalTokens: turn.usage.inputTokens +
|
|
1161
|
+
turn.usage.outputTokens +
|
|
1162
|
+
turn.usage.cacheReadTokens +
|
|
1163
|
+
turn.usage.cacheWriteTokens,
|
|
1164
|
+
cachedReadTokens: turn.usage.cacheReadTokens,
|
|
1165
|
+
cachedWriteTokens: turn.usage.cacheWriteTokens,
|
|
1166
|
+
},
|
|
1167
|
+
}
|
|
1168
|
+
: {}),
|
|
1169
|
+
});
|
|
950
1170
|
}
|
|
951
1171
|
resolveEventTurnId(event) {
|
|
952
1172
|
switch (event.type) {
|
|
@@ -956,6 +1176,10 @@ export class CopilotSdkSessionAdapter {
|
|
|
956
1176
|
case 'tool.execution_start':
|
|
957
1177
|
case 'tool.execution_complete':
|
|
958
1178
|
return event.data.turnId ?? this.currentRootTurnId;
|
|
1179
|
+
case 'session.idle':
|
|
1180
|
+
// Session idle is terminal only after the owned root turn has ended.
|
|
1181
|
+
return this.currentRootTurnId ? undefined : this.lastRootTurnId;
|
|
1182
|
+
case 'assistant.usage':
|
|
959
1183
|
case 'assistant.idle':
|
|
960
1184
|
return this.currentRootTurnId ?? this.lastRootTurnId;
|
|
961
1185
|
default:
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { configureHostedNativeSession, createHostedNativeProvider } from '../native-providers.js';
|
|
3
|
+
import { createSessionOwnershipLock, readProtectedCodexSessions } from './normalized/session-ownership.js';
|
|
4
|
+
import { NormalizedProviderAdapter } from './normalized/adapter.js';
|
|
1
5
|
import { FileChannelContextStore } from '../context/injection.js';
|
|
2
6
|
import { ServerProjectionStrategyResolver } from '../context/projection-strategy.js';
|
|
3
7
|
import { createProviderConnectionsSessionStore } from '../connections-state-store.js';
|
|
@@ -59,6 +63,10 @@ class CliBackedProviderV2 {
|
|
|
59
63
|
async generateReply(input, options) {
|
|
60
64
|
const text = await this.cli.generateReply(input.turn, {
|
|
61
65
|
onProgress: createAwaitingUserProgressHandler(options?.onProgress),
|
|
66
|
+
...(options?.onUsage ? { onUsage: options.onUsage } : {}),
|
|
67
|
+
...(options?.onExecutionContinuation
|
|
68
|
+
? { onExecutionContinuation: options.onExecutionContinuation }
|
|
69
|
+
: {}),
|
|
62
70
|
});
|
|
63
71
|
// Read the session AFTER the turn: a first turn on a channel opens the
|
|
64
72
|
// session as it runs, so before this point there is nothing to report.
|
|
@@ -195,6 +203,22 @@ export function createProvider(config, debugLogger, options = {}) {
|
|
|
195
203
|
return projectionStrategyResolver.resolve(input.channelId);
|
|
196
204
|
},
|
|
197
205
|
});
|
|
206
|
+
const outputMode = config.providerOutputMode ?? 'legacy';
|
|
207
|
+
if (outputMode === 'normalized') {
|
|
208
|
+
const provider = createHostedNativeProvider(config.provider, config.nativeProviderCommand, config);
|
|
209
|
+
return new NormalizedProviderAdapter({
|
|
210
|
+
...(config.provider === 'codex' ? { sessionManagement: { root: config.sessionWorkspaceRoot,
|
|
211
|
+
store: new FileCodexChannelSessionStore({ resolvePath: () => join(config.stateRootDir, 'independent-sessions', 'codex.json') }),
|
|
212
|
+
loadProtectedNativeIds: () => readProtectedCodexSessions(config.stateRootDir),
|
|
213
|
+
withOwnershipLock: createSessionOwnershipLock(join(config.stateRootDir, 'independent-sessions')),
|
|
214
|
+
} } : {}),
|
|
215
|
+
provider, prepare: (input) => turnPreparer.prepare(input),
|
|
216
|
+
...(config.provider === 'codex' ? { prepareSession: (channelId) => turnPreparer.prepareSession(channelId, 'codex') } : {}),
|
|
217
|
+
resolveAgentId: () => config.resolveStableAgentId?.(), serverUrl: config.borgeeBaseUrl,
|
|
218
|
+
configureSession: (session) => configureHostedNativeSession(session, config.provider),
|
|
219
|
+
store: new FileCodexChannelSessionStore({ resolvePath: (agentId) => join(config.stateRootDir, 'native-sessions', config.provider, encodeURIComponent(agentId) + '.json') }),
|
|
220
|
+
});
|
|
221
|
+
}
|
|
198
222
|
switch (config.provider) {
|
|
199
223
|
case 'claude': {
|
|
200
224
|
return createClaudeProvider(config, debugLogger, connectionsStateGateEnabled, turnPreparer, idleBackendShutdownMs);
|