@phnx-labs/agents-cli 1.22.6 → 1.22.8
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 +131 -1
- package/README.md +7 -0
- package/dist/bin/agents +0 -0
- package/dist/commands/browser.js +61 -0
- package/dist/commands/exec.js +83 -16
- package/dist/commands/feed.d.ts +2 -1
- package/dist/commands/feed.js +47 -20
- package/dist/commands/focus.js +22 -1
- package/dist/commands/harness.d.ts +0 -1
- package/dist/commands/harness.js +60 -4
- package/dist/commands/models.js +2 -2
- package/dist/commands/monitors.js +2 -2
- package/dist/commands/projects.js +122 -107
- package/dist/commands/routines.js +2 -2
- package/dist/commands/run-account-picker.js +2 -0
- package/dist/commands/secrets.js +1 -1
- package/dist/commands/sessions-backfill.d.ts +33 -0
- package/dist/commands/sessions-backfill.js +83 -1
- package/dist/commands/sessions-stats.d.ts +36 -0
- package/dist/commands/sessions-stats.js +263 -0
- package/dist/commands/sessions.d.ts +1 -1
- package/dist/commands/sessions.js +21 -1
- package/dist/commands/snapshot.d.ts +11 -0
- package/dist/commands/snapshot.js +107 -0
- package/dist/commands/teams.js +2 -1
- package/dist/commands/view.d.ts +9 -1
- package/dist/commands/view.js +63 -12
- package/dist/index.js +4 -2
- package/dist/lib/activity.js +2 -2
- package/dist/lib/agents.js +68 -11
- package/dist/lib/analytics/recipes.js +11 -5
- package/dist/lib/browser/ipc.js +2 -0
- package/dist/lib/browser/profiles.d.ts +15 -7
- package/dist/lib/browser/profiles.js +53 -12
- package/dist/lib/browser/remote-control.d.ts +35 -0
- package/dist/lib/browser/remote-control.js +48 -0
- package/dist/lib/browser/service.d.ts +19 -0
- package/dist/lib/browser/service.js +19 -1
- package/dist/lib/browser/types.d.ts +14 -2
- package/dist/lib/byok-usage.d.ts +38 -0
- package/dist/lib/byok-usage.js +117 -0
- package/dist/lib/capabilities.js +1 -1
- package/dist/lib/device-config.js +8 -0
- package/dist/lib/exec.d.ts +20 -0
- package/dist/lib/exec.js +73 -8
- package/dist/lib/feed-outcome.d.ts +1 -0
- package/dist/lib/feed-outcome.js +2 -0
- package/dist/lib/feed-post.js +1 -1
- package/dist/lib/feed-ranking.d.ts +1 -0
- package/dist/lib/feed-ranking.js +4 -0
- package/dist/lib/feed.d.ts +4 -1
- package/dist/lib/feed.js +25 -0
- package/dist/lib/hosts/passthrough.d.ts +10 -1
- package/dist/lib/hosts/passthrough.js +23 -3
- package/dist/lib/hosts/remote-cmd.js +1 -0
- package/dist/lib/mcp.js +6 -1
- 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.js +4 -1
- package/dist/lib/models.js +63 -0
- package/dist/lib/placement.d.ts +82 -0
- package/dist/lib/placement.js +188 -0
- package/dist/lib/profiles.d.ts +73 -10
- package/dist/lib/profiles.js +133 -15
- package/dist/lib/project-focus.d.ts +9 -0
- package/dist/lib/project-focus.js +23 -0
- package/dist/lib/project-key.d.ts +1 -1
- package/dist/lib/project-key.js +1 -1
- package/dist/lib/project-probe.d.ts +18 -0
- package/dist/lib/project-probe.js +46 -0
- package/dist/lib/project-status.d.ts +56 -0
- package/dist/lib/project-status.js +125 -18
- package/dist/lib/resources/mcp.js +3 -0
- package/dist/lib/resources/types.d.ts +1 -1
- package/dist/lib/rotate.d.ts +19 -4
- package/dist/lib/rotate.js +24 -1
- package/dist/lib/routines.d.ts +2 -0
- package/dist/lib/runner.d.ts +3 -0
- package/dist/lib/runner.js +90 -7
- 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/secrets/audit.js +1 -1
- package/dist/lib/secrets/index.d.ts +1 -0
- package/dist/lib/secrets/index.js +29 -15
- package/dist/lib/secrets/remote.d.ts +1 -1
- package/dist/lib/secrets/remote.js +1 -1
- package/dist/lib/session/active.d.ts +2 -0
- package/dist/lib/session/bash-command.d.ts +2 -3
- package/dist/lib/session/db.d.ts +92 -1
- package/dist/lib/session/db.js +230 -1
- package/dist/lib/session/digest.d.ts +1 -1
- package/dist/lib/session/digest.js +1 -1
- package/dist/lib/share/publish.js +24 -0
- package/dist/lib/snapshot.d.ts +103 -0
- package/dist/lib/snapshot.js +99 -0
- package/dist/lib/startup/command-registry.d.ts +1 -1
- package/dist/lib/startup/command-registry.js +2 -2
- package/dist/lib/subagents-registry.js +3 -0
- package/dist/lib/types.d.ts +11 -2
- package/dist/lib/usage.d.ts +5 -0
- package/dist/lib/usage.js +3 -3
- package/package.json +1 -1
- package/dist/commands/activity.d.ts +0 -87
- package/dist/commands/activity.js +0 -346
package/dist/lib/activity.js
CHANGED
|
@@ -300,7 +300,7 @@ export function readActivityAsEventRecords(opts = {}) {
|
|
|
300
300
|
return readRecentActivity(opts).map(activityEventToRecord);
|
|
301
301
|
}
|
|
302
302
|
// ---------------------------------------------------------------------------
|
|
303
|
-
// Rendering (shared by
|
|
303
|
+
// Rendering (shared by the feed activity lane and `agents feed --filter updates`)
|
|
304
304
|
// ---------------------------------------------------------------------------
|
|
305
305
|
/** Glyph + color + human label per event, so the lane reads at a glance. */
|
|
306
306
|
export const EVENT_STYLE = {
|
|
@@ -669,7 +669,7 @@ export function formatActivityGroupMeta(group, opts = {}) {
|
|
|
669
669
|
* a logging hiccup never blocks a tool call.
|
|
670
670
|
*/
|
|
671
671
|
export const ACTIVITY_LOG_HOOK_SCRIPT = String.raw `#!/usr/bin/env python3
|
|
672
|
-
"""Append agent-activity events for 'agents feed'
|
|
672
|
+
"""Append agent-activity events for 'agents feed'.
|
|
673
673
|
|
|
674
674
|
Bound to PreToolUse (ExitPlanMode, Task) and PostToolUse (Bash, Write, Edit,
|
|
675
675
|
MultiEdit, TodoWrite, update_plan, TaskUpdate, todo_write, TaskCreate). One
|
package/dist/lib/agents.js
CHANGED
|
@@ -215,7 +215,7 @@ export const AGENTS = {
|
|
|
215
215
|
// Claude Code has no headless Anthropic-hosted dispatch CLI (only
|
|
216
216
|
// --remote-control, which bridges a *local* session). Its cloud is Rush.
|
|
217
217
|
cloudProvider: 'rush',
|
|
218
|
-
capabilities: { hooks: true, mcp: true, mcpHttp: true, mcpHeaders: true, allowlist: true, skills: true, commands: true, plugins: true, subagents: true, rules: { file: 'CLAUDE.md' }, workflows: true, memory: true, modes: ['plan', 'edit', 'auto', 'skip'], rulesImports: true },
|
|
218
|
+
capabilities: { hooks: true, mcp: true, mcpHttp: true, mcpHeaders: true, allowlist: true, skills: true, commands: true, plugins: true, subagents: true, rules: { file: 'CLAUDE.md' }, workflows: true, memory: true, modes: ['plan', 'edit', 'auto', 'skip'], rulesImports: true, interactiveRepl: true },
|
|
219
219
|
},
|
|
220
220
|
// codex hooks: gated to >= 0.116.0 (introduced [features] codex_hooks flag).
|
|
221
221
|
codex: {
|
|
@@ -237,7 +237,7 @@ export const AGENTS = {
|
|
|
237
237
|
cloudProvider: 'codex',
|
|
238
238
|
// Subagents: multi-agent plumbing since 0.117.0; custom agents as
|
|
239
239
|
// ~/.codex/agents/*.toml (name, description, developer_instructions).
|
|
240
|
-
capabilities: { hooks: { since: '0.116.0' }, mcp: true, mcpHttp: true, mcpHeaders: false, allowlist: { since: '0.138.0' }, skills: true, commands: { until: '0.117.0' }, plugins: { since: '0.128.0' }, subagents: { since: '0.117.0' }, rules: { file: 'AGENTS.md' }, workflows: false, memory: true, modes: ['plan', 'edit', 'skip'] },
|
|
240
|
+
capabilities: { hooks: { since: '0.116.0' }, mcp: true, mcpHttp: true, mcpHeaders: false, allowlist: { since: '0.138.0' }, skills: true, commands: { until: '0.117.0' }, plugins: { since: '0.128.0' }, subagents: { since: '0.117.0' }, rules: { file: 'AGENTS.md' }, workflows: false, memory: true, modes: ['plan', 'edit', 'skip'], interactiveRepl: true },
|
|
241
241
|
},
|
|
242
242
|
gemini: {
|
|
243
243
|
id: 'gemini',
|
|
@@ -269,7 +269,7 @@ export const AGENTS = {
|
|
|
269
269
|
},
|
|
270
270
|
// gemini hooks: shipped in v0.26.0 (Jan 2026); older binaries silently ignore the `hooks` key.
|
|
271
271
|
// extensions: gemini-extension.json bundles shipped in v0.8.0; custom subagents in v0.36.0.
|
|
272
|
-
capabilities: { hooks: { since: '0.26.0' }, mcp: true, mcpHttp: true, mcpHeaders: false, allowlist: true, skills: true, commands: true, plugins: { since: '0.8.0' }, subagents: { since: '0.36.0' }, rules: { file: 'GEMINI.md' }, workflows: false, memory: false, modes: ['plan', 'edit', 'skip'], rulesImports: true },
|
|
272
|
+
capabilities: { hooks: { since: '0.26.0' }, mcp: true, mcpHttp: true, mcpHeaders: false, allowlist: true, skills: true, commands: true, plugins: { since: '0.8.0' }, subagents: { since: '0.36.0' }, rules: { file: 'GEMINI.md' }, workflows: false, memory: false, modes: ['plan', 'edit', 'skip'], rulesImports: true, interactiveRepl: false },
|
|
273
273
|
},
|
|
274
274
|
cursor: {
|
|
275
275
|
id: 'cursor',
|
|
@@ -302,7 +302,9 @@ export const AGENTS = {
|
|
|
302
302
|
// + warns for pre-2.4 installs); the direct `subagents add --agents cursor` path
|
|
303
303
|
// writes unconditionally, same as the other since-gated agents.
|
|
304
304
|
// See transformSubagentForCursor / https://cursor.com/docs/subagents.
|
|
305
|
-
|
|
305
|
+
// interactiveRepl: false — cursor-agent exits immediately with no argv. It requires a
|
|
306
|
+
// prompt to do anything useful; a bare invocation is not a REPL (RUSH-2185, EXEC-23a).
|
|
307
|
+
capabilities: { hooks: true, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: true, skills: true, commands: true, plugins: true, subagents: { since: '2026.1.22' }, rules: { file: '.cursorrules' }, workflows: false, memory: false, modes: ['edit', 'skip'], interactiveRepl: false }, // allowlist: ~/.cursor/cli-config.json
|
|
306
308
|
},
|
|
307
309
|
opencode: {
|
|
308
310
|
id: 'opencode',
|
|
@@ -324,7 +326,45 @@ export const AGENTS = {
|
|
|
324
326
|
format: 'markdown',
|
|
325
327
|
variableSyntax: '$ARGUMENTS',
|
|
326
328
|
supportsHooks: true,
|
|
327
|
-
capabilities: { hooks: { since: '0.3.130' }, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: { since: '1.1.1' }, skills: true, commands: true, plugins: true, subagents: true, rules: { file: 'AGENTS.md' }, workflows: false, memory: false, modes: ['plan', 'edit'] },
|
|
329
|
+
capabilities: { hooks: { since: '0.3.130' }, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: { since: '1.1.1' }, skills: true, commands: true, plugins: true, subagents: true, rules: { file: 'AGENTS.md' }, workflows: false, memory: false, modes: ['plan', 'edit'], interactiveRepl: true },
|
|
330
|
+
},
|
|
331
|
+
// Oh My Pi (`omp`, omp.sh) — a Bun-based, terminal-first coding agent that runs
|
|
332
|
+
// against many model providers (OpenRouter, OpenAI, Anthropic, xAI, DeepSeek,
|
|
333
|
+
// Ollama, LM Studio, …). It is Claude-compatible: it natively discovers
|
|
334
|
+
// `.claude/commands`, `.mcp.json`, and Claude-shaped subagents, and keeps its
|
|
335
|
+
// own native resources under `~/.omp/agent/` (config dir reported by
|
|
336
|
+
// `omp config path`). configDir points AT the agent dir (not `~/.omp`) so the
|
|
337
|
+
// rules file lands at `~/.omp/agent/AGENTS.md`, the user context file omp reads.
|
|
338
|
+
pi: {
|
|
339
|
+
id: 'pi',
|
|
340
|
+
name: 'Pi',
|
|
341
|
+
color: 'magenta',
|
|
342
|
+
cliCommand: 'omp',
|
|
343
|
+
npmPackage: '@oh-my-pi/pi-coding-agent',
|
|
344
|
+
configDir: path.join(HOME, '.omp', 'agent'),
|
|
345
|
+
commandsDir: path.join(HOME, '.omp', 'agent', 'commands'),
|
|
346
|
+
commandsSubdir: 'commands',
|
|
347
|
+
skillsDir: path.join(HOME, '.omp', 'agent', 'skills'),
|
|
348
|
+
hooksDir: 'hooks',
|
|
349
|
+
instructionsFile: 'AGENTS.md',
|
|
350
|
+
format: 'markdown',
|
|
351
|
+
variableSyntax: '$ARGUMENTS',
|
|
352
|
+
// omp hooks are per-tool JS/TS extension modules discovered from
|
|
353
|
+
// `~/.omp/agent/hooks/{pre,post}/<tool>.<ext>` (loaded as HookFactory code),
|
|
354
|
+
// NOT the event->shell-command registrations agents-cli's hook sync writes.
|
|
355
|
+
// The two models don't map, so hooks stay off (capabilities.hooks:false).
|
|
356
|
+
supportsHooks: false,
|
|
357
|
+
// MCP: omp reads `.mcp.json` with the Claude `{ "mcpServers": {...} }` schema
|
|
358
|
+
// at user scope (`~/.omp/agent/.mcp.json`) and project scope (`<root>/.mcp.json`),
|
|
359
|
+
// stdio + http + sse with headers. skills (`~/.omp/agent/skills/<name>/SKILL.md`),
|
|
360
|
+
// commands (`~/.omp/agent/commands/*.md`), and subagents (`~/.omp/agent/agents/*.md`,
|
|
361
|
+
// Claude-shaped) are all native. allowlist is OFF: omp gates approval per-TOOL
|
|
362
|
+
// only (`tools.approval` record: allow|prompt|deny) with no command/path/domain
|
|
363
|
+
// patterns, so agents-cli's granular permission format has nothing to map to.
|
|
364
|
+
// plugins are npm packages / TS modules, not the Claude marketplace manifest.
|
|
365
|
+
// interactiveRepl: true — bare `omp` runs the TUI; `omp -p` is the one-shot
|
|
366
|
+
// form that answers a prompt and exits (upstream README, "Four entry points").
|
|
367
|
+
capabilities: { hooks: false, mcp: true, mcpHttp: true, mcpHeaders: true, allowlist: false, skills: true, commands: true, plugins: false, subagents: true, rules: { file: 'AGENTS.md' }, workflows: false, memory: false, modes: ['plan', 'edit', 'skip'], interactiveRepl: true },
|
|
328
368
|
},
|
|
329
369
|
openclaw: {
|
|
330
370
|
id: 'openclaw',
|
|
@@ -348,7 +388,7 @@ export const AGENTS = {
|
|
|
348
388
|
// OpenClaw is self-updating (no pinned since), so `true` is correct.
|
|
349
389
|
// Workflows sync as Lobster `.lobster` files under `.openclaw/workflows/`;
|
|
350
390
|
// the Lobster tool runs them by receiving the file path as `pipeline`.
|
|
351
|
-
capabilities: { hooks: true, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: true, skills: true, commands: false, plugins: true, subagents: true, rules: { file: 'workspace/AGENTS.md' }, workflows: true, memory: true, modes: ['plan', 'edit', 'skip'] },
|
|
391
|
+
capabilities: { hooks: true, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: true, skills: true, commands: false, plugins: true, subagents: true, rules: { file: 'workspace/AGENTS.md' }, workflows: true, memory: true, modes: ['plan', 'edit', 'skip'], interactiveRepl: true },
|
|
352
392
|
},
|
|
353
393
|
copilot: {
|
|
354
394
|
id: 'copilot',
|
|
@@ -373,7 +413,9 @@ export const AGENTS = {
|
|
|
373
413
|
format: 'markdown',
|
|
374
414
|
variableSyntax: '$ARGUMENTS',
|
|
375
415
|
supportsHooks: true,
|
|
376
|
-
|
|
416
|
+
// interactiveRepl: false — copilot requires a prompt for meaningful work; bare invocation
|
|
417
|
+
// opens a welcome screen but not a persistent coding REPL suitable for agents focus.
|
|
418
|
+
capabilities: { hooks: true, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: true, skills: true, commands: true, plugins: true, subagents: { since: '0.0.353' }, rules: { file: 'AGENTS.md' }, workflows: false, memory: false, modes: ['plan', 'edit', 'auto', 'skip'], interactiveRepl: false },
|
|
377
419
|
},
|
|
378
420
|
amp: {
|
|
379
421
|
id: 'amp',
|
|
@@ -390,7 +432,8 @@ export const AGENTS = {
|
|
|
390
432
|
format: 'markdown',
|
|
391
433
|
variableSyntax: '$ARGUMENTS',
|
|
392
434
|
supportsHooks: false,
|
|
393
|
-
|
|
435
|
+
// interactiveRepl: false — amp requires a prompt; bare invocation exits immediately.
|
|
436
|
+
capabilities: { hooks: false, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: false, skills: true, commands: true, plugins: false, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, memory: false, modes: ['plan', 'edit'], interactiveRepl: false },
|
|
394
437
|
},
|
|
395
438
|
kiro: {
|
|
396
439
|
id: 'kiro',
|
|
@@ -413,7 +456,9 @@ export const AGENTS = {
|
|
|
413
456
|
format: 'markdown',
|
|
414
457
|
variableSyntax: '$ARGUMENTS',
|
|
415
458
|
supportsHooks: true,
|
|
416
|
-
|
|
459
|
+
// interactiveRepl: false — kiro-cli requires a prompt for coding sessions; bare
|
|
460
|
+
// invocation does not open a persistent REPL.
|
|
461
|
+
capabilities: { hooks: { since: '0.10.0' }, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: { since: '2.8.0' }, skills: true, commands: true, plugins: false, subagents: { since: '1.23.0' }, rules: { file: 'AGENTS.md' }, workflows: false, memory: false, modes: ['edit'], interactiveRepl: false },
|
|
417
462
|
},
|
|
418
463
|
goose: {
|
|
419
464
|
id: 'goose',
|
|
@@ -445,7 +490,7 @@ export const AGENTS = {
|
|
|
445
490
|
// under `slash_commands: [{ command, recipe_path }]` (see goose-commands.ts).
|
|
446
491
|
// Subagents: recipe YAML named agents under ~/.config/goose/agents/<name>.yaml
|
|
447
492
|
// (goose auto-discovers and delegates to them by name in autonomous mode).
|
|
448
|
-
capabilities: { hooks: { since: '1.34.0' }, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: true, skills: { since: '1.25.0' }, commands: true, plugins: true, subagents: true, rules: { file: 'AGENTS.md' }, workflows: true, memory: false, modes: ['edit'] },
|
|
493
|
+
capabilities: { hooks: { since: '1.34.0' }, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: true, skills: { since: '1.25.0' }, commands: true, plugins: true, subagents: true, rules: { file: 'AGENTS.md' }, workflows: true, memory: false, modes: ['edit'], interactiveRepl: true },
|
|
449
494
|
},
|
|
450
495
|
// Google Antigravity CLI (`agy`) — official replacement for Gemini CLI as of IO 2026.
|
|
451
496
|
// configDir nests inside `~/.gemini/` since agy shares the parent dir with the Gemini
|
|
@@ -474,7 +519,7 @@ export const AGENTS = {
|
|
|
474
519
|
variableSyntax: '{{args}}',
|
|
475
520
|
supportsHooks: true,
|
|
476
521
|
cloudProvider: 'antigravity',
|
|
477
|
-
capabilities: { hooks: true, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: true, skills: true, commands: true, plugins: true, subagents: { since: '1.0.16' }, rules: { file: 'AGENTS.md' }, workflows: { since: '1.0.6' }, memory: false, modes: ['edit', 'skip'], rulesImports: false }, // workflows: markdown files in the shared, HOME-global ~/.gemini/config/global_workflows/ (agy scans it at startup; not version-isolated — see workflows.ts), invoked as /<name> slash commands
|
|
522
|
+
capabilities: { hooks: true, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: true, skills: true, commands: true, plugins: true, subagents: { since: '1.0.16' }, rules: { file: 'AGENTS.md' }, workflows: { since: '1.0.6' }, memory: false, modes: ['edit', 'skip'], rulesImports: false, interactiveRepl: true }, // workflows: markdown files in the shared, HOME-global ~/.gemini/config/global_workflows/ (agy scans it at startup; not version-isolated — see workflows.ts), invoked as /<name> slash commands
|
|
478
523
|
},
|
|
479
524
|
// xAI Grok Build CLI (`grok`) — early beta, SuperGrok Heavy. Auth via OAuth on
|
|
480
525
|
// first launch, or XAI_API_KEY env var for headless. MCP servers configured inline
|
|
@@ -525,6 +570,7 @@ export const AGENTS = {
|
|
|
525
570
|
// to auto (→ edit via resolveMode). Interactive plan is unaffected.
|
|
526
571
|
headlessPlan: false,
|
|
527
572
|
rulesImports: true,
|
|
573
|
+
interactiveRepl: true,
|
|
528
574
|
},
|
|
529
575
|
},
|
|
530
576
|
// Kimi Code CLI (`kimi`) — Moonshot AI coding agent.
|
|
@@ -568,6 +614,7 @@ export const AGENTS = {
|
|
|
568
614
|
// auto-runs). Interactive plan is unaffected.
|
|
569
615
|
headlessPlan: false,
|
|
570
616
|
rulesImports: false,
|
|
617
|
+
interactiveRepl: true,
|
|
571
618
|
},
|
|
572
619
|
},
|
|
573
620
|
// Factory AI Droid CLI (`droid`) — agentic coding CLI from factory.ai.
|
|
@@ -649,6 +696,7 @@ export const AGENTS = {
|
|
|
649
696
|
memory: false,
|
|
650
697
|
modes: ['plan', 'edit', 'auto', 'skip'],
|
|
651
698
|
rulesImports: false,
|
|
699
|
+
interactiveRepl: true,
|
|
652
700
|
},
|
|
653
701
|
},
|
|
654
702
|
// Nous Hermes Agent. Config lives under ~/.hermes/config.yaml; MCP servers
|
|
@@ -698,6 +746,7 @@ export const AGENTS = {
|
|
|
698
746
|
memory: true,
|
|
699
747
|
modes: ['edit'],
|
|
700
748
|
rulesImports: false,
|
|
749
|
+
interactiveRepl: true,
|
|
701
750
|
},
|
|
702
751
|
},
|
|
703
752
|
};
|
|
@@ -2350,6 +2399,9 @@ export function getUserMcpConfigPath(agentId) {
|
|
|
2350
2399
|
return path.join(agent.configDir, 'mcp.json');
|
|
2351
2400
|
case 'hermes':
|
|
2352
2401
|
return path.join(agent.configDir, 'config.yaml');
|
|
2402
|
+
case 'pi':
|
|
2403
|
+
// omp reads user-scope MCP from ~/.omp/agent/.mcp.json (Claude schema).
|
|
2404
|
+
return path.join(agent.configDir, '.mcp.json');
|
|
2353
2405
|
default:
|
|
2354
2406
|
// Gemini and others use settings.json
|
|
2355
2407
|
return path.join(agent.configDir, 'settings.json');
|
|
@@ -2386,6 +2438,8 @@ export function getMcpConfigPathForHome(agentId, home) {
|
|
|
2386
2438
|
return path.join(home, '.factory', 'mcp.json');
|
|
2387
2439
|
case 'hermes':
|
|
2388
2440
|
return path.join(home, '.hermes', 'config.yaml');
|
|
2441
|
+
case 'pi':
|
|
2442
|
+
return path.join(home, '.omp', 'agent', '.mcp.json');
|
|
2389
2443
|
default:
|
|
2390
2444
|
return path.join(home, agentConfigDirName(agentId), 'settings.json');
|
|
2391
2445
|
}
|
|
@@ -2425,6 +2479,9 @@ export function getProjectMcpConfigPath(agentId, cwd = process.cwd()) {
|
|
|
2425
2479
|
return path.join(cwd, '.factory', 'mcp.json');
|
|
2426
2480
|
case 'hermes':
|
|
2427
2481
|
return path.join(cwd, '.hermes', 'config.yaml');
|
|
2482
|
+
case 'pi':
|
|
2483
|
+
// omp reads project MCP from <root>/.mcp.json (Claude-compatible).
|
|
2484
|
+
return path.join(cwd, '.mcp.json');
|
|
2428
2485
|
default:
|
|
2429
2486
|
return path.join(cwd, `.${agentId}`, 'settings.json');
|
|
2430
2487
|
}
|
|
@@ -92,13 +92,19 @@ export function recipeToolsPerSession(win) {
|
|
|
92
92
|
if (!db)
|
|
93
93
|
return { id, title, store: 'sessions', rows: [], empty: true };
|
|
94
94
|
try {
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
// Counts come from tool_scan_ledger — one row per session the tool indexer
|
|
96
|
+
// has scanned, carrying that session's true call_count (0 included). The
|
|
97
|
+
// sessions.tool_call_count column is NOT the source: only the teams
|
|
98
|
+
// summarizer ever writes it (lib/teams/summarizer.ts), so reading it scored
|
|
99
|
+
// every non-teams session as 0 and pinned p50 at 0 fleet-wide.
|
|
100
|
+
const tables = db.prepare(`SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'tool_scan_ledger'`).all();
|
|
101
|
+
if (tables.length === 0) {
|
|
97
102
|
return { id, title, store: 'sessions', rows: [], empty: true };
|
|
98
103
|
}
|
|
99
|
-
const byAgent = db.prepare(`SELECT agent,
|
|
100
|
-
FROM
|
|
101
|
-
|
|
104
|
+
const byAgent = db.prepare(`SELECT s.agent AS agent, l.call_count AS n
|
|
105
|
+
FROM tool_scan_ledger l
|
|
106
|
+
JOIN sessions s ON s.id = l.session_id
|
|
107
|
+
WHERE s.timestamp >= ?`).all(win.sinceIso);
|
|
102
108
|
if (byAgent.length === 0)
|
|
103
109
|
return { id, title, store: 'sessions', rows: [], empty: true };
|
|
104
110
|
const groups = new Map();
|
package/dist/lib/browser/ipc.js
CHANGED
|
@@ -30,13 +30,21 @@ export declare function getProfile(name: string): Promise<BrowserProfile | null>
|
|
|
30
30
|
* Resolve the profile `agents browser start` uses when no `--profile` is given.
|
|
31
31
|
*
|
|
32
32
|
* Order: (1) the device-local configured default (`agents browser profiles
|
|
33
|
-
* set-default <name>`) when it names an existing profile
|
|
34
|
-
* `default` profile
|
|
35
|
-
* browser per the platform priority list in
|
|
36
|
-
* edge > chromium > comet; Linux: chrome >
|
|
37
|
-
* edge > chrome > brave > comet) and pin a
|
|
38
|
-
* actionable error if none of those binaries
|
|
39
|
-
*
|
|
33
|
+
* set-default <name>`) when it names an existing profile that can launch here;
|
|
34
|
+
* (2) an existing `default` profile that can launch here; (3) auto-pick the
|
|
35
|
+
* first installed Chromium-family browser per the platform priority list in
|
|
36
|
+
* chrome.ts (macOS: chrome > brave > edge > chromium > comet; Linux: chrome >
|
|
37
|
+
* chromium > brave > edge; Windows: edge > chrome > brave > comet) and pin a
|
|
38
|
+
* `default` profile to it. Throws an actionable error if none of those binaries
|
|
39
|
+
* are installed.
|
|
40
|
+
*
|
|
41
|
+
* "Can launch here" matters because `agents.yaml` syncs across the fleet: a
|
|
42
|
+
* `default` auto-created on macOS carries a `/Applications/...` binary path that
|
|
43
|
+
* doesn't exist on a Linux box. Rather than hand that broken profile back (the
|
|
44
|
+
* top browser roadblock — "Custom binary not found"), we re-detect and
|
|
45
|
+
* regenerate the `default` for this machine. A configured or existing default
|
|
46
|
+
* that no longer exists, or can't launch here, warns and falls through — never a
|
|
47
|
+
* hard fail.
|
|
40
48
|
*/
|
|
41
49
|
export declare function ensureDefaultBrowserProfile(): Promise<BrowserProfile>;
|
|
42
50
|
/**
|
|
@@ -95,29 +95,63 @@ export async function getProfile(name) {
|
|
|
95
95
|
return null;
|
|
96
96
|
return configToProfile(name, config);
|
|
97
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* True when `profile` can actually launch or attach on THIS machine.
|
|
100
|
+
*
|
|
101
|
+
* A remote (`ssh://`) profile runs its browser on the far host, so a local
|
|
102
|
+
* binary check doesn't apply — assume launchable. A local profile must have its
|
|
103
|
+
* configured browser/binary present on disk here; a `default` profile
|
|
104
|
+
* auto-created on a different OS (e.g. a macOS Chrome path reused on Linux — the
|
|
105
|
+
* "Custom binary not found" failure) fails this check so the caller re-detects
|
|
106
|
+
* for the current machine.
|
|
107
|
+
*/
|
|
108
|
+
function isProfileLaunchableHere(profile) {
|
|
109
|
+
const remote = Object.values(getEndpointPresets(profile)).some((preset) => preset.target.startsWith('ssh://'));
|
|
110
|
+
if (remote)
|
|
111
|
+
return true;
|
|
112
|
+
try {
|
|
113
|
+
findBrowserPath(profile.browser, profile.binary);
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
98
120
|
/**
|
|
99
121
|
* Resolve the profile `agents browser start` uses when no `--profile` is given.
|
|
100
122
|
*
|
|
101
123
|
* Order: (1) the device-local configured default (`agents browser profiles
|
|
102
|
-
* set-default <name>`) when it names an existing profile
|
|
103
|
-
* `default` profile
|
|
104
|
-
* browser per the platform priority list in
|
|
105
|
-
* edge > chromium > comet; Linux: chrome >
|
|
106
|
-
* edge > chrome > brave > comet) and pin a
|
|
107
|
-
* actionable error if none of those binaries
|
|
108
|
-
*
|
|
124
|
+
* set-default <name>`) when it names an existing profile that can launch here;
|
|
125
|
+
* (2) an existing `default` profile that can launch here; (3) auto-pick the
|
|
126
|
+
* first installed Chromium-family browser per the platform priority list in
|
|
127
|
+
* chrome.ts (macOS: chrome > brave > edge > chromium > comet; Linux: chrome >
|
|
128
|
+
* chromium > brave > edge; Windows: edge > chrome > brave > comet) and pin a
|
|
129
|
+
* `default` profile to it. Throws an actionable error if none of those binaries
|
|
130
|
+
* are installed.
|
|
131
|
+
*
|
|
132
|
+
* "Can launch here" matters because `agents.yaml` syncs across the fleet: a
|
|
133
|
+
* `default` auto-created on macOS carries a `/Applications/...` binary path that
|
|
134
|
+
* doesn't exist on a Linux box. Rather than hand that broken profile back (the
|
|
135
|
+
* top browser roadblock — "Custom binary not found"), we re-detect and
|
|
136
|
+
* regenerate the `default` for this machine. A configured or existing default
|
|
137
|
+
* that no longer exists, or can't launch here, warns and falls through — never a
|
|
138
|
+
* hard fail.
|
|
109
139
|
*/
|
|
110
140
|
export async function ensureDefaultBrowserProfile() {
|
|
111
141
|
const configured = getConfiguredDefaultProfileName();
|
|
112
142
|
if (configured) {
|
|
113
143
|
const chosen = await getProfile(configured);
|
|
114
|
-
if (chosen)
|
|
144
|
+
if (chosen && isProfileLaunchableHere(chosen))
|
|
115
145
|
return chosen;
|
|
116
|
-
console.warn(
|
|
117
|
-
`
|
|
146
|
+
console.warn(chosen
|
|
147
|
+
? `warning: configured default browser profile "${configured}" can't launch on this ` +
|
|
148
|
+
`machine (its browser/binary isn't installed here); falling back to auto-detect. ` +
|
|
149
|
+
`Fix with: agents browser profiles set-default <name> (or --unset)`
|
|
150
|
+
: `warning: configured default browser profile "${configured}" no longer exists; ` +
|
|
151
|
+
`falling back to auto-detect. Fix with: agents browser profiles set-default <name> (or --unset)`);
|
|
118
152
|
}
|
|
119
153
|
const existing = await getProfile(DEFAULT_BROWSER_PROFILE_NAME);
|
|
120
|
-
if (existing)
|
|
154
|
+
if (existing && isProfileLaunchableHere(existing))
|
|
121
155
|
return existing;
|
|
122
156
|
const detected = findFirstInstalledBrowser();
|
|
123
157
|
if (!detected) {
|
|
@@ -139,7 +173,14 @@ export async function ensureDefaultBrowserProfile() {
|
|
|
139
173
|
height: DEFAULT_VIEWPORT.height,
|
|
140
174
|
},
|
|
141
175
|
};
|
|
142
|
-
|
|
176
|
+
// A stale `default` (auto-created on another OS, unlaunchable here) is
|
|
177
|
+
// regenerated in place; otherwise this is the first-run create.
|
|
178
|
+
if (existing) {
|
|
179
|
+
await updateProfile(profile);
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
await createProfile(profile);
|
|
183
|
+
}
|
|
143
184
|
return profile;
|
|
144
185
|
}
|
|
145
186
|
/**
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Consent gate for driving THIS machine's browser from another fleet machine.
|
|
3
|
+
*
|
|
4
|
+
* `browser --host <device>` routes a browser command to `<device>` over SSH (the
|
|
5
|
+
* fleet passthrough) and runs `agents browser ...` there. Every such remote
|
|
6
|
+
* invocation carries the {@link FLEET_REMOTE_ENV} marker, set once at the fleet
|
|
7
|
+
* dispatch site (`maybeRunOnHost`). A machine only accepts being driven when its
|
|
8
|
+
* owner has opted in via `agents browser remote-control on` (the device-scope
|
|
9
|
+
* `browser.remote-control` config key, never synced).
|
|
10
|
+
*
|
|
11
|
+
* Local invocations (no marker) are never gated — this only governs cross-machine
|
|
12
|
+
* drives. Read-only queries are not gated here; the gate sits at the drive entry
|
|
13
|
+
* point (`browser start`), the one command that opens/attaches a browser.
|
|
14
|
+
*/
|
|
15
|
+
/** Env marker set on every remote `agents` invocation by `buildRemoteAgentsInvocation`. */
|
|
16
|
+
export declare const FLEET_REMOTE_ENV = "AGENTS_FLEET_REMOTE";
|
|
17
|
+
/** True when this process was dispatched to this machine by a fleet `--host` run. */
|
|
18
|
+
export declare function isFleetRemoteInvocation(env?: NodeJS.ProcessEnv): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Whether this machine allows other fleet machines to drive its browser. Reads
|
|
21
|
+
* the device-scope `browser.remote-control` config key. Unset = off (deny).
|
|
22
|
+
*/
|
|
23
|
+
export declare function remoteControlEnabled(): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Guard the browser-drive entry point. A fleet-remote invocation may only drive
|
|
26
|
+
* this machine's browser when the owner opted in; otherwise throw a clear,
|
|
27
|
+
* actionable error. A local invocation is never gated.
|
|
28
|
+
*
|
|
29
|
+
* `env` and `enabled` are injectable so the decision is unit-testable without a
|
|
30
|
+
* real remote hop or a real config store.
|
|
31
|
+
*/
|
|
32
|
+
export declare function assertRemoteControlAllowed(opts?: {
|
|
33
|
+
env?: NodeJS.ProcessEnv;
|
|
34
|
+
enabled?: boolean;
|
|
35
|
+
}): void;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Consent gate for driving THIS machine's browser from another fleet machine.
|
|
3
|
+
*
|
|
4
|
+
* `browser --host <device>` routes a browser command to `<device>` over SSH (the
|
|
5
|
+
* fleet passthrough) and runs `agents browser ...` there. Every such remote
|
|
6
|
+
* invocation carries the {@link FLEET_REMOTE_ENV} marker, set once at the fleet
|
|
7
|
+
* dispatch site (`maybeRunOnHost`). A machine only accepts being driven when its
|
|
8
|
+
* owner has opted in via `agents browser remote-control on` (the device-scope
|
|
9
|
+
* `browser.remote-control` config key, never synced).
|
|
10
|
+
*
|
|
11
|
+
* Local invocations (no marker) are never gated — this only governs cross-machine
|
|
12
|
+
* drives. Read-only queries are not gated here; the gate sits at the drive entry
|
|
13
|
+
* point (`browser start`), the one command that opens/attaches a browser.
|
|
14
|
+
*/
|
|
15
|
+
import { getConfigValue } from '../device-config.js';
|
|
16
|
+
/** Env marker set on every remote `agents` invocation by `buildRemoteAgentsInvocation`. */
|
|
17
|
+
export const FLEET_REMOTE_ENV = 'AGENTS_FLEET_REMOTE';
|
|
18
|
+
/** True when this process was dispatched to this machine by a fleet `--host` run. */
|
|
19
|
+
export function isFleetRemoteInvocation(env = process.env) {
|
|
20
|
+
return env[FLEET_REMOTE_ENV] === '1';
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Whether this machine allows other fleet machines to drive its browser. Reads
|
|
24
|
+
* the device-scope `browser.remote-control` config key. Unset = off (deny).
|
|
25
|
+
*/
|
|
26
|
+
export function remoteControlEnabled() {
|
|
27
|
+
return getConfigValue('browser.remote-control').value === true;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Guard the browser-drive entry point. A fleet-remote invocation may only drive
|
|
31
|
+
* this machine's browser when the owner opted in; otherwise throw a clear,
|
|
32
|
+
* actionable error. A local invocation is never gated.
|
|
33
|
+
*
|
|
34
|
+
* `env` and `enabled` are injectable so the decision is unit-testable without a
|
|
35
|
+
* real remote hop or a real config store.
|
|
36
|
+
*/
|
|
37
|
+
export function assertRemoteControlAllowed(opts) {
|
|
38
|
+
const env = opts?.env ?? process.env;
|
|
39
|
+
if (!isFleetRemoteInvocation(env))
|
|
40
|
+
return;
|
|
41
|
+
const enabled = opts?.enabled ?? remoteControlEnabled();
|
|
42
|
+
if (enabled)
|
|
43
|
+
return;
|
|
44
|
+
const who = env.AGENTS_ACTOR_HOST || env.AGENTS_ACTOR || 'A fleet machine';
|
|
45
|
+
throw new Error(`${who} tried to drive this machine's browser over \`browser --host\`, but remote ` +
|
|
46
|
+
`browser control is off here. To allow it, run on THIS machine:\n` +
|
|
47
|
+
` agents browser remote-control on`);
|
|
48
|
+
}
|
|
@@ -50,6 +50,22 @@ export interface HealInfo {
|
|
|
50
50
|
role: string;
|
|
51
51
|
name: string;
|
|
52
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Resolve the identity stamped on a task at start: WHO (`owner`) and WHICH run
|
|
55
|
+
* (`launchId`). The forwarded values come from the caller's own CLI process and
|
|
56
|
+
* are authoritative — the browser daemon is shared and long-lived, so resolving
|
|
57
|
+
* the actor daemon-side (the RUSH-2020 bug) mis-attributes every task to the
|
|
58
|
+
* daemon's owner. `resolveLocalActor` is consulted ONLY when no actor was
|
|
59
|
+
* forwarded (a CLI that predates the field, mid-rollout) — never to override a
|
|
60
|
+
* forwarded one.
|
|
61
|
+
*/
|
|
62
|
+
export declare function resolveTaskIdentity(forwarded: {
|
|
63
|
+
actor?: string;
|
|
64
|
+
launchId?: string;
|
|
65
|
+
}, resolveLocalActor: () => string): {
|
|
66
|
+
owner: string;
|
|
67
|
+
launchId?: string;
|
|
68
|
+
};
|
|
53
69
|
export declare class BrowserService {
|
|
54
70
|
private static readonly SOURCE_PREFIX;
|
|
55
71
|
private connections;
|
|
@@ -64,6 +80,9 @@ export declare class BrowserService {
|
|
|
64
80
|
url?: string;
|
|
65
81
|
endpointName?: string;
|
|
66
82
|
skipDomainSkill?: boolean;
|
|
83
|
+
/** Caller identity, forwarded from the CLI (see IPCRequest.actor/launchId). */
|
|
84
|
+
actor?: string;
|
|
85
|
+
launchId?: string;
|
|
67
86
|
}): Promise<{
|
|
68
87
|
task: string;
|
|
69
88
|
name: string;
|
|
@@ -222,6 +222,21 @@ async function isConnHealthy(conn, timeoutMs = 1000) {
|
|
|
222
222
|
return false;
|
|
223
223
|
}
|
|
224
224
|
}
|
|
225
|
+
/**
|
|
226
|
+
* Resolve the identity stamped on a task at start: WHO (`owner`) and WHICH run
|
|
227
|
+
* (`launchId`). The forwarded values come from the caller's own CLI process and
|
|
228
|
+
* are authoritative — the browser daemon is shared and long-lived, so resolving
|
|
229
|
+
* the actor daemon-side (the RUSH-2020 bug) mis-attributes every task to the
|
|
230
|
+
* daemon's owner. `resolveLocalActor` is consulted ONLY when no actor was
|
|
231
|
+
* forwarded (a CLI that predates the field, mid-rollout) — never to override a
|
|
232
|
+
* forwarded one.
|
|
233
|
+
*/
|
|
234
|
+
export function resolveTaskIdentity(forwarded, resolveLocalActor) {
|
|
235
|
+
return {
|
|
236
|
+
owner: forwarded.actor ?? resolveLocalActor(),
|
|
237
|
+
launchId: forwarded.launchId,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
225
240
|
export class BrowserService {
|
|
226
241
|
static SOURCE_PREFIX = {
|
|
227
242
|
'rush-app': 'rush-app-',
|
|
@@ -332,7 +347,10 @@ export class BrowserService {
|
|
|
332
347
|
currentTabId: undefined,
|
|
333
348
|
createdAt: Date.now(),
|
|
334
349
|
pid: conn.pid,
|
|
335
|
-
|
|
350
|
+
// Identity is forwarded from the caller (see resolveTaskIdentity): WHO
|
|
351
|
+
// (owner) and WHICH run (launchId). Resolving daemon-side would attribute
|
|
352
|
+
// every task to the shared daemon's actor (the RUSH-2020 bug).
|
|
353
|
+
...resolveTaskIdentity({ actor: opts.actor, launchId: opts.launchId }, () => resolveActor().id),
|
|
336
354
|
};
|
|
337
355
|
// For Electron, get the existing window as the tab
|
|
338
356
|
if (conn.electron) {
|
|
@@ -79,10 +79,20 @@ export interface Task {
|
|
|
79
79
|
createdAt: number;
|
|
80
80
|
pid: number;
|
|
81
81
|
/**
|
|
82
|
-
* Resolved actor id (`resolveActor().id`) stamped at task start —
|
|
83
|
-
* this browser task
|
|
82
|
+
* Resolved actor id (`resolveActor().id`) stamped at task start — WHO launched
|
|
83
|
+
* this browser task (a person/agent identity, stable across runs). Forwarded
|
|
84
|
+
* from the caller over IPC — the daemon is shared, so it cannot resolve the
|
|
85
|
+
* caller's actor itself. Optional: tasks persisted before RUSH-2020 carry none.
|
|
84
86
|
*/
|
|
85
87
|
owner?: string;
|
|
88
|
+
/**
|
|
89
|
+
* The caller's per-run launch id (`$AGENT_LAUNCH_ID`, minted by exec.ts for
|
|
90
|
+
* every harness) stamped at task start — WHICH run created this task. Distinct
|
|
91
|
+
* from `owner`: two runs by the same actor get different launchIds, which is
|
|
92
|
+
* the scope the current-task default and `status --mine` filter on. Forwarded
|
|
93
|
+
* from the caller over IPC. Optional: tasks from before this shipped carry none.
|
|
94
|
+
*/
|
|
95
|
+
launchId?: string;
|
|
86
96
|
/**
|
|
87
97
|
* Per-tab snapshot of the last ref listing captured for that tab
|
|
88
98
|
* (shortId -> {descriptors, opts}). Persisted to tasks.json so a later
|
|
@@ -184,6 +194,8 @@ export interface IPCRequest {
|
|
|
184
194
|
until?: string;
|
|
185
195
|
appLevel?: string;
|
|
186
196
|
skipDomainSkill?: boolean;
|
|
197
|
+
actor?: string;
|
|
198
|
+
launchId?: string;
|
|
187
199
|
}
|
|
188
200
|
/** Subset of IPCResponse describing a recording start result. */
|
|
189
201
|
export interface RecordStartFields {
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BYOK (Bring Your Own Key) budget fetcher.
|
|
3
|
+
*
|
|
4
|
+
* For custom harnesses that use a provider's API key stored in the keychain
|
|
5
|
+
* (e.g. OpenRouter), this module fetches the account's credit/usage data so
|
|
6
|
+
* `agents view` can show a budget bar alongside the harness detail row — the
|
|
7
|
+
* same bar style the native usage system uses, just sourced from the provider
|
|
8
|
+
* API rather than the CLI's own usage tracking.
|
|
9
|
+
*/
|
|
10
|
+
import type { Profile } from './profiles.js';
|
|
11
|
+
export interface ByokBudgetInfo {
|
|
12
|
+
limitUsd: number | null;
|
|
13
|
+
remainingUsd: number | null;
|
|
14
|
+
usedUsd: number;
|
|
15
|
+
usedPercent: number | null;
|
|
16
|
+
fetchedAt: Date;
|
|
17
|
+
}
|
|
18
|
+
export interface ByokUsageResult {
|
|
19
|
+
budget: ByokBudgetInfo | null;
|
|
20
|
+
error: string | null;
|
|
21
|
+
}
|
|
22
|
+
export declare function setByokFetchForTest(fn: typeof globalThis.fetch): void;
|
|
23
|
+
export declare function resetByokCacheForTest(): void;
|
|
24
|
+
export declare function hasByokProvider(provider: string): boolean;
|
|
25
|
+
export declare function renderByokBar(result: ByokUsageResult): string;
|
|
26
|
+
/**
|
|
27
|
+
* Fetch the BYOK budget for a harness.
|
|
28
|
+
*
|
|
29
|
+
* Returns `null` when the harness has no registered BYOK provider or no auth.
|
|
30
|
+
* Returns a `ByokUsageResult` with `budget: null` when the token is absent
|
|
31
|
+
* from the keychain (so the caller can skip rendering a bar without error).
|
|
32
|
+
*
|
|
33
|
+
* Results are SWR-cached per `keychainItem` so harnesses sharing one API key
|
|
34
|
+
* deduplicate to a single network request.
|
|
35
|
+
*/
|
|
36
|
+
export declare function getByokUsageForHarness(profile: Profile, opts?: {
|
|
37
|
+
forceRefresh?: boolean;
|
|
38
|
+
}): Promise<ByokUsageResult | null>;
|