@borgee/agents-host 0.2.44 → 0.2.62
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 +24 -27
- package/dist/agents-host.d.ts +27 -5
- package/dist/agents-host.js +266 -201
- package/dist/chat/chat-control-plane.d.ts +3 -0
- package/dist/chat/sdk-chat-control-plane.d.ts +4 -3
- package/dist/chat/sdk-chat-control-plane.js +3 -0
- package/dist/cli.js +1 -5
- package/dist/compatibility-gates.d.ts +4 -0
- package/dist/compatibility-gates.js +20 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.js +21 -0
- package/dist/context/claude-file-brief.d.ts +2 -0
- package/dist/context/claude-file-brief.js +83 -0
- package/dist/context/compaction.d.ts +20 -0
- package/dist/context/compaction.js +59 -0
- package/dist/context/injection.d.ts +58 -7
- package/dist/context/injection.js +370 -36
- package/dist/context/main-session-delegation.d.ts +1 -1
- package/dist/context/projection-strategy.d.ts +24 -0
- package/dist/context/projection-strategy.js +90 -0
- package/dist/context/prompt.d.ts +16 -1
- package/dist/context/prompt.js +464 -26
- package/dist/context/resolved-workspace.d.ts +3 -0
- package/dist/context/resolved-workspace.js +106 -0
- package/dist/context/skill-manual.d.ts +1 -0
- package/dist/context/skill-manual.js +4 -1
- package/dist/context/turn-preparation.d.ts +8 -2
- package/dist/context/turn-preparation.js +64 -14
- package/dist/gateway/localhost-gateway.js +4 -5
- package/dist/local-config.js +11 -1
- package/dist/managed-daemon.js +127 -9
- package/dist/plugin-sdk.js +264 -364
- package/dist/plugin-sdk.js.map +4 -4
- package/dist/policy/copilot-permission.d.ts +1 -0
- package/dist/policy/copilot-permission.js +18 -0
- package/dist/policy/gateway-authorization.js +2 -2
- package/dist/progress-to-activity.d.ts +16 -0
- package/dist/progress-to-activity.js +24 -0
- package/dist/projection-strategy-values.d.ts +4 -0
- package/dist/projection-strategy-values.js +28 -0
- package/dist/providers/acp-progress-collector.d.ts +44 -0
- package/dist/providers/acp-progress-collector.js +130 -0
- package/dist/providers/awaiting-user.d.ts +2 -3
- package/dist/providers/awaiting-user.js +5 -7
- package/dist/providers/claude/activity-metadata.d.ts +14 -0
- package/dist/providers/claude/activity-metadata.js +81 -0
- package/dist/providers/claude/cli-client.d.ts +2 -3
- package/dist/providers/claude/cli-client.js +220 -120
- package/dist/providers/codex/cli-client.d.ts +2 -0
- package/dist/providers/codex/cli-client.js +28 -104
- package/dist/providers/codex/project-doc.js +12 -11
- package/dist/providers/copilot/cli-client.d.ts +1 -1
- package/dist/providers/copilot/cli-client.js +12 -86
- package/dist/providers/create-provider.d.ts +2 -0
- package/dist/providers/create-provider.js +22 -4
- package/dist/state-paths.d.ts +1 -1
- package/dist/state-paths.js +3 -3
- package/dist/task-thread-resolution.d.ts +3 -2
- package/dist/types.d.ts +143 -6
- package/package.json +2 -2
- package/skills/borgee-agent/SKILL.md +12 -4
- package/skills/borgee-agent/references/errors.md +2 -2
- package/skills/borgee-agent/references/task-properties.md +6 -2
- package/dist/durable-cursor-store.d.ts +0 -5
- package/dist/durable-cursor-store.js +0 -7
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import { Readable, Writable } from 'node:stream';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
2
4
|
import { createRequire } from 'node:module';
|
|
3
5
|
import { dirname, resolve } from 'node:path';
|
|
4
6
|
import spawn from 'cross-spawn';
|
|
7
|
+
import { DEFAULT_PROJECTION_STRATEGY, normalizeProjectionStrategy, } from '../../projection-strategy-values.js';
|
|
5
8
|
import { assertClaudeCommandCompatibility, DEFAULT_CLAUDE_COMMAND, isLegacyClaudeCompatibilityAlias, } from '../../config.js';
|
|
6
9
|
import { PROTOCOL_VERSION, client, methods, ndJsonStream, } from '@agentclientprotocol/sdk';
|
|
7
10
|
import { HostLogger, summarizeChildStderr, summarizeError } from '../../debug.js';
|
|
11
|
+
import { AcpProgressCollector } from '../acp-progress-collector.js';
|
|
12
|
+
import { readClaudeActivityMetadata } from './activity-metadata.js';
|
|
8
13
|
import { resolveCopilotPermissionResponse } from '../../policy/copilot-permission.js';
|
|
9
|
-
import {
|
|
14
|
+
import { buildClaudeSessionPromptAppend } from '../../context/prompt.js';
|
|
15
|
+
import { isDiscussionOnlyResolvedWorkspace } from '../../context/resolved-workspace.js';
|
|
10
16
|
import { IDLE_BACKEND_SHUTDOWN_DISABLED_MS, IdleBackendShutdownScheduler, } from '../idle-backend-shutdown.js';
|
|
11
17
|
const SESSION_TAINTED_ERRORS = new WeakSet();
|
|
12
18
|
const IDLE_BACKEND_STOPPED_MESSAGE = 'Claude ACP backend stopped after idle timeout';
|
|
@@ -24,6 +30,7 @@ const DEFAULT_SESSION_CAPABILITIES = {
|
|
|
24
30
|
loadSession: false,
|
|
25
31
|
resumeSession: false,
|
|
26
32
|
};
|
|
33
|
+
const CLAUDE_SDK_COMPACTION_EXPERIMENT_CONTEXT_TOKEN_THRESHOLD_ENV = 'CLAUDE_SDK_COMPACTION_EXPERIMENT_CONTEXT_TOKEN_THRESHOLD';
|
|
27
34
|
const require = createRequire(import.meta.url);
|
|
28
35
|
const DEFAULT_RUNTIME = {
|
|
29
36
|
spawn,
|
|
@@ -111,82 +118,95 @@ class ClaudeCliProcessError extends Error {
|
|
|
111
118
|
this.stderrLineCount = stderrLineCount;
|
|
112
119
|
}
|
|
113
120
|
}
|
|
114
|
-
function
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
case 'completed':
|
|
118
|
-
return normalizedTitle ? `Completed ${normalizedTitle}` : 'Completed tool call';
|
|
119
|
-
case 'failed':
|
|
120
|
-
return normalizedTitle ? `Failed ${normalizedTitle}` : 'Tool call failed';
|
|
121
|
-
case 'pending':
|
|
122
|
-
case 'in_progress':
|
|
123
|
-
case undefined:
|
|
124
|
-
case null:
|
|
125
|
-
return normalizedTitle ? `Running ${normalizedTitle}…` : 'Running tool…';
|
|
126
|
-
default:
|
|
127
|
-
return normalizedTitle ? `${status} ${normalizedTitle}` : status;
|
|
121
|
+
function parseCompactionBanner(text) {
|
|
122
|
+
if (/(?:^|\n\n)Compacting\.\.\.\s*$/u.test(text)) {
|
|
123
|
+
return { stage: 'started' };
|
|
128
124
|
}
|
|
125
|
+
if (/(?:^|\n\n)Compacting completed\.\s*$/u.test(text)) {
|
|
126
|
+
return { stage: 'completed' };
|
|
127
|
+
}
|
|
128
|
+
const match = /(?:^|\n\n)Compacting failed(?::\s*(.+?)|\.)\s*$/u.exec(text);
|
|
129
|
+
if (!match) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
stage: 'failed',
|
|
134
|
+
...(match?.[1] ? { failureReason: match[1].trim() } : {}),
|
|
135
|
+
};
|
|
129
136
|
}
|
|
130
|
-
|
|
131
|
-
const current = entries.find((entry) => entry.status === 'in_progress') ??
|
|
132
|
-
entries.find((entry) => entry.status === 'pending') ??
|
|
133
|
-
entries[0];
|
|
134
|
-
return hasVisibleText(current?.content) ? `Plan: ${current.content.trim()}` : null;
|
|
135
|
-
}
|
|
136
|
-
class ClaudeProgressCollector {
|
|
137
|
+
class ClaudeCompactionObserver {
|
|
137
138
|
onProgress;
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
139
|
+
answerText = '';
|
|
140
|
+
compactionState = 'idle';
|
|
141
|
+
lastCompactionBannerKey = null;
|
|
141
142
|
constructor(onProgress) {
|
|
142
143
|
this.onProgress = onProgress;
|
|
143
144
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if (update.update.content.type !== 'text') {
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
this.publicText += update.update.content.text;
|
|
151
|
-
this.publish(this.publicText);
|
|
152
|
-
return;
|
|
153
|
-
case 'tool_call':
|
|
154
|
-
this.toolTitles.set(update.update.toolCallId, update.update.title);
|
|
155
|
-
this.publishFallback(formatToolProgress(update.update.title, update.update.status));
|
|
156
|
-
return;
|
|
157
|
-
case 'tool_call_update': {
|
|
158
|
-
const nextTitle = update.update.title ?? this.toolTitles.get(update.update.toolCallId);
|
|
159
|
-
if (hasVisibleText(nextTitle)) {
|
|
160
|
-
this.toolTitles.set(update.update.toolCallId, nextTitle);
|
|
161
|
-
}
|
|
162
|
-
this.publishFallback(formatToolProgress(nextTitle, update.update.status));
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
case 'plan':
|
|
166
|
-
this.publishFallback(formatPlanProgress(update.update.entries.map((entry) => ({
|
|
167
|
-
content: entry.content,
|
|
168
|
-
status: entry.status,
|
|
169
|
-
}))));
|
|
170
|
-
return;
|
|
171
|
-
default:
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
getFinalText() {
|
|
176
|
-
return this.publicText.trim();
|
|
145
|
+
consumeAnswerChunk(chunk) {
|
|
146
|
+
this.answerText += chunk;
|
|
147
|
+
this.publishCompactionFromText(this.answerText);
|
|
177
148
|
}
|
|
178
|
-
|
|
179
|
-
if (
|
|
149
|
+
consumeUsageUpdate(usedTokens, contextWindowTokens) {
|
|
150
|
+
if (!this.onProgress || this.compactionState !== 'completed-awaiting-usage') {
|
|
180
151
|
return;
|
|
181
152
|
}
|
|
182
|
-
this.
|
|
153
|
+
this.compactionState = 'idle';
|
|
154
|
+
this.onProgress({
|
|
155
|
+
type: 'compaction',
|
|
156
|
+
compaction: {
|
|
157
|
+
provider: 'claude',
|
|
158
|
+
stage: 'completed',
|
|
159
|
+
usedTokens,
|
|
160
|
+
contextWindowTokens,
|
|
161
|
+
},
|
|
162
|
+
});
|
|
183
163
|
}
|
|
184
|
-
|
|
185
|
-
if (!this.onProgress
|
|
164
|
+
publishCompactionFromText(text) {
|
|
165
|
+
if (!this.onProgress) {
|
|
186
166
|
return;
|
|
187
167
|
}
|
|
188
|
-
|
|
189
|
-
|
|
168
|
+
const banner = parseCompactionBanner(text);
|
|
169
|
+
if (!banner) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const bannerKey = `${banner.stage}:${banner.stage === 'failed' ? banner.failureReason ?? '' : ''}:${text.trimEnd().length}`;
|
|
173
|
+
if (bannerKey === this.lastCompactionBannerKey) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
this.lastCompactionBannerKey = bannerKey;
|
|
177
|
+
switch (banner.stage) {
|
|
178
|
+
case 'started':
|
|
179
|
+
this.compactionState = 'active';
|
|
180
|
+
this.onProgress({
|
|
181
|
+
type: 'compaction',
|
|
182
|
+
compaction: {
|
|
183
|
+
provider: 'claude',
|
|
184
|
+
stage: 'started',
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
return;
|
|
188
|
+
case 'completed':
|
|
189
|
+
this.compactionState = 'completed-awaiting-usage';
|
|
190
|
+
this.onProgress({
|
|
191
|
+
type: 'compaction',
|
|
192
|
+
compaction: {
|
|
193
|
+
provider: 'claude',
|
|
194
|
+
stage: 'completed',
|
|
195
|
+
},
|
|
196
|
+
});
|
|
197
|
+
return;
|
|
198
|
+
case 'failed':
|
|
199
|
+
this.compactionState = 'idle';
|
|
200
|
+
this.onProgress({
|
|
201
|
+
type: 'compaction',
|
|
202
|
+
compaction: {
|
|
203
|
+
provider: 'claude',
|
|
204
|
+
stage: 'failed',
|
|
205
|
+
...(banner.failureReason ? { failureReason: banner.failureReason } : {}),
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
190
210
|
}
|
|
191
211
|
}
|
|
192
212
|
function createDeferredTurn(channelId, preparedTurn, sessionPersistence, options) {
|
|
@@ -290,39 +310,65 @@ function resolveClaudeLaunch(command, args, baseCwd) {
|
|
|
290
310
|
args: args.map((arg) => (isRelativePathLike(arg) ? resolve(baseCwd, arg) : arg)),
|
|
291
311
|
};
|
|
292
312
|
}
|
|
293
|
-
|
|
294
|
-
|
|
313
|
+
function resolveClaudeSdkCompactionControl() {
|
|
314
|
+
const rawThreshold = process.env[CLAUDE_SDK_COMPACTION_EXPERIMENT_CONTEXT_TOKEN_THRESHOLD_ENV];
|
|
315
|
+
if (!hasVisibleText(rawThreshold)) {
|
|
295
316
|
return undefined;
|
|
296
317
|
}
|
|
297
|
-
const
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
if (promptContext.skillRuntime && promptContext.gatewayCredentialPath) {
|
|
301
|
-
lines.push(...buildSkillManualLines(promptContext.skillRuntime));
|
|
302
|
-
}
|
|
303
|
-
if (promptContext.localhostGateway && promptContext.gatewayCredentialPath) {
|
|
304
|
-
lines.push(`Gateway credential file for this session: ${promptContext.gatewayCredentialPath}`);
|
|
305
|
-
}
|
|
306
|
-
if (promptContext.taskWorkspace) {
|
|
307
|
-
lines.push(`Task-scoped writable workspace root: ${promptContext.taskWorkspace.rootPath}.`);
|
|
308
|
-
lines.push('This writable workspace is local to agents-host for the current task thread and does not imply that the target repository is already checked out there.');
|
|
318
|
+
const parsedThreshold = Number(rawThreshold.trim());
|
|
319
|
+
if (!Number.isSafeInteger(parsedThreshold) || parsedThreshold <= 0) {
|
|
320
|
+
throw new Error(`Invalid ${CLAUDE_SDK_COMPACTION_EXPERIMENT_CONTEXT_TOKEN_THRESHOLD_ENV}: expected a positive integer context token threshold for the experiment-scoped Claude SDK compaction opt-in`);
|
|
309
321
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
322
|
+
return {
|
|
323
|
+
enabled: true,
|
|
324
|
+
context_token_threshold: parsedThreshold,
|
|
325
|
+
};
|
|
314
326
|
}
|
|
315
|
-
function buildClaudeSessionMeta(
|
|
316
|
-
const
|
|
317
|
-
if (!systemPromptAppend) {
|
|
327
|
+
function buildClaudeSessionMeta(systemPromptAppend) {
|
|
328
|
+
const sdkCompactionControl = resolveClaudeSdkCompactionControl();
|
|
329
|
+
if (!systemPromptAppend && !sdkCompactionControl) {
|
|
318
330
|
return undefined;
|
|
319
331
|
}
|
|
320
332
|
return {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
333
|
+
...(systemPromptAppend
|
|
334
|
+
? {
|
|
335
|
+
systemPrompt: {
|
|
336
|
+
append: systemPromptAppend,
|
|
337
|
+
},
|
|
338
|
+
}
|
|
339
|
+
: {}),
|
|
340
|
+
...(sdkCompactionControl
|
|
341
|
+
? {
|
|
342
|
+
claudeCode: {
|
|
343
|
+
options: {
|
|
344
|
+
compaction_control: sdkCompactionControl,
|
|
345
|
+
},
|
|
346
|
+
},
|
|
347
|
+
}
|
|
348
|
+
: {}),
|
|
324
349
|
};
|
|
325
350
|
}
|
|
351
|
+
function buildFallbackClaudeSessionPrompt(agentName, promptContext, promptStrategy) {
|
|
352
|
+
if (!promptContext) {
|
|
353
|
+
return undefined;
|
|
354
|
+
}
|
|
355
|
+
return buildClaudeSessionPromptAppend({
|
|
356
|
+
agentName: agentName ?? 'Borgee agent',
|
|
357
|
+
promptContext,
|
|
358
|
+
promptStrategy,
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
function resolveProjectionStrategy(promptStrategy) {
|
|
362
|
+
return normalizeProjectionStrategy(promptStrategy) ?? DEFAULT_PROJECTION_STRATEGY;
|
|
363
|
+
}
|
|
364
|
+
function resolveTurnSessionPromptAppend(turn) {
|
|
365
|
+
const promptStrategy = resolveProjectionStrategy(turn.projectionStrategy);
|
|
366
|
+
if (promptStrategy === 'turn-full') {
|
|
367
|
+
return undefined;
|
|
368
|
+
}
|
|
369
|
+
return turn.claudeSessionPromptAppend
|
|
370
|
+
?? buildFallbackClaudeSessionPrompt(turn.agentName, turn.promptContext, promptStrategy);
|
|
371
|
+
}
|
|
326
372
|
function resolveSessionAdditionalDirectories(promptContext) {
|
|
327
373
|
const directories = new Set();
|
|
328
374
|
if (promptContext?.channelContextPayloadPath) {
|
|
@@ -336,27 +382,68 @@ function resolveSessionAdditionalDirectories(promptContext) {
|
|
|
336
382
|
}
|
|
337
383
|
return directories.size > 0 ? [...directories].sort((left, right) => left.localeCompare(right)) : undefined;
|
|
338
384
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
385
|
+
function hashSessionAppend(systemPromptAppend) {
|
|
386
|
+
if (!systemPromptAppend) {
|
|
387
|
+
return undefined;
|
|
388
|
+
}
|
|
389
|
+
return createHash('sha256').update(systemPromptAppend).digest('hex');
|
|
390
|
+
}
|
|
391
|
+
function resolveSkillManualContentHash(promptContext) {
|
|
392
|
+
const skillMarkdownPath = promptContext?.skillRuntime?.skillMarkdownPath;
|
|
393
|
+
if (!skillMarkdownPath) {
|
|
394
|
+
return undefined;
|
|
395
|
+
}
|
|
396
|
+
try {
|
|
397
|
+
return createHash('sha256')
|
|
398
|
+
.update(skillMarkdownPath)
|
|
399
|
+
.update('\0')
|
|
400
|
+
.update(readFileSync(skillMarkdownPath))
|
|
401
|
+
.digest('hex');
|
|
402
|
+
}
|
|
403
|
+
catch {
|
|
404
|
+
return `unreadable:${skillMarkdownPath}`;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
function resolveSessionVisibilityKey(promptContext, systemPromptAppend, promptStrategy) {
|
|
408
|
+
const resolvedPromptStrategy = resolveProjectionStrategy(promptStrategy);
|
|
409
|
+
const explicitPromptStrategy = resolvedPromptStrategy === DEFAULT_PROJECTION_STRATEGY && promptStrategy == null
|
|
410
|
+
? undefined
|
|
411
|
+
: resolvedPromptStrategy === DEFAULT_PROJECTION_STRATEGY
|
|
412
|
+
? undefined
|
|
413
|
+
: resolvedPromptStrategy;
|
|
347
414
|
const additionalDirectories = resolveSessionAdditionalDirectories(promptContext);
|
|
348
|
-
|
|
415
|
+
const sessionAppendHash = hashSessionAppend(systemPromptAppend);
|
|
416
|
+
const skillManualContentHash = resolveSkillManualContentHash(promptContext);
|
|
417
|
+
const projectedBriefHash = promptContext?.claudeProjectedBriefHash;
|
|
418
|
+
if (!additionalDirectories
|
|
419
|
+
&& !sessionAppendHash
|
|
420
|
+
&& !skillManualContentHash
|
|
421
|
+
&& !projectedBriefHash
|
|
422
|
+
&& !promptContext?.gatewayCredentialPath
|
|
423
|
+
&& !explicitPromptStrategy) {
|
|
349
424
|
return undefined;
|
|
350
425
|
}
|
|
426
|
+
if (sessionAppendHash) {
|
|
427
|
+
return JSON.stringify({
|
|
428
|
+
...(additionalDirectories ? { additionalDirectories } : {}),
|
|
429
|
+
sessionAppendHash,
|
|
430
|
+
...(skillManualContentHash ? { skillManualContentHash } : {}),
|
|
431
|
+
...(projectedBriefHash ? { projectedBriefHash } : {}),
|
|
432
|
+
...(explicitPromptStrategy ? { promptStrategy: explicitPromptStrategy } : {}),
|
|
433
|
+
});
|
|
434
|
+
}
|
|
351
435
|
return JSON.stringify({
|
|
352
|
-
additionalDirectories,
|
|
436
|
+
...(additionalDirectories ? { additionalDirectories } : {}),
|
|
437
|
+
...(skillManualContentHash ? { skillManualContentHash } : {}),
|
|
353
438
|
...(promptContext?.gatewayCredentialPath
|
|
354
439
|
? { gatewayCredentialPath: promptContext.gatewayCredentialPath }
|
|
355
440
|
: {}),
|
|
441
|
+
...(projectedBriefHash ? { projectedBriefHash } : {}),
|
|
442
|
+
...(explicitPromptStrategy ? { promptStrategy: explicitPromptStrategy } : {}),
|
|
356
443
|
});
|
|
357
444
|
}
|
|
358
|
-
function buildSessionRequest(cwd,
|
|
359
|
-
const meta = buildClaudeSessionMeta(
|
|
445
|
+
function buildSessionRequest(cwd, systemPromptAppend, additionalDirectories) {
|
|
446
|
+
const meta = buildClaudeSessionMeta(systemPromptAppend);
|
|
360
447
|
return {
|
|
361
448
|
cwd,
|
|
362
449
|
mcpServers: [],
|
|
@@ -656,10 +743,14 @@ export class ClaudeCliClient {
|
|
|
656
743
|
state.activeTurn = turn;
|
|
657
744
|
try {
|
|
658
745
|
state.cwd = this.resolveSessionCwd(turn.preparedTurn.promptContext);
|
|
659
|
-
state.
|
|
746
|
+
state.toolPermissionMode = isDiscussionOnlyResolvedWorkspace(turn.preparedTurn.promptContext)
|
|
747
|
+
? 'deny-all'
|
|
748
|
+
: 'default';
|
|
749
|
+
const sessionPromptAppend = resolveTurnSessionPromptAppend(turn.preparedTurn);
|
|
750
|
+
state.visibilityKey = resolveSessionVisibilityKey(turn.preparedTurn.promptContext, sessionPromptAppend, turn.preparedTurn.projectionStrategy);
|
|
660
751
|
await this.ensureStarted();
|
|
661
752
|
await this.recycleSessionIfScopeChanged(channelId, state);
|
|
662
|
-
const session = await this.getOrCreateSession(channelId, state, turn.preparedTurn
|
|
753
|
+
const session = await this.getOrCreateSession(channelId, state, turn.preparedTurn, sessionPromptAppend);
|
|
663
754
|
let reply;
|
|
664
755
|
try {
|
|
665
756
|
const promptInput = await this.buildPromptInput(turn.preparedTurn);
|
|
@@ -703,7 +794,7 @@ export class ClaudeCliClient {
|
|
|
703
794
|
}
|
|
704
795
|
})();
|
|
705
796
|
}
|
|
706
|
-
async getOrCreateSession(channelId, state,
|
|
797
|
+
async getOrCreateSession(channelId, state, turn, systemPromptAppend) {
|
|
707
798
|
if (state.session) {
|
|
708
799
|
return state.session;
|
|
709
800
|
}
|
|
@@ -717,10 +808,10 @@ export class ClaudeCliClient {
|
|
|
717
808
|
const persistedSessionId = state.sessionPersistence === 'persistent'
|
|
718
809
|
? await this.readPersistedSessionId(channelId, cwd, state.visibilityKey)
|
|
719
810
|
: undefined;
|
|
720
|
-
const additionalDirectories = resolveSessionAdditionalDirectories(promptContext);
|
|
811
|
+
const additionalDirectories = resolveSessionAdditionalDirectories(turn.promptContext);
|
|
721
812
|
const sessionPromise = persistedSessionId
|
|
722
|
-
? this.restoreOrCreateSession(channelId, persistedSessionId, cwd,
|
|
723
|
-
: this.startFreshSession(channelId, cwd,
|
|
813
|
+
? this.restoreOrCreateSession(channelId, persistedSessionId, cwd, systemPromptAppend, additionalDirectories)
|
|
814
|
+
: this.startFreshSession(channelId, cwd, systemPromptAppend, additionalDirectories);
|
|
724
815
|
this.pendingSessionStarts.add(sessionPromise);
|
|
725
816
|
state.sessionPromise = sessionPromise;
|
|
726
817
|
let sessionAdopted = false;
|
|
@@ -759,11 +850,11 @@ export class ClaudeCliClient {
|
|
|
759
850
|
}
|
|
760
851
|
}
|
|
761
852
|
}
|
|
762
|
-
async startFreshSession(channelId, cwd,
|
|
853
|
+
async startFreshSession(channelId, cwd, systemPromptAppend, additionalDirectories) {
|
|
763
854
|
if (!this.connection) {
|
|
764
855
|
throw new Error('Claude ACP connection is not available');
|
|
765
856
|
}
|
|
766
|
-
const session = await this.connection.agent.buildSession(buildSessionRequest(cwd,
|
|
857
|
+
const session = await this.connection.agent.buildSession(buildSessionRequest(cwd, systemPromptAppend, additionalDirectories)).start();
|
|
767
858
|
this.logger?.debug('started fresh Claude ACP session', { channelId, cwd });
|
|
768
859
|
const state = this.channels.get(channelId);
|
|
769
860
|
if (state?.sessionPersistence === 'persistent') {
|
|
@@ -771,13 +862,13 @@ export class ClaudeCliClient {
|
|
|
771
862
|
}
|
|
772
863
|
return session;
|
|
773
864
|
}
|
|
774
|
-
async restoreOrCreateSession(channelId, sessionId, cwd,
|
|
865
|
+
async restoreOrCreateSession(channelId, sessionId, cwd, systemPromptAppend, additionalDirectories) {
|
|
775
866
|
if (!this.sessionCapabilities.resumeSession) {
|
|
776
867
|
this.logger?.debug('Claude ACP restore unsupported; starting fresh session', { channelId });
|
|
777
|
-
return this.startFreshSession(channelId, cwd,
|
|
868
|
+
return this.startFreshSession(channelId, cwd, systemPromptAppend, additionalDirectories);
|
|
778
869
|
}
|
|
779
870
|
try {
|
|
780
|
-
const session = await this.restoreSession(sessionId, cwd,
|
|
871
|
+
const session = await this.restoreSession(sessionId, cwd, systemPromptAppend, additionalDirectories);
|
|
781
872
|
this.logger?.debug('restored Claude ACP session', {
|
|
782
873
|
channelId,
|
|
783
874
|
restoreMethod: this.sessionCapabilities.resumeSession ? 'session/resume' : 'session/load',
|
|
@@ -790,10 +881,10 @@ export class ClaudeCliClient {
|
|
|
790
881
|
}
|
|
791
882
|
this.logger?.debug('discarded stale Claude ACP session and started fresh', { channelId });
|
|
792
883
|
await this.clearPersistedSessionBestEffort(channelId);
|
|
793
|
-
return this.startFreshSession(channelId, cwd,
|
|
884
|
+
return this.startFreshSession(channelId, cwd, systemPromptAppend, additionalDirectories);
|
|
794
885
|
}
|
|
795
886
|
}
|
|
796
|
-
async restoreSession(sessionId, cwd,
|
|
887
|
+
async restoreSession(sessionId, cwd, systemPromptAppend, additionalDirectories) {
|
|
797
888
|
if (!this.connection) {
|
|
798
889
|
throw new Error('Claude ACP connection is not available');
|
|
799
890
|
}
|
|
@@ -805,7 +896,7 @@ export class ClaudeCliClient {
|
|
|
805
896
|
try {
|
|
806
897
|
const request = {
|
|
807
898
|
sessionId,
|
|
808
|
-
...buildSessionRequest(cwd,
|
|
899
|
+
...buildSessionRequest(cwd, systemPromptAppend, additionalDirectories),
|
|
809
900
|
};
|
|
810
901
|
if (this.sessionCapabilities.resumeSession) {
|
|
811
902
|
await this.connection.agent.request(this.runtime.methods.agent.session.resume, request);
|
|
@@ -830,7 +921,7 @@ export class ClaudeCliClient {
|
|
|
830
921
|
}
|
|
831
922
|
}
|
|
832
923
|
resolveSessionCwd(promptContext) {
|
|
833
|
-
return promptContext?.
|
|
924
|
+
return promptContext?.resolvedWorkspace?.rootPath ?? this.runtime.cwd;
|
|
834
925
|
}
|
|
835
926
|
async recycleSessionIfScopeChanged(channelId, state) {
|
|
836
927
|
const session = state.session;
|
|
@@ -862,7 +953,8 @@ export class ClaudeCliClient {
|
|
|
862
953
|
const promptFailure = new Promise((_, reject) => {
|
|
863
954
|
void promptPromise.catch((error) => reject(markSessionTainted(error)));
|
|
864
955
|
});
|
|
865
|
-
const collector = new
|
|
956
|
+
const collector = new AcpProgressCollector(options?.onProgress, readClaudeActivityMetadata);
|
|
957
|
+
const compactionObserver = new ClaudeCompactionObserver(options?.onProgress);
|
|
866
958
|
for (;;) {
|
|
867
959
|
let update;
|
|
868
960
|
try {
|
|
@@ -888,6 +980,12 @@ export class ClaudeCliClient {
|
|
|
888
980
|
}
|
|
889
981
|
return output;
|
|
890
982
|
}
|
|
983
|
+
if (update.update.sessionUpdate === 'agent_message_chunk' && update.update.content.type === 'text') {
|
|
984
|
+
compactionObserver.consumeAnswerChunk(update.update.content.text);
|
|
985
|
+
}
|
|
986
|
+
if (update.update.sessionUpdate === 'usage_update') {
|
|
987
|
+
compactionObserver.consumeUsageUpdate(update.update.used, update.update.size);
|
|
988
|
+
}
|
|
891
989
|
collector.consume(update);
|
|
892
990
|
}
|
|
893
991
|
}
|
|
@@ -926,13 +1024,15 @@ export class ClaudeCliClient {
|
|
|
926
1024
|
this.shutdownPromise = this.shutdownBackend(error);
|
|
927
1025
|
}
|
|
928
1026
|
handlePermissionRequest(params) {
|
|
1027
|
+
const state = this.findChannelStateBySessionId(params.sessionId);
|
|
929
1028
|
return resolveCopilotPermissionResponse({
|
|
930
1029
|
gateEnabled: true,
|
|
931
1030
|
policyMode: 'enforce',
|
|
932
1031
|
params,
|
|
933
1032
|
logger: this.logger,
|
|
934
1033
|
agentId: this.resolveSessionStoreAgentId()?.trim() || undefined,
|
|
935
|
-
channelId:
|
|
1034
|
+
channelId: state?.channelId,
|
|
1035
|
+
toolPermissionMode: state?.toolPermissionMode ?? 'default',
|
|
936
1036
|
});
|
|
937
1037
|
}
|
|
938
1038
|
invalidateSession(channelId, state, session) {
|
|
@@ -984,10 +1084,10 @@ export class ClaudeCliClient {
|
|
|
984
1084
|
}
|
|
985
1085
|
state.queue.length = 0;
|
|
986
1086
|
}
|
|
987
|
-
|
|
1087
|
+
findChannelStateBySessionId(sessionId) {
|
|
988
1088
|
for (const state of this.channels.values()) {
|
|
989
1089
|
if (state.session?.sessionId === sessionId) {
|
|
990
|
-
return state
|
|
1090
|
+
return state;
|
|
991
1091
|
}
|
|
992
1092
|
}
|
|
993
1093
|
return undefined;
|
|
@@ -108,6 +108,8 @@ export declare class CodexCliClient {
|
|
|
108
108
|
private failAll;
|
|
109
109
|
private closeSession;
|
|
110
110
|
private clearIdleTimer;
|
|
111
|
+
private handlePermissionRequest;
|
|
112
|
+
private findChannelStateBySessionId;
|
|
111
113
|
private reconcileIdleChannelState;
|
|
112
114
|
private ensureSessionStoreLoaded;
|
|
113
115
|
private readPersistedSessionId;
|