@agentshouse/kit 0.2.1-cb6679d048e60 → 0.2.1-cb66cb2fd0696

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 (136) hide show
  1. package/README.md +47 -46
  2. package/dist/acp.js +102 -108
  3. package/dist/agents.js +214 -62
  4. package/dist/api.js +36 -3
  5. package/dist/app-git.js +48 -0
  6. package/dist/authority-main.js +2 -2
  7. package/dist/bridge.js +40 -59
  8. package/dist/building.js +50 -0
  9. package/dist/calls.js +71 -0
  10. package/dist/chatgpt.js +32 -0
  11. package/dist/clis.js +161 -95
  12. package/dist/codex-main.js +58 -0
  13. package/dist/connection.js +105 -0
  14. package/dist/conversations.js +732 -330
  15. package/dist/copies.js +205 -150
  16. package/dist/end.js +33 -0
  17. package/dist/enrol-main.js +7 -1
  18. package/dist/events.js +546 -0
  19. package/dist/files.js +140 -17
  20. package/dist/git.js +13 -11
  21. package/dist/home.js +89 -7
  22. package/dist/house-cli.js +299 -0
  23. package/dist/house-main.js +7 -159
  24. package/dist/instructions.js +8 -10
  25. package/dist/kit-cli.js +68 -0
  26. package/dist/kit-main.js +8 -19
  27. package/dist/login.js +119 -104
  28. package/dist/logout.js +14 -13
  29. package/dist/mcp.js +8 -3
  30. package/dist/parts.js +82 -0
  31. package/dist/placement.js +31 -0
  32. package/dist/refusals.js +12 -0
  33. package/dist/relay.js +41 -0
  34. package/dist/resident.js +114 -51
  35. package/dist/scope.js +214 -0
  36. package/dist/scripts.js +255 -0
  37. package/dist/secret-input.js +4 -1
  38. package/dist/service-main.js +9 -0
  39. package/dist/service.js +55 -0
  40. package/dist/shell.js +3 -0
  41. package/dist/sign-in.js +40 -32
  42. package/dist/silence.js +28 -0
  43. package/dist/skills.js +49 -35
  44. package/dist/transcription.js +129 -41
  45. package/dist/translate.js +2 -8
  46. package/dist/update.js +91 -104
  47. package/dist/wire.js +22 -0
  48. package/dist/work.js +346 -0
  49. package/package.json +20 -20
  50. package/dist/blocks.js +0 -51
  51. package/dist/bootstrap.json +0 -24
  52. package/dist/clis-main.js +0 -23
  53. package/dist/stream.js +0 -46
  54. package/how-we-work.md +0 -23
  55. package/skills/ask-matt/LICENSE +0 -21
  56. package/skills/ask-matt/PHASE-BOUNDARIES.md +0 -55
  57. package/skills/ask-matt/SKILL.md +0 -91
  58. package/skills/ask-matt/agents/openai.yaml +0 -4
  59. package/skills/code-review/LICENSE +0 -21
  60. package/skills/code-review/SKILL.md +0 -89
  61. package/skills/code-review/agents/openai.yaml +0 -3
  62. package/skills/codebase-design/DEEPENING.md +0 -37
  63. package/skills/codebase-design/DESIGN-IT-TWICE.md +0 -44
  64. package/skills/codebase-design/LICENSE +0 -21
  65. package/skills/codebase-design/SKILL.md +0 -116
  66. package/skills/codebase-design/agents/openai.yaml +0 -3
  67. package/skills/diagnosing-bugs/LICENSE +0 -21
  68. package/skills/diagnosing-bugs/SKILL.md +0 -140
  69. package/skills/diagnosing-bugs/agents/openai.yaml +0 -3
  70. package/skills/diagnosing-bugs/scripts/hitl-loop.template.sh +0 -44
  71. package/skills/domain-modeling/ADR-FORMAT.md +0 -47
  72. package/skills/domain-modeling/CONTEXT-FORMAT.md +0 -60
  73. package/skills/domain-modeling/LICENSE +0 -21
  74. package/skills/domain-modeling/SKILL.md +0 -76
  75. package/skills/domain-modeling/agents/openai.yaml +0 -3
  76. package/skills/grill-me/LICENSE +0 -21
  77. package/skills/grill-me/SKILL.md +0 -8
  78. package/skills/grill-me/agents/openai.yaml +0 -4
  79. package/skills/grill-with-docs/LICENSE +0 -21
  80. package/skills/grill-with-docs/SKILL.md +0 -8
  81. package/skills/grill-with-docs/agents/openai.yaml +0 -4
  82. package/skills/grilling/LICENSE +0 -21
  83. package/skills/grilling/SKILL.md +0 -30
  84. package/skills/grilling/agents/openai.yaml +0 -3
  85. package/skills/handoff/LICENSE +0 -21
  86. package/skills/handoff/SKILL.md +0 -17
  87. package/skills/handoff/agents/openai.yaml +0 -4
  88. package/skills/implement/LICENSE +0 -21
  89. package/skills/implement/SKILL.md +0 -16
  90. package/skills/implement/agents/openai.yaml +0 -4
  91. package/skills/improve-codebase-architecture/HTML-REPORT.md +0 -123
  92. package/skills/improve-codebase-architecture/LICENSE +0 -21
  93. package/skills/improve-codebase-architecture/SKILL.md +0 -72
  94. package/skills/improve-codebase-architecture/agents/openai.yaml +0 -4
  95. package/skills/prototype/LICENSE +0 -21
  96. package/skills/prototype/LOGIC.md +0 -67
  97. package/skills/prototype/SKILL.md +0 -28
  98. package/skills/prototype/UI.md +0 -112
  99. package/skills/prototype/agents/openai.yaml +0 -3
  100. package/skills/research/LICENSE +0 -21
  101. package/skills/research/SKILL.md +0 -14
  102. package/skills/research/agents/openai.yaml +0 -3
  103. package/skills/resolving-merge-conflicts/LICENSE +0 -21
  104. package/skills/resolving-merge-conflicts/SKILL.md +0 -16
  105. package/skills/resolving-merge-conflicts/agents/openai.yaml +0 -3
  106. package/skills/tdd/LICENSE +0 -21
  107. package/skills/tdd/SKILL.md +0 -40
  108. package/skills/tdd/agents/openai.yaml +0 -3
  109. package/skills/tdd/mocking.md +0 -59
  110. package/skills/tdd/tests.md +0 -77
  111. package/skills/teach/GLOSSARY-FORMAT.md +0 -35
  112. package/skills/teach/LEARNING-RECORD-FORMAT.md +0 -46
  113. package/skills/teach/LICENSE +0 -21
  114. package/skills/teach/MISSION-FORMAT.md +0 -31
  115. package/skills/teach/RESOURCES-FORMAT.md +0 -32
  116. package/skills/teach/SKILL.md +0 -141
  117. package/skills/teach/agents/openai.yaml +0 -4
  118. package/skills/to-questionnaire/LICENSE +0 -21
  119. package/skills/to-questionnaire/SKILL.md +0 -55
  120. package/skills/to-questionnaire/agents/openai.yaml +0 -4
  121. package/skills/to-spec/LICENSE +0 -21
  122. package/skills/to-spec/SKILL.md +0 -76
  123. package/skills/to-spec/agents/openai.yaml +0 -4
  124. package/skills/to-tickets/LICENSE +0 -21
  125. package/skills/to-tickets/SKILL.md +0 -106
  126. package/skills/to-tickets/agents/openai.yaml +0 -4
  127. package/skills/wait-what/LICENSE +0 -21
  128. package/skills/wait-what/SKILL.md +0 -8
  129. package/skills/wait-what/agents/openai.yaml +0 -4
  130. package/skills/wayfinder/LICENSE +0 -21
  131. package/skills/wayfinder/SKILL.md +0 -129
  132. package/skills/wayfinder/agents/openai.yaml +0 -4
  133. package/skills/writing-for-agents/LICENSE +0 -21
  134. package/skills/writing-for-agents/SKILL-MECHANICS.md +0 -22
  135. package/skills/writing-for-agents/SKILL.md +0 -83
  136. package/skills/writing-for-agents/agents/openai.yaml +0 -3
package/dist/events.js ADDED
@@ -0,0 +1,546 @@
1
+ const AUTONOMOUS_ORIGINS = new Set(['task-notification', 'peer', 'coordinator', 'observer', 'observer-activity']);
2
+ const COMMAND = { shown: 'command' };
3
+ const HIDDEN = { shown: 'hidden' };
4
+ const FINISHED = new Set(['completed', 'failed']);
5
+ export const RUNNING_JOB = new Set(['running', 'paused']);
6
+ const MESSAGE_TARGETS = new Map([
7
+ ['send_routine_request', ['routine', 'agent']],
8
+ ['cancel_routine_request', ['request']],
9
+ ['send_mail', ['conversation', 'request']],
10
+ ]);
11
+ function words(line) {
12
+ const found = [];
13
+ let word = null;
14
+ let quote = null;
15
+ for (let index = 0; index < line.length; index++) {
16
+ const character = line[index];
17
+ if (quote !== null) {
18
+ if (character === quote)
19
+ quote = null;
20
+ else if (character === '\\' && quote === '"')
21
+ word += line[++index] ?? '';
22
+ else
23
+ word += character;
24
+ }
25
+ else if (character === "'" || character === '"') {
26
+ quote = character;
27
+ word ??= '';
28
+ }
29
+ else if (character === '\\') {
30
+ word = (word ?? '') + (line[++index] ?? '');
31
+ }
32
+ else if (/\s/.test(character)) {
33
+ if (word !== null)
34
+ found.push(word);
35
+ word = null;
36
+ }
37
+ else {
38
+ word = (word ?? '') + character;
39
+ }
40
+ }
41
+ if (word !== null)
42
+ found.push(word);
43
+ return found;
44
+ }
45
+ export function houseMessage(line) {
46
+ const [program, verb, json] = words(line.trim());
47
+ if (program !== 'house' || verb !== 'run_command' || json === undefined)
48
+ return null;
49
+ let call;
50
+ try {
51
+ call = JSON.parse(json);
52
+ }
53
+ catch {
54
+ return null;
55
+ }
56
+ if (call === null)
57
+ return null;
58
+ const targets = MESSAGE_TARGETS.get(call.command);
59
+ if (targets === undefined)
60
+ return null;
61
+ const target = targets.map((argument) => call.arguments?.[argument]).find((value) => typeof value === 'string');
62
+ return { shown: 'message', command: call.command, target: target ?? null };
63
+ }
64
+ function decidedByLine(command, final) {
65
+ const line = typeof command === 'string' ? command : Array.isArray(command) ? command.at(-1) : undefined;
66
+ if (typeof line !== 'string')
67
+ return final ? COMMAND : null;
68
+ return houseMessage(line) ?? COMMAND;
69
+ }
70
+ function contentText(content) {
71
+ const text = (content ?? [])
72
+ .map((entry) => (entry.type === 'content' && entry.content?.type === 'text' ? entry.content.text : ''))
73
+ .filter((part) => part !== '')
74
+ .join('\n');
75
+ return text === '' ? null : text;
76
+ }
77
+ function plan(entries) {
78
+ return entries.map((entry) => ({ content: entry.content, status: entry.status }));
79
+ }
80
+ function merged(into, from) {
81
+ if (from === null || from === undefined)
82
+ return into;
83
+ const result = { ...into };
84
+ for (const [key, value] of Object.entries(from)) {
85
+ result[key] =
86
+ typeof value === 'object' && value !== null && !Array.isArray(value) && typeof into[key] === 'object'
87
+ ? merged(into[key], value)
88
+ : value;
89
+ }
90
+ return result;
91
+ }
92
+ function names(input, ids) {
93
+ const values = JSON.stringify(input ?? {});
94
+ for (const id of ids)
95
+ if (values.includes(id))
96
+ return true;
97
+ return false;
98
+ }
99
+ class Reader {
100
+ session = null;
101
+ calls = new Map();
102
+ subagents = new Set();
103
+ done = new Set();
104
+ seenOptions = new Map();
105
+ requestedOptions = new Map();
106
+ compactions = new Set();
107
+ jobs = new Map();
108
+ read(message) {
109
+ if (message.method === undefined)
110
+ return this.answered(message.result);
111
+ const session = message.params?.sessionId;
112
+ if (session !== undefined && this.session !== null && session !== this.session) {
113
+ return this.elsewhere(session, message);
114
+ }
115
+ return this.own(message);
116
+ }
117
+ requested(option, value) {
118
+ this.requestedOptions.set(option, value);
119
+ }
120
+ refused(option) {
121
+ this.requestedOptions.delete(option);
122
+ }
123
+ closeTurn() {
124
+ const events = [];
125
+ for (const [id, call] of this.calls) {
126
+ if (call.decided?.shown === 'subagent')
127
+ continue;
128
+ if (call.decided === null)
129
+ events.push(...this.shown(call, this.decide(call, true)));
130
+ this.calls.delete(id);
131
+ }
132
+ return events;
133
+ }
134
+ answered(result) {
135
+ if (typeof result?.sessionId === 'string' && this.session === null)
136
+ this.session = result.sessionId;
137
+ for (const option of (result?.configOptions ?? [])) {
138
+ this.seenOptions.set(option.id, option.currentValue);
139
+ this.requestedOptions.delete(option.id);
140
+ }
141
+ return [];
142
+ }
143
+ elsewhere(_session, _message) {
144
+ return [];
145
+ }
146
+ own(message) {
147
+ if (message.method !== 'session/update')
148
+ return this.extension(message);
149
+ return this.update(message.params.update);
150
+ }
151
+ extension(_message) {
152
+ return [];
153
+ }
154
+ update(update) {
155
+ switch (update.sessionUpdate) {
156
+ case 'agent_message_chunk':
157
+ if (update.content.type === 'image')
158
+ return [{ kind: 'image', data: update.content.data, mediaType: update.content.mimeType }];
159
+ return update.content.type === 'text' ? [{ kind: 'text', id: update.messageId ?? null, text: update.content.text }] : [];
160
+ case 'agent_thought_chunk':
161
+ return update.content.type === 'text' ? [{ kind: 'reasoning', id: update.messageId ?? null, text: update.content.text }] : [];
162
+ case 'tool_call':
163
+ case 'tool_call_update':
164
+ return this.tool(update);
165
+ case 'plan':
166
+ return [{ kind: 'plan', entries: plan(update.entries) }];
167
+ case 'usage_update':
168
+ return [{ kind: 'context', used: update.used, window: update.size }];
169
+ case 'current_mode_update':
170
+ return [{ kind: 'marker', marker: 'mode', text: update.currentModeId }];
171
+ case 'available_commands_update':
172
+ return [{ kind: 'commands', commands: update.availableCommands }];
173
+ case 'config_option_update':
174
+ return [{ kind: 'options', options: update.configOptions }, ...this.optionsChanged(update.configOptions)];
175
+ case 'compaction_update':
176
+ if (update.status !== 'completed' || this.compactions.has(update.compactionId))
177
+ return [];
178
+ this.compactions.add(update.compactionId);
179
+ return [{ kind: 'marker', marker: 'compaction', text: '' }];
180
+ case 'async_task_spawned':
181
+ return this.job(update.asyncTaskId, update.name ?? update.description ?? '', 'running');
182
+ case 'async_task_state_update':
183
+ return RUNNING_JOB.has(update.state) ? [] : this.job(update.asyncTaskId, null, update.state);
184
+ default:
185
+ return [];
186
+ }
187
+ }
188
+ job(id, name, state) {
189
+ if (name !== null)
190
+ this.jobs.set(id, name);
191
+ const label = this.jobs.get(id) ?? '';
192
+ if (state !== 'running')
193
+ this.jobs.delete(id);
194
+ return [{ kind: 'marker', marker: 'job', text: `${label}: ${state}` }];
195
+ }
196
+ optionsChanged(options) {
197
+ const events = [];
198
+ for (const option of options) {
199
+ const marker = this.watchedOptions[option.id];
200
+ const before = this.seenOptions.get(option.id);
201
+ this.seenOptions.set(option.id, option.currentValue);
202
+ if (marker === undefined || before === undefined || before === option.currentValue)
203
+ continue;
204
+ if (this.requestedOptions.get(option.id) === option.currentValue) {
205
+ this.requestedOptions.delete(option.id);
206
+ continue;
207
+ }
208
+ events.push({ kind: 'marker', marker, text: String(option.currentValue) });
209
+ }
210
+ return events;
211
+ }
212
+ tool(update) {
213
+ let call = this.calls.get(update.toolCallId);
214
+ if (call === undefined) {
215
+ if (update.sessionUpdate !== 'tool_call')
216
+ return [];
217
+ call = { id: update.toolCallId, decided: null, meta: {}, imaged: false, status: 'in_progress' };
218
+ this.calls.set(call.id, call);
219
+ }
220
+ if (update.kind !== undefined)
221
+ call.kind = update.kind;
222
+ if (update.title !== undefined)
223
+ call.title = update.title;
224
+ if (update.status !== undefined)
225
+ call.status = update.status;
226
+ if (update.rawInput !== undefined)
227
+ call.rawInput = merged(call.rawInput ?? {}, update.rawInput);
228
+ if (update.content !== undefined)
229
+ call.content = update.content;
230
+ call.meta = merged(call.meta, update._meta);
231
+ const events = [];
232
+ const finished = FINISHED.has(call.status);
233
+ if (call.decided === null) {
234
+ const decided = this.decide(call, finished);
235
+ if (decided !== null)
236
+ events.push(...this.shown(call, decided));
237
+ }
238
+ events.push(...this.produced(call));
239
+ if (call.decided?.shown === 'subagent' && finished)
240
+ events.push(...this.subagentFinished(call));
241
+ else if (finished)
242
+ this.calls.delete(call.id);
243
+ return events;
244
+ }
245
+ shown(call, decided) {
246
+ call.decided = decided;
247
+ switch (decided.shown) {
248
+ case 'command':
249
+ return [{ kind: 'command' }];
250
+ case 'hidden':
251
+ return [];
252
+ case 'message':
253
+ return [{ kind: 'message', command: decided.command, target: decided.target }];
254
+ case 'marker':
255
+ return [{ kind: 'marker', marker: decided.marker, text: decided.text }];
256
+ case 'subagent':
257
+ this.subagents.add(call.id);
258
+ return [{ kind: 'subagent', id: call.id, description: decided.description }];
259
+ }
260
+ }
261
+ produced(_call) {
262
+ return [];
263
+ }
264
+ subagentFinished(call) {
265
+ this.calls.delete(call.id);
266
+ return this.finish(call.id, contentText(call.content));
267
+ }
268
+ finish(id, result) {
269
+ if (!this.subagents.has(id) || this.done.has(id))
270
+ return [];
271
+ this.done.add(id);
272
+ this.calls.delete(id);
273
+ return [{ kind: 'subagent-done', id, result }];
274
+ }
275
+ }
276
+ class ClaudeReader extends Reader {
277
+ watchedOptions = { model: 'model' };
278
+ tasks = new Map();
279
+ stop = null;
280
+ own(message) {
281
+ if (message.params?.update?._meta?.claudeCode?.parentToolUseId !== undefined)
282
+ return [];
283
+ return super.own(message);
284
+ }
285
+ update(update) {
286
+ if (update.sessionUpdate === 'usage_update' && update._meta?.['_claude/origin'] !== undefined) {
287
+ const stop = this.stop;
288
+ this.stop = null;
289
+ return [...super.update(update), { kind: 'ended', stop }];
290
+ }
291
+ if (update.sessionUpdate === 'session_info_update' && update._meta?.jetbrains?.air?.sessionFailure !== undefined) {
292
+ return [{ kind: 'marker', marker: 'retry', text: update._meta.jetbrains.air.sessionFailure.title }];
293
+ }
294
+ return super.update(update);
295
+ }
296
+ extension(message) {
297
+ if (message.method !== '_claude/sdkMessage')
298
+ return [];
299
+ const sdk = message.params.message;
300
+ if (sdk.type === 'result') {
301
+ if (AUTONOMOUS_ORIGINS.has(sdk.origin?.kind))
302
+ this.stop = null;
303
+ else
304
+ this.stop = sdk.stop_reason === 'refusal' ? 'refusal' : sdk.is_error === true ? null : 'end_turn';
305
+ return [];
306
+ }
307
+ switch (sdk.subtype) {
308
+ case 'task_started':
309
+ if (sdk.tool_use_id !== undefined)
310
+ this.tasks.set(sdk.task_id, sdk.tool_use_id);
311
+ return [];
312
+ case 'task_notification':
313
+ return sdk.tool_use_id === undefined ? [] : this.finish(sdk.tool_use_id, sdk.summary ?? null);
314
+ case 'task_progress':
315
+ if (!this.subagents.has(sdk.tool_use_id) || this.done.has(sdk.tool_use_id) || typeof sdk.description !== 'string')
316
+ return [];
317
+ return [{ kind: 'subagent-activity', id: sdk.tool_use_id, activity: sdk.description }];
318
+ default:
319
+ return [];
320
+ }
321
+ }
322
+ decide(call, final) {
323
+ const tool = call.meta.claudeCode?.toolName;
324
+ if (tool === 'Agent' || tool === 'Task' || call.meta.jetbrains?.air?.subagent === true) {
325
+ const description = call.rawInput?.description;
326
+ if (description === undefined && !final)
327
+ return null;
328
+ return { shown: 'subagent', description: description ?? call.title ?? '' };
329
+ }
330
+ if (tool === 'TodoWrite' || tool === 'CronCreate')
331
+ return HIDDEN;
332
+ if (tool === 'ScheduleWakeup') {
333
+ if (call.rawInput === undefined)
334
+ return final ? COMMAND : null;
335
+ return call.rawInput.stop === true ? COMMAND : HIDDEN;
336
+ }
337
+ if (call.kind === 'switch_mode')
338
+ return { shown: 'marker', marker: 'mode', text: call.title ?? '' };
339
+ if (tool === 'Bash')
340
+ return decidedByLine(call.rawInput?.command, final);
341
+ if (tool === 'SendMessage' || tool === 'TaskStop') {
342
+ if (call.rawInput === undefined)
343
+ return final ? COMMAND : null;
344
+ return names(call.rawInput, this.tasks.keys()) ? HIDDEN : COMMAND;
345
+ }
346
+ return COMMAND;
347
+ }
348
+ produced(call) {
349
+ const tool = call.meta.claudeCode?.toolName;
350
+ if (call.status !== 'completed')
351
+ return [];
352
+ if (tool === 'CronCreate' || (tool === 'ScheduleWakeup' && call.rawInput?.stop !== true)) {
353
+ return [{ kind: 'marker', marker: 'job', text: `${call.rawInput?.prompt ?? call.title ?? ''}: scheduled` }];
354
+ }
355
+ return [];
356
+ }
357
+ subagentFinished(call) {
358
+ if (call.meta.claudeCode?.toolResponse?.isAsync === true)
359
+ return [];
360
+ return super.subagentFinished(call);
361
+ }
362
+ }
363
+ const COLLABORATION = new Set(['spawnAgent', 'sendInput', 'resumeAgent', 'wait', 'closeAgent', 'followupTask']);
364
+ class CodexReader extends Reader {
365
+ watchedOptions = { mode: 'mode', collaboration_mode: 'mode' };
366
+ children = new Map();
367
+ own(message) {
368
+ if (message.method === 'session/request_permission' && message.params.toolCall?.kind === 'switch_mode') {
369
+ return [{ kind: 'started' }, { kind: 'marker', marker: 'mode', text: message.params.toolCall.title ?? '' }];
370
+ }
371
+ return super.own(message);
372
+ }
373
+ update(update) {
374
+ switch (update.sessionUpdate) {
375
+ case 'session_info_update': {
376
+ const codex = update._meta?.codex;
377
+ if (codex?.threadStatus?.type === 'active')
378
+ return [{ kind: 'started' }];
379
+ if (codex?.threadStatus?.type === 'idle')
380
+ return [{ kind: 'ended', stop: null }];
381
+ if (codex?.error !== undefined)
382
+ return [{ kind: 'marker', marker: 'retry', text: codex.error.message ?? '' }];
383
+ return [];
384
+ }
385
+ case 'notice':
386
+ return update.severity === 'info' ? [{ kind: 'marker', marker: 'model', text: update.description ?? update.title }] : [];
387
+ case 'subagent_spawned':
388
+ this.children.set(update.subagentSessionId, { id: null, text: '' });
389
+ this.subagents.add(update.subagentSessionId);
390
+ return [{ kind: 'subagent', id: update.subagentSessionId, description: update.name }];
391
+ case 'subagent_state_update':
392
+ return this.finish(update.subagentSessionId, this.children.get(update.subagentSessionId)?.text || null);
393
+ default:
394
+ return super.update(update);
395
+ }
396
+ }
397
+ elsewhere(session, message) {
398
+ const child = this.children.get(session);
399
+ const update = message.params?.update;
400
+ if (child === undefined || update === undefined)
401
+ return [];
402
+ if (update.sessionUpdate === 'agent_message_chunk' && update.content.type === 'text') {
403
+ const id = update.messageId ?? null;
404
+ if (child.id !== id)
405
+ child.text = '';
406
+ child.id = id;
407
+ child.text += update.content.text;
408
+ }
409
+ return [];
410
+ }
411
+ decide(call) {
412
+ const air = call.meta.jetbrains?.air;
413
+ if (COLLABORATION.has(call.title ?? ''))
414
+ return HIDDEN;
415
+ if (air?.subagent !== undefined)
416
+ return { shown: 'subagent', description: call.title ?? '' };
417
+ if (call.kind === 'switch_mode')
418
+ return { shown: 'marker', marker: 'mode', text: call.title ?? '' };
419
+ if (call.kind === 'execute')
420
+ return decidedByLine(call.rawInput?.command, true);
421
+ if (call.title === 'Image generation')
422
+ return HIDDEN;
423
+ return COMMAND;
424
+ }
425
+ produced(call) {
426
+ if (call.imaged || call.kind === 'read')
427
+ return [];
428
+ const image = (call.content ?? []).find((entry) => entry.type === 'content' && entry.content?.type === 'image');
429
+ if (image === undefined)
430
+ return [];
431
+ call.imaged = true;
432
+ return [{ kind: 'image', data: image.content.data, mediaType: image.content.mimeType }];
433
+ }
434
+ }
435
+ class GrokReader extends Reader {
436
+ watchedOptions = {};
437
+ children = new Map();
438
+ models = [];
439
+ model = null;
440
+ answered(result) {
441
+ if (result?.models !== undefined)
442
+ this.offered(result.models);
443
+ return super.answered(result);
444
+ }
445
+ read(message) {
446
+ const session = message.params?.sessionId;
447
+ if (this.session !== null && session !== undefined && session !== this.session && !this.children.has(session))
448
+ return [];
449
+ if (message.method === '_x.ai/queue/changed') {
450
+ return (message.params.runningPromptId ?? null) !== null && session === this.session ? [{ kind: 'started' }] : [];
451
+ }
452
+ if (message.params?.update?.sessionUpdate === 'turn_completed') {
453
+ return session === this.session ? [{ kind: 'ended', stop: null }] : [];
454
+ }
455
+ if (message.method === '_x.ai/models/update') {
456
+ this.offered(message.params);
457
+ return [];
458
+ }
459
+ return super.read(message);
460
+ }
461
+ offered(models) {
462
+ this.models = models.availableModels ?? this.models;
463
+ this.model = models.currentModelId ?? this.model;
464
+ }
465
+ window() {
466
+ return this.models.find((model) => model.modelId === this.model)?._meta?.totalContextTokens ?? null;
467
+ }
468
+ extension(message) {
469
+ const update = message.params?.update;
470
+ switch (message.method) {
471
+ case '_x.ai/task_backgrounded':
472
+ return this.job(update.task_id, update.description ?? update.command ?? '', 'running');
473
+ case '_x.ai/task_completed':
474
+ return this.job(update.task_snapshot.task_id, null, update.task_snapshot.exit_code === 0 ? 'completed' : 'failed');
475
+ case '_x.ai/scheduled_task_created':
476
+ return [{ kind: 'marker', marker: 'job', text: `${update.prompt}: next run ${update.next_fire_at}` }];
477
+ case '_x.ai/session_notification':
478
+ return this.notification(update);
479
+ default:
480
+ return [];
481
+ }
482
+ }
483
+ notification(update) {
484
+ switch (update.sessionUpdate) {
485
+ case 'response_completed': {
486
+ const usage = update.usage;
487
+ const window = this.window();
488
+ if (window === null)
489
+ return [];
490
+ const used = usage.input_tokens + (usage.cache_read_input_tokens ?? 0) + (usage.cache_creation_input_tokens ?? 0) + usage.output_tokens;
491
+ return [{ kind: 'context', used, window }];
492
+ }
493
+ case 'subagent_spawned': {
494
+ const call = [...this.calls.values()].find((candidate) => candidate.decided?.shown === 'subagent' &&
495
+ ![...this.children.values()].includes(candidate.id) &&
496
+ candidate.decided.description === update.description) ??
497
+ [...this.calls.values()].find((candidate) => candidate.decided?.shown === 'subagent' && ![...this.children.values()].includes(candidate.id));
498
+ this.children.set(update.child_session_id, call?.id ?? null);
499
+ return [];
500
+ }
501
+ case 'subagent_finished': {
502
+ const call = this.children.get(update.child_session_id);
503
+ return typeof call === 'string' ? this.finish(call, update.output ?? null) : [];
504
+ }
505
+ case 'auto_compact_completed':
506
+ return [{ kind: 'marker', marker: 'compaction', text: '' }];
507
+ case 'model_auto_switched':
508
+ this.model = update.new_model_id;
509
+ return [{ kind: 'marker', marker: 'model', text: update.new_model_id }];
510
+ case 'retry_state':
511
+ return [{ kind: 'marker', marker: 'retry', text: update.message ?? update.reason ?? update.error_type ?? '' }];
512
+ default:
513
+ return [];
514
+ }
515
+ }
516
+ decide(call) {
517
+ const tool = (call.meta['x.ai/tool'] ?? {});
518
+ if (tool.name === 'spawn_subagent' || tool.kind === 'task') {
519
+ return { shown: 'subagent', description: call.rawInput?.description ?? call.title ?? '' };
520
+ }
521
+ if (tool.name === 'todo_write' || tool.name === 'scheduler_create')
522
+ return HIDDEN;
523
+ if (tool.name === 'enter_plan_mode' || tool.name === 'exit_plan_mode') {
524
+ return { shown: 'marker', marker: 'mode', text: tool.name };
525
+ }
526
+ if (tool.name === 'kill_command_or_subagent')
527
+ return names(call.rawInput, this.children.keys()) ? HIDDEN : COMMAND;
528
+ if (tool.name === 'run_terminal_command')
529
+ return decidedByLine(call.rawInput?.command, true);
530
+ return COMMAND;
531
+ }
532
+ subagentFinished(_call) {
533
+ return [];
534
+ }
535
+ }
536
+ export function answeredFailure(answer) {
537
+ const failure = answer._meta?.jetbrains?.air?.sessionFailure;
538
+ return typeof failure?.title === 'string' ? { title: failure.title, limit: failure.category === 'limit' } : null;
539
+ }
540
+ export function readerFor(kind) {
541
+ if (kind === 'claude-agent-acp')
542
+ return new ClaudeReader();
543
+ if (kind === 'grok-build')
544
+ return new GrokReader();
545
+ return new CodexReader();
546
+ }