@phnx-labs/agents-cli 1.22.119 → 1.22.121
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 +236 -0
- package/README.md +19 -2
- package/dist/bootstrap.d.ts +0 -15
- package/dist/bootstrap.js +10 -409
- package/dist/cli/command-registry.d.ts +0 -58
- package/dist/cli/command-registry.js +2 -74
- package/dist/commands/accounts.d.ts +0 -17
- package/dist/commands/accounts.js +1 -52
- package/dist/commands/alias.d.ts +0 -12
- package/dist/commands/alias.js +0 -6
- package/dist/commands/apply.d.ts +0 -15
- package/dist/commands/apply.js +0 -63
- package/dist/commands/audit.d.ts +0 -12
- package/dist/commands/audit.js +0 -13
- package/dist/commands/auth.js +4 -12
- package/dist/commands/beta.d.ts +0 -6
- package/dist/commands/beta.js +2 -6
- package/dist/commands/browser-sessions-picker.d.ts +0 -8
- package/dist/commands/browser-sessions-picker.js +0 -26
- package/dist/commands/browser.d.ts +0 -57
- package/dist/commands/browser.js +0 -80
- package/dist/commands/budget.d.ts +0 -13
- package/dist/commands/budget.js +0 -2
- package/dist/commands/cli.d.ts +0 -12
- package/dist/commands/cli.js +0 -4
- package/dist/commands/cloud.d.ts +0 -9
- package/dist/commands/cloud.js +0 -43
- package/dist/commands/commands.d.ts +0 -9
- package/dist/commands/commands.js +0 -18
- package/dist/commands/computer-sessions-picker.d.ts +0 -11
- package/dist/commands/computer-sessions-picker.js +0 -29
- package/dist/commands/computer.d.ts +0 -62
- package/dist/commands/computer.js +3 -114
- package/dist/commands/config.d.ts +0 -14
- package/dist/commands/config.js +21 -60
- package/dist/commands/cost.d.ts +0 -11
- package/dist/commands/cost.js +1 -17
- package/dist/commands/daemon-test-harness.d.ts +0 -32
- package/dist/commands/daemon-test-harness.js +1 -36
- package/dist/commands/daemon.d.ts +0 -46
- package/dist/commands/daemon.js +8 -255
- package/dist/commands/detach-core.d.ts +0 -28
- package/dist/commands/detach-core.js +0 -30
- package/dist/commands/detach.d.ts +1 -12
- package/dist/commands/detach.js +16 -52
- package/dist/commands/device-config-test-harness.js +0 -12
- package/dist/commands/doctor.d.ts +0 -103
- package/dist/commands/doctor.js +3 -279
- package/dist/commands/events.d.ts +0 -29
- package/dist/commands/events.js +1 -55
- package/dist/commands/exec.d.ts +0 -163
- package/dist/commands/exec.js +15 -964
- package/dist/commands/factory.d.ts +0 -6
- package/dist/commands/factory.js +0 -2
- package/dist/commands/feed-watch.js +0 -20
- package/dist/commands/feed.d.ts +0 -38
- package/dist/commands/feed.js +1 -151
- package/dist/commands/feedback.d.ts +0 -5
- package/dist/commands/feedback.js +0 -5
- package/dist/commands/fleet-capture.d.ts +0 -12
- package/dist/commands/fleet-capture.js +1 -39
- package/dist/commands/focus.d.ts +6 -94
- package/dist/commands/focus.js +13 -183
- package/dist/commands/fork.d.ts +0 -21
- package/dist/commands/fork.js +0 -45
- package/dist/commands/funnel.d.ts +0 -3
- package/dist/commands/go.d.ts +2 -71
- package/dist/commands/go.js +5 -99
- package/dist/commands/harness-hooks.d.ts +0 -41
- package/dist/commands/harness-hooks.js +0 -48
- package/dist/commands/harness-wizard.d.ts +0 -137
- package/dist/commands/harness-wizard.js +0 -109
- package/dist/commands/harness.d.ts +0 -63
- package/dist/commands/harness.js +0 -103
- package/dist/commands/hooks.d.ts +0 -9
- package/dist/commands/hooks.js +0 -26
- package/dist/commands/humans.d.ts +0 -8
- package/dist/commands/humans.js +0 -8
- package/dist/commands/import.d.ts +0 -22
- package/dist/commands/import.js +0 -79
- package/dist/commands/insights.d.ts +0 -37
- package/dist/commands/insights.js +3 -115
- package/dist/commands/inspect.d.ts +0 -117
- package/dist/commands/inspect.js +2 -448
- package/dist/commands/lease.d.ts +0 -28
- package/dist/commands/lease.js +0 -43
- package/dist/commands/logs.d.ts +0 -23
- package/dist/commands/logs.js +0 -30
- package/dist/commands/mailboxes.d.ts +0 -18
- package/dist/commands/mailboxes.js +0 -29
- package/dist/commands/mcp.d.ts +0 -9
- package/dist/commands/mcp.js +0 -53
- package/dist/commands/memory.d.ts +0 -7
- package/dist/commands/memory.js +0 -7
- package/dist/commands/menubar.d.ts +0 -8
- package/dist/commands/menubar.js +0 -24
- package/dist/commands/message.d.ts +0 -23
- package/dist/commands/message.js +0 -50
- package/dist/commands/mine.d.ts +0 -22
- package/dist/commands/mine.js +0 -21
- package/dist/commands/models.d.ts +0 -8
- package/dist/commands/models.js +0 -33
- package/dist/commands/modes.d.ts +0 -10
- package/dist/commands/modes.js +0 -13
- package/dist/commands/monitors.d.ts +0 -9
- package/dist/commands/monitors.js +3 -160
- package/dist/commands/open.d.ts +0 -26
- package/dist/commands/open.js +0 -10
- package/dist/commands/output.d.ts +0 -18
- package/dist/commands/output.js +1 -34
- package/dist/commands/packages-materialize.d.ts +0 -11
- package/dist/commands/packages-materialize.js +0 -3
- package/dist/commands/packages.d.ts +0 -10
- package/dist/commands/packages.js +0 -50
- package/dist/commands/perf.d.ts +0 -41
- package/dist/commands/perf.js +0 -55
- package/dist/commands/permissions.d.ts +0 -9
- package/dist/commands/permissions.js +0 -43
- package/dist/commands/plugins.d.ts +0 -14
- package/dist/commands/plugins.js +2 -55
- package/dist/commands/profiles.d.ts +0 -25
- package/dist/commands/profiles.js +0 -27
- package/dist/commands/projects-todo.d.ts +2 -0
- package/dist/commands/projects-todo.js +132 -0
- package/dist/commands/projects.d.ts +0 -85
- package/dist/commands/projects.js +344 -291
- package/dist/commands/prune.js +0 -30
- package/dist/commands/ps-roster.d.ts +92 -0
- package/dist/commands/ps-roster.js +744 -0
- package/dist/commands/ps.d.ts +6 -0
- package/dist/commands/ps.js +98 -0
- package/dist/commands/refresh-rules.d.ts +0 -13
- package/dist/commands/refresh-rules.js +0 -14
- package/dist/commands/reminders.d.ts +0 -7
- package/dist/commands/repo.d.ts +0 -89
- package/dist/commands/repo.js +7 -205
- package/dist/commands/resource-view.d.ts +0 -33
- package/dist/commands/resource-view.js +0 -49
- package/dist/commands/resume.d.ts +1 -19
- package/dist/commands/resume.js +0 -50
- package/dist/commands/route.d.ts +0 -10
- package/dist/commands/route.js +0 -19
- package/dist/commands/routines.d.ts +0 -42
- package/dist/commands/routines.js +5 -290
- package/dist/commands/routines.test-fixture.d.ts +0 -34
- package/dist/commands/routines.test-fixture.js +4 -100
- package/dist/commands/rules.d.ts +0 -9
- package/dist/commands/rules.js +1 -6
- package/dist/commands/run-account-picker.d.ts +0 -59
- package/dist/commands/run-account-picker.js +0 -71
- package/dist/commands/run-broadcast.d.ts +0 -9
- package/dist/commands/run-broadcast.js +0 -7
- package/dist/commands/run-cloud.d.ts +0 -15
- package/dist/commands/run-cloud.js +0 -37
- package/dist/commands/run-device-picker.d.ts +0 -25
- package/dist/commands/run-device-picker.js +1 -53
- package/dist/commands/secrets-passthrough.d.ts +0 -31
- package/dist/commands/secrets-passthrough.js +0 -10
- package/dist/commands/send.d.ts +0 -20
- package/dist/commands/send.js +14 -18
- package/dist/commands/sessions-backfill.d.ts +0 -7
- package/dist/commands/sessions-backfill.js +1 -16
- package/dist/commands/sessions-backup-setup.js +0 -11
- package/dist/commands/sessions-bookmark.d.ts +0 -8
- package/dist/commands/sessions-bookmark.js +0 -28
- package/dist/commands/sessions-browser.d.ts +2 -117
- package/dist/commands/sessions-browser.js +5 -243
- package/dist/commands/sessions-export.d.ts +1 -26
- package/dist/commands/sessions-export.js +3 -81
- package/dist/commands/sessions-import.d.ts +0 -14
- package/dist/commands/sessions-import.js +0 -59
- package/dist/commands/sessions-inject.d.ts +0 -60
- package/dist/commands/sessions-inject.js +3 -96
- package/dist/commands/sessions-migrate.d.ts +2 -37
- package/dist/commands/sessions-migrate.js +7 -159
- package/dist/commands/sessions-optimize.d.ts +0 -12
- package/dist/commands/sessions-picker-factory.d.ts +0 -7
- package/dist/commands/sessions-picker-factory.js +0 -8
- package/dist/commands/sessions-picker.d.ts +4 -103
- package/dist/commands/sessions-picker.js +13 -352
- package/dist/commands/sessions-render.d.ts +1 -2
- package/dist/commands/sessions-render.js +3 -7
- package/dist/commands/sessions-resume.d.ts +1 -24
- package/dist/commands/sessions-resume.js +2 -65
- package/dist/commands/sessions-share.d.ts +1 -9
- package/dist/commands/sessions-share.js +1 -45
- package/dist/commands/sessions-stats.d.ts +0 -24
- package/dist/commands/sessions-stats.js +0 -49
- package/dist/commands/sessions-stop.d.ts +1 -20
- package/dist/commands/sessions-stop.js +4 -9
- package/dist/commands/sessions-tail.d.ts +1 -19
- package/dist/commands/sessions-tail.js +3 -36
- package/dist/commands/sessions-trace.d.ts +5 -36
- package/dist/commands/sessions-trace.js +7 -82
- package/dist/commands/sessions.d.ts +1 -666
- package/dist/commands/sessions.js +30 -2279
- package/dist/commands/sessions.test-fixture.d.ts +0 -9
- package/dist/commands/sessions.test-fixture.js +8 -23
- package/dist/commands/setup-accounts.d.ts +0 -6
- package/dist/commands/setup-browser.d.ts +0 -23
- package/dist/commands/setup-browser.js +0 -28
- package/dist/commands/setup-computer.d.ts +0 -16
- package/dist/commands/setup-computer.js +0 -28
- package/dist/commands/setup-fleet.d.ts +0 -8
- package/dist/commands/setup-fleet.js +0 -11
- package/dist/commands/setup-mine.d.ts +0 -14
- package/dist/commands/setup-mine.js +0 -25
- package/dist/commands/setup-preferences.d.ts +0 -47
- package/dist/commands/setup-preferences.js +0 -53
- package/dist/commands/setup-secrets.d.ts +2 -24
- package/dist/commands/setup-secrets.js +0 -24
- package/dist/commands/setup-term.d.ts +0 -21
- package/dist/commands/setup-term.js +0 -21
- package/dist/commands/setup-terminal.d.ts +0 -1
- package/dist/commands/setup-terminal.js +0 -1
- package/dist/commands/setup-tools.d.ts +2 -0
- package/dist/commands/setup-tools.js +73 -0
- package/dist/commands/setup.d.ts +0 -20
- package/dist/commands/setup.js +4 -57
- package/dist/commands/skills.d.ts +0 -9
- package/dist/commands/skills.js +1 -22
- package/dist/commands/snapshot.d.ts +0 -9
- package/dist/commands/snapshot.js +0 -10
- package/dist/commands/ssh.d.ts +0 -64
- package/dist/commands/ssh.js +9 -420
- package/dist/commands/status.d.ts +0 -12
- package/dist/commands/status.js +0 -26
- package/dist/commands/subagents.d.ts +0 -8
- package/dist/commands/subagents.js +0 -31
- package/dist/commands/sync.d.ts +0 -37
- package/dist/commands/sync.js +0 -256
- package/dist/commands/teams-picker.d.ts +0 -3
- package/dist/commands/teams-picker.js +0 -34
- package/dist/commands/teams.d.ts +1 -76
- package/dist/commands/teams.js +9 -477
- package/dist/commands/tmux.d.ts +0 -25
- package/dist/commands/tmux.js +0 -46
- package/dist/commands/traces.js +0 -19
- package/dist/commands/trash.d.ts +0 -17
- package/dist/commands/trash.js +1 -11
- package/dist/commands/uninstall.d.ts +0 -9
- package/dist/commands/uninstall.js +0 -6
- package/dist/commands/update.d.ts +0 -22
- package/dist/commands/update.js +0 -44
- package/dist/commands/upgrade.d.ts +0 -1
- package/dist/commands/upgrade.js +0 -1
- package/dist/commands/utils.d.ts +0 -85
- package/dist/commands/utils.js +1 -95
- package/dist/commands/versions.d.ts +0 -16
- package/dist/commands/versions.js +0 -134
- package/dist/commands/view.d.ts +0 -90
- package/dist/commands/view.js +4 -343
- package/dist/commands/watchdog.d.ts +0 -19
- package/dist/commands/watchdog.js +3 -43
- package/dist/commands/webhook.d.ts +0 -8
- package/dist/commands/webhook.js +0 -14
- package/dist/commands/workflows.d.ts +0 -8
- package/dist/commands/workflows.js +0 -6
- package/dist/index.d.ts +0 -25
- package/dist/index.js +5 -119
- package/dist/lib/account-capabilities.d.ts +0 -34
- package/dist/lib/account-capabilities.js +0 -32
- package/dist/lib/account-catalog.d.ts +0 -128
- package/dist/lib/account-catalog.js +8 -154
- package/dist/lib/account-provider-registry.d.ts +0 -14
- package/dist/lib/account-provider-registry.js +0 -8
- package/dist/lib/account-registry.d.ts +0 -74
- package/dist/lib/account-registry.js +14 -189
- package/dist/lib/account-schema.d.ts +0 -19
- package/dist/lib/account-schema.js +0 -43
- package/dist/lib/accounting/account-launch.d.ts +0 -15
- package/dist/lib/accounting/account-launch.js +1 -3
- package/dist/lib/accounting/account-pool-collect.d.ts +0 -26
- package/dist/lib/accounting/account-pool-collect.js +0 -42
- package/dist/lib/accounting/account-pool.d.ts +0 -41
- package/dist/lib/accounting/account-pool.js +0 -17
- package/dist/lib/accounting/capacity.d.ts +0 -32
- package/dist/lib/accounting/capacity.js +0 -32
- package/dist/lib/accounting/rotate.d.ts +0 -518
- package/dist/lib/accounting/rotate.js +4 -598
- package/dist/lib/accounting/usage-ingest.d.ts +0 -1
- package/dist/lib/accounting/usage-ingest.js +0 -44
- package/dist/lib/accounting/usage-sync.d.ts +0 -60
- package/dist/lib/accounting/usage-sync.js +4 -51
- package/dist/lib/accounting/usage.d.ts +0 -632
- package/dist/lib/accounting/usage.js +14 -1055
- package/dist/lib/accounts/add.d.ts +0 -60
- package/dist/lib/accounts/add.js +0 -127
- package/dist/lib/accounts/auth-operation-lock.d.ts +0 -1
- package/dist/lib/accounts/auth-operation-lock.js +0 -4
- package/dist/lib/accounts/migrate.d.ts +0 -9
- package/dist/lib/accounts/migrate.js +3 -42
- package/dist/lib/accounts/slots.d.ts +0 -25
- package/dist/lib/accounts/slots.js +1 -49
- package/dist/lib/acp/client.d.ts +0 -16
- package/dist/lib/acp/client.js +0 -29
- package/dist/lib/acp/harnesses.d.ts +0 -15
- package/dist/lib/acp/harnesses.js +0 -16
- package/dist/lib/acp/run.d.ts +0 -8
- package/dist/lib/acp/run.js +0 -8
- package/dist/lib/actor.d.ts +1 -71
- package/dist/lib/actor.js +1 -99
- package/dist/lib/add-dir.d.ts +0 -46
- package/dist/lib/add-dir.js +1 -70
- package/dist/lib/address.d.ts +0 -17
- package/dist/lib/address.js +0 -15
- package/dist/lib/agent-cli-commands.d.ts +0 -10
- package/dist/lib/agent-cli-commands.js +0 -10
- package/dist/lib/agent-modes.d.ts +0 -29
- package/dist/lib/agent-modes.js +0 -31
- package/dist/lib/agent-spec/agents.d.ts +0 -426
- package/dist/lib/agent-spec/agents.js +37 -924
- package/dist/lib/agent-spec/index.d.ts +0 -5
- package/dist/lib/agent-spec/index.js +0 -14
- package/dist/lib/agent-spec/materialize.d.ts +0 -10
- package/dist/lib/agent-spec/materialize.js +2 -139
- package/dist/lib/agent-spec/package-resolve.d.ts +0 -8
- package/dist/lib/agent-spec/package-resolve.js +1 -44
- package/dist/lib/agent-spec/package-schema.d.ts +0 -2
- package/dist/lib/agent-spec/package-schema.js +0 -11
- package/dist/lib/agent-spec/package-types.d.ts +0 -27
- package/dist/lib/agent-spec/package-types.js +0 -1
- package/dist/lib/agent-spec/primitives.d.ts +0 -18
- package/dist/lib/agent-spec/primitives.js +0 -27
- package/dist/lib/agent-spec/provider.js +0 -2
- package/dist/lib/agent-spec/resolve.d.ts +0 -23
- package/dist/lib/agent-spec/resolve.js +0 -41
- package/dist/lib/agent-spec/types.d.ts +0 -34
- package/dist/lib/agent-spec/types.js +0 -6
- package/dist/lib/agents.d.ts +0 -1
- package/dist/lib/agents.js +0 -1
- package/dist/lib/analytics/mix-commands.d.ts +0 -23
- package/dist/lib/analytics/mix-commands.js +0 -26
- package/dist/lib/analytics/recipes.d.ts +0 -1
- package/dist/lib/analytics/recipes.js +5 -10
- package/dist/lib/analytics/usage-db.js +4 -6
- package/dist/lib/answer-router.d.ts +0 -61
- package/dist/lib/answer-router.js +0 -57
- package/dist/lib/app-bundle-install.d.ts +0 -13
- package/dist/lib/app-bundle-install.js +0 -50
- package/dist/lib/artifact-actions.d.ts +0 -15
- package/dist/lib/artifact-actions.js +0 -18
- package/dist/lib/artifacts-client.d.ts +0 -10
- package/dist/lib/artifacts-client.js +0 -17
- package/dist/lib/ask-classifier.d.ts +0 -38
- package/dist/lib/ask-classifier.js +0 -27
- package/dist/lib/audit/log.d.ts +0 -47
- package/dist/lib/audit/log.js +1 -59
- package/dist/lib/auth-health.d.ts +0 -213
- package/dist/lib/auth-health.js +16 -266
- package/dist/lib/auth-mint.d.ts +0 -83
- package/dist/lib/auth-mint.js +8 -85
- package/dist/lib/auto-pull-worker.d.ts +0 -10
- package/dist/lib/auto-pull-worker.js +3 -48
- package/dist/lib/auto-pull.d.ts +0 -64
- package/dist/lib/auto-pull.js +0 -79
- package/dist/lib/beta.d.ts +0 -9
- package/dist/lib/beta.js +0 -9
- package/dist/lib/binary-shadow.d.ts +0 -22
- package/dist/lib/binary-shadow.js +4 -34
- package/dist/lib/boot-profile.d.ts +0 -10
- package/dist/lib/boot-profile.js +1 -31
- package/dist/lib/brand.d.ts +0 -20
- package/dist/lib/brand.js +0 -50
- package/dist/lib/browser/context.d.ts +0 -54
- package/dist/lib/browser/context.js +0 -49
- package/dist/lib/browser/paths.d.ts +0 -21
- package/dist/lib/browser/paths.js +0 -26
- package/dist/lib/browser/record.d.ts +0 -34
- package/dist/lib/browser/record.js +0 -38
- package/dist/lib/browser/sessions-list.d.ts +1 -79
- package/dist/lib/browser/sessions-list.js +2 -98
- package/dist/lib/browser-client.d.ts +0 -112
- package/dist/lib/browser-client.js +1 -90
- package/dist/lib/budget/config.d.ts +0 -6
- package/dist/lib/budget/config.js +0 -29
- package/dist/lib/budget/enforce.d.ts +0 -50
- package/dist/lib/budget/enforce.js +0 -27
- package/dist/lib/budget/ledger.d.ts +0 -23
- package/dist/lib/budget/ledger.js +0 -28
- package/dist/lib/budget/live-cloud.d.ts +0 -26
- package/dist/lib/budget/live-cloud.js +0 -12
- package/dist/lib/budget/live-team.d.ts +0 -17
- package/dist/lib/budget/live-team.js +1 -35
- package/dist/lib/budget/preflight.d.ts +0 -51
- package/dist/lib/budget/preflight.js +0 -36
- package/dist/lib/byok-usage.d.ts +0 -20
- package/dist/lib/byok-usage.js +1 -24
- package/dist/lib/capabilities.d.ts +0 -24
- package/dist/lib/capabilities.js +0 -29
- package/dist/lib/catchup.d.ts +0 -87
- package/dist/lib/catchup.js +1 -81
- package/dist/lib/channels/owner-forward.d.ts +0 -70
- package/dist/lib/channels/owner-forward.js +1 -43
- package/dist/lib/channels/owner-sink.js +0 -21
- package/dist/lib/channels/providers/desktop.d.ts +0 -37
- package/dist/lib/channels/providers/desktop.js +0 -72
- package/dist/lib/channels/providers/index.d.ts +0 -1
- package/dist/lib/channels/providers/index.js +2 -6
- package/dist/lib/channels/providers/mailbox.js +0 -7
- package/dist/lib/channels/providers/openclaw-telegram.js +0 -8
- package/dist/lib/channels/providers/rush.d.ts +0 -2
- package/dist/lib/channels/providers/rush.js +0 -26
- package/dist/lib/channels/providers/session.d.ts +2 -0
- package/dist/lib/channels/providers/session.js +26 -0
- package/dist/lib/channels/registry.d.ts +0 -27
- package/dist/lib/channels/registry.js +0 -10
- package/dist/lib/channels/resolve.d.ts +0 -19
- package/dist/lib/channels/resolve.js +0 -2
- package/dist/lib/channels/send.d.ts +0 -50
- package/dist/lib/channels/send.js +0 -25
- package/dist/lib/checkpoint.d.ts +0 -36
- package/dist/lib/checkpoint.js +0 -28
- package/dist/lib/claude-account-token.d.ts +0 -98
- package/dist/lib/claude-account-token.js +7 -169
- package/dist/lib/claude-statusline.d.ts +0 -41
- package/dist/lib/claude-statusline.js +0 -68
- package/dist/lib/cli-entry.d.ts +0 -26
- package/dist/lib/cli-entry.js +1 -61
- package/dist/lib/cli-resources.d.ts +31 -89
- package/dist/lib/cli-resources.js +116 -152
- package/dist/lib/cloud/antigravity.d.ts +0 -29
- package/dist/lib/cloud/antigravity.js +0 -36
- package/dist/lib/cloud/codex.d.ts +0 -8
- package/dist/lib/cloud/codex.js +0 -41
- package/dist/lib/cloud/cursor.d.ts +0 -5
- package/dist/lib/cloud/cursor.js +2 -7
- package/dist/lib/cloud/dispatch.d.ts +0 -13
- package/dist/lib/cloud/dispatch.js +1 -48
- package/dist/lib/cloud/factory.d.ts +0 -52
- package/dist/lib/cloud/factory.js +3 -67
- package/dist/lib/cloud/host.d.ts +0 -39
- package/dist/lib/cloud/host.js +5 -48
- package/dist/lib/cloud/registry.d.ts +0 -25
- package/dist/lib/cloud/registry.js +0 -29
- package/dist/lib/cloud/rush.d.ts +0 -47
- package/dist/lib/cloud/rush.js +1 -82
- package/dist/lib/cloud/session-index.d.ts +0 -27
- package/dist/lib/cloud/session-index.js +1 -32
- package/dist/lib/cloud/store.d.ts +0 -18
- package/dist/lib/cloud/store.js +0 -28
- package/dist/lib/cloud/stream.d.ts +0 -14
- package/dist/lib/cloud/stream.js +0 -25
- package/dist/lib/cloud/types.d.ts +0 -168
- package/dist/lib/cloud/types.js +0 -46
- package/dist/lib/cloudflare/creds.d.ts +0 -2
- package/dist/lib/cloudflare/creds.js +0 -14
- package/dist/lib/cloudflare/provision.d.ts +0 -8
- package/dist/lib/cloudflare/provision.js +1 -19
- package/dist/lib/codex-home.d.ts +0 -60
- package/dist/lib/codex-home.js +2 -72
- package/dist/lib/codex-policy.d.ts +0 -10
- package/dist/lib/codex-policy.js +0 -13
- package/dist/lib/command-skills.d.ts +0 -10
- package/dist/lib/command-skills.js +0 -10
- package/dist/lib/commands.d.ts +0 -74
- package/dist/lib/commands.js +0 -112
- package/dist/lib/comms-render.d.ts +0 -5
- package/dist/lib/comms-render.js +0 -5
- package/dist/lib/computer/context.d.ts +0 -23
- package/dist/lib/computer/context.js +0 -14
- package/dist/lib/computer/policy.d.ts +0 -20
- package/dist/lib/computer/policy.js +0 -53
- package/dist/lib/computer/record.d.ts +0 -33
- package/dist/lib/computer/record.js +1 -48
- package/dist/lib/computer/sessions-list.d.ts +1 -128
- package/dist/lib/computer/sessions-list.js +6 -165
- package/dist/lib/computer-client.d.ts +0 -106
- package/dist/lib/computer-client.js +5 -86
- package/dist/lib/concurrency.d.ts +0 -14
- package/dist/lib/concurrency.js +0 -3
- package/dist/lib/config-drift.d.ts +0 -31
- package/dist/lib/config-drift.js +0 -32
- package/dist/lib/config-keys.d.ts +1 -65
- package/dist/lib/config-keys.js +6 -52
- package/dist/lib/config-transfer.d.ts +0 -14
- package/dist/lib/config-transfer.js +0 -26
- package/dist/lib/convert.d.ts +0 -17
- package/dist/lib/convert.js +0 -18
- package/dist/lib/crabbox/cli.d.ts +0 -156
- package/dist/lib/crabbox/cli.js +1 -188
- package/dist/lib/crabbox/config.d.ts +0 -18
- package/dist/lib/crabbox/config.js +1 -19
- package/dist/lib/crabbox/lease.d.ts +0 -93
- package/dist/lib/crabbox/lease.js +4 -148
- package/dist/lib/crabbox/progress.d.ts +0 -56
- package/dist/lib/crabbox/progress.js +2 -49
- package/dist/lib/crabbox/runtimes.d.ts +0 -94
- package/dist/lib/crabbox/runtimes.js +1 -111
- package/dist/lib/crabbox/setup-copy.d.ts +0 -64
- package/dist/lib/crabbox/setup-copy.js +1 -53
- package/dist/lib/daemon/account-state-daemon-service.d.ts +0 -48
- package/dist/lib/daemon/account-state-daemon-service.js +2 -63
- package/dist/lib/daemon/attention-notify-service.d.ts +0 -12
- package/dist/lib/daemon/attention-notify-service.js +1 -24
- package/dist/lib/daemon/auth-sync-service.d.ts +0 -28
- package/dist/lib/daemon/auth-sync-service.js +0 -56
- package/dist/lib/daemon/catchup-service.d.ts +0 -34
- package/dist/lib/daemon/catchup-service.js +0 -30
- package/dist/lib/daemon/daemon.d.ts +0 -374
- package/dist/lib/daemon/daemon.js +44 -1049
- package/dist/lib/daemon/daemon.test-fixture.d.ts +0 -19
- package/dist/lib/daemon/daemon.test-fixture.js +1 -24
- package/dist/lib/daemon/device-probe-service.d.ts +0 -16
- package/dist/lib/daemon/device-probe-service.js +0 -20
- package/dist/lib/daemon/feed-stream-service.d.ts +0 -15
- package/dist/lib/daemon/feed-stream-service.js +0 -20
- package/dist/lib/daemon/harness-update-service.d.ts +2 -61
- package/dist/lib/daemon/harness-update-service.js +6 -86
- package/dist/lib/daemon/heartbeat-service.d.ts +0 -1
- package/dist/lib/daemon/heartbeat-service.js +0 -1
- package/dist/lib/daemon/leaked-daemons.d.ts +0 -46
- package/dist/lib/daemon/leaked-daemons.js +2 -78
- package/dist/lib/daemon/monitor-engine-service.d.ts +0 -13
- package/dist/lib/daemon/monitor-engine-service.js +3 -15
- package/dist/lib/daemon/runner.d.ts +0 -194
- package/dist/lib/daemon/runner.js +39 -760
- package/dist/lib/daemon/self-heal-service.d.ts +18 -17
- package/dist/lib/daemon/self-heal-service.js +83 -37
- package/dist/lib/daemon/self-update-service.d.ts +1 -131
- package/dist/lib/daemon/self-update-service.js +26 -189
- package/dist/lib/daemon/service.d.ts +0 -73
- package/dist/lib/daemon/service.js +0 -34
- package/dist/lib/daemon/session-index-service.d.ts +0 -12
- package/dist/lib/daemon/session-index-service.js +0 -23
- package/dist/lib/daemon/session-state-service.d.ts +0 -8
- package/dist/lib/daemon/session-state-service.js +0 -8
- package/dist/lib/daemon/session-summarizer-service.d.ts +0 -14
- package/dist/lib/daemon/session-summarizer-service.js +1 -19
- package/dist/lib/daemon/session-title-service.d.ts +0 -25
- package/dist/lib/daemon/session-title-service.js +0 -32
- package/dist/lib/daemon/state-dir-check-service.d.ts +0 -24
- package/dist/lib/daemon/state-dir-check-service.js +0 -29
- package/dist/lib/daemon/supervisor.d.ts +0 -112
- package/dist/lib/daemon/supervisor.js +4 -147
- package/dist/lib/daemon/testdata/process-view-daemon.js +0 -2
- package/dist/lib/daemon/testdata/process-view-start.js +0 -5
- package/dist/lib/daemon/tmux-reap-service.d.ts +0 -1
- package/dist/lib/daemon/tmux-reap-service.js +0 -1
- package/dist/lib/daemon/usage-sync-service.d.ts +0 -23
- package/dist/lib/daemon/usage-sync-service.js +0 -26
- package/dist/lib/daemon/watchdog-service.d.ts +0 -9
- package/dist/lib/daemon/watchdog-service.js +0 -13
- package/dist/lib/daemon/webhook-receiver-service.d.ts +0 -1
- package/dist/lib/daemon/webhook-receiver-service.js +0 -1
- package/dist/lib/daemon-health.d.ts +0 -55
- package/dist/lib/daemon-health.js +4 -77
- package/dist/lib/daemon-services.d.ts +0 -36
- package/dist/lib/daemon-services.js +0 -45
- package/dist/lib/daemon-ticks.d.ts +0 -126
- package/dist/lib/daemon-ticks.js +1 -156
- package/dist/lib/daemon-webhooks.d.ts +0 -57
- package/dist/lib/daemon-webhooks.js +2 -54
- package/dist/lib/deeplink/register.d.ts +0 -15
- package/dist/lib/deeplink/register.js +0 -60
- package/dist/lib/deeplink/url.d.ts +0 -24
- package/dist/lib/deeplink/url.js +1 -33
- package/dist/lib/device-config.d.ts +1 -192
- package/dist/lib/device-config.js +86 -240
- package/dist/lib/devices/config-migration.d.ts +0 -43
- package/dist/lib/devices/config-migration.js +7 -127
- package/dist/lib/devices/connect.d.ts +0 -168
- package/dist/lib/devices/connect.js +6 -254
- package/dist/lib/devices/device-docs.d.ts +0 -24
- package/dist/lib/devices/device-docs.js +0 -46
- package/dist/lib/devices/discovery-policy.d.ts +0 -30
- package/dist/lib/devices/discovery-policy.js +3 -34
- package/dist/lib/devices/doctor-findings.d.ts +0 -109
- package/dist/lib/devices/doctor-findings.js +30 -326
- package/dist/lib/devices/doctor-overview-cache.d.ts +0 -33
- package/dist/lib/devices/doctor-overview-cache.js +0 -81
- package/dist/lib/devices/fleet-divergence.d.ts +0 -89
- package/dist/lib/devices/fleet-divergence.js +1 -60
- package/dist/lib/devices/fleet-inventory.d.ts +0 -27
- package/dist/lib/devices/fleet-inventory.js +0 -38
- package/dist/lib/devices/fleet.d.ts +0 -95
- package/dist/lib/devices/fleet.js +0 -79
- package/dist/lib/devices/harness-inventory.d.ts +0 -75
- package/dist/lib/devices/harness-inventory.js +0 -101
- package/dist/lib/devices/health-report.d.ts +0 -37
- package/dist/lib/devices/health-report.js +0 -73
- package/dist/lib/devices/health.d.ts +0 -71
- package/dist/lib/devices/health.js +2 -82
- package/dist/lib/devices/interactive-host.d.ts +0 -10
- package/dist/lib/devices/interactive-host.js +0 -34
- package/dist/lib/devices/known-hosts.d.ts +0 -45
- package/dist/lib/devices/known-hosts.js +0 -77
- package/dist/lib/devices/pending.d.ts +0 -31
- package/dist/lib/devices/pending.js +7 -67
- package/dist/lib/devices/pool.d.ts +0 -36
- package/dist/lib/devices/pool.js +0 -30
- package/dist/lib/devices/reachability.d.ts +0 -25
- package/dist/lib/devices/reachability.js +0 -14
- package/dist/lib/devices/registry.d.ts +0 -141
- package/dist/lib/devices/registry.js +4 -151
- package/dist/lib/devices/resolve-profile.d.ts +0 -18
- package/dist/lib/devices/resolve-profile.js +0 -18
- package/dist/lib/devices/resolve-target.d.ts +0 -20
- package/dist/lib/devices/resolve-target.js +1 -51
- package/dist/lib/devices/rollout-verify.d.ts +1 -81
- package/dist/lib/devices/rollout-verify.js +0 -77
- package/dist/lib/devices/self-host.d.ts +0 -7
- package/dist/lib/devices/self-host.js +1 -29
- package/dist/lib/devices/ssh-config.d.ts +0 -18
- package/dist/lib/devices/ssh-config.js +0 -9
- package/dist/lib/devices/stats-cache.d.ts +0 -48
- package/dist/lib/devices/stats-cache.js +0 -80
- package/dist/lib/devices/sync.d.ts +0 -97
- package/dist/lib/devices/sync.js +0 -88
- package/dist/lib/devices/tailscale.d.ts +0 -18
- package/dist/lib/devices/tailscale.js +1 -38
- package/dist/lib/devices/terminfo.d.ts +0 -27
- package/dist/lib/devices/terminfo.js +2 -64
- package/dist/lib/devices/worker-pick.d.ts +0 -15
- package/dist/lib/devices/worker-pick.js +0 -48
- package/dist/lib/diff-text.d.ts +0 -18
- package/dist/lib/diff-text.js +0 -15
- package/dist/lib/doctor-diff.d.ts +0 -79
- package/dist/lib/doctor-diff.js +2 -181
- package/dist/lib/drift-sync.d.ts +0 -29
- package/dist/lib/drift-sync.js +0 -40
- package/dist/lib/drift.d.ts +0 -43
- package/dist/lib/drift.js +0 -31
- package/dist/lib/event-families.d.ts +0 -17
- package/dist/lib/event-families.js +0 -33
- package/dist/lib/event-provenance.d.ts +0 -4
- package/dist/lib/event-provenance.js +0 -5
- package/dist/lib/event-stream.d.ts +0 -29
- package/dist/lib/event-stream.js +0 -36
- package/dist/lib/events-ingest.d.ts +0 -27
- package/dist/lib/events-ingest.js +0 -54
- package/dist/lib/exec-account-home.d.ts +0 -24
- package/dist/lib/exec-account-home.js +0 -41
- package/dist/lib/exec-bounded.d.ts +1 -37
- package/dist/lib/exec-bounded.js +10 -46
- package/dist/lib/exec.d.ts +3 -583
- package/dist/lib/exec.js +18 -1025
- package/dist/lib/factory/snapshot.js +1 -15
- package/dist/lib/feed/activity-stream.d.ts +0 -27
- package/dist/lib/feed/activity-stream.js +4 -80
- package/dist/lib/feed/activity.d.ts +1 -243
- package/dist/lib/feed/activity.js +8 -273
- package/dist/lib/feed/answer.d.ts +0 -103
- package/dist/lib/feed/answer.js +0 -267
- package/dist/lib/feed/attention.d.ts +0 -56
- package/dist/lib/feed/attention.js +0 -161
- package/dist/lib/feed/envelope.d.ts +2 -17
- package/dist/lib/feed/envelope.js +0 -14
- package/dist/lib/feed/events.d.ts +0 -118
- package/dist/lib/feed/events.js +13 -213
- package/dist/lib/feed/feed.d.ts +1 -250
- package/dist/lib/feed/feed.js +24 -334
- package/dist/lib/feed/hub-server.d.ts +0 -100
- package/dist/lib/feed/hub-server.js +1 -191
- package/dist/lib/feed/hub.d.ts +0 -53
- package/dist/lib/feed/hub.js +0 -89
- package/dist/lib/feed/pr-status.d.ts +1 -19
- package/dist/lib/feed/pr-status.js +0 -18
- package/dist/lib/feed/tool-activity.d.ts +0 -59
- package/dist/lib/feed/tool-activity.js +1 -119
- package/dist/lib/feed/tools.d.ts +0 -100
- package/dist/lib/feed/tools.js +0 -89
- package/dist/lib/feed/watch.d.ts +0 -33
- package/dist/lib/feed/watch.js +2 -94
- package/dist/lib/feed-broadcast.d.ts +0 -148
- package/dist/lib/feed-broadcast.js +1 -283
- package/dist/lib/feed-outcome.d.ts +0 -50
- package/dist/lib/feed-outcome.js +1 -67
- package/dist/lib/feed-policy.d.ts +0 -6
- package/dist/lib/feed-policy.js +1 -27
- package/dist/lib/feed-post.d.ts +0 -61
- package/dist/lib/feed-post.js +4 -61
- package/dist/lib/feed-ranking.d.ts +1 -1
- package/dist/lib/fleet/apply.d.ts +0 -64
- package/dist/lib/fleet/apply.js +0 -125
- package/dist/lib/fleet/auth-sync.d.ts +0 -48
- package/dist/lib/fleet/auth-sync.js +4 -49
- package/dist/lib/fleet/capture.d.ts +0 -26
- package/dist/lib/fleet/capture.js +1 -36
- package/dist/lib/fleet/manifest.d.ts +0 -38
- package/dist/lib/fleet/manifest.js +0 -40
- package/dist/lib/fleet/types.d.ts +1 -138
- package/dist/lib/fleet/types.js +0 -12
- package/dist/lib/fleet-cache.d.ts +0 -12
- package/dist/lib/fleet-cache.js +0 -24
- package/dist/lib/fleet-shared-state.d.ts +6 -59
- package/dist/lib/fleet-shared-state.js +5 -51
- package/dist/lib/fleet-status.d.ts +0 -27
- package/dist/lib/fleet-status.js +0 -48
- package/dist/lib/format.d.ts +0 -74
- package/dist/lib/format.js +3 -77
- package/dist/lib/friction-heuristics.d.ts +0 -20
- package/dist/lib/friction-heuristics.js +0 -8
- package/dist/lib/fs-atomic.d.ts +0 -51
- package/dist/lib/fs-atomic.js +8 -66
- package/dist/lib/fs-walk.d.ts +0 -13
- package/dist/lib/fs-walk.js +0 -22
- package/dist/lib/fuzzy.d.ts +0 -29
- package/dist/lib/fuzzy.js +0 -31
- package/dist/lib/gemini-settings.js +0 -12
- package/dist/lib/git.d.ts +0 -366
- package/dist/lib/git.js +7 -574
- package/dist/lib/github/ci-failure.d.ts +30 -0
- package/dist/lib/github/ci-failure.js +95 -0
- package/dist/lib/github/gh-overload.d.ts +0 -38
- package/dist/lib/github/gh-overload.js +3 -55
- package/dist/lib/github/pr-mergeable.d.ts +3 -34
- package/dist/lib/github/pr-mergeable.js +2 -42
- package/dist/lib/github/pr-verdict.d.ts +0 -44
- package/dist/lib/github/pr-verdict.js +0 -36
- package/dist/lib/github/project-prs.d.ts +135 -105
- package/dist/lib/github/project-prs.js +530 -170
- package/dist/lib/github/release-drift.d.ts +45 -0
- package/dist/lib/github/release-drift.js +91 -0
- package/dist/lib/github/rest.d.ts +0 -50
- package/dist/lib/github/rest.js +0 -49
- package/dist/lib/github/viewer.d.ts +27 -0
- package/dist/lib/github/viewer.js +101 -0
- package/dist/lib/goose-commands.d.ts +0 -29
- package/dist/lib/goose-commands.js +3 -48
- package/dist/lib/harness/adapter.d.ts +0 -151
- package/dist/lib/harness/adapter.js +0 -41
- package/dist/lib/harness/adapters/claude.d.ts +0 -8
- package/dist/lib/harness/adapters/claude.js +0 -89
- package/dist/lib/harness/adapters/codex.js +0 -18
- package/dist/lib/harness/adapters/copilot.js +0 -3
- package/dist/lib/harness/adapters/cursor.js +0 -16
- package/dist/lib/harness/adapters/droid.js +0 -3
- package/dist/lib/harness/adapters/kimi.js +0 -19
- package/dist/lib/harness/adapters/muse.js +0 -9
- package/dist/lib/harness/adapters/opencode.js +0 -2
- package/dist/lib/harness/exec-config-version.d.ts +0 -2
- package/dist/lib/harness/exec-config-version.js +0 -13
- package/dist/lib/harness/index.js +0 -10
- package/dist/lib/harness-auth-capabilities.d.ts +0 -26
- package/dist/lib/harness-auth-capabilities.js +0 -18
- package/dist/lib/harness-connection-test.d.ts +0 -27
- package/dist/lib/harness-connection-test.js +2 -20
- package/dist/lib/heal.d.ts +0 -45
- package/dist/lib/heal.js +2 -82
- package/dist/lib/help.d.ts +0 -32
- package/dist/lib/help.js +0 -41
- package/dist/lib/helper-download.d.ts +0 -85
- package/dist/lib/helper-download.js +0 -95
- package/dist/lib/helper-versions.d.ts +0 -51
- package/dist/lib/helper-versions.js +1 -45
- package/dist/lib/hooks/cache.d.ts +0 -40
- package/dist/lib/hooks/cache.js +8 -122
- package/dist/lib/hooks/install.d.ts +0 -253
- package/dist/lib/hooks/install.js +49 -822
- package/dist/lib/hooks/match.d.ts +0 -24
- package/dist/lib/hooks/match.js +0 -31
- package/dist/lib/hooks/profile.d.ts +0 -21
- package/dist/lib/hooks/profile.js +0 -28
- package/dist/lib/hosts/credential-transport.d.ts +0 -6
- package/dist/lib/hosts/credential-transport.js +0 -17
- package/dist/lib/hosts/credentials.d.ts +0 -35
- package/dist/lib/hosts/credentials.js +0 -38
- package/dist/lib/hosts/dispatch.d.ts +0 -140
- package/dist/lib/hosts/dispatch.js +0 -176
- package/dist/lib/hosts/logs.d.ts +0 -25
- package/dist/lib/hosts/logs.js +1 -51
- package/dist/lib/hosts/option.d.ts +0 -12
- package/dist/lib/hosts/option.js +0 -12
- package/dist/lib/hosts/passthrough.d.ts +0 -129
- package/dist/lib/hosts/passthrough.js +12 -249
- package/dist/lib/hosts/progress.d.ts +0 -90
- package/dist/lib/hosts/progress.js +5 -115
- package/dist/lib/hosts/providers/devices.d.ts +0 -19
- package/dist/lib/hosts/providers/devices.js +1 -34
- package/dist/lib/hosts/providers/local.d.ts +0 -14
- package/dist/lib/hosts/providers/local.js +0 -22
- package/dist/lib/hosts/ready.d.ts +0 -99
- package/dist/lib/hosts/ready.js +0 -101
- package/dist/lib/hosts/reconcile.d.ts +0 -38
- package/dist/lib/hosts/reconcile.js +1 -50
- package/dist/lib/hosts/reconnect.d.ts +0 -75
- package/dist/lib/hosts/reconnect.js +0 -98
- package/dist/lib/hosts/registry.d.ts +0 -74
- package/dist/lib/hosts/registry.js +0 -110
- package/dist/lib/hosts/remote-cmd.d.ts +1 -228
- package/dist/lib/hosts/remote-cmd.js +19 -294
- package/dist/lib/hosts/remote-cmd.test-fixture.d.ts +0 -1
- package/dist/lib/hosts/remote-cmd.test-fixture.js +0 -12
- package/dist/lib/hosts/remote-os.d.ts +0 -19
- package/dist/lib/hosts/remote-os.js +0 -22
- package/dist/lib/hosts/remote-session-id.d.ts +0 -22
- package/dist/lib/hosts/remote-session-id.js +0 -24
- package/dist/lib/hosts/routing-flag.d.ts +0 -21
- package/dist/lib/hosts/routing-flag.js +0 -21
- package/dist/lib/hosts/run-target.d.ts +0 -53
- package/dist/lib/hosts/run-target.js +0 -56
- package/dist/lib/hosts/session-index.d.ts +1 -53
- package/dist/lib/hosts/session-index.js +2 -59
- package/dist/lib/hosts/session-marker.d.ts +0 -31
- package/dist/lib/hosts/session-marker.js +0 -32
- package/dist/lib/hosts/ssh-config.d.ts +0 -25
- package/dist/lib/hosts/ssh-config.js +1 -32
- package/dist/lib/hosts/tasks.d.ts +0 -54
- package/dist/lib/hosts/tasks.js +0 -35
- package/dist/lib/hosts/types.d.ts +0 -39
- package/dist/lib/hosts/types.js +0 -22
- package/dist/lib/humans.d.ts +0 -21
- package/dist/lib/humans.js +0 -30
- package/dist/lib/identity/client.d.ts +1 -35
- package/dist/lib/identity/client.js +3 -29
- package/dist/lib/identity/index.d.ts +3 -27
- package/dist/lib/identity/index.js +8 -24
- package/dist/lib/import.d.ts +0 -104
- package/dist/lib/import.js +1 -137
- package/dist/lib/installations/active-check.d.ts +0 -53
- package/dist/lib/installations/active-check.js +0 -49
- package/dist/lib/installations/index.d.ts +0 -9
- package/dist/lib/installations/index.js +0 -9
- package/dist/lib/installations/installation-lock.js +0 -8
- package/dist/lib/installations/launch-gate.d.ts +0 -56
- package/dist/lib/installations/launch-gate.js +0 -83
- package/dist/lib/installations/migrate.d.ts +0 -100
- package/dist/lib/installations/migrate.js +105 -572
- package/dist/lib/installations/native-command.d.ts +0 -1
- package/dist/lib/installations/native-command.js +0 -6
- package/dist/lib/installations/resolve.d.ts +0 -24
- package/dist/lib/installations/resolve.js +0 -24
- package/dist/lib/installations/shims.d.ts +0 -395
- package/dist/lib/installations/shims.js +30 -957
- package/dist/lib/installations/store.d.ts +0 -199
- package/dist/lib/installations/store.js +1 -297
- package/dist/lib/installations/strategies.d.ts +0 -44
- package/dist/lib/installations/strategies.js +0 -87
- package/dist/lib/installations/types.d.ts +1 -67
- package/dist/lib/installations/types.js +0 -6
- package/dist/lib/installations/update-cancellation.d.ts +0 -74
- package/dist/lib/installations/update-cancellation.js +0 -74
- package/dist/lib/installations/update-policy.d.ts +0 -57
- package/dist/lib/installations/update-policy.js +0 -62
- package/dist/lib/installations/update-runtime.d.ts +0 -88
- package/dist/lib/installations/update-runtime.js +1 -127
- package/dist/lib/installations/update.d.ts +0 -53
- package/dist/lib/installations/update.js +0 -95
- package/dist/lib/installations/versions.d.ts +0 -310
- package/dist/lib/installations/versions.js +14 -837
- package/dist/lib/isolation-boundary-report.d.ts +0 -7
- package/dist/lib/isolation-boundary-report.js +0 -7
- package/dist/lib/launch-identity.d.ts +0 -8
- package/dist/lib/launch-identity.js +0 -8
- package/dist/lib/linear-autoclose.d.ts +0 -25
- package/dist/lib/linear-autoclose.js +0 -19
- package/dist/lib/linear-cache.d.ts +0 -57
- package/dist/lib/linear-cache.js +1 -81
- package/dist/lib/linear-project-counts.d.ts +0 -83
- package/dist/lib/linear-project-counts.js +2 -64
- package/dist/lib/linear-projects.d.ts +0 -62
- package/dist/lib/linear-projects.js +0 -85
- package/dist/lib/linear-rate-limit.d.ts +0 -21
- package/dist/lib/linear-rate-limit.js +1 -93
- package/dist/lib/linux-userns.d.ts +1 -42
- package/dist/lib/linux-userns.js +0 -47
- package/dist/lib/lock-compromise.d.ts +0 -7
- package/dist/lib/lock-compromise.js +0 -7
- package/dist/lib/log-follow.d.ts +0 -2
- package/dist/lib/log-follow.js +5 -15
- package/dist/lib/loop.d.ts +0 -106
- package/dist/lib/loop.js +0 -135
- package/dist/lib/machine-id.d.ts +0 -19
- package/dist/lib/machine-id.js +0 -19
- package/dist/lib/mailbox-gc.d.ts +0 -7
- package/dist/lib/mailbox-gc.js +0 -25
- package/dist/lib/mailbox-target.d.ts +0 -31
- package/dist/lib/mailbox-target.js +0 -27
- package/dist/lib/mailbox.d.ts +0 -77
- package/dist/lib/mailbox.js +5 -105
- package/dist/lib/manifest.d.ts +0 -4
- package/dist/lib/manifest.js +0 -36
- package/dist/lib/markdown.d.ts +0 -3
- package/dist/lib/markdown.js +0 -9
- package/dist/lib/mcp-registry.d.ts +1 -50
- package/dist/lib/mcp-registry.js +0 -64
- package/dist/lib/mcp.d.ts +0 -115
- package/dist/lib/mcp.js +3 -199
- package/dist/lib/memory-cache.d.ts +0 -11
- package/dist/lib/memory-cache.js +0 -9
- package/dist/lib/memory.d.ts +0 -52
- package/dist/lib/memory.js +12 -74
- package/dist/lib/menubar/download-menubar.d.ts +0 -35
- package/dist/lib/menubar/download-menubar.js +0 -38
- package/dist/lib/menubar/install-menubar.d.ts +0 -405
- package/dist/lib/menubar/install-menubar.js +25 -667
- package/dist/lib/menubar/migrate-prefs.d.ts +0 -47
- package/dist/lib/menubar/migrate-prefs.js +1 -70
- package/dist/lib/menubar/notify-desktop.d.ts +0 -82
- package/dist/lib/menubar/notify-desktop.js +0 -68
- package/dist/lib/menubar/resolve-version.d.ts +0 -41
- package/dist/lib/menubar/resolve-version.js +2 -44
- package/dist/lib/menubar/snapshot.d.ts +13 -66
- package/dist/lib/menubar/snapshot.js +45 -39
- package/dist/lib/merged-resources.d.ts +0 -10
- package/dist/lib/merged-resources.js +0 -10
- package/dist/lib/migrate-fold.d.ts +0 -31
- package/dist/lib/migrate-fold.js +6 -38
- package/dist/lib/model-tier-overrides.d.ts +0 -30
- package/dist/lib/model-tier-overrides.js +0 -14
- package/dist/lib/model-tiers.d.ts +0 -43
- package/dist/lib/model-tiers.js +5 -78
- package/dist/lib/models.d.ts +0 -145
- package/dist/lib/models.js +3 -333
- package/dist/lib/monitors/config.d.ts +0 -187
- package/dist/lib/monitors/config.js +4 -101
- package/dist/lib/monitors/dispatch.d.ts +0 -22
- package/dist/lib/monitors/dispatch.js +0 -35
- package/dist/lib/monitors/engine.d.ts +1 -53
- package/dist/lib/monitors/engine.js +17 -106
- package/dist/lib/monitors/fingerprint.d.ts +0 -43
- package/dist/lib/monitors/fingerprint.js +0 -57
- package/dist/lib/monitors/pid-watch.d.ts +0 -31
- package/dist/lib/monitors/pid-watch.js +0 -31
- package/dist/lib/monitors/remote.d.ts +0 -52
- package/dist/lib/monitors/remote.js +0 -42
- package/dist/lib/monitors/sources/command.d.ts +2 -9
- package/dist/lib/monitors/sources/command.js +13 -34
- package/dist/lib/monitors/sources/device.d.ts +0 -10
- package/dist/lib/monitors/sources/device.js +0 -14
- package/dist/lib/monitors/sources/failure.d.ts +0 -27
- package/dist/lib/monitors/sources/failure.js +0 -27
- package/dist/lib/monitors/sources/file.d.ts +0 -10
- package/dist/lib/monitors/sources/file.js +0 -11
- package/dist/lib/monitors/sources/http.d.ts +0 -7
- package/dist/lib/monitors/sources/http.js +0 -9
- package/dist/lib/monitors/sources/index.d.ts +0 -9
- package/dist/lib/monitors/sources/index.js +0 -9
- package/dist/lib/monitors/sources/poll.d.ts +0 -8
- package/dist/lib/monitors/sources/poll.js +0 -8
- package/dist/lib/monitors/sources/types.d.ts +0 -18
- package/dist/lib/monitors/sources/types.js +0 -8
- package/dist/lib/monitors/sources/webhook.d.ts +0 -7
- package/dist/lib/monitors/sources/webhook.js +0 -7
- package/dist/lib/monitors/sources/ws.d.ts +0 -10
- package/dist/lib/monitors/sources/ws.js +3 -13
- package/dist/lib/monitors/state.d.ts +0 -132
- package/dist/lib/monitors/state.js +2 -86
- package/dist/lib/native-accounts.d.ts +0 -32
- package/dist/lib/native-accounts.js +0 -17
- package/dist/lib/net-close.d.ts +0 -9
- package/dist/lib/net-close.js +0 -5
- package/dist/lib/notify.d.ts +0 -47
- package/dist/lib/notify.js +0 -30
- package/dist/lib/onepassword.d.ts +0 -14
- package/dist/lib/onepassword.js +0 -12
- package/dist/lib/open-url.d.ts +0 -38
- package/dist/lib/open-url.js +1 -69
- package/dist/lib/openclaw-keychain.js +2 -7
- package/dist/lib/operator.d.ts +0 -9
- package/dist/lib/operator.js +0 -34
- package/dist/lib/origin-machine.d.ts +0 -16
- package/dist/lib/origin-machine.js +0 -16
- package/dist/lib/output/git-output.d.ts +0 -31
- package/dist/lib/output/git-output.js +4 -43
- package/dist/lib/overdue.d.ts +0 -35
- package/dist/lib/overdue.js +0 -60
- package/dist/lib/owner-message.d.ts +0 -20
- package/dist/lib/owner-message.js +1 -40
- package/dist/lib/packages/output-home.d.ts +0 -28
- package/dist/lib/packages/output-home.js +0 -114
- package/dist/lib/paths.d.ts +0 -26
- package/dist/lib/paths.js +1 -27
- package/dist/lib/percentile.d.ts +0 -11
- package/dist/lib/percentile.js +0 -11
- package/dist/lib/perf/db.d.ts +0 -16
- package/dist/lib/perf/db.js +3 -32
- package/dist/lib/perf/spool.d.ts +0 -12
- package/dist/lib/perf/spool.js +0 -13
- package/dist/lib/perf/types.d.ts +0 -26
- package/dist/lib/perf/types.js +0 -1
- package/dist/lib/permissions-registry.d.ts +0 -48
- package/dist/lib/permissions-registry.js +0 -108
- package/dist/lib/permissions.d.ts +0 -215
- package/dist/lib/permissions.js +4 -407
- package/dist/lib/picker.d.ts +0 -124
- package/dist/lib/picker.js +0 -140
- package/dist/lib/placement.d.ts +0 -57
- package/dist/lib/placement.js +0 -47
- package/dist/lib/platform/exec.d.ts +0 -73
- package/dist/lib/platform/exec.js +0 -80
- package/dist/lib/platform/index.d.ts +0 -14
- package/dist/lib/platform/index.js +0 -14
- package/dist/lib/platform/ipc.d.ts +0 -10
- package/dist/lib/platform/ipc.js +0 -13
- package/dist/lib/platform/links.d.ts +0 -14
- package/dist/lib/platform/links.js +0 -25
- package/dist/lib/platform/paths.d.ts +0 -48
- package/dist/lib/platform/paths.js +0 -52
- package/dist/lib/platform/posixpath.d.ts +0 -18
- package/dist/lib/platform/posixpath.js +0 -62
- package/dist/lib/platform/process.d.ts +0 -67
- package/dist/lib/platform/process.js +5 -99
- package/dist/lib/platform/winpath.d.ts +0 -50
- package/dist/lib/platform/winpath.js +0 -81
- package/dist/lib/plugins/plugin-marketplace.d.ts +0 -165
- package/dist/lib/plugins/plugin-marketplace.js +10 -238
- package/dist/lib/plugins/plugins.d.ts +0 -238
- package/dist/lib/plugins/plugins.js +20 -454
- package/dist/lib/plugins/skills.d.ts +0 -86
- package/dist/lib/plugins/skills.js +1 -139
- package/dist/lib/pr-land-detach.d.ts +0 -17
- package/dist/lib/pr-land-detach.js +0 -30
- package/dist/lib/pricing/cost.d.ts +0 -29
- package/dist/lib/pricing/cost.js +0 -38
- package/dist/lib/pricing/index.d.ts +0 -6
- package/dist/lib/pricing/index.js +0 -6
- package/dist/lib/pricing/table.d.ts +0 -8
- package/dist/lib/pricing/table.js +3 -56
- package/dist/lib/probe.d.ts +0 -5
- package/dist/lib/probe.js +0 -40
- package/dist/lib/profiles-presets.d.ts +0 -20
- package/dist/lib/profiles-presets.js +1 -36
- package/dist/lib/profiles.d.ts +0 -185
- package/dist/lib/profiles.js +1 -158
- package/dist/lib/project-doctor.d.ts +0 -29
- package/dist/lib/project-doctor.js +0 -26
- package/dist/lib/project-focus.d.ts +0 -41
- package/dist/lib/project-focus.js +0 -49
- package/dist/lib/project-import.d.ts +0 -41
- package/dist/lib/project-import.js +0 -36
- package/dist/lib/project-key.d.ts +0 -65
- package/dist/lib/project-key.js +0 -70
- package/dist/lib/project-launch.d.ts +0 -49
- package/dist/lib/project-launch.js +2 -91
- package/dist/lib/project-probe.d.ts +0 -86
- package/dist/lib/project-probe.js +1 -88
- package/dist/lib/project-pull.d.ts +0 -96
- package/dist/lib/project-pull.js +0 -122
- package/dist/lib/project-resources.d.ts +0 -44
- package/dist/lib/project-resources.js +7 -171
- package/dist/lib/project-root.d.ts +0 -95
- package/dist/lib/project-root.js +0 -101
- package/dist/lib/project-schedule.d.ts +0 -43
- package/dist/lib/project-schedule.js +0 -52
- package/dist/lib/project-status.d.ts +0 -108
- package/dist/lib/project-status.js +0 -117
- package/dist/lib/projects.d.ts +0 -197
- package/dist/lib/projects.js +1 -213
- package/dist/lib/pwsh.d.ts +0 -41
- package/dist/lib/pwsh.js +0 -51
- package/dist/lib/quick-todo.d.ts +84 -0
- package/dist/lib/quick-todo.js +243 -0
- package/dist/lib/reconcile-and-repair.d.ts +0 -78
- package/dist/lib/reconcile-and-repair.js +3 -66
- package/dist/lib/redact.d.ts +0 -29
- package/dist/lib/redact.js +0 -54
- package/dist/lib/refresh-coordinator.d.ts +0 -15
- package/dist/lib/refresh-coordinator.js +0 -10
- package/dist/lib/refresh.d.ts +0 -39
- package/dist/lib/refresh.js +0 -45
- package/dist/lib/registry.d.ts +0 -65
- package/dist/lib/registry.js +0 -127
- package/dist/lib/reminders.d.ts +0 -21
- package/dist/lib/reminders.js +0 -30
- package/dist/lib/remote-agents-json.d.ts +0 -44
- package/dist/lib/remote-agents-json.js +1 -44
- package/dist/lib/reserved-stores.d.ts +0 -28
- package/dist/lib/reserved-stores.js +2 -28
- package/dist/lib/resource-aliases.d.ts +0 -12
- package/dist/lib/resource-aliases.js +0 -12
- package/dist/lib/resource-content-diff.d.ts +0 -28
- package/dist/lib/resource-content-diff.js +7 -34
- package/dist/lib/resource-inventory.d.ts +0 -51
- package/dist/lib/resource-inventory.js +0 -45
- package/dist/lib/resource-patterns.d.ts +0 -32
- package/dist/lib/resource-patterns.js +0 -34
- package/dist/lib/resource-profiles.d.ts +0 -7
- package/dist/lib/resource-profiles.js +0 -11
- package/dist/lib/resources/types.d.ts +0 -50
- package/dist/lib/resources/types.js +0 -7
- package/dist/lib/resources/workflows.d.ts +0 -7
- package/dist/lib/resources/workflows.js +0 -13
- package/dist/lib/resources.d.ts +0 -65
- package/dist/lib/resources.js +0 -78
- package/dist/lib/routers.d.ts +0 -67
- package/dist/lib/routers.js +0 -60
- package/dist/lib/routine-activation.d.ts +0 -23
- package/dist/lib/routine-activation.js +0 -24
- package/dist/lib/routine-context.d.ts +1 -91
- package/dist/lib/routine-context.js +0 -107
- package/dist/lib/routine-notify-owner.d.ts +0 -79
- package/dist/lib/routine-notify-owner.js +1 -103
- package/dist/lib/routine-notify.d.ts +0 -70
- package/dist/lib/routine-notify.js +1 -75
- package/dist/lib/routine-process-cleanup.d.ts +0 -1
- package/dist/lib/routine-process-cleanup.js +1 -4
- package/dist/lib/routine-readiness.d.ts +0 -63
- package/dist/lib/routine-readiness.js +4 -86
- package/dist/lib/routines-format.d.ts +0 -40
- package/dist/lib/routines-format.js +0 -67
- package/dist/lib/routines-placement.d.ts +0 -33
- package/dist/lib/routines-placement.js +0 -40
- package/dist/lib/routines-project.d.ts +0 -57
- package/dist/lib/routines-project.js +6 -80
- package/dist/lib/rules/compile.d.ts +0 -66
- package/dist/lib/rules/compile.js +6 -118
- package/dist/lib/rules/compose.d.ts +0 -56
- package/dist/lib/rules/compose.js +2 -83
- package/dist/lib/rules/rules.d.ts +0 -23
- package/dist/lib/rules/rules.js +0 -35
- package/dist/lib/rules/run-sync.d.ts +0 -16
- package/dist/lib/rules/run-sync.js +1 -44
- package/dist/lib/run-config.d.ts +0 -7
- package/dist/lib/run-config.js +0 -8
- package/dist/lib/run-defaults.d.ts +0 -14
- package/dist/lib/run-defaults.js +0 -14
- package/dist/lib/run-notify.d.ts +0 -18
- package/dist/lib/run-notify.js +0 -36
- package/dist/lib/run-trace-sync.d.ts +0 -21
- package/dist/lib/run-trace-sync.js +2 -66
- package/dist/lib/rush-session.d.ts +0 -26
- package/dist/lib/rush-session.js +0 -28
- package/dist/lib/sandbox.d.ts +0 -45
- package/dist/lib/sandbox.js +5 -93
- package/dist/lib/scheduler.d.ts +0 -28
- package/dist/lib/scheduler.js +0 -45
- package/dist/lib/scheduling/routines.d.ts +0 -652
- package/dist/lib/scheduling/routines.js +8 -475
- package/dist/lib/secrets-cli.d.ts +3 -8
- package/dist/lib/secrets-cli.js +1 -19
- package/dist/lib/secrets-client.d.ts +0 -117
- package/dist/lib/secrets-client.js +4 -275
- package/dist/lib/secrets-policy.d.ts +0 -142
- package/dist/lib/secrets-policy.js +4 -237
- package/dist/lib/secrets-types.d.ts +0 -87
- package/dist/lib/secrets-types.js +0 -13
- package/dist/lib/self-heal/checks/hook-manifest.js +0 -25
- package/dist/lib/self-heal/checks/hook-runtime.js +0 -5
- package/dist/lib/self-heal/checks/install-staging.d.ts +0 -1
- package/dist/lib/self-heal/checks/install-staging.js +1 -28
- package/dist/lib/self-heal/checks/menubar-helper.js +0 -5
- package/dist/lib/self-heal/checks/path.js +0 -9
- package/dist/lib/self-heal/checks/resources.js +0 -5
- package/dist/lib/self-heal/checks/shadowing.js +2 -16
- package/dist/lib/self-heal/checks/shims.js +0 -14
- package/dist/lib/self-heal/child.d.ts +12 -0
- package/dist/lib/self-heal/child.js +23 -0
- package/dist/lib/self-heal/registry.d.ts +0 -9
- package/dist/lib/self-heal/registry.js +0 -16
- package/dist/lib/self-heal/types.d.ts +0 -12
- package/dist/lib/self-heal/types.js +0 -15
- package/dist/lib/self-update.d.ts +0 -312
- package/dist/lib/self-update.js +9 -360
- package/dist/lib/service-manifest.d.ts +0 -39
- package/dist/lib/service-manifest.js +0 -72
- package/dist/lib/session/active.d.ts +13 -869
- package/dist/lib/session/active.js +20 -976
- package/dist/lib/session/actor-sidecar.d.ts +1 -32
- package/dist/lib/session/actor-sidecar.js +1 -41
- package/dist/lib/session/bookmarks.d.ts +0 -33
- package/dist/lib/session/bookmarks.js +0 -37
- package/dist/lib/session/bundle.d.ts +0 -86
- package/dist/lib/session/bundle.js +0 -54
- package/dist/lib/session/claude-accounts.d.ts +0 -40
- package/dist/lib/session/claude-accounts.js +0 -49
- package/dist/lib/session/cloud.d.ts +1 -23
- package/dist/lib/session/cloud.js +0 -26
- package/dist/lib/session/db.d.ts +5 -658
- package/dist/lib/session/db.js +22 -1443
- package/dist/lib/session/detached.d.ts +0 -21
- package/dist/lib/session/detached.js +0 -29
- package/dist/lib/session/discover.d.ts +3 -420
- package/dist/lib/session/discover.js +30 -1123
- package/dist/lib/session/fork.d.ts +1 -41
- package/dist/lib/session/fork.js +0 -19
- package/dist/lib/session/ghostty-tabs.d.ts +0 -24
- package/dist/lib/session/ghostty-tabs.js +0 -37
- package/dist/lib/session/glance-files.d.ts +19 -0
- package/dist/lib/session/glance-files.js +258 -0
- package/dist/lib/session/hook-sessions.d.ts +0 -33
- package/dist/lib/session/hook-sessions.js +4 -99
- package/dist/lib/session/host-link.d.ts +1 -94
- package/dist/lib/session/host-link.js +1 -81
- package/dist/lib/session/inject-target.d.ts +8 -0
- package/dist/lib/session/inject-target.js +32 -0
- package/dist/lib/session/inject.d.ts +0 -15
- package/dist/lib/session/inject.js +0 -15
- package/dist/lib/session/live-metadata.d.ts +1 -34
- package/dist/lib/session/live-metadata.js +1 -73
- package/dist/lib/session/local-tmux-attach.d.ts +0 -37
- package/dist/lib/session/local-tmux-attach.js +1 -76
- package/dist/lib/session/migrate-targets.d.ts +0 -46
- package/dist/lib/session/migrate-targets.js +0 -24
- package/dist/lib/session/migrations.d.ts +0 -12
- package/dist/lib/session/migrations.js +1 -20
- package/dist/lib/session/mirror.d.ts +0 -32
- package/dist/lib/session/mirror.js +0 -77
- package/dist/lib/session/origin-machine.js +0 -3
- package/dist/lib/session/pid-registry.d.ts +0 -80
- package/dist/lib/session/pid-registry.js +2 -81
- package/dist/lib/session/presence.d.ts +0 -39
- package/dist/lib/session/presence.js +2 -52
- package/dist/lib/session/process-view.d.ts +0 -11
- package/dist/lib/session/process-view.js +0 -25
- package/dist/lib/session/projection.d.ts +0 -3
- package/dist/lib/session/projection.js +0 -10
- package/dist/lib/session/provenance.d.ts +1 -37
- package/dist/lib/session/provenance.js +0 -69
- package/dist/lib/session/recovery.d.ts +1 -40
- package/dist/lib/session/recovery.js +2 -80
- package/dist/lib/session/relative-time.js +0 -3
- package/dist/lib/session/remote/peer-stream.d.ts +0 -42
- package/dist/lib/session/remote/peer-stream.js +0 -64
- package/dist/lib/session/remote/remote-bundle.d.ts +0 -6
- package/dist/lib/session/remote/remote-bundle.js +0 -21
- package/dist/lib/session/remote/remote-list.d.ts +1 -110
- package/dist/lib/session/remote/remote-list.js +5 -139
- package/dist/lib/session/remote/remote.d.ts +0 -98
- package/dist/lib/session/remote/remote.js +3 -119
- package/dist/lib/session/remote/watch.d.ts +1 -34
- package/dist/lib/session/remote/watch.js +12 -72
- package/dist/lib/session/remote-active.d.ts +0 -35
- package/dist/lib/session/remote-active.js +0 -48
- package/dist/lib/session/remote-bundle.js +0 -2
- package/dist/lib/session/remote-list.js +0 -2
- package/dist/lib/session/remote-preview-cache.d.ts +0 -7
- package/dist/lib/session/remote-preview-cache.js +1 -76
- package/dist/lib/session/remote.js +0 -2
- package/dist/lib/session/resume-command.d.ts +0 -6
- package/dist/lib/session/resume-command.js +0 -6
- package/dist/lib/session/resume-owner.d.ts +1 -52
- package/dist/lib/session/resume-owner.js +0 -56
- package/dist/lib/session/run-names.d.ts +0 -24
- package/dist/lib/session/run-names.js +0 -26
- package/dist/lib/session/session-cache.d.ts +2 -146
- package/dist/lib/session/session-cache.js +26 -203
- package/dist/lib/session/short-id.js +0 -3
- package/dist/lib/session/sync/agents.d.ts +0 -68
- package/dist/lib/session/sync/agents.js +0 -0
- package/dist/lib/session/sync/backend.d.ts +0 -39
- package/dist/lib/session/sync/backend.js +0 -45
- package/dist/lib/session/sync/config.d.ts +0 -27
- package/dist/lib/session/sync/config.js +0 -56
- package/dist/lib/session/sync/managed-config.d.ts +0 -17
- package/dist/lib/session/sync/managed-config.js +0 -17
- package/dist/lib/session/sync/managed-key.d.ts +0 -38
- package/dist/lib/session/sync/managed-key.js +1 -42
- package/dist/lib/session/sync/net-client.d.ts +0 -36
- package/dist/lib/session/sync/net-client.js +0 -30
- package/dist/lib/session/sync/provision.d.ts +0 -2
- package/dist/lib/session/sync/provision.js +0 -13
- package/dist/lib/session/sync/r2.d.ts +0 -20
- package/dist/lib/session/sync/r2.js +0 -20
- package/dist/lib/session/sync/transcript-crypto.d.ts +0 -37
- package/dist/lib/session/sync/transcript-crypto.js +5 -37
- package/dist/lib/session/sync/worker-template.d.ts +0 -5
- package/dist/lib/session/sync/worker-template.js +0 -40
- package/dist/lib/session/testdata/pid-registry-namespace.js +0 -2
- package/dist/lib/session/testdata/process-view-private.js +0 -1
- package/dist/lib/session/throughput.d.ts +0 -24
- package/dist/lib/session/throughput.js +1 -25
- package/dist/lib/session/timeline-pass.d.ts +1 -63
- package/dist/lib/session/timeline-pass.js +161 -148
- package/dist/lib/session/title.d.ts +0 -182
- package/dist/lib/session/title.js +0 -139
- package/dist/lib/session/tool-index.d.ts +2 -21
- package/dist/lib/session/tool-index.js +3 -75
- package/dist/lib/session/tool-store.d.ts +2 -48
- package/dist/lib/session/tool-store.js +1 -33
- package/dist/lib/session/viewing-in.d.ts +0 -67
- package/dist/lib/session/viewing-in.js +1 -71
- package/dist/lib/session/watch.js +0 -2
- package/dist/lib/session/width.js +0 -3
- package/dist/lib/sessions-client.d.ts +0 -46
- package/dist/lib/sessions-client.js +36 -113
- package/dist/lib/settings-manifest.d.ts +1 -17
- package/dist/lib/settings-manifest.js +4 -57
- package/dist/lib/setup-tool-install.d.ts +0 -1
- package/dist/lib/setup-tool-install.js +2 -7
- package/dist/lib/setup-tool-status.d.ts +0 -3
- package/dist/lib/setup-tool-status.js +4 -14
- package/dist/lib/sha256-asset.d.ts +0 -20
- package/dist/lib/sha256-asset.js +0 -20
- package/dist/lib/share-runtime.d.ts +0 -10
- package/dist/lib/share-runtime.js +1 -27
- package/dist/lib/shim-heal.d.ts +0 -15
- package/dist/lib/shim-heal.js +3 -29
- package/dist/lib/signin-badge.d.ts +0 -72
- package/dist/lib/signin-badge.js +0 -86
- package/dist/lib/sink-format.d.ts +0 -32
- package/dist/lib/sink-format.js +0 -14
- package/dist/lib/smart-launch.d.ts +0 -75
- package/dist/lib/smart-launch.js +1 -83
- package/dist/lib/snapshot.d.ts +0 -46
- package/dist/lib/snapshot.js +5 -31
- package/dist/lib/spinner.d.ts +0 -37
- package/dist/lib/spinner.js +0 -37
- package/dist/lib/sqlite.d.ts +0 -13
- package/dist/lib/sqlite.js +3 -71
- package/dist/lib/ssh-exec.d.ts +0 -209
- package/dist/lib/ssh-exec.js +11 -248
- package/dist/lib/ssh-tunnel.d.ts +0 -43
- package/dist/lib/ssh-tunnel.js +2 -48
- package/dist/lib/staleness/checkers/commands.d.ts +0 -4
- package/dist/lib/staleness/checkers/commands.js +0 -4
- package/dist/lib/staleness/checkers/hooks.d.ts +0 -10
- package/dist/lib/staleness/checkers/hooks.js +0 -15
- package/dist/lib/staleness/checkers/mcp.d.ts +0 -9
- package/dist/lib/staleness/checkers/mcp.js +0 -9
- package/dist/lib/staleness/checkers/permissions.d.ts +0 -12
- package/dist/lib/staleness/checkers/permissions.js +0 -13
- package/dist/lib/staleness/checkers/plugins.d.ts +0 -8
- package/dist/lib/staleness/checkers/plugins.js +0 -8
- package/dist/lib/staleness/checkers/rules.d.ts +0 -15
- package/dist/lib/staleness/checkers/rules.js +0 -26
- package/dist/lib/staleness/checkers/skills.d.ts +0 -4
- package/dist/lib/staleness/checkers/skills.js +0 -4
- package/dist/lib/staleness/checkers/subagents.d.ts +0 -9
- package/dist/lib/staleness/checkers/subagents.js +0 -9
- package/dist/lib/staleness/checkers/types.d.ts +0 -34
- package/dist/lib/staleness/checkers/types.js +0 -19
- package/dist/lib/staleness/checkers/workflows.d.ts +0 -7
- package/dist/lib/staleness/checkers/workflows.js +0 -7
- package/dist/lib/staleness/detectors/commands.js +0 -17
- package/dist/lib/staleness/detectors/hooks.js +1 -6
- package/dist/lib/staleness/detectors/mcp.js +0 -4
- package/dist/lib/staleness/detectors/permissions.js +10 -23
- package/dist/lib/staleness/detectors/rules.js +0 -6
- package/dist/lib/staleness/detectors/skills.d.ts +0 -1
- package/dist/lib/staleness/detectors/skills.js +1 -12
- package/dist/lib/staleness/detectors/types.d.ts +0 -10
- package/dist/lib/staleness/fingerprint.d.ts +0 -19
- package/dist/lib/staleness/fingerprint.js +0 -35
- package/dist/lib/staleness/index.d.ts +0 -28
- package/dist/lib/staleness/index.js +1 -45
- package/dist/lib/staleness/layers.d.ts +0 -23
- package/dist/lib/staleness/layers.js +0 -34
- package/dist/lib/staleness/prune.d.ts +0 -71
- package/dist/lib/staleness/prune.js +2 -27
- package/dist/lib/staleness/registry.d.ts +0 -10
- package/dist/lib/staleness/registry.js +3 -33
- package/dist/lib/staleness/types.d.ts +0 -36
- package/dist/lib/staleness/types.js +0 -5
- package/dist/lib/staleness/writers/commands.js +1 -48
- package/dist/lib/staleness/writers/hooks.js +1 -18
- package/dist/lib/staleness/writers/kinds.d.ts +0 -12
- package/dist/lib/staleness/writers/kinds.js +0 -1
- package/dist/lib/staleness/writers/lazy-map.d.ts +0 -11
- package/dist/lib/staleness/writers/mcp.js +0 -13
- package/dist/lib/staleness/writers/plugins.js +0 -4
- package/dist/lib/staleness/writers/rules.d.ts +0 -1
- package/dist/lib/staleness/writers/rules.js +1 -19
- package/dist/lib/staleness/writers/skills.js +3 -14
- package/dist/lib/staleness/writers/sources.d.ts +0 -11
- package/dist/lib/staleness/writers/sources.js +0 -32
- package/dist/lib/staleness/writers/subagents.js +0 -16
- package/dist/lib/staleness/writers/types.d.ts +0 -58
- package/dist/lib/staleness/writers/workflows.js +1 -1
- package/dist/lib/standalone-tools.d.ts +24 -0
- package/dist/lib/standalone-tools.js +58 -0
- package/dist/lib/star-nudge.d.ts +0 -32
- package/dist/lib/star-nudge.js +2 -36
- package/dist/lib/startup/command-registry.d.ts +0 -45
- package/dist/lib/startup/command-registry.js +1 -46
- package/dist/lib/startup/dev-build.d.ts +0 -9
- package/dist/lib/startup/dev-build.js +1 -42
- package/dist/lib/startup/root-command.d.ts +0 -19
- package/dist/lib/startup/root-command.js +0 -19
- package/dist/lib/startup/spellcheck.d.ts +0 -13
- package/dist/lib/startup/spellcheck.js +0 -13
- package/dist/lib/state.d.ts +0 -376
- package/dist/lib/state.js +19 -737
- package/dist/lib/storage/selection.d.ts +0 -40
- package/dist/lib/storage/selection.js +0 -29
- package/dist/lib/subagents-registry.d.ts +0 -56
- package/dist/lib/subagents-registry.js +0 -99
- package/dist/lib/subagents.d.ts +0 -128
- package/dist/lib/subagents.js +1 -158
- package/dist/lib/summarizer/config.d.ts +0 -37
- package/dist/lib/summarizer/config.js +0 -37
- package/dist/lib/summarizer/pass.d.ts +0 -20
- package/dist/lib/summarizer/pass.js +1 -34
- package/dist/lib/summarizer/summarize.d.ts +1 -49
- package/dist/lib/summarizer/summarize.js +0 -35
- package/dist/lib/sync-passphrase.d.ts +0 -16
- package/dist/lib/sync-passphrase.js +1 -18
- package/dist/lib/sync-status.d.ts +0 -72
- package/dist/lib/sync-status.js +0 -55
- package/dist/lib/sync-umbrella.d.ts +0 -54
- package/dist/lib/sync-umbrella.js +0 -48
- package/dist/lib/system-run-sync.d.ts +0 -5
- package/dist/lib/system-run-sync.js +1 -31
- package/dist/lib/teams/agents.d.ts +1 -444
- package/dist/lib/teams/agents.js +11 -871
- package/dist/lib/teams/api.d.ts +2 -52
- package/dist/lib/teams/api.js +4 -63
- package/dist/lib/teams/debug.d.ts +0 -6
- package/dist/lib/teams/debug.js +0 -6
- package/dist/lib/teams/delivery.d.ts +0 -41
- package/dist/lib/teams/delivery.js +0 -32
- package/dist/lib/teams/file_ops.d.ts +0 -11
- package/dist/lib/teams/file_ops.js +0 -11
- package/dist/lib/teams/forEach.d.ts +0 -76
- package/dist/lib/teams/forEach.js +1 -77
- package/dist/lib/teams/index.d.ts +1 -7
- package/dist/lib/teams/index.js +1 -7
- package/dist/lib/teams/parsers.d.ts +0 -4
- package/dist/lib/teams/parsers.js +0 -81
- package/dist/lib/teams/persistence.d.ts +0 -2
- package/dist/lib/teams/persistence.js +0 -12
- package/dist/lib/teams/placement-probe.d.ts +0 -12
- package/dist/lib/teams/placement-probe.js +1 -51
- package/dist/lib/teams/pr-watch.d.ts +0 -107
- package/dist/lib/teams/pr-watch.js +1 -107
- package/dist/lib/teams/registry.d.ts +0 -36
- package/dist/lib/teams/registry.js +0 -47
- package/dist/lib/teams/remoteWorktree.d.ts +0 -50
- package/dist/lib/teams/remoteWorktree.js +1 -96
- package/dist/lib/teams/scheduler.d.ts +0 -111
- package/dist/lib/teams/scheduler.js +2 -125
- package/dist/lib/teams/summarizer.d.ts +0 -22
- package/dist/lib/teams/summarizer.js +6 -50
- package/dist/lib/teams/supervisor.d.ts +0 -38
- package/dist/lib/teams/supervisor.js +0 -21
- package/dist/lib/teams/worktree.d.ts +0 -95
- package/dist/lib/teams/worktree.js +3 -110
- package/dist/lib/template.d.ts +0 -21
- package/dist/lib/template.js +0 -23
- package/dist/lib/term-client.d.ts +0 -26
- package/dist/lib/term-client.js +0 -48
- package/dist/lib/term-driver.d.ts +0 -5
- package/dist/lib/term-driver.js +0 -8
- package/dist/lib/terminal/backends/ghostty.d.ts +0 -2
- package/dist/lib/terminal/backends/ghostty.js +0 -12
- package/dist/lib/terminal/backends/index.d.ts +0 -17
- package/dist/lib/terminal/backends/index.js +0 -15
- package/dist/lib/terminal/backends/iterm.d.ts +0 -2
- package/dist/lib/terminal/backends/iterm.js +0 -10
- package/dist/lib/terminal/backends/terminal-app.d.ts +0 -10
- package/dist/lib/terminal/backends/terminal-app.js +0 -30
- package/dist/lib/terminal/backends/tmux.d.ts +0 -10
- package/dist/lib/terminal/backends/tmux.js +0 -2
- package/dist/lib/terminal/backends/vscodium-agent.d.ts +0 -25
- package/dist/lib/terminal/backends/vscodium-agent.js +0 -36
- package/dist/lib/terminal/engine.d.ts +0 -17
- package/dist/lib/terminal/engine.js +0 -10
- package/dist/lib/terminal/index.d.ts +0 -8
- package/dist/lib/terminal/inject.d.ts +1 -173
- package/dist/lib/terminal/inject.js +0 -146
- package/dist/lib/terminal/policy.d.ts +0 -8
- package/dist/lib/terminal/policy.js +0 -1
- package/dist/lib/terminal/preferred.d.ts +0 -66
- package/dist/lib/terminal/preferred.js +0 -30
- package/dist/lib/terminal/quote.d.ts +0 -8
- package/dist/lib/terminal/quote.js +0 -8
- package/dist/lib/terminal/resolve.d.ts +0 -46
- package/dist/lib/terminal/resolve.js +0 -30
- package/dist/lib/terminal/run-surface.d.ts +0 -56
- package/dist/lib/terminal/run-surface.js +0 -46
- package/dist/lib/terminal/shell.d.ts +0 -3
- package/dist/lib/terminal/shell.js +0 -13
- package/dist/lib/terminal/transport.d.ts +0 -14
- package/dist/lib/terminal/transport.js +1 -30
- package/dist/lib/terminal/types.d.ts +0 -40
- package/dist/lib/terminal/types.js +0 -10
- package/dist/lib/text/relative-time.d.ts +0 -36
- package/dist/lib/text/relative-time.js +0 -43
- package/dist/lib/text/short-id.d.ts +0 -16
- package/dist/lib/text/short-id.js +0 -16
- package/dist/lib/text/width.d.ts +0 -24
- package/dist/lib/text/width.js +10 -52
- package/dist/lib/tmux/binary.d.ts +0 -36
- package/dist/lib/tmux/binary.js +0 -28
- package/dist/lib/tmux/index.d.ts +0 -5
- package/dist/lib/tmux/index.js +0 -5
- package/dist/lib/tmux/orphan-reap.d.ts +0 -253
- package/dist/lib/tmux/orphan-reap.js +3 -299
- package/dist/lib/tmux/paths.d.ts +0 -14
- package/dist/lib/tmux/paths.js +0 -14
- package/dist/lib/tmux/session.d.ts +0 -263
- package/dist/lib/tmux/session.js +6 -344
- package/dist/lib/traces/backend.d.ts +0 -20
- package/dist/lib/traces/backend.js +0 -23
- package/dist/lib/traces/classify.d.ts +0 -20
- package/dist/lib/traces/classify.js +0 -7
- package/dist/lib/traces/insights.d.ts +1 -84
- package/dist/lib/traces/insights.js +0 -126
- package/dist/lib/traces/phenotype.d.ts +0 -69
- package/dist/lib/traces/phenotype.js +0 -150
- package/dist/lib/traces/provision.d.ts +0 -1
- package/dist/lib/traces/provision.js +0 -5
- package/dist/lib/traces/schema2-build.d.ts +3 -60
- package/dist/lib/traces/schema2-build.js +8 -149
- package/dist/lib/traces/schema2-danger.d.ts +0 -30
- package/dist/lib/traces/schema2-danger.js +1 -63
- package/dist/lib/traces/schema2.d.ts +0 -11
- package/dist/lib/traces/segments.d.ts +0 -44
- package/dist/lib/traces/segments.js +0 -65
- package/dist/lib/traces/sync.d.ts +2 -239
- package/dist/lib/traces/sync.js +15 -237
- package/dist/lib/traces/worker-template.d.ts +0 -3
- package/dist/lib/traces/worker-template.js +0 -29
- package/dist/lib/triggers/handlers.d.ts +0 -82
- package/dist/lib/triggers/handlers.js +0 -75
- package/dist/lib/triggers/webhook.d.ts +0 -158
- package/dist/lib/triggers/webhook.js +0 -143
- package/dist/lib/types.d.ts +0 -736
- package/dist/lib/types.js +0 -50
- package/dist/lib/uninstall.d.ts +0 -17
- package/dist/lib/uninstall.js +0 -75
- package/dist/lib/usage-backoff.d.ts +0 -20
- package/dist/lib/usage-backoff.js +3 -60
- package/dist/lib/usage-refresh.d.ts +0 -154
- package/dist/lib/usage-refresh.js +8 -212
- package/dist/lib/verbs.d.ts +0 -12
- package/dist/lib/verbs.js +0 -12
- package/dist/lib/version-duplicates.d.ts +0 -6
- package/dist/lib/version-duplicates.js +0 -6
- package/dist/lib/version.d.ts +0 -55
- package/dist/lib/version.js +1 -58
- package/dist/lib/view-types.d.ts +0 -48
- package/dist/lib/warn-unpushed.d.ts +0 -27
- package/dist/lib/warn-unpushed.js +1 -45
- package/dist/lib/watchdog/history.d.ts +0 -1
- package/dist/lib/watchdog/history.js +0 -3
- package/dist/lib/watchdog/index.js +0 -2
- package/dist/lib/watchdog/log.d.ts +0 -19
- package/dist/lib/watchdog/log.js +0 -34
- package/dist/lib/watchdog/read.d.ts +0 -27
- package/dist/lib/watchdog/read.js +3 -58
- package/dist/lib/watchdog/rotate.d.ts +0 -139
- package/dist/lib/watchdog/rotate.js +2 -151
- package/dist/lib/watchdog/runner.d.ts +0 -125
- package/dist/lib/watchdog/runner.js +3 -223
- package/dist/lib/watchdog/service.d.ts +0 -1
- package/dist/lib/watchdog/service.js +0 -2
- package/dist/lib/watchdog/watchdog-agent.d.ts +0 -31
- package/dist/lib/watchdog/watchdog-agent.js +0 -27
- package/dist/lib/watchdog/watchdog.d.ts +0 -7
- package/dist/lib/watchdog/watchdog.js +0 -10
- package/dist/lib/watchdog/watchdogTail.js +0 -7
- package/dist/lib/whats-new.d.ts +0 -10
- package/dist/lib/whats-new.js +0 -21
- package/dist/lib/workflows-registry.d.ts +0 -45
- package/dist/lib/workflows-registry.js +0 -60
- package/dist/lib/workflows.d.ts +0 -282
- package/dist/lib/workflows.js +3 -213
- package/dist/lib/worktree/held.d.ts +0 -89
- package/dist/lib/worktree/held.js +1 -103
- package/dist/lib/wrap.d.ts +0 -32
- package/dist/lib/wrap.js +0 -32
- package/dist/lib/yaml-io.d.ts +0 -45
- package/dist/lib/yaml-io.js +0 -47
- package/dist/session-tracker/dist/adapters/claude.js +0 -2
- package/dist/session-tracker/dist/hook.sh +4 -37
- package/dist/session-tracker/dist/index.d.ts +0 -7
- package/dist/session-tracker/dist/index.js +0 -5
- package/dist/session-tracker/dist/install-hook.d.ts +0 -5
- package/dist/session-tracker/dist/install-hook.js +1 -15
- package/dist/session-tracker/dist/prune-state.js +0 -4
- package/dist/session-tracker/dist/reader.js +0 -4
- package/dist/session-tracker/dist/state-file.d.ts +0 -3
- package/dist/session-tracker/dist/state-file.js +3 -8
- package/package.json +3 -4
- package/scripts/postinstall.js +8 -116
- package/dist/lib/session/artifacts.d.ts +0 -14
- package/dist/lib/session/artifacts.js +0 -101
- package/dist/lib/session/bash-command.d.ts +0 -63
- package/dist/lib/session/bash-command.js +0 -490
- package/dist/lib/session/digest.d.ts +0 -66
- package/dist/lib/session/digest.js +0 -328
- package/dist/lib/session/highlights.d.ts +0 -151
- package/dist/lib/session/highlights.js +0 -391
- package/dist/lib/session/insights.d.ts +0 -165
- package/dist/lib/session/insights.js +0 -563
- package/dist/lib/session/linear.d.ts +0 -19
- package/dist/lib/session/linear.js +0 -99
- package/dist/lib/session/parse.d.ts +0 -224
- package/dist/lib/session/parse.js +0 -2408
- package/dist/lib/session/prompt.d.ts +0 -213
- package/dist/lib/session/prompt.js +0 -601
- package/dist/lib/session/render.d.ts +0 -133
- package/dist/lib/session/render.js +0 -1047
- package/dist/lib/session/share-html.d.ts +0 -35
- package/dist/lib/session/share-html.js +0 -319
- package/dist/lib/session/shell-programs.d.ts +0 -33
- package/dist/lib/session/shell-programs.js +0 -380
- package/dist/lib/session/state.d.ts +0 -269
- package/dist/lib/session/state.js +0 -777
- package/dist/lib/session/stream-render.d.ts +0 -4
- package/dist/lib/session/stream-render.js +0 -131
- package/dist/lib/session/tail.d.ts +0 -88
- package/dist/lib/session/tail.js +0 -294
- package/dist/lib/session/team-filter.d.ts +0 -129
- package/dist/lib/session/team-filter.js +0 -269
- package/dist/lib/session/timeline.d.ts +0 -172
- package/dist/lib/session/timeline.js +0 -636
- package/dist/lib/session/tool-calls.d.ts +0 -132
- package/dist/lib/session/tool-calls.js +0 -674
- package/dist/lib/session/trajectory-compare.d.ts +0 -48
- package/dist/lib/session/trajectory-compare.js +0 -116
- package/dist/lib/session/trajectory-html.d.ts +0 -19
- package/dist/lib/session/trajectory-html.js +0 -771
- package/dist/lib/session/trajectory-lineage.d.ts +0 -93
- package/dist/lib/session/trajectory-lineage.js +0 -195
- package/dist/lib/session/trajectory-text.d.ts +0 -38
- package/dist/lib/session/trajectory-text.js +0 -284
- package/dist/lib/session/trajectory.d.ts +0 -145
- package/dist/lib/session/trajectory.js +0 -394
- package/dist/lib/session/types.d.ts +0 -627
- package/dist/lib/session/types.js +0 -43
|
@@ -1,22 +1,4 @@
|
|
|
1
1
|
import { writerProcessView } from './process-view.js';
|
|
2
|
-
/**
|
|
3
|
-
* Active-session detection across every context an agent can run in:
|
|
4
|
-
*
|
|
5
|
-
* - `terminal` — agents launched from VS Code / Cursor / Codium via the
|
|
6
|
-
* agents-cli extension. Published to `~/.agents/.cache/terminals/live-terminals.json`
|
|
7
|
-
* with PID + session UUID per entry.
|
|
8
|
-
* - `teams` — agents spawned by `agents teams add`, tracked in
|
|
9
|
-
* `~/.agents/teams/agents/<id>/meta.json` with a PID the manager polls.
|
|
10
|
-
* - `cloud` — dispatched to Rush / Codex Cloud / Factory, tracked in
|
|
11
|
-
* the SQLite cache at `~/.agents/cloud/tasks.db`.
|
|
12
|
-
* - `headless` — bare `claude` / `codex` / `gemini` / `cursor-agent` /
|
|
13
|
-
* `opencode` processes that don't belong to any of the above. Detected
|
|
14
|
-
* by `ps` minus the PIDs we've already attributed.
|
|
15
|
-
*
|
|
16
|
-
* `running` vs `idle` is a secondary classification within the alive set:
|
|
17
|
-
* the process is holding its session file, but the file's mtime is older
|
|
18
|
-
* than ACTIVE_MTIME_WINDOW_MS, so it's probably waiting on the user.
|
|
19
|
-
*/
|
|
20
2
|
import * as fs from 'fs';
|
|
21
3
|
import * as path from 'path';
|
|
22
4
|
import * as os from 'os';
|
|
@@ -33,12 +15,12 @@ import { loadHookSessionIndex, resolveHookSessionRecord, readStateSessionRecord
|
|
|
33
15
|
import { buildClaudeLabelMap, getAgentSessionDirs } from './discover.js';
|
|
34
16
|
import { buildRunNameMap } from './run-names.js';
|
|
35
17
|
import { latestSessionFileForCwd, findSessionsByShortIds, findSessionMachinesByIds, getSessionById } from './db.js';
|
|
36
|
-
import { extractSessionTopic, classifyUserPrompt, tidyRequest } from '
|
|
37
|
-
import { readSessionTailWithRaw } from '
|
|
38
|
-
import { parseSession } from '
|
|
18
|
+
import { extractSessionTopic, classifyUserPrompt, tidyRequest } from '@phnx-labs/sessions-cli/reader';
|
|
19
|
+
import { readSessionTailWithRaw } from '@phnx-labs/sessions-cli/reader';
|
|
20
|
+
import { parseSession } from '@phnx-labs/sessions-cli/reader';
|
|
39
21
|
import { computeTokPerSec } from './throughput.js';
|
|
40
|
-
import { inferSessionState } from '
|
|
41
|
-
import { isSessionTrackedAgent, SESSION_AGENTS, AG_TMUX_NAME_RE } from '
|
|
22
|
+
import { inferSessionState } from '@phnx-labs/sessions-cli/reader';
|
|
23
|
+
import { isSessionTrackedAgent, SESSION_AGENTS, AG_TMUX_NAME_RE } from '@phnx-labs/sessions-cli/reader';
|
|
42
24
|
import { AGENTS } from '../agents.js';
|
|
43
25
|
import { confirmedProjectForCwd, listProjectDefsCached } from '../projects.js';
|
|
44
26
|
import { detectProvenance } from './provenance.js';
|
|
@@ -47,50 +29,20 @@ import { machineId, normalizeHost } from '../machine-id.js';
|
|
|
47
29
|
import { presenceFromStore } from './detached.js';
|
|
48
30
|
import { classifyHostLink, hostWindowLost, HOST_HEARTBEAT_STALE_MS } from './host-link.js';
|
|
49
31
|
import { mapBounded } from '../concurrency.js';
|
|
50
|
-
import { linearIssueUrl } from '
|
|
32
|
+
import { linearIssueUrl } from '@phnx-labs/sessions-cli/reader';
|
|
51
33
|
import { viewingInLabel } from './viewing-in.js';
|
|
52
34
|
import { claudeProjectDirName } from '../project-key.js';
|
|
53
35
|
const execFileAsync = promisify(execFile);
|
|
54
|
-
/** Prefer live actor attribution; the durable sidecar survives actor-less hook rewrites. */
|
|
55
36
|
export function resolveOwner(pidActor, sessionId) {
|
|
56
37
|
return pidActor ?? (sessionId ? readSessionActorRecord(sessionId)?.actor : undefined) ?? undefined;
|
|
57
38
|
}
|
|
58
|
-
/**
|
|
59
|
-
* Per-PID `lsof` probes run bounded and staggered rather than as one parallel
|
|
60
|
-
* fan-out: a simultaneous system-wide `lsof` burst reads to behavioral EDR
|
|
61
|
-
* (CrowdStrike Falcon) as lateral-movement recon. Results are identical — the
|
|
62
|
-
* cwds are just gathered at a bounded spawn rate instead of a single burst.
|
|
63
|
-
*/
|
|
64
39
|
export const LSOF_CONCURRENCY = 4;
|
|
65
40
|
const LSOF_STAGGER_MS = 10;
|
|
66
|
-
/**
|
|
67
|
-
* Hard ceilings on the two syscalls the status path shells out to. Without them
|
|
68
|
-
* a single hung probe (a wedged NFS `lsof`, an EDR that stalls the `ps` snapshot)
|
|
69
|
-
* pins a bounded worker slot forever and silently drops live sessions to a
|
|
70
|
-
* fallback status. On timeout the call rejects, is caught, and the row degrades
|
|
71
|
-
* honestly (unknown / empty table) instead of the sweep hanging.
|
|
72
|
-
*/
|
|
73
41
|
const LSOF_TIMEOUT_MS = 5_000;
|
|
74
42
|
const PS_SNAPSHOT_TIMEOUT_MS = 10_000;
|
|
75
|
-
/**
|
|
76
|
-
* Process-local process-table memo (#2047). One `getActiveSessions` scan can
|
|
77
|
-
* call `ps -A` from the terminal path, the headless path, and host ancestry —
|
|
78
|
-
* without this, each is a full process-table snapshot. 5s is well under the
|
|
79
|
-
* ~10–30s menu-bar / `--active` poll so a quiet re-poll usually reuses the
|
|
80
|
-
* snapshot; a brand-new agent pid still appears on the next full refresh.
|
|
81
|
-
*/
|
|
82
43
|
export const PROCESS_TABLE_FRESH_MS = 5_000;
|
|
83
|
-
/**
|
|
84
|
-
* How long {@link listUnattributedActive} may reuse its last full `ps`+`lsof`
|
|
85
|
-
* result (#2047). Between full rescans the previous rows are re-emitted after
|
|
86
|
-
* dropping dead PIDs and PIDs that are now attributed. A *shrinking* attributed
|
|
87
|
-
* set forces a rescan so a process that just left teams/terminals can reappear
|
|
88
|
-
* as headless instead of vanishing until the next cold poll.
|
|
89
|
-
*/
|
|
90
44
|
export const UNATTRIBUTED_RESCAN_MS = 15_000;
|
|
91
|
-
/** Injectable clock for the active-scan memos (tests only). */
|
|
92
45
|
let activeScanNow = () => Date.now();
|
|
93
|
-
/** Test seam: override the clock used by process-table / unattributed memos. */
|
|
94
46
|
export function setActiveScanClockForTest(fn) {
|
|
95
47
|
activeScanNow = fn ?? (() => Date.now());
|
|
96
48
|
}
|
|
@@ -98,7 +50,6 @@ let processTableCache;
|
|
|
98
50
|
let processTableLiveReads = 0;
|
|
99
51
|
let unattributedCache;
|
|
100
52
|
let unattributedFullRescans = 0;
|
|
101
|
-
/** Test seam: drop the process-table + unattributed memos and their counters. */
|
|
102
53
|
export function clearActiveScanCachesForTest() {
|
|
103
54
|
processTableCache = undefined;
|
|
104
55
|
processTableLiveReads = 0;
|
|
@@ -106,18 +57,12 @@ export function clearActiveScanCachesForTest() {
|
|
|
106
57
|
unattributedFullRescans = 0;
|
|
107
58
|
activeScanNow = () => Date.now();
|
|
108
59
|
}
|
|
109
|
-
/** Test seam: how many times the live `ps` / CIM path actually ran. */
|
|
110
60
|
export function processTableLiveReadCountForTest() {
|
|
111
61
|
return processTableLiveReads;
|
|
112
62
|
}
|
|
113
|
-
/** Test seam: how many full unattributed (ps + lsof) rescans ran. */
|
|
114
63
|
export function unattributedFullRescanCountForTest() {
|
|
115
64
|
return unattributedFullRescans;
|
|
116
65
|
}
|
|
117
|
-
/**
|
|
118
|
-
* True when any pid in `prev` is absent from `next` — the attributed set
|
|
119
|
-
* shrank, so a process may need to reappear as unattributed. Pure for tests.
|
|
120
|
-
*/
|
|
121
66
|
export function attributedSetLostPids(prev, next) {
|
|
122
67
|
for (const p of prev) {
|
|
123
68
|
if (!next.has(p))
|
|
@@ -125,12 +70,6 @@ export function attributedSetLostPids(prev, next) {
|
|
|
125
70
|
}
|
|
126
71
|
return false;
|
|
127
72
|
}
|
|
128
|
-
/**
|
|
129
|
-
* Drop rows whose pid is now attributed or no longer alive. Pure over the
|
|
130
|
-
* inputs so the unattributed TTL reuse path is unit-testable without a live
|
|
131
|
-
* process table. `alive` receives the row's `startedAtMs` so callers can apply
|
|
132
|
-
* the same pid-reuse guard {@link isPidAlive} uses elsewhere.
|
|
133
|
-
*/
|
|
134
73
|
export function filterCachedUnattributed(sessions, attributed, alive, ppidMap) {
|
|
135
74
|
return sessions.filter((s) => {
|
|
136
75
|
if (s.pid == null)
|
|
@@ -140,14 +79,6 @@ export function filterCachedUnattributed(sessions, attributed, alive, ppidMap) {
|
|
|
140
79
|
return alive(s.pid, s.startedAtMs);
|
|
141
80
|
});
|
|
142
81
|
}
|
|
143
|
-
/**
|
|
144
|
-
* Fold index-only enrichment onto live rows, then RE-DERIVE the recap for every
|
|
145
|
-
* row it touched. The re-derive is load-bearing: `foldRecap` runs inside
|
|
146
|
-
* {@link getActiveSessions}, before any caller reaches the index, so a `label`
|
|
147
|
-
* or `generatedTitle` that only the index knows would otherwise never reach the
|
|
148
|
-
* shown `title` (PHNX-3797). Doing it here rather than at each of the three
|
|
149
|
-
* backfill call sites is what keeps them from drifting apart.
|
|
150
|
-
*/
|
|
151
82
|
export function backfillActiveRowsFromMeta(sessions, metaById) {
|
|
152
83
|
const accounts = listNativeAccounts(readMeta());
|
|
153
84
|
for (const s of sessions) {
|
|
@@ -190,8 +121,6 @@ export function backfillActiveRowsFromMeta(sessions, metaById) {
|
|
|
190
121
|
s.durationMs = m.durationMs;
|
|
191
122
|
if (s.subAgentCount == null && m.subAgentCount != null)
|
|
192
123
|
s.subAgentCount = m.subAgentCount;
|
|
193
|
-
// A pane kept open for days has a start time but no activity signal of its
|
|
194
|
-
// own; the index's last transcript write is what "idle 14d" is read from.
|
|
195
124
|
if (s.lastActivityMs == null && m.lastActivity) {
|
|
196
125
|
const ts = Date.parse(m.lastActivity);
|
|
197
126
|
if (!Number.isNaN(ts))
|
|
@@ -200,7 +129,6 @@ export function backfillActiveRowsFromMeta(sessions, metaById) {
|
|
|
200
129
|
applyRecap(s);
|
|
201
130
|
}
|
|
202
131
|
}
|
|
203
|
-
/** Resolve a display name only when the indexed identity names one registered slot. */
|
|
204
132
|
export function sessionAccountLabel(kind, session, accounts = listNativeAccounts(readMeta())) {
|
|
205
133
|
const matches = accounts.filter(account => {
|
|
206
134
|
if (account.agent !== kind)
|
|
@@ -208,7 +136,6 @@ export function sessionAccountLabel(kind, session, accounts = listNativeAccounts
|
|
|
208
136
|
if (session.accountId)
|
|
209
137
|
return account.id === session.accountId;
|
|
210
138
|
if (session.accountKey && !session.accountKey.startsWith('unattributed:')) {
|
|
211
|
-
// Claude's index groups quota by org; the registry names a login within it.
|
|
212
139
|
const indexed = parseNativeIdentityKey(account.agent, session.accountKey);
|
|
213
140
|
const emailMatches = !!session.account && account.identityLabel?.toLowerCase() === session.account.toLowerCase();
|
|
214
141
|
if (!indexed || (!indexed.account && !emailMatches))
|
|
@@ -236,7 +163,6 @@ function loadBackfillMetaFor(sessions) {
|
|
|
236
163
|
}
|
|
237
164
|
}
|
|
238
165
|
catch {
|
|
239
|
-
/* enrichment is best-effort — an unavailable DB leaves rows un-backfilled */
|
|
240
166
|
}
|
|
241
167
|
return byId;
|
|
242
168
|
}
|
|
@@ -248,6 +174,7 @@ export function isRunningLiveSession(s) {
|
|
|
248
174
|
return false;
|
|
249
175
|
if (s.context === 'cloud')
|
|
250
176
|
return Boolean(s.cloudProvider) && Boolean(s.cloudTaskId);
|
|
177
|
+
// Unknown liveness from an older peer is not enough for bare --active.
|
|
251
178
|
return Boolean(s.machine) && typeof s.pid === 'number' && s.pid > 0 && s.pidAlive === true;
|
|
252
179
|
}
|
|
253
180
|
export function activeSessionProjectKey(s) {
|
|
@@ -256,16 +183,11 @@ export function activeSessionProjectKey(s) {
|
|
|
256
183
|
return s.context === 'cloud' ? 'cloud' : 'other';
|
|
257
184
|
}
|
|
258
185
|
export function serializeActiveSessionsForJson(sessions) {
|
|
259
|
-
// One definition read for the whole batch, not per row (PHNX-3999 F08/F09).
|
|
260
186
|
const defs = listProjectDefsCached();
|
|
261
187
|
return sessions.map((s) => ({
|
|
262
188
|
...s,
|
|
263
189
|
ticketId: s.ticket?.id ?? null,
|
|
264
190
|
project: activeSessionProjectKey(s),
|
|
265
|
-
// The project a person is shown this row under, or null for Uncategorized.
|
|
266
|
-
// `project` above is the always-present join KEY (basename of the cwd, or the
|
|
267
|
-
// explicit cloud/other bucket) and stays exactly as it was — grouping by it is
|
|
268
|
-
// what filed unbound directories as projects of their own.
|
|
269
191
|
confirmedProject: confirmedProjectForCwd(s.cwd, defs) ?? null,
|
|
270
192
|
prLink: s.pr?.url ?? null,
|
|
271
193
|
viewingIn: viewingInLabel(s) ?? null,
|
|
@@ -291,88 +213,20 @@ export function activeStatusFromCloudStatus(status) {
|
|
|
291
213
|
}
|
|
292
214
|
}
|
|
293
215
|
const LIVE_TERMINALS_FILE = path.join(getTerminalsDir(), 'live-terminals.json');
|
|
294
|
-
/**
|
|
295
|
-
* A process is classified `running` if its session file was touched in the
|
|
296
|
-
* last 2 minutes. Every Claude/Codex tool-call appends an event, so a
|
|
297
|
-
* healthy session writes several times a minute.
|
|
298
|
-
*/
|
|
299
216
|
const ACTIVE_MTIME_WINDOW_MS = 2 * 60_000;
|
|
300
|
-
/**
|
|
301
|
-
* Bound on the tmux `list-panes` call in {@link listTmuxAgentSessions}. A wedged
|
|
302
|
-
* tmux server would otherwise hang the whole `--active` scan (the other sources
|
|
303
|
-
* can't run past it); on timeout the tmux source throws {@link TmuxDiscoveryDegradedError}.
|
|
304
|
-
*/
|
|
305
217
|
const TMUX_LIST_PANES_TIMEOUT_MS = 5_000;
|
|
306
|
-
/**
|
|
307
|
-
* Thrown by {@link listTmuxAgentSessions} when the socket exists (a server has
|
|
308
|
-
* run) but tmux could not be asked for its truth — spawn failure, timeout, or a
|
|
309
|
-
* nonzero exit. Distinct from a missing socket, which means no server ever ran
|
|
310
|
-
* and is genuinely empty, not degraded. `getActiveSessions` still swallows this
|
|
311
|
-
* to `[]` so its return type stays a plain session array, but
|
|
312
|
-
* {@link describeActiveDiscoveryHealth} re-probes and surfaces it so the empty
|
|
313
|
-
* case a real fleet sweep found (RUSH-2507) reads differently from a clean one.
|
|
314
|
-
*/
|
|
315
218
|
export class TmuxDiscoveryDegradedError extends Error {
|
|
316
219
|
constructor(message) {
|
|
317
220
|
super(message);
|
|
318
221
|
this.name = 'TmuxDiscoveryDegradedError';
|
|
319
222
|
}
|
|
320
223
|
}
|
|
321
|
-
/**
|
|
322
|
-
* A live process can only borrow an indexed session file if that transcript
|
|
323
|
-
* has been touched recently enough to plausibly belong to the process. This is
|
|
324
|
-
* deliberately wider than ACTIVE_MTIME_WINDOW_MS: an inactive-but-live CLI can
|
|
325
|
-
* be idle for longer than 2 minutes, but it must not attach to a weeks-old
|
|
326
|
-
* transcript just because a GUI app service with the same basename is alive.
|
|
327
|
-
*/
|
|
328
224
|
const ACTIVE_SESSION_STALE_MS = 24 * 60 * 60_000;
|
|
329
|
-
/**
|
|
330
|
-
* A session whose transcript hasn't been written in this long is ABANDONED /
|
|
331
|
-
* dangling — the framework can no longer treat it as live work, whether its PID
|
|
332
|
-
* is dead (long gone) or still alive but making no progress (a hung agent).
|
|
333
|
-
* Two days: past a normal work gap, well short of a session legitimately kept
|
|
334
|
-
* open across a long weekend. Deliberately far wider than
|
|
335
|
-
* {@link ACTIVE_SESSION_STALE_MS} (which bounds transcript-borrowing, a different
|
|
336
|
-
* concern) — this is the lifecycle threshold, not the freshness window.
|
|
337
|
-
*/
|
|
338
225
|
export const ABANDONED_STALE_MS = 2 * 24 * 60 * 60_000;
|
|
339
|
-
/**
|
|
340
|
-
* Field separator for every `tmux list-panes -F` query here.
|
|
341
|
-
*
|
|
342
|
-
* NOT a tab. tmux sanitizes non-printable characters out of format output (3.6a
|
|
343
|
-
* rewrites a literal tab — and any non-ASCII sentinel — to `_`), so a
|
|
344
|
-
* tab-separated format comes back as one unsplittable field.
|
|
345
|
-
* {@link listTmuxAgentSessions} split on `\t`, so on such a tmux every line
|
|
346
|
-
* failed its `sessName` guard and the function returned ZERO rows, silently
|
|
347
|
-
* losing the authoritative tmux source (exact `%pane`, real identities) on every
|
|
348
|
-
* box running a recent tmux.
|
|
349
|
-
*
|
|
350
|
-
* `:` specifically, and not some other printable: tmux itself replaces `:` (and
|
|
351
|
-
* `.`) in a session name with `_`, so the separator provably cannot occur inside
|
|
352
|
-
* the one free-text field that is not last. A path CAN contain `:`, which is why
|
|
353
|
-
* `pane_current_path` is queried last and its tail rejoined rather than
|
|
354
|
-
* destructured. A separator that a session name may contain — `|`, say — would
|
|
355
|
-
* just reintroduce the same class of bug with a lower probability.
|
|
356
|
-
*/
|
|
357
226
|
const TMUX_FIELD_SEP = ':';
|
|
358
|
-
/**
|
|
359
|
-
* Process comm names that are NOT derivable from the AGENTS registry's
|
|
360
|
-
* `cliCommand`. `rush` is a {@link SESSION_AGENTS} member with no AGENTS registry
|
|
361
|
-
* row (it is the Rush app, not a managed harness), so its executable name is
|
|
362
|
-
* mapped explicitly here. Everything else flows from the registry below.
|
|
363
|
-
*/
|
|
364
227
|
const EXTRA_SESSION_AGENT_COMMS = {
|
|
365
228
|
rush: ['rush'],
|
|
366
229
|
};
|
|
367
|
-
/**
|
|
368
|
-
* Every process executable ("comm") name that identifies a given session-agent
|
|
369
|
-
* kind in the headless `ps`-scan. Driven off the AGENTS registry's `cliCommand`
|
|
370
|
-
* (the real executable name — e.g. `agy` for antigravity, `cursor-agent` for
|
|
371
|
-
* cursor) plus {@link EXTRA_SESSION_AGENT_COMMS} for members with no registry
|
|
372
|
-
* row. Exported so the completeness test can assert every {@link SESSION_AGENTS}
|
|
373
|
-
* member resolves — the fix for the harness-parity gap where bare-headless
|
|
374
|
-
* grok/kimi/antigravity/openclaw/hermes/rush were silently dropped.
|
|
375
|
-
*/
|
|
376
230
|
export function sessionAgentComms(id) {
|
|
377
231
|
const comms = new Set();
|
|
378
232
|
const cli = AGENTS[id]?.cliCommand;
|
|
@@ -382,12 +236,6 @@ export function sessionAgentComms(id) {
|
|
|
382
236
|
comms.add(extra);
|
|
383
237
|
return [...comms];
|
|
384
238
|
}
|
|
385
|
-
/**
|
|
386
|
-
* Executables we recognize as agent CLIs when scanning the process table. Built
|
|
387
|
-
* once from {@link sessionAgentComms} across {@link SESSION_AGENTS} — a single
|
|
388
|
-
* derived source, not a second hand-maintained allowlist that drifts from the
|
|
389
|
-
* discovery surface (the harness-parity code-review rule).
|
|
390
|
-
*/
|
|
391
239
|
const AGENT_CLI_NAMES = (() => {
|
|
392
240
|
const map = {};
|
|
393
241
|
for (const id of SESSION_AGENTS) {
|
|
@@ -396,56 +244,23 @@ const AGENT_CLI_NAMES = (() => {
|
|
|
396
244
|
}
|
|
397
245
|
return map;
|
|
398
246
|
})();
|
|
399
|
-
/**
|
|
400
|
-
* Resolve an agent kind from a process's reported executable. `comm` may be an
|
|
401
|
-
* absolute path (shim-launched agents), and Windows image names carry an
|
|
402
|
-
* `.exe` suffix (`claude.exe`), so basename + suffix-strip before the lookup.
|
|
403
|
-
*/
|
|
404
247
|
export function agentKindFromComm(commRaw) {
|
|
405
|
-
// A GUI desktop app can bundle a binary with the SAME name as an agent CLI: the
|
|
406
|
-
// Codex desktop app ships `/Applications/Codex.app/Contents/Resources/codex` (its
|
|
407
|
-
// `app-server`), whose basename `codex` would otherwise match the codex CLI and
|
|
408
|
-
// surface the app's background server as a phantom agent session — running at cwd
|
|
409
|
-
// '/', so it shows up unattributed in the feed. A real agent CLI is never inside a
|
|
410
|
-
// `.app` bundle, so exclude those. (The Claude desktop app is a separate case,
|
|
411
|
-
// already excluded by name below: its process is 'Claude', not the CLI's 'claude'.)
|
|
412
248
|
if (commRaw.includes('.app/Contents/'))
|
|
413
249
|
return undefined;
|
|
414
250
|
const base = path.basename(commRaw);
|
|
415
251
|
const stripped = base.replace(/\.exe$/i, '');
|
|
416
|
-
// Windows image names compare case-insensitively; POSIX comms stay exact —
|
|
417
|
-
// macOS's Claude desktop app process is named 'Claude' and must NOT match.
|
|
418
252
|
const key = stripped === base ? base : stripped.toLowerCase();
|
|
419
253
|
return AGENT_CLI_NAMES[key];
|
|
420
254
|
}
|
|
421
|
-
/**
|
|
422
|
-
* A tmux agent session name is `ag-<agent>-<shortid>` (see src/lib/exec.ts
|
|
423
|
-
* `runInTmux`), where `<agent>` is the agent kind passed to `agents run` (may
|
|
424
|
-
* contain a hyphen, e.g. `cursor-agent`) and `<shortid>` is the first 8 hex chars
|
|
425
|
-
* of the session UUID. Anchored on the 8-hex suffix so the agent part is split
|
|
426
|
-
* unambiguously. The agent part is NOT cross-checked against AGENT_CLI_NAMES (that
|
|
427
|
-
* map is the narrower ps-scan comm set): the panes live on the agent-only socket,
|
|
428
|
-
* and validating there would silently drop grok/kimi/antigravity — the exact
|
|
429
|
-
* harness-parity gap we are fixing.
|
|
430
|
-
*/
|
|
431
255
|
const AG_NAME_RE = AG_TMUX_NAME_RE;
|
|
432
|
-
/** Agent kind from an `ag-<agent>-<shortid>` tmux session name, else undefined. */
|
|
433
256
|
export function agentKindFromName(sessName) {
|
|
434
257
|
const m = AG_NAME_RE.exec(sessName);
|
|
435
258
|
return m ? m[1].toLowerCase() : undefined;
|
|
436
259
|
}
|
|
437
|
-
/** The 8-char session-id prefix from an `ag-<agent>-<shortid>` name, else undefined. */
|
|
438
260
|
export function shortIdFromName(sessName) {
|
|
439
261
|
const m = AG_NAME_RE.exec(sessName);
|
|
440
262
|
return m ? m[2].toLowerCase() : undefined;
|
|
441
263
|
}
|
|
442
|
-
/**
|
|
443
|
-
* Map every `ag-<agent>-<shortid>` tmux session name to its full session UUID in
|
|
444
|
-
* ONE batched DB lookup. The live scan calls this once per poll (not per pane),
|
|
445
|
-
* then resolvePaneIdentity reads the map — the recovery that makes a detached
|
|
446
|
-
* agent findable by `focus <id>` even when its durable identity records are gone.
|
|
447
|
-
* `findSessionsByShortIds` is injected so this stays unit-testable without a DB.
|
|
448
|
-
*/
|
|
449
264
|
export function resolveNamesToSessionIds(sessionNames, deps) {
|
|
450
265
|
const shortIdToNames = new Map();
|
|
451
266
|
for (const name of sessionNames) {
|
|
@@ -468,26 +283,7 @@ export function resolveNamesToSessionIds(sessionNames, deps) {
|
|
|
468
283
|
}
|
|
469
284
|
return out;
|
|
470
285
|
}
|
|
471
|
-
/**
|
|
472
|
-
* A process that began more than this long AFTER a session's recorded
|
|
473
|
-
* `startedAtMs` cannot be that session's process — the OS handed its pid to
|
|
474
|
-
* something newer. The window absorbs clock granularity (`ps -o lstart=` reports
|
|
475
|
-
* whole seconds) and the gap between a process spawning and the SessionStart
|
|
476
|
-
* hook recording `startedAtMs`; it is far below the minutes-to-hours it takes the
|
|
477
|
-
* pid space to wrap and actually recycle a pid, so it never false-kills a live
|
|
478
|
-
* session.
|
|
479
|
-
*/
|
|
480
286
|
const PID_REUSE_TOLERANCE_MS = 60_000;
|
|
481
|
-
/**
|
|
482
|
-
* Epoch-ms start time of the process at `pid`, or null if unknowable.
|
|
483
|
-
*
|
|
484
|
-
* Distinct from teams/agents.ts's `captureProcessStartTime`, which returns an
|
|
485
|
-
* opaque token only meaningful for equality against a prior capture of the SAME
|
|
486
|
-
* pid. Here we need a value comparable to a session's `startedAtMs`, so we read
|
|
487
|
-
* `ps -o lstart=` — a ctime string on both macOS and Linux — and parse it to
|
|
488
|
-
* epoch ms. Windows and any exec/parse failure return null, so the caller falls
|
|
489
|
-
* back to a bare existence check (never worse than before).
|
|
490
|
-
*/
|
|
491
287
|
function processStartMs(pid) {
|
|
492
288
|
if (process.platform === 'win32')
|
|
493
289
|
return null;
|
|
@@ -505,16 +301,6 @@ function processStartMs(pid) {
|
|
|
505
301
|
return null;
|
|
506
302
|
}
|
|
507
303
|
}
|
|
508
|
-
/**
|
|
509
|
-
* True when `pid` names a live process AND — when a session's recorded
|
|
510
|
-
* `startedAtMs` is supplied — that process is plausibly the SAME one, not a later
|
|
511
|
-
* process that recycled the pid. The OS reuses pids, so a bare
|
|
512
|
-
* `process.kill(pid, 0)` existence check reports a dead session as alive (a
|
|
513
|
-
* "zombie") once its pid is handed to an unrelated process. A genuine session
|
|
514
|
-
* process starts at or before its own recorded start, so a process that began
|
|
515
|
-
* meaningfully AFTER `startedAtMs` is a reused pid and the session is dead. When
|
|
516
|
-
* the start time can't be read, we keep the existence answer.
|
|
517
|
-
*/
|
|
518
304
|
export function isPidAlive(pid, startedAtMs) {
|
|
519
305
|
if (!pid || pid < 1)
|
|
520
306
|
return false;
|
|
@@ -522,23 +308,18 @@ export function isPidAlive(pid, startedAtMs) {
|
|
|
522
308
|
process.kill(pid, 0);
|
|
523
309
|
}
|
|
524
310
|
catch (err) {
|
|
525
|
-
// EPERM means the pid exists but is owned by another user — still "alive",
|
|
526
|
-
// fall through to the identity check. Any other error means no such process.
|
|
527
311
|
if (err?.code !== 'EPERM')
|
|
528
312
|
return false;
|
|
529
313
|
}
|
|
530
314
|
if (startedAtMs && startedAtMs > 0) {
|
|
315
|
+
// A live recycled PID must not revive an unrelated dead session.
|
|
531
316
|
const procStartMs = processStartMs(pid);
|
|
532
317
|
if (procStartMs !== null && procStartMs > startedAtMs + PID_REUSE_TOLERANCE_MS) {
|
|
533
|
-
return false;
|
|
318
|
+
return false;
|
|
534
319
|
}
|
|
535
320
|
}
|
|
536
321
|
return true;
|
|
537
322
|
}
|
|
538
|
-
/**
|
|
539
|
-
* Keep dead entries only when their window heartbeat also stopped, proving a crash;
|
|
540
|
-
* a live duplicate always wins.
|
|
541
|
-
*/
|
|
542
323
|
function readLiveTerminals() {
|
|
543
324
|
let raw;
|
|
544
325
|
try {
|
|
@@ -566,13 +347,9 @@ function readLiveTerminals() {
|
|
|
566
347
|
if (!e?.sessionId)
|
|
567
348
|
continue;
|
|
568
349
|
const alive = isPidAlive(e.pid, e.startedAtMs);
|
|
569
|
-
// Dead pid + a window still republishing = an ordinary close mid-debounce.
|
|
570
|
-
// Dead pid + a window that stopped republishing = the crash we must report.
|
|
571
350
|
if (!alive && !windowGone)
|
|
572
351
|
continue;
|
|
573
352
|
const entry = { ...e, windowId, windowHeartbeatMs, pidDead: !alive };
|
|
574
|
-
// A live entry always wins a dead one for the same session (the agent was
|
|
575
|
-
// relaunched into a new window while the crashed window's slice lingers).
|
|
576
353
|
const prev = merged.get(e.sessionId);
|
|
577
354
|
if (prev && !prev.pidDead && !alive)
|
|
578
355
|
continue;
|
|
@@ -581,23 +358,9 @@ function readLiveTerminals() {
|
|
|
581
358
|
}
|
|
582
359
|
return Array.from(merged.values());
|
|
583
360
|
}
|
|
584
|
-
/**
|
|
585
|
-
* Process-local memo for Claude transcript path resolution. Each active-session
|
|
586
|
-
* poll re-walks every Claude version-home `projects/` tree for every live pid
|
|
587
|
-
* (#2047). A resolved path is sticky while the file still exists; a miss or a
|
|
588
|
-
* vanished file re-walks. Cap size so a long-lived process cannot retain every
|
|
589
|
-
* cwd it has ever seen.
|
|
590
|
-
*/
|
|
591
361
|
const CLAUDE_SESSION_FILE_CACHE_MAX = 256;
|
|
592
362
|
const claudeSessionFileCache = new Map();
|
|
593
|
-
/**
|
|
594
|
-
* Search every version home because the live ~/.claude symlink moves after upgrades
|
|
595
|
-
* while older running sessions keep writing to their original home.
|
|
596
|
-
*/
|
|
597
363
|
function findClaudeSessionFile(cwd, sessionId) {
|
|
598
|
-
// Only memoize when the exact session UUID is known. Without an id the
|
|
599
|
-
// resolver picks newest-by-mtime under the project dir; caching that path
|
|
600
|
-
// would stick to an old transcript after a new session starts in the same cwd.
|
|
601
364
|
if (sessionId) {
|
|
602
365
|
const cacheKey = `${cwd}\0${sessionId}`;
|
|
603
366
|
const hit = claudeSessionFileCache.get(cacheKey);
|
|
@@ -606,7 +369,7 @@ function findClaudeSessionFile(cwd, sessionId) {
|
|
|
606
369
|
if (fs.existsSync(hit))
|
|
607
370
|
return hit;
|
|
608
371
|
}
|
|
609
|
-
catch {
|
|
372
|
+
catch { }
|
|
610
373
|
claudeSessionFileCache.delete(cacheKey);
|
|
611
374
|
}
|
|
612
375
|
const resolved = pickClaudeSessionFileAcrossRoots(getAgentSessionDirs('claude', 'projects'), cwd, sessionId);
|
|
@@ -619,12 +382,6 @@ function findClaudeSessionFile(cwd, sessionId) {
|
|
|
619
382
|
}
|
|
620
383
|
return pickClaudeSessionFileAcrossRoots(getAgentSessionDirs('claude', 'projects'), cwd, sessionId);
|
|
621
384
|
}
|
|
622
|
-
/**
|
|
623
|
-
* Resolve a Claude transcript for `cwd` across the given project roots, newest
|
|
624
|
-
* mtime winning when the same id/cwd resolves in more than one version home
|
|
625
|
-
* (the actively-written copy is the newest). Pure over `projectRoots`, so it is
|
|
626
|
-
* testable against temp dirs without touching the real home directory.
|
|
627
|
-
*/
|
|
628
385
|
export function pickClaudeSessionFileAcrossRoots(projectRoots, cwd, sessionId) {
|
|
629
386
|
const enc = claudeProjectDirName(cwd);
|
|
630
387
|
let best;
|
|
@@ -644,18 +401,6 @@ export function pickClaudeSessionFileAcrossRoots(projectRoots, cwd, sessionId) {
|
|
|
644
401
|
}
|
|
645
402
|
return best?.path;
|
|
646
403
|
}
|
|
647
|
-
/**
|
|
648
|
-
* Pick a Claude transcript file within a project dir.
|
|
649
|
-
*
|
|
650
|
-
* With a CONCRETE session id: return that id's `<id>.jsonl` or undefined — NEVER a
|
|
651
|
-
* sibling's. Falling back to the newest file here is the bug that made N distinct
|
|
652
|
-
* co-located sessions (e.g. several editor tabs in one cwd, or two worktree siblings)
|
|
653
|
-
* all collapse onto ONE file and render identical preview + topic (they look like
|
|
654
|
-
* duplicate cards). The mtime fallback is only sound when NO id is known.
|
|
655
|
-
*
|
|
656
|
-
* With NO id: return the newest `.jsonl` by mtime (the legitimate single-session
|
|
657
|
-
* heuristic for a directly-launched agent with no registry entry).
|
|
658
|
-
*/
|
|
659
404
|
export function pickSessionFile(projectDir, sessionId) {
|
|
660
405
|
if (sessionId) {
|
|
661
406
|
const specific = path.join(projectDir, `${sessionId}.jsonl`);
|
|
@@ -676,16 +421,10 @@ export function pickSessionFile(projectDir, sessionId) {
|
|
|
676
421
|
if (!best || m > best.mtime)
|
|
677
422
|
best = { path: p, mtime: m };
|
|
678
423
|
}
|
|
679
|
-
catch {
|
|
424
|
+
catch { }
|
|
680
425
|
}
|
|
681
426
|
return best?.path;
|
|
682
427
|
}
|
|
683
|
-
/**
|
|
684
|
-
* One `stat` → the transcript's creation (≈ session start) and last-write (≈ last
|
|
685
|
-
* activity) epochs. Both `undefined` when the file can't be stat'd (vanished /
|
|
686
|
-
* unresolved). `birthtimeMs` can be 0 on filesystems without creation time — coerce
|
|
687
|
-
* that to `undefined` so callers fall through to a real signal instead of epoch 0.
|
|
688
|
-
*/
|
|
689
428
|
export function sessionFileTimes(sessionFile) {
|
|
690
429
|
if (!sessionFile)
|
|
691
430
|
return {};
|
|
@@ -697,22 +436,6 @@ export function sessionFileTimes(sessionFile) {
|
|
|
697
436
|
return {};
|
|
698
437
|
}
|
|
699
438
|
}
|
|
700
|
-
/**
|
|
701
|
-
* The lifecycle status computed purely from the two hard signals the framework
|
|
702
|
-
* always has — PID liveness and transcript last-write (mtime) — independent of
|
|
703
|
-
* anything the agent self-reports or what its last transcript turn happened to
|
|
704
|
-
* look like. Returns the definitive lifecycle label when one applies, or
|
|
705
|
-
* `undefined` when the process is live and fresh (so the caller uses the richer
|
|
706
|
-
* activity-derived status instead).
|
|
707
|
-
*
|
|
708
|
-
* - No write in {@link ABANDONED_STALE_MS} ⇒ `abandoned` (dangling): a
|
|
709
|
-
* days-stale session is not live work, whether its PID is dead (long gone)
|
|
710
|
-
* or still alive but stuck. Checked first — it outranks a bare `closed`.
|
|
711
|
-
* - Otherwise, PID dead ⇒ `closed`: the process has exited. This is the fix
|
|
712
|
-
* for the old "dead PID reports idle" lie — `idle` reads as "done, waiting
|
|
713
|
-
* for you", but a dead process is done, period.
|
|
714
|
-
* - Otherwise (alive + fresh) ⇒ `undefined`: defer to the activity engine.
|
|
715
|
-
*/
|
|
716
439
|
export function lifecycleStatus(pidAlive, mtimeMs, nowMs = Date.now()) {
|
|
717
440
|
if (mtimeMs !== undefined && nowMs - mtimeMs >= ABANDONED_STALE_MS)
|
|
718
441
|
return 'abandoned';
|
|
@@ -720,49 +443,10 @@ export function lifecycleStatus(pidAlive, mtimeMs, nowMs = Date.now()) {
|
|
|
720
443
|
return 'closed';
|
|
721
444
|
return undefined;
|
|
722
445
|
}
|
|
723
|
-
/**
|
|
724
|
-
* The ONE place a fallback status is decided when no rich transcript state is
|
|
725
|
-
* available — an opaque kind we cannot parse (openclaw), or a transcript
|
|
726
|
-
* whose parse/tail was empty or unreadable. Honest by construction: computed from
|
|
727
|
-
* PID + mtime, never a fabricated `idle`.
|
|
728
|
-
*
|
|
729
|
-
* - Dead PID, or a days-stale transcript ⇒ {@link lifecycleStatus} (`closed` /
|
|
730
|
-
* `abandoned`). A dead process is `closed`, not the old fabricated `idle`
|
|
731
|
-
* (which the UI reads as "done, waiting for you"); a dead process whose file
|
|
732
|
-
* also vanished is still `closed` (death is a definitive answer, not `unknown`).
|
|
733
|
-
* - Live + fresh ⇒ `running`. A live process is, at minimum, running: we may
|
|
734
|
-
* not see WHAT an opaque harness (or an empty tail) is doing, but the process
|
|
735
|
-
* being alive is a positive signal — never a blank `unknown` (the old blanket
|
|
736
|
-
* bug for every non-claude/codex live agent) nor a downgraded `idle`.
|
|
737
|
-
*/
|
|
738
446
|
export function resolveFallbackStatus(sessionFile, pidAlive, nowMs = Date.now()) {
|
|
739
447
|
const { mtimeMs } = sessionFileTimes(sessionFile);
|
|
740
448
|
return lifecycleStatus(pidAlive, mtimeMs, nowMs) ?? 'running';
|
|
741
449
|
}
|
|
742
|
-
/**
|
|
743
|
-
* Locate the live transcript for an agent process. Claude files are keyed by cwd
|
|
744
|
-
* (+ optional session uuid), so they resolve straight off disk. Every OTHER
|
|
745
|
-
* tracked harness — Codex (date-partitioned), plus grok / droid / rush / gemini /
|
|
746
|
-
* kimi / hermes / opencode / antigravity / cursor (per-session dirs, SQLite, single-JSON)
|
|
747
|
-
* — is resolved through the session index. When the session id is KNOWN, that id
|
|
748
|
-
* selects the transcript. Only an id-less process falls back to the newest indexed
|
|
749
|
-
* transcript for that cwd, bounded by ACTIVE_SESSION_STALE_MS so a live pid never
|
|
750
|
-
* borrows a weeks-old transcript. This is what lets a live NON-claude/codex agent
|
|
751
|
-
* get a real status instead of falling through to `unknown` (the file feeds
|
|
752
|
-
* {@link computeLiveSignals}). An opaque kind we don't track still yields undefined
|
|
753
|
-
* here and degrades honestly to a live `running`.
|
|
754
|
-
*
|
|
755
|
-
* The id branch exists because the cwd fallback is only safe when there is nothing
|
|
756
|
-
* better: it answers `WHERE agent = ? AND cwd = ? ORDER BY last_activity DESC LIMIT 1`
|
|
757
|
-
* (`latestSessionFileForCwd`), so with two same-harness agents in ONE cwd — routine on
|
|
758
|
-
* this fleet — every one of them resolves to whichever transcript was touched last.
|
|
759
|
-
* That is the same "one stranger's transcript" hazard the id-less tmux pane already
|
|
760
|
-
* refuses to guess at (see `listTmuxAgentSessions`), and since RUSH-2682 it reaches
|
|
761
|
-
* further than a status badge: the live row now backs `sessions preview <id>`, so the
|
|
762
|
-
* guess renders session B's digest under session A's header and caches it against A.
|
|
763
|
-
* An id we cannot resolve yields undefined — the honest "not indexed here" render —
|
|
764
|
-
* rather than a neighbour's transcript (RUSH-2691).
|
|
765
|
-
*/
|
|
766
450
|
export function findSessionFileForKind(kind, cwd, sessionId) {
|
|
767
451
|
if (!cwd)
|
|
768
452
|
return undefined;
|
|
@@ -774,36 +458,19 @@ export function findSessionFileForKind(kind, cwd, sessionId) {
|
|
|
774
458
|
return indexedSessionFileForId(kind, sessionId);
|
|
775
459
|
return latestSessionFileForCwd(kind, cwd, { maxAgeMs: ACTIVE_SESSION_STALE_MS });
|
|
776
460
|
}
|
|
777
|
-
/**
|
|
778
|
-
* The indexed transcript for one exact session id, or undefined when the index has
|
|
779
|
-
* not reached it yet. The `agent` guard rejects a row that belongs to a different
|
|
780
|
-
* harness than the live process claims: those cannot be the same conversation, and
|
|
781
|
-
* returning it would reintroduce the misattribution this function exists to avoid.
|
|
782
|
-
*/
|
|
783
461
|
function indexedSessionFileForId(kind, sessionId) {
|
|
784
462
|
const row = getSessionById(sessionId);
|
|
785
463
|
if (!row || row.agent !== kind)
|
|
786
464
|
return undefined;
|
|
787
465
|
return row.filePath || undefined;
|
|
788
466
|
}
|
|
789
|
-
/** Recover the session UUID from a transcript filename (Claude `<uuid>.jsonl`, Codex `rollout-…-<uuid>.jsonl`). */
|
|
790
467
|
const UUID_RE = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i;
|
|
791
468
|
function sessionIdFromFile(file) {
|
|
792
469
|
if (!file)
|
|
793
470
|
return undefined;
|
|
794
471
|
return path.basename(file).match(UUID_RE)?.[0];
|
|
795
472
|
}
|
|
796
|
-
/** Cap the events fed to state inference for a non-tailable harness — the last
|
|
797
|
-
* turns are all `inferActivity` needs, and a bound keeps a huge transcript cheap. */
|
|
798
473
|
const LIVE_STATE_MAX_EVENTS = 80;
|
|
799
|
-
/**
|
|
800
|
-
* Parse a NON-claude/codex transcript with that harness's own parser and return
|
|
801
|
-
* its last events for state inference. These formats vary too much for the
|
|
802
|
-
* single-file byte-tail fast path (per-session dirs for grok/kimi, SQLite for
|
|
803
|
-
* opencode/antigravity, single-JSON for gemini/hermes), so parse the whole
|
|
804
|
-
* (size-guarded, via safeReadSessionFile) transcript and keep the tail. A parse
|
|
805
|
-
* failure yields no events, degrading honestly to the live fallback.
|
|
806
|
-
*/
|
|
807
474
|
function parseTailEventsForKind(agent, sessionFile) {
|
|
808
475
|
let events;
|
|
809
476
|
try {
|
|
@@ -814,32 +481,11 @@ function parseTailEventsForKind(agent, sessionFile) {
|
|
|
814
481
|
}
|
|
815
482
|
return events.length > LIVE_STATE_MAX_EVENTS ? events.slice(-LIVE_STATE_MAX_EVENTS) : events;
|
|
816
483
|
}
|
|
817
|
-
/**
|
|
818
|
-
* Process-local memo of the PARSED TAIL for {@link computeLiveSignals}. The
|
|
819
|
-
* menu-bar / `--active` poll re-reads every live session on a ~30s tick (#2047),
|
|
820
|
-
* and while a transcript's mtime is unchanged its bytes are unchanged — so the
|
|
821
|
-
* parse (the expensive half) is memoized on mtime. The CLASSIFICATION is not:
|
|
822
|
-
* `inferSessionState` is re-run on every call against the current clock and the
|
|
823
|
-
* current `pidAlive`, because a time-based verdict (the 30-minute prose-question
|
|
824
|
-
* decay) must expire on schedule even while the bytes sit still — a memo keyed
|
|
825
|
-
* on mtime froze that verdict for as long as nobody typed (PHNX-3999). Bound the
|
|
826
|
-
* map so a long-lived daemon that sees thousands of sessions cannot retain them
|
|
827
|
-
* forever.
|
|
828
|
-
*/
|
|
829
484
|
const LIVE_TAIL_CACHE_MAX = 512;
|
|
830
485
|
const liveTailCache = new Map();
|
|
831
|
-
/** Test seam: drop the in-process parsed-tail memo. */
|
|
832
486
|
export function clearLiveSignalsCacheForTest() {
|
|
833
487
|
liveTailCache.clear();
|
|
834
488
|
}
|
|
835
|
-
/**
|
|
836
|
-
* Parse the transcript tail for one harness. Claude/Codex take the fast bounded
|
|
837
|
-
* byte-tail ({@link readSessionTailWithRaw}) — the hot path, and the only two
|
|
838
|
-
* that also yield throughput (their raw lines carry usage the event model drops).
|
|
839
|
-
* EVERY OTHER tracked harness (grok, droid, rush, gemini, kimi, hermes, opencode,
|
|
840
|
-
* antigravity, cursor) is parsed with its own parser. An opaque/untracked kind
|
|
841
|
-
* yields no events.
|
|
842
|
-
*/
|
|
843
489
|
function parseLiveTail(kind, sessionFile, mtimeMs) {
|
|
844
490
|
if (kind === 'claude' || kind === 'codex') {
|
|
845
491
|
const { events, content } = readSessionTailWithRaw(sessionFile, kind);
|
|
@@ -850,20 +496,6 @@ function parseLiveTail(kind, sessionFile, mtimeMs) {
|
|
|
850
496
|
return { mtimeMs, events: parseTailEventsForKind(kind, sessionFile) };
|
|
851
497
|
return { mtimeMs, events: [] };
|
|
852
498
|
}
|
|
853
|
-
/**
|
|
854
|
-
* Derive the inferred state (working / waiting / idle + preview/badges) and, for
|
|
855
|
-
* the two harnesses whose raw lines carry it, the output-token throughput.
|
|
856
|
-
*
|
|
857
|
-
* Every tracked harness runs through the SAME {@link inferSessionState}, so a
|
|
858
|
-
* live non-claude/codex agent gets a real working/waiting/idle instead of the
|
|
859
|
-
* blanket `unknown` it used to fall through to. An opaque/untracked kind or an
|
|
860
|
-
* unreadable/empty transcript yields an empty signal set, and the caller's
|
|
861
|
-
* {@link resolveFallbackStatus} reports the honest live floor (`running`).
|
|
862
|
-
*
|
|
863
|
-
* An unchanged-mtime call reuses the parsed tail (see {@link liveTailCache}) so a
|
|
864
|
-
* 30s active-session poll does not re-tail every quiet transcript — but it always
|
|
865
|
-
* re-classifies against `nowMs`, so an elapsed-time verdict is never frozen.
|
|
866
|
-
*/
|
|
867
499
|
export function computeLiveSignals(kind, sessionFile, cwd, pidAlive, nowMs = Date.now()) {
|
|
868
500
|
if (!sessionFile)
|
|
869
501
|
return {};
|
|
@@ -871,7 +503,7 @@ export function computeLiveSignals(kind, sessionFile, cwd, pidAlive, nowMs = Dat
|
|
|
871
503
|
try {
|
|
872
504
|
mtimeMs = fs.statSync(sessionFile).mtimeMs;
|
|
873
505
|
}
|
|
874
|
-
catch {
|
|
506
|
+
catch { }
|
|
875
507
|
const cacheKey = `${kind}\0${sessionFile}\0${cwd ?? ''}`;
|
|
876
508
|
let tail = liveTailCache.get(cacheKey);
|
|
877
509
|
if (!tail || tail.mtimeMs !== mtimeMs) {
|
|
@@ -885,38 +517,27 @@ export function computeLiveSignals(kind, sessionFile, cwd, pidAlive, nowMs = Dat
|
|
|
885
517
|
const state = inferSessionState(tail.events, { cwd, pidAlive, mtimeMs, activeWindowMs: ACTIVE_MTIME_WINDOW_MS, nowMs });
|
|
886
518
|
return { state, tokPerSec: tail.tokPerSec };
|
|
887
519
|
}
|
|
888
|
-
/** Map inferred activity onto the coarse ActiveStatus used by the renderer and counts. */
|
|
889
520
|
function statusFromActivity(activity) {
|
|
890
521
|
return activity === 'working' ? 'running' : activity === 'waiting_input' ? 'input_required' : 'idle';
|
|
891
522
|
}
|
|
892
|
-
/**
|
|
893
|
-
* Fold a computed SessionState onto an active-session row: rich status +
|
|
894
|
-
* preview + PR/worktree/ticket badges. With no state (an opaque/untracked kind,
|
|
895
|
-
* or an unreadable/empty transcript) it degrades to
|
|
896
|
-
* {@link resolveFallbackStatus}, which reports the honest live floor (`running`
|
|
897
|
-
* for an alive process) rather than the old blanket `unknown`.
|
|
898
|
-
*/
|
|
899
523
|
function applyState(base, state, fallbackFile, pidAlive) {
|
|
900
524
|
if (!state)
|
|
901
525
|
return { ...base, pidAlive, status: resolveFallbackStatus(fallbackFile, pidAlive) };
|
|
902
|
-
// Lifecycle (closed/abandoned) is computed from PID + mtime and OVERRIDES the
|
|
903
|
-
// activity-derived status: a dead or days-stale process is closed/abandoned no
|
|
904
|
-
// matter what its last parsed transcript turn looked like (a dead session whose
|
|
905
|
-
// tail ended mid-tool-call must not read as `running`). `base.lastActivityMs` is
|
|
906
|
-
// the transcript mtime the row already resolved — reuse it, no extra stat.
|
|
907
526
|
const life = lifecycleStatus(pidAlive, base.lastActivityMs ?? sessionFileTimes(fallbackFile).mtimeMs);
|
|
908
527
|
return {
|
|
909
528
|
...base,
|
|
910
529
|
pidAlive,
|
|
911
530
|
status: life ?? statusFromActivity(state.activity),
|
|
912
531
|
activity: state.activity,
|
|
532
|
+
model: state.model,
|
|
533
|
+
failures: state.failures,
|
|
534
|
+
userTurns: state.userTurns,
|
|
913
535
|
awaitingReason: state.awaitingReason,
|
|
914
536
|
question: state.question,
|
|
915
537
|
lastEventMs: state.lastEventMs,
|
|
916
538
|
plan: state.plan,
|
|
917
539
|
todos: state.todos,
|
|
918
540
|
tail: state.tail,
|
|
919
|
-
// Prefer the live preview (latest turn); keep the first-prompt topic as a fallback.
|
|
920
541
|
preview: state.preview ?? base.preview,
|
|
921
542
|
pr: state.pr,
|
|
922
543
|
worktree: state.worktree,
|
|
@@ -929,11 +550,6 @@ function applyState(base, state, fallbackFile, pidAlive) {
|
|
|
929
550
|
rateLimited: state.rateLimited,
|
|
930
551
|
};
|
|
931
552
|
}
|
|
932
|
-
/**
|
|
933
|
-
* Extract the first user message's content from a Claude JSONL file.
|
|
934
|
-
* Reads only the first ~50 lines for speed, since the user message is
|
|
935
|
-
* typically near the top (after system/queue events).
|
|
936
|
-
*/
|
|
937
553
|
function extractClaudeUserText(parsed) {
|
|
938
554
|
const msg = parsed.message;
|
|
939
555
|
if (!msg?.content)
|
|
@@ -1002,11 +618,6 @@ function quickExtractTopic(sessionFile) {
|
|
|
1002
618
|
}
|
|
1003
619
|
return undefined;
|
|
1004
620
|
}
|
|
1005
|
-
/**
|
|
1006
|
-
* One-line summary of a teammate's spawn prompt — the team's task/target. Takes
|
|
1007
|
-
* the first non-empty line, strips a leading `MISSION:`/`CONTEXT:`/`TASK:` label,
|
|
1008
|
-
* and truncates. Exported for tests.
|
|
1009
|
-
*/
|
|
1010
621
|
export function summarizeMission(prompt) {
|
|
1011
622
|
if (!prompt)
|
|
1012
623
|
return undefined;
|
|
@@ -1018,40 +629,17 @@ export function summarizeMission(prompt) {
|
|
|
1018
629
|
return undefined;
|
|
1019
630
|
return cleaned.length > 80 ? `${cleaned.slice(0, 79)}…` : cleaned;
|
|
1020
631
|
}
|
|
1021
|
-
/**
|
|
1022
|
-
* Live teams teammates. Reuses AgentManager which already polls PIDs via
|
|
1023
|
-
* `kill -0`. `localOnly` (RUSH-2118) skips the ssh round-trip AgentManager
|
|
1024
|
-
* would otherwise issue for every distributed (remote-host) teammate.
|
|
1025
|
-
*/
|
|
1026
632
|
export async function listTeamsActive(opts = {}) {
|
|
1027
633
|
const mgr = new AgentManager(undefined, undefined, undefined, undefined, undefined, opts.localOnly ?? false);
|
|
1028
634
|
const running = await mgr.listRunning();
|
|
1029
635
|
const self = machineId();
|
|
1030
636
|
return running.map((a) => {
|
|
1031
|
-
// The teammate's OWN transcript is `remoteSessionId` (captured from its first
|
|
1032
|
-
// stream event). `parentSessionId` is the ORCHESTRATOR that spawned the team
|
|
1033
|
-
// (AGENTS_SESSION_ID at spawn) — a link, not this teammate's id. Keying the
|
|
1034
|
-
// row off the orchestrator conflated the two (a teammate showed the
|
|
1035
|
-
// orchestrator's id/topic and lineage was invisible); resolve the teammate's
|
|
1036
|
-
// own session for the row and expose the orchestrator separately.
|
|
1037
637
|
const ownSessionId = a.remoteSessionId ?? undefined;
|
|
1038
638
|
const sessionFile = findSessionFileForKind(a.agentType, a.cwd ?? undefined, ownSessionId);
|
|
1039
639
|
const topic = sessionFile ? quickExtractTopic(sessionFile) : undefined;
|
|
1040
640
|
const pidAlive = a.pid ? isPidAlive(a.pid) : true;
|
|
1041
641
|
const { state, tokPerSec } = computeLiveSignals(a.agentType, sessionFile, a.cwd ?? undefined, pidAlive);
|
|
1042
642
|
const resolvedId = ownSessionId ?? sessionIdFromFile(sessionFile);
|
|
1043
|
-
// A remote teams teammate (`teams add --device <peer>`) EXECUTES on that
|
|
1044
|
-
// peer, not on this orchestrator box — but it gets no host-dispatch index
|
|
1045
|
-
// row, so `foldExecutionMachine` can't reach it and the self-stamp in
|
|
1046
|
-
// `commands/sessions.ts` would claim it, listing a peer's teammate under
|
|
1047
|
-
// `--device <orchestrator>` (SES-GAP-10). Attribute the row to the execution
|
|
1048
|
-
// host and mark the dispatcher, the same shape `run --device` gets: `machine`
|
|
1049
|
-
// survives the cross-machine fan-out because `parseRemoteActive` keeps an
|
|
1050
|
-
// `offloadedFrom` row's own machine, and `offloadedFrom` is COMPARED to this
|
|
1051
|
-
// box by `sessionProcessIsLocal` (never merely tested), so a third box seeing
|
|
1052
|
-
// this row over the fan-out reads it as the peer's, not its own. A local
|
|
1053
|
-
// teammate (no `hostName`, or pinned to this box) is left unattributed for
|
|
1054
|
-
// the self-stamp.
|
|
1055
643
|
const execHost = a.hostName ? normalizeHost(a.hostName) : undefined;
|
|
1056
644
|
const offloaded = execHost !== undefined && execHost !== self;
|
|
1057
645
|
return applyState({
|
|
@@ -1073,32 +661,21 @@ export async function listTeamsActive(opts = {}) {
|
|
|
1073
661
|
teamName: a.taskName,
|
|
1074
662
|
assignedTask: summarizeMission(a.prompt),
|
|
1075
663
|
agentId: a.agentId,
|
|
1076
|
-
// The frozen actor stamped on the teammate record (RUSH-2028) — who ran
|
|
1077
|
-
// this teammate, surfaced as the owner in --active (RUSH-2018); sidecar
|
|
1078
|
-
// fallback for a teammate record predating the actor field.
|
|
1079
664
|
owner: resolveOwner(a.actor, resolvedId),
|
|
1080
665
|
}, state, sessionFile, pidAlive);
|
|
1081
666
|
});
|
|
1082
667
|
}
|
|
1083
|
-
/** Live editor-terminal agents across every IDE window. */
|
|
1084
668
|
export async function listTerminalsActive() {
|
|
1085
669
|
const entries = readLiveTerminals();
|
|
1086
670
|
if (entries.length === 0)
|
|
1087
671
|
return [];
|
|
1088
|
-
// Walk the shell PIDs through the process table once so we can name the host
|
|
1089
|
-
// (code / cursor / codium) per entry rather than a generic 'terminal'.
|
|
1090
672
|
const procByPid = new Map();
|
|
1091
673
|
for (const r of await readProcessTable())
|
|
1092
674
|
procByPid.set(r.pid, r);
|
|
1093
675
|
const children = childrenByParent(new Map([...procByPid.values()].map(r => [r.pid, r.ppid])));
|
|
1094
|
-
// Build label map from Claude's sessions/*.json for /rename support
|
|
1095
676
|
const labelMap = buildClaudeLabelMap();
|
|
1096
|
-
// Run-name handles (`agents run --name`) keyed by session id, for the same
|
|
1097
|
-
// sessionId → handle resolution as labels.
|
|
1098
677
|
const runNameMap = buildRunNameMap();
|
|
1099
678
|
return entries.map((t) => {
|
|
1100
|
-
// Keep the shell pid for tab ownership and headless suppression; the live
|
|
1101
|
-
// agent below it owns the session after an exit-and-rerun in the same tab.
|
|
1102
679
|
const directEntry = readPidSessionEntry(t.pid, procByPid.get(t.pid)?.startTime);
|
|
1103
680
|
const candidate = directEntry?.sessionId ? directEntry
|
|
1104
681
|
: (!t.pidDead ? terminalDescendantEntry(t.pid, procByPid, children) : undefined) ?? directEntry;
|
|
@@ -1108,9 +685,7 @@ export async function listTerminalsActive() {
|
|
|
1108
685
|
const sessionKind = pidEntry?.agent ?? t.kind;
|
|
1109
686
|
const sessionFile = findSessionFileForKind(sessionKind, cwd, resolvedId);
|
|
1110
687
|
const label = t.label ?? (resolvedId ? labelMap.get(resolvedId) : undefined) ?? undefined;
|
|
1111
|
-
// Durable run name from `agents run --name`, resolved by the run's session id.
|
|
1112
688
|
const name = resolvedId ? runNameMap.get(resolvedId) ?? undefined : undefined;
|
|
1113
|
-
// Extract topic from session file (first meaningful user message)
|
|
1114
689
|
const topic = sessionFile ? quickExtractTopic(sessionFile) : undefined;
|
|
1115
690
|
const pidAlive = isPidAlive(t.pid, t.startedAtMs);
|
|
1116
691
|
const { state, tokPerSec } = computeLiveSignals(sessionKind, sessionFile, cwd, pidAlive);
|
|
@@ -1139,7 +714,6 @@ export async function listTerminalsActive() {
|
|
|
1139
714
|
}, state, sessionFile, pidAlive);
|
|
1140
715
|
});
|
|
1141
716
|
}
|
|
1142
|
-
/** Cloud tasks still in a non-terminal state. `tasks.db` may not exist; that's fine. */
|
|
1143
717
|
function listCloudActive() {
|
|
1144
718
|
let tasks;
|
|
1145
719
|
try {
|
|
@@ -1159,18 +733,11 @@ function listCloudActive() {
|
|
|
1159
733
|
cloudStatus: t.status,
|
|
1160
734
|
}));
|
|
1161
735
|
}
|
|
1162
|
-
/**
|
|
1163
|
-
* Ordered ancestor-process matchers. First match wins (most specific to least),
|
|
1164
|
-
* so an IDE renderer is preferred over the terminal-app that launched the IDE,
|
|
1165
|
-
* and a terminal-app is preferred over the multiplexer inside it.
|
|
1166
|
-
*/
|
|
1167
736
|
const HOST_MATCHERS = [
|
|
1168
|
-
// IDE renderers (Electron helper processes on macOS, image names on Windows)
|
|
1169
737
|
{ host: 'code', tokens: ['Code Helper', 'Code - Insiders Helper', 'Code.exe'] },
|
|
1170
738
|
{ host: 'cursor', tokens: ['Cursor Helper', 'Cursor.exe'] },
|
|
1171
739
|
{ host: 'codium', tokens: ['VSCodium Helper', 'VSCodium.exe'] },
|
|
1172
740
|
{ host: 'windsurf', tokens: ['Windsurf Helper', 'Windsurf.exe'] },
|
|
1173
|
-
// Native terminal apps
|
|
1174
741
|
{ host: 'iterm', tokens: ['iTerm2', 'iTermServer', 'iTerm'] },
|
|
1175
742
|
{ host: 'terminal', tokens: ['Terminal.app', '/Applications/Utilities/Terminal.app', 'WindowsTerminal.exe'] },
|
|
1176
743
|
{ host: 'warp', tokens: ['Warp.app', 'stable_'] },
|
|
@@ -1179,21 +746,9 @@ const HOST_MATCHERS = [
|
|
|
1179
746
|
{ host: 'hyper', tokens: ['Hyper.app', 'Hyper Helper'] },
|
|
1180
747
|
{ host: 'wezterm', tokens: ['wezterm', 'WezTerm'] },
|
|
1181
748
|
{ host: 'ghostty', tokens: ['ghostty', 'Ghostty'] },
|
|
1182
|
-
// Multiplexers (fallback — only if no UI found above them)
|
|
1183
749
|
{ host: 'tmux', tokens: ['tmux'] },
|
|
1184
750
|
{ host: 'screen', tokens: ['screen'] },
|
|
1185
751
|
];
|
|
1186
|
-
/**
|
|
1187
|
-
* Snapshot the whole process table in one `ps` call. Includes ppid so we can
|
|
1188
|
-
* walk ancestry chains to attribute child processes to their terminal hosts.
|
|
1189
|
-
* `comm` may be an absolute path for shim-launched agents, so basename before
|
|
1190
|
-
* matching against AGENT_CLI_NAMES.
|
|
1191
|
-
*
|
|
1192
|
-
* Memoized for {@link PROCESS_TABLE_FRESH_MS} so one active-session scan (and a
|
|
1193
|
-
* quiet re-poll within the window) does not re-shell `ps -A` / CIM for every
|
|
1194
|
-
* caller — terminal host detection, headless attribution, and `hostFromPid`
|
|
1195
|
-
* all share the same snapshot (#2047).
|
|
1196
|
-
*/
|
|
1197
752
|
async function readProcessTable() {
|
|
1198
753
|
const now = activeScanNow();
|
|
1199
754
|
if (processTableCache && now - processTableCache.at < PROCESS_TABLE_FRESH_MS) {
|
|
@@ -1203,7 +758,6 @@ async function readProcessTable() {
|
|
|
1203
758
|
processTableCache = { at: now, rows };
|
|
1204
759
|
return rows;
|
|
1205
760
|
}
|
|
1206
|
-
/** Uncached process-table snapshot (the live `ps` / CIM path). */
|
|
1207
761
|
async function readProcessTableLive() {
|
|
1208
762
|
processTableLiveReads += 1;
|
|
1209
763
|
if (process.platform === 'win32')
|
|
@@ -1217,8 +771,6 @@ async function readProcessTableLive() {
|
|
|
1217
771
|
}
|
|
1218
772
|
const rows = [];
|
|
1219
773
|
for (const line of out.split('\n')) {
|
|
1220
|
-
// pid ppid tty lstart comm — tty is a single token ('ttys003', 's003', or '??'/'?'
|
|
1221
|
-
// for none); comm stays last so it may contain spaces.
|
|
1222
774
|
const m = line.trim().match(/^(\d+)\s+(\d+)\s+(\S+)\s+(\S+\s+\S+\s+\d+\s+\S+\s+\d+)\s+(.+)$/);
|
|
1223
775
|
if (!m)
|
|
1224
776
|
continue;
|
|
@@ -1233,11 +785,6 @@ async function readProcessTableLive() {
|
|
|
1233
785
|
}
|
|
1234
786
|
return rows;
|
|
1235
787
|
}
|
|
1236
|
-
/**
|
|
1237
|
-
* Windows process table in one CIM query (`wmic` is removed on current
|
|
1238
|
-
* Windows 11, so PowerShell is the stable interface). Same pid/ppid/comm
|
|
1239
|
-
* shape as the POSIX `ps` snapshot; `Name` is the image name (`claude.exe`).
|
|
1240
|
-
*/
|
|
1241
788
|
async function readProcessTableWin32() {
|
|
1242
789
|
let out;
|
|
1243
790
|
try {
|
|
@@ -1251,7 +798,6 @@ async function readProcessTableWin32() {
|
|
|
1251
798
|
}
|
|
1252
799
|
return parseWin32ProcessCsv(out);
|
|
1253
800
|
}
|
|
1254
|
-
/** Parse `ConvertTo-Csv` output of Win32_Process rows. Exported for tests. */
|
|
1255
801
|
export function parseWin32ProcessCsv(out) {
|
|
1256
802
|
const rows = [];
|
|
1257
803
|
for (const line of out.split(/\r?\n/)) {
|
|
@@ -1279,8 +825,6 @@ function childrenByParent(ppidMap) {
|
|
|
1279
825
|
return children;
|
|
1280
826
|
}
|
|
1281
827
|
function terminalDescendantEntry(pid, processes, children) {
|
|
1282
|
-
// Windows only proves pidExists: a recycled PID can be a live shell descendant.
|
|
1283
|
-
// Retain the published identity until that platform can prove process starts.
|
|
1284
828
|
if (process.platform === 'win32')
|
|
1285
829
|
return undefined;
|
|
1286
830
|
let pending = children.get(pid) ?? [];
|
|
@@ -1294,16 +838,15 @@ function terminalDescendantEntry(pid, processes, children) {
|
|
|
1294
838
|
continue;
|
|
1295
839
|
seen.add(child);
|
|
1296
840
|
const entry = readPidSessionEntry(child, processes.get(child)?.startTime);
|
|
1297
|
-
|
|
841
|
+
const agent = entry?.agent || processes.get(child)?.kind;
|
|
842
|
+
if (!entry || !agent || !isSessionTrackedAgent(agent)) {
|
|
1298
843
|
next.push(...(children.get(child) ?? []));
|
|
1299
844
|
continue;
|
|
1300
845
|
}
|
|
1301
|
-
// A recorded agent is a boundary even before its session ID is known:
|
|
1302
|
-
// its tools may launch agents of their own, which never own this tab.
|
|
1303
846
|
recordedAgent = true;
|
|
1304
847
|
if (entry.sessionId && (!newest || entry.startedAtMs > newest.startedAtMs
|
|
1305
848
|
|| (entry.startedAtMs === newest.startedAtMs && entry.pid > newest.pid)))
|
|
1306
|
-
newest = entry;
|
|
849
|
+
newest = { ...entry, agent };
|
|
1307
850
|
}
|
|
1308
851
|
if (recordedAgent)
|
|
1309
852
|
return newest;
|
|
@@ -1311,11 +854,6 @@ function terminalDescendantEntry(pid, processes, children) {
|
|
|
1311
854
|
}
|
|
1312
855
|
return undefined;
|
|
1313
856
|
}
|
|
1314
|
-
/**
|
|
1315
|
-
* True when any ancestor in pid's parent chain is a known attributed PID.
|
|
1316
|
-
* VS Code / Cursor terminals store the *shell* PID in live-terminals.json,
|
|
1317
|
-
* while `ps` reports the *child* claude PID, so a direct set lookup misses.
|
|
1318
|
-
*/
|
|
1319
857
|
function hasAttributedAncestor(pid, ppidMap, attributed) {
|
|
1320
858
|
let cur = ppidMap.get(pid);
|
|
1321
859
|
const seen = new Set();
|
|
@@ -1327,23 +865,10 @@ function hasAttributedAncestor(pid, ppidMap, attributed) {
|
|
|
1327
865
|
}
|
|
1328
866
|
return false;
|
|
1329
867
|
}
|
|
1330
|
-
/**
|
|
1331
|
-
* Resolve every candidate PID's cwd, bounded and staggered so the probes no
|
|
1332
|
-
* longer fan out as one simultaneous system-wide `lsof` burst (a behavioral-EDR
|
|
1333
|
-
* recon trigger). Order matches the input `pids`. The `probe` seam is injectable
|
|
1334
|
-
* for testing the bound; production always uses the real `lsof`-backed probe.
|
|
1335
|
-
*/
|
|
1336
868
|
export function resolveCwds(pids, probe = getCwdForPid) {
|
|
1337
869
|
return mapBounded(pids, probe, { concurrency: LSOF_CONCURRENCY, staggerMs: LSOF_STAGGER_MS });
|
|
1338
870
|
}
|
|
1339
|
-
/**
|
|
1340
|
-
* Resolve a process's current working directory via `lsof`. The `-a` flag
|
|
1341
|
-
* ANDs the filters; without it macOS treats `-p` and `-d` as a union and
|
|
1342
|
-
* returns the cwd of every process on the system.
|
|
1343
|
-
*/
|
|
1344
871
|
async function getCwdForPid(pid) {
|
|
1345
|
-
// No lsof on Windows and no cheap foreign-process cwd API; the pid registry
|
|
1346
|
-
// written by `ag run` supplies the cwd for registry-launched agents instead.
|
|
1347
872
|
if (process.platform === 'win32')
|
|
1348
873
|
return undefined;
|
|
1349
874
|
let out;
|
|
@@ -1363,12 +888,6 @@ async function getCwdForPid(pid) {
|
|
|
1363
888
|
}
|
|
1364
889
|
return undefined;
|
|
1365
890
|
}
|
|
1366
|
-
/**
|
|
1367
|
-
* Walk a pid's ancestor chain and return the most specific host app found.
|
|
1368
|
-
* Checks each HOST_MATCHERS entry against every ancestor, returns the first
|
|
1369
|
-
* host whose tokens match — so IDEs beat terminal apps, terminals beat
|
|
1370
|
-
* multiplexers. Returns undefined if nothing is recognised (true headless).
|
|
1371
|
-
*/
|
|
1372
891
|
function detectHost(pid, procByPid) {
|
|
1373
892
|
const chain = [];
|
|
1374
893
|
let cur = procByPid.get(pid)?.ppid;
|
|
@@ -1387,13 +906,6 @@ function detectHost(pid, procByPid) {
|
|
|
1387
906
|
}
|
|
1388
907
|
return undefined;
|
|
1389
908
|
}
|
|
1390
|
-
/**
|
|
1391
|
-
* Resolve the host app for a single pid by walking its process ancestry with the
|
|
1392
|
-
* same HOST_MATCHERS logic `detectHost` uses. Reads the whole process table per
|
|
1393
|
-
* call, so it's for the low-cardinality renderer path (one tmux client per
|
|
1394
|
-
* session), not a hot loop. Returns undefined when nothing above the pid is a
|
|
1395
|
-
* recognised UI. Exported for the "viewing in <app>" resolver.
|
|
1396
|
-
*/
|
|
1397
909
|
export async function hostFromPid(pid) {
|
|
1398
910
|
if (!pid || pid < 1)
|
|
1399
911
|
return undefined;
|
|
@@ -1402,20 +914,11 @@ export async function hostFromPid(pid) {
|
|
|
1402
914
|
procByPid.set(r.pid, r);
|
|
1403
915
|
return detectHost(pid, procByPid);
|
|
1404
916
|
}
|
|
1405
|
-
/** IDE / terminal / multiplexer hosts all count as UI-hosted. Absence = truly headless. */
|
|
1406
917
|
const UI_HOSTS = new Set([
|
|
1407
918
|
'code', 'cursor', 'codium', 'windsurf',
|
|
1408
919
|
'iterm', 'terminal', 'warp', 'alacritty', 'kitty', 'hyper', 'wezterm', 'ghostty',
|
|
1409
920
|
'tmux', 'screen',
|
|
1410
921
|
]);
|
|
1411
|
-
/**
|
|
1412
|
-
* Find the launch registry entry recorded by a WRAPPER of this process. The
|
|
1413
|
-
* shim delegate records the pid it spawned, but on Windows the `.cmd` shell
|
|
1414
|
-
* path makes that a cmd.exe intermediary whose child is the real agent binary
|
|
1415
|
-
* — so the agent pid itself has no entry and the wrapper one ancestor up does.
|
|
1416
|
-
* The nearest entry wins, and only if its agent matches the candidate's kind:
|
|
1417
|
-
* a claude session shelling out to codex must not hand codex its identity.
|
|
1418
|
-
*/
|
|
1419
922
|
export function readAncestorSessionEntry(pid, ppidMap, kind, readEntry = readPidSessionEntry) {
|
|
1420
923
|
let cur = ppidMap.get(pid);
|
|
1421
924
|
const seen = new Set();
|
|
@@ -1428,24 +931,6 @@ export function readAncestorSessionEntry(pid, ppidMap, kind, readEntry = readPid
|
|
|
1428
931
|
}
|
|
1429
932
|
return undefined;
|
|
1430
933
|
}
|
|
1431
|
-
/**
|
|
1432
|
-
* Collapse agent processes spawned by another live agent process of the same
|
|
1433
|
-
* kind onto their nearest kept ancestor. Claude runs subagents, forks, and
|
|
1434
|
-
* even its bundled ripgrep as child `claude` processes — on POSIX those
|
|
1435
|
-
* children resolve to the parent's cwd and collapse in dedupeBySession, but
|
|
1436
|
-
* where no cwd can be recovered (Windows has no lsof) every fork would print
|
|
1437
|
-
* as its own headless row. Two exceptions keep their own row: a candidate with
|
|
1438
|
-
* its own registry entry — on its pid OR on a wrapper ancestor strictly below
|
|
1439
|
-
* the pid it would fold into (the shim's entry lands on the cmd.exe
|
|
1440
|
-
* intermediary on Windows) — a child whose live argv carries `--session-id`
|
|
1441
|
-
* (RUSH-2384: empty by-pid must not fold a real session into a parent and
|
|
1442
|
-
* drop it from the active set) — and a child of a *different* agent kind
|
|
1443
|
-
* (claude shelling out to codex is a real second session, not a fork).
|
|
1444
|
-
* Returns the kept roots plus, per root pid, how many descendants folded in.
|
|
1445
|
-
*
|
|
1446
|
-
* `hasLiveSessionId` defaults to reading `--session-id` from the live process
|
|
1447
|
-
* argv; tests inject a pure predicate so they do not need real agent pids.
|
|
1448
|
-
*/
|
|
1449
934
|
export function foldSubordinateAgents(candidates, ppidMap, readEntry, hasLiveSessionId = (pid) => sessionIdFromLivePid(pid) != null) {
|
|
1450
935
|
const kindByPid = new Map(candidates.map(c => [c.pid, c.kind]));
|
|
1451
936
|
const nearestSameKindAncestor = (pid, kind) => {
|
|
@@ -1459,11 +944,6 @@ export function foldSubordinateAgents(candidates, ppidMap, readEntry, hasLiveSes
|
|
|
1459
944
|
}
|
|
1460
945
|
return undefined;
|
|
1461
946
|
};
|
|
1462
|
-
// Own launch identity: a matching-kind registry entry on the candidate or on
|
|
1463
|
-
// any wrapper between it and the pid it would fold into (exclusive). Entries
|
|
1464
|
-
// above the fold target belong to that ancestor's session, not this one.
|
|
1465
|
-
// A live `--session-id` on argv is also own identity (RUSH-2384) — the
|
|
1466
|
-
// registry is often empty while the process still names its session.
|
|
1467
947
|
const hasOwnSession = (c, stopPid) => {
|
|
1468
948
|
if (readEntry(c.pid)?.agent === c.kind)
|
|
1469
949
|
return true;
|
|
@@ -1493,15 +973,12 @@ export function foldSubordinateAgents(candidates, ppidMap, readEntry, hasLiveSes
|
|
|
1493
973
|
kept.push(c);
|
|
1494
974
|
continue;
|
|
1495
975
|
}
|
|
1496
|
-
// Walk up through folded intermediates to the nearest kept same-kind pid.
|
|
1497
976
|
let cur = nearestSameKindAncestor(c.pid, c.kind);
|
|
1498
977
|
const seen = new Set();
|
|
1499
978
|
while (cur !== undefined && !keptPids.has(cur) && !seen.has(cur)) {
|
|
1500
979
|
seen.add(cur);
|
|
1501
980
|
cur = nearestSameKindAncestor(cur, c.kind);
|
|
1502
981
|
}
|
|
1503
|
-
// A fold target must itself be a kept row (a ppid cycle from pid reuse
|
|
1504
|
-
// can orphan the whole chain) — otherwise keep the row rather than drop it.
|
|
1505
982
|
if (cur === undefined || !keptPids.has(cur)) {
|
|
1506
983
|
kept.push(c);
|
|
1507
984
|
continue;
|
|
@@ -1510,18 +987,6 @@ export function foldSubordinateAgents(candidates, ppidMap, readEntry, hasLiveSes
|
|
|
1510
987
|
}
|
|
1511
988
|
return { kept, foldedByRoot };
|
|
1512
989
|
}
|
|
1513
|
-
/**
|
|
1514
|
-
* Agent processes not attributed to a team or the runtime registry.
|
|
1515
|
-
* Classified by walking the ppid chain: any recognised UI ancestor (IDE
|
|
1516
|
-
* helper, terminal-app, or multiplexer) means `terminal`; nothing of the
|
|
1517
|
-
* sort means `headless` (daemon, launchd-spawned, orphan).
|
|
1518
|
-
*
|
|
1519
|
-
* Full `ps`+`lsof` rescans are throttled to {@link UNATTRIBUTED_RESCAN_MS}
|
|
1520
|
-
* (#2047). Between rescans the previous rows are re-emitted after dropping
|
|
1521
|
-
* dead PIDs and PIDs that are now attributed. A shrinking attributed set
|
|
1522
|
-
* forces a rescan so a process that just left teams/terminals can reappear
|
|
1523
|
-
* as headless.
|
|
1524
|
-
*/
|
|
1525
990
|
export async function listUnattributedActive(attributed) {
|
|
1526
991
|
const now = activeScanNow();
|
|
1527
992
|
if (unattributedCache &&
|
|
@@ -1533,7 +998,6 @@ export async function listUnattributedActive(attributed) {
|
|
|
1533
998
|
unattributedCache = { at: now, attributed: new Set(attributed), ...result };
|
|
1534
999
|
return result.sessions;
|
|
1535
1000
|
}
|
|
1536
|
-
/** Unthrottled headless scan (live process table + cwd probes). */
|
|
1537
1001
|
async function listUnattributedActiveLive(attributed) {
|
|
1538
1002
|
unattributedFullRescans += 1;
|
|
1539
1003
|
const table = await readProcessTable();
|
|
@@ -1543,7 +1007,6 @@ async function listUnattributedActiveLive(attributed) {
|
|
|
1543
1007
|
procByPid.set(r.pid, r);
|
|
1544
1008
|
ppidMap.set(r.pid, r.ppid);
|
|
1545
1009
|
}
|
|
1546
|
-
// Candidate PIDs first — we only shell out to lsof for these, not the whole table.
|
|
1547
1010
|
const candidates = [];
|
|
1548
1011
|
for (const { pid, kind } of table) {
|
|
1549
1012
|
if (!kind)
|
|
@@ -1554,45 +1017,17 @@ async function listUnattributedActiveLive(attributed) {
|
|
|
1554
1017
|
continue;
|
|
1555
1018
|
candidates.push({ pid, kind });
|
|
1556
1019
|
}
|
|
1557
|
-
// Forks/subagents of a live agent process collapse onto their root before
|
|
1558
|
-
// the cwd probes — fewer spawns, and one session stays one row even when
|
|
1559
|
-
// cwd-based dedupe is unavailable (Windows).
|
|
1560
1020
|
const { kept, foldedByRoot } = foldSubordinateAgents(candidates, ppidMap, readPidSessionEntry);
|
|
1561
|
-
// Bounded + staggered lsof probes: same cwds, but a trickle of spawns instead
|
|
1562
|
-
// of one simultaneous system-wide burst that behavioral EDR flags as recon.
|
|
1563
1021
|
const cwds = await resolveCwds(kept.map(c => c.pid));
|
|
1564
|
-
// The hook state dir is scanned at most ONCE per active-scan, and the ppid map
|
|
1565
|
-
// is inverted at most once — both built lazily on the first candidate that
|
|
1566
|
-
// lacks an exact launch-time id, so an all-Claude set does neither. The ~3s
|
|
1567
|
-
// poll must not re-read the dir (or re-invert the map) per candidate.
|
|
1568
1022
|
let hookIndex;
|
|
1569
1023
|
let children;
|
|
1570
|
-
// Durable `agents run --name` handles keyed by session id — the same source the
|
|
1571
|
-
// terminal path uses to name a row. Headless agents have no live-terminals
|
|
1572
|
-
// label and no /rename, so without this a `--name`d headless run would surface
|
|
1573
|
-
// with only a topic and no tab title. Built once per scan.
|
|
1574
1024
|
const runNameMap = buildRunNameMap();
|
|
1575
1025
|
const ensureChildren = () => children ??= childrenByParent(ppidMap);
|
|
1576
1026
|
const out = [];
|
|
1577
1027
|
for (let i = 0; i < kept.length; i++) {
|
|
1578
1028
|
const { pid, kind } = kept[i];
|
|
1579
|
-
// The per-pid registry (written by `ag run` and the shim delegate) gives
|
|
1580
|
-
// the EXACT session id this pid was launched with — so N agents in one cwd
|
|
1581
|
-
// resolve to N distinct sessions instead of all collapsing onto the newest
|
|
1582
|
-
// .jsonl. The shim's entry may sit on a wrapper ancestor (Windows .cmd
|
|
1583
|
-
// path). Absent entirely (direct launch outside agents-cli) → heuristic.
|
|
1584
1029
|
const entry = readPidSessionEntry(pid) ?? readAncestorSessionEntry(pid, ppidMap, kind);
|
|
1585
|
-
// Exact session id, in priority: (1) the id we recorded at launch (Claude,
|
|
1586
|
-
// known up front via --session-id); (2) the live process's own argv
|
|
1587
|
-
// `--session-id` (RUSH-2384 — by-pid is often empty mid-run while the
|
|
1588
|
-
// process still advertises the id); (3) the agent's OWN SessionStart hook,
|
|
1589
|
-
// authoritative for non-Claude and for agents we didn't launch, joined by
|
|
1590
|
-
// launchId/terminalId/pid and kind-guarded against a stale reused-pid file;
|
|
1591
|
-
// (4) the newest-jsonl heuristic (sessionIdFromFile, below).
|
|
1592
1030
|
let exactId = entry?.sessionId ?? sessionIdFromLivePid(pid);
|
|
1593
|
-
// The hook record (when we fall to it) also carries the SessionStart `ts` — the
|
|
1594
|
-
// real session-start epoch. Capture it so terminal/headless rows get a
|
|
1595
|
-
// `startedAtMs` instead of rendering "0s ago" (they set none before this).
|
|
1596
1031
|
let hookRec;
|
|
1597
1032
|
if (!exactId) {
|
|
1598
1033
|
hookIndex ??= loadHookSessionIndex();
|
|
@@ -1605,11 +1040,6 @@ async function listUnattributedActiveLive(attributed) {
|
|
|
1605
1040
|
});
|
|
1606
1041
|
exactId = hookRec?.session_id;
|
|
1607
1042
|
}
|
|
1608
|
-
// RUSH-2501: the hook-sessions index (terminals/sessions/) is populated only
|
|
1609
|
-
// by @agents/session-tracker, which is not deployed on most fleet machines.
|
|
1610
|
-
// The DEPLOYED SessionStart hook writes to state/sessions/<pid>.json instead.
|
|
1611
|
-
// Try that path when the index lookup found nothing, so cursor/grok/kimi/droid
|
|
1612
|
-
// agents (which carry no --session-id argv) can be attributed.
|
|
1613
1043
|
if (!exactId) {
|
|
1614
1044
|
const stateRec = readStateSessionRecord(pid, entry?.startedAtMs);
|
|
1615
1045
|
if (stateRec)
|
|
@@ -1620,16 +1050,8 @@ async function listUnattributedActiveLive(attributed) {
|
|
|
1620
1050
|
const topic = sessionFile ? quickExtractTopic(sessionFile) : undefined;
|
|
1621
1051
|
const host = detectHost(pid, procByPid);
|
|
1622
1052
|
const context = host && UI_HOSTS.has(host) ? 'terminal' : 'headless';
|
|
1623
|
-
// pidAlive is true by construction: this pid was just enumerated from the
|
|
1624
|
-
// live process table, so an opaque (non-parseable) kind resolves to
|
|
1625
|
-
// `running`, not a fake `idle` or blanket `unknown`.
|
|
1626
1053
|
const { state, tokPerSec } = computeLiveSignals(kind, sessionFile, cwd, true);
|
|
1627
1054
|
const { birthtimeMs, mtimeMs } = sessionFileTimes(sessionFile);
|
|
1628
|
-
// Durable run name from `agents run --name`, resolved by the run's session id
|
|
1629
|
-
// — the tab-title handle for a run we launched by name. A headless row carries
|
|
1630
|
-
// no /rename label and no live-terminals label, so this handle IS its label
|
|
1631
|
-
// (mirrors listTeamsActive `label: a.name`). Absent a `--name`, label stays
|
|
1632
|
-
// undefined and the display falls back to the topic on its own — no band-aid.
|
|
1633
1055
|
const resolvedId = exactId ?? sessionIdFromFile(sessionFile);
|
|
1634
1056
|
const name = resolvedId ? runNameMap.get(resolvedId) ?? undefined : undefined;
|
|
1635
1057
|
const label = name;
|
|
@@ -1647,8 +1069,6 @@ async function listUnattributedActiveLive(attributed) {
|
|
|
1647
1069
|
topic,
|
|
1648
1070
|
tokPerSec,
|
|
1649
1071
|
sessionFile,
|
|
1650
|
-
// Session start: the hook's authoritative SessionStart `ts`, else the
|
|
1651
|
-
// transcript's creation time. Last activity: the transcript's last write.
|
|
1652
1072
|
startedAtMs: hookRec?.ts ?? birthtimeMs,
|
|
1653
1073
|
lastActivityMs: mtimeMs,
|
|
1654
1074
|
pidCount: 1 + (foldedByRoot.get(pid) ?? 0),
|
|
@@ -1657,43 +1077,15 @@ async function listUnattributedActiveLive(attributed) {
|
|
|
1657
1077
|
terminalId: entry?.terminalId ?? hookRec?.terminal_id,
|
|
1658
1078
|
}, state, sessionFile, true));
|
|
1659
1079
|
}
|
|
1660
|
-
// Housekeeping: drop registry files for pids that have since died.
|
|
1661
1080
|
prunePidSessionRegistry(isPidAlive);
|
|
1662
1081
|
return { sessions: out, ppidMap };
|
|
1663
1082
|
}
|
|
1664
|
-
/**
|
|
1665
|
-
* Attribute a single tmux pane to the agent actually running in it.
|
|
1666
|
-
*
|
|
1667
|
-
* The launch registry — written per bare-spawn AND per wrap, each stamped with the
|
|
1668
|
-
* `tmuxPane` it targeted (see src/lib/exec.ts) — is the EXACT, per-pane source of
|
|
1669
|
-
* truth. So an agent spawned into an EXISTING pane (a split, where `$TMUX` is
|
|
1670
|
-
* already set so no new session meta is stamped) is attributed to its OWN launch,
|
|
1671
|
-
* not the session's original agent — closing the gap where such an agent was
|
|
1672
|
-
* dropped by this source and left to the weaker ps-scan fallback. Session-meta
|
|
1673
|
-
* labels remain the fallback for the wrapped origin pane of a session whose
|
|
1674
|
-
* registry entry is absent (a failed best-effort write, or a legacy session that
|
|
1675
|
-
* predates the registry's `tmuxPane` field) — and ONLY for that origin pane
|
|
1676
|
-
* (`meta.pane`), so a split shell pane of a labeled session isn't mis-attributed
|
|
1677
|
-
* the wrapped agent. When `meta.pane` is unknown (attach-existing sessions), any
|
|
1678
|
-
* labeled pane is accepted and the caller's per-session dedupe keeps one.
|
|
1679
|
-
* `source: 'teams'` panes are skipped — teammates are surfaced by listTeamsActive.
|
|
1680
|
-
* Pure so it is unit-tested without tmux.
|
|
1681
|
-
*/
|
|
1682
1083
|
export function resolvePaneIdentity(pane, sessName, meta, liveEntry, getHookIndex, nameToFullId) {
|
|
1683
1084
|
if (meta?.source === 'teams')
|
|
1684
1085
|
return undefined;
|
|
1685
|
-
// The tmux session name encodes the agent kind (100% of ag-* panes) and, for a
|
|
1686
|
-
// spawn whose id was known at creation (Claude), the session-id prefix — already
|
|
1687
|
-
// resolved to a full UUID in the batch map. It is the last-resort id source when
|
|
1688
|
-
// every durable record is missing (the common fleet case: meta/pid-reg/hook all
|
|
1689
|
-
// ~3% populated), and would otherwise leave the pane id-less and mis-collapsed.
|
|
1690
1086
|
const nameAgent = agentKindFromName(sessName);
|
|
1691
1087
|
const nameSessionId = nameToFullId.get(sessName);
|
|
1692
1088
|
if (liveEntry) {
|
|
1693
|
-
// Exact id: the id recorded at launch (Claude), else the agent's own
|
|
1694
|
-
// SessionStart hook joined by launchId/terminalId (non-Claude, or agents we
|
|
1695
|
-
// didn't launch) — kind-guarded against a stale reused-pid file — else the id
|
|
1696
|
-
// carried in the pane's own tmux name.
|
|
1697
1089
|
const sessionId = liveEntry.sessionId
|
|
1698
1090
|
?? resolveHookSessionRecord(getHookIndex(), {
|
|
1699
1091
|
pid: liveEntry.pid,
|
|
@@ -1704,10 +1096,6 @@ export function resolvePaneIdentity(pane, sessName, meta, liveEntry, getHookInde
|
|
|
1704
1096
|
?? nameSessionId;
|
|
1705
1097
|
return { agent: liveEntry.agent, harness: liveEntry.harness, sessionId, pid: liveEntry.pid };
|
|
1706
1098
|
}
|
|
1707
|
-
// No live-registry entry. Session-meta labels are the wrapped-origin fallback;
|
|
1708
|
-
// prefer them, then fall back to the name so a pane with neither a registry
|
|
1709
|
-
// entry nor meta labels still resolves (agent from the name, id from the batch
|
|
1710
|
-
// map when present) instead of being dropped and mis-attributed by the ps-scan.
|
|
1711
1099
|
const agent = meta?.labels?.agent;
|
|
1712
1100
|
const sessionId = meta?.labels?.sessionId;
|
|
1713
1101
|
if (agent && sessionId && (meta?.pane == null || meta.pane === pane))
|
|
@@ -1716,25 +1104,11 @@ export function resolvePaneIdentity(pane, sessName, meta, liveEntry, getHookInde
|
|
|
1716
1104
|
return { agent: nameAgent, sessionId: nameSessionId };
|
|
1717
1105
|
return undefined;
|
|
1718
1106
|
}
|
|
1719
|
-
/**
|
|
1720
|
-
* Agents hosted in the shared-socket tmux server — the authoritative source for
|
|
1721
|
-
* tmux-hosted interactive spawns (see src/lib/exec.ts `runInTmux`). Enumerates
|
|
1722
|
-
* every pane on the shared socket and attributes each to the agent running in it
|
|
1723
|
-
* via {@link resolvePaneIdentity}: the per-pane launch registry (exact) first, the
|
|
1724
|
-
* session-meta labels as fallback. Because tmux (not a per-window
|
|
1725
|
-
* `live-terminals.json`) is the source of truth, a tmux-hosted agent is captured
|
|
1726
|
-
* with its exact `%pane` even when the extension registry is stale — INCLUDING an
|
|
1727
|
-
* agent bare-spawned into a split of an existing session, which older logic dropped
|
|
1728
|
-
* (it kept only the first pane per session meta). `source: 'teams'` is skipped.
|
|
1729
|
-
*/
|
|
1730
1107
|
export async function listTmuxAgentSessions() {
|
|
1731
1108
|
const { getDefaultSocketPath } = await import('../tmux/paths.js');
|
|
1732
1109
|
const { readSessionMeta } = await import('../tmux/session.js');
|
|
1733
1110
|
const { runTmux } = await import('../tmux/binary.js');
|
|
1734
1111
|
const socket = getDefaultSocketPath();
|
|
1735
|
-
// No server has ever run on this socket — genuinely nothing to report, not
|
|
1736
|
-
// a degraded read. Distinct from the throws below, which mean tmux truth
|
|
1737
|
-
// exists but could not be read (RUSH-2507).
|
|
1738
1112
|
if (!fs.existsSync(socket))
|
|
1739
1113
|
return [];
|
|
1740
1114
|
let res;
|
|
@@ -1743,11 +1117,6 @@ export async function listTmuxAgentSessions() {
|
|
|
1743
1117
|
socket,
|
|
1744
1118
|
args: ['list-panes', '-a', '-F', ['#{pane_id}', '#{session_name}', '#{pane_pid}', '#{pane_dead}', '#{pane_current_path}'].join(TMUX_FIELD_SEP)],
|
|
1745
1119
|
throwOnError: false,
|
|
1746
|
-
// A wedged tmux server must not hang the whole active-session scan. The
|
|
1747
|
-
// catch below turns a timeout into a DEGRADED-tmux-source signal (the
|
|
1748
|
-
// other sources still report) rather than a frozen `agents sessions
|
|
1749
|
-
// --active` — and, crucially, no longer collapses into the same silent
|
|
1750
|
-
// `[]` a genuinely-idle socket returns (RUSH-2507).
|
|
1751
1120
|
timeoutMs: TMUX_LIST_PANES_TIMEOUT_MS,
|
|
1752
1121
|
});
|
|
1753
1122
|
}
|
|
@@ -1757,9 +1126,6 @@ export async function listTmuxAgentSessions() {
|
|
|
1757
1126
|
if (res.code !== 0) {
|
|
1758
1127
|
throw new TmuxDiscoveryDegradedError(`tmux list-panes on ${socket} exited ${res.code}: ${res.stderr.trim() || res.stdout.trim()}`);
|
|
1759
1128
|
}
|
|
1760
|
-
// Index live launches by the pane they target, so a pane we did NOT wrap (a
|
|
1761
|
-
// split) resolves to its own agent. Newest launch wins a pane (pid reuse), and
|
|
1762
|
-
// only live pids count — a dead agent's stale entry can't light up its old pane.
|
|
1763
1129
|
const liveByPane = new Map();
|
|
1764
1130
|
for (const recorded of listPidSessionEntries()) {
|
|
1765
1131
|
const e = readPidSessionEntry(recorded.pid);
|
|
@@ -1769,22 +1135,14 @@ export async function listTmuxAgentSessions() {
|
|
|
1769
1135
|
if (!prev || e.startedAtMs > prev.startedAtMs)
|
|
1770
1136
|
liveByPane.set(e.tmuxPane, e);
|
|
1771
1137
|
}
|
|
1772
|
-
// Hook index is scanned lazily — only when a live launch lacks a recorded id
|
|
1773
|
-
// (a non-Claude split) and needs the SessionStart-hook join.
|
|
1774
1138
|
let hookIndex;
|
|
1775
1139
|
const getHookIndex = () => (hookIndex ??= loadHookSessionIndex());
|
|
1776
|
-
// Resolve every `ag-<agent>-<shortid>` pane name to its full session UUID in one
|
|
1777
|
-
// batched DB round-trip, so resolvePaneIdentity can recover the id straight from
|
|
1778
|
-
// the pane name — the signal present on 100% of ag-* panes when the durable
|
|
1779
|
-
// identity stores are empty.
|
|
1780
1140
|
const nameToFullId = resolveNamesToSessionIds(res.stdout.split('\n').map((l) => l.split(TMUX_FIELD_SEP)[1]).filter((n) => !!n), { findSessionsByShortIds });
|
|
1781
1141
|
const out = [];
|
|
1782
1142
|
const seen = new Set();
|
|
1783
1143
|
for (const line of res.stdout.split('\n')) {
|
|
1784
1144
|
if (!line.trim())
|
|
1785
1145
|
continue;
|
|
1786
|
-
// The path is the LAST field, so rejoin its tail: a directory containing the
|
|
1787
|
-
// separator must not truncate it (the earlier fields cannot contain one).
|
|
1788
1146
|
const parts = line.split(TMUX_FIELD_SEP);
|
|
1789
1147
|
const [pane, sessName, pidRaw, paneDeadRaw] = parts;
|
|
1790
1148
|
const curPath = parts.slice(4).join(TMUX_FIELD_SEP);
|
|
@@ -1793,19 +1151,8 @@ export async function listTmuxAgentSessions() {
|
|
|
1793
1151
|
const meta = readSessionMeta(sessName);
|
|
1794
1152
|
const liveEntry = liveByPane.get(pane);
|
|
1795
1153
|
let id = resolvePaneIdentity(pane, sessName, meta, liveEntry, getHookIndex, nameToFullId);
|
|
1796
|
-
// Only a genuinely foreign pane — no live entry, no meta labels, and not one
|
|
1797
|
-
// of our `ag-*` names — is dropped now; every agent pane survives to be either
|
|
1798
|
-
// id-resolved or emitted as its own distinct id-less row.
|
|
1799
1154
|
if (!id)
|
|
1800
1155
|
continue;
|
|
1801
|
-
// RUSH-2007 Layer A: a non-Claude tmux session whose id resolved via neither the
|
|
1802
|
-
// launch registry (no id minted at spawn) nor the session-tracker index (not
|
|
1803
|
-
// deployed on the fleet — its dir is empty) — backfill it from the DEPLOYED
|
|
1804
|
-
// hook's own per-pid record at state/sessions/<pid>.json. Targeted single-file
|
|
1805
|
-
// reads on the pane leaf pid, then the registry launch pid; freshness-guarded by
|
|
1806
|
-
// the launch's known start so a reused-pid graveyard file can't cross sessions.
|
|
1807
|
-
// Without this the session surfaces id-less (keyed on the bare pane) and is
|
|
1808
|
-
// invisible to `agents sessions focus` — the remaining RUSH-2007 discovery gap.
|
|
1809
1156
|
if (!id.sessionId) {
|
|
1810
1157
|
const panePid = parseInt(pidRaw, 10) || undefined;
|
|
1811
1158
|
const backfilled = (panePid ? readStateSessionRecord(panePid, liveEntry?.startedAtMs)?.session_id : undefined)
|
|
@@ -1816,34 +1163,18 @@ export async function listTmuxAgentSessions() {
|
|
|
1816
1163
|
if (id.sessionId && shortIdFromName(sessName)) {
|
|
1817
1164
|
writeSessionAliasRecord(id.sessionId, sessName);
|
|
1818
1165
|
}
|
|
1819
|
-
// Dedupe by resolved session id; an as-yet-unresolved id (a hookless/lagging
|
|
1820
|
-
// split) keys on the unique pane so it still surfaces as its own row.
|
|
1821
1166
|
const dedupKey = id.sessionId ?? pane;
|
|
1822
1167
|
if (seen.has(dedupKey))
|
|
1823
1168
|
continue;
|
|
1824
1169
|
seen.add(dedupKey);
|
|
1825
|
-
// Prefer the registry pid (the agent), falling back to the pane leaf pid. A
|
|
1826
|
-
// pid we emit here goes into getActiveSessions' attributed set, so the ps-scan
|
|
1827
|
-
// does NOT also surface this agent as a duplicate headless row.
|
|
1828
1170
|
const pid = id.pid ?? (parseInt(pidRaw, 10) || undefined);
|
|
1829
1171
|
const cwd = liveEntry?.cwd ?? meta?.cwd ?? (curPath || undefined);
|
|
1830
|
-
// Only resolve a transcript when we KNOW the session id. With no id,
|
|
1831
|
-
// findSessionFileForKind falls back to the newest .jsonl in the cwd — which
|
|
1832
|
-
// collapses every co-located pane onto one stranger's transcript (the ×N-badge
|
|
1833
|
-
// bug). Refuse to guess: an id-less pane surfaces as its own row instead.
|
|
1834
1172
|
const sessionFile = id.sessionId ? findSessionFileForKind(id.agent, cwd, id.sessionId) : undefined;
|
|
1835
1173
|
const topic = sessionFile ? quickExtractTopic(sessionFile) : undefined;
|
|
1836
|
-
// `remain-on-exit` retains the pane after its child exits. pane_dead is the
|
|
1837
|
-
// authoritative signal; a retained pane is diagnostic, not attachable.
|
|
1838
1174
|
const paneDead = paneDeadRaw === '1';
|
|
1839
1175
|
const pidAlive = !paneDead && (pid ? isPidAlive(pid, liveEntry?.startedAtMs) : true);
|
|
1840
1176
|
const { state, tokPerSec } = computeLiveSignals(id.agent, sessionFile, cwd, pidAlive);
|
|
1841
1177
|
const { birthtimeMs, mtimeMs } = sessionFileTimes(sessionFile);
|
|
1842
|
-
// The mux/reply rails are known exactly here (the pane IS a tmux pane), so we
|
|
1843
|
-
// stamp them off the pane. `transport:'local'` is only a placeholder: the pane
|
|
1844
|
-
// can't reveal how the shell above it was reached. enrichProvenance later reads
|
|
1845
|
-
// the pane process's env and upgrades this to 'ssh' (with the real origin) when
|
|
1846
|
-
// SSH_CONNECTION is present, while preserving this mux/reply.
|
|
1847
1178
|
const provenance = {
|
|
1848
1179
|
host: os.hostname(),
|
|
1849
1180
|
transport: 'local',
|
|
@@ -1861,34 +1192,18 @@ export async function listTmuxAgentSessions() {
|
|
|
1861
1192
|
topic,
|
|
1862
1193
|
tokPerSec,
|
|
1863
1194
|
sessionFile,
|
|
1864
|
-
// Prefer the launch registry's start when known (more accurate than
|
|
1865
|
-
// transcript birth for a resumed/reused file); else transcript times.
|
|
1866
1195
|
startedAtMs: liveEntry?.startedAtMs ?? birthtimeMs,
|
|
1867
1196
|
lastActivityMs: mtimeMs,
|
|
1868
1197
|
provenance,
|
|
1869
1198
|
owner: resolveOwner(liveEntry?.actor, id.sessionId ?? sessionIdFromFile(sessionFile)),
|
|
1870
|
-
// Factory / --active join key: AGENT_TERMINAL_ID stamped on the launch
|
|
1871
|
-
// registry and preserved by SessionStart. Without this, Grok/Codex tmux
|
|
1872
|
-
// panes never surface terminalId even when by-pid has it (RUSH-2192).
|
|
1873
1199
|
launchId: liveEntry?.launchId,
|
|
1874
1200
|
terminalId: liveEntry?.terminalId,
|
|
1875
|
-
// An id-less pane keys its dedupe on the unique pane, so two anonymous
|
|
1876
|
-
// co-located panes stay two rows instead of folding into one.
|
|
1877
1201
|
paneId: id.sessionId ?? sessionIdFromFile(sessionFile) ? undefined : pane,
|
|
1878
|
-
// The tmux session name is the one selector an id-less row still exposes —
|
|
1879
|
-
// its `<shortid>` suffix is what `sessions inject` matches on (PHNX-3688).
|
|
1880
1202
|
tmuxName: sessName,
|
|
1881
1203
|
}, state, sessionFile, pidAlive));
|
|
1882
1204
|
}
|
|
1883
1205
|
return out;
|
|
1884
1206
|
}
|
|
1885
|
-
/**
|
|
1886
|
-
* Union of all sources. Teams and terminals spawn actual CLI processes that
|
|
1887
|
-
* also show up in `ps`, so headless attribution runs last with the already-
|
|
1888
|
-
* attributed PIDs removed. The tmux source goes FIRST into the dedupe so a
|
|
1889
|
-
* tmux-hosted agent's row (which carries the exact `%pane`) wins over a staler
|
|
1890
|
-
* terminal/headless row for the same session id.
|
|
1891
|
-
*/
|
|
1892
1207
|
export async function getActiveSessions(opts = {}) {
|
|
1893
1208
|
if (!writerProcessView()) {
|
|
1894
1209
|
const { loadLocalActiveSessions } = await import('./session-cache.js');
|
|
@@ -1917,29 +1232,12 @@ export async function getActiveSessions(opts = {}) {
|
|
|
1917
1232
|
foldPresence(merged);
|
|
1918
1233
|
await foldTmuxClients(merged);
|
|
1919
1234
|
foldHostLink(merged);
|
|
1920
|
-
// Runs before the row leaves this box so every consumer — local render, the
|
|
1921
|
-
// `--json` a peer answers a fan-out with, the browser's device filter — sees
|
|
1922
|
-
// the EXECUTION host rather than the dispatcher (RUSH-2479).
|
|
1923
1235
|
foldExecutionMachine(merged, recordedMachineLookup(merged), machineId());
|
|
1924
1236
|
annotateOrchestratorLabels(merged);
|
|
1925
|
-
// Last: derive the shown title (recap ladder) from label/tail/topic now that
|
|
1926
|
-
// every row's live tail + label are resolved (RUSH-3011).
|
|
1927
1237
|
foldRecap(merged);
|
|
1928
|
-
// Project the coarse lifecycle bucket once status is final (foldHostLink above may
|
|
1929
|
-
// have promoted a row to orphaned/crashed) so consumers read `phase` off the row
|
|
1930
|
-
// instead of re-deriving it (PHNX-2484).
|
|
1931
1238
|
foldPhase(merged);
|
|
1932
1239
|
return merged;
|
|
1933
1240
|
}
|
|
1934
|
-
/**
|
|
1935
|
-
* Cheap post-hoc health probe for the local discovery sources. `getActiveSessions`
|
|
1936
|
-
* always swallows a source failure to `[]` so its return type stays a plain
|
|
1937
|
-
* session array — this re-probes the same sources and reports which ones threw,
|
|
1938
|
-
* so a caller that got an empty list back can tell "genuinely nothing running"
|
|
1939
|
-
* apart from "a source errored and got silently folded into empty" (RUSH-2507).
|
|
1940
|
-
* Intended for the empty-result branch only, not the hot path — it repeats a
|
|
1941
|
-
* `list-panes` call `getActiveSessions` already made.
|
|
1942
|
-
*/
|
|
1943
1241
|
export async function describeActiveDiscoveryHealth() {
|
|
1944
1242
|
const degradedSources = [];
|
|
1945
1243
|
try {
|
|
@@ -1951,14 +1249,6 @@ export async function describeActiveDiscoveryHealth() {
|
|
|
1951
1249
|
}
|
|
1952
1250
|
return { degradedSources };
|
|
1953
1251
|
}
|
|
1954
|
-
/**
|
|
1955
|
-
* True when a live agent process on this host carries `--session-id <id>` in
|
|
1956
|
-
* its argv. RUSH-2384 last-resort for `agents message`: getActiveSessions can
|
|
1957
|
-
* still miss a row (teams status flap, attributed-ancestor skip), but the
|
|
1958
|
-
* process table + argv are ground truth for "this session is alive and
|
|
1959
|
-
* mailbox-reachable". Only UUID-shaped ids are accepted so a short prefix
|
|
1960
|
-
* never false-matches.
|
|
1961
|
-
*/
|
|
1962
1252
|
export async function isSessionIdLiveOnProcessTable(sessionId, deps = {}) {
|
|
1963
1253
|
if (!isSessionIdShape(sessionId))
|
|
1964
1254
|
return false;
|
|
@@ -1972,22 +1262,6 @@ export async function isSessionIdLiveOnProcessTable(sessionId, deps = {}) {
|
|
|
1972
1262
|
}
|
|
1973
1263
|
return false;
|
|
1974
1264
|
}
|
|
1975
|
-
/**
|
|
1976
|
-
* Fold tmux's attached-client count onto every tmux-hosted row.
|
|
1977
|
-
*
|
|
1978
|
-
* Keyed off `provenance.mux` — which {@link enrichProvenance} has already stamped
|
|
1979
|
-
* on any row whose process env names a tmux pane — rather than off
|
|
1980
|
-
* {@link listTmuxAgentSessions}. That source only emits a row when it can resolve
|
|
1981
|
-
* the pane's agent IDENTITY (launch registry or session meta), and on a machine
|
|
1982
|
-
* where neither resolves it emits nothing at all while the same sessions still
|
|
1983
|
-
* arrive through the terminal/headless sources carrying full tmux provenance.
|
|
1984
|
-
* Hanging the client count off the identity-resolving source would have made the
|
|
1985
|
-
* whole orphan signal silently dead on exactly those machines.
|
|
1986
|
-
*
|
|
1987
|
-
* One `list-panes` per distinct socket, and only when some row is tmux-hosted —
|
|
1988
|
-
* a fleet with no tmux pays nothing. A query failure leaves the count undefined,
|
|
1989
|
-
* which the classifier reads as "cannot tell", never as a false zero.
|
|
1990
|
-
*/
|
|
1991
1265
|
export async function foldTmuxClients(rows) {
|
|
1992
1266
|
const tmuxRows = rows.filter((s) => s.provenance?.mux?.kind === 'tmux' && s.provenance.mux.pane);
|
|
1993
1267
|
if (tmuxRows.length === 0)
|
|
@@ -2009,14 +1283,12 @@ export async function foldTmuxClients(rows) {
|
|
|
2009
1283
|
if (!pane)
|
|
2010
1284
|
continue;
|
|
2011
1285
|
const n = parseInt(attached ?? '', 10);
|
|
2012
|
-
// A tmux too old to report `session_attached` yields NaN — leave the pane
|
|
2013
|
-
// unmapped so it stays "cannot tell" rather than becoming a false zero.
|
|
2014
1286
|
if (Number.isFinite(n))
|
|
2015
1287
|
byPane.set(pane, n);
|
|
2016
1288
|
}
|
|
2017
1289
|
}
|
|
2018
1290
|
catch {
|
|
2019
|
-
continue;
|
|
1291
|
+
continue;
|
|
2020
1292
|
}
|
|
2021
1293
|
for (const s of tmuxRows) {
|
|
2022
1294
|
if (s.provenance.mux.socket !== socket)
|
|
@@ -2027,56 +1299,6 @@ export async function foldTmuxClients(rows) {
|
|
|
2027
1299
|
}
|
|
2028
1300
|
}
|
|
2029
1301
|
}
|
|
2030
|
-
/**
|
|
2031
|
-
* Fold the host link onto each row and, where it changes the answer, onto the
|
|
2032
|
-
* status. Runs AFTER {@link foldPresence}, because a deliberately backgrounded
|
|
2033
|
-
* session (`presence` `background`/`parked`) is supposed to have no client and
|
|
2034
|
-
* must not be reported as an orphan.
|
|
2035
|
-
*
|
|
2036
|
-
* Precedence is deliberate, and the two new statuses slot in where they add
|
|
2037
|
-
* information rather than destroy it:
|
|
2038
|
-
*
|
|
2039
|
-
* - `abandoned` wins outright. A days-stale session is already dangling; that
|
|
2040
|
-
* it also lost its window is not the headline, and it keeps a crashed row
|
|
2041
|
-
* from lingering as an alert forever.
|
|
2042
|
-
* - `crashed` REPLACES `closed`. Both mean the process is gone, but `closed`
|
|
2043
|
-
* reads as a normal exit; `crashed` says the host window went down with it
|
|
2044
|
-
* and never cleaned up.
|
|
2045
|
-
* - `orphaned` replaces `idle` / `input_required` on ANY `no-client`, and
|
|
2046
|
-
* `running` ONLY when the owning WINDOW was lost (`hostWindowLost`). A
|
|
2047
|
-
* session still working with merely zero tmux clients is a normal headless
|
|
2048
|
-
* run — since RUSH-3125 a detached remote pane is the steady state — so
|
|
2049
|
-
* flagging every one would bury the real signal (the false positive reverted
|
|
2050
|
-
* in 6d973b823). But a running agent whose IDE window stopped republishing
|
|
2051
|
-
* its heartbeat outlived an unclean host death and is genuinely stranded, so
|
|
2052
|
-
* it IS promoted. A session sitting idle — or worse, waiting on a question —
|
|
2053
|
-
* with no client attached is the same stranded case: nobody is coming.
|
|
2054
|
-
*/
|
|
2055
|
-
/**
|
|
2056
|
-
* Attribute each live row to the machine the session actually EXECUTES on.
|
|
2057
|
-
*
|
|
2058
|
-
* A host-dispatched run (`agents run --device <peer>`) leaves a live process on
|
|
2059
|
-
* the DISPATCHING box — the ssh/TTY shim — carrying the remote run's session id.
|
|
2060
|
-
* Nothing about that local process knows the agent is on the peer, so the row
|
|
2061
|
-
* was tagged with THIS machine: `--device <dispatcher>` then claimed a session
|
|
2062
|
-
* that is not running here, and its preview dead-ended at "full transcript not
|
|
2063
|
-
* indexed here" because the transcript lives on the peer (RUSH-2479).
|
|
2064
|
-
*
|
|
2065
|
-
* The dispatch already recorded the truth. `registerHostSession` /
|
|
2066
|
-
* `registerInteractiveHostSession` write the index row with
|
|
2067
|
-
* `machine: normalizeHost(task.host)` (`lib/hosts/session-index.ts:55,134`), so
|
|
2068
|
-
* this folds that recorded machine back onto the live row. Every consumer then
|
|
2069
|
-
* agrees on one owner: the `--device`/`--device` scope, the browser's device
|
|
2070
|
-
* filter, `_remote`/preview routing (`liveSessionToMeta`), and the id resolver.
|
|
2071
|
-
*
|
|
2072
|
-
* Two rows are deliberately left alone:
|
|
2073
|
-
* - one the cross-machine fan-out already attributed to a peer — that is the
|
|
2074
|
-
* peer's own self-report, which outranks this box's index copy;
|
|
2075
|
-
* - one whose indexed machine IS this box — nothing was offloaded.
|
|
2076
|
-
*
|
|
2077
|
-
* Pure over the array; `machineOf` is injected so the join is unit-tested
|
|
2078
|
-
* without a SQLite index.
|
|
2079
|
-
*/
|
|
2080
1302
|
export function foldExecutionMachine(rows, machineOf, self) {
|
|
2081
1303
|
for (const s of rows) {
|
|
2082
1304
|
if (!s.sessionId)
|
|
@@ -2090,72 +1312,26 @@ export function foldExecutionMachine(rows, machineOf, self) {
|
|
|
2090
1312
|
s.offloadedFrom = self;
|
|
2091
1313
|
}
|
|
2092
1314
|
}
|
|
2093
|
-
/**
|
|
2094
|
-
* Is the PROCESS behind this row running on this machine?
|
|
2095
|
-
*
|
|
2096
|
-
* `machine` answers a different question — "where does the agent execute" —
|
|
2097
|
-
* which is what a `--device` scope, preview routing, and resume ownership need.
|
|
2098
|
-
* For an offloaded run the two answers diverge: {@link foldExecutionMachine}
|
|
2099
|
-
* points `machine` at the peer, while the shim process, its tmux pane, and its
|
|
2100
|
-
* terminal window are all still HERE. `offloadedFrom` marks exactly that row.
|
|
2101
|
-
*
|
|
2102
|
-
* Any caller reaching for a LOCAL pid, pane, or window must ask this rather
|
|
2103
|
-
* than `machine === self`. A local tmux pane id (`%N`) handed to a peer's tmux
|
|
2104
|
-
* server does not fail — pane ids are small per-server integers, so it can
|
|
2105
|
-
* resolve against an unrelated pane and attach the user to someone else's
|
|
2106
|
-
* session. That is why this predicate exists instead of ten copies of the
|
|
2107
|
-
* comparison.
|
|
2108
|
-
*/
|
|
2109
1315
|
export function sessionProcessIsLocal(s, self) {
|
|
2110
|
-
// `offloadedFrom` names WHICH box holds the shim, so it must be compared, not
|
|
2111
|
-
// merely tested. These rows travel: `--active --json` spreads them verbatim
|
|
2112
|
-
// and the fan-out preserves their foreign `machine`, so a THIRD box sees
|
|
2113
|
-
// `{machine: B, offloadedFrom: A}` — a shim that is emphatically not its own.
|
|
2114
|
-
// Answering "local" there sends the caller down the local-tmux path with
|
|
2115
|
-
// A's pane id, attaching an unrelated pane on C: the same hazard this
|
|
2116
|
-
// predicate exists to prevent, one machine over.
|
|
2117
1316
|
if (s.offloadedFrom)
|
|
2118
1317
|
return s.offloadedFrom === self;
|
|
2119
1318
|
return !s.machine || s.machine === self;
|
|
2120
1319
|
}
|
|
2121
|
-
/**
|
|
2122
|
-
* The machine to reach for this session's PROCESS — its pid, tmux pane, and
|
|
2123
|
-
* window — or `undefined` when that process is right here.
|
|
2124
|
-
*
|
|
2125
|
-
* Not the same as `machine`, which is where the AGENT executes. For an offloaded
|
|
2126
|
-
* run the process lives on the dispatcher (`offloadedFrom`) while the agent runs
|
|
2127
|
-
* on the peer, so a caller that ssh'd to `machine` would carry the dispatcher's
|
|
2128
|
-
* pane id to a box that never had it.
|
|
2129
|
-
*/
|
|
2130
1320
|
export function sessionProcessHost(s, self) {
|
|
2131
1321
|
if (sessionProcessIsLocal(s, self))
|
|
2132
1322
|
return undefined;
|
|
2133
1323
|
return s.offloadedFrom ?? s.machine;
|
|
2134
1324
|
}
|
|
2135
|
-
/**
|
|
2136
|
-
* The index lookup behind {@link foldExecutionMachine}. One batched query for
|
|
2137
|
-
* every live row (`findSessionMachinesByIds`), not a per-row `SELECT *` —
|
|
2138
|
-
* `getActiveSessions` is a hot path the daemon, menubar, and watchdog all poll.
|
|
2139
|
-
* Best-effort: an unavailable DB yields an empty map, leaving rows attributed
|
|
2140
|
-
* to this box rather than failing the whole live view.
|
|
2141
|
-
*/
|
|
2142
1325
|
function recordedMachineLookup(rows) {
|
|
2143
1326
|
const byId = findSessionMachinesByIds(rows.map((s) => s.sessionId).filter((id) => !!id));
|
|
2144
1327
|
return (id) => byId.get(id);
|
|
2145
1328
|
}
|
|
2146
1329
|
export function foldHostLink(rows) {
|
|
2147
1330
|
for (const s of rows) {
|
|
2148
|
-
// Cloud tasks have no local pid, window, or tmux server; there is no host
|
|
2149
|
-
// link to classify and no honest answer to give.
|
|
2150
1331
|
if (s.context === 'cloud')
|
|
2151
1332
|
continue;
|
|
2152
|
-
// A days-stale row is already `abandoned`; whether its window is also gone
|
|
2153
|
-
// adds nothing, and its pid liveness is genuinely unknown from the status
|
|
2154
|
-
// (abandoned outranks closed), so claim no host link rather than guess one.
|
|
2155
1333
|
if (s.status === 'abandoned')
|
|
2156
1334
|
continue;
|
|
2157
|
-
// `closed` IS the dead-pid status — `lifecycleStatus` assigns it from
|
|
2158
|
-
// `!pidAlive` and nothing else — so the status is the pid answer here.
|
|
2159
1335
|
const signals = {
|
|
2160
1336
|
pidAlive: s.status !== 'closed',
|
|
2161
1337
|
windowHeartbeatMs: s.windowHeartbeatMs,
|
|
@@ -2164,69 +1340,29 @@ export function foldHostLink(rows) {
|
|
|
2164
1340
|
};
|
|
2165
1341
|
const link = classifyHostLink(signals);
|
|
2166
1342
|
s.hostLink = link;
|
|
2167
|
-
// `foldPresence` gives every terminal row a DERIVED `attached` — "a live
|
|
2168
|
-
// interactive TUI you're watching" — which is exactly the claim a lost host
|
|
2169
|
-
// disproves. A stored record never yields `attached` (it is background or
|
|
2170
|
-
// parked), so clearing only that value drops the derived lie and leaves a
|
|
2171
|
-
// real detach record untouched.
|
|
2172
|
-
// Only a POSITIVE loss signal disproves a derived `attached`. `unknown` means
|
|
2173
|
-
// we never looked — a bare terminal with no IDE window and no tmux has
|
|
2174
|
-
// neither input — so it must not clear presence, or every plain-terminal
|
|
2175
|
-
// session the user is sitting in would lose its `attached` marker.
|
|
2176
1343
|
if ((link === 'no-client' || link === 'host-gone') && s.presence === 'attached') {
|
|
2177
1344
|
s.presence = undefined;
|
|
2178
1345
|
}
|
|
2179
1346
|
if (link === 'host-gone' && s.status === 'closed')
|
|
2180
1347
|
s.status = 'crashed';
|
|
2181
|
-
// A clientless running remote pane is normal; only stopped work can be called orphaned.
|
|
2182
1348
|
else if (link === 'no-client' && (s.status === 'idle' || s.status === 'input_required')) {
|
|
2183
1349
|
s.status = 'orphaned';
|
|
2184
1350
|
}
|
|
2185
|
-
// A still-`running` agent is normally a healthy headless run, so 0 tmux
|
|
2186
|
-
// clients (a detached remote pane) MUST NOT flag it — that false positive was
|
|
2187
|
-
// reverted once already (6d973b823). The ONE exception is a lost WINDOW: its
|
|
2188
|
-
// owning IDE window stopped republishing, so it died uncleanly and the agent
|
|
2189
|
-
// outlived it — genuinely stranded (PHNX-3183, `hostWindowLost`, SES-18a).
|
|
2190
1351
|
else if (s.status === 'running' && hostWindowLost(signals)) {
|
|
2191
1352
|
s.status = 'orphaned';
|
|
2192
1353
|
}
|
|
2193
1354
|
}
|
|
2194
1355
|
}
|
|
2195
|
-
/** One display line, trimmed and capped, or undefined when empty. */
|
|
2196
1356
|
function recapLine(s, max = 120) {
|
|
2197
1357
|
const t = s?.replace(/\s+/g, ' ').trim();
|
|
2198
1358
|
if (!t)
|
|
2199
1359
|
return undefined;
|
|
2200
1360
|
return t.length > max ? t.slice(0, max - 1).trimEnd() + '…' : t;
|
|
2201
1361
|
}
|
|
2202
|
-
/**
|
|
2203
|
-
* The headline ladder (PHNX-3797): an explicit `/rename` label, then the
|
|
2204
|
-
* daemon-generated title, then the classified user prompt.
|
|
2205
|
-
*
|
|
2206
|
-
* The prompt rung classifies the RAW latest genuine user turn, not the
|
|
2207
|
-
* already-collapsed `topic` (PHNX-3939). Classifying `topic` was how a `/model`
|
|
2208
|
-
* echo or a skill body became a session's displayed title: `extractSessionTopic`
|
|
2209
|
-
* accepted text `cleanFirstUserMessage` rejected, and the recap then dressed up
|
|
2210
|
-
* that scaffolding as the user's words. It also never saw the row's attachments,
|
|
2211
|
-
* so an image-only turn could not be recognized. A row that has already been
|
|
2212
|
-
* merged with a daemon-folded {@link ActiveSession.request} uses it directly.
|
|
2213
|
-
*
|
|
2214
|
-
* The agent's last transcript line is deliberately NOT a rung. It is still
|
|
2215
|
-
* returned as `lastAgentLine` — and still shown, in the separate live
|
|
2216
|
-
* preview/activity slot — but a rolling monologue is not what the session IS,
|
|
2217
|
-
* and using it as the headline is the defect this ladder fixes. A session the
|
|
2218
|
-
* titler has not reached yet falls back to the classified user prompt, never
|
|
2219
|
-
* to the agent's last line.
|
|
2220
|
-
*/
|
|
2221
1362
|
export function deriveSessionRecap(row) {
|
|
2222
1363
|
const lastAgentLine = recapLine(row.tail?.length ? row.tail[row.tail.length - 1] : undefined);
|
|
2223
1364
|
const raw = row.lastUserMessage ?? row.firstUserMessage ?? row.topic ?? '';
|
|
2224
|
-
// No `turns` here on purpose: this tidy sees ONE turn, not the transcript, so
|
|
2225
|
-
// it cannot count them. The daemon fold sets it (see SessionRequest.turns).
|
|
2226
1365
|
const tidied = row.request ?? tidyRequest(raw, { attachments: row.attachments });
|
|
2227
|
-
// No genuine turn to show (a synthetic-only transcript, or nothing indexed
|
|
2228
|
-
// yet): fall back to the legacy classifier over whatever `topic` holds rather
|
|
2229
|
-
// than showing an empty prompt rung.
|
|
2230
1366
|
const fallback = tidied ? undefined : classifyUserPrompt(row.topic ?? '', {
|
|
2231
1367
|
hasImageAttachment: row.attachments?.some((a) => a.mediaType?.startsWith('image/')),
|
|
2232
1368
|
});
|
|
@@ -2258,28 +1394,13 @@ export function deriveSessionRecap(row) {
|
|
|
2258
1394
|
...(tidied ? { request: tidied } : {}),
|
|
2259
1395
|
};
|
|
2260
1396
|
}
|
|
2261
|
-
/**
|
|
2262
|
-
* The row's SECONDARY line (PHNX-3797 owner feedback): the most important recent
|
|
2263
|
-
* agent message, ranked so the operator sees a block before generic chatter —
|
|
2264
|
-
* a pending question, then a needs-you wait (plan review / permission /
|
|
2265
|
-
* input-required), then the current activity (the agent's latest line). Returns
|
|
2266
|
-
* `undefined` only when there is nothing recent to show at all.
|
|
2267
|
-
*
|
|
2268
|
-
* Pure over the fields it reads so the ranking is unit-tested directly; folded
|
|
2269
|
-
* onto every row by {@link applyRecap} beside the {@link deriveSessionRecap} title.
|
|
2270
|
-
*/
|
|
2271
1397
|
export function deriveImportantMessage(row) {
|
|
2272
|
-
// The agent's latest words — the fallback text for every kind. `lastAgentLine`
|
|
2273
|
-
// is already trimmed/capped; `preview` (a live tool/turn line) is not.
|
|
2274
1398
|
const recent = recapLine(row.preview) ?? row.lastAgentLine;
|
|
2275
|
-
// A pending question is the single most important thing an agent can be saying.
|
|
2276
1399
|
if (row.awaitingReason === 'question' || row.question) {
|
|
2277
1400
|
const text = recapLine(row.question?.text) ?? recent;
|
|
2278
1401
|
if (text)
|
|
2279
1402
|
return { text, kind: 'question' };
|
|
2280
1403
|
}
|
|
2281
|
-
// Blocked on the operator: a plan review, a permission decision, or a bare
|
|
2282
|
-
// input-required wait. The "needs you" case the Fleet has to make loud.
|
|
2283
1404
|
const needsYou = row.awaitingReason === 'plan_review'
|
|
2284
1405
|
|| row.awaitingReason === 'permission'
|
|
2285
1406
|
|| row.status === 'input_required'
|
|
@@ -2292,12 +1413,10 @@ export function deriveImportantMessage(row) {
|
|
|
2292
1413
|
: 'Waiting for you';
|
|
2293
1414
|
return { text: recent ?? fallback, kind: 'needs_you' };
|
|
2294
1415
|
}
|
|
2295
|
-
// Nothing blocking — just what the agent is doing now.
|
|
2296
1416
|
if (recent)
|
|
2297
1417
|
return { text: recent, kind: 'activity' };
|
|
2298
1418
|
return undefined;
|
|
2299
1419
|
}
|
|
2300
|
-
/** Write one row's recap fields from its current label/generatedTitle/topic/tail. */
|
|
2301
1420
|
function applyRecap(s) {
|
|
2302
1421
|
const recap = deriveSessionRecap(s);
|
|
2303
1422
|
s.title = recap.title;
|
|
@@ -2307,24 +1426,12 @@ function applyRecap(s) {
|
|
|
2307
1426
|
s.lastAgentLine = recap.lastAgentLine;
|
|
2308
1427
|
if (recap.request)
|
|
2309
1428
|
s.request = recap.request;
|
|
2310
|
-
// The secondary line rides the same fold as the headline (PHNX-3797), so every
|
|
2311
|
-
// consumer that reads `title` off a row reads `importantMessage` beside it.
|
|
2312
1429
|
s.importantMessage = deriveImportantMessage(s);
|
|
2313
1430
|
}
|
|
2314
|
-
/** Fold the recap ladder onto every row (see {@link deriveSessionRecap}). */
|
|
2315
1431
|
export function foldRecap(rows) {
|
|
2316
1432
|
for (const s of rows)
|
|
2317
1433
|
applyRecap(s);
|
|
2318
1434
|
}
|
|
2319
|
-
/**
|
|
2320
|
-
* Project a {@link SessionPhase} from the finalized {@link ActiveStatus} (PHNX-2484).
|
|
2321
|
-
* This is the single source of truth the AGI EXT previously mirrored as
|
|
2322
|
-
* `mapStatusToPhase` — the ext now reads the `phase` field instead of re-deriving it.
|
|
2323
|
-
* `queued` counts as `running` (dispatched, in the pipeline); `abandoned`, `orphaned`,
|
|
2324
|
-
* and `crashed` all bucket to `failed` (dangling/dead — needs attention), which is the
|
|
2325
|
-
* drift this fixes: those three used to fall through a status-only map to `idle` and
|
|
2326
|
-
* silently hide a dead agent.
|
|
2327
|
-
*/
|
|
2328
1435
|
export function derivePhase(status) {
|
|
2329
1436
|
switch (status) {
|
|
2330
1437
|
case 'running':
|
|
@@ -2344,21 +1451,13 @@ export function derivePhase(status) {
|
|
|
2344
1451
|
return 'idle';
|
|
2345
1452
|
}
|
|
2346
1453
|
}
|
|
2347
|
-
/** Fold the coarse lifecycle {@link SessionPhase} onto every row (see {@link derivePhase}). */
|
|
2348
1454
|
export function foldPhase(rows) {
|
|
2349
1455
|
for (const s of rows)
|
|
2350
1456
|
s.phase = derivePhase(s.status);
|
|
2351
1457
|
}
|
|
2352
|
-
/** Reap only stale sessions with proven-dead pids; unknown or live processes remain recoverable. */
|
|
2353
1458
|
export function isReapableOrphan(row) {
|
|
2354
1459
|
return row.status === 'abandoned' && row.pidAlive === false;
|
|
2355
1460
|
}
|
|
2356
|
-
/**
|
|
2357
|
-
* Resolve each teams row's `orchestratorLabel` from the orchestrator's own row,
|
|
2358
|
-
* when that orchestrator session is itself in the active set (it usually is — the
|
|
2359
|
-
* agent that ran `agents teams add` is running). Falls back to nothing, so the
|
|
2360
|
-
* renderer shows the short id. Pure over the array; exported for tests.
|
|
2361
|
-
*/
|
|
2362
1461
|
export function annotateOrchestratorLabels(sessions) {
|
|
2363
1462
|
const byId = new Map();
|
|
2364
1463
|
for (const s of sessions)
|
|
@@ -2368,17 +1467,11 @@ export function annotateOrchestratorLabels(sessions) {
|
|
|
2368
1467
|
if (!s.orchestratorSessionId)
|
|
2369
1468
|
continue;
|
|
2370
1469
|
const orch = byId.get(s.orchestratorSessionId);
|
|
2371
|
-
// ladder-exempt:
|
|
1470
|
+
// ladder-exempt: pre-index team label, not a headline render.
|
|
2372
1471
|
if (orch)
|
|
2373
1472
|
s.orchestratorLabel = orch.label || orch.topic || undefined;
|
|
2374
1473
|
}
|
|
2375
1474
|
}
|
|
2376
|
-
/**
|
|
2377
|
-
* Fold detach/attach presence onto each row from the detach store. A stored
|
|
2378
|
-
* record wins (`background`/`parked`); otherwise a live terminal session is
|
|
2379
|
-
* `attached`. Ad-hoc headless runs and cloud/team rows stay unmarked — they are
|
|
2380
|
-
* not on the foreground/background axis.
|
|
2381
|
-
*/
|
|
2382
1475
|
function foldPresence(rows) {
|
|
2383
1476
|
for (const s of rows) {
|
|
2384
1477
|
if (!s.sessionId)
|
|
@@ -2390,23 +1483,6 @@ function foldPresence(rows) {
|
|
|
2390
1483
|
s.presence = 'attached';
|
|
2391
1484
|
}
|
|
2392
1485
|
}
|
|
2393
|
-
/**
|
|
2394
|
-
* Attach provenance (host / local-vs-SSH / tmux pane / reply rail) to every
|
|
2395
|
-
* session that has a live pid. Mutates in place. Runs after dedupe so we probe
|
|
2396
|
-
* each session once, not once per fork pid. Probes run in parallel — each is a
|
|
2397
|
-
* single /proc read (Linux) or `ps` call (macOS); failures leave `provenance`
|
|
2398
|
-
* undefined rather than blocking the listing. The probes use the same bounded
|
|
2399
|
-
* concurrency as the adjacent per-PID lsof sweep so large session lists cannot
|
|
2400
|
-
* spawn one `ps eww` subprocess per row at once on macOS.
|
|
2401
|
-
*
|
|
2402
|
-
* A row that already carries provenance (the tmux path, which knows its exact
|
|
2403
|
-
* mux/reply from the pane) is not skipped — it is probe-and-MERGED. The tmux
|
|
2404
|
-
* path can only stamp a `transport:'local'` placeholder because the pane alone
|
|
2405
|
-
* doesn't reveal how the shell above it was reached; the process env does. So we
|
|
2406
|
-
* still read the env and fill in the real SSH origin/term, while preserving the
|
|
2407
|
-
* authoritative mux/reply the pane already gave us. Skipping this (the old
|
|
2408
|
-
* behavior) is exactly why ssh-launched tmux sessions rendered as local.
|
|
2409
|
-
*/
|
|
2410
1486
|
export async function enrichProvenance(sessions, probe = detectProvenance) {
|
|
2411
1487
|
await mapBounded(sessions, async (s) => {
|
|
2412
1488
|
if (!s.pid)
|
|
@@ -2418,8 +1494,6 @@ export async function enrichProvenance(sessions, probe = detectProvenance) {
|
|
|
2418
1494
|
s.provenance = probed;
|
|
2419
1495
|
return;
|
|
2420
1496
|
}
|
|
2421
|
-
// Row already carries exact mux/reply (the tmux path). Fill only what a
|
|
2422
|
-
// pre-set provenance can't know from the pane alone: the real launch origin.
|
|
2423
1497
|
if (probed.transport === 'ssh' && !s.provenance.ssh) {
|
|
2424
1498
|
s.provenance.transport = 'ssh';
|
|
2425
1499
|
s.provenance.ssh = probed.ssh;
|
|
@@ -2428,11 +1502,6 @@ export async function enrichProvenance(sessions, probe = detectProvenance) {
|
|
|
2428
1502
|
s.provenance.term = probed.term;
|
|
2429
1503
|
}, { concurrency: LSOF_CONCURRENCY });
|
|
2430
1504
|
}
|
|
2431
|
-
/**
|
|
2432
|
-
* Match an SSH client IP to a registered device (pure — testable with a plain
|
|
2433
|
-
* registry object). Returns the device name + ssh login user when the IP is a
|
|
2434
|
-
* known device address.
|
|
2435
|
-
*/
|
|
2436
1505
|
export function matchOriginDevice(clientIp, reg) {
|
|
2437
1506
|
for (const d of Object.values(reg)) {
|
|
2438
1507
|
if (d.address?.ip && d.address.ip === clientIp) {
|
|
@@ -2441,12 +1510,6 @@ export function matchOriginDevice(clientIp, reg) {
|
|
|
2441
1510
|
}
|
|
2442
1511
|
return undefined;
|
|
2443
1512
|
}
|
|
2444
|
-
/**
|
|
2445
|
-
* Resolve the initiating device for every ssh-transport session by matching its
|
|
2446
|
-
* `ssh.clientIp` against the device registry. Read-only and best-effort: a
|
|
2447
|
-
* registry that can't be loaded, or an IP that matches no device, leaves
|
|
2448
|
-
* `origin` undefined (the raw client IP is still on `ssh`). Mutates in place.
|
|
2449
|
-
*/
|
|
2450
1513
|
async function resolveOrigins(sessions) {
|
|
2451
1514
|
const needing = sessions.filter((s) => s.provenance?.ssh && !s.provenance.origin);
|
|
2452
1515
|
if (needing.length === 0)
|
|
@@ -2464,29 +1527,11 @@ async function resolveOrigins(sessions) {
|
|
|
2464
1527
|
s.provenance.origin = match;
|
|
2465
1528
|
}
|
|
2466
1529
|
}
|
|
2467
|
-
/**
|
|
2468
|
-
* Identity for a row the scan could not tie to a session: a daemon's worker
|
|
2469
|
-
* processes (an OpenClaw gateway spawning `codex`, a supervisor pool) have no
|
|
2470
|
-
* session id, no transcript file, and no cloud/run handle — nothing tells two of
|
|
2471
|
-
* them apart, because nothing distinguishes them. Same binary + same working
|
|
2472
|
-
* directory + same context IS the identity, so N indistinguishable workers
|
|
2473
|
-
* collapse to one row carrying `pidCount: N`.
|
|
2474
|
-
*
|
|
2475
|
-
* Returns undefined with no cwd: without it there is no stable identity, and
|
|
2476
|
-
* keying on kind alone would fold unrelated agents onto one row.
|
|
2477
|
-
*/
|
|
2478
1530
|
function anonymousWorkerKey(s) {
|
|
2479
1531
|
if (!s.cwd)
|
|
2480
1532
|
return undefined;
|
|
2481
1533
|
return `anon\0${s.kind}\0${s.context}\0${s.cwd}`;
|
|
2482
1534
|
}
|
|
2483
|
-
/**
|
|
2484
|
-
* Collapse rows that resolve to the *same* session — a session with many
|
|
2485
|
-
* subagent/fork PIDs (all matched to one transcript file) would otherwise print
|
|
2486
|
-
* dozens of identical rows. Keyed by session id, falling back to the transcript
|
|
2487
|
-
* file, then the cloud/run handle, then {@link anonymousWorkerKey}. The first row
|
|
2488
|
-
* wins and carries a `pidCount`.
|
|
2489
|
-
*/
|
|
2490
1535
|
export function dedupeBySession(sessions) {
|
|
2491
1536
|
const out = [];
|
|
2492
1537
|
const byKey = new Map();
|
|
@@ -2498,7 +1543,6 @@ export function dedupeBySession(sessions) {
|
|
|
2498
1543
|
}
|
|
2499
1544
|
const existing = byKey.get(key);
|
|
2500
1545
|
if (existing) {
|
|
2501
|
-
// Carry pre-folded fork counts (headless rows arrive with pidCount set).
|
|
2502
1546
|
existing.pidCount = (existing.pidCount ?? 1) + (s.pidCount ?? 1);
|
|
2503
1547
|
}
|
|
2504
1548
|
else {
|