@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/dist/lib/async-runs.d.ts
CHANGED
|
@@ -6,22 +6,21 @@ import type { CommandTemplateFailureScope, CommandTemplateValue } from "./comman
|
|
|
6
6
|
import { type CurrentPolicyProvenance } from "./model-context.ts";
|
|
7
7
|
import * as RecipesReferences from "./recipes-references.ts";
|
|
8
8
|
import { type RunArtifactDeclaration } from "./runs-artifacts.ts";
|
|
9
|
-
import { type RunOutboxEvent } from "./runs-outbox.ts";
|
|
10
9
|
import { type RunProcessIdentity } from "./runs-process.ts";
|
|
10
|
+
import * as RuntimeIdentity from "./runtime-identity.ts";
|
|
11
11
|
import * as RunsIndex from "./runs-index.ts";
|
|
12
12
|
import * as RunsParentTeardown from "./runs-parent-teardown.ts";
|
|
13
|
-
import { type
|
|
14
|
-
import { type SendRunMessageOptions } from "./runs-messages.ts";
|
|
13
|
+
import { type DeliverRunControlOptions, type DeliverRunControlRequest } from "./runs-control-delivery.ts";
|
|
15
14
|
import { type AsyncRunStatus } from "./runs-status.ts";
|
|
16
15
|
export type AsyncRunLaunchSource = "spawn" | "tool";
|
|
17
16
|
export interface AsyncRunControlEndpoint {
|
|
18
17
|
path: string;
|
|
19
|
-
type: "fifo" | "
|
|
18
|
+
type: "fifo" | "named-pipe";
|
|
20
19
|
}
|
|
21
20
|
export declare function normalizeRunTransportContext(value: unknown): Record<string, string | number | boolean> | undefined;
|
|
22
21
|
export interface AsyncRunStartParams {
|
|
23
22
|
async?: boolean;
|
|
24
|
-
|
|
23
|
+
control_endpoint?: AsyncRunControlEndpoint;
|
|
25
24
|
file?: string;
|
|
26
25
|
launch_source?: AsyncRunLaunchSource;
|
|
27
26
|
lifecycleHooks?: {
|
|
@@ -49,7 +48,7 @@ export interface AsyncRunStartParams {
|
|
|
49
48
|
accept_output?: "review_evidence";
|
|
50
49
|
output?: string;
|
|
51
50
|
artifacts?: Record<string, RunArtifactDeclaration>;
|
|
52
|
-
|
|
51
|
+
control?: string[];
|
|
53
52
|
notification_policy?: "normal" | "silent";
|
|
54
53
|
retire_when?: "children_terminal";
|
|
55
54
|
retry?: number | string;
|
|
@@ -79,13 +78,14 @@ export interface AsyncRunMeta {
|
|
|
79
78
|
run: string;
|
|
80
79
|
run_instance_id: string;
|
|
81
80
|
state_dir: string;
|
|
81
|
+
state_schema: typeof RuntimeIdentity.RUN_STATE_SCHEMA;
|
|
82
82
|
status: AsyncRunStatus;
|
|
83
83
|
tool?: string;
|
|
84
84
|
template: CommandTemplateValue;
|
|
85
85
|
values: Record<string, unknown>;
|
|
86
86
|
artifacts?: Record<string, RunArtifactDeclaration>;
|
|
87
|
-
control?:
|
|
88
|
-
|
|
87
|
+
control?: string[];
|
|
88
|
+
control_endpoint?: AsyncRunControlEndpoint;
|
|
89
89
|
model_policy?: CurrentPolicyProvenance;
|
|
90
90
|
notification_policy?: "normal" | "silent";
|
|
91
91
|
process_identity?: RunProcessIdentity;
|
|
@@ -97,8 +97,6 @@ export { safeRunId } from "./runs-identity.ts";
|
|
|
97
97
|
export { resolveArtifactManifest } from "./runs-artifacts.ts";
|
|
98
98
|
export type { RunArtifactDeclaration, RunArtifactManifestEntry, } from "./runs-artifacts.ts";
|
|
99
99
|
export declare function startRun(params: AsyncRunStartParams, cwd: string): AsyncRunMeta;
|
|
100
|
-
export { parseRunOutboxEventLine } from "./runs-outbox.ts";
|
|
101
|
-
export type { RunOutboxDelivery, RunOutboxEvent, RunOutboxLevel, } from "./runs-outbox.ts";
|
|
102
100
|
export declare function getRunStatus(runOrDir: string): Record<string, unknown>;
|
|
103
101
|
export type { RunStateIndexEntry } from "./runs-index.ts";
|
|
104
102
|
export declare function listRunStateDirs(stateRoot?: string): string[];
|
|
@@ -113,32 +111,11 @@ export declare function teardownRunsOwnedByParent(ownerId: string | undefined, s
|
|
|
113
111
|
trigger?: string;
|
|
114
112
|
}): ParentRunsTeardownSummaryResult;
|
|
115
113
|
export declare function tailRun(runOrDir: string, lines?: number): string;
|
|
116
|
-
export
|
|
117
|
-
export
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
export declare function
|
|
121
|
-
export declare function processRunInboxMessages(runOrDir: string, handler: (message: RunInboxMessage) => Promise<void> | void, options?: {
|
|
122
|
-
limit?: number;
|
|
123
|
-
owner?: string;
|
|
124
|
-
statuses?: string[];
|
|
125
|
-
}): Promise<ProcessRunInboxResult>;
|
|
126
|
-
export declare function appendRunOutboxEvent(runOrDir: string, event: {
|
|
127
|
-
body?: unknown;
|
|
128
|
-
correlation_id?: string;
|
|
129
|
-
data?: unknown;
|
|
130
|
-
delivery?: string;
|
|
131
|
-
event?: string;
|
|
132
|
-
from?: string;
|
|
133
|
-
level?: string;
|
|
134
|
-
metadata?: Record<string, unknown>;
|
|
135
|
-
reply_to?: string;
|
|
136
|
-
summary?: string;
|
|
137
|
-
to?: string;
|
|
138
|
-
type?: string;
|
|
139
|
-
}): Record<string, unknown>;
|
|
140
|
-
export type { SendRunMessageOptions } from "./runs-messages.ts";
|
|
141
|
-
export declare function sendRunMessage(runOrDir: string, message: string, options?: SendRunMessageOptions): Promise<Record<string, unknown>>;
|
|
114
|
+
export type { DeliverRunControlOptions, DeliverRunControlRequest, } from "./runs-control-delivery.ts";
|
|
115
|
+
export interface SendRunControlOptions extends DeliverRunControlOptions {
|
|
116
|
+
ownerId?: string;
|
|
117
|
+
}
|
|
118
|
+
export declare function sendRunControl(runOrDir: string, request: DeliverRunControlRequest, options?: SendRunControlOptions): Promise<Record<string, unknown>>;
|
|
142
119
|
export { getRunProcessSignalPlan } from "./runs-control.ts";
|
|
143
120
|
export type { RunProcessSignalPlan } from "./runs-control.ts";
|
|
144
121
|
export interface RunControlExpectation {
|
|
@@ -149,8 +126,8 @@ export interface RunControlExpectation {
|
|
|
149
126
|
export declare function markRunTerminalNotificationHandled(stateDir: string, status: string): void;
|
|
150
127
|
export declare function recordRunTerminalDeliveryFailure(stateDir: string, status: string, error: unknown): void;
|
|
151
128
|
export declare function cancelRun(runOrDir: string, expected?: RunControlExpectation): Record<string, unknown>;
|
|
152
|
-
export declare function archiveRun(runOrDir: string): Record<string, unknown>;
|
|
129
|
+
export declare function archiveRun(runOrDir: string, expected?: RunControlExpectation): Record<string, unknown>;
|
|
153
130
|
export declare function pruneRun(runOrDir: string, options?: {
|
|
154
131
|
preserveArtifacts?: boolean;
|
|
155
|
-
}): Record<string, unknown>;
|
|
132
|
+
}, expected?: RunControlExpectation): Record<string, unknown>;
|
|
156
133
|
export declare function killRun(runOrDir: string, expected?: RunControlExpectation): Record<string, unknown>;
|
package/dist/lib/async-runs.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { spawn } from "node:child_process";
|
|
6
6
|
import { randomUUID } from "node:crypto";
|
|
7
|
-
import { closeSync, existsSync, mkdirSync, openSync, readdirSync, statSync,
|
|
7
|
+
import { closeSync, existsSync, mkdirSync, openSync, readdirSync, statSync, } from "node:fs";
|
|
8
8
|
import { basename, dirname, extname, isAbsolute, join, relative, resolve } from "node:path";
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
10
|
import { writeJsonAtomic } from "./file-state.js";
|
|
@@ -15,16 +15,17 @@ import * as RecipesUsage from "./recipes-usage.js";
|
|
|
15
15
|
import { resolveArtifactPaths, } from "./runs-artifacts.js";
|
|
16
16
|
import { safeRunId } from "./runs-identity.js";
|
|
17
17
|
import { buildTerminalProgress, markTerminalHandled, signalOwnedRunProcess, } from "./runs-control.js";
|
|
18
|
-
import { buildRunOutboxEventPayload, parseRunOutboxEventLine, } from "./runs-outbox.js";
|
|
19
18
|
import { claimRunStateDirectory } from "./runs-ownership.js";
|
|
20
|
-
import { archiveTerminalRun, pruneTerminalRun } from "./runs-retention.js";
|
|
19
|
+
import { appendRunRetentionEvidence, archiveTerminalRun, pruneTerminalRun, } from "./runs-retention.js";
|
|
21
20
|
import { captureRunProcessIdentity, verifyRunProcessIdentity, } from "./runs-process.js";
|
|
21
|
+
import * as RuntimeIdentity from "./runtime-identity.js";
|
|
22
22
|
import * as RunsStart from "./runs-start.js";
|
|
23
|
+
import { appendRunTraceEvent } from "./runs-trace.js";
|
|
23
24
|
import * as RunsIndex from "./runs-index.js";
|
|
24
25
|
import * as RunsParentTeardown from "./runs-parent-teardown.js";
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import { buildRunStatus, tailFile,
|
|
26
|
+
import { appendRunControlInStateDir, updateRunControlStatusInStateDir, } from "./runs-controls.js";
|
|
27
|
+
import { deliverRunControl, } from "./runs-control-delivery.js";
|
|
28
|
+
import { buildRunStatus, tailFile, } from "./runs-status.js";
|
|
28
29
|
import { readJsonFileResilient } from "./state-readers.js";
|
|
29
30
|
const RUNNER_IDENTITY_GRACE_MS = 5000;
|
|
30
31
|
export function normalizeRunTransportContext(value) {
|
|
@@ -236,13 +237,25 @@ export function startRun(params, cwd) {
|
|
|
236
237
|
const resolved = resolveRunTemplate(startParams);
|
|
237
238
|
const run = safeRunId(startParams.run_id);
|
|
238
239
|
const stateDir = resolveStateDir(startParams, run);
|
|
240
|
+
const recipeFile = startParams.file
|
|
241
|
+
? resolveRecipeFile(startParams.file)
|
|
242
|
+
: undefined;
|
|
243
|
+
const packagedRecipeRoot = resolve(Paths.getPackagedRecipeRoot());
|
|
244
|
+
const recipeRelation = recipeFile
|
|
245
|
+
? relative(packagedRecipeRoot, recipeFile)
|
|
246
|
+
: undefined;
|
|
247
|
+
const packagedRepo = startParams.defaults?.repo === "~/.pi/agent/extensions/pi-actors" &&
|
|
248
|
+
recipeRelation &&
|
|
249
|
+
!recipeRelation.startsWith("..") &&
|
|
250
|
+
!isAbsolute(recipeRelation)
|
|
251
|
+
? dirname(packagedRecipeRoot)
|
|
252
|
+
: undefined;
|
|
239
253
|
const values = {
|
|
254
|
+
...(packagedRepo ? { repo: packagedRepo } : {}),
|
|
240
255
|
...(startParams.values || {}),
|
|
241
|
-
actor_address: `run:${run}`,
|
|
242
|
-
communication_file: join(stateDir, "communication.json"),
|
|
243
|
-
default_room: `room:${run}`,
|
|
244
256
|
run_id: run,
|
|
245
257
|
state_dir: stateDir,
|
|
258
|
+
trace_file: join(stateDir, "trace.jsonl"),
|
|
246
259
|
};
|
|
247
260
|
const modelPolicy = describeCurrentPolicyProvenance({
|
|
248
261
|
defaults: startParams.defaults,
|
|
@@ -261,9 +274,6 @@ export function startRun(params, cwd) {
|
|
|
261
274
|
prepareStateDirForStart(stateDir);
|
|
262
275
|
const stdout = join(stateDir, "stdout.log");
|
|
263
276
|
const stderr = join(stateDir, "stderr.log");
|
|
264
|
-
const recipeFile = startParams.file
|
|
265
|
-
? resolveRecipeFile(startParams.file)
|
|
266
|
-
: undefined;
|
|
267
277
|
const recipe = startParams.name || getRunIdFromFile(recipeFile);
|
|
268
278
|
const includeActorRecipeContext = startParams.actor_context !== false &&
|
|
269
279
|
startParams.actor_context !== "off";
|
|
@@ -300,6 +310,7 @@ export function startRun(params, cwd) {
|
|
|
300
310
|
run,
|
|
301
311
|
run_instance_id: randomUUID(),
|
|
302
312
|
state_dir: stateDir,
|
|
313
|
+
state_schema: RuntimeIdentity.RUN_STATE_SCHEMA,
|
|
303
314
|
status: "running",
|
|
304
315
|
...(startParams.tool ? { tool: startParams.tool } : {}),
|
|
305
316
|
template: resolved.template,
|
|
@@ -307,7 +318,9 @@ export function startRun(params, cwd) {
|
|
|
307
318
|
model_policy: modelPolicy,
|
|
308
319
|
...(artifacts ? { artifacts } : {}),
|
|
309
320
|
...(startParams.control ? { control: startParams.control } : {}),
|
|
310
|
-
...(startParams.
|
|
321
|
+
...(startParams.control_endpoint
|
|
322
|
+
? { control_endpoint: startParams.control_endpoint }
|
|
323
|
+
: {}),
|
|
311
324
|
...(startParams.notification_policy === "silent"
|
|
312
325
|
? { notification_policy: "silent" }
|
|
313
326
|
: {}),
|
|
@@ -340,7 +353,14 @@ export function startRun(params, cwd) {
|
|
|
340
353
|
if (processIdentity)
|
|
341
354
|
meta.process_identity = processIdentity;
|
|
342
355
|
writeJsonAtomic(join(stateDir, "run.json"), meta);
|
|
343
|
-
|
|
356
|
+
appendRunTraceEvent(stateDir, {
|
|
357
|
+
kind: "run.start",
|
|
358
|
+
summary: `Run ${run} started`,
|
|
359
|
+
data: {
|
|
360
|
+
pid: meta.pid,
|
|
361
|
+
run_instance_id: meta.run_instance_id,
|
|
362
|
+
},
|
|
363
|
+
});
|
|
344
364
|
child.unref();
|
|
345
365
|
return meta;
|
|
346
366
|
}
|
|
@@ -348,7 +368,6 @@ export function startRun(params, cwd) {
|
|
|
348
368
|
releaseStartLock();
|
|
349
369
|
}
|
|
350
370
|
}
|
|
351
|
-
export { parseRunOutboxEventLine } from "./runs-outbox.js";
|
|
352
371
|
function resolveRunStateDir(runOrDir) {
|
|
353
372
|
return resolve(/[\\/]/u.test(runOrDir)
|
|
354
373
|
? runOrDir
|
|
@@ -403,17 +422,19 @@ export function teardownRunsOwnedByParent(ownerId, stateRoot = DEFAULT_STATE_ROO
|
|
|
403
422
|
current.run_instance_id !== attempt.runInstanceId) {
|
|
404
423
|
throw new Error("run generation changed before teardown evidence");
|
|
405
424
|
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
425
|
+
appendRunTraceEvent(attempt.stateDir, {
|
|
426
|
+
kind: "run.parent_teardown",
|
|
427
|
+
summary: `Parent teardown ${attempt.outcome}`,
|
|
428
|
+
data: {
|
|
429
|
+
outcome: attempt.outcome,
|
|
430
|
+
owner_id: attempt.ownerId,
|
|
431
|
+
reason: attempt.reason,
|
|
432
|
+
run: attempt.run,
|
|
433
|
+
run_instance_id: attempt.runInstanceId,
|
|
434
|
+
trigger: options.trigger ?? "parent_shutdown",
|
|
435
|
+
},
|
|
436
|
+
...(attempt.outcome === "failed" ? { level: "error" } : {}),
|
|
437
|
+
});
|
|
417
438
|
},
|
|
418
439
|
});
|
|
419
440
|
const summaryPath = join(stateRoot, "teardown", `${Date.now()}-${randomUUID()}.json`);
|
|
@@ -437,73 +458,43 @@ export function teardownRunsOwnedByParent(ownerId, stateRoot = DEFAULT_STATE_ROO
|
|
|
437
458
|
export function tailRun(runOrDir, lines = 40) {
|
|
438
459
|
const status = getRunStatus(runOrDir);
|
|
439
460
|
const stateDir = String(status.state_dir);
|
|
440
|
-
const
|
|
441
|
-
if (
|
|
442
|
-
return
|
|
461
|
+
const trace = tailFile(join(stateDir, "trace.jsonl"), lines);
|
|
462
|
+
if (trace)
|
|
463
|
+
return trace;
|
|
443
464
|
return (tailFile(join(stateDir, "stdout.log"), lines) ||
|
|
444
465
|
tailFile(join(stateDir, "stderr.log"), lines));
|
|
445
466
|
}
|
|
446
|
-
export function
|
|
447
|
-
const
|
|
448
|
-
const
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
.
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
}
|
|
466
|
-
export function claimRunInboxMessage(runOrDir, owner = "runtime", statuses = ["queued"]) {
|
|
467
|
-
const status = getRunStatus(runOrDir);
|
|
468
|
-
const stateDir = String(status.state_dir);
|
|
469
|
-
return claimRunInboxMessageInStateDir(stateDir, owner, statuses);
|
|
470
|
-
}
|
|
471
|
-
export async function processRunInboxMessages(runOrDir, handler, options = {}) {
|
|
472
|
-
const status = getRunStatus(runOrDir);
|
|
473
|
-
return processRunInboxMessagesInStateDir(String(status.state_dir), handler, options);
|
|
474
|
-
}
|
|
475
|
-
export function appendRunOutboxEvent(runOrDir, event) {
|
|
476
|
-
const status = getRunStatus(runOrDir);
|
|
477
|
-
const stateDir = String(status.state_dir);
|
|
478
|
-
const run = String(status.run ?? runOrDir);
|
|
479
|
-
const payload = buildRunOutboxEventPayload(run, event);
|
|
480
|
-
const line = `${JSON.stringify(payload)}\n`;
|
|
481
|
-
writeFileSync(join(stateDir, "outbox.jsonl"), line, { flag: "a" });
|
|
482
|
-
return {
|
|
483
|
-
bytes: Buffer.byteLength(line),
|
|
484
|
-
outbox: "outbox.jsonl",
|
|
485
|
-
run,
|
|
486
|
-
sent: true,
|
|
487
|
-
state_dir: stateDir,
|
|
488
|
-
};
|
|
489
|
-
}
|
|
490
|
-
export async function sendRunMessage(runOrDir, message, options = {}) {
|
|
491
|
-
const status = getRunStatus(runOrDir);
|
|
492
|
-
const stateDir = String(status.state_dir);
|
|
493
|
-
const run = String(status.run ?? runOrDir);
|
|
494
|
-
const pid = Number(status.pid || 0);
|
|
495
|
-
const identity = verifyRunProcessIdentity(pid, status.process_identity);
|
|
496
|
-
if (status.status !== "running") {
|
|
497
|
-
if (identity.status === "owner_mismatch" ||
|
|
498
|
-
identity.status === "unsupported_proof") {
|
|
499
|
-
throw new Error(`Run process identity ${identity.status}: ${run}`);
|
|
467
|
+
export async function sendRunControl(runOrDir, request, options = {}) {
|
|
468
|
+
const stateDir = resolveRunStateDir(runOrDir);
|
|
469
|
+
const releaseControlLock = RunsStart.acquireStateStartLock(stateDir);
|
|
470
|
+
try {
|
|
471
|
+
const status = getRunStatus(stateDir);
|
|
472
|
+
const run = String(status.run ?? runOrDir);
|
|
473
|
+
if (options.ownerId !== undefined && status.ownerId !== options.ownerId) {
|
|
474
|
+
throw Object.assign(new Error(`Run ownership changed: ${run}`), {
|
|
475
|
+
reason: "owner_mismatch",
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
if (status.run_instance_id !== request.run_instance_id) {
|
|
479
|
+
throw Object.assign(new Error(`Run generation changed: ${run}`), {
|
|
480
|
+
reason: "generation_mismatch",
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
if (status.status !== "running") {
|
|
484
|
+
throw Object.assign(new Error(`Run is not running: ${run}`), {
|
|
485
|
+
reason: "terminal_state",
|
|
486
|
+
});
|
|
500
487
|
}
|
|
501
|
-
|
|
488
|
+
const pid = Number(status.pid || 0);
|
|
489
|
+
const identity = verifyRunProcessIdentity(pid, status.process_identity);
|
|
490
|
+
if (!identity.valid) {
|
|
491
|
+
throw Object.assign(new Error(`Run process identity ${identity.status}: ${run}`), { reason: `process_identity_${identity.status}` });
|
|
492
|
+
}
|
|
493
|
+
return await deliverRunControl(run, stateDir, request, options);
|
|
502
494
|
}
|
|
503
|
-
|
|
504
|
-
|
|
495
|
+
finally {
|
|
496
|
+
releaseControlLock();
|
|
505
497
|
}
|
|
506
|
-
return deliverRunMessage(status, run, stateDir, message, options);
|
|
507
498
|
}
|
|
508
499
|
export { getRunProcessSignalPlan } from "./runs-control.js";
|
|
509
500
|
function markTerminalProgress(stateDir, phase) {
|
|
@@ -513,9 +504,9 @@ function markTerminalProgress(stateDir, phase) {
|
|
|
513
504
|
: undefined;
|
|
514
505
|
writeJsonAtomic(join(stateDir, "progress.json"), buildTerminalProgress(progress, phase));
|
|
515
506
|
}
|
|
516
|
-
function
|
|
517
|
-
const
|
|
518
|
-
const manifest = readJson(
|
|
507
|
+
function finalizeInterruptedExecution(stateDir, phase, signal) {
|
|
508
|
+
const executionPath = join(stateDir, "execution.json");
|
|
509
|
+
const manifest = readJson(executionPath);
|
|
519
510
|
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest))
|
|
520
511
|
return;
|
|
521
512
|
const record = manifest;
|
|
@@ -564,7 +555,7 @@ function finalizeInterruptedReviewEvidence(stateDir, phase, signal) {
|
|
|
564
555
|
: {}),
|
|
565
556
|
};
|
|
566
557
|
});
|
|
567
|
-
writeJsonAtomic(
|
|
558
|
+
writeJsonAtomic(executionPath, {
|
|
568
559
|
...record,
|
|
569
560
|
status: phase,
|
|
570
561
|
commands,
|
|
@@ -585,43 +576,80 @@ function stopRun(runOrDir, signal, event, expected = {}) {
|
|
|
585
576
|
status.run_instance_id !== expected.runInstanceId) {
|
|
586
577
|
return { stopped: false, reason: "run generation changed", status };
|
|
587
578
|
}
|
|
579
|
+
const control = event === "run.kill" && typeof status.run_instance_id === "string"
|
|
580
|
+
? appendRunControlInStateDir(stateDir, {
|
|
581
|
+
action: "kill",
|
|
582
|
+
run_instance_id: status.run_instance_id,
|
|
583
|
+
})
|
|
584
|
+
: undefined;
|
|
585
|
+
if (control) {
|
|
586
|
+
updateRunControlStatusInStateDir(stateDir, control.id, "claimed", {}, ["queued"]);
|
|
587
|
+
}
|
|
588
|
+
const finish = (result) => {
|
|
589
|
+
if (!control)
|
|
590
|
+
return result;
|
|
591
|
+
const handled = result.stopped === true;
|
|
592
|
+
updateRunControlStatusInStateDir(stateDir, control.id, handled ? "handled" : "failed", handled ? {} : { error: String(result.reason ?? "kill rejected") }, ["claimed"]);
|
|
593
|
+
return { ...result, control_id: control.id };
|
|
594
|
+
};
|
|
588
595
|
const pid = Number(status.pid || 0);
|
|
589
596
|
if (status.status !== "running" && status.status !== "exited") {
|
|
590
|
-
return { stopped: false, reason: "not running", status };
|
|
597
|
+
return finish({ stopped: false, reason: "not running", status });
|
|
591
598
|
}
|
|
592
599
|
const identity = verifyRunProcessIdentity(pid, status.process_identity);
|
|
593
600
|
if (status.status === "exited") {
|
|
594
601
|
if (identity.status === "owner_mismatch" ||
|
|
595
602
|
identity.status === "unsupported_proof") {
|
|
596
|
-
return {
|
|
603
|
+
return finish({
|
|
597
604
|
stopped: false,
|
|
598
605
|
reason: identity.status.replaceAll("_", " "),
|
|
599
606
|
process_identity_status: identity.status,
|
|
600
607
|
status,
|
|
601
|
-
};
|
|
608
|
+
});
|
|
602
609
|
}
|
|
603
|
-
return { stopped: false, reason: "not running", status };
|
|
610
|
+
return finish({ stopped: false, reason: "not running", status });
|
|
604
611
|
}
|
|
605
612
|
if (!identity.valid) {
|
|
606
|
-
return {
|
|
613
|
+
return finish({
|
|
607
614
|
stopped: false,
|
|
608
615
|
reason: identity.status.replaceAll("_", " "),
|
|
609
616
|
process_identity_status: identity.status,
|
|
610
617
|
status,
|
|
611
|
-
};
|
|
618
|
+
});
|
|
612
619
|
}
|
|
613
|
-
|
|
614
|
-
|
|
620
|
+
let signalResult;
|
|
621
|
+
try {
|
|
622
|
+
signalResult = signalOwnedRunProcess(pid, signal, status.process_identity);
|
|
623
|
+
}
|
|
624
|
+
catch (error) {
|
|
625
|
+
if (control) {
|
|
626
|
+
updateRunControlStatusInStateDir(stateDir, control.id, "failed", {
|
|
627
|
+
error: error instanceof Error ? error.message : String(error),
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
throw error;
|
|
631
|
+
}
|
|
632
|
+
appendRunTraceEvent(stateDir, {
|
|
633
|
+
kind: event,
|
|
634
|
+
summary: `${event === "run.kill" ? "Killed" : "Cancelled"} Run process`,
|
|
635
|
+
data: { pid, signal, ...signalResult },
|
|
636
|
+
});
|
|
615
637
|
markTerminalHandled(stateDir, { event, signal });
|
|
616
638
|
if (event === "run.kill") {
|
|
617
|
-
|
|
639
|
+
finalizeInterruptedExecution(stateDir, "killed", signal);
|
|
618
640
|
markTerminalProgress(stateDir, "killed");
|
|
619
641
|
}
|
|
620
642
|
if (event === "run.cancel") {
|
|
621
|
-
|
|
643
|
+
finalizeInterruptedExecution(stateDir, "cancelled", signal);
|
|
622
644
|
markTerminalProgress(stateDir, "cancelled");
|
|
623
645
|
}
|
|
624
|
-
return {
|
|
646
|
+
return finish({
|
|
647
|
+
stopped: true,
|
|
648
|
+
pid,
|
|
649
|
+
signal,
|
|
650
|
+
...signalResult,
|
|
651
|
+
state_dir: stateDir,
|
|
652
|
+
});
|
|
625
653
|
}
|
|
626
654
|
finally {
|
|
627
655
|
releaseControlLock();
|
|
@@ -653,18 +681,52 @@ export function cancelRun(runOrDir, expected = {}) {
|
|
|
653
681
|
? { cancelled: result.stopped, ...result }
|
|
654
682
|
: result;
|
|
655
683
|
}
|
|
656
|
-
function
|
|
657
|
-
const
|
|
658
|
-
|
|
659
|
-
|
|
684
|
+
function retainRun(runOrDir, action, expected, options = {}) {
|
|
685
|
+
const stateDir = resolveRunStateDir(runOrDir);
|
|
686
|
+
const releaseControlLock = RunsStart.acquireStateStartLock(stateDir);
|
|
687
|
+
let status;
|
|
688
|
+
let evidenceId;
|
|
689
|
+
try {
|
|
690
|
+
expected.onLocked?.();
|
|
691
|
+
status = getRunStatus(stateDir);
|
|
692
|
+
if (expected.ownerId !== undefined && status.ownerId !== expected.ownerId) {
|
|
693
|
+
return { [`${action}d`]: false, reason: "ownership changed", status };
|
|
694
|
+
}
|
|
695
|
+
if (expected.runInstanceId !== undefined &&
|
|
696
|
+
status.run_instance_id !== expected.runInstanceId) {
|
|
697
|
+
return { [`${action}d`]: false, reason: "run generation changed", status };
|
|
698
|
+
}
|
|
699
|
+
if (status.status === "running") {
|
|
700
|
+
throw new Error("Only terminal runs can be archived or pruned.");
|
|
701
|
+
}
|
|
702
|
+
evidenceId = appendRunRetentionEvidence(status, action, "queued");
|
|
703
|
+
const result = action === "archive"
|
|
704
|
+
? archiveTerminalRun(status)
|
|
705
|
+
: pruneTerminalRun(status, options);
|
|
706
|
+
appendRunRetentionEvidence(status, action, "handled", {
|
|
707
|
+
id: evidenceId,
|
|
708
|
+
result,
|
|
709
|
+
});
|
|
710
|
+
return { ...result, retention_id: evidenceId };
|
|
711
|
+
}
|
|
712
|
+
catch (error) {
|
|
713
|
+
if (status && evidenceId) {
|
|
714
|
+
appendRunRetentionEvidence(status, action, "failed", {
|
|
715
|
+
error: error instanceof Error ? error.message : String(error),
|
|
716
|
+
id: evidenceId,
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
throw error;
|
|
720
|
+
}
|
|
721
|
+
finally {
|
|
722
|
+
releaseControlLock();
|
|
660
723
|
}
|
|
661
|
-
return status;
|
|
662
724
|
}
|
|
663
|
-
export function archiveRun(runOrDir) {
|
|
664
|
-
return
|
|
725
|
+
export function archiveRun(runOrDir, expected = {}) {
|
|
726
|
+
return retainRun(runOrDir, "archive", expected);
|
|
665
727
|
}
|
|
666
|
-
export function pruneRun(runOrDir, options = {}) {
|
|
667
|
-
return
|
|
728
|
+
export function pruneRun(runOrDir, options = {}, expected = {}) {
|
|
729
|
+
return retainRun(runOrDir, "prune", expected, options);
|
|
668
730
|
}
|
|
669
731
|
export function killRun(runOrDir, expected = {}) {
|
|
670
732
|
const result = stopRun(runOrDir, "SIGKILL", "run.kill", expected);
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import type * as Pi from "./pi.ts";
|
|
7
7
|
export interface AutomaticReviewRuntime {
|
|
8
8
|
close(): void;
|
|
9
|
-
|
|
9
|
+
handleControl(action: string, input: unknown): Record<string, unknown>;
|
|
10
10
|
schedule(): void;
|
|
11
11
|
start(ctx: Pi.ExtensionContext): void;
|
|
12
12
|
}
|
|
@@ -26,14 +26,14 @@ export function createAutomaticReviewRuntime(deps) {
|
|
|
26
26
|
});
|
|
27
27
|
return {
|
|
28
28
|
close,
|
|
29
|
-
|
|
30
|
-
if (
|
|
31
|
-
throw new Error("
|
|
29
|
+
handleControl(action, input) {
|
|
30
|
+
if (action !== "review.retry" && action !== "review.reset") {
|
|
31
|
+
throw new Error("runtime accepts review.retry or review.reset Controls.");
|
|
32
32
|
}
|
|
33
|
-
if (
|
|
33
|
+
if (action === "review.retry" && !Paths.isAutomaticRecipeReviewEnabled()) {
|
|
34
34
|
throw new Error("Automatic recipe review is disabled by PI_ACTORS_AUTOMATIC_REVIEW.");
|
|
35
35
|
}
|
|
36
|
-
return ReviewControl.controlAutomaticReview(
|
|
36
|
+
return ReviewControl.controlAutomaticReview(action, ReviewControl.parseAutomaticReviewScope(input), {
|
|
37
37
|
scheduleDraft: () => draftScheduler?.schedule(),
|
|
38
38
|
scheduleTool: () => toolScheduler?.schedule(),
|
|
39
39
|
});
|
|
@@ -74,6 +74,8 @@ export declare function getCommandTemplateRiskLabels(config: CommandTemplateConf
|
|
|
74
74
|
export declare function getCommandTemplateDefaults(config: CommandTemplateConfig | undefined): Record<string, string>;
|
|
75
75
|
export declare function splitCommandTemplate(input: string): string[];
|
|
76
76
|
export declare function expandCommandTemplateExecutable(command: string, cwd: string): string;
|
|
77
|
+
export type CommandTemplateExecutableResolver = (name: string) => string | undefined;
|
|
78
|
+
export declare function resolveCommandTemplateScriptInvocation(invocation: CommandTemplateInvocation, resolveExecutable?: CommandTemplateExecutableResolver): CommandTemplateInvocation;
|
|
77
79
|
export declare function shouldRunCommandTemplateNode(value: boolean | string | undefined, values: Record<string, unknown>): boolean;
|
|
78
80
|
export declare function substituteCommandTemplateToken(token: string, values: Record<string, unknown>, missingLabel?: string, depth?: number): string;
|
|
79
81
|
export declare function execCommandTemplate(command: string, args: string[], options?: CommandTemplateExecOptions): Promise<CommandTemplateExecResult>;
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* Owns portable command-template parsing, expansion, risk checks, retries, timeouts, and direct execution.
|
|
5
5
|
*/
|
|
6
6
|
import { spawn } from "node:child_process";
|
|
7
|
-
import { appendFileSync, mkdirSync, mkdtempSync, writeFileSync } from "node:fs";
|
|
7
|
+
import { accessSync, appendFileSync, constants, mkdirSync, mkdtempSync, statSync, writeFileSync } from "node:fs";
|
|
8
8
|
import { homedir, tmpdir } from "node:os";
|
|
9
|
-
import { isAbsolute, join, resolve as resolvePath } from "node:path";
|
|
9
|
+
import { basename, delimiter, extname, isAbsolute, join, resolve as resolvePath } from "node:path";
|
|
10
10
|
const DEFAULT_COMMAND_CAPTURE_LIMIT_BYTES = 1024 * 1024;
|
|
11
11
|
const COMMAND_TEMPLATE_RISK_LABEL_ORDER = [
|
|
12
12
|
"risk.shell",
|
|
@@ -135,7 +135,7 @@ function getLeafCommandTemplateRiskLabels(config) {
|
|
|
135
135
|
const command = getExecutableName(parts[0]);
|
|
136
136
|
const args = parts.slice(1);
|
|
137
137
|
const labels = new Set();
|
|
138
|
-
if (["bash", "sh", "zsh", "fish"].includes(command)) {
|
|
138
|
+
if (["bash", "sh", "zsh", "fish"].includes(command) || command.endsWith(".sh")) {
|
|
139
139
|
labels.add("risk.shell");
|
|
140
140
|
if (hasAnyFlag(args, ["-c"]))
|
|
141
141
|
labels.add("risk.eval");
|
|
@@ -393,6 +393,40 @@ export function expandCommandTemplateExecutable(command, cwd) {
|
|
|
393
393
|
return resolvePath(cwd, command);
|
|
394
394
|
return command;
|
|
395
395
|
}
|
|
396
|
+
function resolveExecutableOnPath(name) {
|
|
397
|
+
if (name === "node" && /^node(?:\.exe)?$/i.test(basename(process.execPath)))
|
|
398
|
+
return process.execPath;
|
|
399
|
+
const pathValue = Object.entries(process.env)
|
|
400
|
+
.find(([key]) => key.toLowerCase() === "path")?.[1] ?? "";
|
|
401
|
+
const suffixes = process.platform === "win32" ? [".exe", ".com"] : [""];
|
|
402
|
+
for (const entry of pathValue.split(delimiter).filter(Boolean)) {
|
|
403
|
+
const directory = entry.replace(/^"|"$/g, "");
|
|
404
|
+
for (const suffix of suffixes) {
|
|
405
|
+
const candidate = join(directory, `${name}${suffix}`);
|
|
406
|
+
try {
|
|
407
|
+
accessSync(candidate, constants.X_OK);
|
|
408
|
+
if (statSync(candidate).isFile())
|
|
409
|
+
return candidate;
|
|
410
|
+
}
|
|
411
|
+
catch { }
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return undefined;
|
|
415
|
+
}
|
|
416
|
+
export function resolveCommandTemplateScriptInvocation(invocation, resolveExecutable = resolveExecutableOnPath) {
|
|
417
|
+
const extension = extname(invocation.command).toLowerCase();
|
|
418
|
+
const interpreters = extension === ".js" || extension === ".mjs"
|
|
419
|
+
? [["node"], ["bun"], ["deno", "run"]]
|
|
420
|
+
: extension === ".sh" ? [["bash"]] : [];
|
|
421
|
+
if (interpreters.length === 0)
|
|
422
|
+
return invocation;
|
|
423
|
+
for (const [name, ...prefix] of interpreters) {
|
|
424
|
+
const executable = resolveExecutable(name);
|
|
425
|
+
if (executable)
|
|
426
|
+
return { command: executable, args: [...prefix, invocation.command, ...invocation.args] };
|
|
427
|
+
}
|
|
428
|
+
throw new Error(`No interpreter available for command-template ${extension} script; tried ${interpreters.map(([name]) => name).join(", ")}.`);
|
|
429
|
+
}
|
|
396
430
|
function evaluateCommandTemplateExpression(expression, values) {
|
|
397
431
|
let index = 0;
|
|
398
432
|
const source = expression.replace(/\s+/g, "");
|
|
@@ -766,5 +800,5 @@ export function buildCommandTemplateInvocation(config, values, cwd, options = {}
|
|
|
766
800
|
.slice(1)
|
|
767
801
|
.map((part) => substituteCommandTemplateToken(part, resolvedValues, options.missingLabel))
|
|
768
802
|
.filter((part) => part !== "");
|
|
769
|
-
return { command, args };
|
|
803
|
+
return resolveCommandTemplateScriptInvocation({ command, args });
|
|
770
804
|
}
|