@posthog/agent 2.3.67 → 2.3.73
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/dist/adapters/claude/permissions/permission-options.js +12 -2
- package/dist/adapters/claude/permissions/permission-options.js.map +1 -1
- package/dist/adapters/claude/session/jsonl-hydration.js.map +1 -1
- package/dist/agent.js +239 -70
- package/dist/agent.js.map +1 -1
- package/dist/claude-cli/cli.js +4002 -2916
- package/dist/claude-cli/vendor/audio-capture/arm64-darwin/audio-capture.node +0 -0
- package/dist/claude-cli/vendor/audio-capture/arm64-linux/audio-capture.node +0 -0
- package/dist/claude-cli/vendor/audio-capture/arm64-win32/audio-capture.node +0 -0
- package/dist/claude-cli/vendor/audio-capture/x64-darwin/audio-capture.node +0 -0
- package/dist/claude-cli/vendor/audio-capture/x64-linux/audio-capture.node +0 -0
- package/dist/claude-cli/vendor/audio-capture/x64-win32/audio-capture.node +0 -0
- package/dist/claude-cli/vendor/tree-sitter-bash/arm64-darwin/tree-sitter-bash.node +0 -0
- package/dist/claude-cli/vendor/tree-sitter-bash/arm64-linux/tree-sitter-bash.node +0 -0
- package/dist/claude-cli/vendor/tree-sitter-bash/arm64-win32/tree-sitter-bash.node +0 -0
- package/dist/claude-cli/vendor/tree-sitter-bash/x64-darwin/tree-sitter-bash.node +0 -0
- package/dist/claude-cli/vendor/tree-sitter-bash/x64-linux/tree-sitter-bash.node +0 -0
- package/dist/claude-cli/vendor/tree-sitter-bash/x64-win32/tree-sitter-bash.node +0 -0
- package/dist/posthog-api.js +3 -3
- package/dist/posthog-api.js.map +1 -1
- package/dist/server/agent-server.js +239 -70
- package/dist/server/agent-server.js.map +1 -1
- package/dist/server/bin.cjs +239 -70
- package/dist/server/bin.cjs.map +1 -1
- package/package.json +3 -3
- package/src/adapters/base-acp-agent.ts +11 -2
- package/src/adapters/claude/UPSTREAM.md +3 -4
- package/src/adapters/claude/claude-agent.ts +217 -35
- package/src/adapters/claude/conversion/sdk-to-acp.ts +2 -25
- package/src/adapters/claude/permissions/permission-handlers.ts +5 -7
- package/src/adapters/claude/permissions/permission-options.ts +17 -2
- package/src/adapters/claude/session/models.ts +94 -4
- package/src/adapters/claude/types.ts +3 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@posthog/agent",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.73",
|
|
4
4
|
"repository": "https://github.com/PostHog/code",
|
|
5
5
|
"description": "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
|
|
6
6
|
"exports": {
|
|
@@ -78,8 +78,8 @@
|
|
|
78
78
|
"@posthog/git": "1.0.0"
|
|
79
79
|
},
|
|
80
80
|
"dependencies": {
|
|
81
|
-
"@agentclientprotocol/sdk": "0.
|
|
82
|
-
"@anthropic-ai/claude-agent-sdk": "0.2.
|
|
81
|
+
"@agentclientprotocol/sdk": "0.16.1",
|
|
82
|
+
"@anthropic-ai/claude-agent-sdk": "0.2.76",
|
|
83
83
|
"@anthropic-ai/sdk": "^0.78.0",
|
|
84
84
|
"@hono/node-server": "^1.19.9",
|
|
85
85
|
"@opentelemetry/api-logs": "^0.208.0",
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
DEFAULT_GATEWAY_MODEL,
|
|
21
21
|
fetchGatewayModels,
|
|
22
22
|
formatGatewayModelName,
|
|
23
|
+
type GatewayModel,
|
|
23
24
|
isAnthropicModel,
|
|
24
25
|
} from "../gateway-models";
|
|
25
26
|
import { Logger } from "../utils/logger";
|
|
@@ -33,6 +34,8 @@ export interface BaseSession {
|
|
|
33
34
|
settingsManager: SettingsManager;
|
|
34
35
|
}
|
|
35
36
|
|
|
37
|
+
const DEFAULT_CONTEXT_WINDOW = 200_000;
|
|
38
|
+
|
|
36
39
|
export abstract class BaseAcpAgent implements Agent {
|
|
37
40
|
abstract readonly adapterName: string;
|
|
38
41
|
protected session!: BaseSession;
|
|
@@ -40,6 +43,7 @@ export abstract class BaseAcpAgent implements Agent {
|
|
|
40
43
|
client: AgentSideConnection;
|
|
41
44
|
logger: Logger;
|
|
42
45
|
fileContentCache: { [key: string]: string } = {};
|
|
46
|
+
protected gatewayModels: GatewayModel[] = [];
|
|
43
47
|
|
|
44
48
|
constructor(client: AgentSideConnection) {
|
|
45
49
|
this.client = client;
|
|
@@ -119,9 +123,9 @@ export abstract class BaseAcpAgent implements Agent {
|
|
|
119
123
|
currentModelId: string;
|
|
120
124
|
options: SessionConfigSelectOption[];
|
|
121
125
|
}> {
|
|
122
|
-
|
|
126
|
+
this.gatewayModels = await fetchGatewayModels();
|
|
123
127
|
|
|
124
|
-
const options = gatewayModels
|
|
128
|
+
const options = this.gatewayModels
|
|
125
129
|
.filter((model) => isAnthropicModel(model))
|
|
126
130
|
.map((model) => ({
|
|
127
131
|
value: model.id,
|
|
@@ -150,4 +154,9 @@ export abstract class BaseAcpAgent implements Agent {
|
|
|
150
154
|
|
|
151
155
|
return { currentModelId, options };
|
|
152
156
|
}
|
|
157
|
+
|
|
158
|
+
getContextWindowForModel(modelId: string): number {
|
|
159
|
+
const match = this.gatewayModels.find((m) => m.id === modelId);
|
|
160
|
+
return match?.context_window ?? DEFAULT_CONTEXT_WINDOW;
|
|
161
|
+
}
|
|
153
162
|
}
|
|
@@ -5,8 +5,8 @@ Fork of `@anthropic-ai/claude-agent-acp`. Upstream repo: https://github.com/anth
|
|
|
5
5
|
## Fork Point
|
|
6
6
|
|
|
7
7
|
- **Forked**: v0.10.9, commit `5411e0f4`, Dec 2 2025
|
|
8
|
-
- **Last sync**: v0.
|
|
9
|
-
- **SDK**: `@anthropic-ai/claude-agent-sdk` 0.2.
|
|
8
|
+
- **Last sync**: v0.22.2, commit `07db59e`, March 25 2026
|
|
9
|
+
- **SDK**: `@anthropic-ai/claude-agent-sdk` 0.2.76, `@agentclientprotocol/sdk` 0.16.1
|
|
10
10
|
|
|
11
11
|
## File Mapping
|
|
12
12
|
|
|
@@ -49,13 +49,12 @@ Fork of `@anthropic-ai/claude-agent-acp`. Upstream repo: https://github.com/anth
|
|
|
49
49
|
| Model resolution | `initializationResult.models` from SDK | `fetchGatewayModels()` from gateway API | Different model backend |
|
|
50
50
|
| permissionMode | Hardcoded `"default"` | Reads from `meta.permissionMode` | More flexible mode selection |
|
|
51
51
|
| Session storage | `this.sessions[sessionId]` (multi) | `this.session` (single) | Architectural choice |
|
|
52
|
-
| ExitPlanMode denial | `interrupt: true` | `interrupt: false` | Better UX — lets Claude refine plan |
|
|
53
52
|
| bypassPermissions | `updatedPermissions` with `destination: "session"` | No `updatedPermissions` | Different permission persistence |
|
|
54
53
|
| Auth methods | Always returns `claude-login` auth method | Returns empty `authMethods` | Auth handled externally |
|
|
55
54
|
|
|
56
55
|
## Next Sync
|
|
57
56
|
|
|
58
|
-
1. Check upstream changelog since v0.
|
|
57
|
+
1. Check upstream changelog since v0.22.2
|
|
59
58
|
2. Diff upstream source against PostHog Code using the file mapping above
|
|
60
59
|
3. Port in phases: bug fixes first, then features
|
|
61
60
|
4. After each phase: `pnpm --filter agent typecheck && pnpm --filter agent build && pnpm lint`
|
|
@@ -64,8 +64,8 @@ import { getAvailableSlashCommands } from "./session/commands";
|
|
|
64
64
|
import { parseMcpServers } from "./session/mcp-config";
|
|
65
65
|
import {
|
|
66
66
|
DEFAULT_MODEL,
|
|
67
|
-
getDefaultContextWindow,
|
|
68
67
|
getEffortOptions,
|
|
68
|
+
resolveModelPreference,
|
|
69
69
|
toSdkModelId,
|
|
70
70
|
} from "./session/models";
|
|
71
71
|
import {
|
|
@@ -89,6 +89,7 @@ import type {
|
|
|
89
89
|
|
|
90
90
|
const SESSION_VALIDATION_TIMEOUT_MS = 10_000;
|
|
91
91
|
const MAX_TITLE_LENGTH = 256;
|
|
92
|
+
const LOCAL_ONLY_COMMANDS = new Set(["/context", "/heapdump", "/extra-usage"]);
|
|
92
93
|
|
|
93
94
|
function sanitizeTitle(text: string): string {
|
|
94
95
|
const sanitized = text
|
|
@@ -141,6 +142,7 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
141
142
|
list: {},
|
|
142
143
|
fork: {},
|
|
143
144
|
resume: {},
|
|
145
|
+
close: {},
|
|
144
146
|
},
|
|
145
147
|
_meta: {
|
|
146
148
|
posthog: {
|
|
@@ -195,6 +197,10 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
195
197
|
async unstable_resumeSession(
|
|
196
198
|
params: ResumeSessionRequest,
|
|
197
199
|
): Promise<ResumeSessionResponse> {
|
|
200
|
+
// Reuse existing session if it matches
|
|
201
|
+
const existing = this.getExistingSessionState(params.sessionId);
|
|
202
|
+
if (existing) return existing;
|
|
203
|
+
|
|
198
204
|
const response = await this.createSession(
|
|
199
205
|
{
|
|
200
206
|
cwd: params.cwd,
|
|
@@ -210,6 +216,10 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
210
216
|
}
|
|
211
217
|
|
|
212
218
|
async loadSession(params: LoadSessionRequest): Promise<LoadSessionResponse> {
|
|
219
|
+
// Reuse existing session if it matches
|
|
220
|
+
const existing = this.getExistingSessionState(params.sessionId);
|
|
221
|
+
if (existing) return existing;
|
|
222
|
+
|
|
213
223
|
const response = await this.createSession(
|
|
214
224
|
{
|
|
215
225
|
cwd: params.cwd,
|
|
@@ -231,7 +241,7 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
231
241
|
};
|
|
232
242
|
}
|
|
233
243
|
|
|
234
|
-
async
|
|
244
|
+
async listSessions(
|
|
235
245
|
params: ListSessionsRequest,
|
|
236
246
|
): Promise<ListSessionsResponse> {
|
|
237
247
|
const sdkSessions = await listSessions({ dir: params.cwd ?? undefined });
|
|
@@ -251,6 +261,12 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
251
261
|
};
|
|
252
262
|
}
|
|
253
263
|
|
|
264
|
+
async unstable_listSessions(
|
|
265
|
+
params: ListSessionsRequest,
|
|
266
|
+
): Promise<ListSessionsResponse> {
|
|
267
|
+
return this.listSessions(params);
|
|
268
|
+
}
|
|
269
|
+
|
|
254
270
|
async prompt(params: PromptRequest): Promise<PromptResponse> {
|
|
255
271
|
this.session.cancelled = false;
|
|
256
272
|
this.session.interruptReason = undefined;
|
|
@@ -262,18 +278,40 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
262
278
|
};
|
|
263
279
|
|
|
264
280
|
const userMessage = promptToClaude(params);
|
|
281
|
+
const promptUuid = randomUUID();
|
|
282
|
+
userMessage.uuid = promptUuid;
|
|
283
|
+
let promptReplayed = false;
|
|
284
|
+
let isLocalOnlyCommand = false;
|
|
285
|
+
|
|
286
|
+
// Detect local-only slash commands that return results without model invocation
|
|
287
|
+
const msgContent = userMessage.message.content;
|
|
288
|
+
let firstTextPart = "";
|
|
289
|
+
if (typeof msgContent === "string") {
|
|
290
|
+
firstTextPart = msgContent;
|
|
291
|
+
} else if (Array.isArray(msgContent)) {
|
|
292
|
+
for (const block of msgContent) {
|
|
293
|
+
if ("type" in block && block.type === "text" && "text" in block) {
|
|
294
|
+
firstTextPart = block.text as string;
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
const commandMatch = firstTextPart.match(/^(\/\S+)/);
|
|
300
|
+
if (commandMatch && LOCAL_ONLY_COMMANDS.has(commandMatch[1])) {
|
|
301
|
+
isLocalOnlyCommand = true;
|
|
302
|
+
promptReplayed = true;
|
|
303
|
+
}
|
|
265
304
|
|
|
266
305
|
if (this.session.promptRunning) {
|
|
267
|
-
const uuid = randomUUID();
|
|
268
|
-
userMessage.uuid = uuid;
|
|
269
306
|
this.session.input.push(userMessage);
|
|
270
307
|
const order = this.session.nextPendingOrder++;
|
|
271
308
|
const cancelled = await new Promise<boolean>((resolve) => {
|
|
272
|
-
this.session.pendingMessages.set(
|
|
309
|
+
this.session.pendingMessages.set(promptUuid, { resolve, order });
|
|
273
310
|
});
|
|
274
311
|
if (cancelled) {
|
|
275
312
|
return { stopReason: "cancelled" };
|
|
276
313
|
}
|
|
314
|
+
promptReplayed = true;
|
|
277
315
|
} else {
|
|
278
316
|
this.session.input.push(userMessage);
|
|
279
317
|
}
|
|
@@ -284,6 +322,16 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
284
322
|
this.session.promptRunning = true;
|
|
285
323
|
let handedOff = false;
|
|
286
324
|
let lastAssistantTotalUsage: number | null = null;
|
|
325
|
+
if (this.session.lastContextWindowSize == null) {
|
|
326
|
+
this.session.lastContextWindowSize = this.getContextWindowForModel(
|
|
327
|
+
this.session.modelId ?? "",
|
|
328
|
+
);
|
|
329
|
+
this.logger.debug("Initial context window size from gateway", {
|
|
330
|
+
modelId: this.session.modelId,
|
|
331
|
+
contextWindowSize: this.session.lastContextWindowSize,
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
let lastContextWindowSize = this.session.lastContextWindowSize;
|
|
287
335
|
|
|
288
336
|
const supportsTerminalOutput =
|
|
289
337
|
(
|
|
@@ -322,11 +370,24 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
322
370
|
case "system":
|
|
323
371
|
if (message.subtype === "compact_boundary") {
|
|
324
372
|
lastAssistantTotalUsage = 0;
|
|
373
|
+
promptReplayed = true;
|
|
374
|
+
}
|
|
375
|
+
if (message.subtype === "local_command_output") {
|
|
376
|
+
promptReplayed = true;
|
|
325
377
|
}
|
|
326
378
|
await handleSystemMessage(message, context);
|
|
327
379
|
break;
|
|
328
380
|
|
|
329
381
|
case "result": {
|
|
382
|
+
// Skip results from background tasks that finished after our prompt started
|
|
383
|
+
if (!promptReplayed) {
|
|
384
|
+
this.logger.debug(
|
|
385
|
+
"Skipping background task result before prompt replay",
|
|
386
|
+
{ sessionId: params.sessionId },
|
|
387
|
+
);
|
|
388
|
+
break;
|
|
389
|
+
}
|
|
390
|
+
|
|
330
391
|
if (this.session.cancelled) {
|
|
331
392
|
return { stopReason: "cancelled" };
|
|
332
393
|
}
|
|
@@ -341,16 +402,25 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
341
402
|
this.session.accumulatedUsage.cachedWriteTokens +=
|
|
342
403
|
message.usage.cache_creation_input_tokens;
|
|
343
404
|
|
|
344
|
-
//
|
|
405
|
+
// SDK can underreport context window (e.g. 200k for 1M models).
|
|
406
|
+
// Use SDK value only if it's larger than what gateway reported.
|
|
345
407
|
const contextWindows = Object.values(message.modelUsage).map(
|
|
346
408
|
(m) => m.contextWindow,
|
|
347
409
|
);
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
410
|
+
if (contextWindows.length > 0) {
|
|
411
|
+
const sdkContextWindow = Math.min(...contextWindows);
|
|
412
|
+
if (sdkContextWindow > lastContextWindowSize) {
|
|
413
|
+
lastContextWindowSize = sdkContextWindow;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
this.session.lastContextWindowSize = lastContextWindowSize;
|
|
417
|
+
this.logger.debug("Context window size from result", {
|
|
418
|
+
sdkReported: contextWindows,
|
|
419
|
+
resolved: lastContextWindowSize,
|
|
420
|
+
modelId: this.session.modelId,
|
|
421
|
+
});
|
|
352
422
|
|
|
353
|
-
this.session.contextSize =
|
|
423
|
+
this.session.contextSize = lastContextWindowSize;
|
|
354
424
|
if (lastAssistantTotalUsage !== null) {
|
|
355
425
|
this.session.contextUsed = lastAssistantTotalUsage;
|
|
356
426
|
}
|
|
@@ -362,7 +432,7 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
362
432
|
update: {
|
|
363
433
|
sessionUpdate: "usage_update",
|
|
364
434
|
used: lastAssistantTotalUsage,
|
|
365
|
-
size:
|
|
435
|
+
size: lastContextWindowSize,
|
|
366
436
|
cost: {
|
|
367
437
|
amount: message.total_cost_usd,
|
|
368
438
|
currency: "USD",
|
|
@@ -398,6 +468,21 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
398
468
|
const result = handleResultMessage(message);
|
|
399
469
|
if (result.error) throw result.error;
|
|
400
470
|
|
|
471
|
+
// For local-only commands, forward the result text to the client
|
|
472
|
+
if (
|
|
473
|
+
isLocalOnlyCommand &&
|
|
474
|
+
message.subtype === "success" &&
|
|
475
|
+
message.result
|
|
476
|
+
) {
|
|
477
|
+
await this.client.sessionUpdate({
|
|
478
|
+
sessionId: params.sessionId,
|
|
479
|
+
update: {
|
|
480
|
+
sessionUpdate: "agent_message_chunk",
|
|
481
|
+
content: { type: "text", text: message.result },
|
|
482
|
+
},
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
|
|
401
486
|
return { stopReason: result.stopReason ?? "end_turn", usage };
|
|
402
487
|
}
|
|
403
488
|
|
|
@@ -411,8 +496,13 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
411
496
|
break;
|
|
412
497
|
}
|
|
413
498
|
|
|
414
|
-
// Check for
|
|
499
|
+
// Check for prompt replay (our own message echoed back)
|
|
415
500
|
if (message.type === "user" && "uuid" in message && message.uuid) {
|
|
501
|
+
if (message.uuid === promptUuid) {
|
|
502
|
+
promptReplayed = true;
|
|
503
|
+
break;
|
|
504
|
+
}
|
|
505
|
+
|
|
416
506
|
const pending = this.session.pendingMessages.get(
|
|
417
507
|
message.uuid as string,
|
|
418
508
|
);
|
|
@@ -449,9 +539,18 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
449
539
|
};
|
|
450
540
|
lastAssistantTotalUsage =
|
|
451
541
|
usage.input_tokens +
|
|
452
|
-
usage.output_tokens +
|
|
453
542
|
usage.cache_read_input_tokens +
|
|
454
543
|
usage.cache_creation_input_tokens;
|
|
544
|
+
|
|
545
|
+
await this.client.sessionUpdate({
|
|
546
|
+
sessionId: params.sessionId,
|
|
547
|
+
update: {
|
|
548
|
+
sessionUpdate: "usage_update",
|
|
549
|
+
used: lastAssistantTotalUsage,
|
|
550
|
+
size: lastContextWindowSize,
|
|
551
|
+
cost: null,
|
|
552
|
+
},
|
|
553
|
+
});
|
|
455
554
|
}
|
|
456
555
|
|
|
457
556
|
const result = await handleUserAssistantMessage(message, context);
|
|
@@ -490,6 +589,7 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
490
589
|
this.logger.error(`Process died: ${msg}`, {
|
|
491
590
|
sessionId: this.sessionId,
|
|
492
591
|
});
|
|
592
|
+
this.session.settingsManager.dispose();
|
|
493
593
|
this.session.input.end();
|
|
494
594
|
throw RequestError.internalError(
|
|
495
595
|
undefined,
|
|
@@ -522,9 +622,11 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
522
622
|
async unstable_setSessionModel(
|
|
523
623
|
params: SetSessionModelRequest,
|
|
524
624
|
): Promise<SetSessionModelResponse | undefined> {
|
|
525
|
-
|
|
526
|
-
await this.session.query.setModel(sdkModelId);
|
|
625
|
+
await this.session.query.setModel(toSdkModelId(params.modelId));
|
|
527
626
|
this.session.modelId = params.modelId;
|
|
627
|
+
this.session.lastContextWindowSize = this.getContextWindowForModel(
|
|
628
|
+
params.modelId,
|
|
629
|
+
);
|
|
528
630
|
this.rebuildEffortConfigOption(params.modelId);
|
|
529
631
|
await this.updateConfigOption("model", params.modelId);
|
|
530
632
|
return {};
|
|
@@ -548,43 +650,72 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
548
650
|
throw new Error(`Unknown config option: ${params.configId}`);
|
|
549
651
|
}
|
|
550
652
|
|
|
551
|
-
|
|
653
|
+
if (typeof params.value !== "string") {
|
|
654
|
+
throw new Error(
|
|
655
|
+
`Invalid value type for config option ${params.configId}`,
|
|
656
|
+
);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
const allValues: { value: string; name?: string; description?: string }[] =
|
|
552
660
|
"options" in option && Array.isArray(option.options)
|
|
553
661
|
? (option.options as Array<Record<string, unknown>>).flatMap((o) =>
|
|
554
662
|
"options" in o && Array.isArray(o.options)
|
|
555
|
-
? (o.options as {
|
|
556
|
-
|
|
663
|
+
? (o.options as {
|
|
664
|
+
value: string;
|
|
665
|
+
name?: string;
|
|
666
|
+
description?: string;
|
|
667
|
+
}[])
|
|
668
|
+
: [o as { value: string; name?: string; description?: string }],
|
|
557
669
|
)
|
|
558
670
|
: [];
|
|
559
|
-
|
|
671
|
+
let validValue = allValues.find((o) => o.value === params.value);
|
|
672
|
+
|
|
673
|
+
// For model options, fall back to alias resolution when exact match fails.
|
|
674
|
+
// This lets callers use human-friendly aliases like "opus" or "sonnet"
|
|
675
|
+
// instead of full model IDs like "claude-opus-4-6".
|
|
676
|
+
if (!validValue && params.configId === "model") {
|
|
677
|
+
const resolved = resolveModelPreference(params.value, allValues);
|
|
678
|
+
if (resolved) {
|
|
679
|
+
validValue = allValues.find((o) => o.value === resolved);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
560
683
|
if (!validValue) {
|
|
561
684
|
throw new Error(
|
|
562
685
|
`Invalid value for config option ${params.configId}: ${params.value}`,
|
|
563
686
|
);
|
|
564
687
|
}
|
|
565
688
|
|
|
689
|
+
// Use the canonical option value so downstream code always receives the
|
|
690
|
+
// model ID rather than the caller-supplied alias.
|
|
691
|
+
const resolvedValue = validValue.value;
|
|
692
|
+
|
|
566
693
|
if (params.configId === "mode") {
|
|
567
|
-
await this.applySessionMode(
|
|
694
|
+
await this.applySessionMode(resolvedValue);
|
|
568
695
|
await this.client.sessionUpdate({
|
|
569
696
|
sessionId: this.sessionId,
|
|
570
697
|
update: {
|
|
571
698
|
sessionUpdate: "current_mode_update",
|
|
572
|
-
currentModeId:
|
|
699
|
+
currentModeId: resolvedValue,
|
|
573
700
|
},
|
|
574
701
|
});
|
|
575
702
|
} else if (params.configId === "model") {
|
|
576
|
-
const sdkModelId = toSdkModelId(
|
|
703
|
+
const sdkModelId = toSdkModelId(resolvedValue);
|
|
577
704
|
await this.session.query.setModel(sdkModelId);
|
|
578
|
-
this.session.modelId =
|
|
579
|
-
this.
|
|
705
|
+
this.session.modelId = resolvedValue;
|
|
706
|
+
this.session.lastContextWindowSize =
|
|
707
|
+
this.getContextWindowForModel(resolvedValue);
|
|
708
|
+
this.rebuildEffortConfigOption(resolvedValue);
|
|
580
709
|
} else if (params.configId === "effort") {
|
|
581
|
-
const newEffort =
|
|
710
|
+
const newEffort = resolvedValue as EffortLevel;
|
|
582
711
|
this.session.effort = newEffort;
|
|
583
712
|
this.session.queryOptions.effort = newEffort;
|
|
584
713
|
}
|
|
585
714
|
|
|
586
715
|
this.session.configOptions = this.session.configOptions.map((o) =>
|
|
587
|
-
o.id === params.configId
|
|
716
|
+
o.id === params.configId && typeof o.currentValue === "string"
|
|
717
|
+
? { ...o, currentValue: resolvedValue }
|
|
718
|
+
: o,
|
|
588
719
|
);
|
|
589
720
|
|
|
590
721
|
return { configOptions: this.session.configOptions };
|
|
@@ -595,7 +726,9 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
595
726
|
value: string,
|
|
596
727
|
): Promise<void> {
|
|
597
728
|
this.session.configOptions = this.session.configOptions.map((o) =>
|
|
598
|
-
o.id === configId
|
|
729
|
+
o.id === configId && typeof o.currentValue === "string"
|
|
730
|
+
? { ...o, currentValue: value }
|
|
731
|
+
: o,
|
|
599
732
|
);
|
|
600
733
|
|
|
601
734
|
await this.client.sessionUpdate({
|
|
@@ -691,7 +824,10 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
691
824
|
sessionId,
|
|
692
825
|
isResume,
|
|
693
826
|
forkSession,
|
|
694
|
-
additionalDirectories:
|
|
827
|
+
additionalDirectories: [
|
|
828
|
+
...(meta?.claudeCode?.options?.additionalDirectories ?? []),
|
|
829
|
+
...(meta?.additionalRoots ?? []),
|
|
830
|
+
],
|
|
695
831
|
disableBuiltInTools: meta?.disableBuiltInTools,
|
|
696
832
|
settingsManager,
|
|
697
833
|
onModeChange: this.createOnModeChange(),
|
|
@@ -788,12 +924,12 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
788
924
|
const modelOptions = await this.getModelConfigOptions();
|
|
789
925
|
const resolvedModelId = settingsModel || modelOptions.currentModelId;
|
|
790
926
|
session.modelId = resolvedModelId;
|
|
927
|
+
session.lastContextWindowSize =
|
|
928
|
+
this.getContextWindowForModel(resolvedModelId);
|
|
791
929
|
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
await this.session.query.setModel(resolvedSdkModel);
|
|
796
|
-
}
|
|
930
|
+
const resolvedSdkModel = toSdkModelId(resolvedModelId);
|
|
931
|
+
if (!isResume && resolvedSdkModel !== DEFAULT_MODEL) {
|
|
932
|
+
await this.session.query.setModel(resolvedSdkModel);
|
|
797
933
|
}
|
|
798
934
|
|
|
799
935
|
const availableModes = getAvailableModes();
|
|
@@ -869,6 +1005,50 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
869
1005
|
};
|
|
870
1006
|
}
|
|
871
1007
|
|
|
1008
|
+
private getExistingSessionState(
|
|
1009
|
+
sessionId: string,
|
|
1010
|
+
): NewSessionResponse | null {
|
|
1011
|
+
if (this.sessionId !== sessionId || !this.session) return null;
|
|
1012
|
+
|
|
1013
|
+
const availableModes = getAvailableModes();
|
|
1014
|
+
const modes: SessionModeState = {
|
|
1015
|
+
currentModeId: this.session.permissionMode,
|
|
1016
|
+
availableModes: availableModes.map((mode) => ({
|
|
1017
|
+
id: mode.id,
|
|
1018
|
+
name: mode.name,
|
|
1019
|
+
description: mode.description ?? undefined,
|
|
1020
|
+
})),
|
|
1021
|
+
};
|
|
1022
|
+
|
|
1023
|
+
const modelOptions = this.session.configOptions.find(
|
|
1024
|
+
(o) => o.id === "model",
|
|
1025
|
+
);
|
|
1026
|
+
const models: SessionModelState = {
|
|
1027
|
+
currentModelId: this.session.modelId ?? DEFAULT_MODEL,
|
|
1028
|
+
availableModels:
|
|
1029
|
+
modelOptions && "options" in modelOptions
|
|
1030
|
+
? (
|
|
1031
|
+
modelOptions.options as Array<{
|
|
1032
|
+
value: string;
|
|
1033
|
+
name: string;
|
|
1034
|
+
description?: string;
|
|
1035
|
+
}>
|
|
1036
|
+
).map((opt) => ({
|
|
1037
|
+
modelId: opt.value,
|
|
1038
|
+
name: opt.name,
|
|
1039
|
+
description: opt.description,
|
|
1040
|
+
}))
|
|
1041
|
+
: [],
|
|
1042
|
+
};
|
|
1043
|
+
|
|
1044
|
+
return {
|
|
1045
|
+
sessionId,
|
|
1046
|
+
modes,
|
|
1047
|
+
models,
|
|
1048
|
+
configOptions: this.session.configOptions,
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
1051
|
+
|
|
872
1052
|
private buildConfigOptions(
|
|
873
1053
|
currentModeId: string,
|
|
874
1054
|
modelOptions: {
|
|
@@ -938,7 +1118,9 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
938
1118
|
return;
|
|
939
1119
|
}
|
|
940
1120
|
|
|
941
|
-
const
|
|
1121
|
+
const rawCurrentValue = existingEffort?.currentValue;
|
|
1122
|
+
const currentValue =
|
|
1123
|
+
typeof rawCurrentValue === "string" ? rawCurrentValue : "high";
|
|
942
1124
|
const isValidValue = effortOptions.some((o) => o.value === currentValue);
|
|
943
1125
|
const resolvedValue = isValidValue ? currentValue : "high";
|
|
944
1126
|
|
|
@@ -544,7 +544,7 @@ export async function handleSystemMessage(
|
|
|
544
544
|
message: Extract<SDKMessage, { type: "system" }>,
|
|
545
545
|
context: MessageHandlerContext,
|
|
546
546
|
): Promise<void> {
|
|
547
|
-
const { sessionId, client, logger } = context;
|
|
547
|
+
const { session, sessionId, client, logger } = context;
|
|
548
548
|
|
|
549
549
|
switch (message.subtype) {
|
|
550
550
|
case "init":
|
|
@@ -554,6 +554,7 @@ export async function handleSystemMessage(
|
|
|
554
554
|
sessionId,
|
|
555
555
|
trigger: message.compact_metadata.trigger,
|
|
556
556
|
preTokens: message.compact_metadata.pre_tokens,
|
|
557
|
+
contextSize: session.contextSize,
|
|
557
558
|
});
|
|
558
559
|
break;
|
|
559
560
|
case "hook_response":
|
|
@@ -797,30 +798,6 @@ export async function handleUserAssistantMessage(
|
|
|
797
798
|
context;
|
|
798
799
|
|
|
799
800
|
if (shouldSkipUserAssistantMessage(message)) {
|
|
800
|
-
const content = message.message.content;
|
|
801
|
-
|
|
802
|
-
// Handle /context by sending its reply as a regular agent message
|
|
803
|
-
if (
|
|
804
|
-
typeof content === "string" &&
|
|
805
|
-
hasLocalCommandStdout(content) &&
|
|
806
|
-
content.includes("Context Usage")
|
|
807
|
-
) {
|
|
808
|
-
const stripped = content
|
|
809
|
-
.replace("<local-command-stdout>", "")
|
|
810
|
-
.replace("</local-command-stdout>", "");
|
|
811
|
-
for (const notification of toAcpNotifications(
|
|
812
|
-
stripped,
|
|
813
|
-
"assistant",
|
|
814
|
-
sessionId,
|
|
815
|
-
toolUseCache,
|
|
816
|
-
fileContentCache,
|
|
817
|
-
client,
|
|
818
|
-
logger,
|
|
819
|
-
)) {
|
|
820
|
-
await client.sessionUpdate(notification);
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
|
|
824
801
|
logSpecialMessages(message, logger);
|
|
825
802
|
|
|
826
803
|
if (isLoginRequiredMessage(message)) {
|
|
@@ -34,7 +34,7 @@ export type ToolPermissionResult =
|
|
|
34
34
|
| {
|
|
35
35
|
behavior: "deny";
|
|
36
36
|
message: string;
|
|
37
|
-
interrupt
|
|
37
|
+
interrupt?: boolean;
|
|
38
38
|
};
|
|
39
39
|
|
|
40
40
|
interface ToolHandlerContext {
|
|
@@ -164,9 +164,11 @@ async function applyPlanApproval(
|
|
|
164
164
|
if (
|
|
165
165
|
response.outcome?.outcome === "selected" &&
|
|
166
166
|
(response.outcome.optionId === "default" ||
|
|
167
|
-
response.outcome.optionId === "acceptEdits"
|
|
167
|
+
response.outcome.optionId === "acceptEdits" ||
|
|
168
|
+
response.outcome.optionId === "bypassPermissions")
|
|
168
169
|
) {
|
|
169
|
-
session.permissionMode = response.outcome
|
|
170
|
+
session.permissionMode = response.outcome
|
|
171
|
+
.optionId as typeof session.permissionMode;
|
|
170
172
|
await session.query.setPermissionMode(response.outcome.optionId);
|
|
171
173
|
await context.client.sessionUpdate({
|
|
172
174
|
sessionId: context.sessionId,
|
|
@@ -261,7 +263,6 @@ async function handleAskUserQuestionTool(
|
|
|
261
263
|
return {
|
|
262
264
|
behavior: "deny",
|
|
263
265
|
message: "No questions provided",
|
|
264
|
-
interrupt: true,
|
|
265
266
|
};
|
|
266
267
|
}
|
|
267
268
|
|
|
@@ -303,7 +304,6 @@ async function handleAskUserQuestionTool(
|
|
|
303
304
|
typeof customMessage === "string"
|
|
304
305
|
? customMessage
|
|
305
306
|
: "User cancelled the questions",
|
|
306
|
-
interrupt: true,
|
|
307
307
|
};
|
|
308
308
|
}
|
|
309
309
|
|
|
@@ -312,7 +312,6 @@ async function handleAskUserQuestionTool(
|
|
|
312
312
|
return {
|
|
313
313
|
behavior: "deny",
|
|
314
314
|
message: "User did not provide answers",
|
|
315
|
-
interrupt: true,
|
|
316
315
|
};
|
|
317
316
|
}
|
|
318
317
|
|
|
@@ -393,7 +392,6 @@ async function handleDefaultPermissionFlow(
|
|
|
393
392
|
return {
|
|
394
393
|
behavior: "deny",
|
|
395
394
|
message,
|
|
396
|
-
interrupt: true,
|
|
397
395
|
};
|
|
398
396
|
}
|
|
399
397
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PermissionUpdate } from "@anthropic-ai/claude-agent-sdk";
|
|
2
|
+
import { IS_ROOT } from "../../../utils/common";
|
|
2
3
|
import { BASH_TOOLS, READ_TOOLS, SEARCH_TOOLS, WRITE_TOOLS } from "../tools";
|
|
3
4
|
|
|
4
5
|
export interface PermissionOption {
|
|
@@ -91,8 +92,20 @@ export function buildPermissionOptions(
|
|
|
91
92
|
return permissionOptions("Yes, always allow");
|
|
92
93
|
}
|
|
93
94
|
|
|
95
|
+
const ALLOW_BYPASS = !IS_ROOT || !!process.env.IS_SANDBOX;
|
|
96
|
+
|
|
94
97
|
export function buildExitPlanModePermissionOptions(): PermissionOption[] {
|
|
95
|
-
|
|
98
|
+
const options: PermissionOption[] = [];
|
|
99
|
+
|
|
100
|
+
if (ALLOW_BYPASS) {
|
|
101
|
+
options.push({
|
|
102
|
+
kind: "allow_always",
|
|
103
|
+
name: "Yes, bypass all permissions",
|
|
104
|
+
optionId: "bypassPermissions",
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
options.push(
|
|
96
109
|
{
|
|
97
110
|
kind: "allow_always",
|
|
98
111
|
name: "Yes, and auto-accept edits",
|
|
@@ -109,5 +122,7 @@ export function buildExitPlanModePermissionOptions(): PermissionOption[] {
|
|
|
109
122
|
optionId: "reject_with_feedback",
|
|
110
123
|
_meta: { customInput: true },
|
|
111
124
|
},
|
|
112
|
-
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
return options;
|
|
113
128
|
}
|