@borgee/agents-host 0.2.74 → 0.2.84
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 +11 -24
- package/dist/agents-host.d.ts +6 -4
- package/dist/agents-host.js +117 -16
- package/dist/background-runs.d.ts +23 -0
- package/dist/background-runs.js +153 -0
- package/dist/chat/chat-control-plane.d.ts +6 -1
- package/dist/chat/sdk-chat-control-plane.d.ts +4 -2
- package/dist/chat/sdk-chat-control-plane.js +7 -0
- package/dist/context/claude-file-brief.js +3 -3
- package/dist/context/injection.d.ts +18 -12
- package/dist/context/injection.js +53 -53
- package/dist/context/prompt.js +10 -10
- package/dist/context/resolved-working-folder.d.ts +3 -0
- package/dist/context/resolved-working-folder.js +106 -0
- package/dist/context/turn-preparation.js +1 -1
- package/dist/managed-daemon.js +30 -30
- package/dist/plugin-sdk.js +179 -9
- package/dist/plugin-sdk.js.map +3 -3
- package/dist/policy/authorization-audit.d.ts +1 -1
- package/dist/policy/copilot-permission.d.ts +9 -0
- package/dist/policy/copilot-permission.js +120 -1
- package/dist/progress-to-activity.d.ts +1 -1
- package/dist/progress-to-activity.js +1 -0
- package/dist/providers/claude/adapter.d.ts +1 -1
- package/dist/providers/claude/adapter.js +7 -0
- package/dist/providers/claude/cli-client.js +4 -3
- package/dist/providers/codex/adapter.d.ts +1 -1
- package/dist/providers/codex/adapter.js +7 -0
- package/dist/providers/codex/cli-client.js +8 -7
- package/dist/providers/codex/project-doc.js +6 -6
- package/dist/providers/copilot/adapter.d.ts +6 -2
- package/dist/providers/copilot/adapter.js +27 -1
- package/dist/providers/copilot/cli-client.d.ts +37 -10
- package/dist/providers/copilot/cli-client.js +751 -122
- package/dist/providers/copilot/sdk-session.d.ts +149 -0
- package/dist/providers/copilot/sdk-session.js +981 -0
- package/dist/providers/create-provider.js +33 -0
- package/dist/providers/provider-adapter.d.ts +16 -1
- package/dist/providers/provider-adapter.js +13 -0
- package/dist/types.d.ts +32 -11
- package/package.json +3 -2
- package/skills/borgee-agent/references/task-properties.md +3 -3
- package/skills/borgee-agent/scripts/borgee-agent.mjs +1 -1
- package/skills/borgee-agent/scripts/borgee-agent.py +1 -1
- package/dist/context/resolved-workspace.d.ts +0 -3
- package/dist/context/resolved-workspace.js +0 -106
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { DebugLogger } from '../debug.js';
|
|
2
2
|
import type { InternalPolicyMode } from '../compatibility-gates.js';
|
|
3
|
-
export type AuthorizationAuditSurface = 'copilot-acp-permission' | 'localhost-gateway';
|
|
3
|
+
export type AuthorizationAuditSurface = 'copilot-acp-permission' | 'copilot-sdk-permission' | 'localhost-gateway';
|
|
4
4
|
export interface AuthorizationAuditRecord {
|
|
5
5
|
timestamp: string;
|
|
6
6
|
surface: AuthorizationAuditSurface;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { RequestPermissionRequest, RequestPermissionResponse } from '@agentclientprotocol/sdk';
|
|
2
|
+
import type { PermissionRequest as CopilotSdkPermissionRequest, PermissionRequestResult as CopilotSdkPermissionRequestResult } from '@github/copilot-sdk';
|
|
2
3
|
import type { DebugLogger } from '../debug.js';
|
|
3
4
|
import type { InternalPolicyMode } from '../compatibility-gates.js';
|
|
4
5
|
import type { AuthorizationAuditSinkLike } from './authorization-audit.js';
|
|
6
|
+
import type { AuthorizationAuditSurface } from './authorization-audit.js';
|
|
5
7
|
export interface CopilotPermissionPolicyContext {
|
|
6
8
|
gateEnabled: boolean;
|
|
7
9
|
policyMode: InternalPolicyMode;
|
|
@@ -10,7 +12,14 @@ export interface CopilotPermissionPolicyContext {
|
|
|
10
12
|
auditSink?: AuthorizationAuditSinkLike;
|
|
11
13
|
agentId?: string;
|
|
12
14
|
channelId?: string;
|
|
15
|
+
sessionBound?: boolean;
|
|
13
16
|
toolPermissionMode?: 'default' | 'deny-all';
|
|
17
|
+
auditSurface?: AuthorizationAuditSurface;
|
|
18
|
+
}
|
|
19
|
+
export interface CopilotSdkPermissionPolicyContext extends Omit<CopilotPermissionPolicyContext, 'params'> {
|
|
20
|
+
request: CopilotSdkPermissionRequest;
|
|
21
|
+
sessionId: string;
|
|
14
22
|
}
|
|
15
23
|
export declare function resolveCopilotPermissionResponse(context: CopilotPermissionPolicyContext): RequestPermissionResponse;
|
|
24
|
+
export declare function resolveCopilotSdkPermissionResponse(context: CopilotSdkPermissionPolicyContext): CopilotSdkPermissionRequestResult;
|
|
16
25
|
export declare function createLegacyMissingAllowOptionError(): Error;
|
|
@@ -1,5 +1,8 @@
|
|
|
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
|
+
const SDK_APPROVE_ONCE_OPTION_ID = 'sdk-approve-once';
|
|
5
|
+
const SDK_REJECT_OPTION_ID = 'sdk-reject';
|
|
3
6
|
function legacySelectPermissionOption(options) {
|
|
4
7
|
const option = options.find((candidate) => candidate.kind === 'allow_always')
|
|
5
8
|
?? options.find((candidate) => candidate.kind === 'allow_once');
|
|
@@ -71,7 +74,7 @@ function decisionToResponse(decision) {
|
|
|
71
74
|
function appendAuditRecord(context, effectiveDecision, reason, policyDecision) {
|
|
72
75
|
context.auditSink?.append({
|
|
73
76
|
timestamp: new Date().toISOString(),
|
|
74
|
-
surface: 'copilot-acp-permission',
|
|
77
|
+
surface: context.auditSurface ?? 'copilot-acp-permission',
|
|
75
78
|
policyMode: context.policyMode,
|
|
76
79
|
effectiveOutcome: effectiveDecision?.outcome ?? 'error',
|
|
77
80
|
reason,
|
|
@@ -84,6 +87,30 @@ function appendAuditRecord(context, effectiveDecision, reason, policyDecision) {
|
|
|
84
87
|
});
|
|
85
88
|
}
|
|
86
89
|
export function resolveCopilotPermissionResponse(context) {
|
|
90
|
+
if (context.sessionBound === false) {
|
|
91
|
+
const rejectOption = resolveRejectOption(context.params.options);
|
|
92
|
+
const unboundDecision = rejectOption
|
|
93
|
+
? {
|
|
94
|
+
outcome: 'selected',
|
|
95
|
+
optionId: rejectOption.optionId,
|
|
96
|
+
optionKind: rejectOption.kind,
|
|
97
|
+
reason: 'unbound-session',
|
|
98
|
+
toolKind: normalizeToolKind(context.params.toolCall.kind),
|
|
99
|
+
}
|
|
100
|
+
: {
|
|
101
|
+
outcome: 'cancelled',
|
|
102
|
+
reason: 'unbound-session',
|
|
103
|
+
toolKind: normalizeToolKind(context.params.toolCall.kind),
|
|
104
|
+
};
|
|
105
|
+
context.logger.error('Copilot ACP permission request rejected for an unbound session', {
|
|
106
|
+
agentId: context.agentId,
|
|
107
|
+
sessionId: context.params.sessionId,
|
|
108
|
+
toolKind: unboundDecision.toolKind,
|
|
109
|
+
reason: unboundDecision.reason,
|
|
110
|
+
});
|
|
111
|
+
appendAuditRecord(context, unboundDecision, unboundDecision.reason, unboundDecision);
|
|
112
|
+
return decisionToResponse(unboundDecision);
|
|
113
|
+
}
|
|
87
114
|
if (context.toolPermissionMode === 'deny-all') {
|
|
88
115
|
const rejectOption = resolveRejectOption(context.params.options);
|
|
89
116
|
const deniedDecision = rejectOption
|
|
@@ -206,6 +233,98 @@ export function resolveCopilotPermissionResponse(context) {
|
|
|
206
233
|
return decisionToResponse(failedClosedDecision);
|
|
207
234
|
}
|
|
208
235
|
}
|
|
236
|
+
function sdkPermissionToolKind(request) {
|
|
237
|
+
switch (request.kind) {
|
|
238
|
+
case 'read':
|
|
239
|
+
return 'read';
|
|
240
|
+
case 'write':
|
|
241
|
+
return 'edit';
|
|
242
|
+
case 'shell':
|
|
243
|
+
return 'execute';
|
|
244
|
+
case 'url':
|
|
245
|
+
return 'fetch';
|
|
246
|
+
default:
|
|
247
|
+
return 'other';
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
function sdkPermissionTitle(request) {
|
|
251
|
+
if ('fullCommandText' in request && typeof request.fullCommandText === 'string') {
|
|
252
|
+
return request.fullCommandText;
|
|
253
|
+
}
|
|
254
|
+
if ('fileName' in request && typeof request.fileName === 'string') {
|
|
255
|
+
return request.fileName;
|
|
256
|
+
}
|
|
257
|
+
if ('toolName' in request && typeof request.toolName === 'string') {
|
|
258
|
+
return request.toolName;
|
|
259
|
+
}
|
|
260
|
+
return request.kind;
|
|
261
|
+
}
|
|
262
|
+
export function resolveCopilotSdkPermissionResponse(context) {
|
|
263
|
+
if (context.request.managedApprovalRequired === true) {
|
|
264
|
+
context.auditSink?.append({
|
|
265
|
+
timestamp: new Date().toISOString(),
|
|
266
|
+
surface: context.auditSurface ?? 'copilot-sdk-permission',
|
|
267
|
+
policyMode: context.policyMode,
|
|
268
|
+
effectiveOutcome: 'rejected',
|
|
269
|
+
reason: 'managed-approval-required',
|
|
270
|
+
agentId: context.agentId,
|
|
271
|
+
channelId: context.channelId,
|
|
272
|
+
selectedOptionKind: 'reject_once',
|
|
273
|
+
policyOutcome: 'selected',
|
|
274
|
+
policySelectedOptionKind: 'reject_once',
|
|
275
|
+
toolKind: sdkPermissionToolKind(context.request),
|
|
276
|
+
});
|
|
277
|
+
return {
|
|
278
|
+
kind: 'reject',
|
|
279
|
+
feedback: 'Managed policy requires interactive approval, which this hosted runtime cannot provide.',
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
const options = [
|
|
283
|
+
{
|
|
284
|
+
optionId: SDK_APPROVE_FOR_SESSION_OPTION_ID,
|
|
285
|
+
name: 'Allow for this session',
|
|
286
|
+
kind: 'allow_always',
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
optionId: SDK_APPROVE_ONCE_OPTION_ID,
|
|
290
|
+
name: 'Allow once',
|
|
291
|
+
kind: 'allow_once',
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
optionId: SDK_REJECT_OPTION_ID,
|
|
295
|
+
name: 'Reject',
|
|
296
|
+
kind: 'reject_once',
|
|
297
|
+
},
|
|
298
|
+
];
|
|
299
|
+
const response = resolveCopilotPermissionResponse({
|
|
300
|
+
...context,
|
|
301
|
+
params: {
|
|
302
|
+
sessionId: context.sessionId,
|
|
303
|
+
toolCall: {
|
|
304
|
+
toolCallId: requestToolCallId(context.request),
|
|
305
|
+
title: sdkPermissionTitle(context.request),
|
|
306
|
+
kind: sdkPermissionToolKind(context.request),
|
|
307
|
+
},
|
|
308
|
+
options,
|
|
309
|
+
},
|
|
310
|
+
});
|
|
311
|
+
if (response.outcome.outcome !== 'selected') {
|
|
312
|
+
return { kind: 'reject' };
|
|
313
|
+
}
|
|
314
|
+
switch (response.outcome.optionId) {
|
|
315
|
+
case SDK_APPROVE_FOR_SESSION_OPTION_ID:
|
|
316
|
+
return { kind: 'approve-for-session' };
|
|
317
|
+
case SDK_APPROVE_ONCE_OPTION_ID:
|
|
318
|
+
return { kind: 'approve-once' };
|
|
319
|
+
default:
|
|
320
|
+
return { kind: 'reject' };
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
function requestToolCallId(request) {
|
|
324
|
+
return 'toolCallId' in request && typeof request.toolCallId === 'string'
|
|
325
|
+
? request.toolCallId
|
|
326
|
+
: 'copilot-sdk-permission';
|
|
327
|
+
}
|
|
209
328
|
export function createLegacyMissingAllowOptionError() {
|
|
210
329
|
return new Error(LEGACY_MISSING_ALLOW_OPTION_MESSAGE);
|
|
211
330
|
}
|
|
@@ -2,7 +2,7 @@ import type { AgentActivity } from './plugin-sdk.js';
|
|
|
2
2
|
import type { ProviderProgressUpdate } from './types.js';
|
|
3
3
|
/** Everything a provider reports. The turn boundary is the reporter's own and never arrives here. */
|
|
4
4
|
export type ReportedActivity = Exclude<AgentActivity, {
|
|
5
|
-
shape: 'turn';
|
|
5
|
+
shape: 'turn' | 'background_run';
|
|
6
6
|
}>;
|
|
7
7
|
/**
|
|
8
8
|
* Restates one progress update in the rail's own vocabulary.
|
|
@@ -7,7 +7,7 @@ export declare class ClaudeProviderAdapter implements ProviderAdapter {
|
|
|
7
7
|
private readonly cli;
|
|
8
8
|
private readonly turnPreparer;
|
|
9
9
|
readonly capabilities: import("../provider-adapter.js").ProviderCapabilities;
|
|
10
|
-
constructor(cli: ClaudeCliClient, turnPreparer: ProviderTurnPreparer);
|
|
10
|
+
constructor(cli: ClaudeCliClient, turnPreparer: Pick<ProviderTurnPreparer, 'prepare'>);
|
|
11
11
|
generateReply(input: ProviderInput, options?: ProviderGenerateOptions): Promise<ProviderReply>;
|
|
12
12
|
cancelTurn(channelId: string): Promise<boolean>;
|
|
13
13
|
dispose(): Promise<void>;
|
|
@@ -7,6 +7,13 @@ export const CLAUDE_HOSTED_PROVIDER_CAPABILITIES = createHostedProviderCapabilit
|
|
|
7
7
|
reason: 'real-media-delivered',
|
|
8
8
|
delivery: ['blob'],
|
|
9
9
|
},
|
|
10
|
+
progressUpdates: {
|
|
11
|
+
support: 'supported',
|
|
12
|
+
},
|
|
13
|
+
backgroundRuns: {
|
|
14
|
+
lifecycle: 'not-supported',
|
|
15
|
+
targetedCancellation: 'not-supported',
|
|
16
|
+
},
|
|
10
17
|
});
|
|
11
18
|
export class ClaudeProviderAdapter {
|
|
12
19
|
cli;
|
|
@@ -13,7 +13,7 @@ import { ProviderTurnCancelledError } from '../provider-adapter.js';
|
|
|
13
13
|
import { readClaudeActivityMetadata } from './activity-metadata.js';
|
|
14
14
|
import { resolveCopilotPermissionResponse } from '../../policy/copilot-permission.js';
|
|
15
15
|
import { buildClaudeSessionPromptAppend } from '../../context/prompt.js';
|
|
16
|
-
import {
|
|
16
|
+
import { isDiscussionOnlyResolvedWorkingFolder } from '../../context/resolved-working-folder.js';
|
|
17
17
|
import { IDLE_BACKEND_SHUTDOWN_DISABLED_MS, IdleBackendShutdownScheduler, } from '../idle-backend-shutdown.js';
|
|
18
18
|
const SESSION_TAINTED_ERRORS = new WeakSet();
|
|
19
19
|
const IDLE_BACKEND_STOPPED_MESSAGE = 'Claude ACP backend stopped after idle timeout';
|
|
@@ -761,7 +761,7 @@ export class ClaudeCliClient {
|
|
|
761
761
|
state.activeTurn = turn;
|
|
762
762
|
try {
|
|
763
763
|
state.cwd = this.resolveSessionCwd(turn.preparedTurn.promptContext);
|
|
764
|
-
state.toolPermissionMode =
|
|
764
|
+
state.toolPermissionMode = isDiscussionOnlyResolvedWorkingFolder(turn.preparedTurn.promptContext)
|
|
765
765
|
? 'deny-all'
|
|
766
766
|
: 'default';
|
|
767
767
|
const sessionPromptAppend = resolveTurnSessionPromptAppend(turn.preparedTurn);
|
|
@@ -939,7 +939,7 @@ export class ClaudeCliClient {
|
|
|
939
939
|
}
|
|
940
940
|
}
|
|
941
941
|
resolveSessionCwd(promptContext) {
|
|
942
|
-
return promptContext?.
|
|
942
|
+
return promptContext?.resolvedWorkingFolder?.rootPath ?? this.runtime.cwd;
|
|
943
943
|
}
|
|
944
944
|
async recycleSessionIfScopeChanged(channelId, state) {
|
|
945
945
|
const session = state.session;
|
|
@@ -1055,6 +1055,7 @@ export class ClaudeCliClient {
|
|
|
1055
1055
|
logger: this.logger,
|
|
1056
1056
|
agentId: this.resolveSessionStoreAgentId()?.trim() || undefined,
|
|
1057
1057
|
channelId: state?.channelId,
|
|
1058
|
+
sessionBound: state !== undefined,
|
|
1058
1059
|
toolPermissionMode: state?.toolPermissionMode ?? 'default',
|
|
1059
1060
|
});
|
|
1060
1061
|
}
|
|
@@ -7,7 +7,7 @@ export declare class CodexProviderAdapter implements ProviderAdapter {
|
|
|
7
7
|
private readonly cli;
|
|
8
8
|
private readonly turnPreparer;
|
|
9
9
|
readonly capabilities: import("../provider-adapter.js").ProviderCapabilities;
|
|
10
|
-
constructor(cli: CodexCliClient, turnPreparer: ProviderTurnPreparer);
|
|
10
|
+
constructor(cli: CodexCliClient, turnPreparer: Pick<ProviderTurnPreparer, 'prepare'>);
|
|
11
11
|
generateReply(input: ProviderInput, options?: ProviderGenerateOptions): Promise<ProviderReply>;
|
|
12
12
|
cancelTurn(channelId: string): Promise<boolean>;
|
|
13
13
|
dispose(): Promise<void>;
|
|
@@ -7,6 +7,13 @@ export const CODEX_HOSTED_PROVIDER_CAPABILITIES = createHostedProviderCapabiliti
|
|
|
7
7
|
reason: 'real-media-delivered',
|
|
8
8
|
delivery: ['blob'],
|
|
9
9
|
},
|
|
10
|
+
progressUpdates: {
|
|
11
|
+
support: 'supported',
|
|
12
|
+
},
|
|
13
|
+
backgroundRuns: {
|
|
14
|
+
lifecycle: 'not-supported',
|
|
15
|
+
targetedCancellation: 'not-supported',
|
|
16
|
+
},
|
|
10
17
|
});
|
|
11
18
|
export class CodexProviderAdapter {
|
|
12
19
|
cli;
|
|
@@ -9,7 +9,7 @@ import { AcpProgressCollector } from '../acp-progress-collector.js';
|
|
|
9
9
|
import { ProviderTurnCancelledError } from '../provider-adapter.js';
|
|
10
10
|
import { assertCodexProjectDocumentSize, buildCodexProjectDocument } from './project-doc.js';
|
|
11
11
|
import { isGatewayCredentialSidecarBasename } from '../../context/injection.js';
|
|
12
|
-
import {
|
|
12
|
+
import { isDiscussionOnlyResolvedWorkingFolder } from '../../context/resolved-working-folder.js';
|
|
13
13
|
import { resolveCopilotPermissionResponse } from '../../policy/copilot-permission.js';
|
|
14
14
|
import { IDLE_BACKEND_SHUTDOWN_DISABLED_MS, IdleBackendShutdownScheduler, } from '../idle-backend-shutdown.js';
|
|
15
15
|
const SESSION_TAINTED_ERRORS = new WeakSet();
|
|
@@ -488,7 +488,7 @@ export class CodexCliClient {
|
|
|
488
488
|
state.activeTurn = turn;
|
|
489
489
|
try {
|
|
490
490
|
state.cwd = await this.resolveSessionCwd(turn.preparedTurn.promptContext);
|
|
491
|
-
state.toolPermissionMode =
|
|
491
|
+
state.toolPermissionMode = isDiscussionOnlyResolvedWorkingFolder(turn.preparedTurn.promptContext)
|
|
492
492
|
? 'deny-all'
|
|
493
493
|
: 'default';
|
|
494
494
|
const projectedPromptContext = await this.refreshProjectedPromptContextBestEffort(turn.channelId, turn.preparedTurn.promptContext);
|
|
@@ -658,7 +658,7 @@ export class CodexCliClient {
|
|
|
658
658
|
}
|
|
659
659
|
}
|
|
660
660
|
async resolveSessionCwd(promptContext) {
|
|
661
|
-
return promptContext?.
|
|
661
|
+
return promptContext?.resolvedWorkingFolder?.rootPath ?? this.runtime.cwd;
|
|
662
662
|
}
|
|
663
663
|
async recycleSessionIfInjectionScopeChanged(channelId, state) {
|
|
664
664
|
const session = state.session;
|
|
@@ -969,7 +969,9 @@ export class CodexCliClient {
|
|
|
969
969
|
policyMode: 'enforce',
|
|
970
970
|
params,
|
|
971
971
|
logger: this.logger,
|
|
972
|
+
agentId: this.resolveSessionStoreAgentId()?.trim() || undefined,
|
|
972
973
|
channelId: state?.channelId,
|
|
974
|
+
sessionBound: state !== undefined,
|
|
973
975
|
toolPermissionMode: state?.toolPermissionMode ?? 'default',
|
|
974
976
|
});
|
|
975
977
|
}
|
|
@@ -1136,6 +1138,7 @@ export class CodexCliClient {
|
|
|
1136
1138
|
this.closeSession(session);
|
|
1137
1139
|
}
|
|
1138
1140
|
this.channels.delete(channelId);
|
|
1141
|
+
this.idleBackendShutdown.reconcile();
|
|
1139
1142
|
}
|
|
1140
1143
|
async shutdownBackend(error) {
|
|
1141
1144
|
if (this.backendClosed) {
|
|
@@ -1194,10 +1197,8 @@ export class CodexCliClient {
|
|
|
1194
1197
|
if (this.pendingSessionStarts.size > 0) {
|
|
1195
1198
|
return false;
|
|
1196
1199
|
}
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
return false;
|
|
1200
|
-
}
|
|
1200
|
+
if (this.channels.size > 0) {
|
|
1201
|
+
return false;
|
|
1201
1202
|
}
|
|
1202
1203
|
return true;
|
|
1203
1204
|
}
|
|
@@ -2,12 +2,12 @@ import { buildAttentionSummaryLines } from '../../context/attention.js';
|
|
|
2
2
|
import { buildCollaborationCapabilityDeclarationSummaryLines, buildMissedCollaborationDiagnosticSummaryLines, } from '../../context/collaboration-capabilities-diagnostics.js';
|
|
3
3
|
import { buildCollaborationOutcomeSummaryLines } from '../../context/collaboration-outcome.js';
|
|
4
4
|
import { MAIN_SESSION_DELEGATION_LINES } from '../../context/main-session-delegation.js';
|
|
5
|
-
import {
|
|
5
|
+
import { buildResolvedWorkingFolderGuidanceLines } from '../../context/resolved-working-folder.js';
|
|
6
6
|
import { buildSkillManualLines, buildSkillManualReadLine } from '../../context/skill-manual.js';
|
|
7
7
|
import { buildTaskThreadCollaborationSummaryLines } from '../../context/task-thread-collaboration.js';
|
|
8
8
|
const PROJECT_DOC_MAX_BYTES = 32 * 1024;
|
|
9
9
|
// Every command the CLI carries needs the gateway credential file, so the capability is only
|
|
10
|
-
// worth naming in a
|
|
10
|
+
// worth naming in a project document that also names one; otherwise the model is handed an invocation
|
|
11
11
|
// template it cannot fill.
|
|
12
12
|
function buildSkillRuntimeLines(context) {
|
|
13
13
|
if (!context?.skillRuntime) {
|
|
@@ -29,15 +29,15 @@ function buildGatewayCredentialLines(context) {
|
|
|
29
29
|
export function buildCodexProjectDocument(context) {
|
|
30
30
|
const sessionDelegationLines = context?.skillRuntime ? [] : MAIN_SESSION_DELEGATION_LINES;
|
|
31
31
|
const lines = [
|
|
32
|
-
'# Borgee channel
|
|
32
|
+
'# Borgee channel session',
|
|
33
33
|
'',
|
|
34
|
-
'This
|
|
34
|
+
'This directory belongs to one Borgee channel session hosted by agents-host.',
|
|
35
35
|
'Keep visible replies concise, do not claim actions you did not perform, and prefer the per-turn prompt when it provides newer turn-local details.',
|
|
36
36
|
'',
|
|
37
37
|
...sessionDelegationLines,
|
|
38
38
|
...(() => {
|
|
39
|
-
const
|
|
40
|
-
return
|
|
39
|
+
const workingFolderLines = buildResolvedWorkingFolderGuidanceLines(context);
|
|
40
|
+
return workingFolderLines.length > 0 ? ['', ...workingFolderLines] : [];
|
|
41
41
|
})(),
|
|
42
42
|
...(() => {
|
|
43
43
|
const collaborationOutcomeLines = buildCollaborationOutcomeSummaryLines(context?.collaborationOutcome);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ProviderAdapter } from '../provider-adapter.js';
|
|
2
|
-
import type { ProviderGenerateOptions, ProviderInput, ProviderReply } from '../../types.js';
|
|
2
|
+
import type { ProviderAutonomousReply, ProviderGenerateOptions, ProviderInput, ProviderReply } from '../../types.js';
|
|
3
3
|
import { ProviderTurnPreparer } from '../../context/turn-preparation.js';
|
|
4
4
|
import { CopilotCliClient } from './cli-client.js';
|
|
5
5
|
export declare const COPILOT_HOSTED_PROVIDER_CAPABILITIES: import("../provider-adapter.js").ProviderCapabilities;
|
|
@@ -7,8 +7,12 @@ export declare class CopilotProviderAdapter implements ProviderAdapter {
|
|
|
7
7
|
private readonly cli;
|
|
8
8
|
private readonly turnPreparer;
|
|
9
9
|
readonly capabilities: import("../provider-adapter.js").ProviderCapabilities;
|
|
10
|
-
constructor(cli: CopilotCliClient, turnPreparer: ProviderTurnPreparer);
|
|
10
|
+
constructor(cli: CopilotCliClient, turnPreparer: Pick<ProviderTurnPreparer, 'prepare'>);
|
|
11
11
|
generateReply(input: ProviderInput, options?: ProviderGenerateOptions): Promise<ProviderReply>;
|
|
12
|
+
onAutonomousReply(handler: ((reply: ProviderAutonomousReply) => void) | undefined): void;
|
|
12
13
|
cancelTurn(channelId: string): Promise<boolean>;
|
|
14
|
+
interruptTurn(channelId: string): Promise<boolean>;
|
|
15
|
+
cancelSession(channelId: string): Promise<boolean>;
|
|
16
|
+
cancelBackgroundRun(channelId: string, providerRunId: string): Promise<boolean>;
|
|
13
17
|
dispose(): Promise<void>;
|
|
14
18
|
}
|
|
@@ -7,6 +7,13 @@ export const COPILOT_HOSTED_PROVIDER_CAPABILITIES = createHostedProviderCapabili
|
|
|
7
7
|
reason: 'real-media-delivered',
|
|
8
8
|
delivery: ['blob'],
|
|
9
9
|
},
|
|
10
|
+
progressUpdates: {
|
|
11
|
+
support: 'supported',
|
|
12
|
+
},
|
|
13
|
+
backgroundRuns: {
|
|
14
|
+
lifecycle: 'supported',
|
|
15
|
+
targetedCancellation: 'supported',
|
|
16
|
+
},
|
|
10
17
|
});
|
|
11
18
|
export class CopilotProviderAdapter {
|
|
12
19
|
cli;
|
|
@@ -25,8 +32,27 @@ export class CopilotProviderAdapter {
|
|
|
25
32
|
// session as it runs, so before this point there is nothing to report.
|
|
26
33
|
return { ...parseProviderReply(text), sessionId: this.cli.sessionIdForChannel(input.channelId) };
|
|
27
34
|
}
|
|
35
|
+
onAutonomousReply(handler) {
|
|
36
|
+
this.cli.onAutonomousReply(handler
|
|
37
|
+
? (reply) => {
|
|
38
|
+
const parsed = parseProviderReply(reply.text);
|
|
39
|
+
if (parsed.text.trim()) {
|
|
40
|
+
handler({ ...reply, text: parsed.text });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
: undefined);
|
|
44
|
+
}
|
|
28
45
|
async cancelTurn(channelId) {
|
|
29
|
-
return this.cli.
|
|
46
|
+
return this.cli.cancelSession(channelId);
|
|
47
|
+
}
|
|
48
|
+
async interruptTurn(channelId) {
|
|
49
|
+
return this.cli.interruptTurn(channelId);
|
|
50
|
+
}
|
|
51
|
+
async cancelSession(channelId) {
|
|
52
|
+
return this.cli.cancelSession(channelId);
|
|
53
|
+
}
|
|
54
|
+
async cancelBackgroundRun(channelId, providerRunId) {
|
|
55
|
+
return this.cli.cancelBackgroundRun(channelId, providerRunId);
|
|
30
56
|
}
|
|
31
57
|
async dispose() {
|
|
32
58
|
await this.cli.dispose();
|
|
@@ -3,9 +3,12 @@ import { PROTOCOL_VERSION, client, methods, ndJsonStream } from '@agentclientpro
|
|
|
3
3
|
import { type DebugLogger } from '../../debug.js';
|
|
4
4
|
import type { InternalPolicyMode } from '../../compatibility-gates.js';
|
|
5
5
|
import type { AuthorizationAuditSinkLike } from '../../policy/authorization-audit.js';
|
|
6
|
-
import type { PreparedProviderTurnInput, ProviderGenerateOptions } from '../../types.js';
|
|
6
|
+
import type { PreparedProviderTurnInput, ProviderAutonomousReply, ProviderGenerateOptions } from '../../types.js';
|
|
7
7
|
import type { CopilotChannelSessionStore } from './session-store.js';
|
|
8
|
-
|
|
8
|
+
import { type CopilotSdkClientFactory } from './sdk-session.js';
|
|
9
|
+
interface CopilotRuntime {
|
|
10
|
+
transport: 'sdk' | 'acp';
|
|
11
|
+
createSdkClient: CopilotSdkClientFactory;
|
|
9
12
|
spawn: typeof spawn;
|
|
10
13
|
client: typeof client;
|
|
11
14
|
ndJsonStream: typeof ndJsonStream;
|
|
@@ -15,6 +18,7 @@ interface CopilotAcpRuntime {
|
|
|
15
18
|
idleSessionTtlMs: number;
|
|
16
19
|
idleBackendShutdownMs: number;
|
|
17
20
|
idleShutdownGracePeriodMs: number;
|
|
21
|
+
sessionCloseTimeoutMs: number;
|
|
18
22
|
shutdownGracePeriodMs: number;
|
|
19
23
|
shutdownForceKillWaitMs: number;
|
|
20
24
|
fetch(input: string | URL, init?: RequestInit): Promise<Response>;
|
|
@@ -29,12 +33,12 @@ interface CopilotHostedImageInputConfig {
|
|
|
29
33
|
agentApiKey?: string;
|
|
30
34
|
}
|
|
31
35
|
/**
|
|
32
|
-
* Persistent
|
|
36
|
+
* Persistent session client for the GitHub Copilot CLI.
|
|
33
37
|
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
+
* Production uses the Copilot SDK headless JSON-RPC transport. The ACP runtime
|
|
39
|
+
* remains injectable for compatibility tests. Both paths reuse one provider
|
|
40
|
+
* session per Borgee channel because the background-agent registry belongs to
|
|
41
|
+
* that session and later read_agent/write_agent turns must keep that identity.
|
|
38
42
|
*/
|
|
39
43
|
export declare class CopilotCliClient {
|
|
40
44
|
private readonly command;
|
|
@@ -45,15 +49,21 @@ export declare class CopilotCliClient {
|
|
|
45
49
|
private readonly imageInputConfig;
|
|
46
50
|
private readonly runtime;
|
|
47
51
|
private readonly channels;
|
|
52
|
+
private readonly channelWorkerRoutes;
|
|
53
|
+
private readonly sessionAuthorizationLeases;
|
|
48
54
|
/**
|
|
49
55
|
* The provider session currently bound to channelId, or undefined before a
|
|
50
56
|
* turn has opened one. Surfaced so agents-host can record which session
|
|
51
57
|
* worked a task without the agent being asked to report its own id.
|
|
52
58
|
*/
|
|
53
59
|
sessionIdForChannel(channelId: string): string | undefined;
|
|
60
|
+
interruptTurn(channelId: string): Promise<boolean>;
|
|
61
|
+
cancelSession(channelId: string): Promise<boolean>;
|
|
54
62
|
cancelTurn(channelId: string): Promise<boolean>;
|
|
63
|
+
cancelBackgroundRun(channelId: string, providerRunId: string): Promise<boolean>;
|
|
55
64
|
private readonly persistedSessions;
|
|
56
65
|
private readonly closingSessions;
|
|
66
|
+
private readonly orphanedSessions;
|
|
57
67
|
private readonly pendingSessionStarts;
|
|
58
68
|
private readonly pendingSessionCloses;
|
|
59
69
|
private readonly pendingSessionStoreOperations;
|
|
@@ -61,6 +71,7 @@ export declare class CopilotCliClient {
|
|
|
61
71
|
private rejectFatalPromise;
|
|
62
72
|
private child?;
|
|
63
73
|
private connection?;
|
|
74
|
+
private sdkClient?;
|
|
64
75
|
private startPromise?;
|
|
65
76
|
private shutdownPromise?;
|
|
66
77
|
private childExitPromise?;
|
|
@@ -71,6 +82,7 @@ export declare class CopilotCliClient {
|
|
|
71
82
|
*/
|
|
72
83
|
private backendGeneration;
|
|
73
84
|
private idleShutdownPromise?;
|
|
85
|
+
private backendShutdownInProgress;
|
|
74
86
|
private fatalError;
|
|
75
87
|
private disposing;
|
|
76
88
|
private backendClosed;
|
|
@@ -78,19 +90,25 @@ export declare class CopilotCliClient {
|
|
|
78
90
|
private sessionStoreLoadPromise;
|
|
79
91
|
private sessionStoreWriteQueue;
|
|
80
92
|
private sessionCapabilities;
|
|
93
|
+
private backendRecycleRequired;
|
|
94
|
+
private autonomousReplyHandler?;
|
|
81
95
|
private readonly idleBackendShutdown;
|
|
82
|
-
constructor(command: string, _ignoredArgs?: string[], runtimeOverrides?: Partial<
|
|
96
|
+
constructor(command: string, _ignoredArgs?: string[], runtimeOverrides?: Partial<CopilotRuntime>, sessionStore?: CopilotChannelSessionStore | undefined, resolveSessionStoreAgentId?: () => string | undefined, logger?: DebugLogger, permissionPolicy?: CopilotPermissionPolicyOptions, imageInputConfig?: CopilotHostedImageInputConfig);
|
|
83
97
|
generateReply(turn: PreparedProviderTurnInput, options?: ProviderGenerateOptions): Promise<string>;
|
|
84
98
|
generateReply(channelId: string, prompt: string, options?: ProviderGenerateOptions): Promise<string>;
|
|
99
|
+
onAutonomousReply(handler: ((reply: ProviderAutonomousReply) => void) | undefined): void;
|
|
85
100
|
dispose(): Promise<void>;
|
|
86
101
|
private ensureStarted;
|
|
87
102
|
private startBackend;
|
|
103
|
+
private startSdkBackend;
|
|
88
104
|
private getOrCreateChannelState;
|
|
105
|
+
private resolveChannelState;
|
|
89
106
|
private processChannelQueue;
|
|
90
107
|
private getOrCreateSession;
|
|
91
108
|
private startFreshSession;
|
|
92
109
|
private restoreOrCreateSession;
|
|
93
110
|
private restoreSession;
|
|
111
|
+
private createSdkSessionAdapter;
|
|
94
112
|
private clearBufferedSessionReplay;
|
|
95
113
|
private resolveSessionCwd;
|
|
96
114
|
private buildPromptInput;
|
|
@@ -99,15 +117,21 @@ export declare class CopilotCliClient {
|
|
|
99
117
|
private runTurn;
|
|
100
118
|
private raceWithFatal;
|
|
101
119
|
private handlePermissionRequest;
|
|
120
|
+
private handleSdkPermissionRequest;
|
|
102
121
|
private failAll;
|
|
103
122
|
private invalidateSession;
|
|
104
123
|
private closeSession;
|
|
124
|
+
private markSessionOrphaned;
|
|
105
125
|
private rejectQueuedTurnsAfterSessionTaint;
|
|
106
126
|
private clearIdleTimer;
|
|
107
|
-
private
|
|
127
|
+
private bindSessionAuthorization;
|
|
128
|
+
private promoteWorkerRoute;
|
|
129
|
+
private releaseWorkerRoute;
|
|
130
|
+
private releaseInactiveWorkerRoute;
|
|
108
131
|
private reconcileIdleChannelState;
|
|
109
132
|
private ensureSessionStoreLoaded;
|
|
110
133
|
private readPersistedSessionId;
|
|
134
|
+
private isPersistentRouteOwner;
|
|
111
135
|
private persistSession;
|
|
112
136
|
private persistSessionBestEffort;
|
|
113
137
|
private clearPersistedSession;
|
|
@@ -119,14 +143,17 @@ export declare class CopilotCliClient {
|
|
|
119
143
|
/**
|
|
120
144
|
* Backend teardown without the fatal latch: the client stays usable and
|
|
121
145
|
* `ensureStarted()` spawns a replacement process on the next turn. Only safe
|
|
122
|
-
* while `isBackendIdle()` holds, because every channel's live
|
|
146
|
+
* while `isBackendIdle()` holds, because every channel's live session is
|
|
123
147
|
* dropped here and no turn is ever replayed onto its successor.
|
|
124
148
|
*/
|
|
125
149
|
private shutdownIdleBackend;
|
|
126
150
|
private isBackendIdle;
|
|
151
|
+
private reconcileBackendLifecycle;
|
|
152
|
+
private releaseBackendAuthorizationLeases;
|
|
127
153
|
private closeBackendProcess;
|
|
128
154
|
private waitForPendingSessionStarts;
|
|
129
155
|
private waitForPendingSessionCloses;
|
|
156
|
+
private waitForSessionClose;
|
|
130
157
|
private waitForChildExit;
|
|
131
158
|
}
|
|
132
159
|
export {};
|