@phnx-labs/agents-cli 1.21.3 → 1.22.1

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 (90) hide show
  1. package/CHANGELOG.md +136 -0
  2. package/README.md +32 -3
  3. package/dist/bin/agents +0 -0
  4. package/dist/commands/computer-actions.js +1 -0
  5. package/dist/commands/doctor.js +34 -2
  6. package/dist/commands/exec.d.ts +27 -0
  7. package/dist/commands/exec.js +123 -6
  8. package/dist/commands/models.js +36 -1
  9. package/dist/commands/projects.js +120 -62
  10. package/dist/commands/sessions-backfill.d.ts +32 -0
  11. package/dist/commands/sessions-backfill.js +186 -0
  12. package/dist/commands/sessions.d.ts +17 -1
  13. package/dist/commands/sessions.js +317 -18
  14. package/dist/commands/teams.js +1 -1
  15. package/dist/commands/worktree.d.ts +3 -3
  16. package/dist/commands/worktree.js +35 -4
  17. package/dist/lib/app-bundle-install.d.ts +17 -0
  18. package/dist/lib/app-bundle-install.js +94 -0
  19. package/dist/lib/daemon.d.ts +5 -1
  20. package/dist/lib/daemon.js +63 -14
  21. package/dist/lib/devices/doctor-findings.js +12 -4
  22. package/dist/lib/devices/doctor-overview-cache.d.ts +45 -0
  23. package/dist/lib/devices/doctor-overview-cache.js +168 -0
  24. package/dist/lib/devices/fleet.js +7 -2
  25. package/dist/lib/devices/resolve-target.d.ts +6 -0
  26. package/dist/lib/devices/resolve-target.js +9 -3
  27. package/dist/lib/devices/self-host.d.ts +9 -0
  28. package/dist/lib/devices/self-host.js +61 -0
  29. package/dist/lib/exec.js +39 -8
  30. package/dist/lib/hosts/dispatch.d.ts +12 -0
  31. package/dist/lib/hosts/dispatch.js +23 -6
  32. package/dist/lib/hosts/passthrough.js +8 -6
  33. package/dist/lib/hosts/reconnect.d.ts +38 -0
  34. package/dist/lib/hosts/reconnect.js +85 -4
  35. package/dist/lib/hosts/run-target.js +14 -2
  36. package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
  37. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  38. package/dist/lib/menubar/install-menubar.js +27 -23
  39. package/dist/lib/model-tiers.d.ts +54 -0
  40. package/dist/lib/model-tiers.js +229 -0
  41. package/dist/lib/models.d.ts +3 -0
  42. package/dist/lib/models.js +44 -7
  43. package/dist/lib/pricing/prices.json +16 -1
  44. package/dist/lib/project-focus.d.ts +42 -0
  45. package/dist/lib/project-focus.js +80 -0
  46. package/dist/lib/project-schedule.d.ts +75 -0
  47. package/dist/lib/project-schedule.js +110 -0
  48. package/dist/lib/project-status.d.ts +7 -0
  49. package/dist/lib/project-status.js +9 -0
  50. package/dist/lib/projects.d.ts +11 -2
  51. package/dist/lib/projects.js +57 -9
  52. package/dist/lib/redact.d.ts +2 -0
  53. package/dist/lib/redact.js +22 -0
  54. package/dist/lib/remote-agents-json.d.ts +2 -0
  55. package/dist/lib/remote-agents-json.js +3 -3
  56. package/dist/lib/rotate.d.ts +84 -1
  57. package/dist/lib/rotate.js +155 -5
  58. package/dist/lib/runner.d.ts +4 -2
  59. package/dist/lib/runner.js +13 -4
  60. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  61. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  62. package/dist/lib/secrets/install-helper.js +28 -31
  63. package/dist/lib/session/bash-command.js +60 -9
  64. package/dist/lib/session/db.d.ts +7 -1
  65. package/dist/lib/session/db.js +301 -32
  66. package/dist/lib/session/discover.d.ts +40 -7
  67. package/dist/lib/session/discover.js +144 -83
  68. package/dist/lib/session/parse.d.ts +8 -1
  69. package/dist/lib/session/parse.js +83 -32
  70. package/dist/lib/session/remote-list.d.ts +71 -0
  71. package/dist/lib/session/remote-list.js +410 -2
  72. package/dist/lib/session/shell-programs.d.ts +15 -0
  73. package/dist/lib/session/shell-programs.js +359 -0
  74. package/dist/lib/session/tool-calls.d.ts +88 -0
  75. package/dist/lib/session/tool-calls.js +612 -0
  76. package/dist/lib/session/tool-index.d.ts +100 -0
  77. package/dist/lib/session/tool-index.js +773 -0
  78. package/dist/lib/session/tool-store.d.ts +15 -0
  79. package/dist/lib/session/tool-store.js +198 -0
  80. package/dist/lib/session/types.d.ts +7 -0
  81. package/dist/lib/state.d.ts +10 -1
  82. package/dist/lib/state.js +11 -2
  83. package/dist/lib/teams/remoteWorktree.d.ts +3 -4
  84. package/dist/lib/teams/remoteWorktree.js +3 -4
  85. package/dist/lib/teams/worktree.d.ts +11 -1
  86. package/dist/lib/teams/worktree.js +42 -4
  87. package/dist/lib/types.d.ts +17 -0
  88. package/dist/lib/types.js +17 -0
  89. package/dist/lib/versions.js +69 -22
  90. 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
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
- const TERMINAL_ESCAPE_REGEX = /\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b\[[0-?]*[ -/]*[@-~]|\x1b[@-_]|[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x9f]/g;
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,6 +370,7 @@ 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,
@@ -460,6 +458,7 @@ export function parseClaudeContent(content) {
460
458
  const toolId = block.tool_use_id;
461
459
  const toolInfo = toolId ? toolUseMap.get(toolId) : undefined;
462
460
  const isError = block.is_error === true;
461
+ const structured = structuredToolResult(block);
463
462
  // Extract output text from tool result
464
463
  let output = '';
465
464
  if (typeof block.content === 'string') {
@@ -477,6 +476,11 @@ export function parseClaudeContent(content) {
477
476
  agent: 'claude',
478
477
  timestamp,
479
478
  tool: toolInfo?.tool,
479
+ callId: toolId,
480
+ outcome: 'error',
481
+ exitCode: structured.exitCode,
482
+ statusCode: structured.statusCode,
483
+ errorCode: structured.errorCode,
480
484
  content: output || 'Tool execution failed',
481
485
  });
482
486
  }
@@ -486,7 +490,12 @@ export function parseClaudeContent(content) {
486
490
  agent: 'claude',
487
491
  timestamp,
488
492
  tool: toolInfo?.tool,
493
+ callId: toolId,
489
494
  success: true,
495
+ outcome: 'ok',
496
+ exitCode: structured.exitCode,
497
+ statusCode: structured.statusCode,
498
+ errorCode: structured.errorCode,
490
499
  output: output.length > 500 ? output.slice(0, 497) + '...' : output,
491
500
  });
492
501
  }
@@ -656,6 +665,7 @@ export function parseCodexContent(content) {
656
665
  agent: 'codex',
657
666
  timestamp,
658
667
  tool: name,
668
+ callId,
659
669
  args,
660
670
  command: name === 'exec_command' ? (args.command || args.cmd) : undefined,
661
671
  path: args.file_path || args.path || undefined,
@@ -664,14 +674,19 @@ export function parseCodexContent(content) {
664
674
  else if (ptype === 'function_call_output') {
665
675
  const callId = payload.call_id || payload.id;
666
676
  const callInfo = callId ? callMap.get(callId) : undefined;
667
- const output = String(payload.output || '');
677
+ const result = structuredToolResult(payload.output);
668
678
  events.push({
669
679
  type: 'tool_result',
670
680
  agent: 'codex',
671
681
  timestamp,
672
682
  tool: callInfo?.name,
673
- success: true,
674
- output: output.length > 500 ? output.slice(0, 497) + '...' : output,
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,
675
690
  });
676
691
  if (callId)
677
692
  callMap.delete(callId);
@@ -698,6 +713,7 @@ export function parseCodexContent(content) {
698
713
  agent: 'codex',
699
714
  timestamp,
700
715
  tool,
716
+ callId,
701
717
  args,
702
718
  path: patchPath,
703
719
  });
@@ -713,14 +729,19 @@ export function parseCodexContent(content) {
713
729
  else if (ptype === 'custom_tool_call_output') {
714
730
  const callId = payload.call_id || payload.id;
715
731
  const callInfo = callId ? callMap.get(callId) : undefined;
716
- const output = String(payload.output || '');
732
+ const result = structuredToolResult(payload.output);
717
733
  events.push({
718
734
  type: 'tool_result',
719
735
  agent: 'codex',
720
736
  timestamp,
721
737
  tool: callInfo?.name,
722
- success: true,
723
- output: output.length > 500 ? output.slice(0, 497) + '...' : output,
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,
724
745
  });
725
746
  if (callId)
726
747
  callMap.delete(callId);
@@ -818,6 +839,7 @@ export function parseGemini(filePath) {
818
839
  agent: 'gemini',
819
840
  timestamp: tc.timestamp || timestamp,
820
841
  tool: toolName,
842
+ callId: typeof tc.id === 'string' ? tc.id : undefined,
821
843
  args,
822
844
  command: ['run_shell_command', 'shell', 'bash'].includes(toolName) ? args.command : undefined,
823
845
  path: args.file_path || args.path || undefined,
@@ -841,6 +863,7 @@ export function parseGemini(filePath) {
841
863
  agent: 'gemini',
842
864
  timestamp: tc.timestamp || timestamp,
843
865
  tool: toolName,
866
+ callId: typeof tc.id === 'string' ? tc.id : undefined,
844
867
  success: tc.status === 'success',
845
868
  output: output.length > 500 ? output.slice(0, 497) + '...' : output,
846
869
  });
@@ -1060,6 +1083,7 @@ export function parseAntigravity(dbPath) {
1060
1083
  agent: 'antigravity',
1061
1084
  timestamp,
1062
1085
  tool: norm,
1086
+ callId: call.id,
1063
1087
  args: a,
1064
1088
  command: norm === 'Bash' ? a.CommandLine : undefined,
1065
1089
  // Antigravity uses PascalCase arg keys; probe the known path-bearing ones.
@@ -1191,6 +1215,7 @@ export function parseGrok(filePath) {
1191
1215
  agent: 'grok',
1192
1216
  timestamp,
1193
1217
  tool: toolName,
1218
+ callId: typeof call?.id === 'string' ? call.id : undefined,
1194
1219
  args,
1195
1220
  path: args.path || args.file_path || undefined,
1196
1221
  command: typeof args.command === 'string' ? args.command : undefined,
@@ -1206,13 +1231,17 @@ export function parseGrok(filePath) {
1206
1231
  const callId = typeof msg.tool_call_id === 'string' ? msg.tool_call_id : undefined;
1207
1232
  const toolName = (callId && toolCallMap.get(callId)) || 'unknown';
1208
1233
  const output = typeof msg.content === 'string' ? msg.content : extractText(msg.content);
1209
- const isError = typeof output === 'string' && output.startsWith('Error:');
1234
+ const structuredError = msg.is_error === true;
1235
+ const structuredSuccess = msg.is_error === false;
1236
+ const displayAsError = structuredError || (typeof output === 'string' && output.startsWith('Error:'));
1210
1237
  events.push({
1211
- type: isError ? 'error' : 'tool_result',
1238
+ type: displayAsError ? 'error' : 'tool_result',
1212
1239
  agent: 'grok',
1213
1240
  timestamp,
1214
1241
  tool: toolName,
1215
- success: !isError,
1242
+ callId,
1243
+ success: structuredError ? false : structuredSuccess ? true : undefined,
1244
+ outcome: structuredError ? 'error' : structuredSuccess ? 'ok' : 'unknown',
1216
1245
  output: output.length > 500 ? output.slice(0, 497) + '...' : output,
1217
1246
  });
1218
1247
  if (callId)
@@ -1311,11 +1340,13 @@ export function parseOpenCode(filePath) {
1311
1340
  const state = partData.state || {};
1312
1341
  const input = state.input || {};
1313
1342
  const output = state.output || '';
1343
+ const callId = typeof partData.callID === 'string' ? partData.callID : undefined;
1314
1344
  events.push({
1315
1345
  type: 'tool_use',
1316
1346
  agent: 'opencode',
1317
1347
  timestamp,
1318
1348
  tool: toolName,
1349
+ callId,
1319
1350
  args: input,
1320
1351
  command: toolName === 'shell' ? input.command : undefined,
1321
1352
  path: input.filePath || input.path || undefined,
@@ -1327,6 +1358,7 @@ export function parseOpenCode(filePath) {
1327
1358
  agent: 'opencode',
1328
1359
  timestamp,
1329
1360
  tool: toolName,
1361
+ callId,
1330
1362
  success: state.status === 'completed',
1331
1363
  output: outputStr.length > 500 ? outputStr.slice(0, 497) + '...' : outputStr,
1332
1364
  });
@@ -1370,8 +1402,10 @@ export function parseRush(filePath) {
1370
1402
  continue;
1371
1403
  }
1372
1404
  const type = raw.type;
1373
- const timestamp = raw.created_at || new Date().toISOString();
1374
- 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
+ : {};
1375
1409
  if (type === 'message') {
1376
1410
  const text = typeof content.text === 'string' ? content.text.trim() : '';
1377
1411
  if (!text)
@@ -1394,9 +1428,11 @@ export function parseRush(filePath) {
1394
1428
  });
1395
1429
  }
1396
1430
  else if (type === 'tool_call') {
1397
- const toolName = raw.name || 'unknown';
1398
- const args = content.input || {};
1399
- const callId = raw.tool_call_id;
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;
1400
1436
  if (callId)
1401
1437
  toolCallMap.set(callId, { tool: toolName, args });
1402
1438
  events.push({
@@ -1404,13 +1440,14 @@ export function parseRush(filePath) {
1404
1440
  agent: 'rush',
1405
1441
  timestamp,
1406
1442
  tool: toolName,
1443
+ callId,
1407
1444
  args,
1408
1445
  path: args.file_path || args.path || undefined,
1409
1446
  command: (toolName === 'Bash' || toolName === 'shell') ? args.command : undefined,
1410
1447
  });
1411
1448
  }
1412
1449
  else if (type === 'tool_result') {
1413
- const callId = raw.tool_call_id;
1450
+ const callId = typeof raw.tool_call_id === 'string' ? raw.tool_call_id : undefined;
1414
1451
  const info = callId ? toolCallMap.get(callId) : undefined;
1415
1452
  const output = content.output;
1416
1453
  let outputStr = '';
@@ -1430,7 +1467,8 @@ export function parseRush(filePath) {
1430
1467
  type: success ? 'tool_result' : 'error',
1431
1468
  agent: 'rush',
1432
1469
  timestamp,
1433
- tool: info?.tool ?? raw.name,
1470
+ tool: info?.tool ?? (typeof raw.name === 'string' ? raw.name : 'unknown'),
1471
+ callId,
1434
1472
  success,
1435
1473
  output: outputStr.length > 500 ? outputStr.slice(0, 497) + '...' : outputStr,
1436
1474
  });
@@ -1591,9 +1629,11 @@ export function parseKimi(filePath) {
1591
1629
  }
1592
1630
  else if (eventType === 'tool.call') {
1593
1631
  const fn = event.function || {};
1594
- const toolName = typeof event.name === 'string' ? event.name : (fn.name || 'unknown');
1632
+ const toolName = typeof event.name === 'string'
1633
+ ? event.name
1634
+ : typeof fn.name === 'string' ? fn.name : 'unknown';
1595
1635
  let args = {};
1596
- if (event.args && typeof event.args === 'object') {
1636
+ if (event.args && typeof event.args === 'object' && !Array.isArray(event.args)) {
1597
1637
  args = event.args;
1598
1638
  }
1599
1639
  else if (typeof fn.arguments === 'string') {
@@ -1604,10 +1644,11 @@ export function parseKimi(filePath) {
1604
1644
  args = { _raw: fn.arguments };
1605
1645
  }
1606
1646
  }
1607
- else if (fn.arguments && typeof fn.arguments === 'object') {
1647
+ else if (fn.arguments && typeof fn.arguments === 'object' && !Array.isArray(fn.arguments)) {
1608
1648
  args = fn.arguments;
1609
1649
  }
1610
- const callId = event.toolCallId || event.uuid;
1650
+ const rawCallId = event.toolCallId || event.uuid;
1651
+ const callId = typeof rawCallId === 'string' ? rawCallId : undefined;
1611
1652
  if (callId) {
1612
1653
  toolCallMap.set(callId, toolName);
1613
1654
  }
@@ -1616,23 +1657,29 @@ export function parseKimi(filePath) {
1616
1657
  agent: 'kimi',
1617
1658
  timestamp,
1618
1659
  tool: toolName,
1660
+ callId,
1619
1661
  args,
1620
1662
  path: args.path || args.file_path || undefined,
1621
1663
  command: toolName === 'Bash' ? args.command : undefined,
1622
1664
  });
1623
1665
  }
1624
1666
  else if (eventType === 'tool.result') {
1625
- const callId = event.toolCallId || event.parentUuid;
1667
+ const rawCallId = event.toolCallId || event.parentUuid;
1668
+ const callId = typeof rawCallId === 'string' ? rawCallId : undefined;
1626
1669
  const toolName = (callId && toolCallMap.get(callId)) || 'unknown';
1627
1670
  const result = event.result || {};
1628
1671
  const output = typeof result.output === 'string' ? result.output : '';
1629
- const isError = result.isError === true || (output && output.startsWith('Error:'));
1672
+ const structuredError = result.isError === true;
1673
+ const structuredSuccess = result.isError === false;
1674
+ const displayAsError = structuredError || (output && output.startsWith('Error:'));
1630
1675
  events.push({
1631
- type: isError ? 'error' : 'tool_result',
1676
+ type: displayAsError ? 'error' : 'tool_result',
1632
1677
  agent: 'kimi',
1633
1678
  timestamp,
1634
1679
  tool: toolName,
1635
- success: !isError,
1680
+ callId,
1681
+ success: structuredError ? false : structuredSuccess ? true : undefined,
1682
+ outcome: structuredError ? 'error' : structuredSuccess ? 'ok' : 'unknown',
1636
1683
  output: output.length > 500 ? output.slice(0, 497) + '...' : output,
1637
1684
  });
1638
1685
  if (callId) {
@@ -1754,6 +1801,7 @@ function parseAnthropicMessageJsonl(filePath, agent) {
1754
1801
  agent,
1755
1802
  timestamp,
1756
1803
  tool: toolName,
1804
+ callId: typeof block.id === 'string' ? block.id : undefined,
1757
1805
  args: toolInput,
1758
1806
  path: toolInput.file_path || toolInput.path || undefined,
1759
1807
  command: (toolName === 'Bash' || toolName === 'Execute' || toolName === 'Shell') ? toolInput.command : undefined,
@@ -1769,11 +1817,14 @@ function parseAnthropicMessageJsonl(filePath, agent) {
1769
1817
  ? block.content.filter((c) => c.type === 'text').map((c) => c.text || '').join('\n')
1770
1818
  : '';
1771
1819
  if (isError) {
1772
- events.push({ type: 'error', agent, timestamp, tool: toolInfo?.tool, content: output || 'Tool execution failed' });
1820
+ events.push({
1821
+ type: 'error', agent, timestamp, tool: toolInfo?.tool,
1822
+ callId: toolId, outcome: 'error', content: output || 'Tool execution failed',
1823
+ });
1773
1824
  }
1774
1825
  else {
1775
1826
  events.push({
1776
- type: 'tool_result', agent, timestamp, tool: toolInfo?.tool, success: true,
1827
+ type: 'tool_result', agent, timestamp, tool: toolInfo?.tool, callId: toolId, success: true,
1777
1828
  output: output.length > 500 ? output.slice(0, 497) + '...' : output,
1778
1829
  });
1779
1830
  }
@@ -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. */