@llblab/pi-actors 0.42.3 → 0.43.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/AGENTS.md +121 -175
- package/CHANGELOG.md +12 -0
- package/README.md +113 -276
- package/dist/fixtures/protocol/control-endpoint.json +6 -0
- package/dist/fixtures/protocol/control-record.json +9 -0
- package/dist/fixtures/protocol/recipe-summary.json +4 -12
- package/dist/fixtures/protocol/trace-event.json +9 -0
- package/dist/lib/async-runs.d.ts +14 -38
- package/dist/lib/async-runs.js +158 -108
- package/dist/lib/control.d.ts +12 -0
- package/dist/lib/control.js +84 -0
- package/dist/lib/execution-sessions.d.ts +17 -0
- package/dist/lib/execution-sessions.js +85 -0
- package/dist/lib/file-state.d.ts +1 -0
- package/dist/lib/file-state.js +17 -5
- package/dist/lib/inspector-actions.d.ts +2 -2
- package/dist/lib/inspector-actions.js +2 -2
- package/dist/lib/inspector-command.js +3 -3
- package/dist/lib/inspector-overlay.d.ts +52 -70
- package/dist/lib/inspector-overlay.js +532 -905
- package/dist/lib/inspector.d.ts +3 -71
- package/dist/lib/inspector.js +19 -665
- package/dist/lib/limits.d.ts +4 -2
- package/dist/lib/limits.js +4 -2
- package/dist/lib/observability.d.ts +16 -16
- package/dist/lib/observability.js +43 -82
- package/dist/lib/prompts.d.ts +1 -1
- package/dist/lib/prompts.js +2 -2
- package/dist/lib/recipe-control.d.ts +7 -0
- package/dist/lib/recipe-control.js +39 -0
- package/dist/lib/recipes-discovery.js +2 -0
- package/dist/lib/recipes-references.d.ts +1 -14
- package/dist/lib/recipes-references.js +6 -21
- package/dist/lib/review-projection.js +1 -5
- package/dist/lib/run-ui-runtime.js +2 -2
- package/dist/lib/runs-control-delivery.d.ts +21 -0
- package/dist/lib/runs-control-delivery.js +127 -0
- package/dist/lib/runs-controls.d.ts +35 -0
- package/dist/lib/runs-controls.js +144 -0
- package/dist/lib/runs-retention.d.ts +7 -0
- package/dist/lib/runs-retention.js +27 -3
- package/dist/lib/runs-start.js +4 -2
- package/dist/lib/runs-status.js +11 -6
- package/dist/lib/runs-trace.d.ts +24 -0
- package/dist/lib/runs-trace.js +98 -0
- package/dist/lib/runtime-notifier.d.ts +1 -1
- package/dist/lib/runtime-notifier.js +1 -1
- package/dist/lib/tools-inspect.d.ts +3 -3
- package/dist/lib/tools-inspect.js +203 -708
- package/dist/lib/tools-local.js +2 -10
- package/dist/lib/tools-message.d.ts +7 -7
- package/dist/lib/tools-message.js +95 -396
- package/dist/lib/tools-response.d.ts +1 -4
- package/dist/lib/tools-response.js +5 -39
- package/dist/lib/tools-spawn.js +16 -28
- package/dist/lib/tools.js +1 -2
- package/dist/lib/trace-projection.d.ts +22 -0
- package/dist/lib/trace-projection.js +165 -0
- package/dist/recipes/draft-review.json +0 -10
- package/dist/recipes/lens-swarm.json +0 -14
- package/dist/recipes/music-player.json +10 -19
- package/dist/recipes/pipeline-architect-coordinator.json +0 -11
- package/dist/recipes/pipeline-artifact-bundle.json +1 -22
- package/dist/recipes/pipeline-artifact-report.json +1 -18
- package/dist/recipes/pipeline-artifact-write.json +1 -18
- package/dist/recipes/pipeline-async-run-ops.json +0 -12
- package/dist/recipes/pipeline-checkpoint-continuation.json +0 -14
- package/dist/recipes/pipeline-development-tasking.json +0 -12
- package/dist/recipes/pipeline-docs-maintenance.json +0 -12
- package/dist/recipes/pipeline-media-library.json +0 -12
- package/dist/recipes/pipeline-quorum-review.json +0 -12
- package/dist/recipes/pipeline-release-readiness.json +0 -12
- package/dist/recipes/pipeline-release-summary.json +0 -12
- package/dist/recipes/pipeline-repo-health.json +0 -12
- package/dist/recipes/pipeline-research-synthesis.json +0 -11
- package/dist/recipes/pipeline-review-readiness.json +0 -12
- package/dist/recipes/resource-locker.json +27 -0
- package/dist/recipes/subagent-artifact.json +0 -9
- package/dist/recipes/subagent-checkpoint.json +0 -10
- package/dist/recipes/subagent-conflict-report.json +0 -11
- package/dist/recipes/subagent-contradiction-map.json +0 -11
- package/dist/recipes/subagent-critic.json +0 -11
- package/dist/recipes/subagent-evidence-map.json +0 -11
- package/dist/recipes/subagent-followup.json +0 -10
- package/dist/recipes/subagent-judge.json +0 -11
- package/dist/recipes/subagent-merge.json +0 -11
- package/dist/recipes/subagent-normalize.json +0 -11
- package/dist/recipes/subagent-plan.json +0 -11
- package/dist/recipes/subagent-preflight.json +0 -11
- package/dist/recipes/subagent-prompt.json +0 -10
- package/dist/recipes/subagent-quorum.json +0 -10
- package/dist/recipes/subagent-review-coordinator.json +0 -14
- package/dist/recipes/subagent-review.json +0 -11
- package/dist/recipes/subagent-task-card.json +0 -11
- package/dist/recipes/subagent-tools.json +0 -10
- package/dist/recipes/subagent-verify.json +0 -11
- package/dist/recipes/subagents-prompts.json +0 -10
- package/dist/recipes/tool-review.json +0 -10
- package/dist/scripts/async-runner.mjs +25 -25
- package/dist/scripts/conformance.mjs +4 -2
- package/dist/scripts/locker.mjs +200 -66
- package/dist/scripts/music-player.mjs +159 -150
- package/dist/scripts/recipe-utils.mjs +6 -96
- package/dist/scripts/release-gates.mjs +60 -0
- package/dist/scripts/validate-recipe.mjs +3 -53
- package/dist/skills/actors/SKILL.md +53 -266
- package/dist/skills/swarm/SKILL.md +11 -33
- package/docs/0.43-baseline.md +44 -0
- package/docs/README.md +3 -3
- package/docs/actor-inspector.md +26 -64
- package/docs/actors-deep-reference.md +92 -50
- package/docs/async-runs.md +81 -328
- package/docs/command-templates.md +2 -2
- package/docs/component-recipes.md +30 -133
- package/docs/recipe-library.md +57 -182
- package/docs/task-first-recipes.md +10 -12
- package/docs/template-recipes.md +76 -289
- package/docs/tool-registry.md +41 -161
- package/fixtures/protocol/control-endpoint.json +6 -0
- package/fixtures/protocol/control-record.json +9 -0
- package/fixtures/protocol/recipe-summary.json +4 -12
- package/fixtures/protocol/trace-event.json +9 -0
- package/lib/async-runs.ts +202 -201
- package/lib/control.ts +102 -0
- package/lib/execution-sessions.ts +111 -0
- package/lib/file-state.ts +17 -4
- package/lib/inspector-actions.ts +2 -2
- package/lib/inspector-command.ts +3 -3
- package/lib/inspector-overlay.ts +577 -1121
- package/lib/inspector.ts +46 -979
- package/lib/limits.ts +4 -2
- package/lib/observability.ts +60 -101
- package/lib/prompts.ts +2 -2
- package/lib/recipe-control.ts +45 -0
- package/lib/recipes-discovery.ts +2 -0
- package/lib/recipes-references.ts +9 -45
- package/lib/review-projection.ts +1 -5
- package/lib/run-ui-runtime.ts +2 -2
- package/lib/runs-control-delivery.ts +181 -0
- package/lib/runs-controls.ts +204 -0
- package/lib/runs-retention.ts +38 -3
- package/lib/runs-start.ts +4 -2
- package/lib/runs-status.ts +11 -6
- package/lib/runs-trace.ts +132 -0
- package/lib/runtime-notifier.ts +1 -1
- package/lib/tools-inspect.ts +240 -901
- package/lib/tools-local.ts +2 -12
- package/lib/tools-message.ts +112 -519
- package/lib/tools-response.ts +5 -52
- package/lib/tools-spawn.ts +16 -32
- package/lib/tools.ts +1 -2
- package/lib/trace-projection.ts +221 -0
- package/package.json +2 -1
- package/recipes/draft-review.json +0 -10
- package/recipes/lens-swarm.json +0 -14
- package/recipes/music-player.json +10 -19
- package/recipes/pipeline-architect-coordinator.json +0 -11
- package/recipes/pipeline-artifact-bundle.json +1 -22
- package/recipes/pipeline-artifact-report.json +1 -18
- package/recipes/pipeline-artifact-write.json +1 -18
- package/recipes/pipeline-async-run-ops.json +0 -12
- package/recipes/pipeline-checkpoint-continuation.json +0 -14
- package/recipes/pipeline-development-tasking.json +0 -12
- package/recipes/pipeline-docs-maintenance.json +0 -12
- package/recipes/pipeline-media-library.json +0 -12
- package/recipes/pipeline-quorum-review.json +0 -12
- package/recipes/pipeline-release-readiness.json +0 -12
- package/recipes/pipeline-release-summary.json +0 -12
- package/recipes/pipeline-repo-health.json +0 -12
- package/recipes/pipeline-research-synthesis.json +0 -11
- package/recipes/pipeline-review-readiness.json +0 -12
- package/recipes/resource-locker.json +27 -0
- package/recipes/subagent-artifact.json +0 -9
- package/recipes/subagent-checkpoint.json +0 -10
- package/recipes/subagent-conflict-report.json +0 -11
- package/recipes/subagent-contradiction-map.json +0 -11
- package/recipes/subagent-critic.json +0 -11
- package/recipes/subagent-evidence-map.json +0 -11
- package/recipes/subagent-followup.json +0 -10
- package/recipes/subagent-judge.json +0 -11
- package/recipes/subagent-merge.json +0 -11
- package/recipes/subagent-normalize.json +0 -11
- package/recipes/subagent-plan.json +0 -11
- package/recipes/subagent-preflight.json +0 -11
- package/recipes/subagent-prompt.json +0 -10
- package/recipes/subagent-quorum.json +0 -10
- package/recipes/subagent-review-coordinator.json +0 -14
- package/recipes/subagent-review.json +0 -11
- package/recipes/subagent-task-card.json +0 -11
- package/recipes/subagent-tools.json +0 -10
- package/recipes/subagent-verify.json +0 -11
- package/recipes/subagents-prompts.json +0 -10
- package/recipes/tool-review.json +0 -10
- package/scripts/async-runner.mjs +25 -25
- package/scripts/conformance.mjs +4 -2
- package/scripts/locker.mjs +200 -66
- package/scripts/music-player.mjs +159 -150
- package/scripts/recipe-utils.mjs +6 -96
- package/scripts/release-gates.mjs +60 -0
- package/scripts/validate-recipe.mjs +3 -53
- package/skills/actors/SKILL.md +53 -266
- package/skills/swarm/SKILL.md +11 -33
- package/dist/fixtures/protocol/actor-message-branch.json +0 -13
- package/dist/fixtures/protocol/mailbox-contract.json +0 -15
- package/dist/fixtures/protocol/room-message.json +0 -11
- package/dist/fixtures/protocol/room-roster.json +0 -11
- package/dist/fixtures/protocol/run-inbox-message.json +0 -9
- package/dist/fixtures/protocol/run-outbox-event.json +0 -9
- package/dist/lib/mailbox-loop.d.ts +0 -41
- package/dist/lib/mailbox-loop.js +0 -60
- package/dist/lib/messages.d.ts +0 -25
- package/dist/lib/messages.js +0 -122
- package/dist/lib/rooms.d.ts +0 -104
- package/dist/lib/rooms.js +0 -647
- package/dist/lib/runs-mailbox.d.ts +0 -25
- package/dist/lib/runs-mailbox.js +0 -146
- package/dist/lib/runs-messages.d.ts +0 -15
- package/dist/lib/runs-messages.js +0 -179
- package/dist/lib/runs-outbox.d.ts +0 -41
- package/dist/lib/runs-outbox.js +0 -87
- package/dist/lib/tools-mailbox.d.ts +0 -8
- package/dist/lib/tools-mailbox.js +0 -48
- package/dist/recipes/actor-worker.json +0 -39
- package/dist/recipes/coordinator-locker.json +0 -45
- package/dist/recipes/locker.json +0 -45
- package/dist/recipes/pipeline-room-swarm.json +0 -50
- package/dist/recipes/subagent-message.json +0 -32
- package/dist/recipes/utility-actor-message.json +0 -23
- package/dist/scripts/actor-worker.mjs +0 -214
- package/dist/scripts/coordinator.mjs +0 -799
- package/docs/actor-messages.md +0 -225
- package/fixtures/protocol/actor-message-branch.json +0 -13
- package/fixtures/protocol/mailbox-contract.json +0 -15
- package/fixtures/protocol/room-message.json +0 -11
- package/fixtures/protocol/room-roster.json +0 -11
- package/fixtures/protocol/run-inbox-message.json +0 -9
- package/fixtures/protocol/run-outbox-event.json +0 -9
- package/lib/mailbox-loop.ts +0 -144
- package/lib/messages.ts +0 -151
- package/lib/rooms.ts +0 -939
- package/lib/runs-mailbox.ts +0 -208
- package/lib/runs-messages.ts +0 -252
- package/lib/runs-outbox.ts +0 -144
- package/lib/tools-mailbox.ts +0 -56
- package/recipes/actor-worker.json +0 -39
- package/recipes/coordinator-locker.json +0 -45
- package/recipes/locker.json +0 -45
- package/recipes/pipeline-room-swarm.json +0 -50
- package/recipes/subagent-message.json +0 -32
- package/recipes/utility-actor-message.json +0 -23
- package/scripts/actor-worker.mjs +0 -214
- package/scripts/coordinator.mjs +0 -799
package/lib/async-runs.ts
CHANGED
|
@@ -47,40 +47,34 @@ import {
|
|
|
47
47
|
signalOwnedRunProcess,
|
|
48
48
|
type RunProcessSignalPlan,
|
|
49
49
|
} from "./runs-control.ts";
|
|
50
|
-
import {
|
|
51
|
-
buildRunOutboxEventPayload,
|
|
52
|
-
parseRunOutboxEventLine,
|
|
53
|
-
type RunOutboxEvent,
|
|
54
|
-
} from "./runs-outbox.ts";
|
|
55
50
|
import { claimRunStateDirectory } from "./runs-ownership.ts";
|
|
56
|
-
import {
|
|
51
|
+
import {
|
|
52
|
+
appendRunRetentionEvidence,
|
|
53
|
+
archiveTerminalRun,
|
|
54
|
+
pruneTerminalRun,
|
|
55
|
+
type RunRetentionAction,
|
|
56
|
+
} from "./runs-retention.ts";
|
|
57
57
|
import {
|
|
58
58
|
captureRunProcessIdentity,
|
|
59
59
|
verifyRunProcessIdentity,
|
|
60
60
|
type RunProcessIdentity,
|
|
61
61
|
} from "./runs-process.ts";
|
|
62
62
|
import * as RunsStart from "./runs-start.ts";
|
|
63
|
+
import { appendRunTraceEvent } from "./runs-trace.ts";
|
|
63
64
|
import * as RunsIndex from "./runs-index.ts";
|
|
64
65
|
import * as RunsParentTeardown from "./runs-parent-teardown.ts";
|
|
65
66
|
import {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
readRunInboxMessagesFromStateDir,
|
|
70
|
-
runInboxFile,
|
|
71
|
-
type ProcessRunInboxResult,
|
|
72
|
-
type RunInboxMessage,
|
|
73
|
-
type RunInboxStatus,
|
|
74
|
-
updateRunInboxMessageStatusInStateDir,
|
|
75
|
-
} from "./runs-mailbox.ts";
|
|
67
|
+
appendRunControlInStateDir,
|
|
68
|
+
updateRunControlStatusInStateDir,
|
|
69
|
+
} from "./runs-controls.ts";
|
|
76
70
|
import {
|
|
77
|
-
|
|
78
|
-
type
|
|
79
|
-
|
|
71
|
+
deliverRunControl,
|
|
72
|
+
type DeliverRunControlOptions,
|
|
73
|
+
type DeliverRunControlRequest,
|
|
74
|
+
} from "./runs-control-delivery.ts";
|
|
80
75
|
import {
|
|
81
76
|
buildRunStatus,
|
|
82
77
|
tailFile,
|
|
83
|
-
tailLines,
|
|
84
78
|
type AsyncRunStatus,
|
|
85
79
|
} from "./runs-status.ts";
|
|
86
80
|
import { readJsonFileResilient } from "./state-readers.ts";
|
|
@@ -91,7 +85,7 @@ export type AsyncRunLaunchSource = "spawn" | "tool";
|
|
|
91
85
|
|
|
92
86
|
export interface AsyncRunControlEndpoint {
|
|
93
87
|
path: string;
|
|
94
|
-
type: "fifo" | "
|
|
88
|
+
type: "fifo" | "named-pipe";
|
|
95
89
|
}
|
|
96
90
|
|
|
97
91
|
export function normalizeRunTransportContext(
|
|
@@ -119,7 +113,7 @@ export function normalizeRunTransportContext(
|
|
|
119
113
|
|
|
120
114
|
export interface AsyncRunStartParams {
|
|
121
115
|
async?: boolean;
|
|
122
|
-
|
|
116
|
+
control_endpoint?: AsyncRunControlEndpoint;
|
|
123
117
|
file?: string;
|
|
124
118
|
launch_source?: AsyncRunLaunchSource;
|
|
125
119
|
lifecycleHooks?: {
|
|
@@ -147,7 +141,7 @@ export interface AsyncRunStartParams {
|
|
|
147
141
|
accept_output?: "review_evidence";
|
|
148
142
|
output?: string;
|
|
149
143
|
artifacts?: Record<string, RunArtifactDeclaration>;
|
|
150
|
-
|
|
144
|
+
control?: string[];
|
|
151
145
|
notification_policy?: "normal" | "silent";
|
|
152
146
|
retire_when?: "children_terminal";
|
|
153
147
|
retry?: number | string;
|
|
@@ -179,13 +173,14 @@ export interface AsyncRunMeta {
|
|
|
179
173
|
run: string;
|
|
180
174
|
run_instance_id: string;
|
|
181
175
|
state_dir: string;
|
|
176
|
+
state_schema: "run-kernel-v1";
|
|
182
177
|
status: AsyncRunStatus;
|
|
183
178
|
tool?: string;
|
|
184
179
|
template: CommandTemplateValue;
|
|
185
180
|
values: Record<string, unknown>;
|
|
186
181
|
artifacts?: Record<string, RunArtifactDeclaration>;
|
|
187
|
-
control?:
|
|
188
|
-
|
|
182
|
+
control?: string[];
|
|
183
|
+
control_endpoint?: AsyncRunControlEndpoint;
|
|
189
184
|
model_policy?: CurrentPolicyProvenance;
|
|
190
185
|
notification_policy?: "normal" | "silent";
|
|
191
186
|
process_identity?: RunProcessIdentity;
|
|
@@ -473,11 +468,9 @@ export function startRun(
|
|
|
473
468
|
const stateDir = resolveStateDir(startParams, run);
|
|
474
469
|
const values = {
|
|
475
470
|
...(startParams.values || {}),
|
|
476
|
-
actor_address: `run:${run}`,
|
|
477
|
-
communication_file: join(stateDir, "communication.json"),
|
|
478
|
-
default_room: `room:${run}`,
|
|
479
471
|
run_id: run,
|
|
480
472
|
state_dir: stateDir,
|
|
473
|
+
trace_file: join(stateDir, "trace.jsonl"),
|
|
481
474
|
};
|
|
482
475
|
const modelPolicy = describeCurrentPolicyProvenance({
|
|
483
476
|
defaults: startParams.defaults,
|
|
@@ -552,6 +545,7 @@ export function startRun(
|
|
|
552
545
|
run,
|
|
553
546
|
run_instance_id: randomUUID(),
|
|
554
547
|
state_dir: stateDir,
|
|
548
|
+
state_schema: "run-kernel-v1",
|
|
555
549
|
status: "running",
|
|
556
550
|
...(startParams.tool ? { tool: startParams.tool } : {}),
|
|
557
551
|
template: resolved.template,
|
|
@@ -559,7 +553,9 @@ export function startRun(
|
|
|
559
553
|
model_policy: modelPolicy,
|
|
560
554
|
...(artifacts ? { artifacts } : {}),
|
|
561
555
|
...(startParams.control ? { control: startParams.control } : {}),
|
|
562
|
-
...(startParams.
|
|
556
|
+
...(startParams.control_endpoint
|
|
557
|
+
? { control_endpoint: startParams.control_endpoint }
|
|
558
|
+
: {}),
|
|
563
559
|
...(startParams.notification_policy === "silent"
|
|
564
560
|
? { notification_policy: "silent" as const }
|
|
565
561
|
: {}),
|
|
@@ -596,11 +592,14 @@ export function startRun(
|
|
|
596
592
|
);
|
|
597
593
|
if (processIdentity) meta.process_identity = processIdentity;
|
|
598
594
|
writeJsonAtomic(join(stateDir, "run.json"), meta);
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
595
|
+
appendRunTraceEvent(stateDir, {
|
|
596
|
+
kind: "run.start",
|
|
597
|
+
summary: `Run ${run} started`,
|
|
598
|
+
data: {
|
|
599
|
+
pid: meta.pid,
|
|
600
|
+
run_instance_id: meta.run_instance_id,
|
|
601
|
+
},
|
|
602
|
+
});
|
|
604
603
|
child.unref();
|
|
605
604
|
return meta;
|
|
606
605
|
} finally {
|
|
@@ -608,13 +607,6 @@ export function startRun(
|
|
|
608
607
|
}
|
|
609
608
|
}
|
|
610
609
|
|
|
611
|
-
export { parseRunOutboxEventLine } from "./runs-outbox.ts";
|
|
612
|
-
export type {
|
|
613
|
-
RunOutboxDelivery,
|
|
614
|
-
RunOutboxEvent,
|
|
615
|
-
RunOutboxLevel,
|
|
616
|
-
} from "./runs-outbox.ts";
|
|
617
|
-
|
|
618
610
|
function resolveRunStateDir(runOrDir: string): string {
|
|
619
611
|
return resolve(
|
|
620
612
|
/[\\/]/u.test(runOrDir)
|
|
@@ -714,21 +706,19 @@ export function teardownRunsOwnedByParent(
|
|
|
714
706
|
) {
|
|
715
707
|
throw new Error("run generation changed before teardown evidence");
|
|
716
708
|
}
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
ownerId: attempt.ownerId,
|
|
709
|
+
appendRunTraceEvent(attempt.stateDir, {
|
|
710
|
+
kind: "run.parent_teardown",
|
|
711
|
+
summary: `Parent teardown ${attempt.outcome}`,
|
|
712
|
+
data: {
|
|
722
713
|
outcome: attempt.outcome,
|
|
714
|
+
owner_id: attempt.ownerId,
|
|
723
715
|
reason: attempt.reason,
|
|
724
716
|
run: attempt.run,
|
|
725
717
|
run_instance_id: attempt.runInstanceId,
|
|
726
|
-
type: "control.kill",
|
|
727
718
|
trigger: options.trigger ?? "parent_shutdown",
|
|
728
|
-
|
|
729
|
-
})
|
|
730
|
-
|
|
731
|
-
);
|
|
719
|
+
},
|
|
720
|
+
...(attempt.outcome === "failed" ? { level: "error" as const } : {}),
|
|
721
|
+
});
|
|
732
722
|
},
|
|
733
723
|
});
|
|
734
724
|
const summaryPath = join(
|
|
@@ -764,139 +754,63 @@ export function teardownRunsOwnedByParent(
|
|
|
764
754
|
export function tailRun(runOrDir: string, lines = 40): string {
|
|
765
755
|
const status = getRunStatus(runOrDir);
|
|
766
756
|
const stateDir = String(status.state_dir);
|
|
767
|
-
const
|
|
768
|
-
if (
|
|
757
|
+
const trace = tailFile(join(stateDir, "trace.jsonl"), lines);
|
|
758
|
+
if (trace) return trace;
|
|
769
759
|
return (
|
|
770
760
|
tailFile(join(stateDir, "stdout.log"), lines) ||
|
|
771
761
|
tailFile(join(stateDir, "stderr.log"), lines)
|
|
772
762
|
);
|
|
773
763
|
}
|
|
774
764
|
|
|
775
|
-
export function readRunEvents(runOrDir: string, lines = 40): RunOutboxEvent[] {
|
|
776
|
-
const status = getRunStatus(runOrDir);
|
|
777
|
-
const stateDir = String(status.state_dir);
|
|
778
|
-
const run = String(status.run ?? runOrDir);
|
|
779
|
-
return tailLines(join(stateDir, "outbox.jsonl"), lines)
|
|
780
|
-
.map((line, index) => parseRunOutboxEventLine(line, run, stateDir, index))
|
|
781
|
-
.filter((event): event is RunOutboxEvent => Boolean(event));
|
|
782
|
-
}
|
|
783
|
-
|
|
784
765
|
export type {
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
} from "./runs-mailbox.ts";
|
|
789
|
-
|
|
790
|
-
export function readRunInboxMessages(
|
|
791
|
-
runOrDir: string,
|
|
792
|
-
lines = 40,
|
|
793
|
-
): RunInboxMessage[] {
|
|
794
|
-
const status = getRunStatus(runOrDir);
|
|
795
|
-
const stateDir = String(status.state_dir);
|
|
796
|
-
return tailLines(runInboxFile(stateDir), lines)
|
|
797
|
-
.map(parseRunInboxLine)
|
|
798
|
-
.filter((message): message is RunInboxMessage => Boolean(message));
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
export function updateRunInboxMessageStatus(
|
|
802
|
-
runOrDir: string,
|
|
803
|
-
id: string,
|
|
804
|
-
nextStatus: RunInboxStatus,
|
|
805
|
-
metadata: Record<string, unknown> = {},
|
|
806
|
-
): boolean {
|
|
807
|
-
const status = getRunStatus(runOrDir);
|
|
808
|
-
const stateDir = String(status.state_dir);
|
|
809
|
-
return updateRunInboxMessageStatusInStateDir(
|
|
810
|
-
stateDir,
|
|
811
|
-
id,
|
|
812
|
-
nextStatus,
|
|
813
|
-
metadata,
|
|
814
|
-
);
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
export function claimRunInboxMessage(
|
|
818
|
-
runOrDir: string,
|
|
819
|
-
owner = "runtime",
|
|
820
|
-
statuses: string[] = ["queued"],
|
|
821
|
-
): RunInboxMessage | undefined {
|
|
822
|
-
const status = getRunStatus(runOrDir);
|
|
823
|
-
const stateDir = String(status.state_dir);
|
|
824
|
-
return claimRunInboxMessageInStateDir(stateDir, owner, statuses);
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
export async function processRunInboxMessages(
|
|
828
|
-
runOrDir: string,
|
|
829
|
-
handler: (message: RunInboxMessage) => Promise<void> | void,
|
|
830
|
-
options: { limit?: number; owner?: string; statuses?: string[] } = {},
|
|
831
|
-
): Promise<ProcessRunInboxResult> {
|
|
832
|
-
const status = getRunStatus(runOrDir);
|
|
833
|
-
return processRunInboxMessagesInStateDir(
|
|
834
|
-
String(status.state_dir),
|
|
835
|
-
handler,
|
|
836
|
-
options,
|
|
837
|
-
);
|
|
838
|
-
}
|
|
766
|
+
DeliverRunControlOptions,
|
|
767
|
+
DeliverRunControlRequest,
|
|
768
|
+
} from "./runs-control-delivery.ts";
|
|
839
769
|
|
|
840
|
-
export
|
|
841
|
-
|
|
842
|
-
event: {
|
|
843
|
-
body?: unknown;
|
|
844
|
-
correlation_id?: string;
|
|
845
|
-
data?: unknown;
|
|
846
|
-
delivery?: string;
|
|
847
|
-
event?: string;
|
|
848
|
-
from?: string;
|
|
849
|
-
level?: string;
|
|
850
|
-
metadata?: Record<string, unknown>;
|
|
851
|
-
reply_to?: string;
|
|
852
|
-
summary?: string;
|
|
853
|
-
to?: string;
|
|
854
|
-
type?: string;
|
|
855
|
-
},
|
|
856
|
-
): Record<string, unknown> {
|
|
857
|
-
const status = getRunStatus(runOrDir);
|
|
858
|
-
const stateDir = String(status.state_dir);
|
|
859
|
-
const run = String(status.run ?? runOrDir);
|
|
860
|
-
const payload = buildRunOutboxEventPayload(run, event);
|
|
861
|
-
const line = `${JSON.stringify(payload)}\n`;
|
|
862
|
-
writeFileSync(join(stateDir, "outbox.jsonl"), line, { flag: "a" });
|
|
863
|
-
return {
|
|
864
|
-
bytes: Buffer.byteLength(line),
|
|
865
|
-
outbox: "outbox.jsonl",
|
|
866
|
-
run,
|
|
867
|
-
sent: true,
|
|
868
|
-
state_dir: stateDir,
|
|
869
|
-
};
|
|
770
|
+
export interface SendRunControlOptions extends DeliverRunControlOptions {
|
|
771
|
+
ownerId?: string;
|
|
870
772
|
}
|
|
871
773
|
|
|
872
|
-
export
|
|
873
|
-
|
|
874
|
-
export async function sendRunMessage(
|
|
774
|
+
export async function sendRunControl(
|
|
875
775
|
runOrDir: string,
|
|
876
|
-
|
|
877
|
-
options:
|
|
776
|
+
request: DeliverRunControlRequest,
|
|
777
|
+
options: SendRunControlOptions = {},
|
|
878
778
|
): Promise<Record<string, unknown>> {
|
|
879
|
-
const
|
|
880
|
-
const
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
if (
|
|
889
|
-
identity.status === "owner_mismatch" ||
|
|
890
|
-
identity.status === "unsupported_proof"
|
|
891
|
-
) {
|
|
892
|
-
throw new Error(`Run process identity ${identity.status}: ${run}`);
|
|
779
|
+
const stateDir = resolveRunStateDir(runOrDir);
|
|
780
|
+
const releaseControlLock = RunsStart.acquireStateStartLock(stateDir);
|
|
781
|
+
try {
|
|
782
|
+
const status = getRunStatus(stateDir);
|
|
783
|
+
const run = String(status.run ?? runOrDir);
|
|
784
|
+
if (options.ownerId !== undefined && status.ownerId !== options.ownerId) {
|
|
785
|
+
throw Object.assign(new Error(`Run ownership changed: ${run}`), {
|
|
786
|
+
reason: "owner_mismatch",
|
|
787
|
+
});
|
|
893
788
|
}
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
789
|
+
if (status.run_instance_id !== request.run_instance_id) {
|
|
790
|
+
throw Object.assign(new Error(`Run generation changed: ${run}`), {
|
|
791
|
+
reason: "generation_mismatch",
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
if (status.status !== "running") {
|
|
795
|
+
throw Object.assign(new Error(`Run is not running: ${run}`), {
|
|
796
|
+
reason: "terminal_state",
|
|
797
|
+
});
|
|
798
|
+
}
|
|
799
|
+
const pid = Number(status.pid || 0);
|
|
800
|
+
const identity = verifyRunProcessIdentity(
|
|
801
|
+
pid,
|
|
802
|
+
status.process_identity as RunProcessIdentity | undefined,
|
|
803
|
+
);
|
|
804
|
+
if (!identity.valid) {
|
|
805
|
+
throw Object.assign(
|
|
806
|
+
new Error(`Run process identity ${identity.status}: ${run}`),
|
|
807
|
+
{ reason: `process_identity_${identity.status}` },
|
|
808
|
+
);
|
|
809
|
+
}
|
|
810
|
+
return await deliverRunControl(run, stateDir, request, options);
|
|
811
|
+
} finally {
|
|
812
|
+
releaseControlLock();
|
|
898
813
|
}
|
|
899
|
-
return deliverRunMessage(status, run, stateDir, message, options);
|
|
900
814
|
}
|
|
901
815
|
|
|
902
816
|
export { getRunProcessSignalPlan } from "./runs-control.ts";
|
|
@@ -917,13 +831,13 @@ function markTerminalProgress(
|
|
|
917
831
|
);
|
|
918
832
|
}
|
|
919
833
|
|
|
920
|
-
function
|
|
834
|
+
function finalizeInterruptedExecution(
|
|
921
835
|
stateDir: string,
|
|
922
836
|
phase: "cancelled" | "killed",
|
|
923
837
|
signal: NodeJS.Signals,
|
|
924
838
|
): void {
|
|
925
|
-
const
|
|
926
|
-
const manifest = readJson(
|
|
839
|
+
const executionPath = join(stateDir, "execution.json");
|
|
840
|
+
const manifest = readJson(executionPath);
|
|
927
841
|
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) return;
|
|
928
842
|
const record = manifest as Record<string, unknown>;
|
|
929
843
|
if (!Array.isArray(record.commands)) return;
|
|
@@ -969,7 +883,7 @@ function finalizeInterruptedReviewEvidence(
|
|
|
969
883
|
: {}),
|
|
970
884
|
};
|
|
971
885
|
});
|
|
972
|
-
writeJsonAtomic(
|
|
886
|
+
writeJsonAtomic(executionPath, {
|
|
973
887
|
...record,
|
|
974
888
|
status: phase,
|
|
975
889
|
commands,
|
|
@@ -1006,9 +920,37 @@ function stopRun(
|
|
|
1006
920
|
) {
|
|
1007
921
|
return { stopped: false, reason: "run generation changed", status };
|
|
1008
922
|
}
|
|
923
|
+
const control =
|
|
924
|
+
event === "run.kill" && typeof status.run_instance_id === "string"
|
|
925
|
+
? appendRunControlInStateDir(stateDir, {
|
|
926
|
+
action: "kill",
|
|
927
|
+
run_instance_id: status.run_instance_id,
|
|
928
|
+
})
|
|
929
|
+
: undefined;
|
|
930
|
+
if (control) {
|
|
931
|
+
updateRunControlStatusInStateDir(
|
|
932
|
+
stateDir,
|
|
933
|
+
control.id,
|
|
934
|
+
"claimed",
|
|
935
|
+
{},
|
|
936
|
+
["queued"],
|
|
937
|
+
);
|
|
938
|
+
}
|
|
939
|
+
const finish = (result: Record<string, unknown>): Record<string, unknown> => {
|
|
940
|
+
if (!control) return result;
|
|
941
|
+
const handled = result.stopped === true;
|
|
942
|
+
updateRunControlStatusInStateDir(
|
|
943
|
+
stateDir,
|
|
944
|
+
control.id,
|
|
945
|
+
handled ? "handled" : "failed",
|
|
946
|
+
handled ? {} : { error: String(result.reason ?? "kill rejected") },
|
|
947
|
+
["claimed"],
|
|
948
|
+
);
|
|
949
|
+
return { ...result, control_id: control.id };
|
|
950
|
+
};
|
|
1009
951
|
const pid = Number(status.pid || 0);
|
|
1010
952
|
if (status.status !== "running" && status.status !== "exited") {
|
|
1011
|
-
return { stopped: false, reason: "not running", status };
|
|
953
|
+
return finish({ stopped: false, reason: "not running", status });
|
|
1012
954
|
}
|
|
1013
955
|
const identity = verifyRunProcessIdentity(
|
|
1014
956
|
pid,
|
|
@@ -1019,43 +961,59 @@ function stopRun(
|
|
|
1019
961
|
identity.status === "owner_mismatch" ||
|
|
1020
962
|
identity.status === "unsupported_proof"
|
|
1021
963
|
) {
|
|
1022
|
-
return {
|
|
964
|
+
return finish({
|
|
1023
965
|
stopped: false,
|
|
1024
966
|
reason: identity.status.replaceAll("_", " "),
|
|
1025
967
|
process_identity_status: identity.status,
|
|
1026
968
|
status,
|
|
1027
|
-
};
|
|
969
|
+
});
|
|
1028
970
|
}
|
|
1029
|
-
return { stopped: false, reason: "not running", status };
|
|
971
|
+
return finish({ stopped: false, reason: "not running", status });
|
|
1030
972
|
}
|
|
1031
973
|
if (!identity.valid) {
|
|
1032
|
-
return {
|
|
974
|
+
return finish({
|
|
1033
975
|
stopped: false,
|
|
1034
976
|
reason: identity.status.replaceAll("_", " "),
|
|
1035
977
|
process_identity_status: identity.status,
|
|
1036
978
|
status,
|
|
1037
|
-
};
|
|
979
|
+
});
|
|
1038
980
|
}
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
981
|
+
let signalResult: RunProcessSignalPlan;
|
|
982
|
+
try {
|
|
983
|
+
signalResult = signalOwnedRunProcess(
|
|
984
|
+
pid,
|
|
985
|
+
signal,
|
|
986
|
+
status.process_identity as RunProcessIdentity,
|
|
987
|
+
);
|
|
988
|
+
} catch (error) {
|
|
989
|
+
if (control) {
|
|
990
|
+
updateRunControlStatusInStateDir(stateDir, control.id, "failed", {
|
|
991
|
+
error: error instanceof Error ? error.message : String(error),
|
|
992
|
+
});
|
|
993
|
+
}
|
|
994
|
+
throw error;
|
|
995
|
+
}
|
|
996
|
+
appendRunTraceEvent(stateDir, {
|
|
997
|
+
kind: event,
|
|
998
|
+
summary: `${event === "run.kill" ? "Killed" : "Cancelled"} Run process`,
|
|
999
|
+
data: { pid, signal, ...signalResult },
|
|
1000
|
+
});
|
|
1049
1001
|
markTerminalHandled(stateDir, { event, signal });
|
|
1050
1002
|
if (event === "run.kill") {
|
|
1051
|
-
|
|
1003
|
+
finalizeInterruptedExecution(stateDir, "killed", signal);
|
|
1052
1004
|
markTerminalProgress(stateDir, "killed");
|
|
1053
1005
|
}
|
|
1054
1006
|
if (event === "run.cancel") {
|
|
1055
|
-
|
|
1007
|
+
finalizeInterruptedExecution(stateDir, "cancelled", signal);
|
|
1056
1008
|
markTerminalProgress(stateDir, "cancelled");
|
|
1057
1009
|
}
|
|
1058
|
-
return {
|
|
1010
|
+
return finish({
|
|
1011
|
+
stopped: true,
|
|
1012
|
+
pid,
|
|
1013
|
+
signal,
|
|
1014
|
+
...signalResult,
|
|
1015
|
+
state_dir: stateDir,
|
|
1016
|
+
});
|
|
1059
1017
|
} finally {
|
|
1060
1018
|
releaseControlLock();
|
|
1061
1019
|
}
|
|
@@ -1100,23 +1058,66 @@ export function cancelRun(
|
|
|
1100
1058
|
: result;
|
|
1101
1059
|
}
|
|
1102
1060
|
|
|
1103
|
-
function
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1061
|
+
function retainRun(
|
|
1062
|
+
runOrDir: string,
|
|
1063
|
+
action: RunRetentionAction,
|
|
1064
|
+
expected: RunControlExpectation,
|
|
1065
|
+
options: { preserveArtifacts?: boolean } = {},
|
|
1066
|
+
): Record<string, unknown> {
|
|
1067
|
+
const stateDir = resolveRunStateDir(runOrDir);
|
|
1068
|
+
const releaseControlLock = RunsStart.acquireStateStartLock(stateDir);
|
|
1069
|
+
let status: Record<string, unknown> | undefined;
|
|
1070
|
+
let evidenceId: string | undefined;
|
|
1071
|
+
try {
|
|
1072
|
+
expected.onLocked?.();
|
|
1073
|
+
status = getRunStatus(stateDir);
|
|
1074
|
+
if (expected.ownerId !== undefined && status.ownerId !== expected.ownerId) {
|
|
1075
|
+
return { [`${action}d`]: false, reason: "ownership changed", status };
|
|
1076
|
+
}
|
|
1077
|
+
if (
|
|
1078
|
+
expected.runInstanceId !== undefined &&
|
|
1079
|
+
status.run_instance_id !== expected.runInstanceId
|
|
1080
|
+
) {
|
|
1081
|
+
return { [`${action}d`]: false, reason: "run generation changed", status };
|
|
1082
|
+
}
|
|
1083
|
+
if (status.status === "running") {
|
|
1084
|
+
throw new Error("Only terminal runs can be archived or pruned.");
|
|
1085
|
+
}
|
|
1086
|
+
evidenceId = appendRunRetentionEvidence(status, action, "queued");
|
|
1087
|
+
const result = action === "archive"
|
|
1088
|
+
? archiveTerminalRun(status)
|
|
1089
|
+
: pruneTerminalRun(status, options);
|
|
1090
|
+
appendRunRetentionEvidence(status, action, "handled", {
|
|
1091
|
+
id: evidenceId,
|
|
1092
|
+
result,
|
|
1093
|
+
});
|
|
1094
|
+
return { ...result, retention_id: evidenceId };
|
|
1095
|
+
} catch (error) {
|
|
1096
|
+
if (status && evidenceId) {
|
|
1097
|
+
appendRunRetentionEvidence(status, action, "failed", {
|
|
1098
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1099
|
+
id: evidenceId,
|
|
1100
|
+
});
|
|
1101
|
+
}
|
|
1102
|
+
throw error;
|
|
1103
|
+
} finally {
|
|
1104
|
+
releaseControlLock();
|
|
1107
1105
|
}
|
|
1108
|
-
return status;
|
|
1109
1106
|
}
|
|
1110
1107
|
|
|
1111
|
-
export function archiveRun(
|
|
1112
|
-
|
|
1108
|
+
export function archiveRun(
|
|
1109
|
+
runOrDir: string,
|
|
1110
|
+
expected: RunControlExpectation = {},
|
|
1111
|
+
): Record<string, unknown> {
|
|
1112
|
+
return retainRun(runOrDir, "archive", expected);
|
|
1113
1113
|
}
|
|
1114
1114
|
|
|
1115
1115
|
export function pruneRun(
|
|
1116
1116
|
runOrDir: string,
|
|
1117
1117
|
options: { preserveArtifacts?: boolean } = {},
|
|
1118
|
+
expected: RunControlExpectation = {},
|
|
1118
1119
|
): Record<string, unknown> {
|
|
1119
|
-
return
|
|
1120
|
+
return retainRun(runOrDir, "prune", expected, options);
|
|
1120
1121
|
}
|
|
1121
1122
|
|
|
1122
1123
|
export function killRun(
|
package/lib/control.ts
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run Control request contract.
|
|
3
|
+
* Zones: public request validation, Run/runtime target normalization, input bounds
|
|
4
|
+
* Owns pure Control validation; journaling, delivery, and lifecycle mutation stay in adapters.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as Limits from "./limits.ts";
|
|
8
|
+
|
|
9
|
+
export interface ControlRequest {
|
|
10
|
+
target: `run:${string}` | "runtime";
|
|
11
|
+
action: string;
|
|
12
|
+
input?: unknown;
|
|
13
|
+
verbose?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const RUN_ID_PATTERN = /^[A-Za-z0-9_.-]+$/;
|
|
17
|
+
const ACTION_PATTERN = /^[a-z][a-z0-9_-]*(?:\.[a-z0-9_-]+)*$/;
|
|
18
|
+
const CONTROL_FIELDS = new Set(["action", "input", "target", "verbose"]);
|
|
19
|
+
const REMOVED_FIELDS = new Set([
|
|
20
|
+
"body",
|
|
21
|
+
"correlation_id",
|
|
22
|
+
"from",
|
|
23
|
+
"metadata",
|
|
24
|
+
"reply_to",
|
|
25
|
+
"summary",
|
|
26
|
+
"to",
|
|
27
|
+
"type",
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
function serializedInputBytes(input: unknown): number {
|
|
31
|
+
let serialized: string | undefined;
|
|
32
|
+
try {
|
|
33
|
+
serialized = JSON.stringify(input);
|
|
34
|
+
} catch {
|
|
35
|
+
throw new Error("control.input must be JSON-serializable");
|
|
36
|
+
}
|
|
37
|
+
if (serialized === undefined) {
|
|
38
|
+
throw new Error("control.input must be JSON-serializable");
|
|
39
|
+
}
|
|
40
|
+
return Buffer.byteLength(serialized);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function normalizeTarget(value: unknown): ControlRequest["target"] {
|
|
44
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
45
|
+
throw new Error("control.target is required");
|
|
46
|
+
}
|
|
47
|
+
const target = value.trim();
|
|
48
|
+
if (target === "runtime") return target;
|
|
49
|
+
if (!target.startsWith("run:")) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
`unsupported control target: ${target}; use run:<id> or runtime`,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
const run = target.slice(4);
|
|
55
|
+
if (!RUN_ID_PATTERN.test(run)) {
|
|
56
|
+
throw new Error(`invalid control Run target: ${target}`);
|
|
57
|
+
}
|
|
58
|
+
return `run:${run}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function normalizeControlRequest(input: unknown): ControlRequest {
|
|
62
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) {
|
|
63
|
+
throw new Error("control request must be an object");
|
|
64
|
+
}
|
|
65
|
+
const record = input as Record<string, unknown>;
|
|
66
|
+
const fields = Object.keys(record);
|
|
67
|
+
const removed = fields.filter((field) => REMOVED_FIELDS.has(field));
|
|
68
|
+
if (removed.length > 0) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
`actor-message fields are removed: ${removed.sort().join(", ")}; use target, action, input, verbose`,
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
const unknown = fields.filter((field) => !CONTROL_FIELDS.has(field));
|
|
74
|
+
if (unknown.length > 0) {
|
|
75
|
+
throw new Error(`unsupported control fields: ${unknown.sort().join(", ")}`);
|
|
76
|
+
}
|
|
77
|
+
const target = normalizeTarget(record.target);
|
|
78
|
+
if (typeof record.action !== "string" || !record.action.trim()) {
|
|
79
|
+
throw new Error("control.action is required");
|
|
80
|
+
}
|
|
81
|
+
const action = record.action.trim();
|
|
82
|
+
if (!ACTION_PATTERN.test(action)) {
|
|
83
|
+
throw new Error(`invalid control action: ${action}`);
|
|
84
|
+
}
|
|
85
|
+
if (record.verbose !== undefined && typeof record.verbose !== "boolean") {
|
|
86
|
+
throw new Error("control.verbose must be a boolean");
|
|
87
|
+
}
|
|
88
|
+
if (
|
|
89
|
+
record.input !== undefined &&
|
|
90
|
+
serializedInputBytes(record.input) > Limits.CONTROL_INPUT_MAX_BYTES
|
|
91
|
+
) {
|
|
92
|
+
throw new Error(
|
|
93
|
+
`control.input exceeds ${Limits.CONTROL_INPUT_MAX_BYTES} bytes`,
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
target,
|
|
98
|
+
action,
|
|
99
|
+
...(record.input !== undefined ? { input: record.input } : {}),
|
|
100
|
+
...(record.verbose !== undefined ? { verbose: record.verbose } : {}),
|
|
101
|
+
};
|
|
102
|
+
}
|