@phnx-labs/agents-cli 1.22.114 → 1.22.116
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 +245 -0
- package/README.md +22 -102
- package/dist/browser.js +0 -0
- package/dist/cli/command-registry.js +0 -7
- package/dist/commands/accounts.d.ts +3 -77
- package/dist/commands/accounts.js +13 -366
- package/dist/commands/audit.js +1 -1
- package/dist/commands/auth.js +0 -2
- package/dist/commands/browser.d.ts +53 -0
- package/dist/commands/browser.js +210 -9
- package/dist/commands/doctor.d.ts +2 -3
- package/dist/commands/doctor.js +0 -11
- package/dist/commands/events.d.ts +1 -3
- package/dist/commands/events.js +4 -7
- package/dist/commands/exec.d.ts +0 -1
- package/dist/commands/exec.js +214 -148
- package/dist/commands/feed-watch.js +52 -8
- package/dist/commands/feed.js +65 -14
- package/dist/commands/focus.js +1 -1
- package/dist/commands/go.d.ts +0 -17
- package/dist/commands/go.js +2 -19
- package/dist/commands/logs.js +1 -1
- package/dist/commands/mcp.js +8 -83
- package/dist/commands/memory.js +4 -47
- package/dist/commands/message.js +4 -4
- package/dist/commands/plugins.js +1 -93
- package/dist/commands/repo.js +0 -44
- package/dist/commands/resume.js +4 -1
- package/dist/commands/secrets-passthrough.js +2 -2
- package/dist/commands/send.d.ts +4 -4
- package/dist/commands/send.js +6 -51
- package/dist/commands/sessions-backup-setup.js +1 -1
- package/dist/commands/sessions-picker.d.ts +11 -0
- package/dist/commands/sessions-picker.js +88 -7
- package/dist/commands/sessions-resume.js +0 -1
- package/dist/commands/sessions-share.d.ts +5 -7
- package/dist/commands/sessions-share.js +98 -49
- package/dist/commands/sessions.d.ts +21 -2
- package/dist/commands/sessions.js +158 -11
- package/dist/commands/setup-browser.js +18 -2
- package/dist/commands/setup-computer.js +20 -6
- package/dist/commands/setup-secrets.d.ts +2 -2
- package/dist/commands/setup-secrets.js +24 -4
- package/dist/commands/setup-term.d.ts +24 -0
- package/dist/commands/setup-term.js +70 -0
- package/dist/commands/setup-terminal.d.ts +3 -0
- package/dist/commands/setup-terminal.js +22 -0
- package/dist/commands/setup.d.ts +1 -1
- package/dist/commands/setup.js +29 -11
- package/dist/commands/skills.js +0 -8
- package/dist/commands/ssh.d.ts +6 -0
- package/dist/commands/ssh.js +93 -302
- package/dist/commands/sync.js +14 -5
- package/dist/commands/teams.js +1 -1
- package/dist/commands/traces.js +1 -1
- package/dist/lib/accounting/rotate.d.ts +63 -1
- package/dist/lib/accounting/rotate.js +56 -0
- package/dist/lib/accounts/add.d.ts +0 -5
- package/dist/lib/accounts/add.js +3 -9
- package/dist/lib/accounts/slots.js +32 -2
- package/dist/lib/answer-router.d.ts +11 -2
- package/dist/lib/answer-router.js +26 -2
- package/dist/lib/artifacts-client.d.ts +20 -0
- package/dist/lib/artifacts-client.js +46 -0
- package/dist/lib/auth-mint.d.ts +5 -4
- package/dist/lib/auth-mint.js +6 -5
- package/dist/lib/browser/drivers/arc.d.ts +1 -1
- package/dist/lib/browser/runtime-state.d.ts +55 -0
- package/dist/lib/browser/runtime-state.js +99 -18
- package/dist/lib/browser/service.d.ts +10 -0
- package/dist/lib/browser/service.js +229 -37
- package/dist/lib/browser/types.d.ts +18 -0
- package/dist/lib/cli-resources.js +3 -1
- package/dist/lib/cloud/dispatch.js +1 -1
- package/dist/lib/cloudflare/creds.d.ts +10 -0
- package/dist/lib/cloudflare/creds.js +46 -0
- package/dist/lib/cloudflare/provision.d.ts +35 -0
- package/dist/lib/cloudflare/provision.js +144 -0
- package/dist/lib/computer/sessions-list.d.ts +55 -0
- package/dist/lib/computer/sessions-list.js +168 -1
- package/dist/lib/config-keys.d.ts +1 -1
- package/dist/lib/config-keys.js +5 -0
- package/dist/lib/daemon/daemon.js +8 -1
- package/dist/lib/daemon/feed-stream-service.d.ts +23 -0
- package/dist/lib/daemon/feed-stream-service.js +40 -0
- package/dist/lib/daemon-services.d.ts +1 -1
- package/dist/lib/daemon-services.js +5 -0
- package/dist/lib/device-config.js +61 -0
- package/dist/lib/devices/connect.d.ts +49 -5
- package/dist/lib/devices/connect.js +169 -21
- package/dist/lib/devices/doctor-findings.js +2 -6
- package/dist/lib/feed/answer.d.ts +153 -4
- package/dist/lib/feed/answer.js +716 -105
- package/dist/lib/feed/envelope.d.ts +79 -0
- package/dist/lib/feed/envelope.js +23 -0
- package/dist/lib/feed/events.d.ts +6 -0
- package/dist/lib/feed/events.js +8 -0
- package/dist/lib/feed/feed.d.ts +61 -1
- package/dist/lib/feed/feed.js +226 -14
- package/dist/lib/feed/hub-server.d.ts +141 -0
- package/dist/lib/feed/hub-server.js +586 -0
- package/dist/lib/feed/hub.d.ts +95 -0
- package/dist/lib/feed/hub.js +255 -0
- package/dist/lib/feed/pr-status.d.ts +8 -0
- package/dist/lib/feed/pr-status.js +9 -1
- package/dist/lib/feed/tool-activity.d.ts +108 -0
- package/dist/lib/feed/tool-activity.js +313 -0
- package/dist/lib/feed/tools.d.ts +198 -0
- package/dist/lib/feed/tools.js +265 -0
- package/dist/lib/feed/watch.d.ts +50 -50
- package/dist/lib/feed/watch.js +147 -16
- package/dist/lib/feed-outcome.d.ts +1 -1
- package/dist/lib/feed-outcome.js +9 -2
- package/dist/lib/feed-policy.js +9 -3
- package/dist/lib/fleet/auth-sync.d.ts +2 -55
- package/dist/lib/fleet/auth-sync.js +2 -89
- package/dist/lib/format.d.ts +1 -1
- package/dist/lib/format.js +1 -1
- package/dist/lib/git.d.ts +0 -16
- package/dist/lib/git.js +0 -58
- package/dist/lib/harness-auth-capabilities.js +7 -2
- package/dist/lib/helper-versions.js +1 -1
- package/dist/lib/hosts/dispatch.d.ts +20 -1
- package/dist/lib/hosts/dispatch.js +52 -30
- package/dist/lib/hosts/remote-cmd.d.ts +72 -1
- package/dist/lib/hosts/remote-cmd.js +151 -10
- package/dist/lib/hosts/remote-cmd.test-fixture.d.ts +2 -0
- package/dist/lib/hosts/remote-cmd.test-fixture.js +22 -0
- package/dist/lib/mailbox.d.ts +12 -0
- package/dist/lib/mailbox.js +16 -2
- package/dist/lib/mcp.js +17 -11
- package/dist/lib/menubar/snapshot.d.ts +51 -0
- package/dist/lib/menubar/snapshot.js +42 -3
- package/dist/lib/open-url.js +2 -2
- package/dist/lib/probe.d.ts +4 -1
- package/dist/lib/probe.js +5 -2
- package/dist/lib/projects.d.ts +23 -0
- package/dist/lib/projects.js +78 -0
- package/dist/lib/pwsh.d.ts +33 -0
- package/dist/lib/pwsh.js +56 -0
- package/dist/lib/redact.d.ts +8 -0
- package/dist/lib/redact.js +11 -0
- package/dist/lib/refresh.d.ts +6 -2
- package/dist/lib/refresh.js +92 -72
- package/dist/lib/secrets-cli.d.ts +11 -0
- package/dist/lib/secrets-cli.js +30 -0
- package/dist/lib/secrets-client.js +3 -2
- package/dist/lib/session/active.d.ts +1 -0
- package/dist/lib/session/active.js +8 -0
- package/dist/lib/session/db.d.ts +67 -3
- package/dist/lib/session/db.js +381 -126
- package/dist/lib/session/detached.d.ts +7 -0
- package/dist/lib/session/detached.js +29 -0
- package/dist/lib/session/prompt.d.ts +23 -7
- package/dist/lib/session/prompt.js +46 -8
- package/dist/lib/session/remote/peer-stream.d.ts +24 -2
- package/dist/lib/session/remote/peer-stream.js +33 -6
- package/dist/lib/session/remote/remote-list.d.ts +20 -0
- package/dist/lib/session/remote/remote-list.js +22 -6
- package/dist/lib/session/remote/watch.d.ts +12 -0
- package/dist/lib/session/remote/watch.js +9 -0
- package/dist/lib/session/remote-preview-cache.d.ts +29 -0
- package/dist/lib/session/remote-preview-cache.js +373 -0
- package/dist/lib/session/sync/backend.d.ts +3 -3
- package/dist/lib/session/sync/backend.js +3 -3
- package/dist/lib/session/sync/provision.d.ts +1 -1
- package/dist/lib/session/sync/provision.js +2 -2
- package/dist/lib/session/tail.d.ts +50 -0
- package/dist/lib/session/tail.js +219 -0
- package/dist/lib/sessions-client.js +0 -3
- package/dist/lib/setup-tool-install.d.ts +3 -0
- package/dist/lib/setup-tool-install.js +27 -0
- package/dist/lib/setup-tool-status.d.ts +22 -0
- package/dist/lib/setup-tool-status.js +220 -0
- package/dist/lib/share-runtime.d.ts +11 -0
- package/dist/lib/share-runtime.js +63 -0
- package/dist/lib/signin-badge.d.ts +19 -4
- package/dist/lib/signin-badge.js +29 -11
- package/dist/lib/smart-launch.d.ts +1 -5
- package/dist/lib/smart-launch.js +3 -11
- package/dist/lib/ssh-exec.d.ts +44 -0
- package/dist/lib/ssh-exec.js +119 -0
- package/dist/lib/startup/command-registry.d.ts +6 -4
- package/dist/lib/startup/command-registry.js +10 -7
- package/dist/lib/state.js +2 -2
- package/dist/lib/storage/selection.d.ts +2 -2
- package/dist/lib/storage/selection.js +2 -2
- package/dist/lib/sync-umbrella.d.ts +5 -0
- package/dist/lib/sync-umbrella.js +18 -10
- package/dist/lib/term-driver.d.ts +24 -0
- package/dist/lib/term-driver.js +36 -0
- package/dist/lib/terminal/index.d.ts +1 -1
- package/dist/lib/terminal/index.js +1 -1
- package/dist/lib/terminal/inject.d.ts +38 -0
- package/dist/lib/terminal/inject.js +55 -9
- package/dist/lib/terminal/transport.d.ts +15 -5
- package/dist/lib/terminal/transport.js +61 -11
- package/dist/lib/traces/backend.d.ts +1 -2
- package/dist/lib/traces/backend.js +1 -2
- package/dist/lib/traces/provision.d.ts +1 -1
- package/dist/lib/traces/provision.js +2 -2
- package/dist/lib/types.d.ts +8 -6
- package/package.json +2 -3
- package/dist/commands/artifacts-setup.d.ts +0 -53
- package/dist/commands/artifacts-setup.js +0 -161
- package/dist/commands/artifacts.d.ts +0 -18
- package/dist/commands/artifacts.js +0 -58
- package/dist/commands/attach.d.ts +0 -12
- package/dist/commands/attach.js +0 -86
- package/dist/commands/auth-mint.d.ts +0 -12
- package/dist/commands/auth-mint.js +0 -108
- package/dist/commands/reconnect.d.ts +0 -46
- package/dist/commands/reconnect.js +0 -115
- package/dist/commands/share.d.ts +0 -293
- package/dist/commands/share.js +0 -1424
- package/dist/lib/fleet/remote-login.d.ts +0 -170
- package/dist/lib/fleet/remote-login.js +0 -568
- package/dist/lib/share/analytics.d.ts +0 -13
- package/dist/lib/share/analytics.js +0 -45
- package/dist/lib/share/backend.d.ts +0 -120
- package/dist/lib/share/backend.js +0 -176
- package/dist/lib/share/capture.d.ts +0 -31
- package/dist/lib/share/capture.js +0 -174
- package/dist/lib/share/config.d.ts +0 -72
- package/dist/lib/share/config.js +0 -211
- package/dist/lib/share/delete.d.ts +0 -123
- package/dist/lib/share/delete.js +0 -173
- package/dist/lib/share/html.d.ts +0 -20
- package/dist/lib/share/html.js +0 -88
- package/dist/lib/share/http-error.d.ts +0 -53
- package/dist/lib/share/http-error.js +0 -65
- package/dist/lib/share/og.d.ts +0 -26
- package/dist/lib/share/og.js +0 -84
- package/dist/lib/share/provision.d.ts +0 -127
- package/dist/lib/share/provision.js +0 -285
- package/dist/lib/share/publish.d.ts +0 -379
- package/dist/lib/share/publish.js +0 -818
- package/dist/lib/share/worker-template.d.ts +0 -27
- package/dist/lib/share/worker-template.js +0 -2424
- package/dist/lib/storage/index.d.ts +0 -14
- package/dist/lib/storage/index.js +0 -14
- package/dist/lib/storage/visibility.d.ts +0 -82
- package/dist/lib/storage/visibility.js +0 -99
|
@@ -505,6 +505,67 @@ export const CONFIG_KEYS = [
|
|
|
505
505
|
defaultValue: true,
|
|
506
506
|
description: 'AGI Menu: show the open pull requests section for each project.',
|
|
507
507
|
},
|
|
508
|
+
{
|
|
509
|
+
name: 'menubar.menu.sessionUpdates',
|
|
510
|
+
yamlKey: 'menubarMenuSessionUpdates',
|
|
511
|
+
scope: 'user',
|
|
512
|
+
type: 'bool',
|
|
513
|
+
defaultValue: true,
|
|
514
|
+
description: 'AGI Menu: show the session-updates section on Home. Off leaves Home to requests and progress ' +
|
|
515
|
+
'only; the full list stays on the Sessions tab either way.',
|
|
516
|
+
},
|
|
517
|
+
{
|
|
518
|
+
name: 'menubar.menu.deviceSort',
|
|
519
|
+
yamlKey: 'menubarMenuDeviceSort',
|
|
520
|
+
scope: 'user',
|
|
521
|
+
type: 'string',
|
|
522
|
+
defaultValue: 'name',
|
|
523
|
+
description: 'AGI Menu: how the device list is ordered — name, role, load, memory, or disk. The spec orders read ' +
|
|
524
|
+
'the same cached fleet stats the rows render, so a device with no observation sorts last rather than ' +
|
|
525
|
+
'as zero.',
|
|
526
|
+
validate: oneOf('menubar.menu.deviceSort', ['name', 'role', 'load', 'memory', 'disk']),
|
|
527
|
+
},
|
|
528
|
+
{
|
|
529
|
+
name: 'menubar.menu.headlessAgent',
|
|
530
|
+
yamlKey: 'menubarMenuHeadlessAgent',
|
|
531
|
+
scope: 'user',
|
|
532
|
+
type: 'string',
|
|
533
|
+
description: 'AGI Menu: the harness a Headless run dispatches to by default (an agent id, e.g. claude). Unset ' +
|
|
534
|
+
'leaves the choice to the menu\'s own default — this key never invents a primary harness.',
|
|
535
|
+
validate: (v) => isAgentId(v) ? null : `menubar.menu.headlessAgent must be an agent id (e.g. claude, codex), got ${JSON.stringify(v)}.`,
|
|
536
|
+
},
|
|
537
|
+
{
|
|
538
|
+
name: 'menubar.menu.headlessFallbackAgent',
|
|
539
|
+
yamlKey: 'menubarMenuHeadlessFallbackAgent',
|
|
540
|
+
scope: 'user',
|
|
541
|
+
type: 'string',
|
|
542
|
+
description: 'AGI Menu: the permitted alternate harness for a Headless run, dispatched as `agents run --fallback ' +
|
|
543
|
+
'<agent>`. It is reached only after every usable account of the primary harness is exhausted (the ' +
|
|
544
|
+
'run\'s own same-agent account failover runs first). Unset sends no --fallback.',
|
|
545
|
+
validate: (v) => isAgentId(v) ? null : `menubar.menu.headlessFallbackAgent must be an agent id (e.g. codex, grok), got ${JSON.stringify(v)}.`,
|
|
546
|
+
},
|
|
547
|
+
{
|
|
548
|
+
name: 'menubar.menu.headlessPlacement',
|
|
549
|
+
yamlKey: 'menubarMenuHeadlessPlacement',
|
|
550
|
+
scope: 'user',
|
|
551
|
+
type: 'string',
|
|
552
|
+
defaultValue: 'auto',
|
|
553
|
+
description: 'AGI Menu: where a Headless run executes — `auto` (the CLI picks an eligible worker; `agents run ' +
|
|
554
|
+
'--device auto` never picks a personal or desktop box), `local` (this machine, no --device), ' +
|
|
555
|
+
'`interactive` (the box pinned as interactive.host), or a device name to pin one explicitly.',
|
|
556
|
+
validate: (v) => {
|
|
557
|
+
const value = v;
|
|
558
|
+
if (value === 'auto' || value === 'local' || value === 'interactive')
|
|
559
|
+
return null;
|
|
560
|
+
try {
|
|
561
|
+
assertValidDeviceName(value);
|
|
562
|
+
return null;
|
|
563
|
+
}
|
|
564
|
+
catch {
|
|
565
|
+
return 'menubar.menu.headlessPlacement must be auto | local | interactive | a device name.';
|
|
566
|
+
}
|
|
567
|
+
},
|
|
568
|
+
},
|
|
508
569
|
];
|
|
509
570
|
/** Look up a key spec by CLI dotted name, or throw listing the known keys. */
|
|
510
571
|
/**
|
|
@@ -30,13 +30,43 @@ export declare function sshTargetFor(device: DeviceProfile): string;
|
|
|
30
30
|
*/
|
|
31
31
|
export declare function fleetDialTarget(device: DeviceProfile): string;
|
|
32
32
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
33
|
+
* Render `cmd` as the single command string ssh sends to the peer.
|
|
34
|
+
*
|
|
35
|
+
* Windows devices speak PowerShell, so the result is run through
|
|
36
|
+
* `powershell -NoProfile -EncodedCommand`; POSIX devices get it as the remote
|
|
37
|
+
* login shell sees it. Returns undefined when no command was given (interactive
|
|
37
38
|
* login).
|
|
39
|
+
*
|
|
40
|
+
* TWO MODES, and the difference is load-bearing rather than a convenience.
|
|
41
|
+
*
|
|
42
|
+
* The default joins the tokens RAW. That is not a bug to be tidied away: every
|
|
43
|
+
* existing caller of `agents ssh` relies on the remote shell interpreting what it
|
|
44
|
+
* is handed — `agents ssh box 'bash -lc "cd x && make"'` arrives as ONE token
|
|
45
|
+
* whose pipeline, globs and redirections the peer's shell must expand. Quoting
|
|
46
|
+
* that would ship the whole line as a literal argument and break it.
|
|
47
|
+
*
|
|
48
|
+
* `{ argv: true }` is for a caller that genuinely holds an argv ARRAY and needs
|
|
49
|
+
* each element delivered as exactly one token. Joining those raw destroys any
|
|
50
|
+
* token containing a space or a metacharacter — `['--title', 'two words']`
|
|
51
|
+
* arrives as three tokens, and `'a & b'` arrives as a backgrounded command — which
|
|
52
|
+
* is what a native client hitting this path actually hit.
|
|
53
|
+
*
|
|
54
|
+
* So fidelity is opt-in at the call site that knows which shape it has, and the
|
|
55
|
+
* quoting itself reuses the canonical helpers (`shellQuote`, {@link pwshQuote})
|
|
56
|
+
* rather than introducing a third escaping scheme.
|
|
38
57
|
*/
|
|
39
|
-
export declare function wrapRemoteCommand(device: DeviceProfile, cmd: string[]
|
|
58
|
+
export declare function wrapRemoteCommand(device: DeviceProfile, cmd: string[], opts?: {
|
|
59
|
+
argv?: boolean;
|
|
60
|
+
prelude?: string[];
|
|
61
|
+
}): string | undefined;
|
|
62
|
+
/**
|
|
63
|
+
* Single-quote one token for PowerShell's OWN parser. Inside a single-quoted pwsh
|
|
64
|
+
* string the only special character is `'`, escaped by doubling.
|
|
65
|
+
*
|
|
66
|
+
* This is correct for a value PowerShell itself consumes, and NOT sufficient for
|
|
67
|
+
* an argument handed on to a native program — see the Win32 note below.
|
|
68
|
+
*/
|
|
69
|
+
export declare function pwshQuote(token: string): string;
|
|
40
70
|
/**
|
|
41
71
|
* True when `cmd` is a browser drive: `agents browser …`, `ag browser …`, or
|
|
42
72
|
* the standalone `browser` binary (`cli/package.json` `bin.browser` →
|
|
@@ -65,6 +95,19 @@ export declare function isAgentsBrowserDrive(cmd: string[]): boolean;
|
|
|
65
95
|
* fan-out stamps the marker before {@link buildSshInvocation}) is left unchanged
|
|
66
96
|
* so nothing is doubled.
|
|
67
97
|
*/
|
|
98
|
+
/**
|
|
99
|
+
* The provenance prefix as READY SHELL SYNTAX for the device's shell.
|
|
100
|
+
*
|
|
101
|
+
* These tokens are already quoted/escaped for their target shell — a POSIX
|
|
102
|
+
* `K=V` pair is `shellQuote`d here, and a PowerShell assignment is a complete
|
|
103
|
+
* statement with its own doubled quotes. That matters because argv mode quotes
|
|
104
|
+
* every token it is handed: quoting THESE again turns
|
|
105
|
+
* `'AGENTS_ACTOR=Some Name'` into `''\''AGENTS_ACTOR=Some Name'\'''` and turns a
|
|
106
|
+
* pwsh assignment into an inert string literal. So the prelude is composed
|
|
107
|
+
* SEPARATELY from the caller's argv rather than concatenated into it, and this is
|
|
108
|
+
* the single definition both paths use.
|
|
109
|
+
*/
|
|
110
|
+
export declare function fleetRemotePrelude(device: Pick<DeviceProfile, 'shell'>, provenanceEnv?: Record<string, string>): string[];
|
|
68
111
|
export declare function markFleetRemote(cmd: string[], device: Pick<DeviceProfile, 'shell'>, provenanceEnv?: Record<string, string>): string[];
|
|
69
112
|
/**
|
|
70
113
|
* Build the remote command that starts an INTERACTIVE LOGIN shell inside a
|
|
@@ -136,6 +179,7 @@ export declare function deviceIdentityArgs(device: DeviceProfile): string[];
|
|
|
136
179
|
export declare function buildSshInvocation(device: DeviceProfile, cmd: string[], askpassShimPath: string, hostKey?: SshHostKeyOptions, opts?: {
|
|
137
180
|
agentOnly?: boolean;
|
|
138
181
|
interactiveCwd?: string;
|
|
182
|
+
argv?: boolean;
|
|
139
183
|
}): {
|
|
140
184
|
args: string[];
|
|
141
185
|
env: Record<string, string>;
|
|
@@ -18,12 +18,14 @@ import * as path from 'path';
|
|
|
18
18
|
import { assertValidSshTarget, shellQuote } from '../ssh-exec.js';
|
|
19
19
|
import { resolveActor, actorEnv } from '../actor.js';
|
|
20
20
|
import { getCliLaunch } from '../cli-entry.js';
|
|
21
|
-
import { encodePwshBase64 } from '../pwsh.js';
|
|
21
|
+
import { encodePwshBase64, pwshLiteral, pwshNativeExecStatements } from '../pwsh.js';
|
|
22
|
+
import { quoteWin32ExecArg } from '../platform/exec.js';
|
|
22
23
|
import { homeRemainder, remoteCdPrefix } from '../project-root.js';
|
|
23
24
|
import { getCacheDir } from '../state.js';
|
|
24
25
|
import { hostKeyCheckingOpts } from './known-hosts.js';
|
|
25
26
|
import { hostNameFor } from './ssh-config.js';
|
|
26
27
|
import { resolveDeviceProfile } from './resolve-profile.js';
|
|
28
|
+
import { renderPowershellCommand, windowsAgentsInvocation } from '../hosts/remote-cmd.js';
|
|
27
29
|
/** Env var the askpass shim reads to know which bundle holds the password. */
|
|
28
30
|
export const ASKPASS_BUNDLE_ENV = 'AGENTS_SSH_BUNDLE';
|
|
29
31
|
/** Env var the askpass shim reads to know which key in the bundle is the password. */
|
|
@@ -72,20 +74,139 @@ export function fleetDialTarget(device) {
|
|
|
72
74
|
}
|
|
73
75
|
}
|
|
74
76
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
77
|
+
* Render `cmd` as the single command string ssh sends to the peer.
|
|
78
|
+
*
|
|
79
|
+
* Windows devices speak PowerShell, so the result is run through
|
|
80
|
+
* `powershell -NoProfile -EncodedCommand`; POSIX devices get it as the remote
|
|
81
|
+
* login shell sees it. Returns undefined when no command was given (interactive
|
|
79
82
|
* login).
|
|
83
|
+
*
|
|
84
|
+
* TWO MODES, and the difference is load-bearing rather than a convenience.
|
|
85
|
+
*
|
|
86
|
+
* The default joins the tokens RAW. That is not a bug to be tidied away: every
|
|
87
|
+
* existing caller of `agents ssh` relies on the remote shell interpreting what it
|
|
88
|
+
* is handed — `agents ssh box 'bash -lc "cd x && make"'` arrives as ONE token
|
|
89
|
+
* whose pipeline, globs and redirections the peer's shell must expand. Quoting
|
|
90
|
+
* that would ship the whole line as a literal argument and break it.
|
|
91
|
+
*
|
|
92
|
+
* `{ argv: true }` is for a caller that genuinely holds an argv ARRAY and needs
|
|
93
|
+
* each element delivered as exactly one token. Joining those raw destroys any
|
|
94
|
+
* token containing a space or a metacharacter — `['--title', 'two words']`
|
|
95
|
+
* arrives as three tokens, and `'a & b'` arrives as a backgrounded command — which
|
|
96
|
+
* is what a native client hitting this path actually hit.
|
|
97
|
+
*
|
|
98
|
+
* So fidelity is opt-in at the call site that knows which shape it has, and the
|
|
99
|
+
* quoting itself reuses the canonical helpers (`shellQuote`, {@link pwshQuote})
|
|
100
|
+
* rather than introducing a third escaping scheme.
|
|
80
101
|
*/
|
|
81
|
-
export function wrapRemoteCommand(device, cmd) {
|
|
102
|
+
export function wrapRemoteCommand(device, cmd, opts = {}) {
|
|
82
103
|
if (cmd.length === 0)
|
|
83
104
|
return undefined;
|
|
84
|
-
const
|
|
105
|
+
const prelude = opts.prelude ?? [];
|
|
106
|
+
let script;
|
|
107
|
+
if (opts.argv) {
|
|
108
|
+
// Quote EACH caller token so the peer receives it byte-for-byte, then prefix
|
|
109
|
+
// the prelude VERBATIM — it is already shell syntax and re-quoting it would
|
|
110
|
+
// break it (see `fleetRemotePrelude`).
|
|
111
|
+
if (device.shell === 'powershell') {
|
|
112
|
+
script = pwshExactArgvScript(cmd, prelude);
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
script = [...prelude, ...cmd.map(shellQuote)].join(' ');
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
// The default joins raw, which is what lets a caller hand the remote shell
|
|
120
|
+
// something to interpret. See the docblock above for why both must exist.
|
|
121
|
+
script = [...prelude, ...cmd].join(' ');
|
|
122
|
+
}
|
|
85
123
|
if (device.shell === 'powershell') {
|
|
86
|
-
|
|
124
|
+
// Same renderer the Windows `agents` launcher uses, so a long script gets the
|
|
125
|
+
// compressed representation here too rather than only on that path. The
|
|
126
|
+
// interactive login route (`buildInteractiveShellCommand`, -NoExit) is
|
|
127
|
+
// deliberately left alone: it must stay an interactive session.
|
|
128
|
+
return renderPowershellCommand(script);
|
|
87
129
|
}
|
|
88
|
-
return
|
|
130
|
+
return script;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Single-quote one token for PowerShell's OWN parser. Inside a single-quoted pwsh
|
|
134
|
+
* string the only special character is `'`, escaped by doubling.
|
|
135
|
+
*
|
|
136
|
+
* This is correct for a value PowerShell itself consumes, and NOT sufficient for
|
|
137
|
+
* an argument handed on to a native program — see the Win32 note below.
|
|
138
|
+
*/
|
|
139
|
+
export function pwshQuote(token) {
|
|
140
|
+
return `'${token.replace(/'/g, "''")}'`;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Emit a PowerShell script that runs `cmd` with EXACT argv, for either kind of
|
|
144
|
+
* target a Windows peer can name.
|
|
145
|
+
*
|
|
146
|
+
* Windows has no argv array: a process receives ONE string and splits it itself.
|
|
147
|
+
* PowerShell 5.1 rebuilds that string when it invokes a native program, and its
|
|
148
|
+
* serializer is lossy — measured on a real peer, an EMPTY argument is dropped and
|
|
149
|
+
* an embedded `"` is discarded, so the callee's argv silently shifts.
|
|
150
|
+
*
|
|
151
|
+
* The obvious fix, the `--%` stop-parsing token, is NOT used, because measurement
|
|
152
|
+
* killed it three ways:
|
|
153
|
+
* - it only applies to a NATIVE command, and `agents` on Windows resolves to
|
|
154
|
+
* `agents.ps1`, so a script target received `--%` as a literal argument and
|
|
155
|
+
* the whole remainder as one string;
|
|
156
|
+
* - a token containing a NEWLINE terminates the directive, producing a parser
|
|
157
|
+
* error;
|
|
158
|
+
* - it performs cmd-style `%VAR%` expansion, so a literal `%PATH%` became six
|
|
159
|
+
* arguments — the exact opposite of exact argv.
|
|
160
|
+
*
|
|
161
|
+
* So the script branches on what the peer's own command discovery finds:
|
|
162
|
+
*
|
|
163
|
+
* - **native executable** — launched through `System.Diagnostics.Process` with a
|
|
164
|
+
* pre-built `Arguments` string escaped by {@link quoteWin32ExecArg}. .NET hands
|
|
165
|
+
* that string to `CreateProcess` essentially verbatim, so the child's
|
|
166
|
+
* `CommandLineToArgvW` reconstructs the tokens exactly; no shell sees it, so
|
|
167
|
+
* no `%VAR%` expansion and no newline sensitivity. `UseShellExecute = $false`
|
|
168
|
+
* with no redirection leaves the child on the inherited handles, which is what
|
|
169
|
+
* lets a binary stdout stream through unchanged.
|
|
170
|
+
* - **anything else** (a `.ps1`/`.cmd` launcher, a function, a cmdlet, an alias)
|
|
171
|
+
* — invoked with a splatted PowerShell array. That is an in-process call, so
|
|
172
|
+
* the native serializer is never involved and every token survives as itself.
|
|
173
|
+
*
|
|
174
|
+
* The exit code is propagated in both branches; a script launcher that sets no
|
|
175
|
+
* `$LASTEXITCODE` is left alone rather than forced to 0.
|
|
176
|
+
*/
|
|
177
|
+
function pwshExactArgvScript(cmd, prelude) {
|
|
178
|
+
// The Agents CLI gets the canonical launcher, not the generic dispatch. On
|
|
179
|
+
// Windows `agents` is an npm `agents.ps1` whose own body splats `$args` into
|
|
180
|
+
// native node.exe — the PowerShell 5.1 lossy path — so even a perfectly
|
|
181
|
+
// splatted call into that script loses an embedded quote one layer deeper.
|
|
182
|
+
// `windowsAgentsInvocation` resolves the package's declared entry and runs it
|
|
183
|
+
// directly, which is the only way the real Agents parser sees exact tokens.
|
|
184
|
+
const bin = cmd[0];
|
|
185
|
+
if (bin === 'agents' || bin === 'ag') {
|
|
186
|
+
// `windowsAgentsInvocation` leaves the child's code in `$zq`.
|
|
187
|
+
return [...prelude, windowsAgentsInvocation(cmd.slice(1), bin), 'exit $zq'].join('\n');
|
|
188
|
+
}
|
|
189
|
+
const program = pwshQuote(cmd[0]);
|
|
190
|
+
const rest = cmd.slice(1);
|
|
191
|
+
const splat = rest.length > 0 ? `@(${rest.map(pwshQuote).join(', ')})` : '@()';
|
|
192
|
+
return [
|
|
193
|
+
...prelude,
|
|
194
|
+
`$ErrorActionPreference='Stop'`,
|
|
195
|
+
`$__c = Get-Command -Name ${program} -ErrorAction Stop`,
|
|
196
|
+
`if ($__c.CommandType -eq 'Application') {`,
|
|
197
|
+
// One emitter for the .NET native-exec block, shared with the Windows
|
|
198
|
+
// `agents` launcher in `hosts/remote-cmd.ts`; a second copy would drift.
|
|
199
|
+
...pwshNativeExecStatements('$__c.Source', pwshLiteral(rest.map(quoteWin32ExecArg).join(' '))).map((line) => ` ${line}`),
|
|
200
|
+
// `pwshNativeExecStatements` names the process handle `$zp`.
|
|
201
|
+
` exit $zp.ExitCode`,
|
|
202
|
+
`}`,
|
|
203
|
+
// `@__a` SPLATS the array into separate arguments. `& $__c @(...)` on an
|
|
204
|
+
// array LITERAL does not splat — it passes one array-valued argument, which
|
|
205
|
+
// a real peer reported back as every token collapsed into one.
|
|
206
|
+
`$__a = ${splat}`,
|
|
207
|
+
`& $__c @__a`,
|
|
208
|
+
`if ($null -ne $LASTEXITCODE) { exit $LASTEXITCODE }`,
|
|
209
|
+
].join('\n');
|
|
89
210
|
}
|
|
90
211
|
/**
|
|
91
212
|
* True when `cmd` is a browser drive: `agents browser …`, `ag browser …`, or
|
|
@@ -123,22 +244,43 @@ export function isAgentsBrowserDrive(cmd) {
|
|
|
123
244
|
* fan-out stamps the marker before {@link buildSshInvocation}) is left unchanged
|
|
124
245
|
* so nothing is doubled.
|
|
125
246
|
*/
|
|
126
|
-
|
|
247
|
+
/**
|
|
248
|
+
* The provenance prefix as READY SHELL SYNTAX for the device's shell.
|
|
249
|
+
*
|
|
250
|
+
* These tokens are already quoted/escaped for their target shell — a POSIX
|
|
251
|
+
* `K=V` pair is `shellQuote`d here, and a PowerShell assignment is a complete
|
|
252
|
+
* statement with its own doubled quotes. That matters because argv mode quotes
|
|
253
|
+
* every token it is handed: quoting THESE again turns
|
|
254
|
+
* `'AGENTS_ACTOR=Some Name'` into `''\''AGENTS_ACTOR=Some Name'\'''` and turns a
|
|
255
|
+
* pwsh assignment into an inert string literal. So the prelude is composed
|
|
256
|
+
* SEPARATELY from the caller's argv rather than concatenated into it, and this is
|
|
257
|
+
* the single definition both paths use.
|
|
258
|
+
*/
|
|
259
|
+
export function fleetRemotePrelude(device, provenanceEnv = actorEnv(resolveActor())) {
|
|
127
260
|
if (device.shell === 'powershell') {
|
|
128
|
-
|
|
129
|
-
// is always this literal, so `startsWith` would only loosen it for no gain.
|
|
130
|
-
if (cmd[0] === `$env:AGENTS_FLEET_REMOTE='1';`)
|
|
131
|
-
return cmd;
|
|
132
|
-
const prelude = [
|
|
261
|
+
return [
|
|
133
262
|
`$env:AGENTS_FLEET_REMOTE='1';`,
|
|
134
263
|
...Object.entries(provenanceEnv).map(([k, v]) => `$env:${k}='${v.replace(/'/g, "''")}';`),
|
|
135
264
|
];
|
|
136
|
-
return [...prelude, ...cmd];
|
|
137
265
|
}
|
|
138
|
-
|
|
266
|
+
return [
|
|
267
|
+
'env',
|
|
268
|
+
'AGENTS_FLEET_REMOTE=1',
|
|
269
|
+
...Object.entries(provenanceEnv).map(([k, v]) => shellQuote(`${k}=${v}`)),
|
|
270
|
+
];
|
|
271
|
+
}
|
|
272
|
+
/** True when `cmd` already carries the prelude {@link fleetRemotePrelude} emits. */
|
|
273
|
+
function alreadyMarked(cmd, device) {
|
|
274
|
+
if (device.shell === 'powershell')
|
|
275
|
+
return cmd[0] === `$env:AGENTS_FLEET_REMOTE='1';`;
|
|
276
|
+
return cmd[0] === 'env' && cmd[1] === 'AGENTS_FLEET_REMOTE=1';
|
|
277
|
+
}
|
|
278
|
+
export function markFleetRemote(cmd, device, provenanceEnv = actorEnv(resolveActor())) {
|
|
279
|
+
// Exact-match guard: the marker token is always this literal, so `startsWith`
|
|
280
|
+
// would only loosen it for no gain.
|
|
281
|
+
if (alreadyMarked(cmd, device))
|
|
139
282
|
return cmd;
|
|
140
|
-
|
|
141
|
-
return ['env', 'AGENTS_FLEET_REMOTE=1', ...actorTokens, ...cmd];
|
|
283
|
+
return [...fleetRemotePrelude(device, provenanceEnv), ...cmd];
|
|
142
284
|
}
|
|
143
285
|
/**
|
|
144
286
|
* Build the remote command that starts an INTERACTIVE LOGIN shell inside a
|
|
@@ -229,10 +371,16 @@ export function buildSshInvocation(device, cmd, askpassShimPath, hostKey = {}, o
|
|
|
229
371
|
// Stamp the consent marker on the REMOTE command, not the local ssh env:
|
|
230
372
|
// SSH_ASKPASS lives on this side; AGENTS_FLEET_REMOTE must be visible to the
|
|
231
373
|
// process that runs on the peer.
|
|
232
|
-
|
|
374
|
+
// Provenance is composed as a PRELUDE, not prepended to the argv array. In argv
|
|
375
|
+
// mode every token handed to `wrapRemoteCommand` is quoted, and the prelude is
|
|
376
|
+
// already shell syntax — mixing them in meant the actor pairs were quoted twice
|
|
377
|
+
// (breaking any value with a space or a quote) and the pwsh assignments became
|
|
378
|
+
// inert string literals.
|
|
379
|
+
const needsProvenance = !interactive && isAgentsBrowserDrive(cmd) && !alreadyMarked(cmd, device);
|
|
380
|
+
const prelude = needsProvenance ? fleetRemotePrelude(device) : [];
|
|
233
381
|
const remote = interactive
|
|
234
382
|
? buildInteractiveShellCommand(device, opts.interactiveCwd)
|
|
235
|
-
: wrapRemoteCommand(device,
|
|
383
|
+
: wrapRemoteCommand(device, cmd, { ...(opts.argv ? { argv: true } : {}), ...(prelude.length > 0 ? { prelude } : {}) });
|
|
236
384
|
const env = {};
|
|
237
385
|
const args = [
|
|
238
386
|
...hostKeyCheckingOpts(hostKey.pinned ?? false, hostKey.knownHostsFile),
|
|
@@ -41,7 +41,7 @@ import chalk from 'chalk';
|
|
|
41
41
|
import * as path from 'path';
|
|
42
42
|
import { AGENTS, ALL_AGENT_IDS, supportsAccountInspection } from '../agents.js';
|
|
43
43
|
import { blocksLocalScripts } from '../platform/winpath.js';
|
|
44
|
-
import { loginHint } from '../signin-badge.js';
|
|
44
|
+
import { loginHint, loginSubcommand, SUBCOMMAND_LOGIN_AGENTS } from '../signin-badge.js';
|
|
45
45
|
import { CONFIG_ENV_ISOLATED_AGENTS } from '../installations/shims.js';
|
|
46
46
|
import { padToWidth, stringWidth } from '../text/width.js';
|
|
47
47
|
import { windowsSshEnrollmentProblem } from './windows-ssh-enrollment.js';
|
|
@@ -78,11 +78,7 @@ const NO_PER_VERSION_LOGIN = new Set(ALL_AGENT_IDS.filter((a) => !ISOLATED_LOGIN
|
|
|
78
78
|
* `subcommand` — `<cli> login` / `<cli> auth login`, runnable via `--`
|
|
79
79
|
* `in-tui` — claude only: a `/login` slash command inside its own TUI
|
|
80
80
|
* `on-launch` — the device/oauth flow starts when the agent launches */
|
|
81
|
-
const LOGIN_SUBCOMMAND =
|
|
82
|
-
codex: 'login',
|
|
83
|
-
grok: 'login',
|
|
84
|
-
opencode: 'auth login',
|
|
85
|
-
};
|
|
81
|
+
const LOGIN_SUBCOMMAND = Object.fromEntries(SUBCOMMAND_LOGIN_AGENTS.map((agent) => [agent, loginSubcommand(agent)]));
|
|
86
82
|
/** Kinds whose fix is inherently PER VERSION, so a row collapsed across versions
|
|
87
83
|
* could not carry a correct remediation. A login is the whole set: there is no
|
|
88
84
|
* `@all` selector for it, and dropping the version falls back to the bare native
|
|
@@ -1,14 +1,133 @@
|
|
|
1
1
|
import { type ActiveSession } from '../session/active.js';
|
|
2
2
|
import { type MessageReceipt } from './feed.js';
|
|
3
|
+
/**
|
|
4
|
+
* Total budget for one answer. Sits below the AGI Menu's 30s abandon so the
|
|
5
|
+
* operator always gets a typed verdict instead of a timeout.
|
|
6
|
+
*/
|
|
7
|
+
export declare const ANSWER_DEADLINE_MS = 20000;
|
|
8
|
+
/** Slice of the budget the optional PR-review enrichment may spend. */
|
|
9
|
+
export declare const PR_ENRICHMENT_BUDGET_MS = 4000;
|
|
10
|
+
/** Budget for the forwarded leg — the remote repeats the local work under its own deadline. */
|
|
11
|
+
export declare const REMOTE_ANSWER_TIMEOUT_MS = 25000;
|
|
12
|
+
/**
|
|
13
|
+
* A claim older than this with no receipt was stranded by a kill, not left in
|
|
14
|
+
* flight: it exceeds a full local deadline plus the remote leg, so no live
|
|
15
|
+
* delivery can still be running behind it.
|
|
16
|
+
*/
|
|
17
|
+
export declare const STRANDED_CLAIM_MS = 60000;
|
|
18
|
+
/**
|
|
19
|
+
* How long to watch a `resume` child before giving up on a verdict. A resume
|
|
20
|
+
* runs the agent's whole next turn — minutes — so waiting for exit would blow the
|
|
21
|
+
* operator deadline on every headless answer. Only a clean early exit is booked;
|
|
22
|
+
* a non-zero exit or a still-running child is unknown, because neither proves
|
|
23
|
+
* the agent did or did not accept the prompt.
|
|
24
|
+
*/
|
|
25
|
+
export declare const RESUME_SETTLE_MS = 2000;
|
|
26
|
+
/**
|
|
27
|
+
* How long a caller that LOST the claim waits for the holder's receipt before
|
|
28
|
+
* reporting unknown. The holder is mid-delivery — a double-clicked answer is the
|
|
29
|
+
* common case — so a short wait turns "I can't tell" into the real receipt,
|
|
30
|
+
* without ever delivering a second copy.
|
|
31
|
+
*/
|
|
32
|
+
export declare const HOLDER_RECEIPT_WAIT_MS = 400;
|
|
33
|
+
/** Why an answer could not be delivered — the discriminant the operator UI branches on. */
|
|
34
|
+
export type AnswerFailureCode = 'malformed_key' | 'no_session' | 'stale' | 'unverified' | 'unauthorized' | 'unknown_choice' | 'empty_answer' | 'refused' | 'rail_failed' | 'timeout' | 'remote_failed';
|
|
35
|
+
/** A CONFIRMED failure: nothing reached a rail, the claim is released, retry is safe. */
|
|
36
|
+
export declare class AnswerError extends Error {
|
|
37
|
+
readonly code: AnswerFailureCode;
|
|
38
|
+
constructor(message: string, code: AnswerFailureCode);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* An UNCONFIRMED outcome: something may have been delivered. The claim is kept
|
|
42
|
+
* so a retry cannot double-send, and the operator is offered a delivery check
|
|
43
|
+
* rather than a resend.
|
|
44
|
+
*/
|
|
45
|
+
export declare class AnswerUnknownError extends Error {
|
|
46
|
+
constructor(message: string);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* delivered — this call handed the answer to a rail.
|
|
50
|
+
* already_answered — another claim owns it; the reported evidence is that claim's.
|
|
51
|
+
* unknown — something may have landed; do NOT resend, check delivery.
|
|
52
|
+
* failed — confirmed: nothing was delivered.
|
|
53
|
+
*/
|
|
54
|
+
export type AnswerStatus = 'delivered' | 'already_answered' | 'unknown' | 'failed';
|
|
55
|
+
/**
|
|
56
|
+
* receipt — a real {@link MessageReceipt} exists on the block.
|
|
57
|
+
* unconfirmed — no receipt evidence either way. The card must NOT be cleared.
|
|
58
|
+
* failed — confirmed failure, including a `dropped`/`expired` receipt.
|
|
59
|
+
*/
|
|
60
|
+
export type AnswerDelivery = 'receipt' | 'unconfirmed' | 'failed';
|
|
61
|
+
export interface FeedAnswerResult {
|
|
62
|
+
status: AnswerStatus;
|
|
63
|
+
/** How much the CLI can actually vouch for. */
|
|
64
|
+
delivery: AnswerDelivery;
|
|
65
|
+
/** The block's real receipt. Absent means no rail ever reported one. */
|
|
66
|
+
receipt?: MessageReceipt;
|
|
67
|
+
/**
|
|
68
|
+
* The AGENT's own evidence that it received the answer — a `consumed` or
|
|
69
|
+
* `continued` receipt. A `queued` receipt is delivery, never resolution, so it
|
|
70
|
+
* leaves this false. Removal of the item from the feed resolves the card on
|
|
71
|
+
* its own; this flag only ever adds evidence, it never withholds it.
|
|
72
|
+
*/
|
|
73
|
+
resolved: boolean;
|
|
74
|
+
/** Human explanation for a failure, an unknown, or an unconfirmed delivery. */
|
|
75
|
+
reason?: string;
|
|
76
|
+
/** Set when `status` is `failed`. */
|
|
77
|
+
code?: AnswerFailureCode;
|
|
78
|
+
attentionKey: string;
|
|
79
|
+
blockId?: string;
|
|
80
|
+
/** The device that owns the item — the exact target a delivery check re-queries. */
|
|
81
|
+
host?: string;
|
|
82
|
+
/**
|
|
83
|
+
* Stable identity of the delivery attempt (the claim timestamp). A delivery
|
|
84
|
+
* check correlates its read-only answer with the attempt it is checking, so
|
|
85
|
+
* "still unconfirmed" is distinguishable from "a newer attempt replaced it".
|
|
86
|
+
*/
|
|
87
|
+
attempt?: string;
|
|
88
|
+
}
|
|
3
89
|
interface VerifiedOperator {
|
|
4
90
|
id?: string;
|
|
5
91
|
verified: boolean;
|
|
6
92
|
label?: string;
|
|
7
93
|
}
|
|
8
|
-
interface
|
|
9
|
-
|
|
10
|
-
|
|
94
|
+
export interface ParsedAttentionKey {
|
|
95
|
+
host: string;
|
|
96
|
+
sessionId: string;
|
|
97
|
+
generation: string;
|
|
11
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* Split `<host>/<session>/<generation>` (attention.ts `attentionKey`). Fails
|
|
101
|
+
* loud on a malformed key: the old `slice(0, indexOf('/'))` returned a truncated
|
|
102
|
+
* host for a key with no separator, which then routed the answer at random.
|
|
103
|
+
*/
|
|
104
|
+
export declare function parseAttentionKey(key: string): ParsedAttentionKey;
|
|
105
|
+
/**
|
|
106
|
+
* Whether THIS machine owns the item. The exact local/remote choice: the key's
|
|
107
|
+
* host is compared through the same {@link normalizeHost} the machine id is
|
|
108
|
+
* minted with, so `Yosemite-M4.local` and `yosemite-m4` are one machine.
|
|
109
|
+
*/
|
|
110
|
+
export declare function answerOwnerIsLocal(host: string, self?: string): boolean;
|
|
111
|
+
/**
|
|
112
|
+
* What a stored receipt proves. The lifecycle vocabulary is fixed by
|
|
113
|
+
* `MessageReceipt`, and each member means exactly one thing here:
|
|
114
|
+
* queued — a rail took the answer. Delivery, never resolution.
|
|
115
|
+
* consumed/continued— the agent itself acknowledged it. This resolves the item.
|
|
116
|
+
* dropped/expired — terminal failure; it is the newest truth for its message
|
|
117
|
+
* (the write rank puts it top precisely so it cannot be
|
|
118
|
+
* regressed), which is exactly why it must not read as success.
|
|
119
|
+
*/
|
|
120
|
+
export declare function classifyReceipt(receipt: MessageReceipt): {
|
|
121
|
+
delivery: AnswerDelivery;
|
|
122
|
+
resolved: boolean;
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* Read-only reconciliation of an attention item's delivery state — what powers
|
|
126
|
+
* "Check delivery" on an unconfirmed answer. It NEVER claims, routes, adopts or
|
|
127
|
+
* resends; it reports the stored claim and the block's real receipt so an
|
|
128
|
+
* operator can tell "still unconfirmed" from "the agent has it".
|
|
129
|
+
*/
|
|
130
|
+
export declare function checkAnswerDelivery(attentionKey: string, feedRoot?: string, expectedAttempt?: string): FeedAnswerResult;
|
|
12
131
|
/** Atomically claim the first answer, then route it over the session's recorded reply rail. */
|
|
13
132
|
export declare function claimAndRouteAttentionAnswer(input: {
|
|
14
133
|
attentionKey: string;
|
|
@@ -18,13 +137,43 @@ export declare function claimAndRouteAttentionAnswer(input: {
|
|
|
18
137
|
feedRoot?: string;
|
|
19
138
|
mailboxRoot?: string;
|
|
20
139
|
sessions?: ActiveSession[];
|
|
140
|
+
deadlineMs?: number;
|
|
21
141
|
}): Promise<FeedAnswerResult>;
|
|
22
|
-
/**
|
|
142
|
+
/**
|
|
143
|
+
* The remote `agents feed answer` argv for a forwarded answer. Exported so the
|
|
144
|
+
* exact tokens that cross the hop are asserted directly.
|
|
145
|
+
*/
|
|
146
|
+
export declare function remoteAnswerArgv(input: {
|
|
147
|
+
attentionKey: string;
|
|
148
|
+
choiceId?: string;
|
|
149
|
+
text?: string;
|
|
150
|
+
operatorId?: string;
|
|
151
|
+
check?: boolean;
|
|
152
|
+
attempt?: string;
|
|
153
|
+
}): string[];
|
|
154
|
+
/**
|
|
155
|
+
* Forward a fleet attention answer to the device that owns its scope.
|
|
156
|
+
*
|
|
157
|
+
* Every token is POSIX-quoted with the canonical {@link shellQuote} before the
|
|
158
|
+
* remote login shell parses it, so a multiline answer or one carrying `$`, `"`,
|
|
159
|
+
* `` ` ``, `;` or a newline arrives byte-identical. The transport is the
|
|
160
|
+
* canonical bounded {@link sshExecAsync} — it disables ControlMaster whenever a
|
|
161
|
+
* timeout is set, so the bound actually tears the remote command down instead of
|
|
162
|
+
* orphaning it behind a control socket.
|
|
163
|
+
*
|
|
164
|
+
* A timeout is reported UNKNOWN: the remote may well have delivered before the
|
|
165
|
+
* link was cut, so the operator is offered a delivery check, never an implicit
|
|
166
|
+
* resend. The returned receipt is verified to be about the key that was asked
|
|
167
|
+
* for, so a mismatched or truncated remote reply cannot be trusted as one.
|
|
168
|
+
*/
|
|
23
169
|
export declare function forwardFeedAnswer(input: {
|
|
24
170
|
host: string;
|
|
25
171
|
attentionKey: string;
|
|
26
172
|
choiceId?: string;
|
|
27
173
|
text?: string;
|
|
28
174
|
operatorId?: string;
|
|
175
|
+
check?: boolean;
|
|
176
|
+
attempt?: string;
|
|
177
|
+
timeoutMs?: number;
|
|
29
178
|
}): Promise<FeedAnswerResult>;
|
|
30
179
|
export {};
|