harnery 0.42.0 → 0.44.0
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/README.md +3 -3
- package/dist/commander.d.ts +16 -0
- package/dist/commander.d.ts.map +1 -1
- package/dist/commander.js +18 -0
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +95 -52
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +38 -11
- package/dist/commands/diagnostics.d.ts.map +1 -1
- package/dist/commands/diagnostics.js +10 -0
- package/dist/commands/doctor.d.ts +12 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +22 -7
- package/dist/commands/governor.js +22 -10
- package/dist/commands/inbox.d.ts +1 -1
- package/dist/commands/inbox.d.ts.map +1 -1
- package/dist/commands/inbox.js +55 -2
- package/dist/commands/rm.d.ts.map +1 -1
- package/dist/commands/rm.js +28 -4
- package/dist/commands/servers.d.ts.map +1 -1
- package/dist/commands/servers.js +16 -1
- package/dist/commands/supervisor.d.ts.map +1 -1
- package/dist/commands/supervisor.js +7 -2
- package/dist/commands/toc.d.ts +14 -0
- package/dist/commands/toc.d.ts.map +1 -1
- package/dist/commands/toc.js +7 -4
- package/dist/commands/tunnel.d.ts +65 -0
- package/dist/commands/tunnel.d.ts.map +1 -1
- package/dist/commands/tunnel.js +290 -101
- package/dist/commands/web-fetch.d.ts +3 -2
- package/dist/commands/web-fetch.d.ts.map +1 -1
- package/dist/commands/web-fetch.js +25 -19
- package/dist/commands/web.d.ts +7 -1
- package/dist/commands/web.d.ts.map +1 -1
- package/dist/commands/web.js +9 -3
- package/dist/commands/workflow.js +24 -30
- package/dist/core/agents/live-authority-v3.d.ts +1 -0
- package/dist/core/agents/live-authority-v3.d.ts.map +1 -1
- package/dist/core/agents/live-authority-v3.js +11 -0
- package/dist/core/agents/live-lifecycle-v3.d.ts +4 -2
- package/dist/core/agents/live-lifecycle-v3.d.ts.map +1 -1
- package/dist/core/agents/live-lifecycle-v3.js +28 -7
- package/dist/core/agents/mailbox.d.ts +9 -8
- package/dist/core/agents/mailbox.d.ts.map +1 -1
- package/dist/core/agents/mailbox.js +24 -17
- package/dist/core/agents/render/prompt-context.d.ts +2 -0
- package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
- package/dist/core/agents/render/prompt-context.js +19 -17
- package/dist/core/agents/render/session-context.d.ts +2 -8
- package/dist/core/agents/render/session-context.d.ts.map +1 -1
- package/dist/core/agents/render/session-context.js +64 -43
- package/dist/core/agents/rules/stop-hook.js +3 -3
- package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-writer.js +25 -14
- package/dist/core/agents/state/live-coordination-writer.d.ts.map +1 -1
- package/dist/core/agents/state/live-coordination-writer.js +5 -0
- package/dist/core/agents/state/names.d.ts +43 -3
- package/dist/core/agents/state/names.d.ts.map +1 -1
- package/dist/core/agents/state/names.js +87 -3
- package/dist/core/agents/state/pidmap.d.ts.map +1 -1
- package/dist/core/agents/state/pidmap.js +5 -0
- package/dist/core/agents/state/proc-start.d.ts +19 -0
- package/dist/core/agents/state/proc-start.d.ts.map +1 -1
- package/dist/core/agents/state/proc-start.js +41 -0
- package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
- package/dist/core/agents/state/stale-sweep.js +180 -70
- package/dist/core/agents/state/unchanged-file.d.ts +44 -0
- package/dist/core/agents/state/unchanged-file.d.ts.map +1 -0
- package/dist/core/agents/state/unchanged-file.js +390 -0
- package/dist/core/context/index.d.ts +22 -10
- package/dist/core/context/index.d.ts.map +1 -1
- package/dist/core/context/index.js +381 -119
- package/dist/core/context/observation.d.ts +26 -0
- package/dist/core/context/observation.d.ts.map +1 -0
- package/dist/core/context/observation.js +11 -0
- package/dist/core/diagnostics/bundle.d.ts.map +1 -1
- package/dist/core/diagnostics/bundle.js +25 -14
- package/dist/core/diagnostics/path-identity.d.ts +3 -0
- package/dist/core/diagnostics/path-identity.d.ts.map +1 -0
- package/dist/core/diagnostics/path-identity.js +12 -0
- package/dist/core/diagnostics/sanitize.d.ts.map +1 -1
- package/dist/core/diagnostics/sanitize.js +4 -3
- package/dist/core/events/v3/authority-outbox.d.ts.map +1 -1
- package/dist/core/events/v3/authority-outbox.js +29 -0
- package/dist/core/events/v3/generation-custody.d.ts +5 -0
- package/dist/core/events/v3/generation-custody.d.ts.map +1 -0
- package/dist/core/events/v3/generation-custody.js +50 -0
- package/dist/core/events/v3/producers/coordination-recorder.d.ts +1 -0
- package/dist/core/events/v3/producers/coordination-recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/coordination-recorder.js +7 -0
- package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/recorder.js +77 -64
- package/dist/core/events/v3/support-storage/pack-reader.d.ts.map +1 -1
- package/dist/core/events/v3/support-storage/pack-reader.js +42 -16
- package/dist/core/governor/planning.d.ts.map +1 -1
- package/dist/core/governor/planning.js +14 -1
- package/dist/core/governor/service-read.d.ts +10 -2
- package/dist/core/governor/service-read.d.ts.map +1 -1
- package/dist/core/governor/service-read.js +105 -22
- package/dist/core/governor/service.d.ts +20 -2
- package/dist/core/governor/service.d.ts.map +1 -1
- package/dist/core/governor/service.js +72 -47
- package/dist/core/hooks/cli.js +120 -23
- package/dist/core/hooks/fork-lineage.d.ts +40 -0
- package/dist/core/hooks/fork-lineage.d.ts.map +1 -0
- package/dist/core/hooks/fork-lineage.js +62 -0
- package/dist/core/hooks/resolve/transcript.d.ts +16 -0
- package/dist/core/hooks/resolve/transcript.d.ts.map +1 -1
- package/dist/core/hooks/resolve/transcript.js +84 -0
- package/dist/core/hooks/shell-rm.d.ts +2 -0
- package/dist/core/hooks/shell-rm.d.ts.map +1 -0
- package/dist/core/hooks/shell-rm.js +105 -0
- package/dist/core/hooks/shell-tokens.d.ts +12 -0
- package/dist/core/hooks/shell-tokens.d.ts.map +1 -0
- package/dist/core/hooks/shell-tokens.js +97 -0
- package/dist/core/hooks/shell-waiter.d.ts.map +1 -1
- package/dist/core/hooks/shell-waiter.js +3 -93
- package/dist/core/inbox/contract.d.ts +2 -1
- package/dist/core/inbox/contract.d.ts.map +1 -1
- package/dist/core/inbox/index.d.ts +2 -0
- package/dist/core/inbox/index.d.ts.map +1 -1
- package/dist/core/inbox/index.js +2 -0
- package/dist/core/inbox/peer-messages.d.ts +37 -0
- package/dist/core/inbox/peer-messages.d.ts.map +1 -0
- package/dist/core/inbox/peer-messages.js +72 -0
- package/dist/core/inbox/service.d.ts +8 -1
- package/dist/core/inbox/service.d.ts.map +1 -1
- package/dist/core/inbox/service.js +67 -21
- package/dist/core/inbox/targets.d.ts +22 -0
- package/dist/core/inbox/targets.d.ts.map +1 -0
- package/dist/core/inbox/targets.js +108 -0
- package/dist/core/inbox/watcher.js +7 -3
- package/dist/core/journal/index.d.ts.map +1 -1
- package/dist/core/journal/index.js +17 -2
- package/dist/core/resources/contract.d.ts +3 -0
- package/dist/core/resources/contract.d.ts.map +1 -1
- package/dist/core/resources/service-status.d.ts +14 -1
- package/dist/core/resources/service-status.d.ts.map +1 -1
- package/dist/core/resources/service-status.js +23 -13
- package/dist/core/resources/service.d.ts +9 -1
- package/dist/core/resources/service.d.ts.map +1 -1
- package/dist/core/resources/service.js +47 -27
- package/dist/core/resources/status.d.ts +2 -5
- package/dist/core/resources/status.d.ts.map +1 -1
- package/dist/core/resources/status.js +36 -7
- package/dist/core/semantic/contract.d.ts +5 -5
- package/dist/core/servers/index.d.ts +24 -13
- package/dist/core/servers/index.d.ts.map +1 -1
- package/dist/core/servers/index.js +132 -92
- package/dist/core/servers/tunnels.d.ts +3 -5
- package/dist/core/servers/tunnels.d.ts.map +1 -1
- package/dist/core/servers/tunnels.js +3 -9
- package/dist/core/storage/process-log.d.ts +12 -0
- package/dist/core/storage/process-log.d.ts.map +1 -1
- package/dist/core/storage/process-log.js +10 -2
- package/dist/core/supervisor/contract.d.ts +12 -0
- package/dist/core/supervisor/contract.d.ts.map +1 -1
- package/dist/core/supervisor/explanations.d.ts.map +1 -1
- package/dist/core/supervisor/explanations.js +2 -1
- package/dist/core/supervisor/findings.d.ts.map +1 -1
- package/dist/core/supervisor/findings.js +54 -9
- package/dist/core/supervisor/service.d.ts +9 -1
- package/dist/core/supervisor/service.d.ts.map +1 -1
- package/dist/core/supervisor/service.js +59 -29
- package/dist/core/supervisor/services.d.ts.map +1 -1
- package/dist/core/supervisor/services.js +22 -1
- package/dist/core/supervisor/status.d.ts +17 -2
- package/dist/core/supervisor/status.d.ts.map +1 -1
- package/dist/core/supervisor/status.js +70 -12
- package/dist/core/supervisor/timeline.d.ts.map +1 -1
- package/dist/core/supervisor/timeline.js +2 -1
- package/dist/core/work/state.js +7 -7
- package/dist/core/workflow/engine.d.ts.map +1 -1
- package/dist/core/workflow/engine.js +105 -32
- package/dist/core/workflow/failure-evidence.d.ts +16 -0
- package/dist/core/workflow/failure-evidence.d.ts.map +1 -0
- package/dist/core/workflow/failure-evidence.js +114 -0
- package/dist/core/workflow/index.d.ts +1 -1
- package/dist/core/workflow/index.d.ts.map +1 -1
- package/dist/core/workflow/index.js +1 -1
- package/dist/core/workflow/live-session-v3.d.ts +6 -0
- package/dist/core/workflow/live-session-v3.d.ts.map +1 -1
- package/dist/core/workflow/live-session-v3.js +126 -5
- package/dist/core/workflow/proof.d.ts +5 -5
- package/dist/core/workflow/proof.d.ts.map +1 -1
- package/dist/core/workflow/proof.js +115 -20
- package/dist/core/workflow/run-state.d.ts +3 -1
- package/dist/core/workflow/run-state.d.ts.map +1 -1
- package/dist/core/workflow/run-state.js +31 -27
- package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
- package/dist/core/workflow/spawn-claude.js +19 -0
- package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
- package/dist/core/workflow/spawn-codex.js +13 -0
- package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
- package/dist/core/workflow/spawn-cursor.js +19 -0
- package/dist/core/workflow/spawn-opencode.d.ts.map +1 -1
- package/dist/core/workflow/spawn-opencode.js +14 -0
- package/dist/core/workflow/types.d.ts +64 -17
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/index.d.ts +0 -1
- package/dist/core/workflow/workspaces/index.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/index.js +0 -1
- package/dist/core/workflow/workspaces/leases.d.ts +3 -1
- package/dist/core/workflow/workspaces/leases.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/leases.js +30 -16
- package/dist/core/workflow/workspaces/local-git.d.ts +6 -2
- package/dist/core/workflow/workspaces/local-git.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/local-git.js +270 -6
- package/dist/core/workflow/workspaces/ownership.d.ts +11 -0
- package/dist/core/workflow/workspaces/ownership.d.ts.map +1 -0
- package/dist/core/workflow/workspaces/ownership.js +194 -0
- package/dist/core/workflow/workspaces/reclaim.d.ts +35 -0
- package/dist/core/workflow/workspaces/reclaim.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/reclaim.js +14 -0
- package/dist/core/workflow/workspaces/recovery.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/recovery.js +21 -13
- package/dist/core/workflow/workspaces/state.d.ts +1 -0
- package/dist/core/workflow/workspaces/state.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/state.js +21 -1
- package/dist/lib/browser/windows-chrome.d.ts +100 -12
- package/dist/lib/browser/windows-chrome.d.ts.map +1 -1
- package/dist/lib/browser/windows-chrome.js +262 -73
- package/dist/lib/docs-links.d.ts.map +1 -1
- package/dist/lib/docs-links.js +45 -2
- package/dist/lib/guarded-remove.d.ts +7 -0
- package/dist/lib/guarded-remove.d.ts.map +1 -1
- package/dist/lib/guarded-remove.js +106 -12
- package/dist/lib/identities/assume.d.ts.map +1 -1
- package/dist/lib/identities/assume.js +4 -0
- package/dist/lib/tunnel/allowlist.d.ts +44 -0
- package/dist/lib/tunnel/allowlist.d.ts.map +1 -0
- package/dist/lib/tunnel/allowlist.js +204 -0
- package/dist/lib/tunnel/current-address.d.ts +41 -0
- package/dist/lib/tunnel/current-address.d.ts.map +1 -0
- package/dist/lib/tunnel/current-address.js +123 -0
- package/dist/lib/tunnel/gate.js +13 -6
- package/dist/lib/tunnel/processes.d.ts +39 -0
- package/dist/lib/tunnel/processes.d.ts.map +1 -0
- package/dist/lib/tunnel/processes.js +121 -0
- package/dist/lib/tunnel/state.d.ts +28 -3
- package/dist/lib/tunnel/state.d.ts.map +1 -1
- package/dist/lib/tunnel/state.js +98 -27
- package/dist/lib/zip/index.d.ts +41 -0
- package/dist/lib/zip/index.d.ts.map +1 -0
- package/dist/lib/zip/index.js +398 -0
- package/package.json +8 -1
- package/src/commander.ts +32 -0
- package/src/commands/agents.ts +151 -74
- package/src/commands/browse.ts +50 -14
- package/src/commands/diagnostics.ts +10 -0
- package/src/commands/doctor.ts +26 -7
- package/src/commands/governor.ts +23 -10
- package/src/commands/inbox.ts +52 -2
- package/src/commands/rm.ts +43 -4
- package/src/commands/servers.ts +16 -4
- package/src/commands/supervisor.ts +7 -2
- package/src/commands/toc.ts +14 -6
- package/src/commands/tunnel.ts +394 -116
- package/src/commands/web-fetch.ts +30 -19
- package/src/commands/web.ts +9 -3
- package/src/commands/workflow.ts +30 -30
- package/src/core/agents/live-authority-v3.ts +17 -0
- package/src/core/agents/live-lifecycle-v3.ts +52 -14
- package/src/core/agents/mailbox.ts +26 -17
- package/src/core/agents/render/prompt-context.ts +35 -20
- package/src/core/agents/render/session-context.ts +92 -48
- package/src/core/agents/rules/stop-hook.ts +3 -3
- package/src/core/agents/state/heartbeat-writer.ts +29 -12
- package/src/core/agents/state/live-coordination-writer.ts +29 -0
- package/src/core/agents/state/names.ts +117 -6
- package/src/core/agents/state/pidmap.ts +13 -0
- package/src/core/agents/state/proc-start.ts +57 -0
- package/src/core/agents/state/stale-sweep.ts +247 -82
- package/src/core/agents/state/unchanged-file.ts +456 -0
- package/src/core/context/index.ts +446 -136
- package/src/core/context/observation.ts +29 -0
- package/src/core/diagnostics/bundle.ts +23 -18
- package/src/core/diagnostics/path-identity.ts +15 -0
- package/src/core/diagnostics/sanitize.ts +4 -3
- package/src/core/events/v3/authority-outbox.ts +56 -0
- package/src/core/events/v3/generation-custody.ts +59 -0
- package/src/core/events/v3/producers/coordination-recorder.ts +15 -0
- package/src/core/events/v3/producers/recorder.ts +94 -68
- package/src/core/events/v3/support-storage/pack-reader.ts +45 -19
- package/src/core/governor/planning.ts +20 -8
- package/src/core/governor/service-read.ts +128 -20
- package/src/core/governor/service.ts +119 -42
- package/src/core/hooks/cli.ts +139 -22
- package/src/core/hooks/fork-lineage.ts +82 -0
- package/src/core/hooks/resolve/transcript.ts +88 -0
- package/src/core/hooks/shell-rm.ts +103 -0
- package/src/core/hooks/shell-tokens.ts +96 -0
- package/src/core/hooks/shell-waiter.ts +4 -91
- package/src/core/inbox/contract.ts +2 -1
- package/src/core/inbox/index.ts +2 -0
- package/src/core/inbox/peer-messages.ts +94 -0
- package/src/core/inbox/service.ts +88 -22
- package/src/core/inbox/targets.ts +148 -0
- package/src/core/inbox/watcher.ts +8 -9
- package/src/core/journal/index.ts +19 -2
- package/src/core/resources/contract.ts +4 -0
- package/src/core/resources/service-status.ts +35 -12
- package/src/core/resources/service.ts +71 -24
- package/src/core/resources/status.ts +57 -8
- package/src/core/servers/index.ts +177 -91
- package/src/core/servers/tunnels.ts +3 -11
- package/src/core/storage/process-log.ts +17 -2
- package/src/core/supervisor/contract.ts +13 -0
- package/src/core/supervisor/explanations.ts +4 -1
- package/src/core/supervisor/findings.ts +68 -10
- package/src/core/supervisor/service.ts +88 -26
- package/src/core/supervisor/services.ts +26 -8
- package/src/core/supervisor/status.ts +89 -12
- package/src/core/supervisor/timeline.ts +2 -1
- package/src/core/work/state.ts +7 -7
- package/src/core/workflow/engine.ts +131 -49
- package/src/core/workflow/failure-evidence.ts +141 -0
- package/src/core/workflow/index.ts +0 -1
- package/src/core/workflow/live-session-v3.ts +146 -5
- package/src/core/workflow/proof.ts +134 -26
- package/src/core/workflow/run-state.ts +31 -29
- package/src/core/workflow/spawn-claude.ts +18 -0
- package/src/core/workflow/spawn-codex.ts +13 -0
- package/src/core/workflow/spawn-cursor.ts +18 -0
- package/src/core/workflow/spawn-opencode.ts +14 -0
- package/src/core/workflow/types.ts +51 -17
- package/src/core/workflow/workspaces/index.ts +0 -1
- package/src/core/workflow/workspaces/leases.ts +48 -17
- package/src/core/workflow/workspaces/local-git.ts +315 -5
- package/src/core/workflow/workspaces/ownership.ts +232 -0
- package/src/core/workflow/workspaces/reclaim.ts +58 -0
- package/src/core/workflow/workspaces/recovery.ts +25 -13
- package/src/core/workflow/workspaces/state.ts +20 -0
- package/src/lib/browser/windows-chrome.ts +346 -86
- package/src/lib/docs-links.ts +49 -2
- package/src/lib/guarded-remove.ts +132 -14
- package/src/lib/identities/assume.ts +4 -0
- package/src/lib/tunnel/allowlist.ts +208 -0
- package/src/lib/tunnel/current-address.ts +158 -0
- package/src/lib/tunnel/gate.ts +16 -8
- package/src/lib/tunnel/processes.ts +149 -0
- package/src/lib/tunnel/state.ts +123 -27
- package/src/lib/zip/index.ts +483 -0
package/src/commands/agents.ts
CHANGED
|
@@ -50,13 +50,6 @@ import {
|
|
|
50
50
|
LiveCoordinationAuthorityV3Error,
|
|
51
51
|
reopenLiveCoordinationGenerationV3,
|
|
52
52
|
} from "../core/agents/live-authority-v3.ts";
|
|
53
|
-
import {
|
|
54
|
-
isAddressableName,
|
|
55
|
-
MailboxCapacityError,
|
|
56
|
-
type QueueResult,
|
|
57
|
-
queueMailboxMessage,
|
|
58
|
-
suggestNames,
|
|
59
|
-
} from "../core/agents/mailbox.ts";
|
|
60
53
|
import { qaSignalStatusRow } from "../core/agents/qa-signal.ts";
|
|
61
54
|
import { reconcileCoordinationV3 } from "../core/agents/reconcile-coordination-v3.ts";
|
|
62
55
|
import {
|
|
@@ -84,6 +77,7 @@ import {
|
|
|
84
77
|
readLiveCoordinationRow,
|
|
85
78
|
readLiveCoordinationRows,
|
|
86
79
|
} from "../core/agents/state/live-coordination-view.ts";
|
|
80
|
+
import { forkRelationLabels, resolveForkAncestry } from "../core/agents/state/names.ts";
|
|
87
81
|
import {
|
|
88
82
|
type AgentActivity,
|
|
89
83
|
foldSessionState,
|
|
@@ -107,6 +101,7 @@ import {
|
|
|
107
101
|
projectCoordinationViewV3,
|
|
108
102
|
readCoordinationViewV3,
|
|
109
103
|
} from "../core/events/v3/coordination-view.ts";
|
|
104
|
+
import { nativeInstanceIdV3 } from "../core/events/v3/live-route-observer.ts";
|
|
110
105
|
import { liveInstanceIdV3 } from "../core/events/v3/live-routing.ts";
|
|
111
106
|
import {
|
|
112
107
|
countSummarizedSinceV3,
|
|
@@ -128,9 +123,12 @@ import {
|
|
|
128
123
|
readRuntimeContextTelemetry,
|
|
129
124
|
readRuntimeContextUsage,
|
|
130
125
|
} from "../core/hooks/adapter/runtime-telemetry.ts";
|
|
126
|
+
import { HarneryInboxCapacityError } from "../core/inbox/contract.ts";
|
|
127
|
+
import { peerInbox, sendPeerMessage } from "../core/inbox/peer-messages.ts";
|
|
131
128
|
import { type RemoteMachine, readRemoteMachines } from "../core/presence/index.ts";
|
|
132
129
|
import { formatResourceSummary, readResourceStatus } from "../core/resources/status.ts";
|
|
133
130
|
import { registerContextCommand } from "./context.ts";
|
|
131
|
+
import { registerInboxCommand } from "./inbox.ts";
|
|
134
132
|
|
|
135
133
|
type LiveCoordinationRow = ReturnType<typeof readLiveCoordinationRows>[number];
|
|
136
134
|
|
|
@@ -311,6 +309,11 @@ export function registerAgentsCommand(
|
|
|
311
309
|
);
|
|
312
310
|
|
|
313
311
|
registerContextCommand(cmd, emitParam, programContext);
|
|
312
|
+
registerInboxCommand(cmd, emitParam, peerInbox(monorepoRoot() ?? process.cwd()), () => {
|
|
313
|
+
const owner = resolveOwner();
|
|
314
|
+
if (!owner) throw new Error("recipient instance is required outside an agent session");
|
|
315
|
+
return owner;
|
|
316
|
+
});
|
|
314
317
|
|
|
315
318
|
cmd
|
|
316
319
|
.command("whoami")
|
|
@@ -437,9 +440,15 @@ export function registerAgentsCommand(
|
|
|
437
440
|
"--session-id <id>",
|
|
438
441
|
"Target the heartbeat with this session_id directly, bypassing the ppid walk.",
|
|
439
442
|
)
|
|
440
|
-
.
|
|
441
|
-
|
|
442
|
-
|
|
443
|
+
.option(
|
|
444
|
+
"--force-ancestor",
|
|
445
|
+
"Allow a fork to change its fork ancestor's lifecycle (only on explicit operator instruction)",
|
|
446
|
+
)
|
|
447
|
+
.action(
|
|
448
|
+
(state: string, opts: { reason?: string; sessionId?: string; forceAncestor?: boolean }) => {
|
|
449
|
+
runLifecycle(state, opts);
|
|
450
|
+
},
|
|
451
|
+
);
|
|
443
452
|
|
|
444
453
|
cmd
|
|
445
454
|
.command("end")
|
|
@@ -453,9 +462,20 @@ export function registerAgentsCommand(
|
|
|
453
462
|
"succeeded | failed | cancelled | timed_out | denied | interrupted | unknown",
|
|
454
463
|
"succeeded",
|
|
455
464
|
)
|
|
456
|
-
.
|
|
457
|
-
|
|
458
|
-
|
|
465
|
+
.option(
|
|
466
|
+
"--force-ancestor",
|
|
467
|
+
"Allow a fork to finalize its fork ancestor's session (only on explicit operator instruction)",
|
|
468
|
+
)
|
|
469
|
+
.action(
|
|
470
|
+
(opts: {
|
|
471
|
+
sessionId?: string;
|
|
472
|
+
instanceId?: string;
|
|
473
|
+
outcome: string;
|
|
474
|
+
forceAncestor?: boolean;
|
|
475
|
+
}) => {
|
|
476
|
+
runEndSession(opts);
|
|
477
|
+
},
|
|
478
|
+
);
|
|
459
479
|
|
|
460
480
|
cmd
|
|
461
481
|
.command("reconcile")
|
|
@@ -511,15 +531,22 @@ export function registerAgentsCommand(
|
|
|
511
531
|
cmd
|
|
512
532
|
.command("ping <name> <message...>")
|
|
513
533
|
.description(
|
|
514
|
-
"
|
|
515
|
-
"
|
|
516
|
-
"
|
|
517
|
-
"Only an unknown name is refused.",
|
|
534
|
+
"Queue a durable message for one known task instance by name. Stale or " +
|
|
535
|
+
"done tasks receive it when that same task resumes. Ambiguous names " +
|
|
536
|
+
"require an exact instance; receipts do not confirm attention.",
|
|
518
537
|
)
|
|
538
|
+
.option("--instance-id <id>", "Select the exact known recipient instance")
|
|
539
|
+
.option("--require-live", "Refuse a recipient outside live presence")
|
|
519
540
|
.option("--json", "JSON output")
|
|
520
|
-
.action(
|
|
521
|
-
|
|
522
|
-
|
|
541
|
+
.action(
|
|
542
|
+
(
|
|
543
|
+
name: string,
|
|
544
|
+
message: string[],
|
|
545
|
+
opts: { json?: boolean; instanceId?: string; requireLive?: boolean },
|
|
546
|
+
) => {
|
|
547
|
+
runPing(name, message.join(" "), opts);
|
|
548
|
+
},
|
|
549
|
+
);
|
|
523
550
|
|
|
524
551
|
cmd
|
|
525
552
|
.command("wait <name>")
|
|
@@ -666,7 +693,40 @@ const SESSION_OUTCOMES = new Set([
|
|
|
666
693
|
"unknown",
|
|
667
694
|
]);
|
|
668
695
|
|
|
669
|
-
|
|
696
|
+
/**
|
|
697
|
+
* Refuse when the calling session targets one of its recorded fork ancestors.
|
|
698
|
+
* A fork's transcript is full of its parent's name, task, and claims, so a
|
|
699
|
+
* fork that "cleans up its earlier self" is usually cutting off a live parent
|
|
700
|
+
* (2026-10-07: a fork offered to close its running parent). Returns the
|
|
701
|
+
* refusal message, or null when the call may proceed.
|
|
702
|
+
*/
|
|
703
|
+
function forkAncestorRefusal(
|
|
704
|
+
root: string,
|
|
705
|
+
targetIds: Array<string | undefined>,
|
|
706
|
+
force: boolean | undefined,
|
|
707
|
+
action: string,
|
|
708
|
+
): string | null {
|
|
709
|
+
if (force) return null;
|
|
710
|
+
const ids = new Set(targetIds.filter((id): id is string => !!id));
|
|
711
|
+
if (ids.size === 0) return null;
|
|
712
|
+
const self = resolveOwner() ?? sessionIdentityFromEnv();
|
|
713
|
+
if (!self || ids.has(self)) return null;
|
|
714
|
+
const ancestor = resolveForkAncestry(root, self).find((a) => ids.has(a.instance_id));
|
|
715
|
+
if (!ancestor) return null;
|
|
716
|
+
const label = ancestor.name ? `agent-${ancestor.name}` : ancestor.instance_id;
|
|
717
|
+
return (
|
|
718
|
+
`${label} is this session's fork ancestor: this conversation was branched from it, so its ` +
|
|
719
|
+
`name, task, and files in your context are its own, and it may still be running. Refusing to ` +
|
|
720
|
+
`${action} it. Rerun with --force-ancestor only when the operator explicitly asked for that.`
|
|
721
|
+
);
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
function runEndSession(opts: {
|
|
725
|
+
sessionId?: string;
|
|
726
|
+
instanceId?: string;
|
|
727
|
+
outcome: string;
|
|
728
|
+
forceAncestor?: boolean;
|
|
729
|
+
}) {
|
|
670
730
|
const root = monorepoRoot();
|
|
671
731
|
if (!root) return failCommand("not_in_repo", "not in an agent session");
|
|
672
732
|
if (!SESSION_OUTCOMES.has(opts.outcome)) {
|
|
@@ -698,6 +758,15 @@ function runEndSession(opts: { sessionId?: string; instanceId?: string; outcome:
|
|
|
698
758
|
return failCommand("session_identity_missing", "live V3 generation disappeared");
|
|
699
759
|
}
|
|
700
760
|
const state = record.state;
|
|
761
|
+
if (opts.instanceId || opts.sessionId) {
|
|
762
|
+
const refusal = forkAncestorRefusal(
|
|
763
|
+
root,
|
|
764
|
+
[target, state.session_id, nativeInstanceIdV3(state.instance_id)],
|
|
765
|
+
opts.forceAncestor,
|
|
766
|
+
"finalize",
|
|
767
|
+
);
|
|
768
|
+
if (refusal) return failCommand("session_is_fork_ancestor", refusal);
|
|
769
|
+
}
|
|
701
770
|
if (state.delegations.length > 0) {
|
|
702
771
|
return failCommand(
|
|
703
772
|
"session_work_open",
|
|
@@ -1992,7 +2061,10 @@ function runSetTask(task: string, opts?: { sessionId?: string }): void {
|
|
|
1992
2061
|
});
|
|
1993
2062
|
}
|
|
1994
2063
|
|
|
1995
|
-
function runLifecycle(
|
|
2064
|
+
function runLifecycle(
|
|
2065
|
+
rawState: string,
|
|
2066
|
+
opts: { reason?: string; sessionId?: string; forceAncestor?: boolean },
|
|
2067
|
+
): void {
|
|
1996
2068
|
const state = rawState.trim().toLowerCase();
|
|
1997
2069
|
if (state !== "active" && state !== "blocked" && state !== "done") {
|
|
1998
2070
|
emit.error({
|
|
@@ -2013,6 +2085,19 @@ function runLifecycle(rawState: string, opts: { reason?: string; sessionId?: str
|
|
|
2013
2085
|
return;
|
|
2014
2086
|
}
|
|
2015
2087
|
if (!opts.sessionId) ensureAdapterSession(root);
|
|
2088
|
+
if (opts.sessionId) {
|
|
2089
|
+
const refusal = forkAncestorRefusal(
|
|
2090
|
+
root,
|
|
2091
|
+
[opts.sessionId],
|
|
2092
|
+
opts.forceAncestor,
|
|
2093
|
+
"change the lifecycle of",
|
|
2094
|
+
);
|
|
2095
|
+
if (refusal) {
|
|
2096
|
+
emit.error({ code: "session_is_fork_ancestor", message: refusal });
|
|
2097
|
+
process.exitCode = 1;
|
|
2098
|
+
return;
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2016
2101
|
const myOwner = opts.sessionId ?? resolveOwner() ?? sessionIdentityFromEnv();
|
|
2017
2102
|
if (!myOwner) {
|
|
2018
2103
|
emit.error(
|
|
@@ -2390,8 +2475,15 @@ function runStatus(opts: {
|
|
|
2390
2475
|
|
|
2391
2476
|
const { livePeers, stale: peersStale } = collectStatusPeerHealth(root, myOwner);
|
|
2392
2477
|
|
|
2393
|
-
//
|
|
2478
|
+
// Recorded fork relations label the parent and forks so a fork cannot read
|
|
2479
|
+
// its live parent as its own earlier session; they sort first.
|
|
2480
|
+
const relations = forkRelationLabels(root, myOwner, new Set(livePeers.map((p) => p.instance_id)));
|
|
2481
|
+
// Sort: related sessions, then file-holders (by file count desc), then idle
|
|
2482
|
+
// peers by recency.
|
|
2394
2483
|
livePeers.sort((a, b) => {
|
|
2484
|
+
const ar = relations.has(a.instance_id) ? 1 : 0;
|
|
2485
|
+
const br = relations.has(b.instance_id) ? 1 : 0;
|
|
2486
|
+
if (ar !== br) return br - ar;
|
|
2395
2487
|
const af = a.files_touched?.length ?? 0;
|
|
2396
2488
|
const bf = b.files_touched?.length ?? 0;
|
|
2397
2489
|
if (af !== bf) return bf - af;
|
|
@@ -2406,7 +2498,7 @@ function runStatus(opts: {
|
|
|
2406
2498
|
);
|
|
2407
2499
|
// Cross-machine presence (ADR 0016): sessions on other machines, advisory.
|
|
2408
2500
|
const remoteMachines = readRemoteMachines(root);
|
|
2409
|
-
const peersStr = formatPeers(livePeers, 4, peersStale, remoteMachines);
|
|
2501
|
+
const peersStr = formatPeers(livePeers, 4, peersStale, remoteMachines, relations);
|
|
2410
2502
|
|
|
2411
2503
|
const ctxUsage = readContextUsage(hb.native_session_id ?? hb.session_id, hb.platform);
|
|
2412
2504
|
let ctxStr: string;
|
|
@@ -2600,13 +2692,15 @@ function formatPeers(
|
|
|
2600
2692
|
cap: number,
|
|
2601
2693
|
staleCount: number,
|
|
2602
2694
|
remoteMachines: RemoteMachine[] = [],
|
|
2695
|
+
relations: Map<string, string> = new Map(),
|
|
2603
2696
|
): string {
|
|
2604
2697
|
if (peers.length === 0 && staleCount === 0 && remoteMachines.length === 0) return "none";
|
|
2605
2698
|
const labels = peers.map((p) => {
|
|
2606
2699
|
const name = p.name || "unnamed";
|
|
2607
2700
|
const plat = formatPlatformLabel(p.platform);
|
|
2608
2701
|
const files = p.files_touched?.length ?? 0;
|
|
2609
|
-
const
|
|
2702
|
+
const relation = relations.get(p.instance_id);
|
|
2703
|
+
const base = relation ? `${name} (${plat}, ${relation})` : `${name} (${plat})`;
|
|
2610
2704
|
return files > 0 ? `${base}, ${files} files` : base;
|
|
2611
2705
|
});
|
|
2612
2706
|
let main: string;
|
|
@@ -4784,7 +4878,11 @@ function formatDuration(ms: number): string {
|
|
|
4784
4878
|
return h > 0 ? `${d}d${h}h` : `${d}d`;
|
|
4785
4879
|
}
|
|
4786
4880
|
|
|
4787
|
-
function runPing(
|
|
4881
|
+
function runPing(
|
|
4882
|
+
name: string,
|
|
4883
|
+
message: string,
|
|
4884
|
+
opts: { json?: boolean; instanceId?: string; requireLive?: boolean },
|
|
4885
|
+
): void {
|
|
4788
4886
|
if (!message || message.trim().length === 0) {
|
|
4789
4887
|
emit.error({ code: "empty_message", message: "message is required (and non-empty)" });
|
|
4790
4888
|
process.exit(1);
|
|
@@ -4805,52 +4903,33 @@ function runPing(name: string, message: string, opts: { json?: boolean }): void
|
|
|
4805
4903
|
);
|
|
4806
4904
|
process.exit(1);
|
|
4807
4905
|
}
|
|
4808
|
-
const coordRoot = monorepoRoot();
|
|
4809
|
-
const peerOwner = resolveOwnerByName(name);
|
|
4810
|
-
|
|
4811
|
-
// A name with no live session is the common case, not an error: the agent it
|
|
4812
|
-
// refers to may have ended minutes ago and may come back under the same name.
|
|
4813
|
-
// Refuse only a name that no agent has ever held, which is a typo — queueing
|
|
4814
|
-
// that would swallow the message silently.
|
|
4815
|
-
if (!peerOwner && coordRoot && !isAddressableName(coordRoot, name)) {
|
|
4816
|
-
const suggestions = suggestNames(coordRoot, name);
|
|
4817
|
-
emit.error({
|
|
4818
|
-
code: "unknown_agent",
|
|
4819
|
-
message:
|
|
4820
|
-
`no agent has ever been named "${name}". ` +
|
|
4821
|
-
(suggestions.length > 0 ? `Did you mean ${suggestions.join(", ")}? ` : "") +
|
|
4822
|
-
`Run \`${resolveBinName()} agents list\` for live sessions.`,
|
|
4823
|
-
});
|
|
4824
|
-
process.exit(1);
|
|
4825
|
-
}
|
|
4826
|
-
|
|
4906
|
+
const coordRoot = monorepoRoot() ?? process.cwd();
|
|
4827
4907
|
const myHb = readCurrentCoordinationRow(myOwner);
|
|
4828
4908
|
const fromName = myHb?.name ?? "anonymous";
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
// A live recipient gets the journal entry immediately, so a peer reading
|
|
4832
|
-
// their own journal mid-session sees it without waiting for a prompt. A
|
|
4833
|
-
// dormant recipient has no journal to write to yet; the drain writes it into
|
|
4834
|
-
// whichever session picks the message up.
|
|
4835
|
-
const doc = peerOwner ? appendEntry(peerOwner, "handoff", body) : null;
|
|
4836
|
-
|
|
4837
|
-
let queued: QueueResult;
|
|
4909
|
+
let sent: ReturnType<typeof sendPeerMessage>;
|
|
4838
4910
|
try {
|
|
4839
|
-
|
|
4840
|
-
coordRoot
|
|
4841
|
-
|
|
4842
|
-
fromName,
|
|
4843
|
-
|
|
4911
|
+
sent = sendPeerMessage({
|
|
4912
|
+
coordRoot,
|
|
4913
|
+
target: { name, instanceId: opts.instanceId, requireLive: opts.requireLive },
|
|
4914
|
+
senderName: fromName,
|
|
4915
|
+
senderInstanceId: myOwner,
|
|
4844
4916
|
body: message.trim(),
|
|
4845
|
-
journaled: Boolean(peerOwner),
|
|
4846
4917
|
});
|
|
4847
4918
|
} catch (error) {
|
|
4848
|
-
if (error instanceof
|
|
4849
|
-
emit.error({ code: `
|
|
4919
|
+
if (error instanceof HarneryInboxCapacityError) {
|
|
4920
|
+
emit.error({ code: `inbox_${error.reason_code}`, message: error.message });
|
|
4850
4921
|
process.exit(1);
|
|
4851
4922
|
}
|
|
4852
4923
|
throw error;
|
|
4853
4924
|
}
|
|
4925
|
+
if (!sent.ok) {
|
|
4926
|
+
emit.error({
|
|
4927
|
+
code: sent.code,
|
|
4928
|
+
message: sent.message,
|
|
4929
|
+
...(sent.candidates ? { candidates: sent.candidates } : {}),
|
|
4930
|
+
});
|
|
4931
|
+
process.exit(1);
|
|
4932
|
+
}
|
|
4854
4933
|
|
|
4855
4934
|
// Canonical delivery record: sender is the envelope owner, recipient rides
|
|
4856
4935
|
// in data, so read-only observers can render the communication without
|
|
@@ -4862,21 +4941,20 @@ function runPing(name: string, message: string, opts: { json?: boolean }): void
|
|
|
4862
4941
|
observation: {
|
|
4863
4942
|
event_type: "coord.message_observed",
|
|
4864
4943
|
direction: "sent",
|
|
4865
|
-
subject:
|
|
4944
|
+
subject: sent.target.instance_id,
|
|
4945
|
+
message_id: sent.message_id,
|
|
4866
4946
|
body: message.trim(),
|
|
4867
4947
|
},
|
|
4868
4948
|
});
|
|
4869
4949
|
|
|
4870
4950
|
const data = {
|
|
4871
|
-
peer:
|
|
4872
|
-
peer_instance_id:
|
|
4873
|
-
|
|
4874
|
-
message_id:
|
|
4875
|
-
|
|
4951
|
+
peer: sent.target.name,
|
|
4952
|
+
peer_instance_id: sent.target.instance_id,
|
|
4953
|
+
state: sent.state,
|
|
4954
|
+
message_id: sent.message_id,
|
|
4955
|
+
synced: sent.synced,
|
|
4956
|
+
body_bytes: sent.body_bytes,
|
|
4876
4957
|
from: fromName,
|
|
4877
|
-
body,
|
|
4878
|
-
journal_path: doc?.path ?? null,
|
|
4879
|
-
journal_bytes: doc?.bytes ?? null,
|
|
4880
4958
|
};
|
|
4881
4959
|
|
|
4882
4960
|
if (opts.json) {
|
|
@@ -4886,9 +4964,8 @@ function runPing(name: string, message: string, opts: { json?: boolean }): void
|
|
|
4886
4964
|
}
|
|
4887
4965
|
emit.data(data);
|
|
4888
4966
|
emit.text(
|
|
4889
|
-
|
|
4890
|
-
|
|
4891
|
-
: `queued for agent-${data.peer} (no live session; delivered when a session of that name next starts): "${truncate(message.trim(), 80)}"\n`,
|
|
4967
|
+
`${data.state} for agent-${data.peer} (${data.peer_instance_id}); ` +
|
|
4968
|
+
`stored as ${data.message_id}, pending until surfaced to that task.\n`,
|
|
4892
4969
|
);
|
|
4893
4970
|
}
|
|
4894
4971
|
|
package/src/commands/browse.ts
CHANGED
|
@@ -134,6 +134,7 @@ import {
|
|
|
134
134
|
import {
|
|
135
135
|
harvestDomain,
|
|
136
136
|
harvestWindowsChromeCookies,
|
|
137
|
+
keepWindowsChromeSession,
|
|
137
138
|
stopWindowsChromeProfile,
|
|
138
139
|
windowsChromePath,
|
|
139
140
|
windowsLocalAppData,
|
|
@@ -3465,15 +3466,18 @@ function useWindowsPlainLogin(): boolean {
|
|
|
3465
3466
|
|
|
3466
3467
|
/**
|
|
3467
3468
|
* The WSL form of `--login`: the person signs in through the Windows
|
|
3468
|
-
* Chrome on a dedicated Windows profile with nothing attached.
|
|
3469
|
-
*
|
|
3470
|
-
*
|
|
3469
|
+
* Chrome on a dedicated Windows profile with nothing attached. Before the
|
|
3470
|
+
* window opens, the profile is set to continue where it left off, so Chrome
|
|
3471
|
+
* keeps browser-session cookies when the window closes. When the window
|
|
3472
|
+
* closes, the profile's cookies for the sign-in site are read and merged
|
|
3473
|
+
* into the shared cookie store, which later `browse` runs attach, and the
|
|
3474
|
+
* last page open on that site is reported so an agent can resume from it.
|
|
3471
3475
|
*/
|
|
3472
3476
|
async function runWindowsPlainLogin(
|
|
3473
3477
|
url: string,
|
|
3474
3478
|
storePath: string,
|
|
3475
3479
|
loginCloseFile: string | null,
|
|
3476
|
-
emit:
|
|
3480
|
+
emit: Pick<EmitContext, "log" | "data">,
|
|
3477
3481
|
): Promise<void> {
|
|
3478
3482
|
const chromeLinux = windowsChromePath();
|
|
3479
3483
|
const localAppData = windowsLocalAppData();
|
|
@@ -3484,6 +3488,13 @@ async function runWindowsPlainLogin(
|
|
|
3484
3488
|
}
|
|
3485
3489
|
const chromeWindows = wslPathToWindows(chromeLinux);
|
|
3486
3490
|
const profileWindows = windowsPlainProfile(localAppData);
|
|
3491
|
+
const keep = keepWindowsChromeSession({ chromeWindows, profileWindows });
|
|
3492
|
+
if (!keep.kept) {
|
|
3493
|
+
emit.log(
|
|
3494
|
+
"[--login] Could not set the sign-in profile to keep browser-session cookies. A sign-in that relies on them survives only if you end it from the terminal instead of closing the window.",
|
|
3495
|
+
"warn",
|
|
3496
|
+
);
|
|
3497
|
+
}
|
|
3487
3498
|
const child = spawn(
|
|
3488
3499
|
chromeLinux,
|
|
3489
3500
|
[`--user-data-dir=${profileWindows}`, "--no-first-run", "--no-default-browser-check", url],
|
|
@@ -3507,24 +3518,49 @@ async function runWindowsPlainLogin(
|
|
|
3507
3518
|
);
|
|
3508
3519
|
await Promise.race(waits);
|
|
3509
3520
|
lifecycleAbort.abort();
|
|
3510
|
-
stopWindowsChromeProfile(profileWindows);
|
|
3521
|
+
const stop = stopWindowsChromeProfile(profileWindows);
|
|
3522
|
+
if (stop.forced > 0) {
|
|
3523
|
+
emit.log(
|
|
3524
|
+
"[--login] Chrome did not close when asked and was stopped. Cookies set in its last 30 seconds may be missing; run the sign-in again if later pages are signed out.",
|
|
3525
|
+
"warn",
|
|
3526
|
+
);
|
|
3527
|
+
}
|
|
3511
3528
|
await Promise.race([exited, new Promise((r) => setTimeout(r, 5000))]);
|
|
3512
3529
|
const domain = harvestDomain(url);
|
|
3513
|
-
const cookies = harvestWindowsChromeCookies({ chromeWindows, profileWindows, domain });
|
|
3530
|
+
const { cookies, pages } = harvestWindowsChromeCookies({ chromeWindows, profileWindows, domain });
|
|
3531
|
+
const lastPage = pages[0] ?? null;
|
|
3532
|
+
const names = [...new Set(cookies.map((cookie) => cookie.name))].sort();
|
|
3533
|
+
const sessionCookies = cookies.filter((cookie) => cookie.expires <= 0).length;
|
|
3514
3534
|
if (cookies.length === 0) {
|
|
3515
3535
|
emit.log(
|
|
3516
3536
|
`[--login] No ${domain} cookies were found in the Windows profile; the sign-in may not have completed.`,
|
|
3517
3537
|
"warn",
|
|
3518
3538
|
);
|
|
3519
|
-
|
|
3539
|
+
} else {
|
|
3540
|
+
const jar = new CookieJar({ path: storePath, source: "harn-browse" });
|
|
3541
|
+
jar.save(mergeCookies(jar.load(), cookies));
|
|
3542
|
+
emit.log(
|
|
3543
|
+
`[--login] Copied ${cookies.length} ${domain} cookies (${sessionCookies} browser-session) into ${storePath}: ${names.join(", ")}.`,
|
|
3544
|
+
"info",
|
|
3545
|
+
);
|
|
3520
3546
|
}
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3547
|
+
if (lastPage) {
|
|
3548
|
+
emit.log(
|
|
3549
|
+
`[--login] Last page: ${lastPage} (the last page you had open on ${domain}; open it to continue where the sign-in left off, since its address may carry a sign-in token that cookies alone do not replace).`,
|
|
3550
|
+
"info",
|
|
3551
|
+
);
|
|
3552
|
+
}
|
|
3553
|
+
emit.data({
|
|
3554
|
+
login: {
|
|
3555
|
+
domain,
|
|
3556
|
+
store: storePath,
|
|
3557
|
+
cookies: cookies.length,
|
|
3558
|
+
sessionCookies,
|
|
3559
|
+
names,
|
|
3560
|
+
lastPage,
|
|
3561
|
+
pages,
|
|
3562
|
+
},
|
|
3563
|
+
});
|
|
3528
3564
|
}
|
|
3529
3565
|
|
|
3530
3566
|
async function waitForLoginCloseFile(path: string, signal?: AbortSignal): Promise<void> {
|
|
@@ -50,6 +50,7 @@ export function registerDiagnosticsCommand(
|
|
|
50
50
|
findings,
|
|
51
51
|
bundles,
|
|
52
52
|
capability,
|
|
53
|
+
supervisor: readSupervisorStatus(repoRoot),
|
|
53
54
|
total_findings: findings.length,
|
|
54
55
|
total_bundles: bundles.length,
|
|
55
56
|
});
|
|
@@ -68,6 +69,7 @@ export function registerDiagnosticsCommand(
|
|
|
68
69
|
schema_version: DIAGNOSTIC_COMMAND_SCHEMA_VERSION,
|
|
69
70
|
kind: "diagnostic_finding",
|
|
70
71
|
finding,
|
|
72
|
+
supervisor: readSupervisorStatus(repoRoot),
|
|
71
73
|
timeline:
|
|
72
74
|
readSupervisorTimeline(repoRoot, finding.id) ?? buildSupervisorTimeline(finding),
|
|
73
75
|
explanation:
|
|
@@ -118,6 +120,7 @@ export function registerDiagnosticsCommand(
|
|
|
118
120
|
schema_version: DIAGNOSTIC_COMMAND_SCHEMA_VERSION,
|
|
119
121
|
kind: "diagnostic_advice",
|
|
120
122
|
source: { mode: "live" },
|
|
123
|
+
supervisor: readSupervisorStatus(repoRoot),
|
|
121
124
|
advice: buildDiagnosticAdvice({
|
|
122
125
|
assessment: published.assessment,
|
|
123
126
|
priorHysteresis: published.prior_hysteresis,
|
|
@@ -204,6 +207,13 @@ function liveFindingsCapability(repoRoot: string, reportPresent: boolean): Super
|
|
|
204
207
|
}
|
|
205
208
|
const status = readSupervisorStatus(repoRoot);
|
|
206
209
|
if (status.running) return { source_kind: "supervisor.findings", state: "supported" };
|
|
210
|
+
if (status.liveness === "unknown")
|
|
211
|
+
return {
|
|
212
|
+
source_kind: "supervisor.findings",
|
|
213
|
+
state: "error",
|
|
214
|
+
reason_code: "supervisor_generation_unknown",
|
|
215
|
+
detail: status.build_status,
|
|
216
|
+
};
|
|
207
217
|
return {
|
|
208
218
|
source_kind: "supervisor.findings",
|
|
209
219
|
state: "expired",
|
package/src/commands/doctor.ts
CHANGED
|
@@ -421,15 +421,34 @@ function checkWorkflowAdapter(adapter: AdapterName): { check: Check; installed:
|
|
|
421
421
|
}
|
|
422
422
|
|
|
423
423
|
function checkNode(): Check {
|
|
424
|
-
|
|
424
|
+
return nodeCheck(whichVersion("node"), process.versions);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Report the `node` on PATH, which is what Node-based tools run. Under Bun,
|
|
429
|
+
* `process.versions.node` is the Node version Bun emulates, not an installed
|
|
430
|
+
* Node, so it is used only when doctor itself runs on Node and PATH has none.
|
|
431
|
+
*/
|
|
432
|
+
export function nodeCheck(
|
|
433
|
+
onPath: { ok: boolean; out: string },
|
|
434
|
+
runtime: { node: string; bun?: string },
|
|
435
|
+
): Check {
|
|
436
|
+
const hint = "https://nodejs.org/en/download";
|
|
437
|
+
let v = onPath.ok ? onPath.out.replace(/^v/, "") : "";
|
|
438
|
+
if (!v) {
|
|
439
|
+
if (runtime.bun) {
|
|
440
|
+
return {
|
|
441
|
+
name: "node",
|
|
442
|
+
severity: "warn",
|
|
443
|
+
detail: "not on PATH (Bun runs harnery; Node-based tools need it)",
|
|
444
|
+
hint,
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
v = runtime.node;
|
|
448
|
+
}
|
|
425
449
|
const major = Number.parseInt(v.split(".")[0], 10);
|
|
426
450
|
if (Number.isNaN(major) || major < 20) {
|
|
427
|
-
return {
|
|
428
|
-
name: "node",
|
|
429
|
-
severity: "fail",
|
|
430
|
-
detail: `${v} (need ≥ 20)`,
|
|
431
|
-
hint: "https://nodejs.org/en/download",
|
|
432
|
-
};
|
|
451
|
+
return { name: "node", severity: "fail", detail: `${v} (need ≥ 20)`, hint };
|
|
433
452
|
}
|
|
434
453
|
return { name: "node", severity: "ok", detail: v };
|
|
435
454
|
}
|
package/src/commands/governor.ts
CHANGED
|
@@ -385,12 +385,13 @@ function registerServiceCommand(
|
|
|
385
385
|
.action(async (opts: { json?: boolean }) => {
|
|
386
386
|
await withGovernorRootAsync(emit, async (coordRoot) => {
|
|
387
387
|
let status = requestGovernorServiceStop(coordRoot);
|
|
388
|
+
const signal = { signal_sent: status.signal_sent, signal_verdict: status.signal_verdict };
|
|
388
389
|
const deadline = Date.now() + 5_000;
|
|
389
390
|
while (status.running && Date.now() < deadline) {
|
|
390
391
|
await delay(50);
|
|
391
392
|
status = readGovernorServiceStatus(coordRoot);
|
|
392
393
|
}
|
|
393
|
-
emitServiceStatus(status, opts.json, emit);
|
|
394
|
+
emitServiceStatus({ ...status, ...signal }, opts.json, emit);
|
|
394
395
|
});
|
|
395
396
|
});
|
|
396
397
|
|
|
@@ -534,19 +535,31 @@ function emitServiceStatus(
|
|
|
534
535
|
emit.data(status);
|
|
535
536
|
return;
|
|
536
537
|
}
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
538
|
+
const state =
|
|
539
|
+
status.liveness === "unknown"
|
|
540
|
+
? "unknown"
|
|
541
|
+
: !status.config && !status.record
|
|
542
|
+
? "unconfigured"
|
|
543
|
+
: status.running
|
|
544
|
+
? (status.record?.state ?? "running")
|
|
545
|
+
: status.stale
|
|
546
|
+
? "stale"
|
|
547
|
+
: "stopped";
|
|
546
548
|
const lines = [
|
|
547
549
|
`governor service: ${state}`,
|
|
550
|
+
`generation: ${status.generation}`,
|
|
551
|
+
`liveness: ${status.liveness}`,
|
|
552
|
+
`running build: ${status.running_build ? `${status.running_build.version} (${status.running_build.revision})` : "unknown"}`,
|
|
553
|
+
`installed build: ${status.installed_build.version} (${status.installed_build.revision})`,
|
|
554
|
+
`build: ${status.build_status}`,
|
|
548
555
|
`goals: ${status.config?.goal_ids.join(", ") || "none"}`,
|
|
549
556
|
];
|
|
557
|
+
if (status.signal_sent !== undefined) {
|
|
558
|
+
lines.push(`signal sent: ${status.signal_sent ? "yes" : "no"} (${status.signal_verdict})`);
|
|
559
|
+
if (!status.signal_sent && status.record?.state !== "stopped") {
|
|
560
|
+
lines.push("The durable stop request remains for the service to read.");
|
|
561
|
+
}
|
|
562
|
+
}
|
|
550
563
|
if (status.record) {
|
|
551
564
|
lines.push(`pid: ${status.record.pid} on ${status.record.host}`);
|
|
552
565
|
lines.push(`heartbeat: ${status.record.heartbeat_at}`);
|