@phnx-labs/agents-cli 1.21.2 → 1.22.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.
- package/CHANGELOG.md +101 -0
- package/README.md +32 -3
- package/dist/bin/agents +0 -0
- package/dist/commands/computer-actions.d.ts +4 -0
- package/dist/commands/computer-actions.js +35 -0
- package/dist/commands/computer.js +4 -2
- package/dist/commands/exec.d.ts +27 -0
- package/dist/commands/exec.js +123 -6
- package/dist/commands/models.js +36 -1
- package/dist/commands/perf.d.ts +16 -0
- package/dist/commands/perf.js +11 -1
- package/dist/commands/projects.d.ts +11 -1
- package/dist/commands/projects.js +38 -4
- package/dist/commands/sessions-backfill.d.ts +32 -0
- package/dist/commands/sessions-backfill.js +186 -0
- package/dist/commands/sessions-picker.js +17 -2
- package/dist/commands/sessions.d.ts +22 -1
- package/dist/commands/sessions.js +331 -18
- package/dist/commands/teams.js +1 -1
- package/dist/commands/worktree.d.ts +3 -3
- package/dist/commands/worktree.js +35 -4
- package/dist/index.js +8 -0
- package/dist/lib/browser/service.js +13 -0
- package/dist/lib/computer/dispatch.d.ts +3 -1
- package/dist/lib/computer/dispatch.js +10 -2
- package/dist/lib/daemon.d.ts +5 -1
- package/dist/lib/daemon.js +63 -14
- package/dist/lib/devices/resolve-target.d.ts +6 -0
- package/dist/lib/devices/resolve-target.js +9 -3
- package/dist/lib/event-stream.d.ts +2 -0
- package/dist/lib/event-stream.js +3 -0
- package/dist/lib/events.d.ts +3 -1
- package/dist/lib/events.js +4 -2
- package/dist/lib/exec.js +39 -8
- package/dist/lib/git.d.ts +14 -0
- package/dist/lib/git.js +36 -0
- package/dist/lib/hooks/profile.js +1 -14
- package/dist/lib/hosts/dispatch.d.ts +12 -0
- package/dist/lib/hosts/dispatch.js +23 -6
- package/dist/lib/hosts/reconnect.d.ts +38 -0
- package/dist/lib/hosts/reconnect.js +85 -4
- package/dist/lib/hosts/run-target.js +14 -2
- package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/model-tiers.d.ts +54 -0
- package/dist/lib/model-tiers.js +229 -0
- package/dist/lib/models.d.ts +3 -0
- package/dist/lib/models.js +44 -7
- package/dist/lib/percentile.d.ts +12 -0
- package/dist/lib/percentile.js +24 -0
- package/dist/lib/perf/db.d.ts +1 -2
- package/dist/lib/perf/db.js +2 -14
- package/dist/lib/plugins.js +12 -1
- package/dist/lib/pricing/prices.json +16 -1
- package/dist/lib/project-focus.d.ts +42 -0
- package/dist/lib/project-focus.js +80 -0
- package/dist/lib/project-schedule.d.ts +75 -0
- package/dist/lib/project-schedule.js +110 -0
- package/dist/lib/redact.d.ts +2 -0
- package/dist/lib/redact.js +22 -0
- package/dist/lib/remote-agents-json.d.ts +2 -0
- package/dist/lib/remote-agents-json.js +3 -3
- package/dist/lib/resources.d.ts +16 -0
- package/dist/lib/resources.js +25 -14
- package/dist/lib/rotate.d.ts +84 -1
- package/dist/lib/rotate.js +155 -5
- package/dist/lib/routines.js +1 -14
- package/dist/lib/runner.d.ts +4 -2
- package/dist/lib/runner.js +21 -5
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/session/bash-command.js +60 -9
- package/dist/lib/session/db.d.ts +22 -1
- package/dist/lib/session/db.js +516 -24
- package/dist/lib/session/discover.d.ts +68 -7
- package/dist/lib/session/discover.js +186 -84
- package/dist/lib/session/highlights.d.ts +24 -4
- package/dist/lib/session/highlights.js +52 -7
- package/dist/lib/session/parse.d.ts +8 -1
- package/dist/lib/session/parse.js +102 -35
- package/dist/lib/session/prompt.d.ts +19 -0
- package/dist/lib/session/prompt.js +43 -0
- package/dist/lib/session/remote-list.d.ts +71 -0
- package/dist/lib/session/remote-list.js +410 -2
- package/dist/lib/session/shell-programs.d.ts +15 -0
- package/dist/lib/session/shell-programs.js +359 -0
- package/dist/lib/session/tool-calls.d.ts +88 -0
- package/dist/lib/session/tool-calls.js +612 -0
- package/dist/lib/session/tool-index.d.ts +100 -0
- package/dist/lib/session/tool-index.js +773 -0
- package/dist/lib/session/tool-store.d.ts +15 -0
- package/dist/lib/session/tool-store.js +198 -0
- package/dist/lib/session/types.d.ts +49 -0
- package/dist/lib/state.d.ts +10 -1
- package/dist/lib/state.js +11 -2
- package/dist/lib/teams/remoteWorktree.d.ts +3 -4
- package/dist/lib/teams/remoteWorktree.js +3 -4
- package/dist/lib/teams/worktree.d.ts +11 -1
- package/dist/lib/teams/worktree.js +42 -4
- package/dist/lib/types.d.ts +31 -0
- package/dist/lib/types.js +17 -0
- package/package.json +3 -1
|
@@ -7,9 +7,11 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import * as fs from 'fs';
|
|
9
9
|
import { truncate } from '../format.js';
|
|
10
|
+
import { sanitizeForTerminal } from '../redact.js';
|
|
10
11
|
import * as path from 'path';
|
|
11
12
|
import Database from '../sqlite.js';
|
|
12
|
-
import { isSyntheticUserMessage } from './prompt.js';
|
|
13
|
+
import { isSyntheticUserMessage, extractSlashCommandName, extractSlashCommandFromToolInput } from './prompt.js';
|
|
14
|
+
import { structuredToolResult } from './tool-calls.js';
|
|
13
15
|
/**
|
|
14
16
|
* Largest session file we will load into memory. Above this we throw a clean
|
|
15
17
|
* error instead of OOMing or hitting V8's ERR_STRING_TOO_LONG. Aligns with
|
|
@@ -23,12 +25,7 @@ export const SESSION_FILE_MAX_BYTES = 200_000_000;
|
|
|
23
25
|
* carriage return (0x0d). Everything else in the C0/C1 range and every CSI/OSC
|
|
24
26
|
* escape is dropped.
|
|
25
27
|
*/
|
|
26
|
-
|
|
27
|
-
export function sanitizeForTerminal(s) {
|
|
28
|
-
if (typeof s !== 'string' || !s)
|
|
29
|
-
return s;
|
|
30
|
-
return s.replace(TERMINAL_ESCAPE_REGEX, '');
|
|
31
|
-
}
|
|
28
|
+
export { sanitizeForTerminal } from '../redact.js';
|
|
32
29
|
/** Recursively sanitize every string value within a tool-args object. */
|
|
33
30
|
function sanitizeArgsDeep(value) {
|
|
34
31
|
if (typeof value === 'string')
|
|
@@ -373,12 +370,21 @@ export function parseClaudeContent(content) {
|
|
|
373
370
|
agent: 'claude',
|
|
374
371
|
timestamp,
|
|
375
372
|
tool: toolName,
|
|
373
|
+
callId: toolId,
|
|
376
374
|
args: toolInput,
|
|
377
375
|
path: toolInput.file_path || undefined,
|
|
378
376
|
command: toolName === 'Bash' ? toolInput.command : undefined,
|
|
379
377
|
};
|
|
380
378
|
if (isLocal)
|
|
381
379
|
event._local = true;
|
|
380
|
+
// SlashCommand: the MODEL invoking a slash command programmatically
|
|
381
|
+
// (distinct from the <command-name> wrapper below, which is the
|
|
382
|
+
// USER typing one) — see prompt.ts's extractSlashCommandFromToolInput.
|
|
383
|
+
if (toolName === 'SlashCommand') {
|
|
384
|
+
const slashCommand = extractSlashCommandFromToolInput(toolInput);
|
|
385
|
+
if (slashCommand)
|
|
386
|
+
event.slashCommand = slashCommand;
|
|
387
|
+
}
|
|
382
388
|
events.push(event);
|
|
383
389
|
}
|
|
384
390
|
}
|
|
@@ -403,13 +409,21 @@ export function parseClaudeContent(content) {
|
|
|
403
409
|
// Simple user text
|
|
404
410
|
const text = contentBlocks.trim();
|
|
405
411
|
if (text) {
|
|
406
|
-
|
|
412
|
+
const event = {
|
|
407
413
|
type: 'message',
|
|
408
414
|
agent: 'claude',
|
|
409
415
|
timestamp,
|
|
410
416
|
role: 'user',
|
|
411
417
|
content: text,
|
|
412
|
-
}
|
|
418
|
+
};
|
|
419
|
+
// The USER typing a slash command — Claude injects a <command-name>
|
|
420
|
+
// wrapper as the message content (see prompt.ts's
|
|
421
|
+
// extractSlashCommandName; distinct from the SlashCommand tool-use
|
|
422
|
+
// above, which is the model invoking one programmatically).
|
|
423
|
+
const slashCommand = extractSlashCommandName(text);
|
|
424
|
+
if (slashCommand)
|
|
425
|
+
event.slashCommand = slashCommand;
|
|
426
|
+
events.push(event);
|
|
413
427
|
}
|
|
414
428
|
}
|
|
415
429
|
else if (Array.isArray(contentBlocks)) {
|
|
@@ -444,6 +458,7 @@ export function parseClaudeContent(content) {
|
|
|
444
458
|
const toolId = block.tool_use_id;
|
|
445
459
|
const toolInfo = toolId ? toolUseMap.get(toolId) : undefined;
|
|
446
460
|
const isError = block.is_error === true;
|
|
461
|
+
const structured = structuredToolResult(block);
|
|
447
462
|
// Extract output text from tool result
|
|
448
463
|
let output = '';
|
|
449
464
|
if (typeof block.content === 'string') {
|
|
@@ -461,6 +476,11 @@ export function parseClaudeContent(content) {
|
|
|
461
476
|
agent: 'claude',
|
|
462
477
|
timestamp,
|
|
463
478
|
tool: toolInfo?.tool,
|
|
479
|
+
callId: toolId,
|
|
480
|
+
outcome: 'error',
|
|
481
|
+
exitCode: structured.exitCode,
|
|
482
|
+
statusCode: structured.statusCode,
|
|
483
|
+
errorCode: structured.errorCode,
|
|
464
484
|
content: output || 'Tool execution failed',
|
|
465
485
|
});
|
|
466
486
|
}
|
|
@@ -470,7 +490,12 @@ export function parseClaudeContent(content) {
|
|
|
470
490
|
agent: 'claude',
|
|
471
491
|
timestamp,
|
|
472
492
|
tool: toolInfo?.tool,
|
|
493
|
+
callId: toolId,
|
|
473
494
|
success: true,
|
|
495
|
+
outcome: 'ok',
|
|
496
|
+
exitCode: structured.exitCode,
|
|
497
|
+
statusCode: structured.statusCode,
|
|
498
|
+
errorCode: structured.errorCode,
|
|
474
499
|
output: output.length > 500 ? output.slice(0, 497) + '...' : output,
|
|
475
500
|
});
|
|
476
501
|
}
|
|
@@ -640,6 +665,7 @@ export function parseCodexContent(content) {
|
|
|
640
665
|
agent: 'codex',
|
|
641
666
|
timestamp,
|
|
642
667
|
tool: name,
|
|
668
|
+
callId,
|
|
643
669
|
args,
|
|
644
670
|
command: name === 'exec_command' ? (args.command || args.cmd) : undefined,
|
|
645
671
|
path: args.file_path || args.path || undefined,
|
|
@@ -648,14 +674,19 @@ export function parseCodexContent(content) {
|
|
|
648
674
|
else if (ptype === 'function_call_output') {
|
|
649
675
|
const callId = payload.call_id || payload.id;
|
|
650
676
|
const callInfo = callId ? callMap.get(callId) : undefined;
|
|
651
|
-
const
|
|
677
|
+
const result = structuredToolResult(payload.output);
|
|
652
678
|
events.push({
|
|
653
679
|
type: 'tool_result',
|
|
654
680
|
agent: 'codex',
|
|
655
681
|
timestamp,
|
|
656
682
|
tool: callInfo?.name,
|
|
657
|
-
|
|
658
|
-
|
|
683
|
+
callId,
|
|
684
|
+
success: result.outcome === 'error' ? false : true,
|
|
685
|
+
outcome: result.outcome,
|
|
686
|
+
exitCode: result.exitCode,
|
|
687
|
+
statusCode: result.statusCode,
|
|
688
|
+
errorCode: result.errorCode,
|
|
689
|
+
output: result.text.length > 500 ? result.text.slice(0, 497) + '...' : result.text,
|
|
659
690
|
});
|
|
660
691
|
if (callId)
|
|
661
692
|
callMap.delete(callId);
|
|
@@ -682,6 +713,7 @@ export function parseCodexContent(content) {
|
|
|
682
713
|
agent: 'codex',
|
|
683
714
|
timestamp,
|
|
684
715
|
tool,
|
|
716
|
+
callId,
|
|
685
717
|
args,
|
|
686
718
|
path: patchPath,
|
|
687
719
|
});
|
|
@@ -697,14 +729,19 @@ export function parseCodexContent(content) {
|
|
|
697
729
|
else if (ptype === 'custom_tool_call_output') {
|
|
698
730
|
const callId = payload.call_id || payload.id;
|
|
699
731
|
const callInfo = callId ? callMap.get(callId) : undefined;
|
|
700
|
-
const
|
|
732
|
+
const result = structuredToolResult(payload.output);
|
|
701
733
|
events.push({
|
|
702
734
|
type: 'tool_result',
|
|
703
735
|
agent: 'codex',
|
|
704
736
|
timestamp,
|
|
705
737
|
tool: callInfo?.name,
|
|
706
|
-
|
|
707
|
-
|
|
738
|
+
callId,
|
|
739
|
+
success: result.outcome === 'error' ? false : true,
|
|
740
|
+
outcome: result.outcome,
|
|
741
|
+
exitCode: result.exitCode,
|
|
742
|
+
statusCode: result.statusCode,
|
|
743
|
+
errorCode: result.errorCode,
|
|
744
|
+
output: result.text.length > 500 ? result.text.slice(0, 497) + '...' : result.text,
|
|
708
745
|
});
|
|
709
746
|
if (callId)
|
|
710
747
|
callMap.delete(callId);
|
|
@@ -802,6 +839,7 @@ export function parseGemini(filePath) {
|
|
|
802
839
|
agent: 'gemini',
|
|
803
840
|
timestamp: tc.timestamp || timestamp,
|
|
804
841
|
tool: toolName,
|
|
842
|
+
callId: typeof tc.id === 'string' ? tc.id : undefined,
|
|
805
843
|
args,
|
|
806
844
|
command: ['run_shell_command', 'shell', 'bash'].includes(toolName) ? args.command : undefined,
|
|
807
845
|
path: args.file_path || args.path || undefined,
|
|
@@ -825,6 +863,7 @@ export function parseGemini(filePath) {
|
|
|
825
863
|
agent: 'gemini',
|
|
826
864
|
timestamp: tc.timestamp || timestamp,
|
|
827
865
|
tool: toolName,
|
|
866
|
+
callId: typeof tc.id === 'string' ? tc.id : undefined,
|
|
828
867
|
success: tc.status === 'success',
|
|
829
868
|
output: output.length > 500 ? output.slice(0, 497) + '...' : output,
|
|
830
869
|
});
|
|
@@ -1044,6 +1083,7 @@ export function parseAntigravity(dbPath) {
|
|
|
1044
1083
|
agent: 'antigravity',
|
|
1045
1084
|
timestamp,
|
|
1046
1085
|
tool: norm,
|
|
1086
|
+
callId: call.id,
|
|
1047
1087
|
args: a,
|
|
1048
1088
|
command: norm === 'Bash' ? a.CommandLine : undefined,
|
|
1049
1089
|
// Antigravity uses PascalCase arg keys; probe the known path-bearing ones.
|
|
@@ -1175,6 +1215,7 @@ export function parseGrok(filePath) {
|
|
|
1175
1215
|
agent: 'grok',
|
|
1176
1216
|
timestamp,
|
|
1177
1217
|
tool: toolName,
|
|
1218
|
+
callId: typeof call?.id === 'string' ? call.id : undefined,
|
|
1178
1219
|
args,
|
|
1179
1220
|
path: args.path || args.file_path || undefined,
|
|
1180
1221
|
command: typeof args.command === 'string' ? args.command : undefined,
|
|
@@ -1190,13 +1231,17 @@ export function parseGrok(filePath) {
|
|
|
1190
1231
|
const callId = typeof msg.tool_call_id === 'string' ? msg.tool_call_id : undefined;
|
|
1191
1232
|
const toolName = (callId && toolCallMap.get(callId)) || 'unknown';
|
|
1192
1233
|
const output = typeof msg.content === 'string' ? msg.content : extractText(msg.content);
|
|
1193
|
-
const
|
|
1234
|
+
const structuredError = msg.is_error === true;
|
|
1235
|
+
const structuredSuccess = msg.is_error === false;
|
|
1236
|
+
const displayAsError = structuredError || (typeof output === 'string' && output.startsWith('Error:'));
|
|
1194
1237
|
events.push({
|
|
1195
|
-
type:
|
|
1238
|
+
type: displayAsError ? 'error' : 'tool_result',
|
|
1196
1239
|
agent: 'grok',
|
|
1197
1240
|
timestamp,
|
|
1198
1241
|
tool: toolName,
|
|
1199
|
-
|
|
1242
|
+
callId,
|
|
1243
|
+
success: structuredError ? false : structuredSuccess ? true : undefined,
|
|
1244
|
+
outcome: structuredError ? 'error' : structuredSuccess ? 'ok' : 'unknown',
|
|
1200
1245
|
output: output.length > 500 ? output.slice(0, 497) + '...' : output,
|
|
1201
1246
|
});
|
|
1202
1247
|
if (callId)
|
|
@@ -1295,11 +1340,13 @@ export function parseOpenCode(filePath) {
|
|
|
1295
1340
|
const state = partData.state || {};
|
|
1296
1341
|
const input = state.input || {};
|
|
1297
1342
|
const output = state.output || '';
|
|
1343
|
+
const callId = typeof partData.callID === 'string' ? partData.callID : undefined;
|
|
1298
1344
|
events.push({
|
|
1299
1345
|
type: 'tool_use',
|
|
1300
1346
|
agent: 'opencode',
|
|
1301
1347
|
timestamp,
|
|
1302
1348
|
tool: toolName,
|
|
1349
|
+
callId,
|
|
1303
1350
|
args: input,
|
|
1304
1351
|
command: toolName === 'shell' ? input.command : undefined,
|
|
1305
1352
|
path: input.filePath || input.path || undefined,
|
|
@@ -1311,6 +1358,7 @@ export function parseOpenCode(filePath) {
|
|
|
1311
1358
|
agent: 'opencode',
|
|
1312
1359
|
timestamp,
|
|
1313
1360
|
tool: toolName,
|
|
1361
|
+
callId,
|
|
1314
1362
|
success: state.status === 'completed',
|
|
1315
1363
|
output: outputStr.length > 500 ? outputStr.slice(0, 497) + '...' : outputStr,
|
|
1316
1364
|
});
|
|
@@ -1354,8 +1402,10 @@ export function parseRush(filePath) {
|
|
|
1354
1402
|
continue;
|
|
1355
1403
|
}
|
|
1356
1404
|
const type = raw.type;
|
|
1357
|
-
const timestamp = raw.created_at
|
|
1358
|
-
const content = raw.content
|
|
1405
|
+
const timestamp = typeof raw.created_at === 'string' ? raw.created_at : new Date().toISOString();
|
|
1406
|
+
const content = raw.content && typeof raw.content === 'object' && !Array.isArray(raw.content)
|
|
1407
|
+
? raw.content
|
|
1408
|
+
: {};
|
|
1359
1409
|
if (type === 'message') {
|
|
1360
1410
|
const text = typeof content.text === 'string' ? content.text.trim() : '';
|
|
1361
1411
|
if (!text)
|
|
@@ -1378,9 +1428,11 @@ export function parseRush(filePath) {
|
|
|
1378
1428
|
});
|
|
1379
1429
|
}
|
|
1380
1430
|
else if (type === 'tool_call') {
|
|
1381
|
-
const toolName = raw.name
|
|
1382
|
-
const args = content.input
|
|
1383
|
-
|
|
1431
|
+
const toolName = typeof raw.name === 'string' && raw.name.length > 0 ? raw.name : 'unknown';
|
|
1432
|
+
const args = content.input && typeof content.input === 'object' && !Array.isArray(content.input)
|
|
1433
|
+
? content.input
|
|
1434
|
+
: {};
|
|
1435
|
+
const callId = typeof raw.tool_call_id === 'string' ? raw.tool_call_id : undefined;
|
|
1384
1436
|
if (callId)
|
|
1385
1437
|
toolCallMap.set(callId, { tool: toolName, args });
|
|
1386
1438
|
events.push({
|
|
@@ -1388,13 +1440,14 @@ export function parseRush(filePath) {
|
|
|
1388
1440
|
agent: 'rush',
|
|
1389
1441
|
timestamp,
|
|
1390
1442
|
tool: toolName,
|
|
1443
|
+
callId,
|
|
1391
1444
|
args,
|
|
1392
1445
|
path: args.file_path || args.path || undefined,
|
|
1393
1446
|
command: (toolName === 'Bash' || toolName === 'shell') ? args.command : undefined,
|
|
1394
1447
|
});
|
|
1395
1448
|
}
|
|
1396
1449
|
else if (type === 'tool_result') {
|
|
1397
|
-
const callId = raw.tool_call_id;
|
|
1450
|
+
const callId = typeof raw.tool_call_id === 'string' ? raw.tool_call_id : undefined;
|
|
1398
1451
|
const info = callId ? toolCallMap.get(callId) : undefined;
|
|
1399
1452
|
const output = content.output;
|
|
1400
1453
|
let outputStr = '';
|
|
@@ -1414,7 +1467,8 @@ export function parseRush(filePath) {
|
|
|
1414
1467
|
type: success ? 'tool_result' : 'error',
|
|
1415
1468
|
agent: 'rush',
|
|
1416
1469
|
timestamp,
|
|
1417
|
-
tool: info?.tool ?? raw.name,
|
|
1470
|
+
tool: info?.tool ?? (typeof raw.name === 'string' ? raw.name : 'unknown'),
|
|
1471
|
+
callId,
|
|
1418
1472
|
success,
|
|
1419
1473
|
output: outputStr.length > 500 ? outputStr.slice(0, 497) + '...' : outputStr,
|
|
1420
1474
|
});
|
|
@@ -1575,9 +1629,11 @@ export function parseKimi(filePath) {
|
|
|
1575
1629
|
}
|
|
1576
1630
|
else if (eventType === 'tool.call') {
|
|
1577
1631
|
const fn = event.function || {};
|
|
1578
|
-
const toolName = typeof event.name === 'string'
|
|
1632
|
+
const toolName = typeof event.name === 'string'
|
|
1633
|
+
? event.name
|
|
1634
|
+
: typeof fn.name === 'string' ? fn.name : 'unknown';
|
|
1579
1635
|
let args = {};
|
|
1580
|
-
if (event.args && typeof event.args === 'object') {
|
|
1636
|
+
if (event.args && typeof event.args === 'object' && !Array.isArray(event.args)) {
|
|
1581
1637
|
args = event.args;
|
|
1582
1638
|
}
|
|
1583
1639
|
else if (typeof fn.arguments === 'string') {
|
|
@@ -1588,10 +1644,11 @@ export function parseKimi(filePath) {
|
|
|
1588
1644
|
args = { _raw: fn.arguments };
|
|
1589
1645
|
}
|
|
1590
1646
|
}
|
|
1591
|
-
else if (fn.arguments && typeof fn.arguments === 'object') {
|
|
1647
|
+
else if (fn.arguments && typeof fn.arguments === 'object' && !Array.isArray(fn.arguments)) {
|
|
1592
1648
|
args = fn.arguments;
|
|
1593
1649
|
}
|
|
1594
|
-
const
|
|
1650
|
+
const rawCallId = event.toolCallId || event.uuid;
|
|
1651
|
+
const callId = typeof rawCallId === 'string' ? rawCallId : undefined;
|
|
1595
1652
|
if (callId) {
|
|
1596
1653
|
toolCallMap.set(callId, toolName);
|
|
1597
1654
|
}
|
|
@@ -1600,23 +1657,29 @@ export function parseKimi(filePath) {
|
|
|
1600
1657
|
agent: 'kimi',
|
|
1601
1658
|
timestamp,
|
|
1602
1659
|
tool: toolName,
|
|
1660
|
+
callId,
|
|
1603
1661
|
args,
|
|
1604
1662
|
path: args.path || args.file_path || undefined,
|
|
1605
1663
|
command: toolName === 'Bash' ? args.command : undefined,
|
|
1606
1664
|
});
|
|
1607
1665
|
}
|
|
1608
1666
|
else if (eventType === 'tool.result') {
|
|
1609
|
-
const
|
|
1667
|
+
const rawCallId = event.toolCallId || event.parentUuid;
|
|
1668
|
+
const callId = typeof rawCallId === 'string' ? rawCallId : undefined;
|
|
1610
1669
|
const toolName = (callId && toolCallMap.get(callId)) || 'unknown';
|
|
1611
1670
|
const result = event.result || {};
|
|
1612
1671
|
const output = typeof result.output === 'string' ? result.output : '';
|
|
1613
|
-
const
|
|
1672
|
+
const structuredError = result.isError === true;
|
|
1673
|
+
const structuredSuccess = result.isError === false;
|
|
1674
|
+
const displayAsError = structuredError || (output && output.startsWith('Error:'));
|
|
1614
1675
|
events.push({
|
|
1615
|
-
type:
|
|
1676
|
+
type: displayAsError ? 'error' : 'tool_result',
|
|
1616
1677
|
agent: 'kimi',
|
|
1617
1678
|
timestamp,
|
|
1618
1679
|
tool: toolName,
|
|
1619
|
-
|
|
1680
|
+
callId,
|
|
1681
|
+
success: structuredError ? false : structuredSuccess ? true : undefined,
|
|
1682
|
+
outcome: structuredError ? 'error' : structuredSuccess ? 'ok' : 'unknown',
|
|
1620
1683
|
output: output.length > 500 ? output.slice(0, 497) + '...' : output,
|
|
1621
1684
|
});
|
|
1622
1685
|
if (callId) {
|
|
@@ -1738,6 +1801,7 @@ function parseAnthropicMessageJsonl(filePath, agent) {
|
|
|
1738
1801
|
agent,
|
|
1739
1802
|
timestamp,
|
|
1740
1803
|
tool: toolName,
|
|
1804
|
+
callId: typeof block.id === 'string' ? block.id : undefined,
|
|
1741
1805
|
args: toolInput,
|
|
1742
1806
|
path: toolInput.file_path || toolInput.path || undefined,
|
|
1743
1807
|
command: (toolName === 'Bash' || toolName === 'Execute' || toolName === 'Shell') ? toolInput.command : undefined,
|
|
@@ -1753,11 +1817,14 @@ function parseAnthropicMessageJsonl(filePath, agent) {
|
|
|
1753
1817
|
? block.content.filter((c) => c.type === 'text').map((c) => c.text || '').join('\n')
|
|
1754
1818
|
: '';
|
|
1755
1819
|
if (isError) {
|
|
1756
|
-
events.push({
|
|
1820
|
+
events.push({
|
|
1821
|
+
type: 'error', agent, timestamp, tool: toolInfo?.tool,
|
|
1822
|
+
callId: toolId, outcome: 'error', content: output || 'Tool execution failed',
|
|
1823
|
+
});
|
|
1757
1824
|
}
|
|
1758
1825
|
else {
|
|
1759
1826
|
events.push({
|
|
1760
|
-
type: 'tool_result', agent, timestamp, tool: toolInfo?.tool, success: true,
|
|
1827
|
+
type: 'tool_result', agent, timestamp, tool: toolInfo?.tool, callId: toolId, success: true,
|
|
1761
1828
|
output: output.length > 500 ? output.slice(0, 497) + '...' : output,
|
|
1762
1829
|
});
|
|
1763
1830
|
}
|
|
@@ -12,6 +12,25 @@
|
|
|
12
12
|
* own scaffolding at parse time simply never reach here.
|
|
13
13
|
*/
|
|
14
14
|
export declare function isSyntheticUserMessage(raw: string | undefined): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Extract the invoked slash-command name from a raw `role=user` message, or
|
|
17
|
+
* undefined when the message carries no `<command-name>` wrapper. Used by
|
|
18
|
+
* `parseClaudeContent` to populate `SessionEvent.slashCommand`.
|
|
19
|
+
*
|
|
20
|
+
* Normalizes a leading `/` onto the result: current Claude Code wraps the
|
|
21
|
+
* name WITH the slash already (`/recap`), but the wrapper predates that and
|
|
22
|
+
* older transcripts carry the bare name (`recap`) — normalizing here means
|
|
23
|
+
* every consumer sees one consistent `/name` shape regardless of vintage.
|
|
24
|
+
*/
|
|
25
|
+
export declare function extractSlashCommandName(raw: string | undefined): string | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* Extract the invoked slash-command name from the `SlashCommand` tool's input
|
|
28
|
+
* — the MODEL invoking a command programmatically, not the user typing it.
|
|
29
|
+
* The tool's `command` field carries the full invocation text (name plus any
|
|
30
|
+
* args, e.g. `/code:commit fix the bug`); only the leading command token is
|
|
31
|
+
* the name, matching {@link extractSlashCommandName}'s shape.
|
|
32
|
+
*/
|
|
33
|
+
export declare function extractSlashCommandFromToolInput(toolInput: Record<string, any> | undefined): string | undefined;
|
|
15
34
|
export declare const HEADLESS_PLAN_MODE_PREFIX = "You are running in HEADLESS PLAN MODE.";
|
|
16
35
|
/** Strip framework noise from a raw session prompt, returning only meaningful user text. */
|
|
17
36
|
export declare function cleanSessionPrompt(raw: string): string;
|
|
@@ -63,6 +63,49 @@ export function isSyntheticUserMessage(raw) {
|
|
|
63
63
|
return false;
|
|
64
64
|
return SYNTHETIC_USER_MESSAGE_PATTERNS.some(pattern => pattern.test(raw));
|
|
65
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* The `<command-name>` wrapper Claude injects into a `role=user` message when
|
|
68
|
+
* a typed slash command is invoked, e.g.:
|
|
69
|
+
* <command-message>recap</command-message>
|
|
70
|
+
* <command-name>/recap</command-name>
|
|
71
|
+
* Captures the name INCLUDING its leading slash (`/recap`), matching how the
|
|
72
|
+
* `SlashCommand` tool's own `command` input is formatted (see
|
|
73
|
+
* `extractSlashCommandFromToolInput`) so both sources land on the same shape.
|
|
74
|
+
*/
|
|
75
|
+
const COMMAND_NAME_WRAPPER_RE = /<command-name>\s*([^<]*?)\s*<\/command-name>/i;
|
|
76
|
+
/**
|
|
77
|
+
* Extract the invoked slash-command name from a raw `role=user` message, or
|
|
78
|
+
* undefined when the message carries no `<command-name>` wrapper. Used by
|
|
79
|
+
* `parseClaudeContent` to populate `SessionEvent.slashCommand`.
|
|
80
|
+
*
|
|
81
|
+
* Normalizes a leading `/` onto the result: current Claude Code wraps the
|
|
82
|
+
* name WITH the slash already (`/recap`), but the wrapper predates that and
|
|
83
|
+
* older transcripts carry the bare name (`recap`) — normalizing here means
|
|
84
|
+
* every consumer sees one consistent `/name` shape regardless of vintage.
|
|
85
|
+
*/
|
|
86
|
+
export function extractSlashCommandName(raw) {
|
|
87
|
+
if (!raw)
|
|
88
|
+
return undefined;
|
|
89
|
+
const m = raw.match(COMMAND_NAME_WRAPPER_RE);
|
|
90
|
+
const name = m?.[1]?.trim();
|
|
91
|
+
if (!name)
|
|
92
|
+
return undefined;
|
|
93
|
+
return name.startsWith('/') ? name : `/${name}`;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Extract the invoked slash-command name from the `SlashCommand` tool's input
|
|
97
|
+
* — the MODEL invoking a command programmatically, not the user typing it.
|
|
98
|
+
* The tool's `command` field carries the full invocation text (name plus any
|
|
99
|
+
* args, e.g. `/code:commit fix the bug`); only the leading command token is
|
|
100
|
+
* the name, matching {@link extractSlashCommandName}'s shape.
|
|
101
|
+
*/
|
|
102
|
+
export function extractSlashCommandFromToolInput(toolInput) {
|
|
103
|
+
const command = toolInput?.command;
|
|
104
|
+
if (typeof command !== 'string')
|
|
105
|
+
return undefined;
|
|
106
|
+
const name = command.trim().split(/\s+/)[0];
|
|
107
|
+
return name || undefined;
|
|
108
|
+
}
|
|
66
109
|
// Prefix prepended to every Claude-in-plan-mode team spawn prompt.
|
|
67
110
|
// Ends at a blank line before the real user task.
|
|
68
111
|
export const HEADLESS_PLAN_MODE_PREFIX = 'You are running in HEADLESS PLAN MODE.';
|
|
@@ -1,4 +1,33 @@
|
|
|
1
|
+
import { type DeviceProfile } from '../devices/registry.js';
|
|
2
|
+
import { type RemoteAgentsJsonParseResult } from '../remote-agents-json.js';
|
|
1
3
|
import type { SessionMeta } from './types.js';
|
|
4
|
+
import { type ToolProgramCountEnvelope, type ToolSearchEnvelope } from './tool-index.js';
|
|
5
|
+
export declare const REMOTE_STDOUT_MAX_BYTES: number;
|
|
6
|
+
export declare const REMOTE_TOOL_AGGREGATE_MAX_BYTES: number;
|
|
7
|
+
export interface RemoteToolByteBudget {
|
|
8
|
+
remainingBytes: number;
|
|
9
|
+
exhausted: boolean;
|
|
10
|
+
}
|
|
11
|
+
/** Preserve UTF-8 code points when SSH splits them across stdout chunks. */
|
|
12
|
+
export declare class RemoteUtf8Accumulator {
|
|
13
|
+
private readonly decoder;
|
|
14
|
+
private value;
|
|
15
|
+
write(chunk: Buffer): void;
|
|
16
|
+
end(): string;
|
|
17
|
+
current(): string;
|
|
18
|
+
}
|
|
19
|
+
/** Claim received bytes against one fleet-query budget before retaining them. */
|
|
20
|
+
export declare function consumeRemoteToolByteBudget(budget: RemoteToolByteBudget, bytes: number): boolean;
|
|
21
|
+
/** Charge sanitized, machine-stamped evidence because redaction may expand it. */
|
|
22
|
+
export declare function consumeParsedRemoteToolSearchBudget(budget: RemoteToolByteBudget, envelope: ToolSearchEnvelope): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* The command run on each peer: answer for itself, as JSON, without recursing.
|
|
25
|
+
* `forwardedArgs` carry the caller's own query/filters (already including the
|
|
26
|
+
* leading `sessions` and a `--json`) so each peer returns a comparable slice.
|
|
27
|
+
* A Windows peer gets a PowerShell invocation (ssh lands in cmd.exe/PowerShell
|
|
28
|
+
* there, where `bash -lc` is not a command); every other OS keeps `bash -lc`.
|
|
29
|
+
*/
|
|
30
|
+
export declare function remoteListCommand(forwardedArgs: string[], os?: string): string;
|
|
2
31
|
/**
|
|
3
32
|
* Parse a peer's `sessions --json` stdout into `SessionMeta[]`, tagging each
|
|
4
33
|
* with `machine`. Defensive against version skew / partial output: non-JSON or
|
|
@@ -12,6 +41,17 @@ export declare function parseRemoteListPayload(stdout: string, machine: string,
|
|
|
12
41
|
items: SessionMeta[];
|
|
13
42
|
valid: boolean;
|
|
14
43
|
};
|
|
44
|
+
/** Run one remote `agents sessions … --json` and capture stdout. Resolves
|
|
45
|
+
* `{ code: null }` on spawn error or timeout (host treated as dead). */
|
|
46
|
+
export declare function sshCapture(target: string, remoteCmd: string, timeoutMs: number, aggregateBudget?: RemoteToolByteBudget, options?: {
|
|
47
|
+
multiplex?: boolean;
|
|
48
|
+
port?: number;
|
|
49
|
+
hostKeyOpts?: string[];
|
|
50
|
+
}): Promise<{
|
|
51
|
+
code: number | null;
|
|
52
|
+
stdout: string;
|
|
53
|
+
aggregateBudgetExceeded?: boolean;
|
|
54
|
+
}>;
|
|
15
55
|
export interface RemoteListResult {
|
|
16
56
|
sessions: SessionMeta[];
|
|
17
57
|
/** How many peer machines we attempted to reach (drives the empty-fleet tip). */
|
|
@@ -24,6 +64,8 @@ export interface RemoteListResult {
|
|
|
24
64
|
*/
|
|
25
65
|
unreachable: string[];
|
|
26
66
|
}
|
|
67
|
+
/** Keep browse and tool-search fan-out on the same automatic peer set. */
|
|
68
|
+
export declare function isAutomaticSessionPeer(d: DeviceProfile, self: string): boolean;
|
|
27
69
|
/**
|
|
28
70
|
* Gather listing sessions from other machines. With an explicit `hosts` list
|
|
29
71
|
* (from `--host`), fan out to exactly those. Otherwise sweep the registered,
|
|
@@ -32,6 +74,35 @@ export interface RemoteListResult {
|
|
|
32
74
|
* already `--json`) so every peer returns the same slice this machine asked for.
|
|
33
75
|
*/
|
|
34
76
|
export declare function gatherRemoteList(forwardedArgs: string[], hosts?: string[]): Promise<RemoteListResult>;
|
|
77
|
+
export interface RemoteToolSearchResult {
|
|
78
|
+
envelopes: Array<{
|
|
79
|
+
machine: string;
|
|
80
|
+
envelope: ToolSearchEnvelope;
|
|
81
|
+
}>;
|
|
82
|
+
deviceCount: number;
|
|
83
|
+
unreachable: string[];
|
|
84
|
+
truncated: string[];
|
|
85
|
+
}
|
|
86
|
+
export interface RemoteToolProgramCountResult {
|
|
87
|
+
envelopes: Array<{
|
|
88
|
+
machine: string;
|
|
89
|
+
envelope: ToolProgramCountEnvelope;
|
|
90
|
+
}>;
|
|
91
|
+
deviceCount: number;
|
|
92
|
+
unreachable: string[];
|
|
93
|
+
}
|
|
94
|
+
export declare function parseRemoteToolProgramCount(stdout: string, machine: string, expectedProgram: string): RemoteAgentsJsonParseResult<{
|
|
95
|
+
machine: string;
|
|
96
|
+
envelope: ToolProgramCountEnvelope;
|
|
97
|
+
}>;
|
|
98
|
+
export declare function gatherRemoteToolProgramCounts(forwardedArgs: string[], hosts: string[] | undefined, expectedProgram: string): Promise<RemoteToolProgramCountResult>;
|
|
99
|
+
export declare function parseRemoteToolSearch(stdout: string, machine: string, expectedClauses?: string[]): ToolSearchEnvelope | undefined;
|
|
100
|
+
/**
|
|
101
|
+
* Fleet sibling of {@link gatherRemoteList} for the versioned tool-search
|
|
102
|
+
* envelope. Each peer executes the same local-only query against its own
|
|
103
|
+
* SQLite index; only compact matches cross SSH.
|
|
104
|
+
*/
|
|
105
|
+
export declare function gatherRemoteToolSearch(forwardedArgs: string[], hosts?: string[], maxAggregateBytes?: number, expectedClauses?: string[]): Promise<RemoteToolSearchResult>;
|
|
35
106
|
/** Resolve a peer's SSH target (and OS) from the device registry by its
|
|
36
107
|
* normalized machine id — the same id the fan-out tags rows with. Returns
|
|
37
108
|
* undefined when no registered device with an address matches. */
|