@adecore/agents 0.31.11 → 0.32.0
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/chat/acp-backend.d.ts +4 -2
- package/dist/chat/acp-backend.js +50 -11
- package/dist/chat/acp-protocol.d.ts +29 -4
- package/dist/chat/acp-protocol.js +262 -23
- package/dist/chat/backend.d.ts +3 -0
- package/dist/chat/chat-core.js +6 -3
- package/dist/chat/chat-session.js +5 -2
- package/dist/chat/claude-backend.js +4 -4
- package/dist/chat/claude-protocol.d.ts +4 -1
- package/dist/chat/claude-protocol.js +46 -2
- package/dist/chat/codex-backend.js +4 -4
- package/dist/chat/fixtures/antigravity-1.4.1-question.json +69 -0
- package/dist/chat/fixtures/antigravity-1.4.1-quota.json +35 -0
- package/dist/chat/fixtures/grok-1.0.62-limits.json +66 -0
- package/dist/chat/fixtures/grok-1.0.62-question.json +99 -0
- package/dist/chat/json-rpc-transport.js +2 -2
- package/dist/chat/projector.js +22 -10
- package/dist/providers/accounts/service.js +4 -0
- package/dist/providers/claude-provider.js +2 -1
- package/dist/providers/detect.d.ts +1 -1
- package/dist/providers/detect.js +6 -3
- package/dist/providers/registry.d.ts +7 -0
- package/dist/providers/registry.js +64 -5
- package/dist/skills.d.ts +9 -1
- package/dist/skills.js +178 -20
- package/dist/testing/fake-acp-capture.d.ts +1 -0
- package/dist/testing/fake-acp-capture.js +4 -3
- package/dist/usage/accounts.js +3 -2
- package/package.json +3 -3
- package/src/chat/acp-backend.ts +56 -11
- package/src/chat/acp-protocol.ts +308 -26
- package/src/chat/backend.ts +5 -1
- package/src/chat/chat-core.ts +6 -3
- package/src/chat/chat-session.ts +5 -2
- package/src/chat/claude-backend.ts +4 -4
- package/src/chat/claude-protocol.ts +48 -2
- package/src/chat/codex-backend.ts +4 -4
- package/src/chat/fixtures/antigravity-1.4.1-question.json +69 -0
- package/src/chat/fixtures/antigravity-1.4.1-quota.json +35 -0
- package/src/chat/fixtures/grok-1.0.62-limits.json +66 -0
- package/src/chat/fixtures/grok-1.0.62-question.json +99 -0
- package/src/chat/json-rpc-transport.ts +2 -2
- package/src/chat/projector.ts +24 -12
- package/src/providers/accounts/service.ts +4 -0
- package/src/providers/claude-provider.ts +2 -1
- package/src/providers/detect.ts +7 -3
- package/src/providers/registry.ts +71 -5
- package/src/skills.ts +198 -19
- package/src/testing/fake-acp-capture.ts +8 -4
- package/src/usage/accounts.ts +3 -2
package/dist/chat/chat-core.js
CHANGED
|
@@ -255,10 +255,12 @@ export class ChatCore {
|
|
|
255
255
|
await this.hooks.admit?.(payload.chatId, cwd);
|
|
256
256
|
// A thread on disk keeps its account too. One it lost still opens, and says why on its next turn.
|
|
257
257
|
// A new one without an account takes the person's pick for this CLI, which is refused rather than replaced when it went.
|
|
258
|
-
|
|
258
|
+
// The pick is checked as named, before the default account loses its id, so one that is turned off is refused too.
|
|
259
|
+
const requested = payload.account ?? this.composerPreferences.for(kind).account;
|
|
259
260
|
if (!stored) {
|
|
260
|
-
this.requireAccount(kind,
|
|
261
|
+
this.requireAccount(kind, requested);
|
|
261
262
|
}
|
|
263
|
+
const account = stored ? stored.info.account : storedAccount(kind, requested);
|
|
262
264
|
const narrowed = (mode) => (this.hooks.runtimeModeFor ? this.hooks.runtimeModeFor(payload.chatId, mode) : mode);
|
|
263
265
|
// Asked again for a chat on disk, since the host may have listed it since.
|
|
264
266
|
const hidden = this.hooks.hiddenFor?.(payload.chatId) ? { hidden: true } : {};
|
|
@@ -369,7 +371,8 @@ export class ChatCore {
|
|
|
369
371
|
if (to === from) {
|
|
370
372
|
return;
|
|
371
373
|
}
|
|
372
|
-
|
|
374
|
+
// As named, so a switch to the default account is refused while it is turned off.
|
|
375
|
+
this.requireAccount(kind, requested);
|
|
373
376
|
const spoke = session.info.agentSessionId !== null || session.info.usage.turns > 0;
|
|
374
377
|
if (spoke && !this.canContinue(kind, from, to)) {
|
|
375
378
|
const label = this.accountLabel(kind, to);
|
|
@@ -557,7 +557,7 @@ export class ChatSession {
|
|
|
557
557
|
compact() {
|
|
558
558
|
const compaction = this.options.provider.capabilities.compaction;
|
|
559
559
|
if (compaction === 'none') {
|
|
560
|
-
throw new ChatError('chat-unsupported', `${this.options.provider.name} cannot
|
|
560
|
+
throw new ChatError('chat-unsupported', `${this.options.provider.name} cannot compact its context`);
|
|
561
561
|
}
|
|
562
562
|
if (compaction === 'prompt') {
|
|
563
563
|
this.send('/compact');
|
|
@@ -711,7 +711,9 @@ export class ChatSession {
|
|
|
711
711
|
...usage,
|
|
712
712
|
contextTokens: 0,
|
|
713
713
|
contextWindow: this.options.provider.catalog.contextWindowFor(this.thread.info.selection),
|
|
714
|
-
breakdown: undefined
|
|
714
|
+
breakdown: undefined,
|
|
715
|
+
cacheTtlMs: undefined,
|
|
716
|
+
cacheExpiresAt: undefined
|
|
715
717
|
}
|
|
716
718
|
})
|
|
717
719
|
]);
|
|
@@ -1639,6 +1641,7 @@ export class ChatSession {
|
|
|
1639
1641
|
resumeNote: this.options.resumeNote?.() ?? null,
|
|
1640
1642
|
resumeInstructions: this.options.resumeInstructions?.() ?? null,
|
|
1641
1643
|
...(info.goal === undefined ? {} : { goal: info.goal }),
|
|
1644
|
+
...(info.usage.cacheTtlMs === undefined ? {} : { cacheTtlMs: info.usage.cacheTtlMs }),
|
|
1642
1645
|
...(folders.length > 0 ? { folders } : {}),
|
|
1643
1646
|
...(projectConfig ? {} : { projectConfig }),
|
|
1644
1647
|
...(this.options.spawn ? { spawn: this.options.spawn } : {})
|
|
@@ -32,7 +32,7 @@ export class ClaudeBackend {
|
|
|
32
32
|
this.launch = launch;
|
|
33
33
|
this.host = host;
|
|
34
34
|
this.allowedTools = options.allowedTools ?? [];
|
|
35
|
-
this.protocol = new ClaudeProtocol(launch.goal ?? null, launch.now);
|
|
35
|
+
this.protocol = new ClaudeProtocol(launch.goal ?? null, launch.now, launch.cacheTtlMs ?? null);
|
|
36
36
|
}
|
|
37
37
|
get running() {
|
|
38
38
|
return this.child !== null;
|
|
@@ -260,10 +260,10 @@ export class ClaudeBackend {
|
|
|
260
260
|
this.failed(error);
|
|
261
261
|
}
|
|
262
262
|
failed(error) {
|
|
263
|
-
this.rejectSteers(`
|
|
263
|
+
this.rejectSteers(`Lost the connection to Claude: ${errorText(error)}`);
|
|
264
264
|
this.protocol.forgetPending();
|
|
265
265
|
try {
|
|
266
|
-
this.host.onEvent({ type: 'failed', message: `
|
|
266
|
+
this.host.onEvent({ type: 'failed', message: `Lost the connection to Claude: ${errorText(error)}`, processAlive: this.running });
|
|
267
267
|
}
|
|
268
268
|
catch (reportError) {
|
|
269
269
|
console.error('Reporting a Claude transport failure failed:', errorText(reportError));
|
|
@@ -311,7 +311,7 @@ export class ClaudeBackend {
|
|
|
311
311
|
return;
|
|
312
312
|
}
|
|
313
313
|
this.child = null;
|
|
314
|
-
this.rejectSteers('Claude
|
|
314
|
+
this.rejectSteers('Claude stopped before it got your message');
|
|
315
315
|
this.protocol.forgetPending();
|
|
316
316
|
this.host.onEvent({ type: 'exit', exitCode, ...(stderr === null ? {} : { stderr }) });
|
|
317
317
|
}
|
|
@@ -15,6 +15,8 @@ export declare class ClaudeProtocol {
|
|
|
15
15
|
private readonly foregroundShells;
|
|
16
16
|
private readonly backgroundTasks;
|
|
17
17
|
private readonly workflows;
|
|
18
|
+
private cacheMessageId;
|
|
19
|
+
private cacheTtlMs;
|
|
18
20
|
private lastUuid;
|
|
19
21
|
private readonly subagentModels;
|
|
20
22
|
private apiError;
|
|
@@ -26,7 +28,7 @@ export declare class ClaudeProtocol {
|
|
|
26
28
|
private goalAnswered;
|
|
27
29
|
private interrupting;
|
|
28
30
|
private readonly backgroundAgents;
|
|
29
|
-
constructor(goal?: ChatGoal | null, now?: () => number);
|
|
31
|
+
constructor(goal?: ChatGoal | null, now?: () => number, cacheTtlMs?: number | null);
|
|
30
32
|
noteInterrupt(): void;
|
|
31
33
|
private setGoal;
|
|
32
34
|
beginSteer(promptId: string): void;
|
|
@@ -45,6 +47,7 @@ export declare class ClaudeProtocol {
|
|
|
45
47
|
stopTaskRequest(taskId: string): unknown;
|
|
46
48
|
private handleControlResponse;
|
|
47
49
|
private handleStreamEvent;
|
|
50
|
+
private noteCache;
|
|
48
51
|
private handleAssistant;
|
|
49
52
|
private trackGoal;
|
|
50
53
|
private settleGoal;
|
|
@@ -27,6 +27,20 @@ function taskUsage(usage) {
|
|
|
27
27
|
function contextTokens(usage) {
|
|
28
28
|
return isRecord(usage) ? num(usage.input_tokens) + num(usage.cache_creation_input_tokens) + num(usage.cache_read_input_tokens) : 0;
|
|
29
29
|
}
|
|
30
|
+
// How long a prompt cache entry lives, by the field of `usage.cache_creation` that counts what a request wrote with it.
|
|
31
|
+
const CACHE_TTL_MS = { ephemeral_5m_input_tokens: 5 * 60_000, ephemeral_1h_input_tokens: 60 * 60_000 };
|
|
32
|
+
/*
|
|
33
|
+
* The lifetime of the cache a response wrote, or null when it wrote none or names no lifetime. A response that wrote
|
|
34
|
+
* with both answers the shorter, which is when the part of its context that goes cold first does.
|
|
35
|
+
*/
|
|
36
|
+
function cacheTtlWritten(usage) {
|
|
37
|
+
const creation = isRecord(usage) && isRecord(usage.cache_creation) ? usage.cache_creation : null;
|
|
38
|
+
if (creation === null) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
const written = Object.entries(CACHE_TTL_MS).filter(([field]) => num(creation[field]) > 0);
|
|
42
|
+
return written.length === 0 ? null : Math.min(...written.map(([, ttl]) => ttl));
|
|
43
|
+
}
|
|
30
44
|
// The kinds of task Claude Code keeps beside its turns that are a command or a monitor; a subagent has a row of its own.
|
|
31
45
|
const BACKGROUND_TASK_TYPES = new Set(['local_bash', 'monitor_mcp', 'monitor_ws']);
|
|
32
46
|
const ENDED_TASK_STATUSES = new Set(['completed', 'failed', 'killed', 'stopped']);
|
|
@@ -168,6 +182,9 @@ export class ClaudeProtocol {
|
|
|
168
182
|
backgroundTasks = new Set();
|
|
169
183
|
// Workflows still running, by task id, to the Workflow call that launched each.
|
|
170
184
|
workflows = new Map();
|
|
185
|
+
// The main-chain response whose cache lifetime the thread has, and the lifetime of the cache last written.
|
|
186
|
+
cacheMessageId = null;
|
|
187
|
+
cacheTtlMs;
|
|
171
188
|
// The uuid of the last main-chain assistant frame of the turn, which is the line of the transcript the turn ends on.
|
|
172
189
|
lastUuid = null;
|
|
173
190
|
// The model each subagent last answered with, so a frame only reports one it had not named yet.
|
|
@@ -187,9 +204,11 @@ export class ClaudeProtocol {
|
|
|
187
204
|
interrupting = false;
|
|
188
205
|
// Background subagents still at work; Claude Code holds a goal's check until they report.
|
|
189
206
|
backgroundAgents = new Set();
|
|
190
|
-
|
|
207
|
+
// `cacheTtlMs` is the lifetime the chat knew before this process, so a resumed CLI that only reads the cache still moves its expiry.
|
|
208
|
+
constructor(goal = null, now = () => Date.now(), cacheTtlMs = null) {
|
|
191
209
|
this.goal = goal;
|
|
192
210
|
this.now = now;
|
|
211
|
+
this.cacheTtlMs = cacheTtlMs;
|
|
193
212
|
}
|
|
194
213
|
/* A stop is on its way; the turn it ends leaves the goal set but nobody working on it. */
|
|
195
214
|
noteInterrupt() {
|
|
@@ -524,6 +543,7 @@ export class ClaudeProtocol {
|
|
|
524
543
|
}
|
|
525
544
|
if (event.type === 'message_start') {
|
|
526
545
|
const message = isRecord(event.message) ? event.message : {};
|
|
546
|
+
this.noteCache(str(message.id), message.usage, events);
|
|
527
547
|
this.streamMessageId = str(message.id);
|
|
528
548
|
this.streamTextCount = 0;
|
|
529
549
|
this.streamThinkingCount = 0;
|
|
@@ -559,6 +579,27 @@ export class ClaudeProtocol {
|
|
|
559
579
|
}
|
|
560
580
|
}
|
|
561
581
|
}
|
|
582
|
+
/*
|
|
583
|
+
* When the prompt cache goes cold, from the first frame of each main-chain response: its `message_start`, or its
|
|
584
|
+
* first `assistant` frame when the CLI streams no partial messages. The lifetime runs from the start of the request,
|
|
585
|
+
* and a response that only read the cache starts the lifetime it was written with again, as a cache hit does.
|
|
586
|
+
*/
|
|
587
|
+
noteCache(messageId, usage, events) {
|
|
588
|
+
if (messageId === null || messageId === this.cacheMessageId || !isRecord(usage)) {
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
this.cacheMessageId = messageId;
|
|
592
|
+
const written = cacheTtlWritten(usage);
|
|
593
|
+
if (written !== null) {
|
|
594
|
+
this.cacheTtlMs = written;
|
|
595
|
+
}
|
|
596
|
+
else if (num(usage.cache_read_input_tokens) === 0) {
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
if (this.cacheTtlMs !== null) {
|
|
600
|
+
events.push({ type: 'usage', cacheTtlMs: this.cacheTtlMs, cacheExpiresAt: this.now() + this.cacheTtlMs });
|
|
601
|
+
}
|
|
602
|
+
}
|
|
562
603
|
handleAssistant(frame, events) {
|
|
563
604
|
const message = isRecord(frame.message) ? frame.message : {};
|
|
564
605
|
const messageId = str(message.id) ?? 'message';
|
|
@@ -607,6 +648,9 @@ export class ClaudeProtocol {
|
|
|
607
648
|
if (tokens > 0) {
|
|
608
649
|
events.push({ type: 'usage', contextTokens: tokens });
|
|
609
650
|
}
|
|
651
|
+
if (!parentRef) {
|
|
652
|
+
this.noteCache(str(message.id), message.usage, events);
|
|
653
|
+
}
|
|
610
654
|
const error = str(frame.error);
|
|
611
655
|
if (error) {
|
|
612
656
|
events.push({ type: 'note', level: 'error', text: `The request failed: ${error.replaceAll('_', ' ')}` });
|
|
@@ -704,7 +748,7 @@ export class ClaudeProtocol {
|
|
|
704
748
|
events.push({ type: 'usage', costUsd: num(frame.total_cost_usd) });
|
|
705
749
|
}
|
|
706
750
|
if (frame.is_error === true) {
|
|
707
|
-
events.push({ type: 'note', level: 'warning', text: '
|
|
751
|
+
events.push({ type: 'note', level: 'warning', text: 'Claude could not finish a turn in the background. Your message is still waiting.' });
|
|
708
752
|
}
|
|
709
753
|
return;
|
|
710
754
|
}
|
|
@@ -243,7 +243,7 @@ export class CodexBackend {
|
|
|
243
243
|
async runGoalCommand(command, input) {
|
|
244
244
|
const transport = this.transport;
|
|
245
245
|
if (!transport) {
|
|
246
|
-
this.emit({ type: 'failed', message: '
|
|
246
|
+
this.emit({ type: 'failed', message: 'Codex is not running' });
|
|
247
247
|
return;
|
|
248
248
|
}
|
|
249
249
|
const threadId = this.threadId;
|
|
@@ -308,7 +308,7 @@ export class CodexBackend {
|
|
|
308
308
|
async goal(action) {
|
|
309
309
|
const transport = this.transport;
|
|
310
310
|
if (!transport) {
|
|
311
|
-
throw new Error('
|
|
311
|
+
throw new Error('Codex is not running');
|
|
312
312
|
}
|
|
313
313
|
if (action === 'clear') {
|
|
314
314
|
await transport.request('thread/goal/clear', { threadId: this.threadId, origin: 'user' });
|
|
@@ -417,7 +417,7 @@ export class CodexBackend {
|
|
|
417
417
|
listThreadItems(params) {
|
|
418
418
|
const transport = this.transport;
|
|
419
419
|
if (!transport) {
|
|
420
|
-
return Promise.reject(new Error('
|
|
420
|
+
return Promise.reject(new Error('Codex is not running'));
|
|
421
421
|
}
|
|
422
422
|
return transport.request('thread/items/list', params);
|
|
423
423
|
}
|
|
@@ -502,7 +502,7 @@ export class CodexBackend {
|
|
|
502
502
|
request(method, params, accepted) {
|
|
503
503
|
const transport = this.transport;
|
|
504
504
|
if (!transport) {
|
|
505
|
-
this.emit({ type: 'failed', message:
|
|
505
|
+
this.emit({ type: 'failed', message: 'Codex is not running' });
|
|
506
506
|
return;
|
|
507
507
|
}
|
|
508
508
|
void transport
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "Antigravity ACP server 1.4.1, `agy_acp_server.par`",
|
|
3
|
+
"source": "Not recorded: a question needs the model to choose to ask one. Built from `ExternalOnInteractionHook` in the server's acp_server/hooks.py inside agy_acp_server.par 1.4.1: a `tool_call` with an `interaction_` id and an empty rawInput, then one `session/request_permission` per question with an `allow_once` option per choice, answered with the chosen optionId.",
|
|
4
|
+
"sessionId": "agy-session-1",
|
|
5
|
+
"initialize": {
|
|
6
|
+
"protocolVersion": 1,
|
|
7
|
+
"agentCapabilities": {
|
|
8
|
+
"loadSession": true,
|
|
9
|
+
"promptCapabilities": {
|
|
10
|
+
"image": false,
|
|
11
|
+
"embeddedContext": true
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"authMethods": []
|
|
15
|
+
},
|
|
16
|
+
"sessionNew": {
|
|
17
|
+
"sessionId": "agy-session-1"
|
|
18
|
+
},
|
|
19
|
+
"commands": [],
|
|
20
|
+
"setConfigOption": {},
|
|
21
|
+
"promptUpdates": [
|
|
22
|
+
{
|
|
23
|
+
"sessionUpdate": "tool_call",
|
|
24
|
+
"toolCallId": "interaction_3f9a1c2e",
|
|
25
|
+
"title": "Which test runner should I set up?",
|
|
26
|
+
"rawInput": {},
|
|
27
|
+
"status": "pending"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"permission": {
|
|
31
|
+
"sessionId": "agy-session-1",
|
|
32
|
+
"toolCall": {
|
|
33
|
+
"toolCallId": "interaction_3f9a1c2e",
|
|
34
|
+
"title": "Which test runner should I set up?",
|
|
35
|
+
"rawInput": {},
|
|
36
|
+
"status": "pending"
|
|
37
|
+
},
|
|
38
|
+
"options": [
|
|
39
|
+
{
|
|
40
|
+
"optionId": "opt_builtin",
|
|
41
|
+
"name": "The built-in runner",
|
|
42
|
+
"kind": "allow_once"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"optionId": "opt_separate",
|
|
46
|
+
"name": "A separate runner",
|
|
47
|
+
"kind": "allow_once"
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"sessionUpdate": "tool_call_update",
|
|
54
|
+
"toolCallId": "interaction_3f9a1c2e",
|
|
55
|
+
"status": "completed",
|
|
56
|
+
"rawOutput": "Response received"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"sessionUpdate": "agent_message_chunk",
|
|
60
|
+
"content": {
|
|
61
|
+
"type": "text",
|
|
62
|
+
"text": "Setting it up."
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
],
|
|
66
|
+
"promptResponse": {
|
|
67
|
+
"stopReason": "end_turn"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "Antigravity ACP server 1.4.1, `agy_acp_server.par`",
|
|
3
|
+
"source": "Not recorded: no account here runs out of quota on demand. Built from the server's own Python modules inside agy_acp_server.par 1.4.1 (acp_server/server.py and ccpa_connection/quota_errors.py): on QUOTA_EXHAUSTED it writes this message as an agent_message_chunk and answers the prompt with `end_turn`; the reset is the delay in its two largest units, or the time when only that is known.",
|
|
4
|
+
"sessionId": "agy-session-1",
|
|
5
|
+
"initialize": {
|
|
6
|
+
"protocolVersion": 1,
|
|
7
|
+
"agentCapabilities": {
|
|
8
|
+
"loadSession": true,
|
|
9
|
+
"promptCapabilities": {
|
|
10
|
+
"image": false,
|
|
11
|
+
"embeddedContext": true
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"authMethods": []
|
|
15
|
+
},
|
|
16
|
+
"sessionNew": {
|
|
17
|
+
"sessionId": "agy-session-1"
|
|
18
|
+
},
|
|
19
|
+
"commands": [],
|
|
20
|
+
"setConfigOption": {},
|
|
21
|
+
"promptUpdates": [
|
|
22
|
+
{
|
|
23
|
+
"sessionUpdate": "agent_message_chunk",
|
|
24
|
+
"content": {
|
|
25
|
+
"type": "text",
|
|
26
|
+
"text": "Usage Limit Reached\n\nYou have reached your current quota for this period. Your limit will reset in 4 days, 23 hours."
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"promptResponse": {
|
|
31
|
+
"stopReason": "end_turn"
|
|
32
|
+
},
|
|
33
|
+
"resetOn": "Usage Limit Reached\n\nYou have reached your current quota for this period. Your limit will reset on Oct 23, 2026 14:05 UTC.",
|
|
34
|
+
"noReset": "Usage Limit Reached\n\nYou have reached your current quota for this period."
|
|
35
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "Grok 1.0.62, `grok --no-auto-update agent stdio`",
|
|
3
|
+
"source": "Not recorded: no account here runs into these limits on demand. Built from the source of xai-org/grok-build at 2bdd1d6a, the closest to 1.0.62 it publishes: `handle_sampling_failure` in acp_session_impl/sampler_turn.rs fails `session/prompt` with -32003 after a 429 and with -32603 for an overloaded model once its retries ran out, and sampling/error.rs builds the `data` (a string, or `message` with `http_status`). The bodies after the status line are examples.",
|
|
4
|
+
"sessionId": "grok-session-1",
|
|
5
|
+
"initialize": {
|
|
6
|
+
"protocolVersion": 1,
|
|
7
|
+
"agentCapabilities": {
|
|
8
|
+
"loadSession": true,
|
|
9
|
+
"promptCapabilities": {
|
|
10
|
+
"image": false,
|
|
11
|
+
"embeddedContext": true
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"authMethods": []
|
|
15
|
+
},
|
|
16
|
+
"sessionNew": {
|
|
17
|
+
"sessionId": "grok-session-1"
|
|
18
|
+
},
|
|
19
|
+
"commands": [],
|
|
20
|
+
"setConfigOption": {},
|
|
21
|
+
"promptUpdates": [
|
|
22
|
+
{
|
|
23
|
+
"sessionUpdate": "agent_thought_chunk",
|
|
24
|
+
"content": {
|
|
25
|
+
"type": "text",
|
|
26
|
+
"text": "Reading the request."
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"promptResponse": {
|
|
31
|
+
"stopReason": "end_turn"
|
|
32
|
+
},
|
|
33
|
+
"errors": {
|
|
34
|
+
"freeUsage": {
|
|
35
|
+
"code": -32003,
|
|
36
|
+
"message": "Rate limited",
|
|
37
|
+
"data": "API error (status 429 Too Many Requests): {\"code\":\"subscription:free-usage-exhausted\",\"error\":\"You have used the free usage of this period.\"}"
|
|
38
|
+
},
|
|
39
|
+
"rateLimited": {
|
|
40
|
+
"code": -32003,
|
|
41
|
+
"message": "Rate limited",
|
|
42
|
+
"data": "API error (status 429 Too Many Requests): {\"code\":\"rate_limit_exceeded\",\"error\":\"Too many requests.\"}"
|
|
43
|
+
},
|
|
44
|
+
"overloaded": {
|
|
45
|
+
"code": -32603,
|
|
46
|
+
"message": "Internal error",
|
|
47
|
+
"data": {
|
|
48
|
+
"message": "API error (status 529): Overloaded",
|
|
49
|
+
"http_status": 529
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"overloadedStream": {
|
|
53
|
+
"code": -32603,
|
|
54
|
+
"message": "Internal error",
|
|
55
|
+
"data": "stream error (overloaded_error): Overloaded"
|
|
56
|
+
},
|
|
57
|
+
"badRequest": {
|
|
58
|
+
"code": -32603,
|
|
59
|
+
"message": "Internal error",
|
|
60
|
+
"data": {
|
|
61
|
+
"message": "API error (status 400 Bad Request): The prompt is too long.",
|
|
62
|
+
"http_status": 400
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "Grok 1.0.62, `grok --no-auto-update agent stdio`",
|
|
3
|
+
"source": "Not recorded: a question needs the model to choose to ask one. Built from the source of xai-org/grok-build at 2bdd1d6a, the closest to 1.0.62 it publishes: the ext request `_x.ai/ask_user_question` (acp_session_impl/spawn.rs) with the params of ask_user_question/types.rs; its answer is `{ outcome: \"accepted\", answers, annotations? }`, keyed by the question's text.",
|
|
4
|
+
"sessionId": "grok-session-1",
|
|
5
|
+
"initialize": {
|
|
6
|
+
"protocolVersion": 1,
|
|
7
|
+
"agentCapabilities": {
|
|
8
|
+
"loadSession": true,
|
|
9
|
+
"promptCapabilities": {
|
|
10
|
+
"image": false,
|
|
11
|
+
"embeddedContext": true
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"authMethods": []
|
|
15
|
+
},
|
|
16
|
+
"sessionNew": {
|
|
17
|
+
"sessionId": "grok-session-1"
|
|
18
|
+
},
|
|
19
|
+
"commands": [],
|
|
20
|
+
"setConfigOption": {},
|
|
21
|
+
"promptUpdates": [
|
|
22
|
+
{
|
|
23
|
+
"sessionUpdate": "tool_call",
|
|
24
|
+
"toolCallId": "call-ask-1",
|
|
25
|
+
"title": "Ask the user",
|
|
26
|
+
"kind": "other",
|
|
27
|
+
"status": "pending"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"request": {
|
|
31
|
+
"method": "_x.ai/ask_user_question",
|
|
32
|
+
"params": {
|
|
33
|
+
"sessionId": "grok-session-1",
|
|
34
|
+
"toolCallId": "call-ask-1",
|
|
35
|
+
"mode": "default",
|
|
36
|
+
"questions": [
|
|
37
|
+
{
|
|
38
|
+
"question": "Which database should the service use?",
|
|
39
|
+
"options": [
|
|
40
|
+
{
|
|
41
|
+
"label": "Postgres",
|
|
42
|
+
"description": "The one the other services run on"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"label": "SQLite",
|
|
46
|
+
"description": "A file beside the service"
|
|
47
|
+
}
|
|
48
|
+
],
|
|
49
|
+
"multiSelect": false
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"question": "Which checks should run before a release?",
|
|
53
|
+
"options": [
|
|
54
|
+
{
|
|
55
|
+
"label": "Tests",
|
|
56
|
+
"description": "The unit tests"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"label": "Lint, strict",
|
|
60
|
+
"description": "Every warning fails"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"label": "Types",
|
|
64
|
+
"description": "The type check"
|
|
65
|
+
}
|
|
66
|
+
],
|
|
67
|
+
"multiSelect": true
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"question": "Anything else to keep in mind?",
|
|
71
|
+
"options": [
|
|
72
|
+
{
|
|
73
|
+
"label": "No",
|
|
74
|
+
"description": "Nothing"
|
|
75
|
+
}
|
|
76
|
+
],
|
|
77
|
+
"multiSelect": null
|
|
78
|
+
}
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"sessionUpdate": "tool_call_update",
|
|
85
|
+
"toolCallId": "call-ask-1",
|
|
86
|
+
"status": "completed"
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"sessionUpdate": "agent_message_chunk",
|
|
90
|
+
"content": {
|
|
91
|
+
"type": "text",
|
|
92
|
+
"text": "Noted."
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
],
|
|
96
|
+
"promptResponse": {
|
|
97
|
+
"stopReason": "end_turn"
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -48,7 +48,7 @@ export class JsonRpcTransport {
|
|
|
48
48
|
const timeoutMs = options.timeoutMs === undefined ? this.options.requestTimeoutMs : options.timeoutMs;
|
|
49
49
|
return new Promise((resolve, reject) => {
|
|
50
50
|
if (this.closed) {
|
|
51
|
-
reject(new Error(`${
|
|
51
|
+
reject(new Error(`${label} is not running`));
|
|
52
52
|
return;
|
|
53
53
|
}
|
|
54
54
|
const id = this.nextId++;
|
|
@@ -122,7 +122,7 @@ export class JsonRpcTransport {
|
|
|
122
122
|
this.failed(error);
|
|
123
123
|
}
|
|
124
124
|
failed(error) {
|
|
125
|
-
const failure = new Error(
|
|
125
|
+
const failure = new Error(`Lost the connection to ${this.options.label}: ${errorText(error)}`);
|
|
126
126
|
this.rejectPending(() => failure);
|
|
127
127
|
try {
|
|
128
128
|
this.options.onError?.(failure);
|
package/dist/chat/projector.js
CHANGED
|
@@ -308,16 +308,28 @@ export class ThreadProjector {
|
|
|
308
308
|
break;
|
|
309
309
|
}
|
|
310
310
|
case 'usage': {
|
|
311
|
-
const
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
311
|
+
const usage = info.usage;
|
|
312
|
+
const contextTokens = event.contextTokens ?? usage.contextTokens;
|
|
313
|
+
const next = {
|
|
314
|
+
...usage,
|
|
315
|
+
contextTokens,
|
|
316
|
+
contextWindow: event.contextWindow ?? usage.contextWindow,
|
|
317
|
+
costUsd: Math.max(usage.costUsd, event.costUsd ?? 0),
|
|
318
|
+
// The estimate reads the whole thread, so only a new count of tokens is worth it.
|
|
319
|
+
...(contextTokens === usage.contextTokens
|
|
320
|
+
? {}
|
|
321
|
+
: { breakdown: contextTokens > 0 ? estimateContextBreakdown(this.thread.list(), contextTokens) : undefined }),
|
|
322
|
+
...(event.cacheTtlMs === undefined || event.cacheExpiresAt === undefined
|
|
323
|
+
? {}
|
|
324
|
+
: { cacheTtlMs: event.cacheTtlMs, cacheExpiresAt: event.cacheExpiresAt })
|
|
325
|
+
};
|
|
326
|
+
if (next.contextTokens !== usage.contextTokens ||
|
|
327
|
+
next.contextWindow !== usage.contextWindow ||
|
|
328
|
+
next.costUsd !== usage.costUsd ||
|
|
329
|
+
next.cacheTtlMs !== usage.cacheTtlMs ||
|
|
330
|
+
next.cacheExpiresAt !== usage.cacheExpiresAt) {
|
|
331
|
+
events.push(this.thread.patchInfo({ usage: next }));
|
|
332
|
+
}
|
|
321
333
|
break;
|
|
322
334
|
}
|
|
323
335
|
case 'compaction':
|
|
@@ -153,6 +153,10 @@ export class ProviderAccountsService {
|
|
|
153
153
|
};
|
|
154
154
|
if (isDefaultAccountOf(kind, id)) {
|
|
155
155
|
const account = this.accounts[kind] ?? { kind };
|
|
156
|
+
// Only a start that names it: every turn of a chat on the default account asks with none.
|
|
157
|
+
if (id !== undefined && account.enabled === false) {
|
|
158
|
+
throw refuse('it is turned off');
|
|
159
|
+
}
|
|
156
160
|
return accountEnv(kind, account, provider, baseEnv, variables(account));
|
|
157
161
|
}
|
|
158
162
|
const account = this.accounts[key];
|
|
@@ -31,7 +31,8 @@ export function createClaudeProvider(options = {}) {
|
|
|
31
31
|
{ kind: 'env', variable: 'ANTHROPIC_API_KEY' }
|
|
32
32
|
]
|
|
33
33
|
},
|
|
34
|
-
|
|
34
|
+
// The version probe must never be the start that updates the CLI.
|
|
35
|
+
detect: (command, env) => detectCli(command, { ...env, DISABLE_AUTOUPDATER: '1' }),
|
|
35
36
|
oneShotArgs: (prompt) => ['-p', prompt, '--output-format', 'text'],
|
|
36
37
|
// `-p` without a prompt argument reads the prompt from stdin.
|
|
37
38
|
oneShotStdinArgs: ['-p', '--output-format', 'text'],
|
|
@@ -3,4 +3,4 @@ export interface CliDetection {
|
|
|
3
3
|
installed: boolean;
|
|
4
4
|
version: string | null;
|
|
5
5
|
}
|
|
6
|
-
export declare function detectCli(command: string, env?: Record<string, string | undefined>, spawn?: SpawnChatProcess): Promise<CliDetection>;
|
|
6
|
+
export declare function detectCli(command: string, env?: Record<string, string | undefined>, spawn?: SpawnChatProcess, flags?: readonly string[]): Promise<CliDetection>;
|
package/dist/providers/detect.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { spawnChatProcess } from '../chat/chat-process.js';
|
|
2
2
|
import { definedEnv } from './accounts/launch.js';
|
|
3
|
-
/*
|
|
4
|
-
|
|
3
|
+
/*
|
|
4
|
+
* Asks a CLI for its version; a missing binary is a plain "not installed", never an error. `flags` go before
|
|
5
|
+
* `--version`, for a CLI that would otherwise look for an update of itself on any start.
|
|
6
|
+
*/
|
|
7
|
+
export async function detectCli(command, env = process.env, spawn = spawnChatProcess, flags = []) {
|
|
5
8
|
try {
|
|
6
9
|
let exited = () => undefined;
|
|
7
10
|
const code = new Promise((resolve) => {
|
|
8
11
|
exited = resolve;
|
|
9
12
|
});
|
|
10
|
-
const proc = spawn({ command: [command, '--version'], cwd: process.cwd(), env: definedEnv(env), onExit: (exitCode) => exited(exitCode) });
|
|
13
|
+
const proc = spawn({ command: [command, ...flags, '--version'], cwd: process.cwd(), env: definedEnv(env), onExit: (exitCode) => exited(exitCode) });
|
|
11
14
|
proc.stdin.end();
|
|
12
15
|
const output = await new Response(proc.stdout).text();
|
|
13
16
|
if ((await code) !== 0) {
|
|
@@ -16,7 +16,11 @@ export declare class ProviderRegistry {
|
|
|
16
16
|
private readonly detect;
|
|
17
17
|
private readonly env;
|
|
18
18
|
private readonly cache;
|
|
19
|
+
private readonly listeners;
|
|
20
|
+
private readonly stops;
|
|
19
21
|
constructor(options?: ProviderRegistryOptions);
|
|
22
|
+
add(provider: ChatProvider): void;
|
|
23
|
+
remove(kind: AgentKind): void;
|
|
20
24
|
list(): Promise<ProviderInfo[]>;
|
|
21
25
|
kinds(): AgentKind[];
|
|
22
26
|
get(kind: AgentKind): ChatProvider;
|
|
@@ -26,5 +30,8 @@ export declare class ProviderRegistry {
|
|
|
26
30
|
onChange(listener: (kind: AgentKind) => void): () => void;
|
|
27
31
|
invalidate(kind: AgentKind): void;
|
|
28
32
|
protected fallback(kind: AgentKind): ChatProvider;
|
|
33
|
+
private follow;
|
|
34
|
+
private unfollow;
|
|
35
|
+
private changed;
|
|
29
36
|
private detection;
|
|
30
37
|
}
|