@phnx-labs/agents-cli 1.20.89 → 1.20.91
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 +361 -0
- package/README.md +6 -0
- package/dist/bin/agents +0 -0
- package/dist/commands/exec.js +7 -1
- package/dist/commands/feed.js +77 -4
- package/dist/commands/harness.d.ts +27 -0
- package/dist/commands/harness.js +120 -13
- package/dist/commands/hooks.js +22 -6
- package/dist/commands/perf.d.ts +14 -0
- package/dist/commands/perf.js +221 -0
- package/dist/commands/profiles.d.ts +3 -0
- package/dist/commands/profiles.js +1 -1
- package/dist/commands/routines.d.ts +19 -0
- package/dist/commands/routines.js +58 -30
- package/dist/commands/secrets.d.ts +52 -4
- package/dist/commands/secrets.js +234 -37
- package/dist/commands/send.d.ts +5 -1
- package/dist/commands/send.js +1 -1
- package/dist/commands/sessions-browser.d.ts +4 -0
- package/dist/commands/sessions-browser.js +51 -9
- package/dist/commands/sessions-favorite.d.ts +20 -0
- package/dist/commands/sessions-favorite.js +120 -0
- package/dist/commands/sessions-picker.js +70 -1
- package/dist/commands/sessions.d.ts +103 -20
- package/dist/commands/sessions.js +356 -62
- package/dist/commands/setup-secrets.d.ts +7 -0
- package/dist/commands/setup-secrets.js +12 -9
- package/dist/commands/versions.js +12 -4
- package/dist/commands/view.d.ts +14 -1
- package/dist/commands/view.js +103 -128
- package/dist/index.js +18 -3
- package/dist/lib/activity.d.ts +11 -1
- package/dist/lib/activity.js +1 -0
- package/dist/lib/agents.d.ts +4 -2
- package/dist/lib/agents.js +21 -6
- package/dist/lib/catchup.d.ts +105 -0
- package/dist/lib/catchup.js +160 -0
- package/dist/lib/channels/providers/desktop.d.ts +49 -0
- package/dist/lib/channels/providers/desktop.js +132 -0
- package/dist/lib/channels/providers/index.js +2 -0
- package/dist/lib/daemon.js +74 -13
- package/dist/lib/events.d.ts +12 -0
- package/dist/lib/events.js +122 -9
- package/dist/lib/exec.js +10 -0
- package/dist/lib/feed-broadcast.d.ts +47 -0
- package/dist/lib/feed-broadcast.js +65 -1
- package/dist/lib/feed-post.d.ts +10 -0
- package/dist/lib/feed-post.js +1 -1
- package/dist/lib/feed.d.ts +47 -1
- package/dist/lib/feed.js +38 -0
- package/dist/lib/hooks/cache.d.ts +2 -0
- package/dist/lib/hooks/cache.js +24 -4
- package/dist/lib/hosts/dispatch.js +19 -1
- package/dist/lib/hq/floor.js +12 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/overdue.d.ts +14 -0
- package/dist/lib/overdue.js +37 -1
- package/dist/lib/perf/db.d.ts +25 -0
- package/dist/lib/perf/db.js +290 -0
- package/dist/lib/perf/spool.d.ts +18 -0
- package/dist/lib/perf/spool.js +79 -0
- package/dist/lib/perf/types.d.ts +45 -0
- package/dist/lib/perf/types.js +2 -0
- package/dist/lib/picker.d.ts +27 -2
- package/dist/lib/picker.js +71 -7
- package/dist/lib/profiles.d.ts +48 -0
- package/dist/lib/profiles.js +67 -0
- package/dist/lib/rotate.d.ts +24 -2
- package/dist/lib/rotate.js +63 -6
- package/dist/lib/routines-project.js +6 -0
- package/dist/lib/routines.d.ts +30 -1
- package/dist/lib/routines.js +11 -0
- 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/list-filter.d.ts +94 -0
- package/dist/lib/secrets/list-filter.js +245 -0
- package/dist/lib/session/active.d.ts +109 -3
- package/dist/lib/session/active.js +269 -13
- package/dist/lib/session/db.d.ts +14 -0
- package/dist/lib/session/db.js +35 -0
- package/dist/lib/session/digest.d.ts +7 -0
- package/dist/lib/session/digest.js +29 -1
- package/dist/lib/session/discover.d.ts +1 -2
- package/dist/lib/session/discover.js +7 -24
- package/dist/lib/session/favorites.d.ts +39 -0
- package/dist/lib/session/favorites.js +101 -0
- package/dist/lib/session/highlights.d.ts +82 -0
- package/dist/lib/session/highlights.js +251 -0
- package/dist/lib/session/host-link.d.ts +68 -0
- package/dist/lib/session/host-link.js +64 -0
- package/dist/lib/session/parse.js +23 -1
- package/dist/lib/session/presence.d.ts +85 -0
- package/dist/lib/session/presence.js +150 -0
- package/dist/lib/session/relative-time.d.ts +14 -0
- package/dist/lib/session/relative-time.js +36 -0
- package/dist/lib/session/remote-list.d.ts +10 -0
- package/dist/lib/session/remote-list.js +47 -9
- package/dist/lib/session/render.d.ts +7 -0
- package/dist/lib/session/render.js +87 -17
- package/dist/lib/session/types.d.ts +4 -1
- package/dist/lib/startup/command-registry.d.ts +1 -0
- package/dist/lib/startup/command-registry.js +2 -0
- package/dist/lib/state.d.ts +9 -0
- package/dist/lib/state.js +11 -0
- package/dist/lib/tmux/binary.d.ts +7 -0
- package/dist/lib/tmux/binary.js +11 -1
- package/dist/lib/types.d.ts +4 -3
- package/dist/lib/usage-backoff.d.ts +29 -0
- package/dist/lib/usage-backoff.js +165 -0
- package/dist/lib/usage.d.ts +112 -5
- package/dist/lib/usage.js +464 -46
- package/dist/lib/watchdog/runner.d.ts +13 -0
- package/dist/lib/watchdog/runner.js +16 -1
- package/package.json +3 -1
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents sessions favorite` — the non-TTY half of the star.
|
|
3
|
+
*
|
|
4
|
+
* The `*` hotkey in the interactive browser is how a human stars a session; this
|
|
5
|
+
* is how a script, an agent, or a machine without a TTY does the same thing, and
|
|
6
|
+
* it is what makes the feature testable end to end without driving a terminal UI.
|
|
7
|
+
* Both write the one store in `lib/session/favorites.ts`.
|
|
8
|
+
*/
|
|
9
|
+
import type { Command } from 'commander';
|
|
10
|
+
/**
|
|
11
|
+
* Resolve one user-typed id (usually the 8-char short id the listing prints) to
|
|
12
|
+
* a full session id. Ambiguity is an ERROR, not a silent first-match: starring
|
|
13
|
+
* the wrong session is invisible until the user wonders where their star went.
|
|
14
|
+
*/
|
|
15
|
+
export declare function resolveFavoriteTarget(idQuery: string): {
|
|
16
|
+
id: string;
|
|
17
|
+
} | {
|
|
18
|
+
error: string;
|
|
19
|
+
};
|
|
20
|
+
export declare function registerSessionsFavoriteCommand(sessionsCmd: Command): void;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents sessions favorite` — the non-TTY half of the star.
|
|
3
|
+
*
|
|
4
|
+
* The `*` hotkey in the interactive browser is how a human stars a session; this
|
|
5
|
+
* is how a script, an agent, or a machine without a TTY does the same thing, and
|
|
6
|
+
* it is what makes the feature testable end to end without driving a terminal UI.
|
|
7
|
+
* Both write the one store in `lib/session/favorites.ts`.
|
|
8
|
+
*/
|
|
9
|
+
import chalk from 'chalk';
|
|
10
|
+
import { setHelpSections } from '../lib/help.js';
|
|
11
|
+
import { findSessionsById } from '../lib/session/db.js';
|
|
12
|
+
import { isCompleteSessionId } from '../lib/session/discover.js';
|
|
13
|
+
import { isFavorite, listFavorites, setFavorite } from '../lib/session/favorites.js';
|
|
14
|
+
/**
|
|
15
|
+
* Resolve one user-typed id (usually the 8-char short id the listing prints) to
|
|
16
|
+
* a full session id. Ambiguity is an ERROR, not a silent first-match: starring
|
|
17
|
+
* the wrong session is invisible until the user wonders where their star went.
|
|
18
|
+
*/
|
|
19
|
+
export function resolveFavoriteTarget(idQuery) {
|
|
20
|
+
const matches = findSessionsById(idQuery);
|
|
21
|
+
// A COMPLETE id needs no index entry: the id is the key the store is built on,
|
|
22
|
+
// and requiring a transcript row would refuse exactly the newest sessions — a
|
|
23
|
+
// live one that has not been indexed yet. The browser's `*` stars those from
|
|
24
|
+
// the live row, so demanding a DB hit here would make the two disagree.
|
|
25
|
+
if (matches.length === 0) {
|
|
26
|
+
return isCompleteSessionId(idQuery.trim())
|
|
27
|
+
? { id: idQuery.trim() }
|
|
28
|
+
: { error: `No session matches "${idQuery}".` };
|
|
29
|
+
}
|
|
30
|
+
if (matches.length > 1) {
|
|
31
|
+
const ids = matches.slice(0, 5).map((m) => m.shortId).join(', ');
|
|
32
|
+
return { error: `"${idQuery}" matches ${matches.length} sessions (${ids}…) — use a longer id.` };
|
|
33
|
+
}
|
|
34
|
+
return { id: matches[0].id };
|
|
35
|
+
}
|
|
36
|
+
export function registerSessionsFavoriteCommand(sessionsCmd) {
|
|
37
|
+
const cmd = sessionsCmd
|
|
38
|
+
.command('favorite')
|
|
39
|
+
.argument('[ids...]', 'Session ids to star (full or short id prefix)')
|
|
40
|
+
.description('Star sessions so they are easy to find again — list them with --favorites, or `f` in the browser.')
|
|
41
|
+
.option('--remove', 'Unstar the given sessions instead of starring them')
|
|
42
|
+
.option('--list', 'List the starred sessions (the default when no ids are given)')
|
|
43
|
+
.option('--json', 'Output JSON');
|
|
44
|
+
setHelpSections(cmd, {
|
|
45
|
+
examples: `
|
|
46
|
+
# Star a session by its short id (the 8 chars the listing prints)
|
|
47
|
+
agents sessions favorite 26c27162
|
|
48
|
+
|
|
49
|
+
# See what is starred
|
|
50
|
+
agents sessions favorite --list
|
|
51
|
+
|
|
52
|
+
# Browse only the starred ones
|
|
53
|
+
agents sessions --favorites
|
|
54
|
+
|
|
55
|
+
# Unstar it again
|
|
56
|
+
agents sessions favorite 26c27162 --remove
|
|
57
|
+
`,
|
|
58
|
+
notes: `
|
|
59
|
+
In the interactive browser (\`agents sessions\`), \`*\` stars the highlighted
|
|
60
|
+
session and \`f\` filters the list down to the starred ones.
|
|
61
|
+
|
|
62
|
+
Stars live in ~/.agents/.history/favorites.json, keyed by session id, so
|
|
63
|
+
they survive a reindex of the session cache. They are per-machine: session
|
|
64
|
+
sync carries transcripts, not this file.
|
|
65
|
+
`,
|
|
66
|
+
});
|
|
67
|
+
cmd.action((ids, options, self) => {
|
|
68
|
+
// `--json` has to come from the merged view, not `options`. The parent
|
|
69
|
+
// `sessions` command declares `--json` AND takes a positional `[query]`, so
|
|
70
|
+
// commander keeps parsing parent-known options past the subcommand name and
|
|
71
|
+
// binds `--json` to the PARENT — `options.json` is silently undefined here
|
|
72
|
+
// while `--remove`/`--list` (unknown to the parent) arrive fine.
|
|
73
|
+
// `optsWithGlobals` is commander's own answer for reading an option a parent
|
|
74
|
+
// owns; it is still declared on this command so `--help` documents it.
|
|
75
|
+
const json = self.optsWithGlobals().json === true;
|
|
76
|
+
if (options.list || ids.length === 0) {
|
|
77
|
+
const starred = [...listFavorites()].sort();
|
|
78
|
+
if (json) {
|
|
79
|
+
process.stdout.write(JSON.stringify({ favorites: starred }, null, 2) + '\n');
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (starred.length === 0) {
|
|
83
|
+
console.log(chalk.gray('No favorited sessions. Star one with `agents sessions favorite <id>`.'));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
for (const id of starred)
|
|
87
|
+
console.log(`${chalk.yellow('★')} ${id}`);
|
|
88
|
+
console.log(chalk.gray(`\n${starred.length} favorite${starred.length === 1 ? '' : 's'}.`));
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const on = !options.remove;
|
|
92
|
+
const results = [];
|
|
93
|
+
for (const idQuery of ids) {
|
|
94
|
+
const resolved = resolveFavoriteTarget(idQuery);
|
|
95
|
+
if ('error' in resolved) {
|
|
96
|
+
results.push({ query: idQuery, error: resolved.error });
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
// Unstarring something that was never starred, or starring it twice, is a
|
|
100
|
+
// no-op the store already short-circuits — report the resulting state.
|
|
101
|
+
setFavorite(resolved.id, on);
|
|
102
|
+
results.push({ query: idQuery, id: resolved.id, favorite: isFavorite(resolved.id) });
|
|
103
|
+
}
|
|
104
|
+
if (json) {
|
|
105
|
+
process.stdout.write(JSON.stringify({ results }, null, 2) + '\n');
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
for (const r of results) {
|
|
109
|
+
if (r.error)
|
|
110
|
+
console.error(chalk.red(r.error));
|
|
111
|
+
else
|
|
112
|
+
console.log(`${r.favorite ? chalk.yellow('★ favorited') : chalk.gray('☆ unfavorited')} ${r.id}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// A failed lookup is a failed command — a script must not read "starred" from
|
|
116
|
+
// a zero exit when nothing was starred.
|
|
117
|
+
if (results.some((r) => r.error))
|
|
118
|
+
process.exitCode = 1;
|
|
119
|
+
});
|
|
120
|
+
}
|
|
@@ -18,6 +18,7 @@ import { extractTodoProgress, WORKTREE_RE } from '../lib/session/state.js';
|
|
|
18
18
|
import { renderMarkdown } from '../lib/markdown.js';
|
|
19
19
|
import { itemPicker } from '../lib/picker.js';
|
|
20
20
|
import { classifyFileChanges, changeCounts, toolHistogram, detectTestResult } from '../lib/session/digest.js';
|
|
21
|
+
import { extractArtifacts, extractHooks, extractLinks, extractRepos, extractSkills } from '../lib/session/highlights.js';
|
|
21
22
|
/** A session whose transcript lives on another machine (folded in over the live
|
|
22
23
|
* cross-machine fan-out): its `filePath` is on that peer's disk, so the preview
|
|
23
24
|
* can't parse it locally — it shows metadata + a "resume there" note instead.
|
|
@@ -417,9 +418,15 @@ function formatCompactPreview(events, session) {
|
|
|
417
418
|
}
|
|
418
419
|
// Recent activity = directories touched (not raw tool calls). Prefer a
|
|
419
420
|
// parser-supplied dirsTouched when present; else derive from event paths.
|
|
421
|
+
// Width-capped: a long Dirs line used to wrap and swamp the whole pane.
|
|
420
422
|
const dirs = directoriesTouched(session, events, changes);
|
|
421
423
|
if (dirs.length) {
|
|
422
|
-
lines.push(chalk.cyan('Dirs: ') +
|
|
424
|
+
lines.push(chalk.cyan('Dirs: ') + joinWidthCapped(dirs, termWidth - 12));
|
|
425
|
+
}
|
|
426
|
+
// Repos worked in (basename of each `.git` root under the touched paths).
|
|
427
|
+
const repos = extractRepos(events, session.cwd);
|
|
428
|
+
if (repos.length) {
|
|
429
|
+
lines.push(chalk.cyan('Repos: ') + chalk.white(repos.slice(0, 4).join(chalk.gray(' · '))));
|
|
423
430
|
}
|
|
424
431
|
const teamLine = formatTeamLineage(session);
|
|
425
432
|
if (teamLine) {
|
|
@@ -442,6 +449,41 @@ function formatCompactPreview(events, session) {
|
|
|
442
449
|
if (activity.length) {
|
|
443
450
|
lines.push(chalk.cyan('Changes: ') + activity.join(chalk.gray(' · ')));
|
|
444
451
|
}
|
|
452
|
+
// Documents the session produced (`.agents/artifacts|plans|reports`, other
|
|
453
|
+
// *.md/*.html creations) — the files a human browses later, named + clickable.
|
|
454
|
+
const artifacts = extractArtifacts(changes);
|
|
455
|
+
if (artifacts.length) {
|
|
456
|
+
const shown = artifacts.slice(0, 5).map(a => linkPath(a.path, a.basename));
|
|
457
|
+
const more = artifacts.length > 5 ? chalk.gray(` · +${artifacts.length - 5} more`) : '';
|
|
458
|
+
lines.push(chalk.cyan('Artifacts: ') + shown.join(chalk.gray(' · ')) + more);
|
|
459
|
+
}
|
|
460
|
+
// Skills invoked (plugin skills included — they ride the same Skill tool).
|
|
461
|
+
const skills = extractSkills(events);
|
|
462
|
+
if (skills.length) {
|
|
463
|
+
const shown = skills.slice(0, 5).map(s => chalk.white(s.name) + (s.count > 1 ? chalk.gray(` ×${s.count}`) : ''));
|
|
464
|
+
const more = skills.length > 5 ? chalk.gray(` · +${skills.length - 5} more`) : '';
|
|
465
|
+
lines.push(chalk.cyan('Skills: ') + shown.join(chalk.gray(' · ')) + more);
|
|
466
|
+
}
|
|
467
|
+
// Hooks fired (Claude transcripts record firings; other harnesses don't).
|
|
468
|
+
const hooks = extractHooks(events);
|
|
469
|
+
if (hooks.length) {
|
|
470
|
+
const shown = hooks.slice(0, 4).map(h => chalk.white(h.name) + (h.count > 1 ? chalk.gray(` ×${h.count}`) : '') + (h.failed ? chalk.red(` (${h.failed} failed)`) : ''));
|
|
471
|
+
const more = hooks.length > 4 ? chalk.gray(` · +${hooks.length - 4} more`) : '';
|
|
472
|
+
lines.push(chalk.cyan('Hooks: ') + shown.join(chalk.gray(' · ')) + more);
|
|
473
|
+
}
|
|
474
|
+
// Links mentioned in the conversation — clickable (OSC 8), tracker-classified.
|
|
475
|
+
const links = extractLinks(events);
|
|
476
|
+
if (links.length) {
|
|
477
|
+
const shown = links.slice(0, 5).map(l => chalk.blue(linkUrl(l.url, l.label)));
|
|
478
|
+
const more = links.length > 5 ? chalk.gray(` · +${links.length - 5} more`) : '';
|
|
479
|
+
lines.push(chalk.cyan('Links: ') + shown.join(chalk.gray(' · ')) + more);
|
|
480
|
+
}
|
|
481
|
+
// Error tally, mirroring the full summary's Errors section in one line.
|
|
482
|
+
const errorEvents = events.filter(e => e.type === 'error');
|
|
483
|
+
if (errorEvents.length) {
|
|
484
|
+
const first = errorEvents[0].tool || 'unknown';
|
|
485
|
+
lines.push(chalk.cyan('Errors: ') + chalk.red(`${errorEvents.length} failure${errorEvents.length === 1 ? '' : 's'}`) + chalk.gray(` — first: ${first}`));
|
|
486
|
+
}
|
|
445
487
|
const metadata = [
|
|
446
488
|
...toolTags,
|
|
447
489
|
subAgentCount ? `${subAgentCount} sub-agent${subAgentCount === 1 ? '' : 's'}` : '',
|
|
@@ -559,12 +601,39 @@ function encodeClaudeSlug(absPath) {
|
|
|
559
601
|
}
|
|
560
602
|
/** The `.agents/worktrees/<name>` marker, Claude-slug-encoded (`/.` → `--`). */
|
|
561
603
|
const SLUG_WORKTREE_RE = /--agents-worktrees-(.+)$/;
|
|
604
|
+
/**
|
|
605
|
+
* Join display tokens with ` · `, stopping before the line exceeds `maxWidth`
|
|
606
|
+
* and appending `… +N more` for the rest. Keeps the Dirs line on one row.
|
|
607
|
+
*/
|
|
608
|
+
function joinWidthCapped(items, maxWidth) {
|
|
609
|
+
const sep = ' · ';
|
|
610
|
+
let out = '';
|
|
611
|
+
let shown = 0;
|
|
612
|
+
for (const item of items) {
|
|
613
|
+
const next = shown === 0 ? item : out + sep + item;
|
|
614
|
+
if (shown > 0 && next.length > maxWidth)
|
|
615
|
+
break;
|
|
616
|
+
out = next;
|
|
617
|
+
shown++;
|
|
618
|
+
}
|
|
619
|
+
const remaining = items.length - shown;
|
|
620
|
+
const suffix = remaining > 0 ? ` … +${remaining} more` : '';
|
|
621
|
+
return chalk.white(out) + (suffix ? chalk.gray(suffix) : '');
|
|
622
|
+
}
|
|
562
623
|
/** Relativize a file path to its parent dir, short enough for one preview line. */
|
|
563
624
|
export function relativizeDir(filePath, cwd) {
|
|
564
625
|
const norm = filePath.replace(/\\/g, '/');
|
|
565
626
|
if (!norm || norm.includes('node_modules') || norm.includes('/.git/') || norm.includes('/plans/')) {
|
|
566
627
|
return undefined;
|
|
567
628
|
}
|
|
629
|
+
// agents-cli internals — version homes, session/run archives, the bare
|
|
630
|
+
// worktree container/root — are never meaningful "directories the user works
|
|
631
|
+
// in". Cwd is exempt: a session running inside such a dir keeps its own paths.
|
|
632
|
+
const normBase = cwd?.replace(/\\/g, '/').replace(/\/$/, '');
|
|
633
|
+
const underCwd = normBase && (norm === normBase || norm.startsWith(normBase + '/'));
|
|
634
|
+
if (!underCwd && (norm.includes('/.agents/.history/') || /\/\.agents\/worktrees(\/[^/]+)?\/?$/.test(norm))) {
|
|
635
|
+
return undefined;
|
|
636
|
+
}
|
|
568
637
|
let dir = path.posix.dirname(norm);
|
|
569
638
|
// Claude project-slug form: a leading `-`-segment (`-home-me-…`) that carries
|
|
570
639
|
// the cwd, lossily encoded. Handle it in SLUG SPACE — never lossy-decode to a
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Command } from 'commander';
|
|
2
2
|
import type { SessionAgentId, SessionMeta, ViewMode } from '../lib/session/types.js';
|
|
3
3
|
import { type ActiveSession } from '../lib/session/active.js';
|
|
4
4
|
import { gatherRemoteList, runOnPeer } from '../lib/session/remote-list.js';
|
|
@@ -17,6 +17,10 @@ interface SessionsOptions extends SessionFilterOptions {
|
|
|
17
17
|
/** Also list sessions from the user's own unmanaged ~/.<agent> installs. */
|
|
18
18
|
unmanaged?: boolean;
|
|
19
19
|
query?: string;
|
|
20
|
+
/** Resolve one historical selector to metadata only (requires --json). */
|
|
21
|
+
resolve?: string;
|
|
22
|
+
/** Versioned internal peer protocol; old/unsafe peers must reject it. */
|
|
23
|
+
resolveSafeV1?: string;
|
|
20
24
|
limit?: string;
|
|
21
25
|
sort?: string;
|
|
22
26
|
json?: boolean;
|
|
@@ -40,6 +44,8 @@ interface SessionsOptions extends SessionFilterOptions {
|
|
|
40
44
|
flat?: boolean;
|
|
41
45
|
/** With --active: show only sessions waiting on user input; exit 1 if any. */
|
|
42
46
|
waiting?: boolean;
|
|
47
|
+
/** Show only favorited (starred) sessions — the `f` key's flag twin. */
|
|
48
|
+
favorites?: boolean;
|
|
43
49
|
/** Enrich the listing with live glyphs/preview for running rows. Default on;
|
|
44
50
|
* `--no-live` sets this false. Commander's `--no-` convention. */
|
|
45
51
|
live?: boolean;
|
|
@@ -120,6 +126,29 @@ export declare function liveGlyphAndPreview(a: ActiveSession | undefined): {
|
|
|
120
126
|
* exported for the row tests.
|
|
121
127
|
*/
|
|
122
128
|
export declare function liveStatusWord(a: ActiveSession | undefined): string;
|
|
129
|
+
/**
|
|
130
|
+
* True when a session is blocked on a human — the `--waiting` contract.
|
|
131
|
+
*
|
|
132
|
+
* NOT `status === 'input_required'`. `foldHostLink` rewrites that status to
|
|
133
|
+
* `orphaned` when nothing is attached, and a session waiting on a question with
|
|
134
|
+
* NOBODY watching is the most acute case `--waiting` exists to surface, not one
|
|
135
|
+
* it should drop. The underlying `activity` is never rewritten, so it is the
|
|
136
|
+
* honest signal here.
|
|
137
|
+
*
|
|
138
|
+
* But `activity` is never rewritten for a DEAD session either: one that died
|
|
139
|
+
* mid-question keeps `waiting_input` forever, and answering it is not a thing a
|
|
140
|
+
* human can do — it needs a relaunch. `--waiting` is a scriptable gate ("does
|
|
141
|
+
* anything need me?"), so a corpse must not trip it.
|
|
142
|
+
*
|
|
143
|
+
* `closed` and `crashed` are unconditionally dead, so they are excluded outright.
|
|
144
|
+
* `abandoned` is NOT: it fires on transcript staleness before the liveness check,
|
|
145
|
+
* so it also covers the live-but-forgotten case — an interactive session that
|
|
146
|
+
* asked a question and sat untouched over a long weekend is still answerable, and
|
|
147
|
+
* is exactly what this gate exists for. It is excluded only when we positively
|
|
148
|
+
* know its process is gone; unknown liveness (an older peer, a row with no pid)
|
|
149
|
+
* stays excluded rather than inventing a human who can answer.
|
|
150
|
+
*/
|
|
151
|
+
export declare function isAwaitingUser(s: ActiveSession): boolean;
|
|
123
152
|
/**
|
|
124
153
|
* The tracker/PR ref for a session's dedicated column: the ticket id when known,
|
|
125
154
|
* else `PR#<n>`, else empty. Pulled out of the trailing badge blob so refs align
|
|
@@ -230,6 +259,10 @@ export declare function mergeLocalFirst(sessions: SessionMeta[], localMachine: s
|
|
|
230
259
|
* `--json --host` remote fan-out so both emit byte-identical row shapes.
|
|
231
260
|
*/
|
|
232
261
|
export declare function serializeSessionsJson(sessions: SessionMeta[]): string;
|
|
262
|
+
/** The intentionally small metadata contract emitted by `sessions --resolve`.
|
|
263
|
+
* Transcript locations, extracted plans, account data, costs, and other indexed
|
|
264
|
+
* payload stay local to the machine that owns them. */
|
|
265
|
+
export declare function serializeResolvedSessionsJson(sessions: SessionMeta[]): string;
|
|
233
266
|
/**
|
|
234
267
|
* Whether the local machine's sessions belong in an `--active` view. Local is
|
|
235
268
|
* included by default; an explicit `--host`/`--device` list scopes the view to
|
|
@@ -281,6 +314,17 @@ export declare function isBareBrowserListing(options: SessionsOptions, query: st
|
|
|
281
314
|
* `runSessionBrowser` picker cannot represent.
|
|
282
315
|
*/
|
|
283
316
|
export declare function hasNoBrowserDisqualifyingFlags(options: SessionsOptions, query: string | undefined): boolean;
|
|
317
|
+
/**
|
|
318
|
+
* The one-line live status banner shown above a session preview: the glyph, the
|
|
319
|
+
* status word, and — when the session needs a human or has LOST one — a plain
|
|
320
|
+
* sentence saying so. Shared by `--preview` and the interactive browser's preview
|
|
321
|
+
* pane so both explain a state the same way.
|
|
322
|
+
*
|
|
323
|
+
* `crashed` and `orphaned` are the states a glyph alone cannot carry: nobody
|
|
324
|
+
* reads "orphan" and knows it means "still running in tmux with no window
|
|
325
|
+
* attached", so those two spell it out.
|
|
326
|
+
*/
|
|
327
|
+
export declare function formatLiveStatusHeadline(live: ActiveSession | undefined, favorite?: boolean): string;
|
|
284
328
|
/**
|
|
285
329
|
* Whether a session belongs to `team`, from either end: it spawned the team, or
|
|
286
330
|
* it is one of the team's teammates. Case-insensitive, matching the SQL
|
|
@@ -305,7 +349,7 @@ export declare function teamBadge(session: SessionMeta): {
|
|
|
305
349
|
* (tracker/PR ref, pulled out of the badge blob so refs align) is only rendered
|
|
306
350
|
* when `showTicket` — otherwise a listing with no refs would waste a column of
|
|
307
351
|
* dashes and needlessly truncate the topic. Worktree stays a trailing badge. */
|
|
308
|
-
export declare function flatSessionRow(session: SessionMeta, live?: ActiveSession, showTicket?: boolean, cols?: PickerColumns): string;
|
|
352
|
+
export declare function flatSessionRow(session: SessionMeta, live?: ActiveSession, showTicket?: boolean, cols?: PickerColumns, favorite?: boolean): string;
|
|
309
353
|
/**
|
|
310
354
|
* Group key for the overview: prefer the indexed project name; else fold the cwd
|
|
311
355
|
* to its repo — a worktree (`.../<repo>/.agents/worktrees/<slug>`) folds to the
|
|
@@ -370,6 +414,19 @@ export interface PickerColumns {
|
|
|
370
414
|
* off for a plain transcript listing, where no row has a host.
|
|
371
415
|
*/
|
|
372
416
|
showHost?: boolean;
|
|
417
|
+
/**
|
|
418
|
+
* Render the favorite marker column. Like every other conditional column here,
|
|
419
|
+
* it earns its 2 cells only when some row in the pool is actually starred — a
|
|
420
|
+
* user who has never favorited anything pays nothing for the feature.
|
|
421
|
+
*/
|
|
422
|
+
showFavorite?: boolean;
|
|
423
|
+
/**
|
|
424
|
+
* Render the live status column (`working` / `waiting` / `orphan` / `crashed`).
|
|
425
|
+
* Live-only, gated the same way as {@link showHost}: it comes from the
|
|
426
|
+
* active-session scan, so the running-filtered browser sets it and a plain
|
|
427
|
+
* transcript listing — where no row has a status — leaves it off.
|
|
428
|
+
*/
|
|
429
|
+
showStatus?: boolean;
|
|
373
430
|
/**
|
|
374
431
|
* Cells the picker prepends before each row: 2 for the single-select cursor
|
|
375
432
|
* ('> '), 6 for the multi-select cursor + checkbox ('> [x] '). Reserved from
|
|
@@ -399,7 +456,7 @@ export declare function pickerColumnsFor(sessions: SessionMeta[]): PickerColumns
|
|
|
399
456
|
* resolvable host (cloud rows, an unreadable process env).
|
|
400
457
|
*/
|
|
401
458
|
export declare function liveHostLabel(a: ActiveSession | undefined): string;
|
|
402
|
-
export declare function formatPickerLabel(s: SessionMeta, query: string, cols?: PickerColumns, ssh?: SshOriginTag, host?: string): string;
|
|
459
|
+
export declare function formatPickerLabel(s: SessionMeta, query: string, cols?: PickerColumns, ssh?: SshOriginTag, host?: string, favorite?: boolean, live?: ActiveSession): string;
|
|
403
460
|
/**
|
|
404
461
|
* Pick a hint to show above the picker. Deterministic (keys off the pool size)
|
|
405
462
|
* so it stays fixed across the picker's re-renders within a single run.
|
|
@@ -466,7 +523,9 @@ export interface SessionQueryResolution {
|
|
|
466
523
|
* search (a bare id must not surface every transcript that merely mentions it).
|
|
467
524
|
* A genuine search phrase keeps the ranked metadata+content search.
|
|
468
525
|
*/
|
|
469
|
-
export declare function resolveSessionQuery(pool: SessionMeta[], query: string
|
|
526
|
+
export declare function resolveSessionQuery(pool: SessionMeta[], query: string, options?: {
|
|
527
|
+
indexFallback?: boolean;
|
|
528
|
+
}): SessionQueryResolution;
|
|
470
529
|
/** Filter and rank sessions by a multi-term search query across metadata and content. */
|
|
471
530
|
export declare function filterSessionsByQuery(sessions: SessionMeta[], query: string | undefined): SessionMeta[];
|
|
472
531
|
/**
|
|
@@ -489,36 +548,60 @@ export interface FleetResolveDeps {
|
|
|
489
548
|
gatherRemoteList: typeof gatherRemoteList;
|
|
490
549
|
runOnPeer: typeof runOnPeer;
|
|
491
550
|
}
|
|
492
|
-
/** One distinct machine that reported
|
|
551
|
+
/** One distinct machine that reported a logical session, plus its winning row. */
|
|
493
552
|
interface FleetHit {
|
|
494
553
|
machine: string;
|
|
495
554
|
session: SessionMeta;
|
|
496
555
|
}
|
|
497
|
-
/**
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
556
|
+
/** One logical session returned by the fleet, including every machine holding a copy. */
|
|
557
|
+
export interface FleetSessionCandidate {
|
|
558
|
+
id: string;
|
|
559
|
+
hits: FleetHit[];
|
|
560
|
+
}
|
|
561
|
+
export type MetadataResolveOutcome = {
|
|
562
|
+
kind: 'resolved';
|
|
563
|
+
session: SessionMeta;
|
|
564
|
+
} | {
|
|
565
|
+
kind: 'not-found';
|
|
566
|
+
} | {
|
|
567
|
+
kind: 'ambiguous';
|
|
568
|
+
candidates: FleetSessionCandidate[];
|
|
569
|
+
} | {
|
|
570
|
+
kind: 'partial';
|
|
571
|
+
failedPeers: string[];
|
|
572
|
+
};
|
|
573
|
+
/** Resolve a fleet sweep through the same canonical full-id / prefix resolver as
|
|
574
|
+
* local lookups, then group copies by logical session id. Synced mirrors of one
|
|
575
|
+
* session therefore stay one candidate even when several machines report them;
|
|
576
|
+
* distinct ids sharing a prefix remain distinct ambiguity candidates. */
|
|
577
|
+
export declare function fleetCandidatesByQuery(rows: SessionMeta[], query: string): FleetSessionCandidate[];
|
|
578
|
+
/** Fixed peer argv for the metadata resolver. Scope flags compose identically on
|
|
579
|
+
* every host; `--all` removes the SSH login cwd/time window, not agent/project filters. */
|
|
580
|
+
export declare function metadataResolveForwardedArgs(selector: string, scope: Pick<SessionFilterOptions, 'agent' | 'project'>): string[];
|
|
581
|
+
/** Resolution must fail closed when any selected peer did not answer. Choosing
|
|
582
|
+
* from a partial fleet can turn an unseen candidate into a false unique match. */
|
|
583
|
+
export declare function metadataResolveOutcome(localMatches: SessionMeta[], remote: {
|
|
584
|
+
sessions: SessionMeta[];
|
|
585
|
+
unreachable: string[];
|
|
586
|
+
}, selector: string): MetadataResolveOutcome;
|
|
587
|
+
/**
|
|
588
|
+
* Locate a full session id or short id prefix across the online fleet and render it from the machine
|
|
506
589
|
* that holds it. The local disk already missed; this fans `sessions <id> --json
|
|
507
590
|
* --all` out to every registered online peer (or the explicit `hosts` set),
|
|
508
591
|
* groups the rows to distinct machines, then:
|
|
509
592
|
*
|
|
510
|
-
* - exactly one
|
|
593
|
+
* - exactly one logical session → delegate rendering to one peer via `runOnPeer`
|
|
511
594
|
* (its transcript and agent binary live there — a local `--host` hop would
|
|
512
595
|
* re-discover locally and dead-end), returning `'rendered'`.
|
|
513
|
-
* - more than one
|
|
514
|
-
*
|
|
596
|
+
* - more than one logical session → print every full-id candidate with its
|
|
597
|
+
* machine labels, returning `'conflict'`.
|
|
515
598
|
* - none → `'not-found'`, letting the caller print the local
|
|
516
599
|
* "no session on this machine" message.
|
|
517
600
|
*
|
|
518
|
-
* No fuzzy/content fallback: the sweep forwards
|
|
519
|
-
*
|
|
601
|
+
* No fuzzy/content fallback: the sweep forwards the id selector and every result
|
|
602
|
+
* is resolved through `resolveSessionQuery`, the same id-only resolver used locally.
|
|
520
603
|
*/
|
|
521
|
-
export declare function resolveSessionAcrossFleet(
|
|
604
|
+
export declare function resolveSessionAcrossFleet(query: string, mode: ViewMode, hosts?: string[], deps?: FleetResolveDeps): Promise<'rendered' | 'conflict' | 'not-found'>;
|
|
522
605
|
/** Register the `agents sessions` command with all its options and help text. */
|
|
523
606
|
export declare function registerSessionsCommands(program: Command): void;
|
|
524
607
|
export {};
|