@adecore/agents 0.48.4 → 0.49.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/claude-protocol.js +17 -1
- package/dist/chat/codex-backend.js +10 -1
- package/dist/chat/codex-protocol.d.ts +1 -1
- package/dist/chat/codex-protocol.js +7 -2
- package/package.json +3 -3
- package/src/chat/claude-protocol.ts +21 -2
- package/src/chat/codex-backend.ts +17 -1
- package/src/chat/codex-protocol.ts +8 -2
|
@@ -22,6 +22,12 @@ function taskUsage(usage) {
|
|
|
22
22
|
function contextTokens(usage) {
|
|
23
23
|
return isRecord(usage) ? num(usage.input_tokens) + num(usage.cache_creation_input_tokens) + num(usage.cache_read_input_tokens) : 0;
|
|
24
24
|
}
|
|
25
|
+
/*
|
|
26
|
+
* Claude Code 2.1.312 says `completed` for a subagent that stopped to wait on its own background work, and
|
|
27
|
+
* notifies the same task again when it reports. No field tells them apart: `handback` is also absent from a
|
|
28
|
+
* subagent that does not report through `SubagentHandback`, and `background_tasks_changed` drops the agent too.
|
|
29
|
+
*/
|
|
30
|
+
const INTERIM_TASK_SUMMARY = 'This agent has not reported yet';
|
|
25
31
|
// How long a prompt cache entry lives, by the field of `usage.cache_creation` that counts what a request wrote with it.
|
|
26
32
|
const CACHE_TTL_MS = { ephemeral_5m_input_tokens: 5 * 60_000, ephemeral_1h_input_tokens: 60 * 60_000 };
|
|
27
33
|
/*
|
|
@@ -409,10 +415,20 @@ export class ClaudeProtocol {
|
|
|
409
415
|
}
|
|
410
416
|
else if (frame.subtype === 'task_notification') {
|
|
411
417
|
const ref = str(frame.tool_use_id);
|
|
418
|
+
const taskId = str(frame.task_id);
|
|
419
|
+
if (str(frame.status) === 'completed' && str(frame.summary)?.startsWith(INTERIM_TASK_SUMMARY)) {
|
|
420
|
+
// `task_updated` already took it off the list of running agents.
|
|
421
|
+
if (taskId) {
|
|
422
|
+
this.backgroundAgents.add(taskId);
|
|
423
|
+
}
|
|
424
|
+
if (ref) {
|
|
425
|
+
events.push({ type: 'task.progress', ref, taskId, summary: null, lastTool: null, usage: taskUsage(frame.usage) });
|
|
426
|
+
}
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
412
429
|
if (ref) {
|
|
413
430
|
this.subagentModels.delete(ref);
|
|
414
431
|
}
|
|
415
|
-
const taskId = str(frame.task_id);
|
|
416
432
|
if (taskId) {
|
|
417
433
|
this.endBackground(taskId, events);
|
|
418
434
|
this.workflows.delete(taskId);
|
|
@@ -146,7 +146,7 @@ export class CodexBackend {
|
|
|
146
146
|
result = await transport.request('thread/start', { ...params, ...instructions });
|
|
147
147
|
}
|
|
148
148
|
this.imageInputSupported = await this.readImageSupport(transport);
|
|
149
|
-
for (const event of this.protocol.threadReady(result)) {
|
|
149
|
+
for (const event of this.protocol.threadReady(result, params.sandbox)) {
|
|
150
150
|
if (event.type === 'session' && event.agentSessionId) {
|
|
151
151
|
this.threadId = event.agentSessionId;
|
|
152
152
|
}
|
|
@@ -213,6 +213,15 @@ export class CodexBackend {
|
|
|
213
213
|
return [...textInput(parts.join('')), ...images.map((attachment) => ({ type: 'localImage', path: attachment.path }))];
|
|
214
214
|
}
|
|
215
215
|
sendTurn(input) {
|
|
216
|
+
// TUI menus are not model instructions; explain the missing host flow instead of spending a turn on it.
|
|
217
|
+
if (this.transport &&
|
|
218
|
+
input.attachments.length === 0 &&
|
|
219
|
+
input.mentions.length === 0 &&
|
|
220
|
+
input.skills.length === 0 &&
|
|
221
|
+
/^\/usage(?:[ \t]+[^\r\n]*)?$/.test(input.text.trim())) {
|
|
222
|
+
this.reply(input.promptId, 'This chat does not support the /usage menu. Open Codex in a terminal and run /usage to view account activity or use an available earned rate-limit reset.');
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
216
225
|
const command = input.attachments.length === 0 ? parseGoalCommand(input.text) : null;
|
|
217
226
|
if (command !== null) {
|
|
218
227
|
void this.runGoalCommand(command, input);
|
|
@@ -24,7 +24,7 @@ export declare class CodexProtocol {
|
|
|
24
24
|
private readonly agentRows;
|
|
25
25
|
constructor(generation: number);
|
|
26
26
|
get turnId(): string | null;
|
|
27
|
-
threadReady(result: unknown): BackendEvent[];
|
|
27
|
+
threadReady(result: unknown, askedSandbox?: string): BackendEvent[];
|
|
28
28
|
handle(frame: CodexFrame): BackendEvent[];
|
|
29
29
|
approvalDecision(requestId: string, decision: ApprovalDecision): {
|
|
30
30
|
rpcId: CodexRpcId;
|
|
@@ -57,6 +57,7 @@ function textOf(content) {
|
|
|
57
57
|
.filter((text) => text !== null)
|
|
58
58
|
.join('\n');
|
|
59
59
|
}
|
|
60
|
+
const SANDBOX_NOT_SET_UP = "Codex's sandbox is not set up here, so it can only read and asks before each command. Run `codex` once in a terminal to set it up.";
|
|
60
61
|
// What `agentsStates` says of an agent that stopped working; the call that reports it may have completed long before.
|
|
61
62
|
const ENDED_AGENT_STATES = new Set(['completed', 'errored', 'interrupted', 'shutdown', 'notFound']);
|
|
62
63
|
/* What became of every agent a collab call touched, one line each; empty while the call still runs. */
|
|
@@ -189,7 +190,7 @@ export class CodexProtocol {
|
|
|
189
190
|
return this.codexTurnId;
|
|
190
191
|
}
|
|
191
192
|
/* The result of `thread/start` or `thread/resume`. The thread id is what a terminal resumes. */
|
|
192
|
-
threadReady(result) {
|
|
193
|
+
threadReady(result, askedSandbox) {
|
|
193
194
|
const thread = isRecord(result) && isRecord(result.thread) ? result.thread : {};
|
|
194
195
|
this.ownThreadId = str(thread.id) ?? this.ownThreadId;
|
|
195
196
|
const policy = isRecord(result) ? str(result.approvalPolicy) : null;
|
|
@@ -203,7 +204,11 @@ export class CodexProtocol {
|
|
|
203
204
|
title: cleanTitle(thread.name),
|
|
204
205
|
...(policy && sandbox ? { permissionMode: `${policy} / ${sandbox}` } : {}),
|
|
205
206
|
...(effectiveRuntimeMode === undefined ? {} : { effectiveRuntimeMode })
|
|
206
|
-
}
|
|
207
|
+
},
|
|
208
|
+
// On Windows a sandbox that was never set up leaves a thread read-only without an error, and the question that sets it up lives in Codex's TUI.
|
|
209
|
+
...(askedSandbox === 'workspace-write' && sandbox === 'readOnly'
|
|
210
|
+
? [{ type: 'note', level: 'warning', text: SANDBOX_NOT_SET_UP }]
|
|
211
|
+
: [])
|
|
207
212
|
];
|
|
208
213
|
}
|
|
209
214
|
handle(frame) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adecore/agents",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.49.0",
|
|
4
4
|
"description": "Node-compatible agent chat hosts, providers, accounts, usage and durable task coordination.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -93,8 +93,8 @@
|
|
|
93
93
|
"test": "bun --conditions=source test"
|
|
94
94
|
},
|
|
95
95
|
"dependencies": {
|
|
96
|
-
"@adecore/agent-contracts": "0.
|
|
97
|
-
"@adecore/intelligent-ui": "0.
|
|
96
|
+
"@adecore/agent-contracts": "0.49.0",
|
|
97
|
+
"@adecore/intelligent-ui": "0.49.0",
|
|
98
98
|
"zod": "^4.6.7"
|
|
99
99
|
},
|
|
100
100
|
"devDependencies": {
|
|
@@ -41,6 +41,13 @@ function contextTokens(usage: unknown): number {
|
|
|
41
41
|
return isRecord(usage) ? num(usage.input_tokens) + num(usage.cache_creation_input_tokens) + num(usage.cache_read_input_tokens) : 0;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
/*
|
|
45
|
+
* Claude Code 2.1.312 says `completed` for a subagent that stopped to wait on its own background work, and
|
|
46
|
+
* notifies the same task again when it reports. No field tells them apart: `handback` is also absent from a
|
|
47
|
+
* subagent that does not report through `SubagentHandback`, and `background_tasks_changed` drops the agent too.
|
|
48
|
+
*/
|
|
49
|
+
const INTERIM_TASK_SUMMARY = 'This agent has not reported yet';
|
|
50
|
+
|
|
44
51
|
// How long a prompt cache entry lives, by the field of `usage.cache_creation` that counts what a request wrote with it.
|
|
45
52
|
const CACHE_TTL_MS: Record<string, number> = { ephemeral_5m_input_tokens: 5 * 60_000, ephemeral_1h_input_tokens: 60 * 60_000 };
|
|
46
53
|
|
|
@@ -511,13 +518,25 @@ export class ClaudeProtocol {
|
|
|
511
518
|
}
|
|
512
519
|
} else if (frame.subtype === 'task_notification') {
|
|
513
520
|
const ref = str(frame.tool_use_id);
|
|
521
|
+
const taskId = str(frame.task_id);
|
|
522
|
+
|
|
523
|
+
if (str(frame.status) === 'completed' && str(frame.summary)?.startsWith(INTERIM_TASK_SUMMARY)) {
|
|
524
|
+
// `task_updated` already took it off the list of running agents.
|
|
525
|
+
if (taskId) {
|
|
526
|
+
this.backgroundAgents.add(taskId);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
if (ref) {
|
|
530
|
+
events.push({ type: 'task.progress', ref, taskId, summary: null, lastTool: null, usage: taskUsage(frame.usage) });
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
514
535
|
|
|
515
536
|
if (ref) {
|
|
516
537
|
this.subagentModels.delete(ref);
|
|
517
538
|
}
|
|
518
539
|
|
|
519
|
-
const taskId = str(frame.task_id);
|
|
520
|
-
|
|
521
540
|
if (taskId) {
|
|
522
541
|
this.endBackground(taskId, events);
|
|
523
542
|
this.workflows.delete(taskId);
|
|
@@ -180,7 +180,7 @@ export class CodexBackend implements ChatBackend {
|
|
|
180
180
|
|
|
181
181
|
this.imageInputSupported = await this.readImageSupport(transport);
|
|
182
182
|
|
|
183
|
-
for (const event of this.protocol.threadReady(result)) {
|
|
183
|
+
for (const event of this.protocol.threadReady(result, params.sandbox)) {
|
|
184
184
|
if (event.type === 'session' && event.agentSessionId) {
|
|
185
185
|
this.threadId = event.agentSessionId;
|
|
186
186
|
}
|
|
@@ -264,6 +264,22 @@ export class CodexBackend implements ChatBackend {
|
|
|
264
264
|
}
|
|
265
265
|
|
|
266
266
|
sendTurn(input: TurnInput): void {
|
|
267
|
+
// TUI menus are not model instructions; explain the missing host flow instead of spending a turn on it.
|
|
268
|
+
if (
|
|
269
|
+
this.transport &&
|
|
270
|
+
input.attachments.length === 0 &&
|
|
271
|
+
input.mentions.length === 0 &&
|
|
272
|
+
input.skills.length === 0 &&
|
|
273
|
+
/^\/usage(?:[ \t]+[^\r\n]*)?$/.test(input.text.trim())
|
|
274
|
+
) {
|
|
275
|
+
this.reply(
|
|
276
|
+
input.promptId,
|
|
277
|
+
'This chat does not support the /usage menu. Open Codex in a terminal and run /usage to view account activity or use an available earned rate-limit reset.'
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
267
283
|
const command = input.attachments.length === 0 ? parseGoalCommand(input.text) : null;
|
|
268
284
|
|
|
269
285
|
if (command !== null) {
|
|
@@ -83,6 +83,8 @@ function textOf(content: unknown): string {
|
|
|
83
83
|
.join('\n');
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
const SANDBOX_NOT_SET_UP = "Codex's sandbox is not set up here, so it can only read and asks before each command. Run `codex` once in a terminal to set it up.";
|
|
87
|
+
|
|
86
88
|
// What `agentsStates` says of an agent that stopped working; the call that reports it may have completed long before.
|
|
87
89
|
const ENDED_AGENT_STATES = new Set(['completed', 'errored', 'interrupted', 'shutdown', 'notFound']);
|
|
88
90
|
|
|
@@ -259,7 +261,7 @@ export class CodexProtocol {
|
|
|
259
261
|
}
|
|
260
262
|
|
|
261
263
|
/* The result of `thread/start` or `thread/resume`. The thread id is what a terminal resumes. */
|
|
262
|
-
threadReady(result: unknown): BackendEvent[] {
|
|
264
|
+
threadReady(result: unknown, askedSandbox?: string): BackendEvent[] {
|
|
263
265
|
const thread = isRecord(result) && isRecord(result.thread) ? result.thread : {};
|
|
264
266
|
this.ownThreadId = str(thread.id) ?? this.ownThreadId;
|
|
265
267
|
const policy = isRecord(result) ? str(result.approvalPolicy) : null;
|
|
@@ -274,7 +276,11 @@ export class CodexProtocol {
|
|
|
274
276
|
title: cleanTitle(thread.name),
|
|
275
277
|
...(policy && sandbox ? { permissionMode: `${policy} / ${sandbox}` } : {}),
|
|
276
278
|
...(effectiveRuntimeMode === undefined ? {} : { effectiveRuntimeMode })
|
|
277
|
-
}
|
|
279
|
+
},
|
|
280
|
+
// On Windows a sandbox that was never set up leaves a thread read-only without an error, and the question that sets it up lives in Codex's TUI.
|
|
281
|
+
...(askedSandbox === 'workspace-write' && sandbox === 'readOnly'
|
|
282
|
+
? [{ type: 'note' as const, level: 'warning' as const, text: SANDBOX_NOT_SET_UP }]
|
|
283
|
+
: [])
|
|
278
284
|
];
|
|
279
285
|
}
|
|
280
286
|
|