@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.
Files changed (170) 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.d.ts +1 -0
  5. package/dist/chat/chat-core.js +9 -2
  6. package/dist/chat/chat-process.d.ts +1 -0
  7. package/dist/chat/chat-process.js +1 -1
  8. package/dist/chat/chat-session.d.ts +2 -0
  9. package/dist/chat/chat-session.js +9 -10
  10. package/dist/chat/claude-protocol.js +1 -6
  11. package/dist/chat/claude-transcript.js +1 -3
  12. package/dist/chat/codex-protocol.js +1 -6
  13. package/dist/chat/projector.js +1 -3
  14. package/dist/chat/request-summary.js +1 -3
  15. package/dist/coded-error.js +1 -5
  16. package/dist/context/argv.js +1 -2
  17. package/dist/context/verb.js +1 -3
  18. package/dist/error-text.d.ts +1 -0
  19. package/dist/error-text.js +4 -0
  20. package/dist/fields.d.ts +3 -0
  21. package/dist/fields.js +12 -0
  22. package/dist/fs.d.ts +1 -0
  23. package/dist/fs.js +8 -4
  24. package/dist/lineage.js +11 -5
  25. package/dist/messages/notice-store.js +3 -4
  26. package/dist/providers/accounts/acp-account.js +3 -20
  27. package/dist/providers/accounts/service.d.ts +1 -2
  28. package/dist/providers/accounts/service.js +5 -9
  29. package/dist/providers/accounts/status.js +1 -3
  30. package/dist/providers/acp-agent-state.js +1 -3
  31. package/dist/providers/claude.js +0 -1
  32. package/dist/providers/codex.js +0 -1
  33. package/dist/providers/detect.js +1 -1
  34. package/dist/providers/shell-commands.js +1 -2
  35. package/dist/run-process.js +1 -3
  36. package/dist/skills.d.ts +1 -1
  37. package/dist/skills.js +5 -8
  38. package/dist/tasks/background-limit.js +3 -2
  39. package/dist/tasks/task-coordinator.js +11 -17
  40. package/dist/testing/watch.js +0 -1
  41. package/dist/usage/limits/monitor.js +1 -2
  42. package/dist/usage/readers/claude.js +3 -10
  43. package/dist/usage/readers/codex.js +2 -9
  44. package/dist/usage/readers/grok.js +3 -10
  45. package/dist/usage/readers/host-log.js +3 -10
  46. package/dist/usage/record.d.ts +2 -0
  47. package/dist/usage/record.js +13 -0
  48. package/package.json +6 -6
  49. package/src/async.ts +23 -5
  50. package/src/chat/acp-backend.ts +121 -0
  51. package/src/chat/acp-protocol.ts +172 -6
  52. package/src/chat/attachment-store.ts +21 -0
  53. package/src/chat/backend.ts +0 -2
  54. package/src/chat/bookmark-store.ts +23 -0
  55. package/src/chat/chat-core.ts +218 -7
  56. package/src/chat/chat-log.ts +19 -0
  57. package/src/chat/chat-process.ts +52 -1
  58. package/src/chat/chat-session.ts +414 -13
  59. package/src/chat/chat-store.ts +50 -0
  60. package/src/chat/chat-title.ts +9 -0
  61. package/src/chat/claude-backend.ts +50 -0
  62. package/src/chat/claude-protocol.ts +155 -8
  63. package/src/chat/claude-title.ts +10 -0
  64. package/src/chat/claude-transcript.ts +47 -4
  65. package/src/chat/codex-backend.ts +131 -0
  66. package/src/chat/codex-protocol.ts +180 -8
  67. package/src/chat/codex-thread.ts +23 -0
  68. package/src/chat/composer-preferences.ts +6 -0
  69. package/src/chat/context-breakdown.ts +11 -0
  70. package/src/chat/delta-coalescer.ts +8 -0
  71. package/src/chat/external-sessions.ts +28 -0
  72. package/src/chat/generated-image.ts +25 -0
  73. package/src/chat/index.ts +5 -0
  74. package/src/chat/input.ts +13 -0
  75. package/src/chat/json-rpc-transport.ts +28 -0
  76. package/src/chat/limit-resume.ts +13 -0
  77. package/src/chat/projector.ts +160 -4
  78. package/src/chat/readable-text.ts +3 -0
  79. package/src/chat/request-summary.ts +18 -4
  80. package/src/chat/subagent-projection.ts +3 -0
  81. package/src/chat/subagent-reader.ts +76 -0
  82. package/src/chat/subagent-settlement.ts +24 -0
  83. package/src/chat/thread-search.ts +13 -0
  84. package/src/chat/thread.ts +31 -0
  85. package/src/chat/ui-queries.ts +122 -0
  86. package/src/chat/ui-replies.ts +34 -0
  87. package/src/chat/visual-store.ts +49 -0
  88. package/src/chat/wake-chat.ts +3 -0
  89. package/src/client-sinks.ts +2 -0
  90. package/src/coded-error.ts +1 -5
  91. package/src/context/argv.ts +18 -2
  92. package/src/context/index.ts +1 -0
  93. package/src/context/refusal.ts +3 -0
  94. package/src/context/verb.ts +32 -4
  95. package/src/coordination/index.ts +10 -0
  96. package/src/error-text.ts +7 -0
  97. package/src/fields.ts +14 -0
  98. package/src/fs.ts +20 -4
  99. package/src/host/agent-host.ts +20 -0
  100. package/src/host/environment.ts +6 -0
  101. package/src/host/handlers.ts +18 -0
  102. package/src/host/memory-port.ts +4 -0
  103. package/src/host/wiring.ts +10 -0
  104. package/src/index.ts +1 -0
  105. package/src/lineage.ts +24 -5
  106. package/src/messages/deliver-message.ts +6 -0
  107. package/src/messages/deliver-notice.ts +13 -0
  108. package/src/messages/notice-store.ts +16 -4
  109. package/src/modes.ts +1 -0
  110. package/src/outbox/outbox-worker.ts +31 -0
  111. package/src/outbox/outbox.ts +5 -0
  112. package/src/providers/accounts/accounts.ts +36 -0
  113. package/src/providers/accounts/acp-account.ts +22 -27
  114. package/src/providers/accounts/launch.ts +4 -0
  115. package/src/providers/accounts/service.ts +110 -12
  116. package/src/providers/accounts/shadow-home.ts +10 -0
  117. package/src/providers/accounts/status.ts +13 -4
  118. package/src/providers/accounts/store.ts +25 -0
  119. package/src/providers/accounts/variables.ts +11 -1
  120. package/src/providers/acp-agent-state.ts +24 -3
  121. package/src/providers/acp-provider.ts +5 -0
  122. package/src/providers/acp-usage-log.ts +2 -0
  123. package/src/providers/catalog.ts +17 -0
  124. package/src/providers/claude.ts +10 -1
  125. package/src/providers/codex-rules.ts +3 -0
  126. package/src/providers/codex.ts +2 -1
  127. package/src/providers/detect.ts +6 -1
  128. package/src/providers/provider.ts +1 -1
  129. package/src/providers/registry.ts +18 -2
  130. package/src/providers/shell-commands.ts +37 -2
  131. package/src/record-directory.ts +19 -0
  132. package/src/run-process.ts +24 -3
  133. package/src/serializer.ts +3 -0
  134. package/src/skills.ts +73 -10
  135. package/src/tasks/background-limit.ts +9 -2
  136. package/src/tasks/end-children.ts +9 -0
  137. package/src/tasks/give-task.ts +8 -0
  138. package/src/tasks/task-coordinator.ts +81 -17
  139. package/src/tasks/task-store.ts +28 -0
  140. package/src/tasks/waiting-child.ts +21 -0
  141. package/src/tasks/wake-parent.ts +26 -0
  142. package/src/tasks/wiring.ts +22 -0
  143. package/src/testing/accounts.ts +2 -0
  144. package/src/testing/fake-acp-capture.ts +16 -0
  145. package/src/testing/fake-acp.ts +66 -0
  146. package/src/testing/fake-claude.ts +177 -0
  147. package/src/testing/fake-cli.ts +29 -0
  148. package/src/testing/fake-codex.ts +122 -0
  149. package/src/testing/manual-clock.ts +2 -0
  150. package/src/testing/watch.ts +7 -1
  151. package/src/title-file.ts +12 -0
  152. package/src/usage/accounts.ts +6 -0
  153. package/src/usage/aggregate.ts +19 -0
  154. package/src/usage/exchange.ts +12 -0
  155. package/src/usage/index-file.ts +27 -0
  156. package/src/usage/limits/monitor.ts +31 -3
  157. package/src/usage/limits/normalize.ts +30 -0
  158. package/src/usage/limits/probe.ts +17 -0
  159. package/src/usage/pricing.ts +35 -0
  160. package/src/usage/projects.ts +8 -0
  161. package/src/usage/readers/claude.ts +12 -9
  162. package/src/usage/readers/codex.ts +40 -8
  163. package/src/usage/readers/grok.ts +17 -9
  164. package/src/usage/readers/host-log.ts +9 -9
  165. package/src/usage/record.ts +22 -0
  166. package/src/usage/roots.ts +6 -0
  167. package/src/usage/scanner.ts +61 -0
  168. package/src/usage/usage-service.ts +14 -0
  169. package/src/watch-seam.ts +8 -0
  170. package/src/windows-command.ts +12 -0
@@ -33,26 +33,32 @@ export class ClaudeTitleReader {
33
33
  /* The title of a session found by its id, for a chat, which knows no transcript path; in the projects of its account, its own folder first. */
34
34
  async forSession(agentSessionId: string, projectsDir: string = this.projectsDir, cwd?: string): Promise<string | null> {
35
35
  const path = await findClaudeSession(projectsDir, agentSessionId, cwd);
36
+
36
37
  return path === null ? null : await this.forTranscript(path);
37
38
  }
38
39
 
39
40
  async forTranscript(path: string): Promise<string | null> {
40
41
  let size: number;
42
+
41
43
  try {
42
44
  size = (await stat(path)).size;
43
45
  } catch {
44
46
  return null;
45
47
  }
48
+
46
49
  let progress = this.progress.get(path);
50
+
47
51
  // Smaller than where we were is a file that was replaced, not one that grew.
48
52
  if (!progress || size < progress.offset) {
49
53
  progress = { offset: 0, aiTitle: null, customTitle: null };
50
54
  this.progress.set(path, progress);
51
55
  }
56
+
52
57
  if (size > progress.offset) {
53
58
  const current = progress;
54
59
  current.offset = await readLines(path, current.offset, size, this.chunkBytes, (line) => this.take(line, current));
55
60
  }
61
+
56
62
  return progress.customTitle ?? progress.aiTitle;
57
63
  }
58
64
 
@@ -60,15 +66,19 @@ export class ClaudeTitleReader {
60
66
  if (!MARKERS.some((marker) => line.includes(marker))) {
61
67
  return;
62
68
  }
69
+
63
70
  let record: unknown;
71
+
64
72
  try {
65
73
  record = JSON.parse(line);
66
74
  } catch {
67
75
  return;
68
76
  }
77
+
69
78
  if (!isRecord(record)) {
70
79
  return;
71
80
  }
81
+
72
82
  if (record.type === 'ai-title') {
73
83
  progress.aiTitle = cleanTitle(record.aiTitle) ?? progress.aiTitle;
74
84
  } else if (record.type === 'custom-title') {
@@ -3,6 +3,7 @@ import { readdir, readFile, stat } from 'node:fs/promises';
3
3
  import { dirname, join } from 'node:path';
4
4
  import type { ChatHistoryResult, ChatItem } from '@adecore/agent-contracts';
5
5
  import { isRecord } from '@adecore/agent-contracts';
6
+ import { filledStr as str } from '../fields.ts';
6
7
  import { fileExists } from '../fs.ts';
7
8
  import { readLines } from '../title-file.ts';
8
9
  import { ClaudeProtocol } from './claude-protocol.ts';
@@ -12,19 +13,18 @@ import type { ChatThread } from './thread.ts';
12
13
 
13
14
  const CHUNK_BYTES = 4 * 1024 * 1024;
14
15
 
15
- function str(value: unknown): string | null {
16
- return typeof value === 'string' && value !== '' ? value : null;
17
- }
18
-
19
16
  /* The words of a user line that somebody said, or null for a line that carries tool results. */
20
17
  export function spokenText(content: unknown): string | null {
21
18
  if (typeof content === 'string') {
22
19
  return content;
23
20
  }
21
+
24
22
  if (!Array.isArray(content) || content.some((block) => isRecord(block) && block.type === 'tool_result')) {
25
23
  return null;
26
24
  }
25
+
27
26
  const texts = content.filter((block) => isRecord(block) && block.type === 'text').map((block) => String((block as { text?: unknown }).text ?? ''));
27
+
28
28
  return texts.length === 0 ? null : texts.join('\n');
29
29
  }
30
30
 
@@ -56,67 +56,89 @@ export async function findClaudeSession(projectsDir: string, sessionId: string,
56
56
  if (projectsDir === '' || sessionId === '' || /[/\\]|\.\./.test(sessionId)) {
57
57
  return null;
58
58
  }
59
+
59
60
  const file = `${sessionId}.jsonl`;
60
61
  const key = `${projectsDir}\n${sessionId}`;
61
62
  const own = cwd === undefined ? null : join(projectsDir, claudeProjectSlug(cwd), file);
63
+
62
64
  if (own !== null && (await fileExists(own))) {
63
65
  sessionPaths.set(key, own);
66
+
64
67
  return own;
65
68
  }
69
+
66
70
  const known = sessionPaths.get(key);
71
+
67
72
  if (known !== undefined && known !== own && (await fileExists(known))) {
68
73
  return known;
69
74
  }
75
+
70
76
  sessionPaths.delete(key);
71
77
  let dirs: string[];
78
+
72
79
  try {
73
80
  dirs = await readdir(projectsDir);
74
81
  } catch {
75
82
  return null;
76
83
  }
84
+
77
85
  const candidates = dirs.map((dir) => join(projectsDir, dir, file)).filter((candidate) => candidate !== own);
78
86
  const present = await Promise.all(candidates.map(fileExists));
79
87
  const found = candidates.find((_candidate, i) => present[i]) ?? null;
88
+
80
89
  if (found !== null) {
81
90
  sessionPaths.set(key, found);
82
91
  }
92
+
83
93
  return found;
84
94
  }
85
95
 
86
96
  /* Where the subagents of a session keep their transcripts: the folder beside its own transcript, once the CLI made it. */
87
97
  export async function findSubagentsDir(projectsDir: string, cwd: string, sessionId: string): Promise<string | null> {
88
98
  const transcript = await findClaudeSession(projectsDir, sessionId, cwd);
99
+
89
100
  if (transcript === null) {
90
101
  return null;
91
102
  }
103
+
92
104
  const dir = join(dirname(transcript), sessionId, 'subagents');
105
+
93
106
  return (await fileExists(dir)) ? dir : null;
94
107
  }
95
108
 
96
109
  /* Every meta file in a subagents folder that says which agent it is about; one that does not parse is left out. */
97
110
  export async function readSubagentMetas(dir: string): Promise<SubagentMeta[]> {
98
111
  let names: string[];
112
+
99
113
  try {
100
114
  names = await readdir(dir);
101
115
  } catch {
102
116
  return [];
103
117
  }
118
+
104
119
  const metas: SubagentMeta[] = [];
120
+
105
121
  for (const name of names) {
106
122
  const match = /^agent-(.+)\.meta\.json$/.exec(name);
123
+
107
124
  if (!match) {
108
125
  continue;
109
126
  }
127
+
110
128
  let parsed: unknown;
129
+
111
130
  try {
112
131
  parsed = JSON.parse(await readFile(join(dir, name), 'utf8'));
113
132
  } catch {
114
133
  continue;
115
134
  }
135
+
116
136
  if (!isRecord(parsed)) {
117
137
  continue;
118
138
  }
139
+
119
140
  const agentId = match[1]!;
141
+
120
142
  metas.push({
121
143
  agentId,
122
144
  toolUseId: str(parsed.toolUseId),
@@ -125,6 +147,7 @@ export async function readSubagentMetas(dir: string): Promise<SubagentMeta[]> {
125
147
  transcript: join(dir, `agent-${agentId}.jsonl`)
126
148
  });
127
149
  }
150
+
128
151
  return metas;
129
152
  }
130
153
 
@@ -136,20 +159,25 @@ export async function findWorkflowAgent(subagentsDir: string, agentId: string):
136
159
  if (!/^[\w-]+$/.test(agentId)) {
137
160
  return null;
138
161
  }
162
+
139
163
  const workflowsDir = join(subagentsDir, 'workflows');
140
164
  let runs: string[];
165
+
141
166
  try {
142
167
  runs = await readdir(workflowsDir);
143
168
  } catch {
144
169
  return null;
145
170
  }
171
+
146
172
  for (const run of runs) {
147
173
  const dir = join(workflowsDir, run);
148
174
  const transcript = join(dir, `agent-${agentId}.jsonl`);
175
+
149
176
  if (existsSync(transcript)) {
150
177
  return { agentId, toolUseId: null, parentAgentId: null, description: null, transcript };
151
178
  }
152
179
  }
180
+
153
181
  return null;
154
182
  }
155
183
 
@@ -178,19 +206,24 @@ export class TranscriptProjection {
178
206
  /* Reads what was added since the last call; false when the file is not there. */
179
207
  async refresh(): Promise<boolean> {
180
208
  let info: { size: number; mtimeMs: number };
209
+
181
210
  try {
182
211
  info = await stat(this.path);
183
212
  } catch {
184
213
  return false;
185
214
  }
215
+
186
216
  if (info.size < this.size || info.mtimeMs < this.mtimeMs) {
187
217
  this.reset();
188
218
  }
219
+
189
220
  this.size = info.size;
190
221
  this.mtimeMs = info.mtimeMs;
222
+
191
223
  if (info.size > this.offset) {
192
224
  this.offset = await readLines(this.path, this.offset, info.size, CHUNK_BYTES, (line) => this.take(line));
193
225
  }
226
+
194
227
  return true;
195
228
  }
196
229
 
@@ -201,6 +234,7 @@ export class TranscriptProjection {
201
234
  /* The page before `cursor`, or the newest page without one; the cursors are the thread's own. */
202
235
  page(limit: number, cursor?: string): ChatHistoryResult {
203
236
  const page = this.thread.history(limit, cursor);
237
+
204
238
  return { items: page.items.map(settledReading), history: page.history };
205
239
  }
206
240
 
@@ -218,21 +252,28 @@ export class TranscriptProjection {
218
252
  if (line.trim() === '') {
219
253
  return;
220
254
  }
255
+
221
256
  let entry: unknown;
257
+
222
258
  try {
223
259
  entry = JSON.parse(line);
224
260
  } catch {
225
261
  return;
226
262
  }
263
+
227
264
  if (!isRecord(entry) || (entry.type !== 'user' && entry.type !== 'assistant')) {
228
265
  return;
229
266
  }
267
+
230
268
  const time = typeof entry.timestamp === 'string' ? Date.parse(entry.timestamp) : Number.NaN;
269
+
231
270
  if (Number.isFinite(time)) {
232
271
  this.lineTime = time;
233
272
  }
273
+
234
274
  const message = isRecord(entry.message) ? entry.message : {};
235
275
  const said = entry.type === 'user' ? spokenText(message.content) : null;
276
+
236
277
  if (said !== null) {
237
278
  // The prompt it was given, or a message sent to it later; a reminder the CLI slipped in is not somebody talking.
238
279
  if (entry.isMeta !== true && said.trim() !== '') {
@@ -244,8 +285,10 @@ export class TranscriptProjection {
244
285
  text: said
245
286
  });
246
287
  }
288
+
247
289
  return;
248
290
  }
291
+
249
292
  for (const event of this.protocol.handle({ type: entry.type, message })) {
250
293
  this.projector.project(0, event);
251
294
  }