@adecore/agents 0.39.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.
Files changed (168) hide show
  1. package/dist/async.d.ts +5 -0
  2. package/dist/async.js +13 -5
  3. package/dist/chat/acp-protocol.js +3 -5
  4. package/dist/chat/chat-core.js +0 -2
  5. package/dist/chat/chat-process.d.ts +1 -0
  6. package/dist/chat/chat-process.js +1 -1
  7. package/dist/chat/chat-session.js +6 -8
  8. package/dist/chat/claude-protocol.js +1 -6
  9. package/dist/chat/claude-transcript.js +1 -3
  10. package/dist/chat/codex-protocol.js +1 -6
  11. package/dist/chat/projector.js +1 -3
  12. package/dist/chat/request-summary.js +1 -3
  13. package/dist/coded-error.js +1 -5
  14. package/dist/context/argv.js +1 -2
  15. package/dist/context/verb.js +1 -3
  16. package/dist/error-text.d.ts +1 -0
  17. package/dist/error-text.js +4 -0
  18. package/dist/fields.d.ts +3 -0
  19. package/dist/fields.js +12 -0
  20. package/dist/fs.d.ts +1 -0
  21. package/dist/fs.js +8 -4
  22. package/dist/lineage.js +11 -5
  23. package/dist/messages/notice-store.js +3 -4
  24. package/dist/providers/accounts/acp-account.js +3 -20
  25. package/dist/providers/accounts/service.d.ts +1 -2
  26. package/dist/providers/accounts/service.js +5 -9
  27. package/dist/providers/accounts/status.js +1 -3
  28. package/dist/providers/acp-agent-state.js +1 -3
  29. package/dist/providers/claude.js +0 -1
  30. package/dist/providers/codex.js +0 -1
  31. package/dist/providers/detect.js +1 -1
  32. package/dist/providers/shell-commands.js +1 -2
  33. package/dist/run-process.js +1 -3
  34. package/dist/skills.d.ts +1 -1
  35. package/dist/skills.js +5 -8
  36. package/dist/tasks/background-limit.js +3 -2
  37. package/dist/tasks/task-coordinator.js +11 -17
  38. package/dist/testing/watch.js +0 -1
  39. package/dist/usage/limits/monitor.js +1 -2
  40. package/dist/usage/readers/claude.js +3 -10
  41. package/dist/usage/readers/codex.js +2 -9
  42. package/dist/usage/readers/grok.js +3 -10
  43. package/dist/usage/readers/host-log.js +3 -10
  44. package/dist/usage/record.d.ts +2 -0
  45. package/dist/usage/record.js +13 -0
  46. package/package.json +6 -6
  47. package/src/async.ts +23 -5
  48. package/src/chat/acp-backend.ts +121 -0
  49. package/src/chat/acp-protocol.ts +172 -6
  50. package/src/chat/attachment-store.ts +21 -0
  51. package/src/chat/backend.ts +0 -2
  52. package/src/chat/bookmark-store.ts +23 -0
  53. package/src/chat/chat-core.ts +208 -7
  54. package/src/chat/chat-log.ts +19 -0
  55. package/src/chat/chat-process.ts +52 -1
  56. package/src/chat/chat-session.ts +401 -10
  57. package/src/chat/chat-store.ts +50 -0
  58. package/src/chat/chat-title.ts +9 -0
  59. package/src/chat/claude-backend.ts +50 -0
  60. package/src/chat/claude-protocol.ts +155 -8
  61. package/src/chat/claude-title.ts +10 -0
  62. package/src/chat/claude-transcript.ts +47 -4
  63. package/src/chat/codex-backend.ts +131 -0
  64. package/src/chat/codex-protocol.ts +180 -8
  65. package/src/chat/codex-thread.ts +23 -0
  66. package/src/chat/composer-preferences.ts +6 -0
  67. package/src/chat/context-breakdown.ts +11 -0
  68. package/src/chat/delta-coalescer.ts +8 -0
  69. package/src/chat/external-sessions.ts +28 -0
  70. package/src/chat/generated-image.ts +25 -0
  71. package/src/chat/index.ts +5 -0
  72. package/src/chat/input.ts +13 -0
  73. package/src/chat/json-rpc-transport.ts +28 -0
  74. package/src/chat/limit-resume.ts +13 -0
  75. package/src/chat/projector.ts +160 -4
  76. package/src/chat/readable-text.ts +3 -0
  77. package/src/chat/request-summary.ts +18 -4
  78. package/src/chat/subagent-projection.ts +3 -0
  79. package/src/chat/subagent-reader.ts +76 -0
  80. package/src/chat/subagent-settlement.ts +24 -0
  81. package/src/chat/thread-search.ts +13 -0
  82. package/src/chat/thread.ts +31 -0
  83. package/src/chat/ui-queries.ts +122 -0
  84. package/src/chat/ui-replies.ts +34 -0
  85. package/src/chat/visual-store.ts +49 -0
  86. package/src/chat/wake-chat.ts +3 -0
  87. package/src/client-sinks.ts +2 -0
  88. package/src/coded-error.ts +1 -5
  89. package/src/context/argv.ts +18 -2
  90. package/src/context/index.ts +1 -0
  91. package/src/context/refusal.ts +3 -0
  92. package/src/context/verb.ts +32 -4
  93. package/src/coordination/index.ts +10 -0
  94. package/src/error-text.ts +7 -0
  95. package/src/fields.ts +14 -0
  96. package/src/fs.ts +20 -4
  97. package/src/host/agent-host.ts +20 -0
  98. package/src/host/environment.ts +6 -0
  99. package/src/host/handlers.ts +18 -0
  100. package/src/host/memory-port.ts +4 -0
  101. package/src/host/wiring.ts +10 -0
  102. package/src/index.ts +1 -0
  103. package/src/lineage.ts +24 -5
  104. package/src/messages/deliver-message.ts +6 -0
  105. package/src/messages/deliver-notice.ts +13 -0
  106. package/src/messages/notice-store.ts +16 -4
  107. package/src/modes.ts +1 -0
  108. package/src/outbox/outbox-worker.ts +31 -0
  109. package/src/outbox/outbox.ts +5 -0
  110. package/src/providers/accounts/accounts.ts +36 -0
  111. package/src/providers/accounts/acp-account.ts +22 -27
  112. package/src/providers/accounts/launch.ts +4 -0
  113. package/src/providers/accounts/service.ts +110 -12
  114. package/src/providers/accounts/shadow-home.ts +10 -0
  115. package/src/providers/accounts/status.ts +13 -4
  116. package/src/providers/accounts/store.ts +25 -0
  117. package/src/providers/accounts/variables.ts +11 -1
  118. package/src/providers/acp-agent-state.ts +24 -3
  119. package/src/providers/acp-provider.ts +5 -0
  120. package/src/providers/acp-usage-log.ts +2 -0
  121. package/src/providers/catalog.ts +17 -0
  122. package/src/providers/claude.ts +10 -1
  123. package/src/providers/codex-rules.ts +3 -0
  124. package/src/providers/codex.ts +2 -1
  125. package/src/providers/detect.ts +6 -1
  126. package/src/providers/provider.ts +1 -1
  127. package/src/providers/registry.ts +18 -2
  128. package/src/providers/shell-commands.ts +37 -2
  129. package/src/record-directory.ts +19 -0
  130. package/src/run-process.ts +24 -3
  131. package/src/serializer.ts +3 -0
  132. package/src/skills.ts +73 -10
  133. package/src/tasks/background-limit.ts +9 -2
  134. package/src/tasks/end-children.ts +9 -0
  135. package/src/tasks/give-task.ts +8 -0
  136. package/src/tasks/task-coordinator.ts +81 -17
  137. package/src/tasks/task-store.ts +28 -0
  138. package/src/tasks/waiting-child.ts +21 -0
  139. package/src/tasks/wake-parent.ts +26 -0
  140. package/src/tasks/wiring.ts +22 -0
  141. package/src/testing/accounts.ts +2 -0
  142. package/src/testing/fake-acp-capture.ts +16 -0
  143. package/src/testing/fake-acp.ts +66 -0
  144. package/src/testing/fake-claude.ts +177 -0
  145. package/src/testing/fake-cli.ts +29 -0
  146. package/src/testing/fake-codex.ts +122 -0
  147. package/src/testing/manual-clock.ts +2 -0
  148. package/src/testing/watch.ts +7 -1
  149. package/src/title-file.ts +12 -0
  150. package/src/usage/accounts.ts +6 -0
  151. package/src/usage/aggregate.ts +19 -0
  152. package/src/usage/exchange.ts +12 -0
  153. package/src/usage/index-file.ts +27 -0
  154. package/src/usage/limits/monitor.ts +31 -3
  155. package/src/usage/limits/normalize.ts +30 -0
  156. package/src/usage/limits/probe.ts +17 -0
  157. package/src/usage/pricing.ts +35 -0
  158. package/src/usage/projects.ts +8 -0
  159. package/src/usage/readers/claude.ts +12 -9
  160. package/src/usage/readers/codex.ts +40 -8
  161. package/src/usage/readers/grok.ts +17 -9
  162. package/src/usage/readers/host-log.ts +9 -9
  163. package/src/usage/record.ts +22 -0
  164. package/src/usage/roots.ts +6 -0
  165. package/src/usage/scanner.ts +61 -0
  166. package/src/usage/usage-service.ts +14 -0
  167. package/src/watch-seam.ts +8 -0
  168. 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
  }
@@ -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
  }