@phnx-labs/agents-cli 1.20.77 → 1.20.82
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 +589 -0
- package/README.md +27 -25
- package/dist/bin/agents +0 -0
- package/dist/commands/activity.d.ts +9 -0
- package/dist/commands/activity.js +153 -24
- package/dist/commands/apply.js +11 -4
- package/dist/commands/cli.js +1 -1
- package/dist/commands/commands.d.ts +1 -1
- package/dist/commands/commands.js +7 -6
- package/dist/commands/defaults.js +3 -11
- package/dist/commands/doctor.d.ts +74 -0
- package/dist/commands/doctor.js +718 -40
- package/dist/commands/events.js +1 -1
- package/dist/commands/exec.js +53 -8
- package/dist/commands/feed.d.ts +4 -0
- package/dist/commands/feed.js +87 -11
- package/dist/commands/harness.js +2 -2
- package/dist/commands/hooks.js +1 -1
- package/dist/commands/import.js +8 -4
- package/dist/commands/logs.js +6 -0
- package/dist/commands/models.js +1 -1
- package/dist/commands/packages.js +6 -6
- package/dist/commands/permissions.js +1 -1
- package/dist/commands/profiles.js +1 -1
- package/dist/commands/pull.d.ts +1 -1
- package/dist/commands/pull.js +4 -4
- package/dist/commands/repo.d.ts +78 -0
- package/dist/commands/repo.js +195 -11
- package/dist/commands/routines.js +16 -4
- package/dist/commands/rules.js +13 -12
- package/dist/commands/secrets.d.ts +3 -1
- package/dist/commands/secrets.js +108 -34
- package/dist/commands/sessions-browser.d.ts +45 -3
- package/dist/commands/sessions-browser.js +118 -12
- package/dist/commands/sessions-export.d.ts +3 -0
- package/dist/commands/sessions-export.js +13 -10
- package/dist/commands/sessions-inject.d.ts +7 -4
- package/dist/commands/sessions-inject.js +18 -18
- package/dist/commands/sessions-picker.d.ts +15 -1
- package/dist/commands/sessions-picker.js +90 -7
- package/dist/commands/sessions-resume.d.ts +16 -0
- package/dist/commands/sessions-resume.js +14 -13
- package/dist/commands/sessions.d.ts +126 -1
- package/dist/commands/sessions.js +365 -42
- package/dist/commands/set.d.ts +15 -0
- package/dist/commands/set.js +79 -0
- package/dist/commands/setup-fleet.d.ts +21 -0
- package/dist/commands/setup-fleet.js +201 -0
- package/dist/commands/setup-secrets.d.ts +20 -0
- package/dist/commands/setup-secrets.js +223 -0
- package/dist/commands/setup.js +17 -3
- package/dist/commands/skills.js +1 -1
- package/dist/commands/ssh.js +9 -1
- package/dist/commands/sync.js +7 -2
- package/dist/commands/teams-picker.js +0 -1
- package/dist/commands/teams.d.ts +33 -1
- package/dist/commands/teams.js +208 -44
- package/dist/commands/versions.js +10 -5
- package/dist/commands/view.d.ts +1 -0
- package/dist/commands/view.js +11 -0
- package/dist/index.js +8 -13
- package/dist/lib/acp/harnesses.js +0 -7
- package/dist/lib/activity.d.ts +156 -0
- package/dist/lib/activity.js +282 -0
- package/dist/lib/agents.d.ts +9 -23
- package/dist/lib/agents.js +72 -100
- package/dist/lib/auto-pull.d.ts +16 -8
- package/dist/lib/auto-pull.js +23 -28
- package/dist/lib/browser/service.js +3 -0
- package/dist/lib/browser/types.d.ts +7 -0
- package/dist/lib/capabilities.js +6 -2
- package/dist/lib/crabbox/lease.js +2 -2
- package/dist/lib/crabbox/setup-copy.d.ts +4 -4
- package/dist/lib/crabbox/setup-copy.js +4 -4
- package/dist/lib/daemon.d.ts +26 -40
- package/dist/lib/daemon.js +63 -123
- package/dist/lib/devices/fleet-divergence.d.ts +101 -0
- package/dist/lib/devices/fleet-divergence.js +188 -0
- package/dist/lib/devices/fleet-inventory.d.ts +19 -0
- package/dist/lib/devices/fleet-inventory.js +57 -0
- package/dist/lib/devices/fleet.d.ts +1 -1
- package/dist/lib/devices/fleet.js +1 -1
- package/dist/lib/devices/health-report.d.ts +10 -2
- package/dist/lib/devices/health-report.js +32 -1
- package/dist/lib/doctor-diff.d.ts +20 -0
- package/dist/lib/doctor-diff.js +6 -1
- package/dist/lib/drift.d.ts +31 -11
- package/dist/lib/drift.js +58 -7
- package/dist/lib/events.d.ts +8 -1
- package/dist/lib/events.js +10 -1
- package/dist/lib/exec.js +35 -8
- package/dist/lib/feed-post.d.ts +28 -1
- package/dist/lib/feed-post.js +110 -2
- package/dist/lib/fleet/apply.js +8 -1
- package/dist/lib/fleet/auth-sync.d.ts +18 -1
- package/dist/lib/fleet/auth-sync.js +25 -11
- package/dist/lib/fleet/remote-login.js +5 -0
- package/dist/lib/git.d.ts +27 -14
- package/dist/lib/git.js +138 -43
- package/dist/lib/hooks.d.ts +42 -0
- package/dist/lib/hooks.js +137 -93
- package/dist/lib/hosts/dispatch.d.ts +28 -1
- package/dist/lib/hosts/dispatch.js +33 -5
- package/dist/lib/hosts/option.js +2 -2
- package/dist/lib/hosts/passthrough.d.ts +22 -1
- package/dist/lib/hosts/passthrough.js +238 -13
- package/dist/lib/hosts/remote-cmd.js +2 -0
- package/dist/lib/hosts/run-target.d.ts +2 -0
- package/dist/lib/hosts/run-target.js +1 -0
- package/dist/lib/hq/floor.d.ts +1 -1
- package/dist/lib/hq/floor.js +6 -1
- package/dist/lib/mcp.js +3 -71
- package/dist/lib/menubar/MenubarHelper.app/Contents/Info.plist +2 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/Resources/AppIcon.icns +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/_CodeSignature/CodeResources +15 -2
- package/dist/lib/menubar/notify-desktop.d.ts +15 -0
- package/dist/lib/menubar/notify-desktop.js +41 -5
- package/dist/lib/merged-resources.d.ts +11 -0
- package/dist/{commands/resources.js → lib/merged-resources.js} +11 -19
- package/dist/lib/permissions.d.ts +0 -32
- package/dist/lib/permissions.js +4 -157
- package/dist/lib/picker.js +1 -1
- package/dist/lib/platform/process.d.ts +16 -5
- package/dist/lib/platform/process.js +54 -0
- package/dist/lib/plugins.d.ts +0 -8
- package/dist/lib/plugins.js +4 -110
- package/dist/lib/project-resources.js +4 -1
- package/dist/lib/refresh.d.ts +5 -3
- package/dist/lib/refresh.js +7 -5
- package/dist/lib/resources/commands.js +4 -1
- package/dist/lib/resources/mcp.js +0 -4
- package/dist/lib/resources/permissions.d.ts +1 -1
- package/dist/lib/resources/permissions.js +1 -5
- package/dist/lib/resources/rules.js +4 -1
- package/dist/lib/resources/skills.js +4 -1
- package/dist/lib/resources/subagents.js +4 -0
- package/dist/lib/resources/types.d.ts +1 -1
- package/dist/lib/routines.d.ts +20 -0
- package/dist/lib/routines.js +6 -0
- package/dist/lib/run-defaults.d.ts +1 -0
- package/dist/lib/run-defaults.js +9 -0
- package/dist/lib/runner.d.ts +0 -12
- package/dist/lib/runner.js +71 -29
- package/dist/lib/sandbox.js +0 -16
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/Info.plist +2 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/Resources/AppIcon.icns +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +13 -1
- package/dist/lib/secrets/agent.d.ts +18 -0
- package/dist/lib/secrets/agent.js +131 -17
- package/dist/lib/secrets/audit.d.ts +46 -0
- package/dist/lib/secrets/audit.js +56 -0
- package/dist/lib/secrets/bundles.d.ts +12 -9
- package/dist/lib/secrets/bundles.js +47 -31
- package/dist/lib/secrets/index.d.ts +2 -1
- package/dist/lib/secrets/index.js +8 -2
- package/dist/lib/secrets/remote.d.ts +75 -2
- package/dist/lib/secrets/remote.js +139 -5
- package/dist/lib/secrets/scope.d.ts +26 -0
- package/dist/lib/secrets/scope.js +29 -0
- package/dist/lib/secrets/session-store.d.ts +10 -0
- package/dist/lib/secrets/session-store.js +64 -11
- package/dist/lib/secrets/sync.js +3 -3
- package/dist/lib/session/active.d.ts +117 -26
- package/dist/lib/session/active.js +199 -53
- package/dist/lib/session/actor-sidecar.d.ts +23 -0
- package/dist/lib/session/actor-sidecar.js +101 -0
- package/dist/lib/session/db.d.ts +41 -1
- package/dist/lib/session/db.js +173 -8
- package/dist/lib/session/digest.js +126 -21
- package/dist/lib/session/discover.d.ts +19 -2
- package/dist/lib/session/discover.js +32 -11
- package/dist/lib/session/hook-sessions.d.ts +9 -0
- package/dist/lib/session/hook-sessions.js +57 -8
- package/dist/lib/session/origin-machine.d.ts +18 -0
- package/dist/lib/session/origin-machine.js +34 -0
- package/dist/lib/session/pid-registry.d.ts +13 -0
- package/dist/lib/session/render.d.ts +2 -0
- package/dist/lib/session/render.js +1 -1
- package/dist/lib/session/state.d.ts +1 -0
- package/dist/lib/session/state.js +1 -1
- package/dist/lib/session/sync/config.js +2 -2
- package/dist/lib/session/types.d.ts +16 -0
- package/dist/lib/smart-launch.d.ts +86 -0
- package/dist/lib/smart-launch.js +172 -0
- package/dist/lib/staleness/detectors/commands.js +2 -2
- package/dist/lib/staleness/detectors/permissions.js +1 -45
- package/dist/lib/staleness/writers/commands.js +4 -5
- package/dist/lib/staleness/writers/hooks.js +1 -1
- package/dist/lib/startup/command-registry.d.ts +1 -2
- package/dist/lib/startup/command-registry.js +2 -4
- package/dist/lib/subagents-registry.js +5 -12
- package/dist/lib/subagents.d.ts +0 -10
- package/dist/lib/subagents.js +0 -12
- package/dist/lib/teams/agents.d.ts +0 -2
- package/dist/lib/teams/agents.js +10 -27
- package/dist/lib/teams/index.d.ts +1 -1
- package/dist/lib/teams/index.js +1 -1
- package/dist/lib/types.d.ts +17 -9
- package/dist/lib/usage.d.ts +28 -5
- package/dist/lib/usage.js +271 -8
- package/dist/lib/versions.js +7 -1
- package/dist/lib/watchdog/watchdog.d.ts +1 -1
- package/dist/lib/watchdog/watchdog.js +25 -13
- package/package.json +1 -2
- package/dist/commands/check.d.ts +0 -15
- package/dist/commands/check.js +0 -180
- package/dist/commands/resources.d.ts +0 -5
- package/dist/lib/secrets/account-token.d.ts +0 -20
- package/dist/lib/secrets/account-token.js +0 -64
package/dist/lib/session/db.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* SQLite-backed session index and full-text search.
|
|
3
3
|
*
|
|
4
4
|
* Stores session metadata and user-prompt text in a WAL-mode SQLite database
|
|
5
|
-
* at ~/.agents/sessions/sessions.db. Provides incremental upsert, scan-stamp
|
|
5
|
+
* at ~/.agents/.history/sessions/sessions.db. Provides incremental upsert, scan-stamp
|
|
6
6
|
* ledger (mtime/size tracking to skip unchanged files), FTS5 search with
|
|
7
7
|
* BM25 ranking, and label-first search for /rename'd sessions.
|
|
8
8
|
*/
|
|
@@ -12,10 +12,12 @@ import Database from '../sqlite.js';
|
|
|
12
12
|
import { parseSession } from './parse.js';
|
|
13
13
|
import { extractRecentDirectoriesTouched, extractTodoProgressFromEvents } from './state.js';
|
|
14
14
|
import { getSessionsDir, getSessionsDbPath } from '../state.js';
|
|
15
|
+
import { machineForSessionFile } from './origin-machine.js';
|
|
16
|
+
import { loadSessionActorIndex, readSessionActorRecord } from './actor-sidecar.js';
|
|
15
17
|
const SESSIONS_DIR = getSessionsDir();
|
|
16
18
|
const DB_PATH = getSessionsDbPath();
|
|
17
19
|
/** Current schema version; bumped when migrations are added. */
|
|
18
|
-
const SCHEMA_VERSION =
|
|
20
|
+
const SCHEMA_VERSION = 20;
|
|
19
21
|
/**
|
|
20
22
|
* Canonicalize a file path for use as a scan_ledger key. The same physical
|
|
21
23
|
* session file is reachable via multiple aliases — `~/.claude/projects/x.jsonl`
|
|
@@ -62,6 +64,7 @@ CREATE TABLE IF NOT EXISTS sessions (
|
|
|
62
64
|
output_tokens INTEGER,
|
|
63
65
|
cost_usd REAL,
|
|
64
66
|
duration_ms INTEGER,
|
|
67
|
+
model TEXT,
|
|
65
68
|
file_path TEXT NOT NULL,
|
|
66
69
|
file_mtime_ms INTEGER,
|
|
67
70
|
file_size INTEGER,
|
|
@@ -72,16 +75,21 @@ CREATE TABLE IF NOT EXISTS sessions (
|
|
|
72
75
|
worktree_slug TEXT,
|
|
73
76
|
ticket_id TEXT,
|
|
74
77
|
plan TEXT,
|
|
78
|
+
machine TEXT,
|
|
75
79
|
todos TEXT,
|
|
76
80
|
recent_directories_touched TEXT,
|
|
77
81
|
linear_project TEXT,
|
|
78
|
-
linear_project_url TEXT
|
|
82
|
+
linear_project_url TEXT,
|
|
83
|
+
actor TEXT,
|
|
84
|
+
initiated_by TEXT
|
|
79
85
|
);
|
|
80
86
|
CREATE INDEX IF NOT EXISTS idx_sessions_timestamp ON sessions(timestamp DESC);
|
|
81
87
|
CREATE INDEX IF NOT EXISTS idx_sessions_cwd ON sessions(cwd);
|
|
82
88
|
CREATE INDEX IF NOT EXISTS idx_sessions_agent ON sessions(agent);
|
|
83
89
|
CREATE INDEX IF NOT EXISTS idx_sessions_file_path ON sessions(file_path);
|
|
84
90
|
CREATE INDEX IF NOT EXISTS idx_sessions_short_id ON sessions(short_id);
|
|
91
|
+
-- idx_sessions_machine_ts / idx_sessions_agent_ts are created after migration
|
|
92
|
+
-- v17 guarantees the machine column exists (same pattern as last_activity).
|
|
85
93
|
|
|
86
94
|
CREATE VIRTUAL TABLE IF NOT EXISTS session_text USING fts5(
|
|
87
95
|
session_id UNINDEXED,
|
|
@@ -323,6 +331,7 @@ function migrateSchema(db, fromVersion) {
|
|
|
323
331
|
db.exec(`UPDATE sessions SET short_id = substr(id, 1, 8) WHERE short_id IS NULL OR short_id = ''`);
|
|
324
332
|
}
|
|
325
333
|
if (fromVersion < 17) {
|
|
334
|
+
// v16 → v17 (main): todos / recent dirs / linear project metadata.
|
|
326
335
|
const cols = db.prepare(`PRAGMA table_info(sessions)`).all();
|
|
327
336
|
if (!cols.some(c => c.name === 'todos'))
|
|
328
337
|
db.exec(`ALTER TABLE sessions ADD COLUMN todos TEXT`);
|
|
@@ -334,6 +343,46 @@ function migrateSchema(db, fromVersion) {
|
|
|
334
343
|
db.exec(`ALTER TABLE sessions ADD COLUMN linear_project_url TEXT`);
|
|
335
344
|
db.exec(`DELETE FROM scan_ledger; DELETE FROM dir_ledger;`);
|
|
336
345
|
}
|
|
346
|
+
if (fromVersion < 18) {
|
|
347
|
+
// v17 → v18: persist origin machine for smart-launch affinity GROUP BY machine.
|
|
348
|
+
const cols = db.prepare(`PRAGMA table_info(sessions)`).all();
|
|
349
|
+
if (!cols.some(c => c.name === 'machine')) {
|
|
350
|
+
db.exec(`ALTER TABLE sessions ADD COLUMN machine TEXT`);
|
|
351
|
+
}
|
|
352
|
+
db.exec(`
|
|
353
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_machine_ts ON sessions(machine, timestamp DESC);
|
|
354
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_agent_ts ON sessions(agent, timestamp DESC);
|
|
355
|
+
`);
|
|
356
|
+
const rows = db
|
|
357
|
+
.prepare(`SELECT id, agent, file_path FROM sessions WHERE machine IS NULL OR machine = ''`)
|
|
358
|
+
.all();
|
|
359
|
+
const upd = db.prepare(`UPDATE sessions SET machine = ? WHERE id = ?`);
|
|
360
|
+
const txn = db.transaction((items) => {
|
|
361
|
+
for (const r of items) {
|
|
362
|
+
upd.run(machineForSessionFile(r.file_path, r.agent), r.id);
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
txn(rows);
|
|
366
|
+
}
|
|
367
|
+
if (fromVersion < 19) {
|
|
368
|
+
// v18 → v19: actor provenance (RUSH-2018) — who initiated the session and
|
|
369
|
+
// the actor's kind. Populated at write time from the resolved actor (the
|
|
370
|
+
// pid-registry/spawn path), not derivable from the transcript, so there is
|
|
371
|
+
// no ledger wipe here: a rescan can't backfill these, and forcing a full
|
|
372
|
+
// re-parse would be pure churn. Existing rows stay NULL until re-created.
|
|
373
|
+
const cols = db.prepare(`PRAGMA table_info(sessions)`).all();
|
|
374
|
+
if (!cols.some(c => c.name === 'actor'))
|
|
375
|
+
db.exec(`ALTER TABLE sessions ADD COLUMN actor TEXT`);
|
|
376
|
+
if (!cols.some(c => c.name === 'initiated_by'))
|
|
377
|
+
db.exec(`ALTER TABLE sessions ADD COLUMN initiated_by TEXT`);
|
|
378
|
+
}
|
|
379
|
+
if (fromVersion < 20) {
|
|
380
|
+
// v19 → v20: persist the transcript's model for the static session list.
|
|
381
|
+
const cols = db.prepare(`PRAGMA table_info(sessions)`).all();
|
|
382
|
+
if (!cols.some(c => c.name === 'model'))
|
|
383
|
+
db.exec(`ALTER TABLE sessions ADD COLUMN model TEXT`);
|
|
384
|
+
db.exec(`DELETE FROM scan_ledger; DELETE FROM dir_ledger;`);
|
|
385
|
+
}
|
|
337
386
|
}
|
|
338
387
|
/** Open (or return the cached) sessions database, applying migrations as needed. */
|
|
339
388
|
export function getDB() {
|
|
@@ -367,6 +416,27 @@ export function getDB() {
|
|
|
367
416
|
db.exec(`CREATE INDEX IF NOT EXISTS idx_sessions_last_activity ON sessions(last_activity DESC)`);
|
|
368
417
|
db.exec(`CREATE INDEX IF NOT EXISTS idx_sessions_origin ON sessions(origin)`);
|
|
369
418
|
db.exec(`CREATE INDEX IF NOT EXISTS idx_sessions_routine_run_id ON sessions(routine_run_id)`);
|
|
419
|
+
// machine column + indexes: only after the column is guaranteed present.
|
|
420
|
+
// Fresh SCHEMA (v17) includes the column; older DBs get it from migrate v17.
|
|
421
|
+
// If a partial upgrade left schema_version ahead of the column, repair here.
|
|
422
|
+
{
|
|
423
|
+
const cols = db.prepare(`PRAGMA table_info(sessions)`).all();
|
|
424
|
+
if (!cols.some((c) => c.name === 'machine')) {
|
|
425
|
+
db.exec(`ALTER TABLE sessions ADD COLUMN machine TEXT`);
|
|
426
|
+
const rows = db
|
|
427
|
+
.prepare(`SELECT id, agent, file_path FROM sessions WHERE machine IS NULL OR machine = ''`)
|
|
428
|
+
.all();
|
|
429
|
+
const upd = db.prepare(`UPDATE sessions SET machine = ? WHERE id = ?`);
|
|
430
|
+
const txn = db.transaction((items) => {
|
|
431
|
+
for (const r of items) {
|
|
432
|
+
upd.run(machineForSessionFile(r.file_path, r.agent), r.id);
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
txn(rows);
|
|
436
|
+
}
|
|
437
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_sessions_machine_ts ON sessions(machine, timestamp DESC)`);
|
|
438
|
+
db.exec(`CREATE INDEX IF NOT EXISTS idx_sessions_agent_ts ON sessions(agent, timestamp DESC)`);
|
|
439
|
+
}
|
|
370
440
|
// One-shot cleanup of the pre-SQLite JSONL indexes. Safe — nothing reads
|
|
371
441
|
// them anymore. Guarded by a meta flag so we only try once.
|
|
372
442
|
const cleaned = db.prepare(`SELECT value FROM meta WHERE key = 'legacy_indexes_removed'`).get();
|
|
@@ -672,18 +742,20 @@ const upsertSessionStmt = (db) => db.prepare(`
|
|
|
672
742
|
id, short_id, agent, origin, routine_name, routine_run_id,
|
|
673
743
|
version, account, timestamp, last_activity,
|
|
674
744
|
project, cwd, git_branch, topic, label, message_count, token_count,
|
|
675
|
-
output_tokens, cost_usd, duration_ms,
|
|
745
|
+
output_tokens, cost_usd, duration_ms, model,
|
|
676
746
|
file_path, file_mtime_ms, file_size, scanned_at, is_team_origin,
|
|
677
747
|
pr_url, pr_number, worktree_slug, ticket_id, plan, todos,
|
|
678
|
-
recent_directories_touched, linear_project, linear_project_url
|
|
748
|
+
recent_directories_touched, linear_project, linear_project_url, machine,
|
|
749
|
+
actor, initiated_by
|
|
679
750
|
) VALUES (
|
|
680
751
|
@id, @short_id, @agent, @origin, @routine_name, @routine_run_id,
|
|
681
752
|
@version, @account, @timestamp, @last_activity,
|
|
682
753
|
@project, @cwd, @git_branch, @topic, @label, @message_count, @token_count,
|
|
683
|
-
@output_tokens, @cost_usd, @duration_ms,
|
|
754
|
+
@output_tokens, @cost_usd, @duration_ms, @model,
|
|
684
755
|
@file_path, @file_mtime_ms, @file_size, @scanned_at, @is_team_origin,
|
|
685
756
|
@pr_url, @pr_number, @worktree_slug, @ticket_id, @plan, @todos,
|
|
686
|
-
@recent_directories_touched, @linear_project, @linear_project_url
|
|
757
|
+
@recent_directories_touched, @linear_project, @linear_project_url, @machine,
|
|
758
|
+
@actor, @initiated_by
|
|
687
759
|
)
|
|
688
760
|
ON CONFLICT(id) DO UPDATE SET
|
|
689
761
|
short_id = excluded.short_id,
|
|
@@ -713,6 +785,7 @@ const upsertSessionStmt = (db) => db.prepare(`
|
|
|
713
785
|
output_tokens = excluded.output_tokens,
|
|
714
786
|
cost_usd = excluded.cost_usd,
|
|
715
787
|
duration_ms = excluded.duration_ms,
|
|
788
|
+
model = excluded.model,
|
|
716
789
|
file_path = excluded.file_path,
|
|
717
790
|
file_mtime_ms = excluded.file_mtime_ms,
|
|
718
791
|
file_size = excluded.file_size,
|
|
@@ -732,7 +805,16 @@ const upsertSessionStmt = (db) => db.prepare(`
|
|
|
732
805
|
linear_project_url = CASE
|
|
733
806
|
WHEN excluded.ticket_id IS NOT sessions.ticket_id THEN excluded.linear_project_url
|
|
734
807
|
ELSE COALESCE(excluded.linear_project_url, sessions.linear_project_url)
|
|
735
|
-
END
|
|
808
|
+
END,
|
|
809
|
+
machine = excluded.machine,
|
|
810
|
+
-- actor / initiated_by record who launched the session. COALESCE(existing,
|
|
811
|
+
-- incoming) keeps a stored owner (a rescan carries no actor -> excluded.actor
|
|
812
|
+
-- is NULL -> the stored value wins, never clobbered) BUT backfills a row that
|
|
813
|
+
-- was inserted NULL-first — e.g. an older scanner, or any scan that ran before
|
|
814
|
+
-- the actor sidecar landed — once the sidecar-join finally provides one. Plain
|
|
815
|
+
-- exclusion locked those rows to NULL forever (RUSH-2018/2019 fix).
|
|
816
|
+
actor = COALESCE(sessions.actor, excluded.actor),
|
|
817
|
+
initiated_by = COALESCE(sessions.initiated_by, excluded.initiated_by)
|
|
736
818
|
`);
|
|
737
819
|
function enrichCachedSessionMeta(meta) {
|
|
738
820
|
if (!meta.filePath)
|
|
@@ -778,12 +860,24 @@ function storedFtsLabel(readLabel, id) {
|
|
|
778
860
|
const row = readLabel.get(id);
|
|
779
861
|
return row?.label ?? '';
|
|
780
862
|
}
|
|
863
|
+
/** Resolve origin machine for a row: prefer caller-stamped meta, else path. */
|
|
864
|
+
function resolveMachine(meta) {
|
|
865
|
+
if (meta.machine && meta.machine.trim())
|
|
866
|
+
return meta.machine.trim();
|
|
867
|
+
return machineForSessionFile(meta.filePath, meta.agent);
|
|
868
|
+
}
|
|
781
869
|
/**
|
|
782
870
|
* Upsert a session row and replace its FTS5 content in a single transaction.
|
|
783
871
|
* `content` is the tokenizable user-prompt text; pass '' to leave the row unsearchable.
|
|
784
872
|
*/
|
|
785
873
|
export function upsertSession(meta, content, scan) {
|
|
786
874
|
meta = enrichCachedSessionMeta(meta);
|
|
875
|
+
// Join the durable sessionId -> actor sidecar (RUSH-2019) when the caller
|
|
876
|
+
// didn't already carry an actor, so a scanned transcript still attributes to a
|
|
877
|
+
// person. The ON CONFLICT COALESCEs actor/initiated_by, so this fills a fresh
|
|
878
|
+
// row AND backfills one indexed null-first (before its sidecar existed), while a
|
|
879
|
+
// rescan carrying no actor still keeps the stored owner.
|
|
880
|
+
const actorRec = meta.actor ? undefined : readSessionActorRecord(meta.id);
|
|
787
881
|
const db = getDB();
|
|
788
882
|
const { upsert, delText, insText, readLabel } = stmts(db);
|
|
789
883
|
const row = {
|
|
@@ -807,6 +901,7 @@ export function upsertSession(meta, content, scan) {
|
|
|
807
901
|
output_tokens: meta.outputTokens ?? null,
|
|
808
902
|
cost_usd: meta.costUsd ?? null,
|
|
809
903
|
duration_ms: meta.durationMs ?? null,
|
|
904
|
+
model: meta.model ?? null,
|
|
810
905
|
file_path: meta.filePath,
|
|
811
906
|
file_mtime_ms: scan?.fileMtimeMs ?? null,
|
|
812
907
|
file_size: scan?.fileSize ?? null,
|
|
@@ -821,6 +916,9 @@ export function upsertSession(meta, content, scan) {
|
|
|
821
916
|
recent_directories_touched: meta.recentDirectoriesTouched ? JSON.stringify(meta.recentDirectoriesTouched) : null,
|
|
822
917
|
linear_project: meta.linearProject ?? null,
|
|
823
918
|
linear_project_url: meta.linearProjectUrl ?? null,
|
|
919
|
+
machine: resolveMachine(meta),
|
|
920
|
+
actor: meta.actor ?? actorRec?.actor ?? null,
|
|
921
|
+
initiated_by: meta.initiatedBy ?? actorRec?.initiatedBy ?? null,
|
|
824
922
|
};
|
|
825
923
|
const txn = db.transaction(() => {
|
|
826
924
|
upsert.run(row);
|
|
@@ -839,6 +937,12 @@ export function upsertSessionsBatch(entries) {
|
|
|
839
937
|
const db = getDB();
|
|
840
938
|
const { upsert, delText, insText, readLabel } = stmts(db);
|
|
841
939
|
const now = Date.now();
|
|
940
|
+
// One directory read for the whole batch: join the durable sessionId -> actor
|
|
941
|
+
// sidecar (RUSH-2019) so scanned transcripts attribute to a person. Only used
|
|
942
|
+
// for entries whose meta carries no actor; the ON CONFLICT COALESCEs the column,
|
|
943
|
+
// so this fills fresh rows AND backfills null-first ones, never clobbering a
|
|
944
|
+
// stored owner on rescan.
|
|
945
|
+
const actorIndex = loadSessionActorIndex();
|
|
842
946
|
// Persist the Claude resumable-parse continuation (parser_state + content_text)
|
|
843
947
|
// alongside the stamp. On a full/incremental Claude parse the caller passes the
|
|
844
948
|
// serialized newState + accumulated user doc so the NEXT scan can resume from
|
|
@@ -917,6 +1021,7 @@ export function upsertSessionsBatch(entries) {
|
|
|
917
1021
|
output_tokens: meta.outputTokens ?? null,
|
|
918
1022
|
cost_usd: meta.costUsd ?? null,
|
|
919
1023
|
duration_ms: meta.durationMs ?? null,
|
|
1024
|
+
model: meta.model ?? null,
|
|
920
1025
|
file_path: meta.filePath,
|
|
921
1026
|
file_mtime_ms: scan?.fileMtimeMs ?? null,
|
|
922
1027
|
file_size: scan?.fileSize ?? null,
|
|
@@ -931,6 +1036,9 @@ export function upsertSessionsBatch(entries) {
|
|
|
931
1036
|
recent_directories_touched: meta.recentDirectoriesTouched ? JSON.stringify(meta.recentDirectoriesTouched) : null,
|
|
932
1037
|
linear_project: meta.linearProject ?? null,
|
|
933
1038
|
linear_project_url: meta.linearProjectUrl ?? null,
|
|
1039
|
+
machine: resolveMachine(meta),
|
|
1040
|
+
actor: meta.actor ?? actorIndex.get(meta.id)?.actor ?? null,
|
|
1041
|
+
initiated_by: meta.initiatedBy ?? actorIndex.get(meta.id)?.initiatedBy ?? null,
|
|
934
1042
|
});
|
|
935
1043
|
delText.run(meta.id);
|
|
936
1044
|
insText.run(meta.id,
|
|
@@ -1101,6 +1209,7 @@ function rowToMeta(row) {
|
|
|
1101
1209
|
outputTokens: row.output_tokens ?? undefined,
|
|
1102
1210
|
costUsd: row.cost_usd ?? undefined,
|
|
1103
1211
|
durationMs: row.duration_ms ?? undefined,
|
|
1212
|
+
model: row.model ?? undefined,
|
|
1104
1213
|
version: row.version ?? undefined,
|
|
1105
1214
|
account: row.account ?? undefined,
|
|
1106
1215
|
topic: row.topic ?? undefined,
|
|
@@ -1115,6 +1224,11 @@ function rowToMeta(row) {
|
|
|
1115
1224
|
recentDirectoriesTouched: parseJsonColumn(row.recent_directories_touched),
|
|
1116
1225
|
linearProject: row.linear_project ?? undefined,
|
|
1117
1226
|
linearProjectUrl: row.linear_project_url ?? undefined,
|
|
1227
|
+
machine: row.machine ?? undefined,
|
|
1228
|
+
actor: row.actor ?? undefined,
|
|
1229
|
+
// Narrow the free-text column to the known kinds; an unexpected value maps
|
|
1230
|
+
// to undefined rather than being asserted as a valid kind.
|
|
1231
|
+
initiatedBy: row.initiated_by === 'human' || row.initiated_by === 'agent' ? row.initiated_by : undefined,
|
|
1118
1232
|
};
|
|
1119
1233
|
}
|
|
1120
1234
|
function parseJsonColumn(value) {
|
|
@@ -1287,6 +1401,57 @@ export function countSessions(options = {}) {
|
|
|
1287
1401
|
const row = db.prepare(sql).get(...params);
|
|
1288
1402
|
return row ? row.n : 0;
|
|
1289
1403
|
}
|
|
1404
|
+
export function queryAffinityRollup(options) {
|
|
1405
|
+
const db = getDB();
|
|
1406
|
+
const where = [];
|
|
1407
|
+
const params = [];
|
|
1408
|
+
const sinceMs = options.sinceMs ?? (Date.now() - 14 * 24 * 60 * 60 * 1000);
|
|
1409
|
+
// ISO timestamps sort lexicographically; compare as string prefix of datetime.
|
|
1410
|
+
where.push(`timestamp >= ?`);
|
|
1411
|
+
params.push(new Date(sinceMs).toISOString());
|
|
1412
|
+
if (options.onlyCli !== false) {
|
|
1413
|
+
where.push(`IFNULL(origin, 'cli') = 'cli'`);
|
|
1414
|
+
}
|
|
1415
|
+
if (options.excludeTeamOrigin !== false) {
|
|
1416
|
+
where.push(`IFNULL(is_team_origin, 0) = 0`);
|
|
1417
|
+
}
|
|
1418
|
+
if (options.agents && options.agents.length > 0) {
|
|
1419
|
+
where.push(`agent IN (${options.agents.map(() => '?').join(',')})`);
|
|
1420
|
+
params.push(...options.agents);
|
|
1421
|
+
}
|
|
1422
|
+
if (options.project) {
|
|
1423
|
+
where.push(`LOWER(IFNULL(project, '')) LIKE ?`);
|
|
1424
|
+
params.push(`%${options.project.toLowerCase()}%`);
|
|
1425
|
+
}
|
|
1426
|
+
let keyExpr;
|
|
1427
|
+
let selectExtra;
|
|
1428
|
+
if (options.groupBy === 'machine') {
|
|
1429
|
+
keyExpr = `IFNULL(NULLIF(machine, ''), '(unknown)')`;
|
|
1430
|
+
selectExtra = `${keyExpr} AS key, ${keyExpr} AS machine, NULL AS agent`;
|
|
1431
|
+
}
|
|
1432
|
+
else if (options.groupBy === 'agent') {
|
|
1433
|
+
keyExpr = `agent`;
|
|
1434
|
+
selectExtra = `agent AS key, NULL AS machine, agent AS agent`;
|
|
1435
|
+
}
|
|
1436
|
+
else {
|
|
1437
|
+
keyExpr = `IFNULL(NULLIF(machine, ''), '(unknown)') || char(9) || agent`;
|
|
1438
|
+
selectExtra = `${keyExpr} AS key, IFNULL(NULLIF(machine, ''), '(unknown)') AS machine, agent AS agent`;
|
|
1439
|
+
}
|
|
1440
|
+
const clause = where.length ? `WHERE ${where.join(' AND ')}` : '';
|
|
1441
|
+
const sql = `
|
|
1442
|
+
SELECT
|
|
1443
|
+
${selectExtra},
|
|
1444
|
+
COUNT(*) AS launches,
|
|
1445
|
+
IFNULL(SUM(duration_ms), 0) AS durationMs,
|
|
1446
|
+
IFNULL(SUM(token_count), 0) AS tokenCount,
|
|
1447
|
+
IFNULL(SUM(cost_usd), 0) AS costUsd
|
|
1448
|
+
FROM sessions
|
|
1449
|
+
${clause}
|
|
1450
|
+
GROUP BY key
|
|
1451
|
+
ORDER BY launches DESC, key ASC
|
|
1452
|
+
`;
|
|
1453
|
+
return db.prepare(sql).all(...params);
|
|
1454
|
+
}
|
|
1290
1455
|
/**
|
|
1291
1456
|
* Aggregate cost / duration / tokens across sessions, grouped by agent,
|
|
1292
1457
|
* project, or calendar day. Honors the same filter shape as querySessions
|
|
@@ -99,31 +99,136 @@ export function toolHistogram(toolCounts, top = 8) {
|
|
|
99
99
|
.sort((a, b) => b.count - a.count || a.tool.localeCompare(b.tool))
|
|
100
100
|
.slice(0, top);
|
|
101
101
|
}
|
|
102
|
-
/**
|
|
102
|
+
/**
|
|
103
|
+
* Recognized test/build runners → the label we show.
|
|
104
|
+
*
|
|
105
|
+
* These match a *real invocation* of a runner, not any command that merely
|
|
106
|
+
* contains a runner token. In particular we must NOT fire on npm-script
|
|
107
|
+
* sub-targets like `bun test:setup`, `npm run test:watch`, or `pnpm test:ci` —
|
|
108
|
+
* those are setup/watch/CI-orchestration scripts, not a test run whose output we
|
|
109
|
+
* can scrape for a pass/fail verdict. A `test` token followed by `:something` is
|
|
110
|
+
* a distinct script name and is excluded via the `(?![:\w-])` guard.
|
|
111
|
+
*/
|
|
103
112
|
const TEST_RUNNERS = [
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
{ re: /\
|
|
108
|
-
|
|
113
|
+
// Package-manager `test` script: `bun test`, `npm test`, `npm run test`,
|
|
114
|
+
// `yarn test`, `pnpm test`. The negative lookahead `(?![:\w-])` rejects
|
|
115
|
+
// `test:watch` / `test-ci` / `testfoo` — only the bare `test` target counts.
|
|
116
|
+
{ re: /\b(?:bun|npm|yarn|pnpm)\s+(?:run\s+)?test(?![:\w-])/, label: 'tests' },
|
|
117
|
+
// Direct binaries — `vitest`, `jest`, `mocha` (optionally via a runner). These
|
|
118
|
+
// are real invocations; a trailing subcommand like `vitest run` is fine.
|
|
119
|
+
{ re: /\b(?:vitest|jest|mocha)(?![:\w-])/, label: 'tests' },
|
|
120
|
+
{ re: /\bpytest(?![:\w-])/, label: 'pytest' },
|
|
121
|
+
{ re: /\bgo\s+test(?![:\w-])/, label: 'go test' },
|
|
122
|
+
{ re: /\bcargo\s+test(?![:\w-])/, label: 'cargo test' },
|
|
123
|
+
{ re: /\btsc(?:\s+--noEmit)?(?![:\w-])/, label: 'tsc' },
|
|
109
124
|
];
|
|
125
|
+
/**
|
|
126
|
+
* Anchored summary-line extractors, per runner family. Each returns a pass/fail
|
|
127
|
+
* verdict ONLY when it matches that runner's authoritative summary construct —
|
|
128
|
+
* never an arbitrary `\d+ pass`-shaped substring elsewhere in stdout. This is
|
|
129
|
+
* what stops `442 passwords generated`, `442 files`, or `442 passes/sec` from
|
|
130
|
+
* being reported as `442 pass`.
|
|
131
|
+
*/
|
|
132
|
+
function parseSummaryLine(output) {
|
|
133
|
+
const lines = output.split(/\r?\n/);
|
|
134
|
+
// vitest: ` Tests 1 failed | 2 passed (3)` (mixed) or ` Tests 2 passed (2)`
|
|
135
|
+
// (all green). Leading indent + the `Tests` / `Test Files` label + double
|
|
136
|
+
// space is the summary row. Prefer the ` Tests ` row (test count) over the
|
|
137
|
+
// ` Test Files ` row (file count).
|
|
138
|
+
for (const label of ['Tests', 'Test Files']) {
|
|
139
|
+
for (const line of lines) {
|
|
140
|
+
const row = new RegExp(`^\\s*${label}\\s{2,}(.+)$`).exec(line);
|
|
141
|
+
if (!row)
|
|
142
|
+
continue;
|
|
143
|
+
const seg = row[1];
|
|
144
|
+
const passed = /(\d+)\s+passed/.exec(seg);
|
|
145
|
+
const failed = /(\d+)\s+failed/.exec(seg);
|
|
146
|
+
// Only accept a vitest row that actually reports a pass/fail count.
|
|
147
|
+
if (passed || failed) {
|
|
148
|
+
return {
|
|
149
|
+
passed: passed ? +passed[1] : undefined,
|
|
150
|
+
failed: failed ? +failed[1] : 0,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// jest: `Tests: 1 failed, 2 passed, 3 total`. The `Tests:` label anchors
|
|
156
|
+
// it; order of failed/passed varies.
|
|
157
|
+
for (const line of lines) {
|
|
158
|
+
const row = /^\s*Tests:\s+(.+)$/.exec(line);
|
|
159
|
+
if (!row)
|
|
160
|
+
continue;
|
|
161
|
+
const seg = row[1];
|
|
162
|
+
const passed = /(\d+)\s+passed/.exec(seg);
|
|
163
|
+
const failed = /(\d+)\s+failed/.exec(seg);
|
|
164
|
+
if (passed || failed) {
|
|
165
|
+
return { passed: passed ? +passed[1] : undefined, failed: failed ? +failed[1] : 0 };
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// pytest: the summary rule line `==== 3 passed, 1 failed in 0.12s ====`. The
|
|
169
|
+
// surrounding `=` rule and the ` in <dur>s` tail anchor it to the real footer,
|
|
170
|
+
// not a stray `N passed` in captured stdout.
|
|
171
|
+
for (const line of lines) {
|
|
172
|
+
if (!/={3,}.*\bin\s+[\d.]+s\b.*={3,}/.test(line) && !/^={3,}.*={3,}$/.test(line))
|
|
173
|
+
continue;
|
|
174
|
+
if (!/\bin\s+[\d.]+m?s\b/.test(line))
|
|
175
|
+
continue;
|
|
176
|
+
const passed = /(\d+)\s+passed/.exec(line);
|
|
177
|
+
const failed = /(\d+)\s+failed/.exec(line);
|
|
178
|
+
const errors = /(\d+)\s+error/.exec(line);
|
|
179
|
+
if (passed || failed || errors) {
|
|
180
|
+
const failCount = (failed ? +failed[1] : 0) + (errors ? +errors[1] : 0);
|
|
181
|
+
return { passed: passed ? +passed[1] : undefined, failed: failed || errors ? failCount : 0 };
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// bun test: a summary block of ` N pass` / ` N fail` lines (each its own line,
|
|
185
|
+
// leading-indented), closed by `Ran N tests across M files.`. Require the
|
|
186
|
+
// `Ran … tests` sentinel to be present so we only read bun's real summary
|
|
187
|
+
// block — bun also prints inline `(pass)`/`(fail)` per-test lines we ignore.
|
|
188
|
+
if (/^Ran\s+\d+\s+tests?\b/m.test(output)) {
|
|
189
|
+
let passed;
|
|
190
|
+
let failed;
|
|
191
|
+
for (const line of lines) {
|
|
192
|
+
const p = /^\s*(\d+)\s+pass$/.exec(line);
|
|
193
|
+
const f = /^\s*(\d+)\s+fail$/.exec(line);
|
|
194
|
+
if (p)
|
|
195
|
+
passed = +p[1];
|
|
196
|
+
if (f)
|
|
197
|
+
failed = +f[1];
|
|
198
|
+
}
|
|
199
|
+
if (passed !== undefined || failed !== undefined) {
|
|
200
|
+
return { passed, failed: failed ?? 0 };
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// mocha: ` 2 passing` / ` 1 failing` (leading-indented, own line). The
|
|
204
|
+
// `passing`/`failing` gerund is mocha-specific and won't match `442 passwords`.
|
|
205
|
+
{
|
|
206
|
+
let passed;
|
|
207
|
+
let failed;
|
|
208
|
+
for (const line of lines) {
|
|
209
|
+
const p = /^\s*(\d+)\s+passing\b/.exec(line);
|
|
210
|
+
const f = /^\s*(\d+)\s+failing\b/.exec(line);
|
|
211
|
+
if (p)
|
|
212
|
+
passed = +p[1];
|
|
213
|
+
if (f)
|
|
214
|
+
failed = +f[1];
|
|
215
|
+
}
|
|
216
|
+
if (passed !== undefined || failed !== undefined) {
|
|
217
|
+
return { passed, failed: failed ?? 0 };
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return undefined;
|
|
221
|
+
}
|
|
110
222
|
/** Parse pass/fail counts from common runner output. */
|
|
111
223
|
function parseTestOutput(runner, output) {
|
|
112
|
-
//
|
|
113
|
-
//
|
|
114
|
-
//
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
val = +m[1];
|
|
121
|
-
return val;
|
|
122
|
-
};
|
|
123
|
-
const passed = lastNum(/(\d+)\s+pass(?:ed)?/);
|
|
124
|
-
const failed = lastNum(/(\d+)\s+fail(?:ed|ures?)?/);
|
|
125
|
-
if (passed !== undefined || failed !== undefined) {
|
|
126
|
-
return { passed, failed, ok: true };
|
|
224
|
+
// Anchor to a recognized runner summary construct (vitest ` Tests `, jest
|
|
225
|
+
// `Tests:`, pytest `=== N passed in Xs ===`, bun's `N pass`/`N fail` block,
|
|
226
|
+
// mocha `N passing`) — never a blanket `\d+ pass`-shaped scrape over stdout.
|
|
227
|
+
// That blanket scrape used to report `442 passwords`, `442 files passed
|
|
228
|
+
// validation`, or a `442 passes/sec` benchmark as `442 pass`.
|
|
229
|
+
const summary = parseSummaryLine(output);
|
|
230
|
+
if (summary) {
|
|
231
|
+
return { ...summary, ok: true };
|
|
127
232
|
}
|
|
128
233
|
// tsc: no news is good news; "error TSxxxx" means failure.
|
|
129
234
|
if (runner === 'tsc') {
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* subsequent queries are served entirely from the cache.
|
|
8
8
|
*/
|
|
9
9
|
import type { SessionAgentId, SessionEvent, SessionMeta, TodoProgress } from './types.js';
|
|
10
|
+
export { machineForSessionFile } from './origin-machine.js';
|
|
10
11
|
import { type ScanStamp } from './db.js';
|
|
11
12
|
/** Options controlling which sessions to discover and how to report progress. */
|
|
12
13
|
export interface DiscoverOptions {
|
|
@@ -53,6 +54,7 @@ interface ClaudeSessionScan {
|
|
|
53
54
|
cwd?: string;
|
|
54
55
|
gitBranch?: string;
|
|
55
56
|
version?: string;
|
|
57
|
+
model?: string;
|
|
56
58
|
topic?: string;
|
|
57
59
|
messageCount: number;
|
|
58
60
|
tokenCount?: number;
|
|
@@ -92,6 +94,7 @@ interface CodexSessionScan {
|
|
|
92
94
|
cwd?: string;
|
|
93
95
|
gitBranch?: string;
|
|
94
96
|
version?: string;
|
|
97
|
+
model?: string;
|
|
95
98
|
topic?: string;
|
|
96
99
|
messageCount: number;
|
|
97
100
|
tokenCount?: number;
|
|
@@ -148,7 +151,6 @@ export declare function scanAgentsBounded<T>(items: readonly T[], run: (item: T)
|
|
|
148
151
|
* `agents add --isolated`, where the whole point was to keep the two apart.
|
|
149
152
|
*/
|
|
150
153
|
export declare function isManagedSessionFile(filePath: string): boolean;
|
|
151
|
-
export declare function machineForSessionFile(filePath: string, agent: string): string;
|
|
152
154
|
/**
|
|
153
155
|
* Count sessions in scope without running an incremental scan. Assumes the DB
|
|
154
156
|
* is already fresh (typically true because `discoverSessions` ran first this
|
|
@@ -194,6 +196,20 @@ export declare function _normalizeCwdForTest(cwd?: string): string;
|
|
|
194
196
|
* ids, whose charset is too permissive to distinguish from a search phrase.
|
|
195
197
|
*/
|
|
196
198
|
export declare function isCompleteSessionId(query: string): boolean;
|
|
199
|
+
/**
|
|
200
|
+
* Whether a query should be treated as a session id rather than a search phrase
|
|
201
|
+
* — the one canonical id-shaped test, shared by every session-id resolver.
|
|
202
|
+
*
|
|
203
|
+
* True for a complete id (`isCompleteSessionId`) AND for a bare hex short-id or
|
|
204
|
+
* prefix (`d3470b57`), which the complete-id check rejects. Any id-shaped query
|
|
205
|
+
* resolves by id ONLY (exact -> prefix -> `findSessionsById` index) and must
|
|
206
|
+
* never fall back to fuzzy content search: a short id like `d3470b57` otherwise
|
|
207
|
+
* surfaces every transcript that merely MENTIONS the string (a resume prompt
|
|
208
|
+
* echoes the parent id into the body of many later sessions). The hex test
|
|
209
|
+
* catches the bare short-id/prefix; `isCompleteSessionId` additionally catches
|
|
210
|
+
* the prefixed whole ids (`session_…`, `ses_…`) that the hex test rejects.
|
|
211
|
+
*/
|
|
212
|
+
export declare function looksLikeSessionId(query: string): boolean;
|
|
197
213
|
/**
|
|
198
214
|
* Resolve a session by full or short ID. Accepts a pre-loaded session list
|
|
199
215
|
* (fast path from discoverSessions) and falls back to a DB lookup for the
|
|
@@ -352,6 +368,7 @@ export interface ClaudeParseState {
|
|
|
352
368
|
cwd?: string;
|
|
353
369
|
gitBranch?: string;
|
|
354
370
|
version?: string;
|
|
371
|
+
model?: string;
|
|
355
372
|
topic?: string;
|
|
356
373
|
customTitle?: string;
|
|
357
374
|
aiTitle?: string;
|
|
@@ -410,6 +427,7 @@ export interface ClaudeParserState {
|
|
|
410
427
|
cwd?: string;
|
|
411
428
|
gitBranch?: string;
|
|
412
429
|
version?: string;
|
|
430
|
+
model?: string;
|
|
413
431
|
entrypoint?: string;
|
|
414
432
|
firstTsMs?: number;
|
|
415
433
|
topic?: string;
|
|
@@ -653,4 +671,3 @@ export declare function readGrokMeta(filePath: string, currentVersion?: string):
|
|
|
653
671
|
} | null;
|
|
654
672
|
/** Parse a time filter string (relative like '7d' or ISO timestamp) into epoch milliseconds. */
|
|
655
673
|
export declare function parseTimeFilter(input: string): number;
|
|
656
|
-
export {};
|