@phnx-labs/agents-cli 1.22.46 → 1.22.49
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 +663 -0
- package/LICENSE +102 -182
- package/README.md +96 -49
- package/dist/bootstrap.js +4 -2
- package/dist/cli/command-registry.d.ts +2 -1
- package/dist/cli/command-registry.js +6 -2
- package/dist/commands/accounts.d.ts +58 -0
- package/dist/commands/accounts.js +243 -25
- package/dist/commands/artifacts.js +7 -6
- package/dist/commands/attach.js +1 -1
- package/dist/commands/auth.js +7 -5
- package/dist/commands/browser-sessions-picker.js +9 -7
- package/dist/commands/browser.js +711 -62
- package/dist/commands/config.d.ts +0 -1
- package/dist/commands/config.js +26 -24
- package/dist/commands/cost.d.ts +1 -1
- package/dist/commands/daemon.d.ts +21 -0
- package/dist/commands/daemon.js +147 -12
- package/dist/commands/detach.d.ts +13 -0
- package/dist/commands/detach.js +11 -8
- package/dist/commands/exec.js +107 -53
- package/dist/commands/feed-watch.d.ts +2 -0
- package/dist/commands/feed-watch.js +28 -0
- package/dist/commands/feed.js +27 -0
- package/dist/commands/feedback.js +3 -19
- package/dist/commands/focus.d.ts +14 -0
- package/dist/commands/focus.js +48 -5
- package/dist/commands/go.d.ts +3 -0
- package/dist/commands/go.js +16 -3
- package/dist/commands/insights.d.ts +1 -1
- package/dist/commands/insights.js +2 -2
- package/dist/commands/inspect.js +2 -2
- package/dist/commands/lease.d.ts +1 -1
- package/dist/commands/lease.js +7 -6
- package/dist/commands/mailboxes.js +4 -3
- package/dist/commands/menubar.js +11 -4
- package/dist/commands/projects.d.ts +16 -1
- package/dist/commands/projects.js +58 -64
- package/dist/commands/resume.js +1 -1
- package/dist/commands/route.js +4 -4
- package/dist/commands/routines.js +4 -3
- package/dist/commands/run-broadcast.d.ts +1 -1
- package/dist/commands/run-broadcast.js +1 -1
- package/dist/commands/sessions-picker-factory.d.ts +2 -1
- package/dist/commands/sessions-picker-factory.js +1 -1
- package/dist/commands/sessions-picker.d.ts +22 -0
- package/dist/commands/sessions-picker.js +48 -9
- package/dist/commands/sessions-stop.d.ts +24 -0
- package/dist/commands/sessions-stop.js +66 -0
- package/dist/commands/sessions-trace.d.ts +82 -0
- package/dist/commands/sessions-trace.js +387 -0
- package/dist/commands/sessions.js +5 -0
- package/dist/commands/setup-secrets.js +23 -0
- package/dist/commands/share.d.ts +20 -5
- package/dist/commands/share.js +152 -52
- package/dist/commands/ssh.d.ts +15 -0
- package/dist/commands/ssh.js +243 -18
- package/dist/commands/teams.js +34 -2
- package/dist/commands/traces.d.ts +2 -0
- package/dist/commands/traces.js +184 -0
- package/dist/commands/view.d.ts +2 -0
- package/dist/commands/view.js +32 -62
- package/dist/commands/watchdog.js +9 -14
- package/dist/index.d.ts +1 -0
- package/dist/index.js +5 -0
- package/dist/lib/account-capabilities.d.ts +4 -0
- package/dist/lib/account-capabilities.js +13 -9
- package/dist/lib/account-registry.d.ts +2 -0
- package/dist/lib/account-registry.js +30 -2
- package/dist/lib/accounting/rotate.d.ts +9 -0
- package/dist/lib/accounting/rotate.js +12 -2
- package/dist/lib/accounting/usage.d.ts +84 -6
- package/dist/lib/accounting/usage.js +293 -75
- package/dist/lib/agent-modes.js +29 -1
- package/dist/lib/agent-spec/agents.d.ts +4 -3
- package/dist/lib/agent-spec/agents.js +25 -15
- package/dist/lib/analytics/mix-commands.d.ts +1 -1
- package/dist/lib/analytics/mix-commands.js +2 -2
- package/dist/lib/auth-health.d.ts +13 -3
- package/dist/lib/auth-health.js +50 -8
- package/dist/lib/browser/devices.d.ts +19 -0
- package/dist/lib/browser/devices.js +23 -0
- package/dist/lib/browser/drivers/ssh.d.ts +16 -2
- package/dist/lib/browser/drivers/ssh.js +36 -9
- package/dist/lib/browser/ipc.d.ts +1 -1
- package/dist/lib/browser/ipc.js +40 -9
- package/dist/lib/browser/profiles.d.ts +104 -65
- package/dist/lib/browser/profiles.js +277 -165
- package/dist/lib/browser/registry.d.ts +39 -0
- package/dist/lib/browser/registry.js +149 -0
- package/dist/lib/browser/remote-control.d.ts +28 -2
- package/dist/lib/browser/remote-control.js +35 -3
- package/dist/lib/browser/resolve-target.d.ts +68 -0
- package/dist/lib/browser/resolve-target.js +250 -0
- package/dist/lib/browser/runtime-state.d.ts +44 -11
- package/dist/lib/browser/runtime-state.js +98 -15
- package/dist/lib/browser/service.d.ts +65 -9
- package/dist/lib/browser/service.js +196 -61
- package/dist/lib/browser/sessions-list.d.ts +1 -3
- package/dist/lib/browser/sessions-list.js +3 -16
- package/dist/lib/browser/task-index.d.ts +70 -0
- package/dist/lib/browser/task-index.js +180 -0
- package/dist/lib/browser/types.d.ts +12 -1
- package/dist/lib/byok-usage.js +2 -2
- package/dist/lib/claude-statusline.d.ts +29 -0
- package/dist/lib/claude-statusline.js +139 -0
- package/dist/lib/cloud/host.d.ts +1 -1
- package/dist/lib/cloud/host.js +1 -1
- package/dist/lib/cloud/rush.js +1 -1
- package/dist/lib/codex-policy.d.ts +11 -1
- package/dist/lib/codex-policy.js +20 -5
- package/dist/lib/computer/computer-rpc.js +2 -2
- package/dist/lib/computer/download.d.ts +15 -15
- package/dist/lib/computer/download.js +35 -98
- package/dist/lib/computer/ssh-tunnel.d.ts +0 -3
- package/dist/lib/computer/ssh-tunnel.js +12 -22
- package/dist/lib/config-keys.d.ts +6 -11
- package/dist/lib/config-keys.js +18 -19
- package/dist/lib/crabbox/cli.d.ts +1 -1
- package/dist/lib/crabbox/cli.js +2 -2
- package/dist/lib/crabbox/lease.d.ts +9 -0
- package/dist/lib/crabbox/lease.js +70 -15
- package/dist/lib/crabbox/runtimes.js +1 -1
- package/dist/lib/daemon/account-state-daemon-service.d.ts +16 -0
- package/dist/lib/daemon/account-state-daemon-service.js +26 -0
- package/dist/lib/daemon/browser-ipc-service.d.ts +20 -0
- package/dist/lib/daemon/browser-ipc-service.js +46 -0
- package/dist/lib/daemon/daemon.d.ts +3 -0
- package/dist/lib/daemon/daemon.js +212 -444
- package/dist/lib/daemon/device-probe-service.d.ts +26 -0
- package/dist/lib/daemon/device-probe-service.js +47 -0
- package/dist/lib/daemon/keychain-reap-service.d.ts +17 -0
- package/dist/lib/daemon/keychain-reap-service.js +32 -0
- package/dist/lib/daemon/monitor-engine-service.d.ts +22 -0
- package/dist/lib/daemon/monitor-engine-service.js +29 -0
- package/dist/lib/daemon/runner.js +15 -2
- package/dist/lib/daemon/secrets-broker-service.d.ts +20 -0
- package/dist/lib/daemon/secrets-broker-service.js +66 -0
- package/dist/lib/daemon/self-heal-service.d.ts +27 -0
- package/dist/lib/daemon/self-heal-service.js +48 -0
- package/dist/lib/daemon/service.d.ts +102 -0
- package/dist/lib/daemon/service.js +98 -0
- package/dist/lib/daemon/session-index-service.d.ts +22 -0
- package/dist/lib/daemon/session-index-service.js +40 -0
- package/dist/lib/daemon/state-dir-check-service.d.ts +43 -0
- package/dist/lib/daemon/state-dir-check-service.js +63 -0
- package/dist/lib/daemon/supervisor.d.ts +87 -0
- package/dist/lib/daemon/supervisor.js +298 -0
- package/dist/lib/daemon/watchdog-service.d.ts +19 -0
- package/dist/lib/daemon/watchdog-service.js +40 -0
- package/dist/lib/daemon-health.d.ts +15 -0
- package/dist/lib/daemon-health.js +29 -5
- package/dist/lib/daemon-services.d.ts +8 -1
- package/dist/lib/daemon-services.js +55 -0
- package/dist/lib/daemon-ticks.d.ts +13 -2
- package/dist/lib/daemon-ticks.js +22 -20
- package/dist/lib/device-config.d.ts +25 -10
- package/dist/lib/device-config.js +90 -39
- package/dist/lib/devices/config-migration.d.ts +7 -0
- package/dist/lib/devices/config-migration.js +67 -3
- package/dist/lib/devices/health.d.ts +27 -2
- package/dist/lib/devices/health.js +39 -9
- package/dist/lib/devices/interactive-host.d.ts +15 -0
- package/dist/lib/devices/interactive-host.js +53 -0
- package/dist/lib/devices/registry.d.ts +61 -4
- package/dist/lib/devices/registry.js +94 -38
- package/dist/lib/devices/stats-cache.d.ts +26 -0
- package/dist/lib/devices/stats-cache.js +59 -7
- package/dist/lib/devices/worker-pick.d.ts +48 -0
- package/dist/lib/devices/worker-pick.js +113 -0
- package/dist/lib/exec.d.ts +70 -14
- package/dist/lib/exec.js +110 -38
- package/dist/lib/feed/answer.d.ts +29 -0
- package/dist/lib/feed/answer.js +145 -0
- package/dist/lib/feed/attention.d.ts +83 -0
- package/dist/lib/feed/attention.js +0 -0
- package/dist/lib/feed/feed.d.ts +109 -0
- package/dist/lib/feed/feed.js +154 -3
- package/dist/lib/feed/pr-status.d.ts +14 -0
- package/dist/lib/feed/pr-status.js +47 -0
- package/dist/lib/feed/watch.d.ts +59 -0
- package/dist/lib/feed/watch.js +180 -0
- package/dist/lib/fleet/capture.js +6 -0
- package/dist/lib/fleet/remote-login.js +8 -3
- package/dist/lib/fleet/types.d.ts +17 -0
- package/dist/lib/harness/adapter.d.ts +11 -0
- package/dist/lib/harness/adapters/claude.js +38 -24
- package/dist/lib/harness/adapters/codex.js +2 -4
- package/dist/lib/harness/adapters/cursor.js +8 -11
- package/dist/lib/helper-download.d.ts +108 -0
- package/dist/lib/helper-download.js +194 -0
- package/dist/lib/helper-versions.d.ts +52 -0
- package/dist/lib/helper-versions.js +58 -0
- package/dist/lib/hooks/cache.js +19 -0
- package/dist/lib/hooks/install.js +2 -2
- package/dist/lib/hooks/match.d.ts +3 -0
- package/dist/lib/hooks/match.js +25 -0
- package/dist/lib/hosts/credentials.d.ts +1 -1
- package/dist/lib/hosts/credentials.js +1 -1
- package/dist/lib/hosts/dispatch.d.ts +7 -1
- package/dist/lib/hosts/dispatch.js +24 -5
- package/dist/lib/hosts/passthrough.d.ts +8 -15
- package/dist/lib/hosts/passthrough.js +35 -16
- package/dist/lib/hosts/providers/devices.d.ts +1 -1
- package/dist/lib/hosts/providers/devices.js +1 -1
- package/dist/lib/hosts/reconnect.d.ts +179 -21
- package/dist/lib/hosts/reconnect.js +266 -45
- package/dist/lib/hosts/registry.js +7 -0
- package/dist/lib/installations/migrate.d.ts +4 -5
- package/dist/lib/installations/migrate.js +6 -7
- package/dist/lib/installations/shims.d.ts +1 -1
- package/dist/lib/installations/shims.js +23 -13
- package/dist/lib/installations/versions.js +25 -2
- package/dist/lib/linear-autoclose.d.ts +1 -1
- package/dist/lib/linear-autoclose.js +1 -1
- package/dist/lib/loop.d.ts +2 -2
- package/dist/lib/loop.js +1 -1
- package/dist/lib/menubar/download-menubar.d.ts +47 -0
- package/dist/lib/menubar/download-menubar.js +60 -0
- package/dist/lib/menubar/install-menubar.d.ts +44 -7
- package/dist/lib/menubar/install-menubar.js +105 -23
- package/dist/lib/menubar/notify-desktop.d.ts +3 -3
- package/dist/lib/menubar/notify-desktop.js +6 -6
- package/dist/lib/model-tiers.d.ts +1 -1
- package/dist/lib/open-url.d.ts +60 -2
- package/dist/lib/open-url.js +167 -12
- package/dist/lib/project-probe.d.ts +1 -1
- package/dist/lib/project-probe.js +1 -1
- package/dist/lib/project-pull.d.ts +1 -1
- package/dist/lib/project-pull.js +2 -2
- package/dist/lib/projects.d.ts +1 -1
- package/dist/lib/projects.js +1 -1
- package/dist/lib/refresh-coordinator.d.ts +1 -1
- package/dist/lib/refresh-coordinator.js +1 -1
- package/dist/lib/sandbox.js +6 -7
- package/dist/lib/secrets/download-keychain.d.ts +47 -0
- package/dist/lib/secrets/download-keychain.js +70 -0
- package/dist/lib/secrets/install-helper.d.ts +20 -1
- package/dist/lib/secrets/install-helper.js +44 -3
- package/dist/lib/session/active.d.ts +67 -0
- package/dist/lib/session/active.js +86 -2
- package/dist/lib/session/db.d.ts +11 -1
- package/dist/lib/session/db.js +142 -8
- package/dist/lib/session/discover.d.ts +23 -1
- package/dist/lib/session/discover.js +44 -10
- package/dist/lib/session/highlights.d.ts +49 -1
- package/dist/lib/session/highlights.js +95 -0
- package/dist/lib/session/host-link.d.ts +16 -2
- package/dist/lib/session/host-link.js +7 -1
- package/dist/lib/session/parse.js +33 -1
- package/dist/lib/session/prompt.d.ts +35 -0
- package/dist/lib/session/prompt.js +56 -0
- package/dist/lib/session/remote/remote-list.d.ts +9 -0
- package/dist/lib/session/remote/remote-list.js +19 -2
- package/dist/lib/session/remote/watch.d.ts +1 -1
- package/dist/lib/session/remote/watch.js +17 -3
- package/dist/lib/session/render.js +7 -3
- package/dist/lib/session/session-cache.d.ts +53 -0
- package/dist/lib/session/session-cache.js +93 -1
- package/dist/lib/session/state.js +1 -1
- package/dist/lib/session/tool-calls.d.ts +2 -0
- package/dist/lib/session/tool-calls.js +1 -1
- package/dist/lib/session/trajectory-compare.d.ts +47 -0
- package/dist/lib/session/trajectory-compare.js +116 -0
- package/dist/lib/session/trajectory-html.d.ts +19 -0
- package/dist/lib/session/trajectory-html.js +770 -0
- package/dist/lib/session/trajectory-lineage.d.ts +92 -0
- package/dist/lib/session/trajectory-lineage.js +195 -0
- package/dist/lib/session/trajectory-text.d.ts +37 -0
- package/dist/lib/session/trajectory-text.js +284 -0
- package/dist/lib/session/trajectory.d.ts +102 -0
- package/dist/lib/session/trajectory.js +384 -0
- package/dist/lib/session/types.d.ts +15 -0
- package/dist/lib/sha256-asset.d.ts +26 -0
- package/dist/lib/sha256-asset.js +41 -0
- package/dist/lib/share/backend.d.ts +95 -0
- package/dist/lib/share/backend.js +139 -0
- package/dist/lib/share/delete.d.ts +6 -2
- package/dist/lib/share/delete.js +8 -9
- package/dist/lib/share/html.d.ts +22 -0
- package/dist/lib/share/html.js +88 -0
- package/dist/lib/share/provision.d.ts +18 -4
- package/dist/lib/share/provision.js +38 -4
- package/dist/lib/share/publish.d.ts +39 -15
- package/dist/lib/share/publish.js +55 -26
- package/dist/lib/share/worker-template.js +171 -11
- package/dist/lib/sqlite.js +16 -3
- package/dist/lib/ssh-exec.d.ts +65 -0
- package/dist/lib/ssh-exec.js +120 -4
- package/dist/lib/startup/command-registry.d.ts +3 -1
- package/dist/lib/startup/command-registry.js +6 -3
- package/dist/lib/startup/dev-build.js +3 -3
- package/dist/lib/state.d.ts +2 -2
- package/dist/lib/state.js +11 -5
- package/dist/lib/subagents-registry.js +1 -1
- package/dist/lib/teams/agents.js +9 -0
- package/dist/lib/terminal/index.d.ts +1 -1
- package/dist/lib/terminal/inject.d.ts +19 -0
- package/dist/lib/terminal/inject.js +19 -9
- package/dist/lib/terminal/types.d.ts +1 -1
- package/dist/lib/terminal/types.js +1 -1
- package/dist/lib/tmux/orphan-reap.js +1 -1
- package/dist/lib/tmux/session.d.ts +46 -13
- package/dist/lib/tmux/session.js +204 -27
- package/dist/lib/traces/backend.d.ts +29 -0
- package/dist/lib/traces/backend.js +46 -0
- package/dist/lib/traces/classify.d.ts +52 -0
- package/dist/lib/traces/classify.js +72 -0
- package/dist/lib/traces/config.d.ts +10 -0
- package/dist/lib/traces/config.js +2 -0
- package/dist/lib/traces/provision.d.ts +19 -0
- package/dist/lib/traces/provision.js +30 -0
- package/dist/lib/traces/sync.d.ts +150 -0
- package/dist/lib/traces/sync.js +447 -0
- package/dist/lib/traces/worker-template.d.ts +4 -0
- package/dist/lib/traces/worker-template.js +159 -0
- package/dist/lib/types.d.ts +34 -19
- package/dist/lib/types.js +14 -0
- package/dist/lib/usage-backoff.d.ts +2 -1
- package/dist/lib/usage-backoff.js +60 -19
- package/dist/lib/usage-refresh.d.ts +18 -4
- package/dist/lib/usage-refresh.js +68 -19
- package/dist/lib/watchdog/log.d.ts +1 -1
- package/dist/lib/watchdog/log.js +1 -1
- package/dist/lib/watchdog/runner.d.ts +36 -45
- package/dist/lib/watchdog/runner.js +150 -119
- package/dist/lib/watchdog/service.d.ts +0 -1
- package/dist/lib/watchdog/service.js +0 -1
- package/dist/lib/watchdog/watchdog-agent.d.ts +38 -0
- package/dist/lib/watchdog/watchdog-agent.js +59 -0
- package/dist/lib/watchdog/watchdog.d.ts +11 -2
- package/dist/lib/watchdog/watchdog.js +37 -104
- package/dist/lib/workflows.d.ts +1 -1
- package/package.json +5 -7
- package/dist/commands/usage.d.ts +0 -27
- package/dist/commands/usage.js +0 -96
- package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/Info.plist +0 -24
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/Resources/AppIcon.icns +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/_CodeSignature/CodeResources +0 -128
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/Info.plist +0 -24
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +0 -123
- package/dist/lib/secrets/Agents CLI.app/Contents/embedded.provisionprofile +0 -0
- package/dist/lib/usage-fleet.d.ts +0 -32
- package/dist/lib/usage-fleet.js +0 -125
package/dist/lib/ssh-exec.js
CHANGED
|
@@ -18,6 +18,14 @@ import { getCacheDir } from './state.js';
|
|
|
18
18
|
* smuggled in as part of a remote command, and `sshExec` additionally rejects a
|
|
19
19
|
* leading `-` so it can never be parsed as an ssh argv flag.
|
|
20
20
|
*/
|
|
21
|
+
/**
|
|
22
|
+
* ssh's own connection-layer failure code — the transport dropped or never came
|
|
23
|
+
* up, as opposed to the remote command choosing an exit status. Defined here,
|
|
24
|
+
* in the layer that owns the ssh invocation, and re-exported by
|
|
25
|
+
* `lib/hosts/reconnect.ts` as `SSH_CONN_FAILURE` so there is one constant for
|
|
26
|
+
* the concept rather than two that can drift apart.
|
|
27
|
+
*/
|
|
28
|
+
export const SSH_CONN_FAILURE_CODE = 255;
|
|
21
29
|
export const SSH_TARGET_RE = /^[a-zA-Z0-9._-]+(@[a-zA-Z0-9._-]+)?$/;
|
|
22
30
|
export function assertValidSshTarget(host) {
|
|
23
31
|
if (host.startsWith('-') || !SSH_TARGET_RE.test(host)) {
|
|
@@ -314,20 +322,128 @@ export function sshExecRawStream(target, remoteCmd, opts) {
|
|
|
314
322
|
export function sshReachable(target, timeoutMs = 10000) {
|
|
315
323
|
return sshExec(target, 'true', { timeoutMs, multiplex: true }).code === 0;
|
|
316
324
|
}
|
|
325
|
+
/**
|
|
326
|
+
* The DEC private modes a full-screen remote app turns ON and, when it exits
|
|
327
|
+
* cleanly, turns back off itself. A remote agent TUI killed by a dropped link
|
|
328
|
+
* never gets to, so they stay enabled on the LOCAL terminal after ssh is gone:
|
|
329
|
+
* focus reporting (1004) and the mode/colour-scheme reports (996/997) then make
|
|
330
|
+
* the terminal emit answerback sequences at a shell that is not expecting them,
|
|
331
|
+
* which is the `^[[?997;1n ^[[I ^[[O` litter seen after a drop (RUSH-3125). Also
|
|
332
|
+
* resets bracketed paste (2004), the alternate screen (1049), mouse tracking
|
|
333
|
+
* (1000/1002/1003/1006), and re-shows the cursor (25) — every one of which a TUI
|
|
334
|
+
* leaves armed the same way.
|
|
335
|
+
*/
|
|
336
|
+
export const TERMINAL_MODE_RESET = '\x1b[?1004l\x1b[?996l\x1b[?997l\x1b[?2004l\x1b[?1049l'
|
|
337
|
+
+ '\x1b[?1000l\x1b[?1002l\x1b[?1003l\x1b[?1006l\x1b[?25h';
|
|
338
|
+
/**
|
|
339
|
+
* Put the local terminal back the way ssh found it.
|
|
340
|
+
*
|
|
341
|
+
* Two independent kinds of state survive a hard ssh death, and BOTH have to be
|
|
342
|
+
* undone or the next thing the user sees is corrupted:
|
|
343
|
+
*
|
|
344
|
+
* - **termios** (kernel side): `-tt` puts the tty in raw mode, so a killed ssh
|
|
345
|
+
* leaves it with echo and line-editing off. `saved` is the `stty -g` snapshot
|
|
346
|
+
* taken before the spawn; restoring it is exact, unlike guessing at `sane`.
|
|
347
|
+
* - **DEC private modes** (terminal side): see {@link TERMINAL_MODE_RESET}.
|
|
348
|
+
*
|
|
349
|
+
* Then drain whatever the terminal already answered back into the input buffer.
|
|
350
|
+
* Without the drain those bytes are not merely cosmetic — they are still queued
|
|
351
|
+
* on the tty, so the NEXT attach hands them straight to the agent as if the user
|
|
352
|
+
* had typed them.
|
|
353
|
+
*
|
|
354
|
+
* Best-effort by construction: this runs while recovering from a failure, so a
|
|
355
|
+
* missing `stty`, a non-TTY stdin, or a closed stream must never turn a
|
|
356
|
+
* recoverable drop into a thrown error.
|
|
357
|
+
*/
|
|
358
|
+
export function restoreLocalTerminal(saved, opts) {
|
|
359
|
+
try {
|
|
360
|
+
if (saved)
|
|
361
|
+
spawnSync('stty', [saved], { stdio: ['inherit', 'ignore', 'ignore'] });
|
|
362
|
+
}
|
|
363
|
+
catch { /* no stty, or stdin is not a tty — nothing to restore */ }
|
|
364
|
+
try {
|
|
365
|
+
if (process.stdout.isTTY)
|
|
366
|
+
process.stdout.write(TERMINAL_MODE_RESET);
|
|
367
|
+
}
|
|
368
|
+
catch { /* stream closed */ }
|
|
369
|
+
try {
|
|
370
|
+
if (process.stdin.isTTY)
|
|
371
|
+
process.stdin.setRawMode?.(false);
|
|
372
|
+
}
|
|
373
|
+
catch { /* stdin not controllable in this context */ }
|
|
374
|
+
// The DRAIN is the one destructive step here, so it is opt-in per exit. See
|
|
375
|
+
// {@link sshStream} for why it must not run on a clean exit.
|
|
376
|
+
if (!opts.drainStdin)
|
|
377
|
+
return;
|
|
378
|
+
try {
|
|
379
|
+
// read() on a paused non-flowing stdin returns the buffered bytes and
|
|
380
|
+
// discards them; the loop clears a burst rather than one chunk.
|
|
381
|
+
if (process.stdin.isTTY) {
|
|
382
|
+
while (process.stdin.read() !== null) { /* discard */ }
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
catch { /* stdin not readable in this context */ }
|
|
386
|
+
}
|
|
387
|
+
/** `stty -g` snapshot of the local tty, or undefined when there is nothing to snapshot. */
|
|
388
|
+
export function saveLocalTerminal() {
|
|
389
|
+
if (!process.stdin.isTTY)
|
|
390
|
+
return undefined;
|
|
391
|
+
try {
|
|
392
|
+
const r = spawnSync('stty', ['-g'], { stdio: ['inherit', 'pipe', 'ignore'], encoding: 'utf-8' });
|
|
393
|
+
const out = r.status === 0 ? r.stdout?.trim() : '';
|
|
394
|
+
return out || undefined;
|
|
395
|
+
}
|
|
396
|
+
catch {
|
|
397
|
+
return undefined;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
317
400
|
/**
|
|
318
401
|
* Foreground counterpart to `sshExec`: run `remoteCmd` on `target` with the
|
|
319
402
|
* local stdio wired straight through (`stdio: 'inherit'`), so output streams as
|
|
320
403
|
* it is produced and — with `tty` — keystrokes reach a remote picker. Blocks
|
|
321
404
|
* until the remote command exits and returns its exit code (255 is ssh's own
|
|
322
405
|
* connection-layer failure; any other non-zero is the remote command's code).
|
|
406
|
+
*
|
|
407
|
+
* On a `tty` stream the local terminal state is snapshotted before the spawn and
|
|
408
|
+
* restored after it. ssh restores termios itself when it exits cleanly, so this
|
|
409
|
+
* is for the case that matters here: the link dying under a full-screen agent
|
|
410
|
+
* TUI, which leaves the tty raw and the TUI's DEC modes armed. Doing it HERE
|
|
411
|
+
* rather than in the reconnect loop means every caller that opens an interactive
|
|
412
|
+
* remote stream is covered, not just the one that noticed (RUSH-3125).
|
|
413
|
+
*
|
|
414
|
+
* **The stdin drain is gated on an ABNORMAL exit, and the distinction is not
|
|
415
|
+
* cosmetic.** Resetting termios and the DEC modes is idempotent — on a clean
|
|
416
|
+
* exit ssh and the remote TUI have already done it, so re-doing it changes
|
|
417
|
+
* nothing. Draining stdin is destructive: it discards whatever the user has
|
|
418
|
+
* typed ahead. On a clean exit those bytes are legitimate type-ahead the
|
|
419
|
+
* resuming shell should receive, and eating them would be a new bug in every
|
|
420
|
+
* caller of this function (`runInteractiveOnHost`, the remote-tmux attach in
|
|
421
|
+
* commands/go.ts, the remote secrets browse) rather than a fix. Only an
|
|
422
|
+
* abnormal exit — ssh killed by a signal (`status === null`) or its own
|
|
423
|
+
* connection-layer failure (255) — produces the answerback storm the drain
|
|
424
|
+
* exists to clear, because only then did the TUI die without sending its own
|
|
425
|
+
* mode resets.
|
|
323
426
|
*/
|
|
324
427
|
export function sshStream(target, remoteCmd, opts = {}) {
|
|
325
428
|
assertValidSshTarget(target);
|
|
326
429
|
const mux = opts.multiplex === false ? [] : controlOpts();
|
|
327
430
|
const tty = opts.tty ? ['-tt'] : [];
|
|
328
431
|
const args = [...sshConnectOpts(mux, opts.hostKeyOpts), ...(opts.extraSshArgs ?? []), ...tty, target, remoteCmd];
|
|
329
|
-
const
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
432
|
+
const saved = opts.tty ? saveLocalTerminal() : undefined;
|
|
433
|
+
let abnormal = true; // a throw before/inside the spawn is abnormal by definition
|
|
434
|
+
try {
|
|
435
|
+
const res = spawnSync('ssh', args, { stdio: 'inherit' });
|
|
436
|
+
const code = typeof res.status === 'number' ? res.status : 255;
|
|
437
|
+
// Killed by a signal (no numeric status) or ssh's own connection-layer
|
|
438
|
+
// failure — the two shapes in which the remote TUI never got to reset the
|
|
439
|
+
// terminal. Any other code is ssh exiting normally with the remote's status.
|
|
440
|
+
abnormal = res.status === null || code === SSH_CONN_FAILURE_CODE;
|
|
441
|
+
return code;
|
|
442
|
+
}
|
|
443
|
+
finally {
|
|
444
|
+
// `finally`, because the abnormal exits are precisely the ones that leave
|
|
445
|
+
// the terminal wrecked — but the drain within it is gated (see the doc).
|
|
446
|
+
if (opts.tty)
|
|
447
|
+
restoreLocalTerminal(saved, { drainStdin: abnormal });
|
|
448
|
+
}
|
|
333
449
|
}
|
|
@@ -32,7 +32,9 @@ export declare const KNOWN_TOP_LEVEL_COMMANDS: ReadonlySet<string>;
|
|
|
32
32
|
* `agents fleet apply` / `agents devices apply`. `beta` nested under
|
|
33
33
|
* `agents setup beta` (RUSH-2981). `org` (the Prix-coupled account layer) was
|
|
34
34
|
* removed; `agents auth` returned against Phoenix ID with `auth space` as the
|
|
35
|
-
* team surface (RUSH-2581).
|
|
35
|
+
* team surface (RUSH-2581). `usage` was removed as a duplicate surface of
|
|
36
|
+
* `agents view`, which renders per-account usage with account, version, and
|
|
37
|
+
* auth state beside it (RUSH-3079).
|
|
36
38
|
*/
|
|
37
39
|
export declare const RETIRED_TOP_LEVEL_COMMANDS: ReadonlySet<string>;
|
|
38
40
|
export declare function isKnownTopLevelCommand(name: string): boolean;
|
|
@@ -5,12 +5,12 @@ const LOADED_COMMAND_NAMES = [
|
|
|
5
5
|
'routines', 'monitors', 'projects', 'run', 'open', 'reconnect', 'fork', 'config',
|
|
6
6
|
'models', 'modes', 'trash', 'restore', 'doctor',
|
|
7
7
|
'route', 'harness', 'harnesses', 'secrets', 'menubar', 'sync',
|
|
8
|
-
'refresh-rules', 'factory', '
|
|
8
|
+
'refresh-rules', 'factory', 'insights', 'trace', 'perf',
|
|
9
9
|
'pty', 'tmux', 'watchdog', 'browser', 'computer', 'logs', 'events',
|
|
10
10
|
'ssh', 'devices', 'fleet', 'repos', 'repo', 'setup', 'uninstall', 'upgrade', 'sessions',
|
|
11
11
|
'teams', 'cloud', 'message', 'send', 'notify', 'feed',
|
|
12
12
|
'mailboxes', 'mailbox', 'artifacts', 'webhooks',
|
|
13
|
-
'humans', 'daemon',
|
|
13
|
+
'humans', 'daemon', 'traces',
|
|
14
14
|
];
|
|
15
15
|
const INLINE_COMMAND_NAMES = [
|
|
16
16
|
'perms', 'exec', 'jobs', 'cron', 'check', 'resources', 'hq', '_internal',
|
|
@@ -52,12 +52,15 @@ export const KNOWN_TOP_LEVEL_COMMANDS = new Set([
|
|
|
52
52
|
* `agents fleet apply` / `agents devices apply`. `beta` nested under
|
|
53
53
|
* `agents setup beta` (RUSH-2981). `org` (the Prix-coupled account layer) was
|
|
54
54
|
* removed; `agents auth` returned against Phoenix ID with `auth space` as the
|
|
55
|
-
* team surface (RUSH-2581).
|
|
55
|
+
* team surface (RUSH-2581). `usage` was removed as a duplicate surface of
|
|
56
|
+
* `agents view`, which renders per-account usage with account, version, and
|
|
57
|
+
* auth state beside it (RUSH-3079).
|
|
56
58
|
*/
|
|
57
59
|
export const RETIRED_TOP_LEVEL_COMMANDS = new Set([
|
|
58
60
|
'webhook',
|
|
59
61
|
'org',
|
|
60
62
|
'serve',
|
|
63
|
+
'usage',
|
|
61
64
|
'login',
|
|
62
65
|
'logout',
|
|
63
66
|
'budget',
|
|
@@ -22,12 +22,12 @@ import * as path from 'path';
|
|
|
22
22
|
* looking for `.git`.
|
|
23
23
|
*
|
|
24
24
|
* That package root does not always hold `.git` itself, though — this repo is a
|
|
25
|
-
* monorepo where the package lives at `
|
|
25
|
+
* monorepo where the package lives at `cli` and `.git` sits one level
|
|
26
26
|
* higher, at the true repo root. `.git` is therefore searched for in a SMALL,
|
|
27
27
|
* bounded set of ancestors above the package root (see
|
|
28
28
|
* `DEV_BUILD_GIT_ANCESTOR_DEPTH`), not just the package root itself — a source
|
|
29
|
-
* checkout run via tsx/ts-node from `
|
|
30
|
-
* `
|
|
29
|
+
* checkout run via tsx/ts-node from `cli/src/` or built to
|
|
30
|
+
* `cli/dist/` must still be recognized. The bound keeps this well short of
|
|
31
31
|
* an unrelated ancestor repo: a real Homebrew npm-global install's package root
|
|
32
32
|
* sits 3 directories below `/opt/homebrew` (`lib/node_modules/@phnx-labs/…`),
|
|
33
33
|
* so a depth of 2 finds this repo's `.git` while staying short of Homebrew's.
|
package/dist/lib/state.d.ts
CHANGED
|
@@ -31,7 +31,7 @@ export declare function getHumansFilePath(): string;
|
|
|
31
31
|
* file against `schema/agents-yaml.schema.json`. Exported so
|
|
32
32
|
* `lib/devices/config-migration.ts` rewrites a device doc with the same header.
|
|
33
33
|
*/
|
|
34
|
-
export declare const META_HEADER = "# agents-cli metadata\n# Auto-generated - do not edit manually\n# https://github.com/phnx-labs/agents-cli\n# yaml-language-server: $schema=https://raw.githubusercontent.com/phnx-labs/agents-cli/main/
|
|
34
|
+
export declare const META_HEADER = "# agents-cli metadata\n# Auto-generated - do not edit manually\n# https://github.com/phnx-labs/agents-cli\n# yaml-language-server: $schema=https://raw.githubusercontent.com/phnx-labs/agents-cli/main/cli/schema/agents-yaml.schema.json\n\n";
|
|
35
35
|
/** Root of the system data directory (~/.agents/.system/). */
|
|
36
36
|
export declare function getAgentsDir(): string;
|
|
37
37
|
/** Root of the system data directory (~/.agents/.system/). */
|
|
@@ -258,7 +258,7 @@ export declare function getTeamsAgentsDir(): string;
|
|
|
258
258
|
export declare function getTeamsRegistryPath(): string;
|
|
259
259
|
/** Path to the device registry — SSH device profiles with platform/auth metadata. Durable runtime, per-machine (host list + addresses are NOT pulled by `agents repo push`). */
|
|
260
260
|
export declare function getDevicesRegistryPath(): string;
|
|
261
|
-
/** Path to the device ignore-list —
|
|
261
|
+
/** Path to the LEGACY per-machine device ignore-list — superseded by the tracked `fleet.ignored` list in central agents.yaml (RUSH-3062); only lib/devices/config-migration.ts still reads it (to fold + remove it). */
|
|
262
262
|
export declare function getDevicesIgnoredPath(): string;
|
|
263
263
|
/** Path to the LEGACY device auto-launch preference file — which registered devices are eligible/preferred for the ext's auto-host selection. Superseded by the per-device doc `config:` block; only lib/devices/config-migration.ts still reads it (to fold + remove it). */
|
|
264
264
|
export declare function getDevicesAutoLaunchPath(): string;
|
package/dist/lib/state.js
CHANGED
|
@@ -168,7 +168,7 @@ const USER_PROMPTCUTS_FILE = path.join(USER_AGENTS_DIR, 'hooks', 'promptcuts.yam
|
|
|
168
168
|
export const META_HEADER = `# agents-cli metadata
|
|
169
169
|
# Auto-generated - do not edit manually
|
|
170
170
|
# https://github.com/phnx-labs/agents-cli
|
|
171
|
-
# yaml-language-server: $schema=https://raw.githubusercontent.com/phnx-labs/agents-cli/main/
|
|
171
|
+
# yaml-language-server: $schema=https://raw.githubusercontent.com/phnx-labs/agents-cli/main/cli/schema/agents-yaml.schema.json
|
|
172
172
|
|
|
173
173
|
`;
|
|
174
174
|
// ─── Root getters ─────────────────────────────────────────────────────────────
|
|
@@ -563,7 +563,9 @@ export function getTeamsAgentsDir() { return TEAMS_AGENTS_DIR; }
|
|
|
563
563
|
/** Path to the team registry — list of named teams with timestamps. Durable runtime, per-machine. */
|
|
564
564
|
export function getTeamsRegistryPath() { return path.join(HISTORY_DIR, 'teams', 'registry.json'); }
|
|
565
565
|
/**
|
|
566
|
-
* The devices dir (registry
|
|
566
|
+
* The devices dir (the registry lives here; the ignore-list moved to the
|
|
567
|
+
* tracked central agents.yaml as `fleet.ignored` — RUSH-3062). Read at CALL
|
|
568
|
+
* time so a
|
|
567
569
|
* test can redirect it to a temp dir via AGENTS_DEVICES_DIR without racing the
|
|
568
570
|
* module-load capture of HISTORY_DIR — mirrors the AGENTS_EVENTS_PATH /
|
|
569
571
|
* AGENTS_SECRETS_AGENT_DIR test-isolation escape hatches. Never set in
|
|
@@ -575,7 +577,7 @@ function getDevicesDir() {
|
|
|
575
577
|
}
|
|
576
578
|
/** Path to the device registry — SSH device profiles with platform/auth metadata. Durable runtime, per-machine (host list + addresses are NOT pulled by `agents repo push`). */
|
|
577
579
|
export function getDevicesRegistryPath() { return path.join(getDevicesDir(), 'registry.json'); }
|
|
578
|
-
/** Path to the device ignore-list —
|
|
580
|
+
/** Path to the LEGACY per-machine device ignore-list — superseded by the tracked `fleet.ignored` list in central agents.yaml (RUSH-3062); only lib/devices/config-migration.ts still reads it (to fold + remove it). */
|
|
579
581
|
export function getDevicesIgnoredPath() { return path.join(getDevicesDir(), 'ignored.json'); }
|
|
580
582
|
/** Path to the LEGACY device auto-launch preference file — which registered devices are eligible/preferred for the ext's auto-host selection. Superseded by the per-device doc `config:` block; only lib/devices/config-migration.ts still reads it (to fold + remove it). */
|
|
581
583
|
export function getDevicesAutoLaunchPath() { return path.join(getDevicesDir(), 'auto-launch.json'); }
|
|
@@ -893,7 +895,6 @@ const META_KEY_SCOPE = {
|
|
|
893
895
|
actors: 'central',
|
|
894
896
|
seededPresets: 'central',
|
|
895
897
|
hooks: 'central',
|
|
896
|
-
browser: 'central',
|
|
897
898
|
config: 'central',
|
|
898
899
|
hosts: 'central',
|
|
899
900
|
fleet: 'central',
|
|
@@ -908,7 +909,12 @@ const META_KEY_SCOPE = {
|
|
|
908
909
|
* in central is stale and must be migrated out). A key NOT listed here — e.g. one
|
|
909
910
|
* a newer CLI version added — is preserved verbatim, never dropped + synced away.
|
|
910
911
|
*/
|
|
911
|
-
const KNOWN_META_KEYS = new Set(
|
|
912
|
+
const KNOWN_META_KEYS = new Set([
|
|
913
|
+
...Object.keys(META_KEY_SCOPE),
|
|
914
|
+
// Removed browser-profile store. Kept only as a serializer tombstone so the
|
|
915
|
+
// first registry read can migrate it into this device's file and delete it.
|
|
916
|
+
'browser',
|
|
917
|
+
]);
|
|
912
918
|
/**
|
|
913
919
|
* Serialize the central (synced) meta to `agents.yaml` WITHOUT destroying the
|
|
914
920
|
* hand-written comments in the committed file.
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
*
|
|
28
28
|
* The per-agent `transform`/metadata parsers are the escape hatch: they live in
|
|
29
29
|
* `subagents.ts` and are referenced by the table, so the generic engine has zero
|
|
30
|
-
* per-agent branches. See
|
|
30
|
+
* per-agent branches. See `docs/orchestration.md`.
|
|
31
31
|
*/
|
|
32
32
|
import * as fs from 'fs';
|
|
33
33
|
import * as path from 'path';
|
package/dist/lib/teams/agents.js
CHANGED
|
@@ -1451,6 +1451,15 @@ export class AgentManager {
|
|
|
1451
1451
|
}
|
|
1452
1452
|
this.defaultMode = resolvedDefaultMode;
|
|
1453
1453
|
this.initPromise = this.doInitialize();
|
|
1454
|
+
// Mark the deferred rejection as observed. Construction fires init
|
|
1455
|
+
// fire-and-forget; every public method still surfaces a failed init at its
|
|
1456
|
+
// own `await this.initialize()` (the same promise rejects for each new
|
|
1457
|
+
// awaiter). Without this, an init that loses a race with its directory
|
|
1458
|
+
// being removed — measured twice as an unhandled
|
|
1459
|
+
// `ENOENT mkdir /tmp/agents-retention-*` that failed a fully-green suite
|
|
1460
|
+
// (exit 1 with 12k tests passed) and blocked release attestation — crashes
|
|
1461
|
+
// the process instead of failing the caller that actually cares.
|
|
1462
|
+
this.initPromise.catch(() => { });
|
|
1454
1463
|
}
|
|
1455
1464
|
async initialize() {
|
|
1456
1465
|
if (!this.initPromise) {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Public entry point. Callers typically use `openSurfaces` (a batch with a
|
|
6
6
|
* layout policy) or `openSurface` (a single request), and `availableBackends` /
|
|
7
|
-
* `detectCurrentBackend` to pick a target. See docs/
|
|
7
|
+
* `detectCurrentBackend` to pick a target. See docs/interfaces.md.
|
|
8
8
|
*/
|
|
9
9
|
export type { Backend, SplitDirection, Layout, EngineContext, LaunchSpec, LaunchRequest, LaunchResult, TerminalBackend, } from './types.js';
|
|
10
10
|
export { currentContext } from './types.js';
|
|
@@ -98,12 +98,31 @@ export interface InjectOptions {
|
|
|
98
98
|
export interface InjectResult {
|
|
99
99
|
ok: boolean;
|
|
100
100
|
backend: InjectBackend;
|
|
101
|
+
/**
|
|
102
|
+
* Delivery is CONFIRMED to have reached the running agent, not merely dispatched.
|
|
103
|
+
*
|
|
104
|
+
* For tmux / iterm / ghostty / pty, `ok` already means delivered: `tmux
|
|
105
|
+
* send-keys` errors on a missing pane, `osascript write text` errors on a bad
|
|
106
|
+
* session id, and a pty write errors if the sidecar can't reach the fd — so a
|
|
107
|
+
* successful transport IS the confirmation. For **vscodium**, `ok` only means the
|
|
108
|
+
* editor CLI accepted the `--open-url` hand-off; whether the swarm-ext extension
|
|
109
|
+
* actually parsed the verb and typed anything is unknown from the CLI side (the
|
|
110
|
+
* ext must ack — RUSH follow-up). So vscodium is delivered-but-UNCONFIRMED, and
|
|
111
|
+
* the watchdog must not book a nudge as landed on that alone.
|
|
112
|
+
*/
|
|
113
|
+
confirmed: boolean;
|
|
101
114
|
/** Discrete writes delivered: 2 for the Ink-safe text+Enter split, 1 when combined or enter=false. */
|
|
102
115
|
writes: number;
|
|
103
116
|
/** For the tmux / AppleScript backends (or any dryRun), the spec(s) that ran / would run. */
|
|
104
117
|
specs?: LaunchSpec[];
|
|
105
118
|
error?: string;
|
|
106
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* Whether a successful transport on this backend CONFIRMS delivery to the agent.
|
|
122
|
+
* Everything but vscodium self-confirms (see InjectResult.confirmed); vscodium's
|
|
123
|
+
* `--open-url` is fire-and-forget until the swarm-ext extension acks the verb.
|
|
124
|
+
*/
|
|
125
|
+
export declare function backendConfirmsDelivery(backend: InjectBackend): boolean;
|
|
107
126
|
/**
|
|
108
127
|
* argv for `tmux send-keys` targeting a pane by id. Starts with `tmux` (like the
|
|
109
128
|
* engine's `tmuxTabArgv`) so the same transport runs it locally or over SSH. The
|
|
@@ -46,6 +46,14 @@ import { appleScriptStr } from './quote.js';
|
|
|
46
46
|
import { runSpec } from './transport.js';
|
|
47
47
|
import { itermBackend, ghosttyBackend } from './backends/index.js';
|
|
48
48
|
import { currentContext } from './types.js';
|
|
49
|
+
/**
|
|
50
|
+
* Whether a successful transport on this backend CONFIRMS delivery to the agent.
|
|
51
|
+
* Everything but vscodium self-confirms (see InjectResult.confirmed); vscodium's
|
|
52
|
+
* `--open-url` is fire-and-forget until the swarm-ext extension acks the verb.
|
|
53
|
+
*/
|
|
54
|
+
export function backendConfirmsDelivery(backend) {
|
|
55
|
+
return backend !== 'vscodium';
|
|
56
|
+
}
|
|
49
57
|
/** Carriage return — what Enter delivers into a raw PTY/tmux byte stream. */
|
|
50
58
|
const CR = '\r';
|
|
51
59
|
// --- tmux -------------------------------------------------------------------
|
|
@@ -180,22 +188,23 @@ export function vscodiumInjectSpec(target, text, opts) {
|
|
|
180
188
|
// --- pty --------------------------------------------------------------------
|
|
181
189
|
async function injectPty(target, text, o) {
|
|
182
190
|
if (o.host && o.host !== 'local') {
|
|
183
|
-
return { ok: false, backend: 'pty', writes: 0, error: 'pty injection is local-only (the sidecar is not a remote surface)' };
|
|
191
|
+
return { ok: false, confirmed: false, backend: 'pty', writes: 0, error: 'pty injection is local-only (the sidecar is not a remote surface)' };
|
|
184
192
|
}
|
|
185
193
|
// Same Ink-safe split as tmux: text write, then a separate CR write.
|
|
186
194
|
const writes = o.enter && o.combined ? [text + CR] : o.enter ? [text, CR] : [text];
|
|
187
195
|
if (o.dryRun)
|
|
188
|
-
return { ok: true, backend: 'pty', writes: writes.length };
|
|
196
|
+
return { ok: true, confirmed: true, backend: 'pty', writes: writes.length };
|
|
189
197
|
try {
|
|
190
198
|
for (const input of writes) {
|
|
191
199
|
const res = await ptyRequest('write', target.id, { input });
|
|
192
200
|
if (!res.ok)
|
|
193
|
-
return { ok: false, backend: 'pty', writes: 0, error: res.error ?? 'pty write failed' };
|
|
201
|
+
return { ok: false, confirmed: false, backend: 'pty', writes: 0, error: res.error ?? 'pty write failed' };
|
|
194
202
|
}
|
|
195
|
-
|
|
203
|
+
// A successful sidecar write reached the fd — real delivery.
|
|
204
|
+
return { ok: true, confirmed: true, backend: 'pty', writes: writes.length };
|
|
196
205
|
}
|
|
197
206
|
catch (err) {
|
|
198
|
-
return { ok: false, backend: 'pty', writes: 0, error: err instanceof Error ? err.message : String(err) };
|
|
207
|
+
return { ok: false, confirmed: false, backend: 'pty', writes: 0, error: err instanceof Error ? err.message : String(err) };
|
|
199
208
|
}
|
|
200
209
|
}
|
|
201
210
|
// --- public entry -----------------------------------------------------------
|
|
@@ -225,8 +234,9 @@ export async function injectIntoTerminal(target, text, opts = {}) {
|
|
|
225
234
|
: target.backend === 'ghostty'
|
|
226
235
|
? enter ? 2 : 1
|
|
227
236
|
: enter && !combined ? 2 : 1;
|
|
237
|
+
const confirmed = backendConfirmsDelivery(target.backend);
|
|
228
238
|
if (opts.dryRun)
|
|
229
|
-
return { ok: true, backend: target.backend, writes, specs };
|
|
239
|
+
return { ok: true, confirmed, backend: target.backend, writes, specs };
|
|
230
240
|
// AppleScript backends: guard on the backend's own availability, but only for a
|
|
231
241
|
// LOCAL run (a remote host is assumed to have the app — its ssh leg reports failure).
|
|
232
242
|
if (target.backend === 'iterm' || target.backend === 'ghostty') {
|
|
@@ -234,14 +244,14 @@ export async function injectIntoTerminal(target, text, opts = {}) {
|
|
|
234
244
|
const backend = target.backend === 'iterm' ? itermBackend : ghosttyBackend;
|
|
235
245
|
const ctx = opts.ctx ?? currentContext();
|
|
236
246
|
if (!backend.isAvailable(ctx)) {
|
|
237
|
-
return { ok: false, backend: target.backend, writes: 0, specs, error: `${backend.label} is not available here (platform ${ctx.platform})` };
|
|
247
|
+
return { ok: false, confirmed: false, backend: target.backend, writes: 0, specs, error: `${backend.label} is not available here (platform ${ctx.platform})` };
|
|
238
248
|
}
|
|
239
249
|
}
|
|
240
250
|
}
|
|
241
251
|
for (const spec of specs) {
|
|
242
252
|
const res = await runSpec(spec, opts.host, opts.resolveHost);
|
|
243
253
|
if (!res.ok)
|
|
244
|
-
return { ok: false, backend: target.backend, writes: 0, specs, error: res.error };
|
|
254
|
+
return { ok: false, confirmed: false, backend: target.backend, writes: 0, specs, error: res.error };
|
|
245
255
|
}
|
|
246
|
-
return { ok: true, backend: target.backend, writes, specs };
|
|
256
|
+
return { ok: true, confirmed, backend: target.backend, writes, specs };
|
|
247
257
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* command, in a chosen terminal backend (iTerm / Ghostty / tmux), on this
|
|
6
6
|
* machine or a remote host. This is distinct from cloud providers
|
|
7
7
|
* (src/lib/cloud), which dispatch autonomous headless tasks; a terminal surface
|
|
8
|
-
* is attended and live. See docs/
|
|
8
|
+
* is attended and live. See docs/interfaces.md.
|
|
9
9
|
*/
|
|
10
10
|
/** An interactive terminal backend the engine can drive. */
|
|
11
11
|
export type Backend = 'iterm' | 'ghostty' | 'tmux' | 'vscodium-agent' | 'terminal';
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* command, in a chosen terminal backend (iTerm / Ghostty / tmux), on this
|
|
6
6
|
* machine or a remote host. This is distinct from cloud providers
|
|
7
7
|
* (src/lib/cloud), which dispatch autonomous headless tasks; a terminal surface
|
|
8
|
-
* is attended and live. See docs/
|
|
8
|
+
* is attended and live. See docs/interfaces.md.
|
|
9
9
|
*/
|
|
10
10
|
/** Snapshot the live process context. */
|
|
11
11
|
export function currentContext() {
|
|
@@ -172,7 +172,7 @@ export const DETACHED_HELPER_RULES = [CLAUDE_BG_DAEMON];
|
|
|
172
172
|
export function isProtectedAgentsService(args) {
|
|
173
173
|
return /\b__daemon-run\b/.test(args)
|
|
174
174
|
|| /\bsecrets\s+_agent-run\b/.test(args)
|
|
175
|
-
|| /\
|
|
175
|
+
|| /\bAGI Menu\b/.test(args)
|
|
176
176
|
|| /\bAgents CLI\.app\b/.test(args);
|
|
177
177
|
}
|
|
178
178
|
/** Is this pid currently running? Signal-0 probe, matching `platform/process.ts`. */
|
|
@@ -9,6 +9,22 @@
|
|
|
9
9
|
* pure provenance — `listSessions()` always reconciles them against
|
|
10
10
|
* `tmux list-sessions` and prunes stale entries on the fly.
|
|
11
11
|
*/
|
|
12
|
+
/**
|
|
13
|
+
* Ten times tmux's 2,000-line default, while keeping per-pane memory bounded:
|
|
14
|
+
* history storage scales with pane width, so a 200-column pane retains at most
|
|
15
|
+
* four million grid cells rather than an unbounded agent transcript.
|
|
16
|
+
*/
|
|
17
|
+
export declare const AGENTS_TMUX_HISTORY_LIMIT = 20000;
|
|
18
|
+
/**
|
|
19
|
+
* Bump when the generated startup config changes, so an ALREADY-RUNNING server
|
|
20
|
+
* picks the new settings up. tmux reads `-f` only when it actually starts a
|
|
21
|
+
* server: a second `new-session -f other.conf` against a live server exits 0
|
|
22
|
+
* and silently applies nothing (verified against tmux 3.4). Since agents-cli
|
|
23
|
+
* uses one long-lived shared socket, the normal state at upgrade is a running
|
|
24
|
+
* server — so without this reconcile every existing machine would get none of
|
|
25
|
+
* these defaults, silently. Same shape as AGENT_HOOK_SCHEMA below.
|
|
26
|
+
*/
|
|
27
|
+
export declare const AGENTS_TMUX_CONFIG_SCHEMA = 1;
|
|
12
28
|
/** Provenance written alongside each live tmux session. */
|
|
13
29
|
export interface SessionMeta {
|
|
14
30
|
name: string;
|
|
@@ -62,6 +78,8 @@ export interface ListedSession {
|
|
|
62
78
|
attached: boolean;
|
|
63
79
|
meta?: SessionMeta;
|
|
64
80
|
}
|
|
81
|
+
/** Build the atomic server-configuration + session-creation command. */
|
|
82
|
+
export declare function buildCreateSessionArgs(opts: CreateSessionOptions, startupConfig: string): string[];
|
|
65
83
|
export declare class TmuxSessionError extends Error {
|
|
66
84
|
readonly cause?: unknown | undefined;
|
|
67
85
|
constructor(message: string, cause?: unknown | undefined);
|
|
@@ -213,21 +231,36 @@ export declare function setSessionHook(name: string, hook: string, command: stri
|
|
|
213
231
|
* variant could stall the hook on a loaded CI runner, letting the dead
|
|
214
232
|
* split survive until the test's wait timeout expired (flake in CI shard
|
|
215
233
|
* 3: pane-guarded pane-died hook / user split exit).
|
|
234
|
+
* v6 — the agent-pane branch is now `session_attached`-aware: with a client
|
|
235
|
+
* attached it `detach-client`s (the interactive path, so runInTmux's
|
|
236
|
+
* attach returns and reads the exit status, EXEC-23b unchanged); with NO
|
|
237
|
+
* client it `kill-session`s outright. A `remain-on-exit` pane with no
|
|
238
|
+
* attach client to reap it otherwise lingers as a dead husk until the
|
|
239
|
+
* daemon's periodic `reapDeadTmuxPanes` sweep — so a run whose terminal
|
|
240
|
+
* was closed, or a `/exit` after the user detached, left an idle-looking
|
|
241
|
+
* dead session on the socket. Killing it the instant the agent exits is
|
|
242
|
+
* the "one close tears down every layer" contract (#5a).
|
|
216
243
|
*/
|
|
217
|
-
export declare const AGENT_HOOK_SCHEMA =
|
|
244
|
+
export declare const AGENT_HOOK_SCHEMA = 6;
|
|
218
245
|
/**
|
|
219
|
-
* The guarded `pane-died` hook.
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
246
|
+
* The guarded `pane-died` hook. When the AGENT pane dies, tear the whole session
|
|
247
|
+
* down — but HOW depends on whether a client is attached:
|
|
248
|
+
* - attached (`#{session_attached}`): `detach-client` so the blocking attach in
|
|
249
|
+
* runInTmux returns and `resolveAfterAttach` reads the exit status off the
|
|
250
|
+
* still-`remain-on-exit` dead pane, then kills the session (EXEC-23b, unchanged);
|
|
251
|
+
* - no client: `kill-session` outright. Without this the dead pane sits under
|
|
252
|
+
* `remain-on-exit` as an idle-looking husk until the daemon's periodic
|
|
253
|
+
* `reapDeadTmuxPanes` sweep — the "second exit" / orphaned-idle session bug
|
|
254
|
+
* when a wrapped agent exits with nobody attached (a closed terminal, or a
|
|
255
|
+
* `/exit` after the user detached). `detach-client` on an unattached session
|
|
256
|
+
* is a no-op, so the old hook left the husk; `kill-session` ends it at once.
|
|
257
|
+
* A user split's death still runs the else-branch, closing just that split via
|
|
258
|
+
* `run-shell -b -C` with an explicit `-t #{hook_pane}` target: tmux format-expands
|
|
259
|
+
* the command at fire time and executes it inside the server command queue in the
|
|
260
|
+
* background, so the event pane is always the one killed without launching a second
|
|
261
|
+
* tmux client against the same socket and without stalling the hook on a loaded
|
|
262
|
+
* runner. Single source of truth: both the spawn-wrap (exec.ts) and the daemon
|
|
263
|
+
* reconcile build the hook here, so the two can never drift.
|
|
231
264
|
*/
|
|
232
265
|
export declare function agentPaneDiedHook(sessionName: string, agentPane: string): string;
|
|
233
266
|
/** Stamp a session's hook-schema marker to the current version. */
|