@adecore/agents 0.39.0 → 0.40.1
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.js +0 -2
- package/dist/chat/chat-process.d.ts +1 -0
- package/dist/chat/chat-process.js +1 -1
- package/dist/chat/chat-session.js +6 -8
- 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-models.json +3 -2
- 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/prices-snapshot.json +8 -0
- 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 +208 -7
- package/src/chat/chat-log.ts +19 -0
- package/src/chat/chat-process.ts +52 -1
- package/src/chat/chat-session.ts +401 -10
- 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-models.json +3 -2
- 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/prices-snapshot.json +8 -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
|
@@ -22,14 +22,19 @@ const SAFE_EXTENSION = /^[A-Za-z0-9]{1,8}$/;
|
|
|
22
22
|
/* What the file is called on disk: the id, plus an extension the agent's tools can recognize. */
|
|
23
23
|
export function extensionFor(name: string, mime: string): string {
|
|
24
24
|
const own = extname(name).slice(1).toLowerCase();
|
|
25
|
+
|
|
25
26
|
if (SAFE_EXTENSION.test(own)) {
|
|
26
27
|
return own;
|
|
27
28
|
}
|
|
29
|
+
|
|
28
30
|
const known = EXTENSIONS[mime.toLowerCase()];
|
|
31
|
+
|
|
29
32
|
if (known) {
|
|
30
33
|
return known;
|
|
31
34
|
}
|
|
35
|
+
|
|
32
36
|
const subtype = mime.split('/')[1]?.replace(/\+.*$/, '') ?? '';
|
|
37
|
+
|
|
33
38
|
return SAFE_EXTENSION.test(subtype) ? subtype.toLowerCase() : 'bin';
|
|
34
39
|
}
|
|
35
40
|
|
|
@@ -54,6 +59,7 @@ export class AttachmentStore {
|
|
|
54
59
|
await mkdir(folder, { recursive: true, mode: 0o700 });
|
|
55
60
|
const path = join(folder, `${id}.${extensionFor(upload.name, mime)}`);
|
|
56
61
|
await writeFile(path, bytes, { mode: 0o600 });
|
|
62
|
+
|
|
57
63
|
return { id, name: upload.name, mime, size: bytes.byteLength, path };
|
|
58
64
|
}
|
|
59
65
|
|
|
@@ -64,8 +70,10 @@ export class AttachmentStore {
|
|
|
64
70
|
await mkdir(folder, { recursive: true, mode: 0o700 });
|
|
65
71
|
const path = join(folder, `${id}.${extensionFor('', upload.mime)}`);
|
|
66
72
|
const temporary = join(folder, `.${id}-${randomBytes(8).toString('hex')}.tmp`);
|
|
73
|
+
|
|
67
74
|
try {
|
|
68
75
|
await writeFile(temporary, bytes, { mode: 0o600, flag: 'wx' });
|
|
76
|
+
|
|
69
77
|
try {
|
|
70
78
|
// Publish complete bytes without replacing an image another replay has already saved.
|
|
71
79
|
await link(temporary, path);
|
|
@@ -73,7 +81,9 @@ export class AttachmentStore {
|
|
|
73
81
|
if ((error as NodeJS.ErrnoException).code !== 'EEXIST') {
|
|
74
82
|
throw error;
|
|
75
83
|
}
|
|
84
|
+
|
|
76
85
|
const previous = await readFile(path);
|
|
86
|
+
|
|
77
87
|
if (!previous.equals(bytes)) {
|
|
78
88
|
throw new Error('The generated image id already belongs to another image');
|
|
79
89
|
}
|
|
@@ -81,6 +91,7 @@ export class AttachmentStore {
|
|
|
81
91
|
} finally {
|
|
82
92
|
await rm(temporary, { force: true });
|
|
83
93
|
}
|
|
94
|
+
|
|
84
95
|
return { id, name: upload.name, mime: upload.mime, size: bytes.byteLength, path };
|
|
85
96
|
}
|
|
86
97
|
|
|
@@ -90,6 +101,7 @@ export class AttachmentStore {
|
|
|
90
101
|
await mkdir(folder, { recursive: true, mode: 0o700 });
|
|
91
102
|
const path = join(folder, `${createHash('sha256').update(attachment.id).digest('hex')}.${extensionFor(attachment.name, attachment.mime)}`);
|
|
92
103
|
await writeFile(path, bytes, { mode: 0o600 });
|
|
104
|
+
|
|
93
105
|
return { ...attachment, path };
|
|
94
106
|
}
|
|
95
107
|
|
|
@@ -102,6 +114,7 @@ export class AttachmentStore {
|
|
|
102
114
|
folderOf(chatId: string): string {
|
|
103
115
|
// `encodeURIComponent` leaves `.` and `..` as they are, and those would name this folder or the host's own.
|
|
104
116
|
const segment = chatId === '.' || chatId === '..' ? chatId.replaceAll('.', '%2E') : encodeURIComponent(chatId);
|
|
117
|
+
|
|
105
118
|
return join(this.dir, segment);
|
|
106
119
|
}
|
|
107
120
|
}
|
|
@@ -118,26 +131,34 @@ function isInline(value: unknown): value is { name: string; mediaType: string; d
|
|
|
118
131
|
*/
|
|
119
132
|
export async function migrateInlineAttachments(chatId: string, record: unknown, store: AttachmentStore): Promise<unknown | null> {
|
|
120
133
|
const items = isRecord(record) && Array.isArray(record.items) ? record.items : null;
|
|
134
|
+
|
|
121
135
|
if (!items) {
|
|
122
136
|
return null;
|
|
123
137
|
}
|
|
138
|
+
|
|
124
139
|
let migrated = false;
|
|
140
|
+
|
|
125
141
|
const next = await Promise.all(
|
|
126
142
|
items.map(async (item) => {
|
|
127
143
|
if (!isRecord(item) || !Array.isArray(item.attachments) || !item.attachments.some(isInline)) {
|
|
128
144
|
return item;
|
|
129
145
|
}
|
|
146
|
+
|
|
130
147
|
const attachments = await Promise.all(
|
|
131
148
|
item.attachments.map(async (attachment) => {
|
|
132
149
|
if (!isInline(attachment)) {
|
|
133
150
|
return attachment;
|
|
134
151
|
}
|
|
152
|
+
|
|
135
153
|
migrated = true;
|
|
154
|
+
|
|
136
155
|
return await store.save(chatId, { name: attachment.name, mime: attachment.mediaType, data: attachment.data });
|
|
137
156
|
})
|
|
138
157
|
);
|
|
158
|
+
|
|
139
159
|
return { ...item, attachments };
|
|
140
160
|
})
|
|
141
161
|
);
|
|
162
|
+
|
|
142
163
|
return migrated ? { ...(record as Record<string, unknown>), items: next } : null;
|
|
143
164
|
}
|
package/src/chat/backend.ts
CHANGED
|
@@ -22,7 +22,6 @@ import type { SpawnChatProcess } from './chat-process.ts';
|
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
export interface BackendLaunch {
|
|
25
|
-
// A test points this at a fake CLI.
|
|
26
25
|
command: string[];
|
|
27
26
|
cwd: string;
|
|
28
27
|
env: Record<string, string>;
|
|
@@ -55,7 +54,6 @@ export interface BackendLaunch {
|
|
|
55
54
|
folders?: readonly string[];
|
|
56
55
|
// False keeps the CLI from loading configuration out of `cwd` (settings, hooks, MCP servers); absent loads it.
|
|
57
56
|
projectConfig?: boolean;
|
|
58
|
-
// How the CLI is started; a test runs a fake in the same process, everything else spawns it.
|
|
59
57
|
spawn?: SpawnChatProcess;
|
|
60
58
|
saveGeneratedImage?(ref: string, upload: ChatAttachmentUpload): Promise<ChatAttachment>;
|
|
61
59
|
}
|
|
@@ -24,11 +24,13 @@ export type BookmarkListener = (chatId: string, bookmarks: ChatBookmark[]) => vo
|
|
|
24
24
|
/* A name as it is kept: trimmed, and none at all when nothing is left. */
|
|
25
25
|
function cleanName(name: string | undefined): string | undefined {
|
|
26
26
|
const trimmed = name === undefined ? undefined : clipText(name.trim(), CHAT_BOOKMARK_LIMITS.name);
|
|
27
|
+
|
|
27
28
|
return trimmed === undefined || trimmed === '' ? undefined : trimmed;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
function named(bookmark: ChatBookmark, name: string | undefined): ChatBookmark {
|
|
31
32
|
const { name: _previous, ...rest } = bookmark;
|
|
33
|
+
|
|
32
34
|
return name === undefined ? rest : { ...rest, name };
|
|
33
35
|
}
|
|
34
36
|
|
|
@@ -49,6 +51,7 @@ export class BookmarkStore {
|
|
|
49
51
|
|
|
50
52
|
listen(listener: BookmarkListener): () => void {
|
|
51
53
|
this.listeners.add(listener);
|
|
54
|
+
|
|
52
55
|
return () => {
|
|
53
56
|
this.listeners.delete(listener);
|
|
54
57
|
};
|
|
@@ -64,13 +67,17 @@ export class BookmarkStore {
|
|
|
64
67
|
return this.change(chatId, (bookmarks) => {
|
|
65
68
|
const name = cleanName(input.name);
|
|
66
69
|
const existing = bookmarks.find((bookmark) => bookmark.itemId === input.itemId);
|
|
70
|
+
|
|
67
71
|
if (existing) {
|
|
68
72
|
return name === undefined ? null : bookmarks.map((bookmark) => (bookmark === existing ? named(bookmark, name) : bookmark));
|
|
69
73
|
}
|
|
74
|
+
|
|
70
75
|
if (bookmarks.length >= CHAT_BOOKMARK_LIMITS.perChat) {
|
|
71
76
|
throw new ChatError('too-many-bookmarks', `This chat already keeps ${CHAT_BOOKMARK_LIMITS.perChat} bookmarks; remove one first`);
|
|
72
77
|
}
|
|
78
|
+
|
|
73
79
|
const excerpt = clipText(input.excerpt, CHAT_BOOKMARK_LIMITS.excerpt);
|
|
80
|
+
|
|
74
81
|
return [...bookmarks, named({ itemId: input.itemId, excerpt, createdAt: now }, name)];
|
|
75
82
|
});
|
|
76
83
|
}
|
|
@@ -80,6 +87,7 @@ export class BookmarkStore {
|
|
|
80
87
|
if (!bookmarks.some((bookmark) => bookmark.itemId === itemId)) {
|
|
81
88
|
throw new ChatError('bookmark-not-found', `This chat has no bookmark on ${itemId}`);
|
|
82
89
|
}
|
|
90
|
+
|
|
83
91
|
return bookmarks.map((bookmark) => (bookmark.itemId === itemId ? named(bookmark, cleanName(name)) : bookmark));
|
|
84
92
|
});
|
|
85
93
|
}
|
|
@@ -97,7 +105,9 @@ export class BookmarkStore {
|
|
|
97
105
|
(bookmarks) => bookmarks.length > 0,
|
|
98
106
|
() => true
|
|
99
107
|
);
|
|
108
|
+
|
|
100
109
|
await rm(join(this.dir, bookmarkFileName(chatId)), { force: true });
|
|
110
|
+
|
|
101
111
|
if (had) {
|
|
102
112
|
this.tell(chatId, []);
|
|
103
113
|
}
|
|
@@ -107,9 +117,11 @@ export class BookmarkStore {
|
|
|
107
117
|
/* A fork keeps the bookmarks on the messages it copied, which are the ones up to where it was cut. */
|
|
108
118
|
async copyChat(fromChatId: string, toChatId: string, keep: (itemId: string) => boolean): Promise<void> {
|
|
109
119
|
const kept = (await this.read(fromChatId)).filter((bookmark) => keep(bookmark.itemId));
|
|
120
|
+
|
|
110
121
|
if (kept.length === 0) {
|
|
111
122
|
return;
|
|
112
123
|
}
|
|
124
|
+
|
|
113
125
|
await this.writes.run(toChatId, async () => {
|
|
114
126
|
await this.writeFile(toChatId, kept);
|
|
115
127
|
this.tell(toChatId, kept);
|
|
@@ -121,11 +133,14 @@ export class BookmarkStore {
|
|
|
121
133
|
return this.writes.run(chatId, async () => {
|
|
122
134
|
const bookmarks = await this.readFile(chatId);
|
|
123
135
|
const next = decide(bookmarks);
|
|
136
|
+
|
|
124
137
|
if (next === null) {
|
|
125
138
|
return bookmarks;
|
|
126
139
|
}
|
|
140
|
+
|
|
127
141
|
await this.writeFile(chatId, next);
|
|
128
142
|
this.tell(chatId, next);
|
|
143
|
+
|
|
129
144
|
return next;
|
|
130
145
|
});
|
|
131
146
|
}
|
|
@@ -138,28 +153,36 @@ export class BookmarkStore {
|
|
|
138
153
|
|
|
139
154
|
private async readFile(chatId: string): Promise<ChatBookmark[]> {
|
|
140
155
|
let raw: string;
|
|
156
|
+
|
|
141
157
|
try {
|
|
142
158
|
raw = await readFile(join(this.dir, bookmarkFileName(chatId)), 'utf8');
|
|
143
159
|
} catch (e) {
|
|
144
160
|
if (isNotFound(e)) {
|
|
145
161
|
return [];
|
|
146
162
|
}
|
|
163
|
+
|
|
147
164
|
throw e;
|
|
148
165
|
}
|
|
166
|
+
|
|
149
167
|
// A file that does not parse is refused rather than read as empty, which the next write would make true.
|
|
150
168
|
const parsed = BookmarkFileSchema.safeParse(JSON.parse(raw));
|
|
169
|
+
|
|
151
170
|
if (!parsed.success) {
|
|
152
171
|
throw new Error(`The bookmarks file of chat ${chatId} is not valid: ${parsed.error.issues[0]?.message ?? 'unknown problem'}`);
|
|
153
172
|
}
|
|
173
|
+
|
|
154
174
|
return parsed.data.bookmarks;
|
|
155
175
|
}
|
|
156
176
|
|
|
157
177
|
private async writeFile(chatId: string, bookmarks: readonly ChatBookmark[]): Promise<void> {
|
|
158
178
|
const path = join(this.dir, bookmarkFileName(chatId));
|
|
179
|
+
|
|
159
180
|
if (bookmarks.length === 0) {
|
|
160
181
|
await rm(path, { force: true });
|
|
182
|
+
|
|
161
183
|
return;
|
|
162
184
|
}
|
|
185
|
+
|
|
163
186
|
await mkdir(this.dir, { recursive: true, mode: 0o700 });
|
|
164
187
|
await writeAtomic(path, JSON.stringify({ version: 1, bookmarks }));
|
|
165
188
|
}
|