@adecore/agents 0.38.0 → 0.40.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/async.d.ts +5 -0
- package/dist/async.js +13 -5
- package/dist/chat/acp-protocol.js +3 -5
- package/dist/chat/chat-core.d.ts +1 -0
- package/dist/chat/chat-core.js +9 -2
- package/dist/chat/chat-process.d.ts +1 -0
- package/dist/chat/chat-process.js +1 -1
- package/dist/chat/chat-session.d.ts +2 -0
- package/dist/chat/chat-session.js +9 -10
- package/dist/chat/claude-protocol.js +1 -6
- package/dist/chat/claude-transcript.js +1 -3
- package/dist/chat/codex-protocol.js +1 -6
- package/dist/chat/projector.js +1 -3
- package/dist/chat/request-summary.js +1 -3
- package/dist/coded-error.js +1 -5
- package/dist/context/argv.js +1 -2
- package/dist/context/verb.js +1 -3
- package/dist/error-text.d.ts +1 -0
- package/dist/error-text.js +4 -0
- package/dist/fields.d.ts +3 -0
- package/dist/fields.js +12 -0
- package/dist/fs.d.ts +1 -0
- package/dist/fs.js +8 -4
- package/dist/lineage.js +11 -5
- package/dist/messages/notice-store.js +3 -4
- package/dist/providers/accounts/acp-account.js +3 -20
- package/dist/providers/accounts/service.d.ts +1 -2
- package/dist/providers/accounts/service.js +5 -9
- package/dist/providers/accounts/status.js +1 -3
- package/dist/providers/acp-agent-state.js +1 -3
- package/dist/providers/claude.js +0 -1
- package/dist/providers/codex.js +0 -1
- package/dist/providers/detect.js +1 -1
- package/dist/providers/shell-commands.js +1 -2
- package/dist/run-process.js +1 -3
- package/dist/skills.d.ts +1 -1
- package/dist/skills.js +5 -8
- package/dist/tasks/background-limit.js +3 -2
- package/dist/tasks/task-coordinator.js +11 -17
- package/dist/testing/watch.js +0 -1
- package/dist/usage/limits/monitor.js +1 -2
- package/dist/usage/readers/claude.js +3 -10
- package/dist/usage/readers/codex.js +2 -9
- package/dist/usage/readers/grok.js +3 -10
- package/dist/usage/readers/host-log.js +3 -10
- package/dist/usage/record.d.ts +2 -0
- package/dist/usage/record.js +13 -0
- package/package.json +6 -6
- package/src/async.ts +23 -5
- package/src/chat/acp-backend.ts +121 -0
- package/src/chat/acp-protocol.ts +172 -6
- package/src/chat/attachment-store.ts +21 -0
- package/src/chat/backend.ts +0 -2
- package/src/chat/bookmark-store.ts +23 -0
- package/src/chat/chat-core.ts +218 -7
- package/src/chat/chat-log.ts +19 -0
- package/src/chat/chat-process.ts +52 -1
- package/src/chat/chat-session.ts +414 -13
- package/src/chat/chat-store.ts +50 -0
- package/src/chat/chat-title.ts +9 -0
- package/src/chat/claude-backend.ts +50 -0
- package/src/chat/claude-protocol.ts +155 -8
- package/src/chat/claude-title.ts +10 -0
- package/src/chat/claude-transcript.ts +47 -4
- package/src/chat/codex-backend.ts +131 -0
- package/src/chat/codex-protocol.ts +180 -8
- package/src/chat/codex-thread.ts +23 -0
- package/src/chat/composer-preferences.ts +6 -0
- package/src/chat/context-breakdown.ts +11 -0
- package/src/chat/delta-coalescer.ts +8 -0
- package/src/chat/external-sessions.ts +28 -0
- package/src/chat/generated-image.ts +25 -0
- package/src/chat/index.ts +5 -0
- package/src/chat/input.ts +13 -0
- package/src/chat/json-rpc-transport.ts +28 -0
- package/src/chat/limit-resume.ts +13 -0
- package/src/chat/projector.ts +160 -4
- package/src/chat/readable-text.ts +3 -0
- package/src/chat/request-summary.ts +18 -4
- package/src/chat/subagent-projection.ts +3 -0
- package/src/chat/subagent-reader.ts +76 -0
- package/src/chat/subagent-settlement.ts +24 -0
- package/src/chat/thread-search.ts +13 -0
- package/src/chat/thread.ts +31 -0
- package/src/chat/ui-queries.ts +122 -0
- package/src/chat/ui-replies.ts +34 -0
- package/src/chat/visual-store.ts +49 -0
- package/src/chat/wake-chat.ts +3 -0
- package/src/client-sinks.ts +2 -0
- package/src/coded-error.ts +1 -5
- package/src/context/argv.ts +18 -2
- package/src/context/index.ts +1 -0
- package/src/context/refusal.ts +3 -0
- package/src/context/verb.ts +32 -4
- package/src/coordination/index.ts +10 -0
- package/src/error-text.ts +7 -0
- package/src/fields.ts +14 -0
- package/src/fs.ts +20 -4
- package/src/host/agent-host.ts +20 -0
- package/src/host/environment.ts +6 -0
- package/src/host/handlers.ts +18 -0
- package/src/host/memory-port.ts +4 -0
- package/src/host/wiring.ts +10 -0
- package/src/index.ts +1 -0
- package/src/lineage.ts +24 -5
- package/src/messages/deliver-message.ts +6 -0
- package/src/messages/deliver-notice.ts +13 -0
- package/src/messages/notice-store.ts +16 -4
- package/src/modes.ts +1 -0
- package/src/outbox/outbox-worker.ts +31 -0
- package/src/outbox/outbox.ts +5 -0
- package/src/providers/accounts/accounts.ts +36 -0
- package/src/providers/accounts/acp-account.ts +22 -27
- package/src/providers/accounts/launch.ts +4 -0
- package/src/providers/accounts/service.ts +110 -12
- package/src/providers/accounts/shadow-home.ts +10 -0
- package/src/providers/accounts/status.ts +13 -4
- package/src/providers/accounts/store.ts +25 -0
- package/src/providers/accounts/variables.ts +11 -1
- package/src/providers/acp-agent-state.ts +24 -3
- package/src/providers/acp-provider.ts +5 -0
- package/src/providers/acp-usage-log.ts +2 -0
- package/src/providers/catalog.ts +17 -0
- package/src/providers/claude.ts +10 -1
- package/src/providers/codex-rules.ts +3 -0
- package/src/providers/codex.ts +2 -1
- package/src/providers/detect.ts +6 -1
- package/src/providers/provider.ts +1 -1
- package/src/providers/registry.ts +18 -2
- package/src/providers/shell-commands.ts +37 -2
- package/src/record-directory.ts +19 -0
- package/src/run-process.ts +24 -3
- package/src/serializer.ts +3 -0
- package/src/skills.ts +73 -10
- package/src/tasks/background-limit.ts +9 -2
- package/src/tasks/end-children.ts +9 -0
- package/src/tasks/give-task.ts +8 -0
- package/src/tasks/task-coordinator.ts +81 -17
- package/src/tasks/task-store.ts +28 -0
- package/src/tasks/waiting-child.ts +21 -0
- package/src/tasks/wake-parent.ts +26 -0
- package/src/tasks/wiring.ts +22 -0
- package/src/testing/accounts.ts +2 -0
- package/src/testing/fake-acp-capture.ts +16 -0
- package/src/testing/fake-acp.ts +66 -0
- package/src/testing/fake-claude.ts +177 -0
- package/src/testing/fake-cli.ts +29 -0
- package/src/testing/fake-codex.ts +122 -0
- package/src/testing/manual-clock.ts +2 -0
- package/src/testing/watch.ts +7 -1
- package/src/title-file.ts +12 -0
- package/src/usage/accounts.ts +6 -0
- package/src/usage/aggregate.ts +19 -0
- package/src/usage/exchange.ts +12 -0
- package/src/usage/index-file.ts +27 -0
- package/src/usage/limits/monitor.ts +31 -3
- package/src/usage/limits/normalize.ts +30 -0
- package/src/usage/limits/probe.ts +17 -0
- package/src/usage/pricing.ts +35 -0
- package/src/usage/projects.ts +8 -0
- package/src/usage/readers/claude.ts +12 -9
- package/src/usage/readers/codex.ts +40 -8
- package/src/usage/readers/grok.ts +17 -9
- package/src/usage/readers/host-log.ts +9 -9
- package/src/usage/record.ts +22 -0
- package/src/usage/roots.ts +6 -0
- package/src/usage/scanner.ts +61 -0
- package/src/usage/usage-service.ts +14 -0
- package/src/watch-seam.ts +8 -0
- package/src/windows-command.ts +12 -0
package/dist/async.d.ts
CHANGED
|
@@ -1,2 +1,7 @@
|
|
|
1
1
|
export declare function wait(ms: number, signal?: AbortSignal): Promise<void>;
|
|
2
|
+
export interface DeadlineTimer {
|
|
3
|
+
setTimeout(run: () => void, ms: number): unknown;
|
|
4
|
+
clearTimeout(handle: unknown): void;
|
|
5
|
+
}
|
|
6
|
+
export declare function withDeadline<T>(promise: Promise<T>, ms: number, message: string, timer?: DeadlineTimer): Promise<T>;
|
|
2
7
|
export declare function withTimeout<T>(promise: Promise<T>, ms: number, message: string): Promise<T>;
|
package/dist/async.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { asError } from './error-text.js';
|
|
1
2
|
/*
|
|
2
3
|
* Resolves after `ms`, or at once when the signal is raised. An abort resolves instead of rejecting:
|
|
3
4
|
* every caller is a loop that checks the signal itself on its next turn.
|
|
@@ -11,16 +12,23 @@ export function wait(ms, signal) {
|
|
|
11
12
|
}, { once: true });
|
|
12
13
|
});
|
|
13
14
|
}
|
|
15
|
+
const SYSTEM_TIMER = {
|
|
16
|
+
setTimeout: (run, ms) => setTimeout(run, ms),
|
|
17
|
+
clearTimeout: (handle) => clearTimeout(handle)
|
|
18
|
+
};
|
|
14
19
|
/* The timer is cleared either way, so a call that answers at once does not hold the process until its deadline. */
|
|
15
|
-
export function
|
|
20
|
+
export function withDeadline(promise, ms, message, timer = SYSTEM_TIMER) {
|
|
16
21
|
return new Promise((resolve, reject) => {
|
|
17
|
-
const
|
|
22
|
+
const handle = timer.setTimeout(() => reject(new Error(message)), ms);
|
|
18
23
|
promise.then((value) => {
|
|
19
|
-
clearTimeout(
|
|
24
|
+
timer.clearTimeout(handle);
|
|
20
25
|
resolve(value);
|
|
21
26
|
}, (e) => {
|
|
22
|
-
clearTimeout(
|
|
23
|
-
reject(
|
|
27
|
+
timer.clearTimeout(handle);
|
|
28
|
+
reject(asError(e));
|
|
24
29
|
});
|
|
25
30
|
});
|
|
26
31
|
}
|
|
32
|
+
export function withTimeout(promise, ms, message) {
|
|
33
|
+
return withDeadline(promise, ms, message);
|
|
34
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isRecord } from '@adecore/agent-contracts';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
+
import { filledStr } from '../fields.js';
|
|
3
4
|
import { MODE_ORDER } from '../modes.js';
|
|
4
5
|
import { cleanTitle } from '../title-file.js';
|
|
5
6
|
/*
|
|
@@ -357,12 +358,9 @@ function withheld(text, afterRefusal) {
|
|
|
357
358
|
function foldSpace(text) {
|
|
358
359
|
return text.replace(/\s+/g, ' ').trim();
|
|
359
360
|
}
|
|
360
|
-
function str(value) {
|
|
361
|
-
return typeof value === 'string' && value !== '' ? value : null;
|
|
362
|
-
}
|
|
363
|
-
/* A field of the agent's own input, which no schema promises; the first one that is a non-empty string. */
|
|
364
361
|
// Where agents put the command a call runs; Antigravity names it `CommandLine`.
|
|
365
362
|
const COMMAND_KEYS = ['command', 'cmd', 'CommandLine'];
|
|
363
|
+
/* A field of the agent's own input, which no schema promises; the first one that is a non-empty string. */
|
|
366
364
|
function rawField(raw, ...keys) {
|
|
367
365
|
if (!isRecord(raw)) {
|
|
368
366
|
return null;
|
|
@@ -372,7 +370,7 @@ function rawField(raw, ...keys) {
|
|
|
372
370
|
if (Array.isArray(value) && value.every((part) => typeof part === 'string') && value.length > 0) {
|
|
373
371
|
return value.join(' ');
|
|
374
372
|
}
|
|
375
|
-
if (
|
|
373
|
+
if (filledStr(value) !== null) {
|
|
376
374
|
return value;
|
|
377
375
|
}
|
|
378
376
|
}
|
package/dist/chat/chat-core.d.ts
CHANGED
|
@@ -229,6 +229,7 @@ export declare class ChatCore {
|
|
|
229
229
|
unqueue(chatId: string, messageId: string): ChatQueuedMessage;
|
|
230
230
|
sendNow(chatId: string, messageId: string): void;
|
|
231
231
|
skills(chatId: string): Promise<ChatSkill[]>;
|
|
232
|
+
skillsIn(provider: AgentKind, cwd: string): Promise<ChatSkill[]>;
|
|
232
233
|
restart(chatId: string): Promise<void>;
|
|
233
234
|
compact(chatId: string): void;
|
|
234
235
|
clear(chatId: string, force?: boolean): Promise<void>;
|
package/dist/chat/chat-core.js
CHANGED
|
@@ -173,7 +173,6 @@ export class ChatCore {
|
|
|
173
173
|
const folder = this.accounts?.transcriptFolder('claude', info.account) ?? null;
|
|
174
174
|
return folder === null ? '' : join(folder, 'projects');
|
|
175
175
|
}
|
|
176
|
-
/* Whether a conversation of one account of this CLI can go on under the other. */
|
|
177
176
|
canContinue(kind, from, to) {
|
|
178
177
|
if ((from ?? kind) === (to ?? kind)) {
|
|
179
178
|
return true;
|
|
@@ -184,7 +183,6 @@ export class ChatCore {
|
|
|
184
183
|
requireAccount(kind, account) {
|
|
185
184
|
launchEnv(this.accounts, kind, account, this.env);
|
|
186
185
|
}
|
|
187
|
-
/* What a person calls an account of this CLI. */
|
|
188
186
|
accountLabel(kind, account) {
|
|
189
187
|
return isDefaultAccountOf(kind, account) || account === undefined ? this.providers.get(kind).name : (this.accounts?.labelOf(account) ?? account);
|
|
190
188
|
}
|
|
@@ -421,6 +419,11 @@ export class ChatCore {
|
|
|
421
419
|
* read leaves its list out rather than the thread.
|
|
422
420
|
*/
|
|
423
421
|
async attachWithBookmarks(chatId, clientId, historyLimit, since) {
|
|
422
|
+
// A client that only watches a chat sends no create, so a chat on disk nobody loaded since the host started is loaded for it.
|
|
423
|
+
await this.loaded(chatId);
|
|
424
|
+
if (!this.chats.has(chatId) && (await this.store?.has(chatId)) === true) {
|
|
425
|
+
await this.create({ chatId });
|
|
426
|
+
}
|
|
424
427
|
this.require(chatId);
|
|
425
428
|
const unreadable = (what) => (e) => {
|
|
426
429
|
console.error(`The ${what} of chat ${chatId} could not be read:`, errorText(e));
|
|
@@ -722,6 +725,10 @@ export class ChatCore {
|
|
|
722
725
|
const session = this.require(chatId);
|
|
723
726
|
return session.skills(() => this.skillIndex.list(session.info.provider, session.info.cwd));
|
|
724
727
|
}
|
|
728
|
+
/* What a CLI would run as a skill in a folder before any chat of it exists there, such as for a prompt saved to send later. */
|
|
729
|
+
skillsIn(provider, cwd) {
|
|
730
|
+
return this.skillIndex.list(provider, cwd);
|
|
731
|
+
}
|
|
725
732
|
/* Ends the chat's CLI and keeps its conversation; the next message resumes it in a process that reads its skills and plugins again. */
|
|
726
733
|
async restart(chatId) {
|
|
727
734
|
const session = this.require(chatId);
|
|
@@ -24,6 +24,7 @@ export interface ProcessTreeKillOptions {
|
|
|
24
24
|
run?(command: string[]): void;
|
|
25
25
|
}
|
|
26
26
|
export declare function killProcessTree(pid: number, signal: 'SIGTERM' | 'SIGKILL', options?: ProcessTreeKillOptions): void;
|
|
27
|
+
export declare const KILL_GRACE_MS = 2000;
|
|
27
28
|
export declare class StreamTail {
|
|
28
29
|
readonly done: Promise<void>;
|
|
29
30
|
private text;
|
|
@@ -77,7 +77,7 @@ function runQuietly([command, ...args]) {
|
|
|
77
77
|
// After stdin closed, a CLI that is still around is not going to say more.
|
|
78
78
|
const EXIT_GRACE_MS = 3000;
|
|
79
79
|
// What a CLI and everything it started get between the SIGTERM and the SIGKILL.
|
|
80
|
-
const KILL_GRACE_MS = 2000;
|
|
80
|
+
export const KILL_GRACE_MS = 2000;
|
|
81
81
|
// How long a crash note waits for the last of stderr; a process the CLI started may hold the pipe open.
|
|
82
82
|
const STDERR_DRAIN_MS = 250;
|
|
83
83
|
// A pipe nobody reads is buffered in memory for as long as the CLI lives, so stderr is read into this much and no more.
|
|
@@ -191,6 +191,8 @@ export declare class ChatSession {
|
|
|
191
191
|
taskIds: string[];
|
|
192
192
|
summaryFor?: string;
|
|
193
193
|
messageFrom?: string[];
|
|
194
|
+
mentions?: string[];
|
|
195
|
+
skills?: string[];
|
|
194
196
|
}): string | null;
|
|
195
197
|
resumeSettingChanged(): void;
|
|
196
198
|
takeUpAfterLimit(turnId: string): 'opened' | 'wait' | 'lapsed';
|
|
@@ -8,7 +8,7 @@ import { isMainAgentOutput, ThreadProjector } from './projector.js';
|
|
|
8
8
|
import { readableAssistantText } from './readable-text.js';
|
|
9
9
|
import { ChatThread } from './thread.js';
|
|
10
10
|
import { UiReplies } from './ui-replies.js';
|
|
11
|
-
import { errorText } from '../error-text.js';
|
|
11
|
+
import { asError, errorText } from '../error-text.js';
|
|
12
12
|
import { systemClock } from '../outbox/outbox-worker.js';
|
|
13
13
|
import { cleanTitle } from '../title-file.js';
|
|
14
14
|
// Claude Code names a session about six seconds after its first prompt, and a first turn can run for minutes.
|
|
@@ -239,9 +239,7 @@ export class ChatSession {
|
|
|
239
239
|
this.drainQueue();
|
|
240
240
|
this.emit([this.thread.patchInfo({})]);
|
|
241
241
|
}
|
|
242
|
-
/*
|
|
243
|
-
* Internal follow-ups wait for a new turn. User input goes through `sendInput`, which can steer native work.
|
|
244
|
-
*/
|
|
242
|
+
/* Internal follow-ups wait for a new turn. User input goes through `sendInput`, which can steer native work. */
|
|
245
243
|
send(text, sent = {}) {
|
|
246
244
|
const extras = this.vetted(sent);
|
|
247
245
|
const turnId = newId('turn');
|
|
@@ -872,7 +870,8 @@ export class ChatSession {
|
|
|
872
870
|
* turn carrying what woke it, so a turn it opens cannot itself wake anyone. A caller passes `note`
|
|
873
871
|
* only when its news is not already written in the thread. Checked and opened in one step, so null
|
|
874
872
|
* means a turn (or a shutdown) was in the way. Never through `send`, which would step on a turn
|
|
875
|
-
* with `origin: 'agent'` and close the wake it just made.
|
|
873
|
+
* with `origin: 'agent'` and close the wake it just made. `mentions` and `skills` reach a CLI that
|
|
874
|
+
* expands them itself, as a person's picks in a message do.
|
|
876
875
|
*/
|
|
877
876
|
wake(wake) {
|
|
878
877
|
if (this.frozen || this.relocating || this.thread.info.activeTurnId !== null || this.replacementWaits()) {
|
|
@@ -906,7 +905,7 @@ export class ChatSession {
|
|
|
906
905
|
]);
|
|
907
906
|
this.options.persist();
|
|
908
907
|
this.turnReady = this.checkpoint(turnId);
|
|
909
|
-
this.submit(turnId, { text: wake.text, preamble, attachments: [], mentions: [], skills: [] });
|
|
908
|
+
this.submit(turnId, { text: wake.text, preamble, attachments: [], mentions: wake.mentions ?? [], skills: wake.skills ?? [] });
|
|
910
909
|
return turnId;
|
|
911
910
|
}
|
|
912
911
|
/* The host's switch or this chat's own changed: a limit still ahead is owed a resume now, or what was owed lapses. */
|
|
@@ -1523,7 +1522,7 @@ export class ChatSession {
|
|
|
1523
1522
|
}
|
|
1524
1523
|
catch (error) {
|
|
1525
1524
|
// What the host prepares for a launch (its folders, its instructions) can throw too; the turn already opened, so it fails.
|
|
1526
|
-
backend = Promise.reject(
|
|
1525
|
+
backend = Promise.reject(asError(error));
|
|
1527
1526
|
}
|
|
1528
1527
|
const generation = this.generation;
|
|
1529
1528
|
void Promise.all([backend, this.turnReady, this.options.save?.()])
|
|
@@ -1626,7 +1625,7 @@ export class ChatSession {
|
|
|
1626
1625
|
this.launchedAccount = this.thread.info.account;
|
|
1627
1626
|
}
|
|
1628
1627
|
catch (error) {
|
|
1629
|
-
return Promise.reject(
|
|
1628
|
+
return Promise.reject(asError(error));
|
|
1630
1629
|
}
|
|
1631
1630
|
this.generation = nextGeneration();
|
|
1632
1631
|
const generation = this.generation;
|
|
@@ -1668,7 +1667,7 @@ export class ChatSession {
|
|
|
1668
1667
|
});
|
|
1669
1668
|
}
|
|
1670
1669
|
catch (error) {
|
|
1671
|
-
return Promise.reject(
|
|
1670
|
+
return Promise.reject(asError(error));
|
|
1672
1671
|
}
|
|
1673
1672
|
const backend = made.backend;
|
|
1674
1673
|
this.backend = backend;
|
|
@@ -1680,7 +1679,7 @@ export class ChatSession {
|
|
|
1680
1679
|
}
|
|
1681
1680
|
catch (error) {
|
|
1682
1681
|
// A spawn that throws on the spot (no such executable) is a start that failed like any other.
|
|
1683
|
-
started = Promise.reject(
|
|
1682
|
+
started = Promise.reject(asError(error));
|
|
1684
1683
|
}
|
|
1685
1684
|
this.starting = started
|
|
1686
1685
|
.then(() => backend)
|
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import { isRecord } from '@adecore/agent-contracts';
|
|
2
2
|
import { readClaudeEvent } from '../usage/limits/normalize.js';
|
|
3
|
+
import { num, str } from '../fields.js';
|
|
3
4
|
import { claudeRuntimeMode } from '../providers/claude.js';
|
|
4
|
-
function str(value) {
|
|
5
|
-
return typeof value === 'string' ? value : null;
|
|
6
|
-
}
|
|
7
|
-
function num(value) {
|
|
8
|
-
return typeof value === 'number' && Number.isFinite(value) ? value : 0;
|
|
9
|
-
}
|
|
10
5
|
// A tool result is a string or a list of content blocks; only the text is worth showing.
|
|
11
6
|
function resultText(content) {
|
|
12
7
|
if (typeof content === 'string') {
|
|
@@ -2,14 +2,12 @@ import { existsSync } from 'node:fs';
|
|
|
2
2
|
import { readdir, readFile, stat } from 'node:fs/promises';
|
|
3
3
|
import { dirname, join } from 'node:path';
|
|
4
4
|
import { isRecord } from '@adecore/agent-contracts';
|
|
5
|
+
import { filledStr as str } from '../fields.js';
|
|
5
6
|
import { fileExists } from '../fs.js';
|
|
6
7
|
import { readLines } from '../title-file.js';
|
|
7
8
|
import { ClaudeProtocol } from './claude-protocol.js';
|
|
8
9
|
import { readingThread, settledReading } from './subagent-projection.js';
|
|
9
10
|
const CHUNK_BYTES = 4 * 1024 * 1024;
|
|
10
|
-
function str(value) {
|
|
11
|
-
return typeof value === 'string' && value !== '' ? value : null;
|
|
12
|
-
}
|
|
13
11
|
/* The words of a user line that somebody said, or null for a line that carries tool results. */
|
|
14
12
|
export function spokenText(content) {
|
|
15
13
|
if (typeof content === 'string') {
|
|
@@ -2,13 +2,8 @@ import { generatedImage } from './generated-image.js';
|
|
|
2
2
|
import { isRecord } from '@adecore/agent-contracts';
|
|
3
3
|
import { readCodexLimits } from '../usage/limits/normalize.js';
|
|
4
4
|
import { codexRuntimeMode } from '../providers/codex.js';
|
|
5
|
+
import { num, str } from '../fields.js';
|
|
5
6
|
import { cleanTitle } from '../title-file.js';
|
|
6
|
-
function str(value) {
|
|
7
|
-
return typeof value === 'string' ? value : null;
|
|
8
|
-
}
|
|
9
|
-
function num(value) {
|
|
10
|
-
return typeof value === 'number' && Number.isFinite(value) ? value : 0;
|
|
11
|
-
}
|
|
12
7
|
const GOAL_STATUSES = {
|
|
13
8
|
active: 'active',
|
|
14
9
|
paused: 'paused',
|
package/dist/chat/projector.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isRecord } from '@adecore/agent-contracts';
|
|
2
2
|
import { clipText } from '@adecore/agent-contracts/text';
|
|
3
|
+
import { str } from '../fields.js';
|
|
3
4
|
import { estimateContextBreakdown } from './context-breakdown.js';
|
|
4
5
|
/*
|
|
5
6
|
* Content of the main conversation, which the CLI only writes inside a turn it closes with a `result`.
|
|
@@ -26,9 +27,6 @@ export function isMainAgentOutput(event) {
|
|
|
26
27
|
function startsAgentTurn(event) {
|
|
27
28
|
return isMainAgentOutput(event) || ((event.type === 'approval.requested' || event.type === 'question.requested') && event.background !== true);
|
|
28
29
|
}
|
|
29
|
-
function str(value) {
|
|
30
|
-
return typeof value === 'string' ? value : null;
|
|
31
|
-
}
|
|
32
30
|
/* A markdown code block around text that may hold backticks of its own. */
|
|
33
31
|
function fenced(text) {
|
|
34
32
|
const longest = Math.max(0, ...(text.match(/`+/g) ?? []).map((run) => run.length));
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { isRecord, CHAT_REQUEST_LIMITS } from '@adecore/agent-contracts';
|
|
2
|
-
|
|
3
|
-
return typeof value === 'string' ? value : null;
|
|
4
|
-
}
|
|
2
|
+
import { str } from '../fields.js';
|
|
5
3
|
// The keys a tool's input names its subject under, in the order a person would look for one.
|
|
6
4
|
const SUBJECT_KEYS = ['file_path', 'notebook_path', 'path', 'url', 'query', 'pattern', 'skill', 'description'];
|
|
7
5
|
/* Cuts text at `max` code units, never between the halves of a surrogate pair. */
|
package/dist/coded-error.js
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* The shape every failure on the wire has: a code a client can branch on and a message a person reads.
|
|
3
|
-
* A domain gives it a union of its own codes, so a typo in a code is a compile error instead of a
|
|
4
|
-
* string the other end never matches.
|
|
5
|
-
*/
|
|
1
|
+
/* The shape every failure on the wire has: a code a client can branch on and a message a person reads. */
|
|
6
2
|
export class CodedError extends Error {
|
|
7
3
|
code;
|
|
8
4
|
/* What the actor can do instead. Only a refusal fills this; the rest carry an empty list. */
|
package/dist/context/argv.js
CHANGED
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
* Splits the words after a verb into positionals, `--flag value` pairs (`--flag=value` too, for a
|
|
3
3
|
* value that itself starts with `--`) and the switches the verb declared. Anything not declared a
|
|
4
4
|
* switch takes a value, so a flag that lost its argument is refused instead of swallowing the next
|
|
5
|
-
* word
|
|
6
|
-
* both a switch and a flag is on when written bare and takes a value only after `=` (`--state=full`).
|
|
5
|
+
* word. A name that is both a switch and a flag takes a value only after `=` (`--state=full`).
|
|
7
6
|
*/
|
|
8
7
|
export function parseArgv(argv, known, switches = []) {
|
|
9
8
|
const positionals = [];
|
package/dist/context/verb.js
CHANGED
|
@@ -57,11 +57,9 @@ export function createVerbRegistry(options) {
|
|
|
57
57
|
* points at these, so an agent never gets a silent nothing from a dry run that was never dry.
|
|
58
58
|
*/
|
|
59
59
|
const dryRunVerbs = [];
|
|
60
|
-
/* What a refusal over arguments points at; for an action `name` is both words, which is what `help` takes. */
|
|
61
|
-
const usageLinesOf = (name, usage) => [`usage\t${name}\t${usage}`, `detail\t${cli} help ${name}`];
|
|
62
60
|
/* One set of arguments parsed and run. `name` is what a refusal calls it, so `view new` refuses under both its words. */
|
|
63
61
|
const runArgs = async (name, usage, spec, argv, call) => {
|
|
64
|
-
const usageLines =
|
|
62
|
+
const usageLines = [`usage\t${name}\t${usage}`, `detail\t${cli} help ${name}`];
|
|
65
63
|
const { values, switches } = flagsOf(spec);
|
|
66
64
|
const parsed = parseArgv(argv, values, switches);
|
|
67
65
|
if (!parsed.ok) {
|
package/dist/error-text.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/** The message of an error, or its whole stack with `debug` set. */
|
|
2
2
|
export declare function describeError(error: unknown, debug: boolean): string;
|
|
3
|
+
export declare function asError(thrown: unknown): Error;
|
|
3
4
|
export declare function setErrorStacks(on: boolean): void;
|
|
4
5
|
/** `describeError` as the host set it, for a log line or a note. */
|
|
5
6
|
export declare function errorText(error: unknown): string;
|
package/dist/error-text.js
CHANGED
|
@@ -13,6 +13,10 @@ export function describeError(error, debug) {
|
|
|
13
13
|
}
|
|
14
14
|
return error.message === '' ? error.name : error.message;
|
|
15
15
|
}
|
|
16
|
+
/* What was thrown, as an Error a rejection can carry. */
|
|
17
|
+
export function asError(thrown) {
|
|
18
|
+
return thrown instanceof Error ? thrown : new Error(String(thrown));
|
|
19
|
+
}
|
|
16
20
|
// The host decides once whether a stack is worth showing; the package reads no variable of its own.
|
|
17
21
|
let showStacks = false;
|
|
18
22
|
export function setErrorStacks(on) {
|
package/dist/fields.d.ts
ADDED
package/dist/fields.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/* A field of a frame or a file that no schema promises a string in. */
|
|
2
|
+
export function str(value) {
|
|
3
|
+
return typeof value === 'string' ? value : null;
|
|
4
|
+
}
|
|
5
|
+
/* A count of a frame, zero for anything that is not a finite number. */
|
|
6
|
+
export function num(value) {
|
|
7
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : 0;
|
|
8
|
+
}
|
|
9
|
+
/* The same, with an empty string read as nothing said. */
|
|
10
|
+
export function filledStr(value) {
|
|
11
|
+
return typeof value === 'string' && value !== '' ? value : null;
|
|
12
|
+
}
|
package/dist/fs.d.ts
CHANGED
package/dist/fs.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { renameSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { open, rename, rm, stat, writeFile } from 'node:fs/promises';
|
|
3
3
|
import { dirname } from 'node:path';
|
|
4
|
+
import { wait } from './async.js';
|
|
4
5
|
// Windows can refuse a rename while a watcher or an indexer holds the target; a few short retries cover it.
|
|
5
6
|
const RENAME_RETRIES = 5;
|
|
6
7
|
const RENAME_RETRY_MS = 40;
|
|
@@ -8,8 +9,11 @@ const RENAME_RETRY_MS = 40;
|
|
|
8
9
|
function sleepSync(ms) {
|
|
9
10
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
10
11
|
}
|
|
12
|
+
export function hasErrorCode(e, ...codes) {
|
|
13
|
+
return typeof e === 'object' && e !== null && 'code' in e && codes.includes(e.code);
|
|
14
|
+
}
|
|
11
15
|
function isTransient(e) {
|
|
12
|
-
return
|
|
16
|
+
return hasErrorCode(e, 'EPERM', 'EBUSY', 'EACCES');
|
|
13
17
|
}
|
|
14
18
|
// Two writes of the same file in flight at once (a fire-and-forget save next to an awaited one) must not share a temp name.
|
|
15
19
|
let nextTemp = 0;
|
|
@@ -62,7 +66,7 @@ export async function writeAtomic(target, content, mode = 0o600, options = {}) {
|
|
|
62
66
|
if (!isTransient(e) || attempt >= RENAME_RETRIES) {
|
|
63
67
|
throw e;
|
|
64
68
|
}
|
|
65
|
-
await
|
|
69
|
+
await wait(RENAME_RETRY_MS);
|
|
66
70
|
}
|
|
67
71
|
}
|
|
68
72
|
if (options.durable) {
|
|
@@ -94,7 +98,7 @@ export function writeAtomicSync(target, content, mode = 0o600) {
|
|
|
94
98
|
replaceSync(temp, target);
|
|
95
99
|
}
|
|
96
100
|
export function isNotFound(e) {
|
|
97
|
-
return
|
|
101
|
+
return hasErrorCode(e, 'ENOENT');
|
|
98
102
|
}
|
|
99
103
|
// Bun on Windows fails a forced rm with EFAULT while another delete of the same file is pending; it goes either way.
|
|
100
104
|
export async function removeFile(path, remove = rm) {
|
|
@@ -102,7 +106,7 @@ export async function removeFile(path, remove = rm) {
|
|
|
102
106
|
await remove(path, { force: true });
|
|
103
107
|
}
|
|
104
108
|
catch (e) {
|
|
105
|
-
if (!(
|
|
109
|
+
if (!hasErrorCode(e, 'EFAULT')) {
|
|
106
110
|
throw e;
|
|
107
111
|
}
|
|
108
112
|
}
|
package/dist/lineage.js
CHANGED
|
@@ -51,9 +51,10 @@ export class AgentLineageStore {
|
|
|
51
51
|
descendants(nodeId) {
|
|
52
52
|
const found = [];
|
|
53
53
|
const seen = new Set([nodeId]);
|
|
54
|
+
const entries = this.opened.all();
|
|
54
55
|
for (let i = -1; i < found.length; i++) {
|
|
55
56
|
const parent = i === -1 ? nodeId : found[i];
|
|
56
|
-
for (const entry of
|
|
57
|
+
for (const entry of entries) {
|
|
57
58
|
if (entry.openedBy === parent && openedByAgent(entry) && entry.endedAt === undefined && !seen.has(entry.nodeId)) {
|
|
58
59
|
seen.add(entry.nodeId);
|
|
59
60
|
found.push(entry.nodeId);
|
|
@@ -89,7 +90,8 @@ export class AgentLineageStore {
|
|
|
89
90
|
}
|
|
90
91
|
/* The nodes of this project whose opener it no longer places, and that no cascade ended yet. */
|
|
91
92
|
orphans(projectId, ids) {
|
|
92
|
-
return
|
|
93
|
+
return this.opened
|
|
94
|
+
.all()
|
|
93
95
|
.filter((entry) => entry.projectId === projectId && openedByAgent(entry) && entry.endedAt === undefined && ids.has(entry.nodeId) && !ids.has(entry.openedBy))
|
|
94
96
|
.map((entry) => ({ nodeId: entry.nodeId, openedBy: entry.openedBy }));
|
|
95
97
|
}
|
|
@@ -102,7 +104,7 @@ export class AgentLineageStore {
|
|
|
102
104
|
peerOf(nodeId) {
|
|
103
105
|
return this.opened.get(nodeId)?.peer ?? null;
|
|
104
106
|
}
|
|
105
|
-
/*
|
|
107
|
+
/* A node nobody wrote down is one a person made, which is where a chain starts. */
|
|
106
108
|
depthOf(nodeId) {
|
|
107
109
|
return this.opened.get(nodeId)?.depth ?? 0;
|
|
108
110
|
}
|
|
@@ -132,11 +134,15 @@ export class AgentLineageStore {
|
|
|
132
134
|
return entry?.relation === 'fork' ? entry.openedBy : null;
|
|
133
135
|
}
|
|
134
136
|
forksOf(nodeId) {
|
|
135
|
-
return
|
|
137
|
+
return this.opened
|
|
138
|
+
.all()
|
|
139
|
+
.filter((entry) => entry.relation === 'fork' && entry.openedBy === nodeId)
|
|
140
|
+
.map((entry) => entry.nodeId);
|
|
136
141
|
}
|
|
137
142
|
/* The forks of this project whose node is gone, read before `prune` forgets them. */
|
|
138
143
|
forksLeaving(projectId, ids) {
|
|
139
|
-
return
|
|
144
|
+
return this.opened
|
|
145
|
+
.all()
|
|
140
146
|
.filter((entry) => entry.projectId === projectId && entry.relation === 'fork' && !ids.has(entry.nodeId))
|
|
141
147
|
.map((entry) => entry.nodeId);
|
|
142
148
|
}
|
|
@@ -28,10 +28,9 @@ const NoticeSchema = z.object({
|
|
|
28
28
|
/* A queue waits in the file of the node it is for, so the first message in it names that node. */
|
|
29
29
|
const FileSchema = z.object({ notices: z.array(NoticeSchema).min(1) });
|
|
30
30
|
/*
|
|
31
|
-
* The messages waiting for each node, held until that node's agent has a moment to hear them
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* first.
|
|
31
|
+
* The messages waiting for each node, held until that node's agent has a moment to hear them. On disk
|
|
32
|
+
* because the receiver may well be a node nobody has mounted yet, and a host that restarts must not
|
|
33
|
+
* lose what was left for it.
|
|
35
34
|
*/
|
|
36
35
|
export class NoticeStore {
|
|
37
36
|
dir;
|
|
@@ -1,31 +1,14 @@
|
|
|
1
1
|
import { mkdtemp, rm } from 'node:fs/promises';
|
|
2
2
|
import { tmpdir } from 'node:os';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
|
+
import { withDeadline } from '../../async.js';
|
|
4
5
|
import { DEFAULT_ACP_CLIENT } from '../../chat/acp-backend.js';
|
|
5
6
|
import { ACP_AUTH_REQUIRED, ACP_PROTOCOL_VERSION, parseInitialize, parseSessionInfo, sessionIdOf, sessionUpdateOf } from '../../chat/acp-protocol.js';
|
|
6
7
|
import { JsonRpcError, JsonRpcTransport } from '../../chat/json-rpc-transport.js';
|
|
8
|
+
import { definedEnv } from './launch.js';
|
|
7
9
|
import { ASK_TIMEOUT_MS } from './status.js';
|
|
8
10
|
const SIGNED_OUT = { signedIn: false, email: null, plan: null, organization: null };
|
|
9
11
|
const SIGNED_IN = { signedIn: true, email: null, plan: null, organization: null };
|
|
10
|
-
const REAL_TIMER = {
|
|
11
|
-
setTimeout: (run, ms) => setTimeout(run, ms),
|
|
12
|
-
clearTimeout: (handle) => clearTimeout(handle)
|
|
13
|
-
};
|
|
14
|
-
function definedEnv(env) {
|
|
15
|
-
return Object.fromEntries(Object.entries(env).filter((entry) => entry[1] !== undefined));
|
|
16
|
-
}
|
|
17
|
-
function withDeadline(work, ms, timer, message) {
|
|
18
|
-
return new Promise((resolve, reject) => {
|
|
19
|
-
const handle = timer.setTimeout(() => reject(new Error(message)), ms);
|
|
20
|
-
work.then((value) => {
|
|
21
|
-
timer.clearTimeout(handle);
|
|
22
|
-
resolve(value);
|
|
23
|
-
}, (e) => {
|
|
24
|
-
timer.clearTimeout(handle);
|
|
25
|
-
reject(e instanceof Error ? e : new Error(String(e)));
|
|
26
|
-
});
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
12
|
/*
|
|
30
13
|
* Asks an ACP agent whether the account's environment is signed in: a session it opens means yes, "authentication
|
|
31
14
|
* required" means no. ACP has no request that says who, so a reading carries no email or plan. The session is opened
|
|
@@ -102,7 +85,7 @@ export async function askAcpAccount(probe) {
|
|
|
102
85
|
}
|
|
103
86
|
return SIGNED_IN;
|
|
104
87
|
};
|
|
105
|
-
return await withDeadline(reading(), probe.timeoutMs ?? ASK_TIMEOUT_MS,
|
|
88
|
+
return await withDeadline(reading(), probe.timeoutMs ?? ASK_TIMEOUT_MS, `${probe.name} did not answer in time`, probe.timer);
|
|
106
89
|
}
|
|
107
90
|
finally {
|
|
108
91
|
await transport?.dispose();
|
|
@@ -42,7 +42,7 @@ export declare class ProviderAccountsService implements AccountLaunches {
|
|
|
42
42
|
private readonly now;
|
|
43
43
|
private readonly sleep;
|
|
44
44
|
private readonly watches;
|
|
45
|
-
private writes;
|
|
45
|
+
private readonly writes;
|
|
46
46
|
private readonly sinks;
|
|
47
47
|
private readonly listeners;
|
|
48
48
|
private readonly launches;
|
|
@@ -74,7 +74,6 @@ export declare class ProviderAccountsService implements AccountLaunches {
|
|
|
74
74
|
private runWatch;
|
|
75
75
|
private kinds;
|
|
76
76
|
private accountProviders;
|
|
77
|
-
private serial;
|
|
78
77
|
private commit;
|
|
79
78
|
private planSecrets;
|
|
80
79
|
private requireSecrets;
|
|
@@ -5,6 +5,7 @@ import { KNOWN_AGENT_KINDS } from '@adecore/agent-contracts';
|
|
|
5
5
|
import { DEFAULT_CODEX_CLIENT } from '../../chat/codex-transport.js';
|
|
6
6
|
import { ClientSinks } from '../../client-sinks.js';
|
|
7
7
|
import { errorText } from '../../error-text.js';
|
|
8
|
+
import { Serializer } from '../../serializer.js';
|
|
8
9
|
import { accountEnv, accountFolder, canContinue, defaultFolder, expandHome, isDefaultAccount, isOfferedKind, transcriptFolder } from './accounts.js';
|
|
9
10
|
import { AccountError, isDefaultAccountOf } from './launch.js';
|
|
10
11
|
import { prepareShadowHome } from './shadow-home.js';
|
|
@@ -72,7 +73,8 @@ export class ProviderAccountsService {
|
|
|
72
73
|
now;
|
|
73
74
|
sleep;
|
|
74
75
|
watches = new Map();
|
|
75
|
-
|
|
76
|
+
// One change of the file at a time: a create and a save both read what the other just wrote.
|
|
77
|
+
writes = new Serializer();
|
|
76
78
|
sinks = new ClientSinks();
|
|
77
79
|
listeners = new Set();
|
|
78
80
|
// When a CLI last started under each account, by id; kept in memory, so a restart forgets it.
|
|
@@ -228,14 +230,14 @@ export class ProviderAccountsService {
|
|
|
228
230
|
}
|
|
229
231
|
/* Answers at once; an account that changed reads `checking` until its CLI answered, which arrives as `accounts.changed`. */
|
|
230
232
|
save(accounts) {
|
|
231
|
-
return this.
|
|
233
|
+
return this.writes.run(() => this.commit(accounts, this.folderExists));
|
|
232
234
|
}
|
|
233
235
|
/*
|
|
234
236
|
* An account in a folder of its own under `<home>/accounts`, signed in by nobody yet. A
|
|
235
237
|
* Codex account is a shadow home over the CLI's own folder, so its threads go on under the other.
|
|
236
238
|
*/
|
|
237
239
|
create(payload) {
|
|
238
|
-
return this.
|
|
240
|
+
return this.writes.run(async () => {
|
|
239
241
|
const { kind, label, color } = payload;
|
|
240
242
|
const provider = this.providers.get(kind);
|
|
241
243
|
if (provider.home === undefined) {
|
|
@@ -303,12 +305,6 @@ export class ProviderAccountsService {
|
|
|
303
305
|
accountProviders() {
|
|
304
306
|
return { kinds: this.kinds(), get: (kind) => this.providers.get(kind) };
|
|
305
307
|
}
|
|
306
|
-
/* One change of the file at a time: a create and a save both read what the other just wrote. */
|
|
307
|
-
serial(work) {
|
|
308
|
-
const next = this.writes.then(work);
|
|
309
|
-
this.writes = next.catch(() => undefined);
|
|
310
|
-
return next;
|
|
311
|
-
}
|
|
312
308
|
async commit(accounts, isDirectory) {
|
|
313
309
|
const stored = acceptAccounts(accounts, this.stored, this.accountProviders(), this.env, isDirectory, this.host);
|
|
314
310
|
const { writes, removals } = this.planSecrets(stored);
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { isRecord } from '@adecore/agent-contracts';
|
|
2
2
|
import { withTimeout } from '../../async.js';
|
|
3
|
+
import { filledStr as text } from '../../fields.js';
|
|
3
4
|
import { CodexTransport } from '../../chat/codex-transport.js';
|
|
4
5
|
import { runProcess } from '../../run-process.js';
|
|
5
6
|
/* A CLI that has not said who is signed in by now is not going to; the next pass asks again. */
|
|
6
7
|
export const ASK_TIMEOUT_MS = 20_000;
|
|
7
|
-
function text(value) {
|
|
8
|
-
return typeof value === 'string' && value !== '' ? value : null;
|
|
9
|
-
}
|
|
10
8
|
/* `claude auth status --json`, as Claude Code 2.1.282 prints it: `loggedIn`, `email`, `orgName`, `subscriptionType`. */
|
|
11
9
|
export function readClaudeAuthStatus(output) {
|
|
12
10
|
let status;
|
|
@@ -79,9 +79,7 @@ export class AcpAgentState {
|
|
|
79
79
|
if (!this.options.adoptModels || info.models === null) {
|
|
80
80
|
return;
|
|
81
81
|
}
|
|
82
|
-
|
|
83
|
-
const changed = this.adopt(adopted);
|
|
84
|
-
this.settle(changed);
|
|
82
|
+
this.settle(this.adopt({ defaultModel: info.models.current, models: adoptableModels(info), options: effortOption(info) }));
|
|
85
83
|
}
|
|
86
84
|
commandsListed(commands) {
|
|
87
85
|
const names = [...new Set(commands)].sort();
|
package/dist/providers/claude.js
CHANGED
|
@@ -80,7 +80,6 @@ export function claudeArgs(launch) {
|
|
|
80
80
|
export function claudeEnv(selection) {
|
|
81
81
|
return selection.options.contextWindow === '200k' ? { CLAUDE_CODE_AUTO_COMPACT_WINDOW: '200000' } : {};
|
|
82
82
|
}
|
|
83
|
-
// What the CLI can do, as far as a client has to know.
|
|
84
83
|
export const CLAUDE_CAPABILITIES = {
|
|
85
84
|
chat: true,
|
|
86
85
|
terminal: true,
|