@llblab/pi-actors 0.42.3 → 0.43.1
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 +127 -175
- package/BACKLOG.md +189 -1
- package/CHANGELOG.md +183 -292
- package/README.md +115 -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/index.js +1 -1
- package/dist/lib/async-runs.d.ts +15 -38
- package/dist/lib/async-runs.js +173 -111
- package/dist/lib/automatic-review-runtime.d.ts +1 -1
- package/dist/lib/automatic-review-runtime.js +5 -5
- package/dist/lib/command-templates.d.ts +2 -0
- package/dist/lib/command-templates.js +38 -4
- package/dist/lib/control-projection.d.ts +20 -0
- package/dist/lib/control-projection.js +66 -0
- package/dist/lib/control.d.ts +15 -0
- package/dist/lib/control.js +97 -0
- package/dist/lib/draft-sleep.js +3 -3
- package/dist/lib/execution-sessions.d.ts +17 -0
- package/dist/lib/execution-sessions.js +85 -0
- package/dist/lib/file-state.d.ts +2 -0
- package/dist/lib/file-state.js +114 -46
- 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 +54 -70
- package/dist/lib/inspector-overlay.js +576 -910
- package/dist/lib/inspector.d.ts +3 -71
- package/dist/lib/inspector.js +19 -665
- package/dist/lib/limits.d.ts +7 -3
- package/dist/lib/limits.js +7 -3
- 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 +3 -3
- package/dist/lib/recipe-control.d.ts +7 -0
- package/dist/lib/recipe-control.js +43 -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-control.d.ts +1 -1
- package/dist/lib/review-control.js +4 -5
- 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 +28 -0
- package/dist/lib/runs-control-delivery.js +150 -0
- package/dist/lib/runs-controls.d.ts +37 -0
- package/dist/lib/runs-controls.js +146 -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 +102 -0
- package/dist/lib/runtime-identity.d.ts +7 -0
- package/dist/lib/runtime-identity.js +35 -0
- package/dist/lib/runtime-notifier.d.ts +1 -1
- package/dist/lib/runtime-notifier.js +1 -1
- package/dist/lib/runtime-triage.d.ts +29 -0
- package/dist/lib/runtime-triage.js +76 -0
- package/dist/lib/tool-review-scheduler.js +7 -7
- package/dist/lib/tools-inspect.d.ts +3 -3
- package/dist/lib/tools-inspect.js +241 -707
- package/dist/lib/tools-local.js +2 -10
- package/dist/lib/tools-message.d.ts +6 -7
- package/dist/lib/tools-message.js +95 -396
- package/dist/lib/tools-response.d.ts +1 -5
- package/dist/lib/tools-response.js +5 -48
- package/dist/lib/tools-spawn.js +16 -28
- package/dist/lib/tools.d.ts +1 -1
- package/dist/lib/tools.js +2 -3
- package/dist/lib/trace-projection.d.ts +22 -0
- package/dist/lib/trace-projection.js +185 -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 +196 -69
- package/dist/scripts/music-player.mjs +162 -159
- package/dist/scripts/recipe-utils.mjs +6 -96
- package/dist/scripts/release-gates.mjs +91 -0
- package/dist/scripts/validate-recipe.mjs +8 -57
- package/dist/skills/actors/SKILL.md +57 -265
- package/dist/skills/swarm/SKILL.md +10 -34
- package/docs/0.43-baseline.md +39 -0
- package/docs/README.md +4 -6
- package/docs/actor-inspector.md +26 -64
- package/docs/async-runs.md +81 -328
- package/docs/command-templates.md +8 -118
- package/docs/recipe-library.md +55 -182
- package/docs/releasing.md +28 -0
- 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/index.ts +1 -1
- package/lib/async-runs.ts +218 -204
- package/lib/automatic-review-runtime.ts +7 -7
- package/lib/command-templates.ts +44 -4
- package/lib/control-projection.ts +105 -0
- package/lib/control.ts +117 -0
- package/lib/draft-sleep.ts +3 -3
- package/lib/execution-sessions.ts +111 -0
- package/lib/file-state.ts +84 -64
- package/lib/inspector-actions.ts +2 -2
- package/lib/inspector-command.ts +3 -3
- package/lib/inspector-overlay.ts +617 -1126
- package/lib/inspector.ts +46 -979
- package/lib/limits.ts +7 -3
- package/lib/observability.ts +60 -101
- package/lib/prompts.ts +3 -3
- package/lib/recipe-control.ts +52 -0
- package/lib/recipes-discovery.ts +2 -0
- package/lib/recipes-references.ts +9 -45
- package/lib/review-control.ts +4 -5
- package/lib/review-projection.ts +1 -5
- package/lib/run-ui-runtime.ts +2 -2
- package/lib/runs-control-delivery.ts +209 -0
- package/lib/runs-controls.ts +213 -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 +136 -0
- package/lib/runtime-identity.ts +39 -0
- package/lib/runtime-notifier.ts +1 -1
- package/lib/runtime-triage.ts +120 -0
- package/lib/tool-review-scheduler.ts +7 -7
- package/lib/tools-inspect.ts +283 -900
- package/lib/tools-local.ts +2 -12
- package/lib/tools-message.ts +112 -520
- package/lib/tools-response.ts +5 -64
- package/lib/tools-spawn.ts +16 -32
- package/lib/tools.ts +5 -6
- package/lib/trace-projection.ts +244 -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 +196 -69
- package/scripts/music-player.mjs +162 -159
- package/scripts/recipe-utils.mjs +6 -96
- package/scripts/release-gates.mjs +91 -0
- package/scripts/validate-recipe.mjs +8 -57
- package/skills/actors/SKILL.md +57 -265
- package/skills/swarm/SKILL.md +10 -34
- 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/docs/actors-deep-reference.md +0 -66
- package/docs/component-recipes.md +0 -148
- package/docs/task-first-recipes.md +0 -263
- 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,35 @@ 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
|
+
import * as RuntimeIdentity from "./runtime-identity.ts";
|
|
62
63
|
import * as RunsStart from "./runs-start.ts";
|
|
64
|
+
import { appendRunTraceEvent } from "./runs-trace.ts";
|
|
63
65
|
import * as RunsIndex from "./runs-index.ts";
|
|
64
66
|
import * as RunsParentTeardown from "./runs-parent-teardown.ts";
|
|
65
67
|
import {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
readRunInboxMessagesFromStateDir,
|
|
70
|
-
runInboxFile,
|
|
71
|
-
type ProcessRunInboxResult,
|
|
72
|
-
type RunInboxMessage,
|
|
73
|
-
type RunInboxStatus,
|
|
74
|
-
updateRunInboxMessageStatusInStateDir,
|
|
75
|
-
} from "./runs-mailbox.ts";
|
|
68
|
+
appendRunControlInStateDir,
|
|
69
|
+
updateRunControlStatusInStateDir,
|
|
70
|
+
} from "./runs-controls.ts";
|
|
76
71
|
import {
|
|
77
|
-
|
|
78
|
-
type
|
|
79
|
-
|
|
72
|
+
deliverRunControl,
|
|
73
|
+
type DeliverRunControlOptions,
|
|
74
|
+
type DeliverRunControlRequest,
|
|
75
|
+
} from "./runs-control-delivery.ts";
|
|
80
76
|
import {
|
|
81
77
|
buildRunStatus,
|
|
82
78
|
tailFile,
|
|
83
|
-
tailLines,
|
|
84
79
|
type AsyncRunStatus,
|
|
85
80
|
} from "./runs-status.ts";
|
|
86
81
|
import { readJsonFileResilient } from "./state-readers.ts";
|
|
@@ -91,7 +86,7 @@ export type AsyncRunLaunchSource = "spawn" | "tool";
|
|
|
91
86
|
|
|
92
87
|
export interface AsyncRunControlEndpoint {
|
|
93
88
|
path: string;
|
|
94
|
-
type: "fifo" | "
|
|
89
|
+
type: "fifo" | "named-pipe";
|
|
95
90
|
}
|
|
96
91
|
|
|
97
92
|
export function normalizeRunTransportContext(
|
|
@@ -119,7 +114,7 @@ export function normalizeRunTransportContext(
|
|
|
119
114
|
|
|
120
115
|
export interface AsyncRunStartParams {
|
|
121
116
|
async?: boolean;
|
|
122
|
-
|
|
117
|
+
control_endpoint?: AsyncRunControlEndpoint;
|
|
123
118
|
file?: string;
|
|
124
119
|
launch_source?: AsyncRunLaunchSource;
|
|
125
120
|
lifecycleHooks?: {
|
|
@@ -147,7 +142,7 @@ export interface AsyncRunStartParams {
|
|
|
147
142
|
accept_output?: "review_evidence";
|
|
148
143
|
output?: string;
|
|
149
144
|
artifacts?: Record<string, RunArtifactDeclaration>;
|
|
150
|
-
|
|
145
|
+
control?: string[];
|
|
151
146
|
notification_policy?: "normal" | "silent";
|
|
152
147
|
retire_when?: "children_terminal";
|
|
153
148
|
retry?: number | string;
|
|
@@ -179,13 +174,14 @@ export interface AsyncRunMeta {
|
|
|
179
174
|
run: string;
|
|
180
175
|
run_instance_id: string;
|
|
181
176
|
state_dir: string;
|
|
177
|
+
state_schema: typeof RuntimeIdentity.RUN_STATE_SCHEMA;
|
|
182
178
|
status: AsyncRunStatus;
|
|
183
179
|
tool?: string;
|
|
184
180
|
template: CommandTemplateValue;
|
|
185
181
|
values: Record<string, unknown>;
|
|
186
182
|
artifacts?: Record<string, RunArtifactDeclaration>;
|
|
187
|
-
control?:
|
|
188
|
-
|
|
183
|
+
control?: string[];
|
|
184
|
+
control_endpoint?: AsyncRunControlEndpoint;
|
|
189
185
|
model_policy?: CurrentPolicyProvenance;
|
|
190
186
|
notification_policy?: "normal" | "silent";
|
|
191
187
|
process_identity?: RunProcessIdentity;
|
|
@@ -471,13 +467,26 @@ export function startRun(
|
|
|
471
467
|
const resolved = resolveRunTemplate(startParams);
|
|
472
468
|
const run = safeRunId(startParams.run_id);
|
|
473
469
|
const stateDir = resolveStateDir(startParams, run);
|
|
470
|
+
const recipeFile = startParams.file
|
|
471
|
+
? resolveRecipeFile(startParams.file)
|
|
472
|
+
: undefined;
|
|
473
|
+
const packagedRecipeRoot = resolve(Paths.getPackagedRecipeRoot());
|
|
474
|
+
const recipeRelation = recipeFile
|
|
475
|
+
? relative(packagedRecipeRoot, recipeFile)
|
|
476
|
+
: undefined;
|
|
477
|
+
const packagedRepo =
|
|
478
|
+
startParams.defaults?.repo === "~/.pi/agent/extensions/pi-actors" &&
|
|
479
|
+
recipeRelation &&
|
|
480
|
+
!recipeRelation.startsWith("..") &&
|
|
481
|
+
!isAbsolute(recipeRelation)
|
|
482
|
+
? dirname(packagedRecipeRoot)
|
|
483
|
+
: undefined;
|
|
474
484
|
const values = {
|
|
485
|
+
...(packagedRepo ? { repo: packagedRepo } : {}),
|
|
475
486
|
...(startParams.values || {}),
|
|
476
|
-
actor_address: `run:${run}`,
|
|
477
|
-
communication_file: join(stateDir, "communication.json"),
|
|
478
|
-
default_room: `room:${run}`,
|
|
479
487
|
run_id: run,
|
|
480
488
|
state_dir: stateDir,
|
|
489
|
+
trace_file: join(stateDir, "trace.jsonl"),
|
|
481
490
|
};
|
|
482
491
|
const modelPolicy = describeCurrentPolicyProvenance({
|
|
483
492
|
defaults: startParams.defaults,
|
|
@@ -501,9 +510,6 @@ export function startRun(
|
|
|
501
510
|
prepareStateDirForStart(stateDir);
|
|
502
511
|
const stdout = join(stateDir, "stdout.log");
|
|
503
512
|
const stderr = join(stateDir, "stderr.log");
|
|
504
|
-
const recipeFile = startParams.file
|
|
505
|
-
? resolveRecipeFile(startParams.file)
|
|
506
|
-
: undefined;
|
|
507
513
|
const recipe = startParams.name || getRunIdFromFile(recipeFile);
|
|
508
514
|
const includeActorRecipeContext =
|
|
509
515
|
startParams.actor_context !== false &&
|
|
@@ -552,6 +558,7 @@ export function startRun(
|
|
|
552
558
|
run,
|
|
553
559
|
run_instance_id: randomUUID(),
|
|
554
560
|
state_dir: stateDir,
|
|
561
|
+
state_schema: RuntimeIdentity.RUN_STATE_SCHEMA,
|
|
555
562
|
status: "running",
|
|
556
563
|
...(startParams.tool ? { tool: startParams.tool } : {}),
|
|
557
564
|
template: resolved.template,
|
|
@@ -559,7 +566,9 @@ export function startRun(
|
|
|
559
566
|
model_policy: modelPolicy,
|
|
560
567
|
...(artifacts ? { artifacts } : {}),
|
|
561
568
|
...(startParams.control ? { control: startParams.control } : {}),
|
|
562
|
-
...(startParams.
|
|
569
|
+
...(startParams.control_endpoint
|
|
570
|
+
? { control_endpoint: startParams.control_endpoint }
|
|
571
|
+
: {}),
|
|
563
572
|
...(startParams.notification_policy === "silent"
|
|
564
573
|
? { notification_policy: "silent" as const }
|
|
565
574
|
: {}),
|
|
@@ -596,11 +605,14 @@ export function startRun(
|
|
|
596
605
|
);
|
|
597
606
|
if (processIdentity) meta.process_identity = processIdentity;
|
|
598
607
|
writeJsonAtomic(join(stateDir, "run.json"), meta);
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
608
|
+
appendRunTraceEvent(stateDir, {
|
|
609
|
+
kind: "run.start",
|
|
610
|
+
summary: `Run ${run} started`,
|
|
611
|
+
data: {
|
|
612
|
+
pid: meta.pid,
|
|
613
|
+
run_instance_id: meta.run_instance_id,
|
|
614
|
+
},
|
|
615
|
+
});
|
|
604
616
|
child.unref();
|
|
605
617
|
return meta;
|
|
606
618
|
} finally {
|
|
@@ -608,13 +620,6 @@ export function startRun(
|
|
|
608
620
|
}
|
|
609
621
|
}
|
|
610
622
|
|
|
611
|
-
export { parseRunOutboxEventLine } from "./runs-outbox.ts";
|
|
612
|
-
export type {
|
|
613
|
-
RunOutboxDelivery,
|
|
614
|
-
RunOutboxEvent,
|
|
615
|
-
RunOutboxLevel,
|
|
616
|
-
} from "./runs-outbox.ts";
|
|
617
|
-
|
|
618
623
|
function resolveRunStateDir(runOrDir: string): string {
|
|
619
624
|
return resolve(
|
|
620
625
|
/[\\/]/u.test(runOrDir)
|
|
@@ -714,21 +719,19 @@ export function teardownRunsOwnedByParent(
|
|
|
714
719
|
) {
|
|
715
720
|
throw new Error("run generation changed before teardown evidence");
|
|
716
721
|
}
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
ownerId: attempt.ownerId,
|
|
722
|
+
appendRunTraceEvent(attempt.stateDir, {
|
|
723
|
+
kind: "run.parent_teardown",
|
|
724
|
+
summary: `Parent teardown ${attempt.outcome}`,
|
|
725
|
+
data: {
|
|
722
726
|
outcome: attempt.outcome,
|
|
727
|
+
owner_id: attempt.ownerId,
|
|
723
728
|
reason: attempt.reason,
|
|
724
729
|
run: attempt.run,
|
|
725
730
|
run_instance_id: attempt.runInstanceId,
|
|
726
|
-
type: "control.kill",
|
|
727
731
|
trigger: options.trigger ?? "parent_shutdown",
|
|
728
|
-
|
|
729
|
-
})
|
|
730
|
-
|
|
731
|
-
);
|
|
732
|
+
},
|
|
733
|
+
...(attempt.outcome === "failed" ? { level: "error" as const } : {}),
|
|
734
|
+
});
|
|
732
735
|
},
|
|
733
736
|
});
|
|
734
737
|
const summaryPath = join(
|
|
@@ -764,139 +767,63 @@ export function teardownRunsOwnedByParent(
|
|
|
764
767
|
export function tailRun(runOrDir: string, lines = 40): string {
|
|
765
768
|
const status = getRunStatus(runOrDir);
|
|
766
769
|
const stateDir = String(status.state_dir);
|
|
767
|
-
const
|
|
768
|
-
if (
|
|
770
|
+
const trace = tailFile(join(stateDir, "trace.jsonl"), lines);
|
|
771
|
+
if (trace) return trace;
|
|
769
772
|
return (
|
|
770
773
|
tailFile(join(stateDir, "stdout.log"), lines) ||
|
|
771
774
|
tailFile(join(stateDir, "stderr.log"), lines)
|
|
772
775
|
);
|
|
773
776
|
}
|
|
774
777
|
|
|
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
778
|
export type {
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
} from "./runs-mailbox.ts";
|
|
779
|
+
DeliverRunControlOptions,
|
|
780
|
+
DeliverRunControlRequest,
|
|
781
|
+
} from "./runs-control-delivery.ts";
|
|
789
782
|
|
|
790
|
-
export
|
|
791
|
-
|
|
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
|
-
}
|
|
839
|
-
|
|
840
|
-
export function appendRunOutboxEvent(
|
|
841
|
-
runOrDir: string,
|
|
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
|
-
};
|
|
783
|
+
export interface SendRunControlOptions extends DeliverRunControlOptions {
|
|
784
|
+
ownerId?: string;
|
|
870
785
|
}
|
|
871
786
|
|
|
872
|
-
export
|
|
873
|
-
|
|
874
|
-
export async function sendRunMessage(
|
|
787
|
+
export async function sendRunControl(
|
|
875
788
|
runOrDir: string,
|
|
876
|
-
|
|
877
|
-
options:
|
|
789
|
+
request: DeliverRunControlRequest,
|
|
790
|
+
options: SendRunControlOptions = {},
|
|
878
791
|
): 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}`);
|
|
792
|
+
const stateDir = resolveRunStateDir(runOrDir);
|
|
793
|
+
const releaseControlLock = RunsStart.acquireStateStartLock(stateDir);
|
|
794
|
+
try {
|
|
795
|
+
const status = getRunStatus(stateDir);
|
|
796
|
+
const run = String(status.run ?? runOrDir);
|
|
797
|
+
if (options.ownerId !== undefined && status.ownerId !== options.ownerId) {
|
|
798
|
+
throw Object.assign(new Error(`Run ownership changed: ${run}`), {
|
|
799
|
+
reason: "owner_mismatch",
|
|
800
|
+
});
|
|
893
801
|
}
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
802
|
+
if (status.run_instance_id !== request.run_instance_id) {
|
|
803
|
+
throw Object.assign(new Error(`Run generation changed: ${run}`), {
|
|
804
|
+
reason: "generation_mismatch",
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
if (status.status !== "running") {
|
|
808
|
+
throw Object.assign(new Error(`Run is not running: ${run}`), {
|
|
809
|
+
reason: "terminal_state",
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
const pid = Number(status.pid || 0);
|
|
813
|
+
const identity = verifyRunProcessIdentity(
|
|
814
|
+
pid,
|
|
815
|
+
status.process_identity as RunProcessIdentity | undefined,
|
|
816
|
+
);
|
|
817
|
+
if (!identity.valid) {
|
|
818
|
+
throw Object.assign(
|
|
819
|
+
new Error(`Run process identity ${identity.status}: ${run}`),
|
|
820
|
+
{ reason: `process_identity_${identity.status}` },
|
|
821
|
+
);
|
|
822
|
+
}
|
|
823
|
+
return await deliverRunControl(run, stateDir, request, options);
|
|
824
|
+
} finally {
|
|
825
|
+
releaseControlLock();
|
|
898
826
|
}
|
|
899
|
-
return deliverRunMessage(status, run, stateDir, message, options);
|
|
900
827
|
}
|
|
901
828
|
|
|
902
829
|
export { getRunProcessSignalPlan } from "./runs-control.ts";
|
|
@@ -917,13 +844,13 @@ function markTerminalProgress(
|
|
|
917
844
|
);
|
|
918
845
|
}
|
|
919
846
|
|
|
920
|
-
function
|
|
847
|
+
function finalizeInterruptedExecution(
|
|
921
848
|
stateDir: string,
|
|
922
849
|
phase: "cancelled" | "killed",
|
|
923
850
|
signal: NodeJS.Signals,
|
|
924
851
|
): void {
|
|
925
|
-
const
|
|
926
|
-
const manifest = readJson(
|
|
852
|
+
const executionPath = join(stateDir, "execution.json");
|
|
853
|
+
const manifest = readJson(executionPath);
|
|
927
854
|
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) return;
|
|
928
855
|
const record = manifest as Record<string, unknown>;
|
|
929
856
|
if (!Array.isArray(record.commands)) return;
|
|
@@ -969,7 +896,7 @@ function finalizeInterruptedReviewEvidence(
|
|
|
969
896
|
: {}),
|
|
970
897
|
};
|
|
971
898
|
});
|
|
972
|
-
writeJsonAtomic(
|
|
899
|
+
writeJsonAtomic(executionPath, {
|
|
973
900
|
...record,
|
|
974
901
|
status: phase,
|
|
975
902
|
commands,
|
|
@@ -1006,9 +933,37 @@ function stopRun(
|
|
|
1006
933
|
) {
|
|
1007
934
|
return { stopped: false, reason: "run generation changed", status };
|
|
1008
935
|
}
|
|
936
|
+
const control =
|
|
937
|
+
event === "run.kill" && typeof status.run_instance_id === "string"
|
|
938
|
+
? appendRunControlInStateDir(stateDir, {
|
|
939
|
+
action: "kill",
|
|
940
|
+
run_instance_id: status.run_instance_id,
|
|
941
|
+
})
|
|
942
|
+
: undefined;
|
|
943
|
+
if (control) {
|
|
944
|
+
updateRunControlStatusInStateDir(
|
|
945
|
+
stateDir,
|
|
946
|
+
control.id,
|
|
947
|
+
"claimed",
|
|
948
|
+
{},
|
|
949
|
+
["queued"],
|
|
950
|
+
);
|
|
951
|
+
}
|
|
952
|
+
const finish = (result: Record<string, unknown>): Record<string, unknown> => {
|
|
953
|
+
if (!control) return result;
|
|
954
|
+
const handled = result.stopped === true;
|
|
955
|
+
updateRunControlStatusInStateDir(
|
|
956
|
+
stateDir,
|
|
957
|
+
control.id,
|
|
958
|
+
handled ? "handled" : "failed",
|
|
959
|
+
handled ? {} : { error: String(result.reason ?? "kill rejected") },
|
|
960
|
+
["claimed"],
|
|
961
|
+
);
|
|
962
|
+
return { ...result, control_id: control.id };
|
|
963
|
+
};
|
|
1009
964
|
const pid = Number(status.pid || 0);
|
|
1010
965
|
if (status.status !== "running" && status.status !== "exited") {
|
|
1011
|
-
return { stopped: false, reason: "not running", status };
|
|
966
|
+
return finish({ stopped: false, reason: "not running", status });
|
|
1012
967
|
}
|
|
1013
968
|
const identity = verifyRunProcessIdentity(
|
|
1014
969
|
pid,
|
|
@@ -1019,43 +974,59 @@ function stopRun(
|
|
|
1019
974
|
identity.status === "owner_mismatch" ||
|
|
1020
975
|
identity.status === "unsupported_proof"
|
|
1021
976
|
) {
|
|
1022
|
-
return {
|
|
977
|
+
return finish({
|
|
1023
978
|
stopped: false,
|
|
1024
979
|
reason: identity.status.replaceAll("_", " "),
|
|
1025
980
|
process_identity_status: identity.status,
|
|
1026
981
|
status,
|
|
1027
|
-
};
|
|
982
|
+
});
|
|
1028
983
|
}
|
|
1029
|
-
return { stopped: false, reason: "not running", status };
|
|
984
|
+
return finish({ stopped: false, reason: "not running", status });
|
|
1030
985
|
}
|
|
1031
986
|
if (!identity.valid) {
|
|
1032
|
-
return {
|
|
987
|
+
return finish({
|
|
1033
988
|
stopped: false,
|
|
1034
989
|
reason: identity.status.replaceAll("_", " "),
|
|
1035
990
|
process_identity_status: identity.status,
|
|
1036
991
|
status,
|
|
1037
|
-
};
|
|
992
|
+
});
|
|
1038
993
|
}
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
994
|
+
let signalResult: RunProcessSignalPlan;
|
|
995
|
+
try {
|
|
996
|
+
signalResult = signalOwnedRunProcess(
|
|
997
|
+
pid,
|
|
998
|
+
signal,
|
|
999
|
+
status.process_identity as RunProcessIdentity,
|
|
1000
|
+
);
|
|
1001
|
+
} catch (error) {
|
|
1002
|
+
if (control) {
|
|
1003
|
+
updateRunControlStatusInStateDir(stateDir, control.id, "failed", {
|
|
1004
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
throw error;
|
|
1008
|
+
}
|
|
1009
|
+
appendRunTraceEvent(stateDir, {
|
|
1010
|
+
kind: event,
|
|
1011
|
+
summary: `${event === "run.kill" ? "Killed" : "Cancelled"} Run process`,
|
|
1012
|
+
data: { pid, signal, ...signalResult },
|
|
1013
|
+
});
|
|
1049
1014
|
markTerminalHandled(stateDir, { event, signal });
|
|
1050
1015
|
if (event === "run.kill") {
|
|
1051
|
-
|
|
1016
|
+
finalizeInterruptedExecution(stateDir, "killed", signal);
|
|
1052
1017
|
markTerminalProgress(stateDir, "killed");
|
|
1053
1018
|
}
|
|
1054
1019
|
if (event === "run.cancel") {
|
|
1055
|
-
|
|
1020
|
+
finalizeInterruptedExecution(stateDir, "cancelled", signal);
|
|
1056
1021
|
markTerminalProgress(stateDir, "cancelled");
|
|
1057
1022
|
}
|
|
1058
|
-
return {
|
|
1023
|
+
return finish({
|
|
1024
|
+
stopped: true,
|
|
1025
|
+
pid,
|
|
1026
|
+
signal,
|
|
1027
|
+
...signalResult,
|
|
1028
|
+
state_dir: stateDir,
|
|
1029
|
+
});
|
|
1059
1030
|
} finally {
|
|
1060
1031
|
releaseControlLock();
|
|
1061
1032
|
}
|
|
@@ -1100,23 +1071,66 @@ export function cancelRun(
|
|
|
1100
1071
|
: result;
|
|
1101
1072
|
}
|
|
1102
1073
|
|
|
1103
|
-
function
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1074
|
+
function retainRun(
|
|
1075
|
+
runOrDir: string,
|
|
1076
|
+
action: RunRetentionAction,
|
|
1077
|
+
expected: RunControlExpectation,
|
|
1078
|
+
options: { preserveArtifacts?: boolean } = {},
|
|
1079
|
+
): Record<string, unknown> {
|
|
1080
|
+
const stateDir = resolveRunStateDir(runOrDir);
|
|
1081
|
+
const releaseControlLock = RunsStart.acquireStateStartLock(stateDir);
|
|
1082
|
+
let status: Record<string, unknown> | undefined;
|
|
1083
|
+
let evidenceId: string | undefined;
|
|
1084
|
+
try {
|
|
1085
|
+
expected.onLocked?.();
|
|
1086
|
+
status = getRunStatus(stateDir);
|
|
1087
|
+
if (expected.ownerId !== undefined && status.ownerId !== expected.ownerId) {
|
|
1088
|
+
return { [`${action}d`]: false, reason: "ownership changed", status };
|
|
1089
|
+
}
|
|
1090
|
+
if (
|
|
1091
|
+
expected.runInstanceId !== undefined &&
|
|
1092
|
+
status.run_instance_id !== expected.runInstanceId
|
|
1093
|
+
) {
|
|
1094
|
+
return { [`${action}d`]: false, reason: "run generation changed", status };
|
|
1095
|
+
}
|
|
1096
|
+
if (status.status === "running") {
|
|
1097
|
+
throw new Error("Only terminal runs can be archived or pruned.");
|
|
1098
|
+
}
|
|
1099
|
+
evidenceId = appendRunRetentionEvidence(status, action, "queued");
|
|
1100
|
+
const result = action === "archive"
|
|
1101
|
+
? archiveTerminalRun(status)
|
|
1102
|
+
: pruneTerminalRun(status, options);
|
|
1103
|
+
appendRunRetentionEvidence(status, action, "handled", {
|
|
1104
|
+
id: evidenceId,
|
|
1105
|
+
result,
|
|
1106
|
+
});
|
|
1107
|
+
return { ...result, retention_id: evidenceId };
|
|
1108
|
+
} catch (error) {
|
|
1109
|
+
if (status && evidenceId) {
|
|
1110
|
+
appendRunRetentionEvidence(status, action, "failed", {
|
|
1111
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1112
|
+
id: evidenceId,
|
|
1113
|
+
});
|
|
1114
|
+
}
|
|
1115
|
+
throw error;
|
|
1116
|
+
} finally {
|
|
1117
|
+
releaseControlLock();
|
|
1107
1118
|
}
|
|
1108
|
-
return status;
|
|
1109
1119
|
}
|
|
1110
1120
|
|
|
1111
|
-
export function archiveRun(
|
|
1112
|
-
|
|
1121
|
+
export function archiveRun(
|
|
1122
|
+
runOrDir: string,
|
|
1123
|
+
expected: RunControlExpectation = {},
|
|
1124
|
+
): Record<string, unknown> {
|
|
1125
|
+
return retainRun(runOrDir, "archive", expected);
|
|
1113
1126
|
}
|
|
1114
1127
|
|
|
1115
1128
|
export function pruneRun(
|
|
1116
1129
|
runOrDir: string,
|
|
1117
1130
|
options: { preserveArtifacts?: boolean } = {},
|
|
1131
|
+
expected: RunControlExpectation = {},
|
|
1118
1132
|
): Record<string, unknown> {
|
|
1119
|
-
return
|
|
1133
|
+
return retainRun(runOrDir, "prune", expected, options);
|
|
1120
1134
|
}
|
|
1121
1135
|
|
|
1122
1136
|
export function killRun(
|
|
@@ -16,7 +16,7 @@ import * as ToolReviewScheduler from "./tool-review-scheduler.ts";
|
|
|
16
16
|
|
|
17
17
|
export interface AutomaticReviewRuntime {
|
|
18
18
|
close(): void;
|
|
19
|
-
|
|
19
|
+
handleControl(action: string, input: unknown): Record<string, unknown>;
|
|
20
20
|
schedule(): void;
|
|
21
21
|
start(ctx: Pi.ExtensionContext): void;
|
|
22
22
|
}
|
|
@@ -52,18 +52,18 @@ export function createAutomaticReviewRuntime(
|
|
|
52
52
|
|
|
53
53
|
return {
|
|
54
54
|
close,
|
|
55
|
-
|
|
56
|
-
if (
|
|
57
|
-
throw new Error("
|
|
55
|
+
handleControl(action, input) {
|
|
56
|
+
if (action !== "review.retry" && action !== "review.reset") {
|
|
57
|
+
throw new Error("runtime accepts review.retry or review.reset Controls.");
|
|
58
58
|
}
|
|
59
|
-
if (
|
|
59
|
+
if (action === "review.retry" && !Paths.isAutomaticRecipeReviewEnabled()) {
|
|
60
60
|
throw new Error(
|
|
61
61
|
"Automatic recipe review is disabled by PI_ACTORS_AUTOMATIC_REVIEW.",
|
|
62
62
|
);
|
|
63
63
|
}
|
|
64
64
|
return ReviewControl.controlAutomaticReview(
|
|
65
|
-
|
|
66
|
-
ReviewControl.parseAutomaticReviewScope(
|
|
65
|
+
action,
|
|
66
|
+
ReviewControl.parseAutomaticReviewScope(input),
|
|
67
67
|
{
|
|
68
68
|
scheduleDraft: () => draftScheduler?.schedule(),
|
|
69
69
|
scheduleTool: () => toolScheduler?.schedule(),
|