harnery 0.43.0 → 0.45.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.js +1 -0
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +41 -48
- 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/files.d.ts +2 -2
- package/dist/commands/files.d.ts.map +1 -1
- package/dist/commands/files.js +7 -4
- 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 +26 -17
- 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 +15 -0
- package/dist/commands/tunnel.d.ts.map +1 -1
- package/dist/commands/tunnel.js +453 -153
- package/dist/commands/turn-rollback.d.ts +5 -0
- package/dist/commands/turn-rollback.d.ts.map +1 -0
- package/dist/commands/turn-rollback.js +76 -0
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +68 -30
- package/dist/core/agents/coord-client.d.ts.map +1 -1
- package/dist/core/agents/coord-client.js +18 -0
- 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 +8 -12
- 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 +7 -30
- 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/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 -1
- package/dist/core/agents/state/proc-start.d.ts.map +1 -1
- package/dist/core/agents/state/proc-start.js +46 -1
- package/dist/core/agents/state/process-handle.d.ts +26 -0
- package/dist/core/agents/state/process-handle.d.ts.map +1 -0
- package/dist/core/agents/state/process-handle.js +121 -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/artifacts/delivery-card.d.ts.map +1 -1
- package/dist/core/artifacts/delivery-card.js +2 -1
- 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/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 +33 -5
- 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 +7 -13
- package/dist/core/resources/service.d.ts.map +1 -1
- package/dist/core/resources/service.js +34 -91
- 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/service.d.ts.map +1 -1
- package/dist/core/semantic/service.js +2 -9
- package/dist/core/servers/index.d.ts +34 -22
- package/dist/core/servers/index.d.ts.map +1 -1
- package/dist/core/servers/index.js +297 -144
- package/dist/core/servers/tunnels.d.ts +7 -7
- package/dist/core/servers/tunnels.d.ts.map +1 -1
- package/dist/core/servers/tunnels.js +47 -10
- 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 +7 -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/turn-rollback/content.d.ts +53 -0
- package/dist/core/turn-rollback/content.d.ts.map +1 -0
- package/dist/core/turn-rollback/content.js +396 -0
- package/dist/core/turn-rollback/index.d.ts +72 -0
- package/dist/core/turn-rollback/index.d.ts.map +1 -0
- package/dist/core/turn-rollback/index.js +443 -0
- 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 +5 -3
- package/dist/core/workflow/index.d.ts.map +1 -1
- package/dist/core/workflow/index.js +3 -2
- 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/recovery-audit.d.ts +30 -0
- package/dist/core/workflow/recovery-audit.d.ts.map +1 -0
- package/dist/core/workflow/recovery-audit.js +116 -0
- package/dist/core/workflow/run-state.d.ts +20 -1
- package/dist/core/workflow/run-state.d.ts.map +1 -1
- package/dist/core/workflow/run-state.js +141 -32
- 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 +328 -8
- 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 +40 -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 +2 -0
- package/dist/core/workflow/workspaces/recovery.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/recovery.js +42 -10
- 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/state.d.ts +13 -6
- package/dist/lib/tunnel/state.d.ts.map +1 -1
- package/dist/lib/tunnel/state.js +58 -12
- 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 +12 -1
- package/src/commander.ts +10 -0
- package/src/commands/agents.ts +54 -63
- package/src/commands/browse.ts +50 -14
- package/src/commands/diagnostics.ts +10 -0
- package/src/commands/doctor.ts +26 -7
- package/src/commands/files.ts +13 -5
- 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 +27 -26
- package/src/commands/supervisor.ts +7 -2
- package/src/commands/toc.ts +14 -6
- package/src/commands/tunnel.ts +533 -166
- package/src/commands/turn-rollback.ts +111 -0
- package/src/commands/workflow.ts +89 -30
- package/src/core/agents/coord-client.ts +20 -0
- 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 +10 -12
- package/src/core/agents/render/session-context.ts +9 -32
- 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/pidmap.ts +13 -0
- package/src/core/agents/state/proc-start.ts +62 -2
- package/src/core/agents/state/process-handle.ts +165 -0
- package/src/core/agents/state/stale-sweep.ts +247 -82
- package/src/core/agents/state/unchanged-file.ts +456 -0
- package/src/core/artifacts/delivery-card.ts +7 -1
- 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/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 +33 -5
- 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 +57 -95
- package/src/core/resources/status.ts +57 -8
- package/src/core/semantic/service.ts +2 -8
- package/src/core/servers/index.ts +376 -141
- package/src/core/servers/tunnels.ts +57 -13
- 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 +87 -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/turn-rollback/content.ts +499 -0
- package/src/core/turn-rollback/index.ts +580 -0
- 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 +12 -2
- package/src/core/workflow/live-session-v3.ts +146 -5
- package/src/core/workflow/proof.ts +134 -26
- package/src/core/workflow/recovery-audit.ts +181 -0
- package/src/core/workflow/run-state.ts +165 -34
- 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 +388 -7
- package/src/core/workflow/workspaces/ownership.ts +232 -0
- package/src/core/workflow/workspaces/reclaim.ts +62 -0
- package/src/core/workflow/workspaces/recovery.ts +45 -10
- 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/state.ts +70 -15
- package/src/lib/zip/index.ts +483 -0
|
@@ -12,9 +12,8 @@ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
|
12
12
|
import { join } from "node:path";
|
|
13
13
|
import { resolveBinName, resolveHooksSetupHint } from "../../config.ts";
|
|
14
14
|
import { harneryVersion, loadAdapterWiring } from "../../hooks/adapter/wiring.ts";
|
|
15
|
-
import {
|
|
15
|
+
import { formatPeerInbox, peerInbox } from "../../inbox/peer-messages.ts";
|
|
16
16
|
import { readRemoteMachines } from "../../presence/index.ts";
|
|
17
|
-
import { drainMailbox, formatMailboxDelivery, type MailboxMessageV1 } from "../mailbox.ts";
|
|
18
17
|
import { readLiveCoordinationRows } from "../state/live-coordination-view.ts";
|
|
19
18
|
import { forkRelationLabels, readForkState } from "../state/names.ts";
|
|
20
19
|
import type { AgentActivity, TaskState } from "../state/session-state.ts";
|
|
@@ -42,6 +41,8 @@ export interface RenderOpts {
|
|
|
42
41
|
/** Adapter label rendered in parens after the self-name (e.g. "Cursor", "Codex").
|
|
43
42
|
* Claude Code omits it. */
|
|
44
43
|
platformLabel?: string;
|
|
44
|
+
/** The emitter acknowledges exactly these IDs only after output succeeds. */
|
|
45
|
+
onInboxPrepared?: (messageIds: readonly string[]) => void;
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
/**
|
|
@@ -92,15 +93,12 @@ export function renderSessionContext(opts: RenderOpts): string {
|
|
|
92
93
|
);
|
|
93
94
|
}
|
|
94
95
|
|
|
95
|
-
// 3.
|
|
96
|
-
//
|
|
97
|
-
|
|
98
|
-
if (
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
recordDeliveredMessages(instanceId, delivered);
|
|
102
|
-
messages.push(formatMailboxDelivery(coordRoot, delivered));
|
|
103
|
-
}
|
|
96
|
+
// 3. Rendering never consumes a message. The native emitter acknowledges the
|
|
97
|
+
// prepared instance-bound batch only after it has flushed the output.
|
|
98
|
+
const delivered = peerInbox(coordRoot).prepareSurface(instanceId);
|
|
99
|
+
if (delivered.length > 0) {
|
|
100
|
+
opts.onInboxPrepared?.(delivered.map(({ message_id }) => message_id));
|
|
101
|
+
messages.push(formatPeerInbox(coordRoot, delivered));
|
|
104
102
|
}
|
|
105
103
|
|
|
106
104
|
// 4. Council invites
|
|
@@ -147,27 +145,6 @@ export function renderSessionContext(opts: RenderOpts): string {
|
|
|
147
145
|
return messages.join("\n\n");
|
|
148
146
|
}
|
|
149
147
|
|
|
150
|
-
/**
|
|
151
|
-
* Write delivered peer messages into the recipient's own journal, so they
|
|
152
|
-
* survive context compaction the way any other coordination breadcrumb does.
|
|
153
|
-
* A message the sender already journaled (the recipient was live when it was
|
|
154
|
-
* sent) is skipped to avoid a duplicate entry.
|
|
155
|
-
*/
|
|
156
|
-
export function recordDeliveredMessages(
|
|
157
|
-
instanceId: string,
|
|
158
|
-
messages: readonly MailboxMessageV1[],
|
|
159
|
-
): void {
|
|
160
|
-
for (const m of messages) {
|
|
161
|
-
if (m.journaled) continue;
|
|
162
|
-
try {
|
|
163
|
-
appendEntry(instanceId, "handoff", `from agent-${m.from_name}: ${m.body}`);
|
|
164
|
-
} catch {
|
|
165
|
-
// A journal write failure must never cost the delivery itself; the
|
|
166
|
-
// message is already in the rendered context the model reads.
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
148
|
/** One-line-per-agent view of sessions on other machines (presence refs). */
|
|
172
149
|
function formatRemoteMachines(coordRoot: string): string {
|
|
173
150
|
try {
|
|
@@ -424,9 +424,9 @@ function sessionNameBlock(coordRoot: string, instanceId: string): VerdictResult
|
|
|
424
424
|
} catch {
|
|
425
425
|
// The ledger evidence still authorizes the block; the cache is UX only.
|
|
426
426
|
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
427
|
+
// The first post-mint reply was already rejected. Repeating its title later
|
|
428
|
+
// cannot change that evidence; the JSON retry result opens a new window.
|
|
429
|
+
const instruction = `Run \`${resolveBinName(coordRoot)} agents suggest-name --json\` as your only tool call to create a fresh verification boundary, then display its suggested_session_name verbatim by itself in a fenced code block${name ? `: ${name}` : "."}`;
|
|
430
430
|
return {
|
|
431
431
|
allow: false,
|
|
432
432
|
exit_code: 2,
|
|
@@ -24,6 +24,11 @@ import {
|
|
|
24
24
|
writeFileSync,
|
|
25
25
|
} from "node:fs";
|
|
26
26
|
import { dirname, join } from "node:path";
|
|
27
|
+
import {
|
|
28
|
+
assertCurrentGenerationV3,
|
|
29
|
+
withGenerationCustodyV3,
|
|
30
|
+
} from "../../events/v3/generation-custody.ts";
|
|
31
|
+
import { liveInstanceIdV3 } from "../../events/v3/live-routing.ts";
|
|
27
32
|
import { canonicalClaimPath } from "../claim-path.ts";
|
|
28
33
|
import { type Heartbeat, heartbeatPath, readHeartbeat } from "./heartbeat-reader.ts";
|
|
29
34
|
import type { TaskState } from "./session-state.ts";
|
|
@@ -63,12 +68,15 @@ function mutate(
|
|
|
63
68
|
instanceId: string,
|
|
64
69
|
fn: (hb: Heartbeat) => Heartbeat,
|
|
65
70
|
): Heartbeat | null {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
return withGenerationCustodyV3(coordRoot, [liveInstanceIdV3(instanceId)], () => {
|
|
72
|
+
const hb = readHeartbeat(coordRoot, instanceId);
|
|
73
|
+
if (!hb) return null;
|
|
74
|
+
const next = fn(hb);
|
|
75
|
+
next.last_heartbeat = nowIsoSeconds();
|
|
76
|
+
assertCurrentGenerationV3(coordRoot, liveInstanceIdV3(instanceId), hb.v3_generation_id);
|
|
77
|
+
atomicWrite(heartbeatPath(coordRoot, instanceId), JSON.stringify(next, null, 2));
|
|
78
|
+
return next;
|
|
79
|
+
});
|
|
72
80
|
}
|
|
73
81
|
|
|
74
82
|
export function setTask(coordRoot: string, instanceId: string, task: string): Heartbeat | null {
|
|
@@ -100,12 +108,15 @@ export function setIdentityCache(
|
|
|
100
108
|
name: string,
|
|
101
109
|
agentId: string,
|
|
102
110
|
): Heartbeat | null {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
111
|
+
return withGenerationCustodyV3(coordRoot, [liveInstanceIdV3(instanceId)], () => {
|
|
112
|
+
const heartbeat = readHeartbeat(coordRoot, instanceId);
|
|
113
|
+
if (!heartbeat) return null;
|
|
114
|
+
heartbeat.name = name;
|
|
115
|
+
heartbeat.agent_id = agentId;
|
|
116
|
+
assertCurrentGenerationV3(coordRoot, liveInstanceIdV3(instanceId), heartbeat.v3_generation_id);
|
|
117
|
+
atomicWrite(heartbeatPath(coordRoot, instanceId), JSON.stringify(heartbeat, null, 2));
|
|
118
|
+
return heartbeat;
|
|
119
|
+
});
|
|
109
120
|
}
|
|
110
121
|
|
|
111
122
|
/** Restore the pool-assigned display name on a generation that was
|
|
@@ -328,6 +339,12 @@ export function clearCoordinationCache(coordRoot: string, instanceId: string): b
|
|
|
328
339
|
}
|
|
329
340
|
|
|
330
341
|
export function healPidmap(coordRoot: string, instanceId: string, pid: number): void {
|
|
342
|
+
withGenerationCustodyV3(coordRoot, [liveInstanceIdV3(instanceId)], () =>
|
|
343
|
+
healPidmapUnderCustody(coordRoot, instanceId, pid),
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function healPidmapUnderCustody(coordRoot: string, instanceId: string, pid: number): void {
|
|
331
348
|
const dir = join(coordRoot, ".harnery", "pid-map");
|
|
332
349
|
mkdirSync(dir, { recursive: true });
|
|
333
350
|
const hb = readHeartbeat(coordRoot, instanceId);
|
|
@@ -5,6 +5,10 @@ import {
|
|
|
5
5
|
readCoordinationViewV3,
|
|
6
6
|
requireAuthoritySafeCoordinationViewV3,
|
|
7
7
|
} from "../../events/v3/coordination-view.ts";
|
|
8
|
+
import {
|
|
9
|
+
assertCurrentGenerationV3,
|
|
10
|
+
withGenerationCustodyV3,
|
|
11
|
+
} from "../../events/v3/generation-custody.ts";
|
|
8
12
|
import { liveInstanceIdV3, resolveLiveEventLedgerRouteV3 } from "../../events/v3/live-routing.ts";
|
|
9
13
|
import { type Heartbeat, readHeartbeat } from "./heartbeat-reader.ts";
|
|
10
14
|
import {
|
|
@@ -64,6 +68,26 @@ function materializeLiveCoordinationHeartbeat(
|
|
|
64
68
|
_adapter: Adapter,
|
|
65
69
|
model: string | undefined,
|
|
66
70
|
force: boolean,
|
|
71
|
+
): Heartbeat | null {
|
|
72
|
+
return withGenerationCustodyV3(coordRoot, [liveInstanceIdV3(nativeInstanceId)], () =>
|
|
73
|
+
materializeHeartbeatUnderCustody(
|
|
74
|
+
coordRoot,
|
|
75
|
+
nativeInstanceId,
|
|
76
|
+
nativeSessionId,
|
|
77
|
+
_adapter,
|
|
78
|
+
model,
|
|
79
|
+
force,
|
|
80
|
+
),
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function materializeHeartbeatUnderCustody(
|
|
85
|
+
coordRoot: string,
|
|
86
|
+
nativeInstanceId: string,
|
|
87
|
+
nativeSessionId: string,
|
|
88
|
+
_adapter: Adapter,
|
|
89
|
+
model: string | undefined,
|
|
90
|
+
force: boolean,
|
|
67
91
|
): Heartbeat | null {
|
|
68
92
|
const route = resolveLiveEventLedgerRouteV3(coordRoot);
|
|
69
93
|
if (route.state === "blocked") throw new Error(`event_v3_coordination_view:${route.reason}`);
|
|
@@ -133,6 +157,11 @@ function materializeLiveCoordinationHeartbeat(
|
|
|
133
157
|
v3_projection_event_id: generation.last_event_id,
|
|
134
158
|
v3_task_state: generation.task_state === "set" ? "set" : "cleared",
|
|
135
159
|
};
|
|
160
|
+
assertCurrentGenerationV3(
|
|
161
|
+
coordRoot,
|
|
162
|
+
liveInstanceIdV3(nativeInstanceId),
|
|
163
|
+
generation.generation_id,
|
|
164
|
+
);
|
|
136
165
|
writeHeartbeatCache(coordRoot, nativeInstanceId, materialized);
|
|
137
166
|
return materialized;
|
|
138
167
|
}
|
|
@@ -24,6 +24,8 @@ import {
|
|
|
24
24
|
writeFileSync,
|
|
25
25
|
} from "node:fs";
|
|
26
26
|
import { dirname, join } from "node:path";
|
|
27
|
+
import { withGenerationCustodyV3 } from "../../events/v3/generation-custody.ts";
|
|
28
|
+
import { liveInstanceIdV3 } from "../../events/v3/live-routing.ts";
|
|
27
29
|
import { checkPidToken, processStartToken } from "./proc-start.ts";
|
|
28
30
|
|
|
29
31
|
/**
|
|
@@ -126,6 +128,17 @@ export function writePidmapRow(
|
|
|
126
128
|
pid: number,
|
|
127
129
|
instanceId: string,
|
|
128
130
|
platform: string,
|
|
131
|
+
): void {
|
|
132
|
+
withGenerationCustodyV3(coordRoot, [liveInstanceIdV3(instanceId)], () =>
|
|
133
|
+
writePidmapRowUnderCustody(coordRoot, pid, instanceId, platform),
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function writePidmapRowUnderCustody(
|
|
138
|
+
coordRoot: string,
|
|
139
|
+
pid: number,
|
|
140
|
+
instanceId: string,
|
|
141
|
+
platform: string,
|
|
129
142
|
): void {
|
|
130
143
|
const dir = join(coordRoot, ".harnery", "pid-map");
|
|
131
144
|
const path = join(dir, String(pid));
|
|
@@ -34,9 +34,10 @@
|
|
|
34
34
|
import { spawnSync } from "node:child_process";
|
|
35
35
|
import { existsSync, readFileSync } from "node:fs";
|
|
36
36
|
import { coordEnv } from "../../../lib/env.ts";
|
|
37
|
+
import { signalProcessWithHandle, windowsProcessStartToken } from "./process-handle.ts";
|
|
37
38
|
|
|
38
39
|
/** Which probe answers "when did this process start?" on this machine. */
|
|
39
|
-
export type StartTokenProbe = "procfs" | "ps";
|
|
40
|
+
export type StartTokenProbe = "procfs" | "ps" | "windows";
|
|
40
41
|
|
|
41
42
|
/**
|
|
42
43
|
* The `ps` probe is text formatted by `strftime("%c", localtime(...))`, so its
|
|
@@ -60,6 +61,7 @@ let cachedBootId: string | null | undefined;
|
|
|
60
61
|
export function startTokenProbe(): StartTokenProbe {
|
|
61
62
|
const override = coordEnv("PID_PROBE");
|
|
62
63
|
if (override === "procfs" || override === "ps") return override;
|
|
64
|
+
if (process.platform === "win32") return "windows";
|
|
63
65
|
if (cachedProbe === undefined) {
|
|
64
66
|
cachedProbe = existsSync("/proc/self/stat") ? "procfs" : "ps";
|
|
65
67
|
}
|
|
@@ -157,7 +159,65 @@ function defaultPsRun(pid: number): { status: number | null; stdout: string } {
|
|
|
157
159
|
*/
|
|
158
160
|
export function processStartToken(pid: number): string | null {
|
|
159
161
|
if (!Number.isFinite(pid) || pid <= 0) return null;
|
|
160
|
-
|
|
162
|
+
const probe = startTokenProbe();
|
|
163
|
+
return probe === "windows"
|
|
164
|
+
? windowsProcessStartToken(pid)
|
|
165
|
+
: probe === "procfs"
|
|
166
|
+
? procfsStartToken(pid)
|
|
167
|
+
: psStartToken(pid);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export type ProcessGenerationVerdict = "same" | "different" | "unknown";
|
|
171
|
+
|
|
172
|
+
type ReadProcessStartToken = (pid: number) => string | null;
|
|
173
|
+
|
|
174
|
+
/** A missing or unreadable token never establishes process identity. */
|
|
175
|
+
export function verifyProcessGeneration(
|
|
176
|
+
pid: number,
|
|
177
|
+
recordedToken: string | null | undefined,
|
|
178
|
+
readToken: ReadProcessStartToken = processStartToken,
|
|
179
|
+
): ProcessGenerationVerdict {
|
|
180
|
+
if (
|
|
181
|
+
!Number.isInteger(pid) ||
|
|
182
|
+
pid <= 0 ||
|
|
183
|
+
typeof recordedToken !== "string" ||
|
|
184
|
+
!recordedToken.trim()
|
|
185
|
+
)
|
|
186
|
+
return "unknown";
|
|
187
|
+
// A bootless Linux token cannot identify a process across machine boots.
|
|
188
|
+
if (/^l\d+$/.test(recordedToken)) return "unknown";
|
|
189
|
+
try {
|
|
190
|
+
const currentToken = readToken(pid);
|
|
191
|
+
if (typeof currentToken !== "string" || !currentToken.trim()) return "unknown";
|
|
192
|
+
if (/^l\d+$/.test(currentToken)) return "unknown";
|
|
193
|
+
return currentToken === recordedToken ? "same" : "different";
|
|
194
|
+
} catch {
|
|
195
|
+
return "unknown";
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export interface ProcessGenerationSignalDependencies {
|
|
200
|
+
readToken?: ReadProcessStartToken;
|
|
201
|
+
signalProcess?: typeof signalProcessWithHandle;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Observations may refuse a signal, but only the retained native process handle
|
|
206
|
+
* can authorize it. No numeric-PID effect follows the preliminary token read.
|
|
207
|
+
*/
|
|
208
|
+
export function signalIfSameGeneration(
|
|
209
|
+
pid: number,
|
|
210
|
+
recordedToken: string | null | undefined,
|
|
211
|
+
signal: NodeJS.Signals | number,
|
|
212
|
+
dependencies: ProcessGenerationSignalDependencies = {},
|
|
213
|
+
): { sent: boolean; verdict: ProcessGenerationVerdict } {
|
|
214
|
+
const verdict = verifyProcessGeneration(pid, recordedToken, dependencies.readToken);
|
|
215
|
+
if (verdict !== "same") return { sent: false, verdict };
|
|
216
|
+
try {
|
|
217
|
+
return (dependencies.signalProcess ?? signalProcessWithHandle)(pid, recordedToken!, signal);
|
|
218
|
+
} catch {
|
|
219
|
+
return { sent: false, verdict: "unknown" };
|
|
220
|
+
}
|
|
161
221
|
}
|
|
162
222
|
|
|
163
223
|
/**
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { constants } from "node:os";
|
|
3
|
+
import type { ProcessGenerationVerdict } from "./proc-start.ts";
|
|
4
|
+
|
|
5
|
+
export interface ProcessHandleSignalResult {
|
|
6
|
+
sent: boolean;
|
|
7
|
+
verdict: ProcessGenerationVerdict;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface NativeHelperResult extends ProcessHandleSignalResult {
|
|
11
|
+
token?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type NativeProcessHelperRunner = (
|
|
15
|
+
command: string,
|
|
16
|
+
args: string[],
|
|
17
|
+
options: {
|
|
18
|
+
input: string;
|
|
19
|
+
encoding: "utf8";
|
|
20
|
+
timeout: number;
|
|
21
|
+
maxBuffer: number;
|
|
22
|
+
windowsHide: boolean;
|
|
23
|
+
},
|
|
24
|
+
) => { status: number | null; stdout: string | Buffer | null; error?: Error };
|
|
25
|
+
|
|
26
|
+
// The helper owns the descriptor throughout observation and effect. The second
|
|
27
|
+
// input line authorizes the effect; EOF releases the handle without signaling.
|
|
28
|
+
export const LINUX_PROCESS_HANDLE_SCRIPT = `
|
|
29
|
+
import json, os, pathlib, signal, sys
|
|
30
|
+
result = {"sent": False, "verdict": "unknown"}
|
|
31
|
+
fd = None
|
|
32
|
+
try:
|
|
33
|
+
request = json.loads(sys.stdin.readline())
|
|
34
|
+
if not hasattr(os, "pidfd_open") or not hasattr(signal, "pidfd_send_signal"):
|
|
35
|
+
raise RuntimeError("pidfd support unavailable")
|
|
36
|
+
fd = os.pidfd_open(request["pid"])
|
|
37
|
+
stat = pathlib.Path("/proc/%d/stat" % request["pid"]).read_text()
|
|
38
|
+
ticks = stat[stat.rfind(") ") + 2:].split()[19]
|
|
39
|
+
boot = pathlib.Path("/proc/sys/kernel/random/boot_id").read_text().strip().replace("-", "")[:8]
|
|
40
|
+
token = "l%s.%s" % (boot, ticks)
|
|
41
|
+
result["token"] = token
|
|
42
|
+
recorded = request.get("token")
|
|
43
|
+
result["verdict"] = "same" if token == recorded else "different"
|
|
44
|
+
if request["mode"] == "signal" and result["verdict"] == "same":
|
|
45
|
+
print(json.dumps({"phase": "verified"}), flush=True)
|
|
46
|
+
if sys.stdin.readline().strip() == "signal":
|
|
47
|
+
signal.pidfd_send_signal(fd, request["signal"])
|
|
48
|
+
result["sent"] = True
|
|
49
|
+
except Exception:
|
|
50
|
+
pass
|
|
51
|
+
finally:
|
|
52
|
+
if fd is not None:
|
|
53
|
+
os.close(fd)
|
|
54
|
+
print(json.dumps(result), flush=True)
|
|
55
|
+
`;
|
|
56
|
+
|
|
57
|
+
export const WINDOWS_PROCESS_HANDLE_SCRIPT = `
|
|
58
|
+
$ErrorActionPreference = 'Stop'
|
|
59
|
+
$result = @{ sent = $false; verdict = 'unknown' }
|
|
60
|
+
$handle = [IntPtr]::Zero
|
|
61
|
+
try {
|
|
62
|
+
Add-Type -TypeDefinition @'
|
|
63
|
+
using System;
|
|
64
|
+
using System.Runtime.InteropServices;
|
|
65
|
+
public static class HarneryProcessHandle {
|
|
66
|
+
[DllImport("kernel32.dll", SetLastError=true)] public static extern IntPtr OpenProcess(uint access, bool inherit, uint processId);
|
|
67
|
+
[DllImport("kernel32.dll", SetLastError=true)] public static extern bool GetProcessTimes(IntPtr handle, out long creation, out long exit, out long kernel, out long user);
|
|
68
|
+
[DllImport("kernel32.dll", SetLastError=true)] public static extern bool TerminateProcess(IntPtr handle, uint exitCode);
|
|
69
|
+
[DllImport("kernel32.dll")] public static extern uint WaitForSingleObject(IntPtr handle, uint milliseconds);
|
|
70
|
+
[DllImport("kernel32.dll")] public static extern bool CloseHandle(IntPtr handle);
|
|
71
|
+
}
|
|
72
|
+
'@
|
|
73
|
+
$request = [Console]::In.ReadLine() | ConvertFrom-Json
|
|
74
|
+
$access = [uint32]0x101000
|
|
75
|
+
if ($request.mode -eq 'signal') { $access = $access -bor 1 }
|
|
76
|
+
$handle = [HarneryProcessHandle]::OpenProcess($access, $false, [uint32]$request.pid)
|
|
77
|
+
if ($handle -eq [IntPtr]::Zero) { throw 'process handle unavailable' }
|
|
78
|
+
[long]$creation = 0; [long]$exitTime = 0; [long]$kernel = 0; [long]$userTime = 0
|
|
79
|
+
if (-not [HarneryProcessHandle]::GetProcessTimes($handle, [ref]$creation, [ref]$exitTime, [ref]$kernel, [ref]$userTime)) { throw 'process time unavailable' }
|
|
80
|
+
$token = 'w' + $creation.ToString([Globalization.CultureInfo]::InvariantCulture)
|
|
81
|
+
$result.token = $token
|
|
82
|
+
$result.verdict = if ($token -ceq $request.token) { 'same' } else { 'different' }
|
|
83
|
+
if ($request.mode -eq 'signal' -and $result.verdict -eq 'same') {
|
|
84
|
+
[Console]::Out.WriteLine('{"phase":"verified"}')
|
|
85
|
+
[Console]::Out.Flush()
|
|
86
|
+
if ([Console]::In.ReadLine() -eq 'signal' -and [HarneryProcessHandle]::WaitForSingleObject($handle, 0) -eq 258) {
|
|
87
|
+
$result.sent = [HarneryProcessHandle]::TerminateProcess($handle, 1)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
} catch {} finally {
|
|
91
|
+
if ($handle -ne [IntPtr]::Zero) { [void][HarneryProcessHandle]::CloseHandle($handle) }
|
|
92
|
+
}
|
|
93
|
+
$result | ConvertTo-Json -Compress
|
|
94
|
+
`;
|
|
95
|
+
|
|
96
|
+
function runHelper(
|
|
97
|
+
request: { mode: "probe" | "signal"; pid: number; token?: string; signal?: number },
|
|
98
|
+
platform: NodeJS.Platform,
|
|
99
|
+
run: NativeProcessHelperRunner,
|
|
100
|
+
): NativeHelperResult {
|
|
101
|
+
const unknown = { sent: false, verdict: "unknown" } as const;
|
|
102
|
+
if (!Number.isInteger(request.pid) || request.pid <= 0) return unknown;
|
|
103
|
+
if (platform !== "linux" && platform !== "win32") return unknown;
|
|
104
|
+
try {
|
|
105
|
+
const command = platform === "linux" ? "python3" : "powershell.exe";
|
|
106
|
+
const args =
|
|
107
|
+
platform === "linux"
|
|
108
|
+
? ["-I", "-c", LINUX_PROCESS_HANDLE_SCRIPT]
|
|
109
|
+
: ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", WINDOWS_PROCESS_HANDLE_SCRIPT];
|
|
110
|
+
const output = run(command, args, {
|
|
111
|
+
input: `${JSON.stringify(request)}\nsignal\n`,
|
|
112
|
+
encoding: "utf8",
|
|
113
|
+
timeout: 10_000,
|
|
114
|
+
maxBuffer: 64 * 1024,
|
|
115
|
+
windowsHide: true,
|
|
116
|
+
});
|
|
117
|
+
if (output.error || output.status !== 0) return unknown;
|
|
118
|
+
const lines = String(output.stdout ?? "")
|
|
119
|
+
.trim()
|
|
120
|
+
.split(/\r?\n/);
|
|
121
|
+
const parsed = JSON.parse(lines.at(-1) ?? "") as NativeHelperResult;
|
|
122
|
+
if (
|
|
123
|
+
typeof parsed.sent !== "boolean" ||
|
|
124
|
+
!["same", "different", "unknown"].includes(parsed.verdict) ||
|
|
125
|
+
(parsed.sent && parsed.verdict !== "same")
|
|
126
|
+
)
|
|
127
|
+
return unknown;
|
|
128
|
+
return parsed;
|
|
129
|
+
} catch {
|
|
130
|
+
return unknown;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** Windows creation FILETIME, read from an OpenProcess handle. */
|
|
135
|
+
export function windowsProcessStartToken(
|
|
136
|
+
pid: number,
|
|
137
|
+
run: NativeProcessHelperRunner = spawnSync,
|
|
138
|
+
): string | null {
|
|
139
|
+
const result = runHelper({ mode: "probe", pid }, "win32", run);
|
|
140
|
+
return typeof result.token === "string" && /^w\d+$/.test(result.token) ? result.token : null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** No numeric-PID signaling fallback is permitted when an exact handle fails. */
|
|
144
|
+
export function signalProcessWithHandle(
|
|
145
|
+
pid: number,
|
|
146
|
+
recordedToken: string,
|
|
147
|
+
signal: NodeJS.Signals | number,
|
|
148
|
+
options: { platform?: NodeJS.Platform; run?: NativeProcessHelperRunner } = {},
|
|
149
|
+
): ProcessHandleSignalResult {
|
|
150
|
+
const platform = options.platform ?? process.platform;
|
|
151
|
+
const number = typeof signal === "number" ? signal : constants.signals[signal];
|
|
152
|
+
if (
|
|
153
|
+
!Number.isInteger(number) ||
|
|
154
|
+
number <= 0 ||
|
|
155
|
+
(platform === "linux" && !/^l[0-9a-f]{8}\.\d+$/.test(recordedToken)) ||
|
|
156
|
+
(platform === "win32" && (!/^w\d+$/.test(recordedToken) || ![9, 15].includes(number)))
|
|
157
|
+
)
|
|
158
|
+
return { sent: false, verdict: "unknown" };
|
|
159
|
+
const result = runHelper(
|
|
160
|
+
{ mode: "signal", pid, token: recordedToken, signal: number },
|
|
161
|
+
platform,
|
|
162
|
+
options.run ?? spawnSync,
|
|
163
|
+
);
|
|
164
|
+
return { sent: result.sent, verdict: result.verdict };
|
|
165
|
+
}
|