@agentshouse/kit 0.2.1-c30728be20485 → 0.2.1-c3cf4b145dcda
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/README.md +8 -9
- package/dist/acp.js +26 -13
- package/dist/agents.js +46 -28
- package/dist/app-git.js +64 -0
- package/dist/bootstrap.json +4 -4
- package/dist/clis.js +36 -62
- package/dist/conversations.js +276 -229
- package/dist/events.js +587 -0
- package/dist/files.js +14 -0
- package/dist/house-main.js +4 -1
- package/dist/kit-main.js +3 -0
- package/dist/login.js +4 -0
- package/dist/logout.js +3 -0
- package/dist/parts.js +64 -0
- package/dist/resident.js +3 -3
- package/dist/shell.js +3 -0
- package/dist/sign-in.js +33 -9
- package/dist/transcription.js +54 -8
- package/package.json +1 -1
- package/dist/blocks.js +0 -51
package/dist/events.js
ADDED
|
@@ -0,0 +1,587 @@
|
|
|
1
|
+
const COMMAND = { shown: 'command' };
|
|
2
|
+
const HIDDEN = { shown: 'hidden' };
|
|
3
|
+
const FINISHED = new Set(['completed', 'failed']);
|
|
4
|
+
const RUNNING_JOB = new Set(['running', 'paused']);
|
|
5
|
+
const ROUTINE_TARGETS = { send_routine_request: 'access', cancel_routine_request: 'request' };
|
|
6
|
+
function words(line) {
|
|
7
|
+
const found = [];
|
|
8
|
+
let word = null;
|
|
9
|
+
let quote = null;
|
|
10
|
+
for (let index = 0; index < line.length; index++) {
|
|
11
|
+
const character = line[index];
|
|
12
|
+
if (quote !== null) {
|
|
13
|
+
if (character === quote)
|
|
14
|
+
quote = null;
|
|
15
|
+
else if (character === '\\' && quote === '"')
|
|
16
|
+
word += line[++index] ?? '';
|
|
17
|
+
else
|
|
18
|
+
word += character;
|
|
19
|
+
}
|
|
20
|
+
else if (character === "'" || character === '"') {
|
|
21
|
+
quote = character;
|
|
22
|
+
word ??= '';
|
|
23
|
+
}
|
|
24
|
+
else if (character === '\\') {
|
|
25
|
+
word = (word ?? '') + (line[++index] ?? '');
|
|
26
|
+
}
|
|
27
|
+
else if (/\s/.test(character)) {
|
|
28
|
+
if (word !== null)
|
|
29
|
+
found.push(word);
|
|
30
|
+
word = null;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
word = (word ?? '') + character;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (word !== null)
|
|
37
|
+
found.push(word);
|
|
38
|
+
return found;
|
|
39
|
+
}
|
|
40
|
+
export function houseMessage(line) {
|
|
41
|
+
const [program, verb, json] = words(line.trim());
|
|
42
|
+
if (program !== 'house' || verb !== 'run_command' || json === undefined)
|
|
43
|
+
return null;
|
|
44
|
+
let call;
|
|
45
|
+
try {
|
|
46
|
+
call = JSON.parse(json);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
const argument = ROUTINE_TARGETS[call.command];
|
|
52
|
+
if (argument === undefined)
|
|
53
|
+
return null;
|
|
54
|
+
const target = call.arguments?.[argument];
|
|
55
|
+
return { shown: 'message', command: call.command, target: typeof target === 'string' ? target : null };
|
|
56
|
+
}
|
|
57
|
+
function decidedByLine(command, final) {
|
|
58
|
+
const line = typeof command === 'string' ? command : Array.isArray(command) ? command.at(-1) : undefined;
|
|
59
|
+
if (typeof line !== 'string')
|
|
60
|
+
return final ? COMMAND : null;
|
|
61
|
+
return houseMessage(line) ?? COMMAND;
|
|
62
|
+
}
|
|
63
|
+
function contentText(content) {
|
|
64
|
+
const text = (content ?? [])
|
|
65
|
+
.map((entry) => (entry.type === 'content' && entry.content?.type === 'text' ? entry.content.text : ''))
|
|
66
|
+
.filter((part) => part !== '')
|
|
67
|
+
.join('\n');
|
|
68
|
+
return text === '' ? null : text;
|
|
69
|
+
}
|
|
70
|
+
function plan(entries) {
|
|
71
|
+
return entries.map((entry) => ({ content: entry.content, status: entry.status }));
|
|
72
|
+
}
|
|
73
|
+
function merged(into, from) {
|
|
74
|
+
if (from === null || from === undefined)
|
|
75
|
+
return into;
|
|
76
|
+
const result = { ...into };
|
|
77
|
+
for (const [key, value] of Object.entries(from)) {
|
|
78
|
+
result[key] =
|
|
79
|
+
typeof value === 'object' && value !== null && !Array.isArray(value) && typeof into[key] === 'object'
|
|
80
|
+
? merged(into[key], value)
|
|
81
|
+
: value;
|
|
82
|
+
}
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
function names(input, ids) {
|
|
86
|
+
const values = JSON.stringify(input ?? {});
|
|
87
|
+
for (const id of ids)
|
|
88
|
+
if (values.includes(id))
|
|
89
|
+
return true;
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
class Reader {
|
|
93
|
+
session = null;
|
|
94
|
+
calls = new Map();
|
|
95
|
+
subagents = new Set();
|
|
96
|
+
done = new Set();
|
|
97
|
+
seenOptions = new Map();
|
|
98
|
+
requestedOptions = new Map();
|
|
99
|
+
compactions = new Set();
|
|
100
|
+
jobs = new Map();
|
|
101
|
+
read(message) {
|
|
102
|
+
if (message.method === undefined)
|
|
103
|
+
return this.answered(message.result);
|
|
104
|
+
const session = message.params?.sessionId;
|
|
105
|
+
if (session !== undefined && this.session !== null && session !== this.session) {
|
|
106
|
+
return this.elsewhere(session, message);
|
|
107
|
+
}
|
|
108
|
+
return this.own(message);
|
|
109
|
+
}
|
|
110
|
+
requested(option, value) {
|
|
111
|
+
this.requestedOptions.set(option, value);
|
|
112
|
+
}
|
|
113
|
+
refused(option) {
|
|
114
|
+
this.requestedOptions.delete(option);
|
|
115
|
+
}
|
|
116
|
+
turnStarted() {
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
closeTurn() {
|
|
120
|
+
const events = [];
|
|
121
|
+
for (const [id, call] of this.calls) {
|
|
122
|
+
if (call.decided?.shown === 'subagent')
|
|
123
|
+
continue;
|
|
124
|
+
if (call.decided === null)
|
|
125
|
+
events.push(...this.shown(call, this.decide(call, true)));
|
|
126
|
+
this.calls.delete(id);
|
|
127
|
+
}
|
|
128
|
+
return events;
|
|
129
|
+
}
|
|
130
|
+
answered(result) {
|
|
131
|
+
if (typeof result?.sessionId === 'string' && this.session === null)
|
|
132
|
+
this.session = result.sessionId;
|
|
133
|
+
for (const option of (result?.configOptions ?? [])) {
|
|
134
|
+
this.seenOptions.set(option.id, option.currentValue);
|
|
135
|
+
this.requestedOptions.delete(option.id);
|
|
136
|
+
}
|
|
137
|
+
return [];
|
|
138
|
+
}
|
|
139
|
+
elsewhere(_session, _message) {
|
|
140
|
+
return [];
|
|
141
|
+
}
|
|
142
|
+
own(message) {
|
|
143
|
+
if (message.method !== 'session/update')
|
|
144
|
+
return this.extension(message);
|
|
145
|
+
return this.update(message.params.update);
|
|
146
|
+
}
|
|
147
|
+
extension(_message) {
|
|
148
|
+
return [];
|
|
149
|
+
}
|
|
150
|
+
update(update) {
|
|
151
|
+
switch (update.sessionUpdate) {
|
|
152
|
+
case 'agent_message_chunk':
|
|
153
|
+
if (update.content.type === 'image')
|
|
154
|
+
return [{ kind: 'image', data: update.content.data, mediaType: update.content.mimeType }];
|
|
155
|
+
return update.content.type === 'text' ? [{ kind: 'text', id: update.messageId ?? null, text: update.content.text }] : [];
|
|
156
|
+
case 'agent_thought_chunk':
|
|
157
|
+
return update.content.type === 'text' ? [{ kind: 'reasoning', id: update.messageId ?? null, text: update.content.text }] : [];
|
|
158
|
+
case 'tool_call':
|
|
159
|
+
case 'tool_call_update':
|
|
160
|
+
return this.tool(update);
|
|
161
|
+
case 'plan':
|
|
162
|
+
return [{ kind: 'plan', entries: plan(update.entries) }];
|
|
163
|
+
case 'usage_update':
|
|
164
|
+
return [{ kind: 'context', used: update.used, window: update.size }];
|
|
165
|
+
case 'current_mode_update':
|
|
166
|
+
return [{ kind: 'marker', marker: 'mode', text: update.currentModeId }];
|
|
167
|
+
case 'available_commands_update':
|
|
168
|
+
return [{ kind: 'commands', commands: update.availableCommands }];
|
|
169
|
+
case 'config_option_update':
|
|
170
|
+
return [{ kind: 'options', options: update.configOptions }, ...this.optionsChanged(update.configOptions)];
|
|
171
|
+
case 'compaction_update':
|
|
172
|
+
if (update.status !== 'completed' || this.compactions.has(update.compactionId))
|
|
173
|
+
return [];
|
|
174
|
+
this.compactions.add(update.compactionId);
|
|
175
|
+
return [{ kind: 'marker', marker: 'compaction', text: '' }];
|
|
176
|
+
case 'async_task_spawned':
|
|
177
|
+
return this.job(update.asyncTaskId, update.name ?? update.description ?? '', 'running');
|
|
178
|
+
case 'async_task_state_update':
|
|
179
|
+
return RUNNING_JOB.has(update.state) ? [] : this.job(update.asyncTaskId, null, update.state);
|
|
180
|
+
default:
|
|
181
|
+
return [];
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
job(id, name, state) {
|
|
185
|
+
if (name !== null)
|
|
186
|
+
this.jobs.set(id, name);
|
|
187
|
+
const label = this.jobs.get(id) ?? '';
|
|
188
|
+
if (state !== 'running')
|
|
189
|
+
this.jobs.delete(id);
|
|
190
|
+
return [
|
|
191
|
+
{ kind: 'busy', key: `job:${id}`, running: state === 'running' },
|
|
192
|
+
{ kind: 'marker', marker: 'job', text: `${label}: ${state}` },
|
|
193
|
+
];
|
|
194
|
+
}
|
|
195
|
+
optionsChanged(options) {
|
|
196
|
+
const events = [];
|
|
197
|
+
for (const option of options) {
|
|
198
|
+
const marker = this.watchedOptions[option.id];
|
|
199
|
+
const before = this.seenOptions.get(option.id);
|
|
200
|
+
this.seenOptions.set(option.id, option.currentValue);
|
|
201
|
+
if (marker === undefined || before === undefined || before === option.currentValue)
|
|
202
|
+
continue;
|
|
203
|
+
if (this.requestedOptions.get(option.id) === option.currentValue) {
|
|
204
|
+
this.requestedOptions.delete(option.id);
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
events.push({ kind: 'marker', marker, text: String(option.currentValue) });
|
|
208
|
+
}
|
|
209
|
+
return events;
|
|
210
|
+
}
|
|
211
|
+
tool(update) {
|
|
212
|
+
let call = this.calls.get(update.toolCallId);
|
|
213
|
+
if (call === undefined) {
|
|
214
|
+
if (update.sessionUpdate !== 'tool_call')
|
|
215
|
+
return [];
|
|
216
|
+
call = { id: update.toolCallId, decided: null, meta: {}, imaged: false, status: 'in_progress' };
|
|
217
|
+
this.calls.set(call.id, call);
|
|
218
|
+
}
|
|
219
|
+
if (update.kind !== undefined)
|
|
220
|
+
call.kind = update.kind;
|
|
221
|
+
if (update.title !== undefined)
|
|
222
|
+
call.title = update.title;
|
|
223
|
+
if (update.status !== undefined)
|
|
224
|
+
call.status = update.status;
|
|
225
|
+
if (update.rawInput !== undefined)
|
|
226
|
+
call.rawInput = merged(call.rawInput ?? {}, update.rawInput);
|
|
227
|
+
if (update.content !== undefined)
|
|
228
|
+
call.content = update.content;
|
|
229
|
+
call.meta = merged(call.meta, update._meta);
|
|
230
|
+
const events = [];
|
|
231
|
+
const finished = FINISHED.has(call.status);
|
|
232
|
+
if (call.decided === null) {
|
|
233
|
+
const decided = this.decide(call, finished);
|
|
234
|
+
if (decided !== null)
|
|
235
|
+
events.push(...this.shown(call, decided));
|
|
236
|
+
}
|
|
237
|
+
events.push(...this.produced(call));
|
|
238
|
+
if (call.decided?.shown === 'subagent' && finished)
|
|
239
|
+
events.push(...this.subagentFinished(call));
|
|
240
|
+
else if (finished)
|
|
241
|
+
this.calls.delete(call.id);
|
|
242
|
+
return events;
|
|
243
|
+
}
|
|
244
|
+
shown(call, decided) {
|
|
245
|
+
call.decided = decided;
|
|
246
|
+
switch (decided.shown) {
|
|
247
|
+
case 'command':
|
|
248
|
+
return [{ kind: 'command' }];
|
|
249
|
+
case 'hidden':
|
|
250
|
+
return [];
|
|
251
|
+
case 'message':
|
|
252
|
+
return [{ kind: 'message', command: decided.command, target: decided.target }];
|
|
253
|
+
case 'marker':
|
|
254
|
+
return [{ kind: 'marker', marker: decided.marker, text: decided.text }];
|
|
255
|
+
case 'subagent':
|
|
256
|
+
this.subagents.add(call.id);
|
|
257
|
+
return [{ kind: 'subagent', id: call.id, description: decided.description }];
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
produced(_call) {
|
|
261
|
+
return [];
|
|
262
|
+
}
|
|
263
|
+
subagentFinished(call) {
|
|
264
|
+
this.calls.delete(call.id);
|
|
265
|
+
return this.finish(call.id, contentText(call.content));
|
|
266
|
+
}
|
|
267
|
+
finish(id, result) {
|
|
268
|
+
if (!this.subagents.has(id) || this.done.has(id))
|
|
269
|
+
return [];
|
|
270
|
+
this.done.add(id);
|
|
271
|
+
this.calls.delete(id);
|
|
272
|
+
return [{ kind: 'subagent-done', id, result }];
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
class ClaudeReader extends Reader {
|
|
276
|
+
watchedOptions = { model: 'model' };
|
|
277
|
+
tasks = new Map();
|
|
278
|
+
scheduled = new Map();
|
|
279
|
+
own(message) {
|
|
280
|
+
if (message.params?.update?._meta?.claudeCode?.parentToolUseId !== undefined)
|
|
281
|
+
return [];
|
|
282
|
+
return super.own(message);
|
|
283
|
+
}
|
|
284
|
+
update(update) {
|
|
285
|
+
if (update.sessionUpdate === 'usage_update' && update._meta?.['_claude/origin'] !== undefined) {
|
|
286
|
+
return [...super.update(update), { kind: 'ended' }];
|
|
287
|
+
}
|
|
288
|
+
if (update.sessionUpdate === 'session_info_update' && update._meta?.jetbrains?.air?.sessionFailure !== undefined) {
|
|
289
|
+
return [{ kind: 'marker', marker: 'retry', text: update._meta.jetbrains.air.sessionFailure.title }];
|
|
290
|
+
}
|
|
291
|
+
return super.update(update);
|
|
292
|
+
}
|
|
293
|
+
extension(message) {
|
|
294
|
+
if (message.method !== '_claude/sdkMessage')
|
|
295
|
+
return [];
|
|
296
|
+
const sdk = message.params.message;
|
|
297
|
+
switch (sdk.subtype) {
|
|
298
|
+
case 'background_tasks_changed':
|
|
299
|
+
return [{ kind: 'busy', key: 'tasks', running: sdk.tasks.length > 0 }];
|
|
300
|
+
case 'session_state_changed':
|
|
301
|
+
return [{ kind: 'busy', key: 'state', running: sdk.state === 'running' }];
|
|
302
|
+
case 'task_started':
|
|
303
|
+
if (sdk.tool_use_id !== undefined)
|
|
304
|
+
this.tasks.set(sdk.task_id, sdk.tool_use_id);
|
|
305
|
+
return [];
|
|
306
|
+
case 'task_notification':
|
|
307
|
+
return sdk.tool_use_id === undefined ? [] : this.finish(sdk.tool_use_id, sdk.summary ?? null);
|
|
308
|
+
default:
|
|
309
|
+
return [];
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
turnStarted() {
|
|
313
|
+
const events = [];
|
|
314
|
+
for (const [id, entry] of this.scheduled) {
|
|
315
|
+
if (entry.recurring)
|
|
316
|
+
continue;
|
|
317
|
+
this.scheduled.delete(id);
|
|
318
|
+
events.push({ kind: 'busy', key: `scheduled:${id}`, running: false });
|
|
319
|
+
}
|
|
320
|
+
return events;
|
|
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
|
+
this.scheduled.set(call.id, {
|
|
354
|
+
recurring: tool === 'CronCreate' && call.rawInput?.recurring !== false,
|
|
355
|
+
text: contentText(call.content) ?? '',
|
|
356
|
+
});
|
|
357
|
+
return [
|
|
358
|
+
{ kind: 'busy', key: `scheduled:${call.id}`, running: true },
|
|
359
|
+
{ kind: 'marker', marker: 'job', text: `${call.rawInput?.prompt ?? call.title ?? ''}: scheduled` },
|
|
360
|
+
];
|
|
361
|
+
}
|
|
362
|
+
if (tool === 'CronDelete') {
|
|
363
|
+
for (const [id, entry] of this.scheduled) {
|
|
364
|
+
if (!entry.text.includes(String(call.rawInput?.id)))
|
|
365
|
+
continue;
|
|
366
|
+
this.scheduled.delete(id);
|
|
367
|
+
return [{ kind: 'busy', key: `scheduled:${id}`, running: false }];
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
return [];
|
|
371
|
+
}
|
|
372
|
+
subagentFinished(call) {
|
|
373
|
+
if (call.meta.claudeCode?.toolResponse?.isAsync === true)
|
|
374
|
+
return [];
|
|
375
|
+
return super.subagentFinished(call);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
const COLLABORATION = new Set(['spawnAgent', 'sendInput', 'resumeAgent', 'wait', 'closeAgent', 'followupTask']);
|
|
379
|
+
class CodexReader extends Reader {
|
|
380
|
+
watchedOptions = { mode: 'mode', collaboration_mode: 'mode' };
|
|
381
|
+
children = new Map();
|
|
382
|
+
own(message) {
|
|
383
|
+
if (message.method === 'session/request_permission' && message.params.toolCall?.kind === 'switch_mode') {
|
|
384
|
+
return [{ kind: 'started' }, { kind: 'marker', marker: 'mode', text: message.params.toolCall.title ?? '' }];
|
|
385
|
+
}
|
|
386
|
+
return super.own(message);
|
|
387
|
+
}
|
|
388
|
+
update(update) {
|
|
389
|
+
switch (update.sessionUpdate) {
|
|
390
|
+
case 'session_info_update': {
|
|
391
|
+
const codex = update._meta?.codex;
|
|
392
|
+
if (codex?.threadStatus?.type === 'active')
|
|
393
|
+
return [{ kind: 'started' }];
|
|
394
|
+
if (codex?.threadStatus?.type === 'idle')
|
|
395
|
+
return [{ kind: 'ended' }];
|
|
396
|
+
if (codex?.error !== undefined)
|
|
397
|
+
return [{ kind: 'marker', marker: 'retry', text: codex.error.message ?? '' }];
|
|
398
|
+
return [];
|
|
399
|
+
}
|
|
400
|
+
case 'notice':
|
|
401
|
+
return update.severity === 'info' ? [{ kind: 'marker', marker: 'model', text: update.description ?? update.title }] : [];
|
|
402
|
+
case 'subagent_spawned':
|
|
403
|
+
this.children.set(update.subagentSessionId, { id: null, text: '' });
|
|
404
|
+
this.subagents.add(update.subagentSessionId);
|
|
405
|
+
return [
|
|
406
|
+
{ kind: 'busy', key: `subagent:${update.subagentSessionId}`, running: true },
|
|
407
|
+
{ kind: 'subagent', id: update.subagentSessionId, description: update.name },
|
|
408
|
+
];
|
|
409
|
+
case 'subagent_state_update':
|
|
410
|
+
return [
|
|
411
|
+
{ kind: 'busy', key: `subagent:${update.subagentSessionId}`, running: false },
|
|
412
|
+
...this.finish(update.subagentSessionId, this.children.get(update.subagentSessionId)?.text || null),
|
|
413
|
+
];
|
|
414
|
+
default:
|
|
415
|
+
return super.update(update);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
elsewhere(session, message) {
|
|
419
|
+
const child = this.children.get(session);
|
|
420
|
+
const update = message.params?.update;
|
|
421
|
+
if (child === undefined || update === undefined)
|
|
422
|
+
return [];
|
|
423
|
+
if (update.sessionUpdate === 'agent_message_chunk' && update.content.type === 'text') {
|
|
424
|
+
const id = update.messageId ?? null;
|
|
425
|
+
if (child.id !== id)
|
|
426
|
+
child.text = '';
|
|
427
|
+
child.id = id;
|
|
428
|
+
child.text += update.content.text;
|
|
429
|
+
}
|
|
430
|
+
const status = update._meta?.codex?.threadStatus?.type;
|
|
431
|
+
if (update.sessionUpdate === 'session_info_update' && status !== undefined) {
|
|
432
|
+
return [{ kind: 'busy', key: `thread:${session}`, running: status === 'active' }];
|
|
433
|
+
}
|
|
434
|
+
return [];
|
|
435
|
+
}
|
|
436
|
+
decide(call) {
|
|
437
|
+
const air = call.meta.jetbrains?.air;
|
|
438
|
+
if (COLLABORATION.has(call.title ?? ''))
|
|
439
|
+
return HIDDEN;
|
|
440
|
+
if (air?.subagent !== undefined)
|
|
441
|
+
return { shown: 'subagent', description: call.title ?? '' };
|
|
442
|
+
if (call.kind === 'switch_mode')
|
|
443
|
+
return { shown: 'marker', marker: 'mode', text: call.title ?? '' };
|
|
444
|
+
if (call.kind === 'execute')
|
|
445
|
+
return decidedByLine(call.rawInput?.command, true);
|
|
446
|
+
if (call.title === 'Image generation')
|
|
447
|
+
return HIDDEN;
|
|
448
|
+
return COMMAND;
|
|
449
|
+
}
|
|
450
|
+
produced(call) {
|
|
451
|
+
if (call.imaged || call.kind === 'read')
|
|
452
|
+
return [];
|
|
453
|
+
const image = (call.content ?? []).find((entry) => entry.type === 'content' && entry.content?.type === 'image');
|
|
454
|
+
if (image === undefined)
|
|
455
|
+
return [];
|
|
456
|
+
call.imaged = true;
|
|
457
|
+
return [{ kind: 'image', data: image.content.data, mediaType: image.content.mimeType }];
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
class GrokReader extends Reader {
|
|
461
|
+
watchedOptions = {};
|
|
462
|
+
children = new Map();
|
|
463
|
+
queues = new Map();
|
|
464
|
+
models = [];
|
|
465
|
+
model = null;
|
|
466
|
+
answered(result) {
|
|
467
|
+
if (result?.models !== undefined)
|
|
468
|
+
this.offered(result.models);
|
|
469
|
+
return super.answered(result);
|
|
470
|
+
}
|
|
471
|
+
read(message) {
|
|
472
|
+
const session = message.params?.sessionId;
|
|
473
|
+
if (this.session !== null && session !== undefined && session !== this.session && !this.children.has(session))
|
|
474
|
+
return [];
|
|
475
|
+
if (message.method === '_x.ai/queue/changed') {
|
|
476
|
+
const running = message.params.runningPromptId ?? null;
|
|
477
|
+
this.queues.set(session, { running, entries: message.params.entries.length });
|
|
478
|
+
return [this.queued(session), ...(running !== null && session === this.session ? [{ kind: 'started' }] : [])];
|
|
479
|
+
}
|
|
480
|
+
if (message.params?.update?.sessionUpdate === 'turn_completed') {
|
|
481
|
+
const queue = this.queues.get(session);
|
|
482
|
+
if (queue !== undefined && queue.running === message.params.update.prompt_id)
|
|
483
|
+
queue.running = null;
|
|
484
|
+
return [this.queued(session), ...(session === this.session ? [{ kind: 'ended' }] : [])];
|
|
485
|
+
}
|
|
486
|
+
if (message.method === '_x.ai/models/update') {
|
|
487
|
+
this.offered(message.params);
|
|
488
|
+
return [];
|
|
489
|
+
}
|
|
490
|
+
return super.read(message);
|
|
491
|
+
}
|
|
492
|
+
queued(session) {
|
|
493
|
+
const queue = this.queues.get(session);
|
|
494
|
+
return { kind: 'busy', key: `queue:${session}`, running: queue !== undefined && (queue.running !== null || queue.entries > 0) };
|
|
495
|
+
}
|
|
496
|
+
offered(models) {
|
|
497
|
+
this.models = models.availableModels ?? this.models;
|
|
498
|
+
this.model = models.currentModelId ?? this.model;
|
|
499
|
+
}
|
|
500
|
+
window() {
|
|
501
|
+
return this.models.find((model) => model.modelId === this.model)?._meta?.totalContextTokens ?? null;
|
|
502
|
+
}
|
|
503
|
+
extension(message) {
|
|
504
|
+
const update = message.params?.update;
|
|
505
|
+
switch (message.method) {
|
|
506
|
+
case '_x.ai/task_backgrounded':
|
|
507
|
+
return this.job(update.task_id, update.description ?? update.command ?? '', 'running');
|
|
508
|
+
case '_x.ai/task_completed':
|
|
509
|
+
return this.job(update.task_snapshot.task_id, null, update.task_snapshot.exit_code === 0 ? 'completed' : 'failed');
|
|
510
|
+
case '_x.ai/scheduled_task_created':
|
|
511
|
+
return [
|
|
512
|
+
{ kind: 'busy', key: `scheduled:${update.task_id}`, running: true },
|
|
513
|
+
{ kind: 'marker', marker: 'job', text: `${update.prompt}: next run ${update.next_fire_at}` },
|
|
514
|
+
];
|
|
515
|
+
case '_x.ai/scheduled_task_deleted':
|
|
516
|
+
return [{ kind: 'busy', key: `scheduled:${update.task_id}`, running: false }];
|
|
517
|
+
case '_x.ai/session_notification':
|
|
518
|
+
return this.notification(update);
|
|
519
|
+
case '_x.ai/mcp_initialized':
|
|
520
|
+
return [{ kind: 'servers' }];
|
|
521
|
+
default:
|
|
522
|
+
return [];
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
notification(update) {
|
|
526
|
+
switch (update.sessionUpdate) {
|
|
527
|
+
case 'response_completed': {
|
|
528
|
+
const usage = update.usage;
|
|
529
|
+
const window = this.window();
|
|
530
|
+
if (window === null)
|
|
531
|
+
return [];
|
|
532
|
+
const used = usage.input_tokens + (usage.cache_read_input_tokens ?? 0) + (usage.cache_creation_input_tokens ?? 0) + usage.output_tokens;
|
|
533
|
+
return [{ kind: 'context', used, window }];
|
|
534
|
+
}
|
|
535
|
+
case 'subagent_spawned': {
|
|
536
|
+
const call = [...this.calls.values()].find((candidate) => candidate.decided?.shown === 'subagent' &&
|
|
537
|
+
![...this.children.values()].includes(candidate.id) &&
|
|
538
|
+
candidate.decided.description === update.description) ??
|
|
539
|
+
[...this.calls.values()].find((candidate) => candidate.decided?.shown === 'subagent' && ![...this.children.values()].includes(candidate.id));
|
|
540
|
+
this.children.set(update.child_session_id, call?.id ?? null);
|
|
541
|
+
return [{ kind: 'busy', key: `subagent:${update.child_session_id}`, running: true }];
|
|
542
|
+
}
|
|
543
|
+
case 'subagent_finished': {
|
|
544
|
+
const call = this.children.get(update.child_session_id);
|
|
545
|
+
return [
|
|
546
|
+
{ kind: 'busy', key: `subagent:${update.child_session_id}`, running: false },
|
|
547
|
+
...(typeof call === 'string' ? this.finish(call, update.output ?? null) : []),
|
|
548
|
+
];
|
|
549
|
+
}
|
|
550
|
+
case 'auto_compact_completed':
|
|
551
|
+
return [{ kind: 'marker', marker: 'compaction', text: '' }];
|
|
552
|
+
case 'model_auto_switched':
|
|
553
|
+
this.model = update.new_model_id;
|
|
554
|
+
return [{ kind: 'marker', marker: 'model', text: update.new_model_id }];
|
|
555
|
+
case 'retry_state':
|
|
556
|
+
return [{ kind: 'marker', marker: 'retry', text: update.message ?? update.reason ?? update.error_type ?? '' }];
|
|
557
|
+
default:
|
|
558
|
+
return [];
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
decide(call) {
|
|
562
|
+
const tool = (call.meta['x.ai/tool'] ?? {});
|
|
563
|
+
if (tool.name === 'spawn_subagent' || tool.kind === 'task') {
|
|
564
|
+
return { shown: 'subagent', description: call.rawInput?.description ?? call.title ?? '' };
|
|
565
|
+
}
|
|
566
|
+
if (tool.name === 'todo_write' || tool.name === 'scheduler_create')
|
|
567
|
+
return HIDDEN;
|
|
568
|
+
if (tool.name === 'enter_plan_mode' || tool.name === 'exit_plan_mode') {
|
|
569
|
+
return { shown: 'marker', marker: 'mode', text: tool.name };
|
|
570
|
+
}
|
|
571
|
+
if (tool.name === 'kill_command_or_subagent')
|
|
572
|
+
return names(call.rawInput, this.children.keys()) ? HIDDEN : COMMAND;
|
|
573
|
+
if (tool.name === 'run_terminal_command')
|
|
574
|
+
return decidedByLine(call.rawInput?.command, true);
|
|
575
|
+
return COMMAND;
|
|
576
|
+
}
|
|
577
|
+
subagentFinished(_call) {
|
|
578
|
+
return [];
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
export function readerFor(kind) {
|
|
582
|
+
if (kind === 'claude-agent-acp')
|
|
583
|
+
return new ClaudeReader();
|
|
584
|
+
if (kind === 'grok-build')
|
|
585
|
+
return new GrokReader();
|
|
586
|
+
return new CodexReader();
|
|
587
|
+
}
|
package/dist/files.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { mkdir, writeFile } from 'node:fs/promises';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
+
import { HouseRefusal, retrying } from './api.js';
|
|
3
4
|
import { relayed } from './relay.js';
|
|
4
5
|
export async function placeFiles(house, directory, files, signal) {
|
|
5
6
|
const paths = [];
|
|
@@ -20,3 +21,16 @@ export async function placeFiles(house, directory, files, signal) {
|
|
|
20
21
|
}
|
|
21
22
|
return paths;
|
|
22
23
|
}
|
|
24
|
+
export function uploadBytes(upload, bytes) {
|
|
25
|
+
return retrying(async () => {
|
|
26
|
+
const answer = await fetch(relayed(upload.url), {
|
|
27
|
+
method: 'POST',
|
|
28
|
+
headers: { 'content-type': 'application/octet-stream', 'x-house-byte-operation': upload.operation },
|
|
29
|
+
body: bytes,
|
|
30
|
+
});
|
|
31
|
+
const text = await answer.text();
|
|
32
|
+
if (!answer.ok)
|
|
33
|
+
throw new HouseRefusal(upload.url, answer.status, text);
|
|
34
|
+
return JSON.parse(text);
|
|
35
|
+
});
|
|
36
|
+
}
|
package/dist/house-main.js
CHANGED
|
@@ -11,6 +11,7 @@ const PUSH_USAGE = 'usage: house push [commit] [--owner]';
|
|
|
11
11
|
const PUSH = "push [commit]: sends the Local copy's committed changes to House";
|
|
12
12
|
const ARGUMENTS = `the arguments are one JSON object in single quotes, like house search '{"query":"invoice"}'`;
|
|
13
13
|
const CLOSED = "this conversation's House connection is closed; nothing to do from here";
|
|
14
|
+
const SANDBOXED = "your CLI's sandbox kept house from House; run house as a command of its own, with no pipe, chain or redirect";
|
|
14
15
|
const REVOKED = 'this conversation no longer has House access; nothing to do from here';
|
|
15
16
|
const UNCONNECTED = "this computer's own agent has no House connection; run kit login";
|
|
16
17
|
const DISCONNECTED = "House no longer accepts this computer's own agent; run kit login";
|
|
@@ -42,7 +43,7 @@ function bridged(path, body, headers = { 'content-type': 'application/json' }, t
|
|
|
42
43
|
reject(new Error(refused(path, answer.statusCode, text)));
|
|
43
44
|
});
|
|
44
45
|
});
|
|
45
|
-
sent.on('error', () => reject(new Error(CLOSED)));
|
|
46
|
+
sent.on('error', (error) => reject(new Error(error.code === 'EPERM' || error.code === 'EACCES' ? SANDBOXED : CLOSED)));
|
|
46
47
|
sent.end(body instanceof Buffer ? body : JSON.stringify(body));
|
|
47
48
|
});
|
|
48
49
|
}
|
|
@@ -156,6 +157,8 @@ async function house([verb, argument]) {
|
|
|
156
157
|
const argv = process.argv.slice(2);
|
|
157
158
|
(argv[0] === 'push' ? push(argv.slice(1)) : house(argv)).then((text) => {
|
|
158
159
|
process.stdout.write(`${text}\n`);
|
|
160
|
+
if (argv[0] === 'shell')
|
|
161
|
+
process.exitCode = Number(/^exit: (\d+)(?:\n|$)/.exec(text)?.[1] ?? 0);
|
|
159
162
|
}, (error) => {
|
|
160
163
|
process.stderr.write(`house: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
161
164
|
process.exitCode = 1;
|
package/dist/kit-main.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { setGlobalProxyFromEnv } from 'node:http';
|
|
3
|
+
import { answerGitCredential } from './app-git.js';
|
|
3
4
|
import { login } from './login.js';
|
|
4
5
|
import { logout } from './logout.js';
|
|
5
6
|
import { resident } from './resident.js';
|
|
@@ -12,6 +13,8 @@ async function run() {
|
|
|
12
13
|
return logout();
|
|
13
14
|
if (command === 'resident')
|
|
14
15
|
return resident();
|
|
16
|
+
if (command === 'git-credential')
|
|
17
|
+
return answerGitCredential(argv[0]);
|
|
15
18
|
process.stderr.write('usage: kit <login|logout|resident>\n');
|
|
16
19
|
process.exitCode = 2;
|
|
17
20
|
}
|
package/dist/login.js
CHANGED
|
@@ -4,6 +4,7 @@ import { hostname } from 'node:os';
|
|
|
4
4
|
import { createInterface } from 'node:readline/promises';
|
|
5
5
|
import { parseArgs } from 'node:util';
|
|
6
6
|
import { post } from './api.js';
|
|
7
|
+
import { placeAppGit } from './app-git.js';
|
|
7
8
|
import { forgetOwnAgent, readDisconnected, readEnrolment, readOwnAgent, writeEnrolment, writeOwnAgent } from './home.js';
|
|
8
9
|
import { callHouse, rpc } from './mcp.js';
|
|
9
10
|
const DEFAULT_HOUSE = 'https://agents.house';
|
|
@@ -104,6 +105,7 @@ export async function login(argv) {
|
|
|
104
105
|
process.stdout.write("This computer's own agent was another User's; run kit login again to connect it to you.\n");
|
|
105
106
|
}
|
|
106
107
|
};
|
|
108
|
+
const placeGit = async () => placeAppGit(ownAgent, await readOwnAgent());
|
|
107
109
|
if (values.manual) {
|
|
108
110
|
const { start_url } = await post(house, '/kit', {
|
|
109
111
|
...started,
|
|
@@ -111,6 +113,7 @@ export async function login(argv) {
|
|
|
111
113
|
});
|
|
112
114
|
process.stdout.write(`Open this link and confirm: ${start_url}\n`);
|
|
113
115
|
await exchange(await typed('Code: '));
|
|
116
|
+
await placeGit();
|
|
114
117
|
return;
|
|
115
118
|
}
|
|
116
119
|
const callback = await listen();
|
|
@@ -133,4 +136,5 @@ export async function login(argv) {
|
|
|
133
136
|
finally {
|
|
134
137
|
callback.close();
|
|
135
138
|
}
|
|
139
|
+
await placeGit();
|
|
136
140
|
}
|