@llblab/pi-actors 0.41.1 → 0.42.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 +11 -6
- package/BACKLOG.md +1 -65
- package/CHANGELOG.md +25 -0
- package/README.md +24 -4
- package/dist/index.js +25 -121
- package/dist/lib/async-runs.d.ts +37 -5
- package/dist/lib/async-runs.js +181 -51
- package/dist/lib/automatic-review-runtime.d.ts +18 -0
- package/dist/lib/automatic-review-runtime.js +96 -0
- package/dist/lib/command-templates.js +1 -1
- package/dist/lib/draft-consolidation-transaction.d.ts +65 -0
- package/dist/lib/draft-consolidation-transaction.js +610 -0
- package/dist/lib/draft-consolidation.d.ts +35 -0
- package/dist/lib/draft-consolidation.js +126 -0
- package/dist/lib/draft-review.d.ts +56 -0
- package/dist/lib/draft-review.js +254 -0
- package/dist/lib/draft-sleep.d.ts +65 -0
- package/dist/lib/draft-sleep.js +468 -0
- package/dist/lib/file-state.d.ts +8 -1
- package/dist/lib/file-state.js +115 -18
- package/dist/lib/inspector-actions.d.ts +16 -0
- package/dist/lib/inspector-actions.js +57 -0
- package/dist/lib/inspector-command.d.ts +7 -0
- package/dist/lib/inspector-command.js +37 -0
- package/dist/lib/inspector-overlay.d.ts +23 -3
- package/dist/lib/inspector-overlay.js +327 -70
- package/dist/lib/inspector.d.ts +9 -0
- package/dist/lib/inspector.js +49 -1
- package/dist/lib/observability.d.ts +13 -0
- package/dist/lib/observability.js +160 -6
- package/dist/lib/paths.d.ts +7 -0
- package/dist/lib/paths.js +28 -0
- package/dist/lib/recipes-discovery.js +32 -24
- package/dist/lib/recipes-usage.d.ts +18 -6
- package/dist/lib/recipes-usage.js +445 -34
- package/dist/lib/review-control.d.ts +14 -0
- package/dist/lib/review-control.js +111 -0
- package/dist/lib/review-diagnostics.d.ts +11 -0
- package/dist/lib/review-diagnostics.js +148 -0
- package/dist/lib/review-projection.d.ts +14 -0
- package/dist/lib/review-projection.js +170 -0
- package/dist/lib/run-ui-runtime.d.ts +18 -0
- package/dist/lib/run-ui-runtime.js +123 -0
- package/dist/lib/runs-artifacts.d.ts +1 -1
- package/dist/lib/runs-artifacts.js +1 -1
- package/dist/lib/runs-control.d.ts +10 -2
- package/dist/lib/runs-control.js +37 -7
- package/dist/lib/runs-identity.d.ts +1 -1
- package/dist/lib/runs-identity.js +1 -1
- package/dist/lib/runs-index.d.ts +11 -2
- package/dist/lib/runs-index.js +46 -23
- package/dist/lib/runs-mailbox.d.ts +1 -1
- package/dist/lib/runs-mailbox.js +1 -1
- package/dist/lib/runs-messages.d.ts +1 -1
- package/dist/lib/runs-messages.js +1 -1
- package/dist/lib/runs-outbox.d.ts +1 -1
- package/dist/lib/runs-outbox.js +1 -1
- package/dist/lib/runs-ownership.d.ts +1 -1
- package/dist/lib/runs-ownership.js +18 -4
- package/dist/lib/runs-parent-teardown.d.ts +51 -0
- package/dist/lib/runs-parent-teardown.js +172 -0
- package/dist/lib/runs-process.d.ts +1 -1
- package/dist/lib/runs-process.js +5 -4
- package/dist/lib/runs-retention.d.ts +1 -1
- package/dist/lib/runs-retention.js +1 -1
- package/dist/lib/runs-start.d.ts +5 -3
- package/dist/lib/runs-start.js +7 -48
- package/dist/lib/runs-status.d.ts +5 -3
- package/dist/lib/runs-status.js +7 -6
- package/dist/lib/runtime.d.ts +7 -1
- package/dist/lib/runtime.js +32 -18
- package/dist/lib/tool-review-lineage-transaction.d.ts +27 -0
- package/dist/lib/tool-review-lineage-transaction.js +597 -0
- package/dist/lib/tool-review-lineage.d.ts +24 -0
- package/dist/lib/tool-review-lineage.js +98 -0
- package/dist/lib/tool-review-scheduler.d.ts +80 -0
- package/dist/lib/tool-review-scheduler.js +494 -0
- package/dist/lib/tool-review-transaction.d.ts +50 -0
- package/dist/lib/tool-review-transaction.js +362 -0
- package/dist/lib/tool-review.d.ts +56 -0
- package/dist/lib/tool-review.js +197 -0
- package/dist/lib/tools-inspect.js +28 -4
- package/dist/lib/tools-local.js +21 -4
- package/dist/lib/tools-message.d.ts +1 -0
- package/dist/lib/tools-message.js +29 -17
- package/dist/lib/tools-spawn.js +14 -2
- package/dist/lib/tools.d.ts +1 -0
- package/dist/lib/tools.js +1 -0
- package/dist/recipes/draft-review.json +24 -0
- package/dist/recipes/tool-review.json +24 -0
- package/dist/scripts/async-runner.mjs +9 -9
- package/dist/scripts/build-dist.mjs +6 -1
- package/dist/scripts/conformance.mjs +6 -1
- package/dist/scripts/recipe-utils.mjs +3 -3
- package/dist/scripts/release-gates.mjs +165 -0
- package/dist/skills/actors/SKILL.md +9 -8
- package/dist/skills/swarm/SKILL.md +1 -1
- package/docs/actor-inspector.md +20 -11
- package/docs/async-runs.md +16 -2
- package/docs/recipe-library.md +7 -3
- package/docs/template-recipes.md +4 -11
- package/docs/tool-registry.md +13 -4
- package/index.ts +27 -142
- package/lib/async-runs.ts +283 -67
- package/lib/automatic-review-runtime.ts +135 -0
- package/lib/command-templates.ts +1 -1
- package/lib/draft-consolidation-transaction.ts +821 -0
- package/lib/draft-consolidation.ts +181 -0
- package/lib/draft-review.ts +325 -0
- package/lib/draft-sleep.ts +576 -0
- package/lib/file-state.ts +143 -19
- package/lib/inspector-actions.ts +79 -0
- package/lib/inspector-command.ts +54 -0
- package/lib/inspector-overlay.ts +377 -91
- package/lib/inspector.ts +73 -1
- package/lib/observability.ts +194 -5
- package/lib/paths.ts +43 -0
- package/lib/recipes-discovery.ts +34 -26
- package/lib/recipes-usage.ts +569 -40
- package/lib/review-control.ts +137 -0
- package/lib/review-diagnostics.ts +164 -0
- package/lib/review-projection.ts +200 -0
- package/lib/run-ui-runtime.ts +153 -0
- package/lib/runs-artifacts.ts +1 -1
- package/lib/runs-control.ts +69 -6
- package/lib/runs-identity.ts +1 -1
- package/lib/runs-index.ts +57 -21
- package/lib/runs-mailbox.ts +1 -1
- package/lib/runs-messages.ts +1 -1
- package/lib/runs-outbox.ts +1 -1
- package/lib/runs-ownership.ts +23 -4
- package/lib/runs-parent-teardown.ts +257 -0
- package/lib/runs-process.ts +5 -4
- package/lib/runs-retention.ts +1 -1
- package/lib/runs-start.ts +13 -68
- package/lib/runs-status.ts +17 -8
- package/lib/runtime.ts +34 -17
- package/lib/tool-review-lineage-transaction.ts +881 -0
- package/lib/tool-review-lineage.ts +145 -0
- package/lib/tool-review-scheduler.ts +635 -0
- package/lib/tool-review-transaction.ts +563 -0
- package/lib/tool-review.ts +270 -0
- package/lib/tools-inspect.ts +35 -4
- package/lib/tools-local.ts +29 -4
- package/lib/tools-message.ts +45 -30
- package/lib/tools-spawn.ts +22 -2
- package/lib/tools.ts +5 -0
- package/package.json +5 -3
- package/recipes/draft-review.json +24 -0
- package/recipes/tool-review.json +24 -0
- package/scripts/async-runner.mjs +9 -9
- package/scripts/build-dist.mjs +6 -1
- package/scripts/conformance.mjs +6 -1
- package/scripts/recipe-utils.mjs +3 -3
- package/scripts/release-gates.mjs +165 -0
- package/skills/actors/SKILL.md +9 -8
- package/skills/swarm/SKILL.md +1 -1
|
@@ -6,11 +6,13 @@
|
|
|
6
6
|
import { execFileSync } from "node:child_process";
|
|
7
7
|
import { existsSync, readFileSync } from "node:fs";
|
|
8
8
|
import { dirname, join } from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
9
10
|
import * as AsyncRuns from "./async-runs.js";
|
|
10
11
|
import * as Limits from "./limits.js";
|
|
11
12
|
import * as Messages from "./messages.js";
|
|
12
13
|
import * as Paths from "./paths.js";
|
|
13
14
|
import * as RecipesDiscovery from "./recipes-discovery.js";
|
|
15
|
+
import * as ReviewDiagnostics from "./review-diagnostics.js";
|
|
14
16
|
import * as Rooms from "./rooms.js";
|
|
15
17
|
import * as Schema from "./schema.js";
|
|
16
18
|
import * as ToolsAccess from "./tools-access.js";
|
|
@@ -18,6 +20,14 @@ import * as ToolsMailbox from "./tools-mailbox.js";
|
|
|
18
20
|
import * as ToolsResponse from "./tools-response.js";
|
|
19
21
|
const asRecord = ToolsResponse.asRecord;
|
|
20
22
|
const maybeJsonText = ToolsResponse.maybeJsonText;
|
|
23
|
+
function compactAutomaticReviewDiagnostics(diagnostics) {
|
|
24
|
+
const draft = asRecord(diagnostics.draft_review);
|
|
25
|
+
const tool = asRecord(diagnostics.tool_review);
|
|
26
|
+
return [
|
|
27
|
+
`automatic_reviews draft=${String(draft.phase ?? "idle")} tool=${String(tool.phase ?? "idle")}`,
|
|
28
|
+
`lineages=${String(diagnostics.lineage_count ?? 0)} revision_snapshots=${String(diagnostics.revision_snapshots ?? 0)}`,
|
|
29
|
+
].join("\n");
|
|
30
|
+
}
|
|
21
31
|
function compactRunMessages(messages) {
|
|
22
32
|
if (messages.length === 0)
|
|
23
33
|
return "\n(no actor messages)";
|
|
@@ -220,8 +230,9 @@ function getPiActorsRuntimeStatus() {
|
|
|
220
230
|
catch {
|
|
221
231
|
git_commit = undefined;
|
|
222
232
|
}
|
|
223
|
-
const entrypoint =
|
|
233
|
+
const entrypoint = fileURLToPath(import.meta.url);
|
|
224
234
|
return {
|
|
235
|
+
automatic_recipe_review: Paths.isAutomaticRecipeReviewEnabled(),
|
|
225
236
|
entrypoint,
|
|
226
237
|
git_commit,
|
|
227
238
|
mode: entrypoint.includes("/dist/") ? "dist" : "source",
|
|
@@ -233,7 +244,7 @@ function getPiActorsRuntimeStatus() {
|
|
|
233
244
|
};
|
|
234
245
|
}
|
|
235
246
|
function compactPiActorsRuntimeStatus(status) {
|
|
236
|
-
return `\npi-actors version=${String(status.version)} mode=${String(status.mode)} path=${String(status.package_root)} entrypoint=${String(status.entrypoint)}${status.git_commit ? ` git=${String(status.git_commit)}` : ""}`;
|
|
247
|
+
return `\npi-actors version=${String(status.version)} mode=${String(status.mode)} automatic_review=${String(status.automatic_recipe_review)} path=${String(status.package_root)} entrypoint=${String(status.entrypoint)}${status.git_commit ? ` git=${String(status.git_commit)}` : ""}`;
|
|
237
248
|
}
|
|
238
249
|
function isStaleClaim(message, now) {
|
|
239
250
|
if (message.status !== "claimed")
|
|
@@ -436,8 +447,21 @@ export function createInspectToolDefinition(deps = {}) {
|
|
|
436
447
|
if (view !== "status" &&
|
|
437
448
|
view !== "summary" &&
|
|
438
449
|
view !== "doctor" &&
|
|
439
|
-
view !== "imports"
|
|
440
|
-
|
|
450
|
+
view !== "imports" &&
|
|
451
|
+
view !== "reviews") {
|
|
452
|
+
throw new Error("inspect recipes supports view=status, view=summary, view=doctor, view=imports, or view=reviews.");
|
|
453
|
+
}
|
|
454
|
+
if (view === "reviews") {
|
|
455
|
+
const diagnostics = ReviewDiagnostics.readAutomaticReviewDiagnostics({
|
|
456
|
+
recipeRoot: deps.recipeRoot ?? Paths.getRecipeRoot(),
|
|
457
|
+
});
|
|
458
|
+
return {
|
|
459
|
+
content: [{
|
|
460
|
+
type: "text",
|
|
461
|
+
text: maybeJsonText(diagnostics, input.verbose === true, compactAutomaticReviewDiagnostics(diagnostics)),
|
|
462
|
+
}],
|
|
463
|
+
details: diagnostics,
|
|
464
|
+
};
|
|
441
465
|
}
|
|
442
466
|
const discovered = RecipesDiscovery.discoverRecipeSources([
|
|
443
467
|
{
|
package/dist/lib/tools-local.js
CHANGED
|
@@ -100,6 +100,10 @@ export function createRuntimeToolDefinition(cfg, exec) {
|
|
|
100
100
|
}
|
|
101
101
|
if (isAsyncRecipe)
|
|
102
102
|
paramSchema.run_id = Schema.stringSchema("Optional run id override for this async template recipe invocation.");
|
|
103
|
+
if (isAsyncRecipe) {
|
|
104
|
+
paramSchema.correlation_id = Schema.stringSchema("Optional workflow correlation id preserved in terminal follow-up delivery.");
|
|
105
|
+
paramSchema.transport_context = Schema.looseObjectSchema("Optional originating transport route preserved for detached terminal follow-up.");
|
|
106
|
+
}
|
|
103
107
|
return {
|
|
104
108
|
name: cfg.name,
|
|
105
109
|
label: cfg.name,
|
|
@@ -108,13 +112,15 @@ export function createRuntimeToolDefinition(cfg, exec) {
|
|
|
108
112
|
promptSnippet: isRecipe
|
|
109
113
|
? Prompts.formatRecipeToolPromptSnippet(cfg.recipe?.name ?? String(cfg.template), isAsyncRecipe)
|
|
110
114
|
: Prompts.formatRegisteredToolPromptSnippet(cfg.template),
|
|
111
|
-
async execute(
|
|
115
|
+
async execute(toolCallId, params, signal, _onUpdate, ctx) {
|
|
112
116
|
try {
|
|
113
|
-
if (cfg.sourcePath
|
|
114
|
-
RecipesUsage.recordRecipeLaunch(cfg.sourcePath, new Date(), "tool")
|
|
117
|
+
if (cfg.sourcePath &&
|
|
118
|
+
!RecipesUsage.recordRecipeLaunch(cfg.sourcePath, new Date(), "tool")) {
|
|
119
|
+
throw new Error(`Recipe launch rejected because its source changed during activation: ${cfg.sourcePath}. Reload recipe tools and retry.`);
|
|
120
|
+
}
|
|
115
121
|
if (isAsyncRecipe) {
|
|
116
122
|
const input = params;
|
|
117
|
-
const { run_id, ...values } = input;
|
|
123
|
+
const { correlation_id, run_id, transport_context, ...values } = input;
|
|
118
124
|
const base = cfg.recipe ? cfg.recipe : { file: String(cfg.template) };
|
|
119
125
|
const runId = typeof run_id === "string" && run_id.trim()
|
|
120
126
|
? run_id.trim()
|
|
@@ -122,6 +128,17 @@ export function createRuntimeToolDefinition(cfg, exec) {
|
|
|
122
128
|
const meta = AsyncRuns.startRun({
|
|
123
129
|
...base,
|
|
124
130
|
launch_source: "tool",
|
|
131
|
+
launch_correlation: {
|
|
132
|
+
...(typeof correlation_id === "string"
|
|
133
|
+
? { correlation_id } : {}),
|
|
134
|
+
tool_call_id: toolCallId,
|
|
135
|
+
},
|
|
136
|
+
...(transport_context &&
|
|
137
|
+
typeof transport_context === "object" &&
|
|
138
|
+
!Array.isArray(transport_context)
|
|
139
|
+
? {
|
|
140
|
+
transport_context: transport_context,
|
|
141
|
+
} : {}),
|
|
125
142
|
ownerId: getRunOwnerId(ctx),
|
|
126
143
|
run_id: runId,
|
|
127
144
|
tool: cfg.name,
|
|
@@ -5,5 +5,6 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export interface ActorMessageToolDeps<TContext = unknown> {
|
|
7
7
|
getTool?: (name: string) => any | undefined;
|
|
8
|
+
handleRuntimeMessage?: (type: string, body: unknown) => Record<string, unknown>;
|
|
8
9
|
}
|
|
9
10
|
export declare function createActorMessageToolDefinition<TContext = unknown>(deps?: ActorMessageToolDeps<TContext>): any;
|
|
@@ -274,7 +274,14 @@ export function createActorMessageToolDefinition(deps = {}) {
|
|
|
274
274
|
]
|
|
275
275
|
: [];
|
|
276
276
|
if (message.type === "control.kill") {
|
|
277
|
-
result =
|
|
277
|
+
result = typeof status.run_instance_id === "string"
|
|
278
|
+
? AsyncRuns.killRun(address.value, {
|
|
279
|
+
...(typeof status.ownerId === "string"
|
|
280
|
+
? { ownerId: status.ownerId }
|
|
281
|
+
: {}),
|
|
282
|
+
runInstanceId: status.run_instance_id,
|
|
283
|
+
})
|
|
284
|
+
: { killed: false, reason: "run generation unavailable" };
|
|
278
285
|
}
|
|
279
286
|
else if (message.type === "control.archive") {
|
|
280
287
|
result = AsyncRuns.archiveRun(address.value);
|
|
@@ -344,24 +351,29 @@ export function createActorMessageToolDefinition(deps = {}) {
|
|
|
344
351
|
};
|
|
345
352
|
}
|
|
346
353
|
else if (address.kind === "tool" && address.value) {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
throw new Error(`tool actor not found or not executable: ${address.value}`);
|
|
354
|
+
if (address.value === "pi-actors" && deps.handleRuntimeMessage) {
|
|
355
|
+
result = deps.handleRuntimeMessage(message.type, message.body);
|
|
350
356
|
}
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
357
|
+
else {
|
|
358
|
+
const tool = deps.getTool?.(address.value);
|
|
359
|
+
if (!tool || typeof tool.execute !== "function") {
|
|
360
|
+
throw new Error(`tool actor not found or not executable: ${address.value}`);
|
|
361
|
+
}
|
|
362
|
+
const toolParams = messageBodyToToolParams(message);
|
|
363
|
+
let toolResult;
|
|
364
|
+
try {
|
|
365
|
+
toolResult = await tool.execute(`message:${message.type}`, toolParams, _signal, _onUpdate, ctx);
|
|
366
|
+
}
|
|
367
|
+
catch (error) {
|
|
368
|
+
throw formatToolActorFailure(address.value, message, toolParams, error);
|
|
369
|
+
}
|
|
370
|
+
result = {
|
|
371
|
+
invoked: true,
|
|
372
|
+
sent: true,
|
|
373
|
+
tool: address.value,
|
|
374
|
+
tool_result: toolResult,
|
|
375
|
+
};
|
|
358
376
|
}
|
|
359
|
-
result = {
|
|
360
|
-
invoked: true,
|
|
361
|
-
sent: true,
|
|
362
|
-
tool: address.value,
|
|
363
|
-
tool_result: toolResult,
|
|
364
|
-
};
|
|
365
377
|
}
|
|
366
378
|
else if (address.kind === "coordinator" || address.kind === "session") {
|
|
367
379
|
if (!message.from) {
|
package/dist/lib/tools-spawn.js
CHANGED
|
@@ -6,10 +6,12 @@
|
|
|
6
6
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
7
7
|
import { join } from "node:path";
|
|
8
8
|
import * as AsyncRuns from "./async-runs.js";
|
|
9
|
+
import { withFileMutationLock } from "./file-state.js";
|
|
9
10
|
import * as Messages from "./messages.js";
|
|
10
11
|
import * as ModelContext from "./model-context.js";
|
|
11
12
|
import * as Paths from "./paths.js";
|
|
12
13
|
import * as RecipesDiscovery from "./recipes-discovery.js";
|
|
14
|
+
import * as RecipesUsage from "./recipes-usage.js";
|
|
13
15
|
import * as Rooms from "./rooms.js";
|
|
14
16
|
import * as Schema from "./schema.js";
|
|
15
17
|
import * as ToolsResponse from "./tools-response.js";
|
|
@@ -62,7 +64,8 @@ function writeSpawnDraftRecipe(input, meta) {
|
|
|
62
64
|
...(defaults ? { defaults } : {}),
|
|
63
65
|
template: input.template,
|
|
64
66
|
};
|
|
65
|
-
writeFileSync(path, `${JSON.stringify(recipe, null, 2)}\n`, { flag: "wx" });
|
|
67
|
+
withFileMutationLock(root, () => withFileMutationLock(path, () => writeFileSync(path, `${JSON.stringify(recipe, null, 2)}\n`, { flag: "wx" })));
|
|
68
|
+
RecipesUsage.recordRecipeLaunch(path, new Date(), "spawn", Paths.getRecipeRoot());
|
|
66
69
|
return path;
|
|
67
70
|
}
|
|
68
71
|
function enhanceSpawnRecipeError(error, recipe) {
|
|
@@ -92,6 +95,7 @@ export function createSpawnToolDefinition() {
|
|
|
92
95
|
parameters: Schema.objectSchema({
|
|
93
96
|
artifacts: Schema.looseObjectSchema("Optional named artifact paths for the spawned actor."),
|
|
94
97
|
as: Schema.stringSchema("Optional actor address for the spawned run, e.g. run:<id>."),
|
|
98
|
+
correlation_id: Schema.stringSchema("Optional workflow correlation id preserved in terminal follow-up delivery."),
|
|
95
99
|
file: Schema.stringSchema("Optional template recipe JSON file. Bare names resolve under ~/.pi/agent/recipes."),
|
|
96
100
|
recipe: Schema.stringSchema("Alias for file; template recipe JSON file/name to spawn."),
|
|
97
101
|
template: Schema.unionSchema([
|
|
@@ -100,9 +104,10 @@ export function createSpawnToolDefinition() {
|
|
|
100
104
|
Schema.looseObjectSchema("Inline command-template object with flags such as parallel, repeat, retry, failure, and nested template."),
|
|
101
105
|
]),
|
|
102
106
|
values: Schema.looseObjectSchema("Runtime placeholder values passed to the actor."),
|
|
107
|
+
transport_context: Schema.looseObjectSchema("Optional originating transport route preserved for detached terminal follow-up, e.g. Telegram chat_id and thread_id."),
|
|
103
108
|
verbose: Schema.booleanSchema("Return full JSON instead of compact text."),
|
|
104
109
|
}, []),
|
|
105
|
-
async execute(
|
|
110
|
+
async execute(toolCallId, params, _signal, _onUpdate, ctx) {
|
|
106
111
|
const input = asRecord(params);
|
|
107
112
|
if (input.state_dir !== undefined) {
|
|
108
113
|
throw new Error("spawn.state_dir is not supported; run state is runtime-owned so run:<id> remains addressable and retention-safe.");
|
|
@@ -118,7 +123,14 @@ export function createSpawnToolDefinition() {
|
|
|
118
123
|
meta = AsyncRuns.startRun({
|
|
119
124
|
file: recipe,
|
|
120
125
|
launch_source: "spawn",
|
|
126
|
+
launch_correlation: {
|
|
127
|
+
...(typeof input.correlation_id === "string"
|
|
128
|
+
? { correlation_id: input.correlation_id } : {}),
|
|
129
|
+
tool_call_id: toolCallId,
|
|
130
|
+
},
|
|
121
131
|
ownerId: getRunOwnerId(ctx),
|
|
132
|
+
...(input.transport_context
|
|
133
|
+
? { transport_context: asRecord(input.transport_context) } : {}),
|
|
122
134
|
run_id: runId,
|
|
123
135
|
...(input.template !== undefined
|
|
124
136
|
? {
|
package/dist/lib/tools.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export interface CoreActorToolDefinitionDeps<TContext extends RuntimeToolContext
|
|
|
15
15
|
configPath: string;
|
|
16
16
|
getActiveTools: () => string[];
|
|
17
17
|
getRuntimeTool: (name: string) => unknown;
|
|
18
|
+
handleRuntimeMessage?: (type: string, body: unknown) => Record<string, unknown>;
|
|
18
19
|
registryRuntime: Pick<RegisterToolRuntimeDeps<TContext>, "getToolNameBlocker" | "getTools" | "notify" | "registerRuntimeTool">;
|
|
19
20
|
setActiveTools: (toolNames: string[]) => void;
|
|
20
21
|
}
|
package/dist/lib/tools.js
CHANGED
|
@@ -38,6 +38,7 @@ export function createCoreActorToolDefinitions(deps) {
|
|
|
38
38
|
ToolsSpawn.createSpawnToolDefinition(),
|
|
39
39
|
ToolsMessage.createActorMessageToolDefinition({
|
|
40
40
|
getTool: (name) => deps.getRuntimeTool(name),
|
|
41
|
+
handleRuntimeMessage: deps.handleRuntimeMessage,
|
|
41
42
|
}),
|
|
42
43
|
ToolsInspect.createInspectToolDefinition({
|
|
43
44
|
getTool: (name) => deps.getRuntimeTool(name),
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"async": true,
|
|
3
|
+
"args": [
|
|
4
|
+
"input_path:path",
|
|
5
|
+
"model:string",
|
|
6
|
+
"thinking:string"
|
|
7
|
+
],
|
|
8
|
+
"defaults": {
|
|
9
|
+
"model": "{current_model}",
|
|
10
|
+
"thinking": "{current_thinking}"
|
|
11
|
+
},
|
|
12
|
+
"description": "Read-only background reviewer for one immutable automatic draft-memory batch.",
|
|
13
|
+
"mailbox": {
|
|
14
|
+
"accepts": [
|
|
15
|
+
"control.kill"
|
|
16
|
+
],
|
|
17
|
+
"emits": [
|
|
18
|
+
"command.done",
|
|
19
|
+
"run.done",
|
|
20
|
+
"run.failed"
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
"template": "pi -p --model {model} --thinking {thinking} --no-tools @{input_path} Review the attached immutable pi-actors draft batch. Evaluate every draft independently using launch history, universality, flexibility, parameterization, duplication, safety, and likely future usefulness. Treat draft and sha256 fields as batch-local opaque occurrence/content-group identifiers and return them verbatim. Choose exactly one promote or discard decision per draft; there is no selection quota. Promotion requires a unique absent snake_case target and targetSha256 null. Never return recipe content: the executor derives the exact immutable captured source. Invalid or secret-touching drafts must be discarded. Include assessment with launches, universality, flexibility, futureUsefulness, and safety. Do not mutate, move, register, or delete recipes. End stdout with DRAFT_REVIEW_RESULT on its own line followed by exactly one JSON object with batchId, createdAt, and decisions."
|
|
24
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"async": true,
|
|
3
|
+
"args": [
|
|
4
|
+
"input_path:path",
|
|
5
|
+
"model:string",
|
|
6
|
+
"thinking:string"
|
|
7
|
+
],
|
|
8
|
+
"defaults": {
|
|
9
|
+
"model": "{current_model}",
|
|
10
|
+
"thinking": "{current_thinking}"
|
|
11
|
+
},
|
|
12
|
+
"description": "Read-only background reviewer for one immutable 36-tool evolution portfolio.",
|
|
13
|
+
"mailbox": {
|
|
14
|
+
"accepts": [
|
|
15
|
+
"control.kill"
|
|
16
|
+
],
|
|
17
|
+
"emits": [
|
|
18
|
+
"command.done",
|
|
19
|
+
"run.done",
|
|
20
|
+
"run.failed"
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
"template": "pi -p --model {model} --thinking {thinking} --no-tools @{input_path} Review the attached immutable pi-actors active-tool portfolio. Evaluate all 36 tools independently using lifetime and revision usage, adaptability, redundancy, safety, contract quality, and likely future usefulness. Treat source/name and sha256 fields as batch-local opaque occurrence/content-group identifiers, return them verbatim, and infer exact duplication only from a shared content group. Choose exactly one keep, evolve, demote, or merge decision per tool; there is no action quota. evolve may only rename one unchanged captured recipe, demote moves the unchanged recipe to draft memory, and merge may only deduplicate at least two canonically identical captured recipes. Never return recipe or outputs fields; replace and split require explicit operator-authored mutation and are unavailable to automatic review. Do not mutate, register, move, or delete recipes. End stdout with TOOL_REVIEW_RESULT on its own line followed by exactly one JSON object with reviewId, createdAt, and decisions."
|
|
24
|
+
}
|
|
@@ -135,7 +135,7 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
|
|
|
135
135
|
try {
|
|
136
136
|
return readdirSync(sessionDir, { withFileTypes: true })
|
|
137
137
|
.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl"))
|
|
138
|
-
.map((entry) => relative(stateDir, join(sessionDir, entry.name)))
|
|
138
|
+
.map((entry) => relative(stateDir, join(sessionDir, entry.name)).replaceAll("\\", "/"))
|
|
139
139
|
.sort();
|
|
140
140
|
} catch {
|
|
141
141
|
return [];
|
|
@@ -167,12 +167,12 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
|
|
|
167
167
|
...(recipeContext ? { recipe_context: recipeContext } : {}),
|
|
168
168
|
command: commandDetail,
|
|
169
169
|
...(materialized.promptFile
|
|
170
|
-
? { prompt_file: relative(stateDir, materialized.promptFile) }
|
|
170
|
+
? { prompt_file: relative(stateDir, materialized.promptFile).replaceAll("\\", "/") }
|
|
171
171
|
: {}),
|
|
172
172
|
...(materialized.promptBytes
|
|
173
173
|
? { prompt_bytes: materialized.promptBytes }
|
|
174
174
|
: {}),
|
|
175
|
-
...(sessionDir ? { session_dir: relative(stateDir, sessionDir) } : {}),
|
|
175
|
+
...(sessionDir ? { session_dir: relative(stateDir, sessionDir).replaceAll("\\", "/") } : {}),
|
|
176
176
|
attempts: [],
|
|
177
177
|
semantic_acceptance:
|
|
178
178
|
options?.evidenceContext?.acceptOutput === "review_evidence" ||
|
|
@@ -209,11 +209,11 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
|
|
|
209
209
|
attempts.push({
|
|
210
210
|
attempt,
|
|
211
211
|
stdout: {
|
|
212
|
-
path: relative(stateDir, stdoutFile),
|
|
212
|
+
path: relative(stateDir, stdoutFile).replaceAll("\\", "/"),
|
|
213
213
|
bytes: existsSync(stdoutFile) ? statSync(stdoutFile).size : 0,
|
|
214
214
|
},
|
|
215
215
|
stderr: {
|
|
216
|
-
path: relative(stateDir, stderrFile),
|
|
216
|
+
path: relative(stateDir, stderrFile).replaceAll("\\", "/"),
|
|
217
217
|
bytes: existsSync(stderrFile) ? statSync(stderrFile).size : 0,
|
|
218
218
|
},
|
|
219
219
|
});
|
|
@@ -248,12 +248,12 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
|
|
|
248
248
|
...(recipeContext ? { recipe_context: recipeContext } : {}),
|
|
249
249
|
command: commandDetail,
|
|
250
250
|
...(materialized.promptFile
|
|
251
|
-
? { prompt_file: relative(stateDir, materialized.promptFile) }
|
|
251
|
+
? { prompt_file: relative(stateDir, materialized.promptFile).replaceAll("\\", "/") }
|
|
252
252
|
: {}),
|
|
253
253
|
...(materialized.promptBytes
|
|
254
254
|
? { prompt_bytes: materialized.promptBytes }
|
|
255
255
|
: {}),
|
|
256
|
-
...(sessionDir ? { session_dir: relative(stateDir, sessionDir) } : {}),
|
|
256
|
+
...(sessionDir ? { session_dir: relative(stateDir, sessionDir).replaceAll("\\", "/") } : {}),
|
|
257
257
|
...(commandSessionFiles(sessionDir).length > 0
|
|
258
258
|
? { session_files: commandSessionFiles(sessionDir) }
|
|
259
259
|
: {}),
|
|
@@ -386,7 +386,7 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
|
|
|
386
386
|
command: commandDetail,
|
|
387
387
|
...(materialized.promptFile ? { prompt_file: materialized.promptFile } : {}),
|
|
388
388
|
...(materialized.promptBytes ? { prompt_bytes: materialized.promptBytes } : {}),
|
|
389
|
-
...(session.sessionDir ? { session_dir: relative(stateDir, session.sessionDir) } : {}),
|
|
389
|
+
...(session.sessionDir ? { session_dir: relative(stateDir, session.sessionDir).replaceAll("\\", "/") } : {}),
|
|
390
390
|
});
|
|
391
391
|
progressRunning();
|
|
392
392
|
const captureDir = join(stateDir, "captures", commandId);
|
|
@@ -457,7 +457,7 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
|
|
|
457
457
|
...captureDetails(result),
|
|
458
458
|
...(materialized.promptFile ? { prompt_file: materialized.promptFile } : {}),
|
|
459
459
|
...(materialized.promptBytes ? { prompt_bytes: materialized.promptBytes } : {}),
|
|
460
|
-
...(session.sessionDir ? { session_dir: relative(stateDir, session.sessionDir) } : {}),
|
|
460
|
+
...(session.sessionDir ? { session_dir: relative(stateDir, session.sessionDir).replaceAll("\\", "/") } : {}),
|
|
461
461
|
...(commandSessionFiles(session.sessionDir).length > 0
|
|
462
462
|
? { session_files: commandSessionFiles(session.sessionDir) }
|
|
463
463
|
: {}),
|
|
@@ -20,13 +20,18 @@ import { join } from "node:path";
|
|
|
20
20
|
|
|
21
21
|
function run(command, args) {
|
|
22
22
|
const result = spawnSync(command, args, { stdio: "inherit" });
|
|
23
|
+
if (result.error) throw result.error;
|
|
23
24
|
if (result.status !== 0) process.exit(result.status ?? 1);
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
rmSync("dist", { recursive: true, force: true });
|
|
27
28
|
mkdirSync("dist", { recursive: true });
|
|
28
29
|
|
|
29
|
-
run(
|
|
30
|
+
run(process.execPath, [
|
|
31
|
+
join("node_modules", "typescript", "bin", "tsc"),
|
|
32
|
+
"-p",
|
|
33
|
+
"tsconfig.build.json",
|
|
34
|
+
]);
|
|
30
35
|
|
|
31
36
|
mkdirSync(join("dist", "pi-actors"), { recursive: true });
|
|
32
37
|
writeFileSync(
|
|
@@ -28,7 +28,12 @@ function packageRoot() {
|
|
|
28
28
|
|
|
29
29
|
const result = spawnSync(
|
|
30
30
|
process.execPath,
|
|
31
|
-
[
|
|
31
|
+
[
|
|
32
|
+
"--experimental-strip-types",
|
|
33
|
+
"--test",
|
|
34
|
+
"--test-concurrency=1",
|
|
35
|
+
...conformanceSuites,
|
|
36
|
+
],
|
|
32
37
|
{ cwd: packageRoot(), encoding: "utf8", stdio: "pipe" },
|
|
33
38
|
);
|
|
34
39
|
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
statSync,
|
|
17
17
|
writeFileSync,
|
|
18
18
|
} from "node:fs";
|
|
19
|
-
import { dirname, extname, join, relative, resolve } from "node:path";
|
|
19
|
+
import { basename, dirname, extname, join, relative, resolve, sep } from "node:path";
|
|
20
20
|
|
|
21
21
|
function usage() {
|
|
22
22
|
console.error(`Usage:
|
|
@@ -101,7 +101,7 @@ function collectRunSummary(rootValue) {
|
|
|
101
101
|
const root = resolve(
|
|
102
102
|
rootValue.replace(/^~(?=\/|$)/, process.env.HOME ?? "~"),
|
|
103
103
|
);
|
|
104
|
-
const files = walkFiles(root, 2).filter((file) => file
|
|
104
|
+
const files = walkFiles(root, 2).filter((file) => basename(file) === "run.json");
|
|
105
105
|
const rows = [];
|
|
106
106
|
for (const file of files) {
|
|
107
107
|
const run = readJson(file);
|
|
@@ -118,7 +118,7 @@ function collectRunSummary(rootValue) {
|
|
|
118
118
|
const progress = readJson(join(runDir, "progress.json"));
|
|
119
119
|
const result = readJson(join(runDir, "result.json"));
|
|
120
120
|
rows.push({
|
|
121
|
-
run: run.run_id ?? run.run ?? relative(root, file).split(
|
|
121
|
+
run: run.run_id ?? run.run ?? relative(root, file).split(sep)[0],
|
|
122
122
|
status: getRunStatus(run, progress, result),
|
|
123
123
|
recipe: run.recipe ?? run.recipe_file ?? "",
|
|
124
124
|
updated:
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/** Reproducible exact-tree, Domain DAG, secret-hygiene, and ABCd release gates. */
|
|
3
|
+
|
|
4
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import { dirname, extname, join, normalize, relative, resolve } from "node:path";
|
|
7
|
+
import { spawnSync } from "node:child_process";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
|
|
10
|
+
const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
11
|
+
const temp = mkdtempSync(join(tmpdir(), "pi-actors-release-index-"));
|
|
12
|
+
const indexPath = join(temp, "index");
|
|
13
|
+
const gitEnv = { ...process.env, GIT_INDEX_FILE: indexPath };
|
|
14
|
+
const failures = [];
|
|
15
|
+
|
|
16
|
+
function run(command, args, options = {}) {
|
|
17
|
+
const result = spawnSync(command, args, {
|
|
18
|
+
cwd: root,
|
|
19
|
+
encoding: "utf8",
|
|
20
|
+
maxBuffer: 32 * 1024 * 1024,
|
|
21
|
+
...options,
|
|
22
|
+
});
|
|
23
|
+
if (result.status !== 0) {
|
|
24
|
+
throw new Error(
|
|
25
|
+
`${command} ${args.join(" ")} failed (${result.status}): ${(result.stderr || result.stdout || "no output").trim()}`,
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
return result.stdout;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function check(condition, message) {
|
|
32
|
+
if (!condition) failures.push(message);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function stagedText(path) {
|
|
36
|
+
const result = spawnSync("git", ["show", `:${path}`], {
|
|
37
|
+
cwd: root,
|
|
38
|
+
env: gitEnv,
|
|
39
|
+
encoding: "buffer",
|
|
40
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
41
|
+
});
|
|
42
|
+
if (result.status !== 0) {
|
|
43
|
+
failures.push(`could not read staged file: ${path}`);
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
const bytes = result.stdout;
|
|
47
|
+
if (bytes.includes(0)) return undefined;
|
|
48
|
+
return bytes.toString("utf8");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function sourceImports(source) {
|
|
52
|
+
return [...source.matchAll(/^import(?:\s+type)?\s+(?:[^"']+?\s+from\s+)?["']([^"']+)["'];?$/gmu)]
|
|
53
|
+
.map((match) => match[1]);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function resolveLocalImport(from, specifier, sourceSet) {
|
|
57
|
+
if (!specifier.startsWith(".")) return undefined;
|
|
58
|
+
const base = normalize(join(dirname(from), specifier)).replaceAll("\\", "/");
|
|
59
|
+
const candidates = extname(base)
|
|
60
|
+
? [base.replace(/\.js$/u, ".ts")]
|
|
61
|
+
: [`${base}.ts`, `${base}/index.ts`];
|
|
62
|
+
return candidates.find((candidate) => sourceSet.has(candidate));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
run("git", ["read-tree", "HEAD"], { env: gitEnv });
|
|
67
|
+
run("git", ["add", "-A"], { env: gitEnv });
|
|
68
|
+
run("git", ["diff", "--cached", "--check"], { env: gitEnv });
|
|
69
|
+
const files = run("git", ["ls-files", "-z"], { env: gitEnv })
|
|
70
|
+
.split("\0")
|
|
71
|
+
.filter(Boolean);
|
|
72
|
+
const fileSet = new Set(files);
|
|
73
|
+
console.log(`[release] exact temporary index: ${files.length} files`);
|
|
74
|
+
|
|
75
|
+
const secretPatterns = [
|
|
76
|
+
[/-----BEGIN(?: [A-Z0-9]+)? PRIVATE KEY-----[\s\S]{80,}?-----END(?: [A-Z0-9]+)? PRIVATE KEY-----/u, "private-key block"],
|
|
77
|
+
[/\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/u, "AWS access key"],
|
|
78
|
+
[/\bgithub_pat_[A-Za-z0-9_]{20,}\b|\bgh[pousr]_[A-Za-z0-9]{20,}\b/u, "GitHub token"],
|
|
79
|
+
[/\bglpat-[A-Za-z0-9_-]{20,}\b|\bnpm_[A-Za-z0-9]{20,}\b|\bxox[baprs]-[A-Za-z0-9-]{10,}\b/u, "service token"],
|
|
80
|
+
];
|
|
81
|
+
const publicPath = /^(?:README\.md|AGENTS\.md|BACKLOG\.md|CHANGELOG\.md|docs\/|skills\/|recipes\/)/u;
|
|
82
|
+
for (const path of files) {
|
|
83
|
+
const text = stagedText(path);
|
|
84
|
+
if (text === undefined) continue;
|
|
85
|
+
if (!path.startsWith("tests/") && !path.startsWith("fixtures/")) {
|
|
86
|
+
for (const [pattern, label] of secretPatterns) {
|
|
87
|
+
check(!pattern.test(text), `${label} detected in ${path}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (publicPath.test(path)) {
|
|
91
|
+
check(!/(?:^|[\s"'`])\/home\/[A-Za-z0-9._-]+\//u.test(text), `machine-local path in ${path}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
console.log("[release] bounded secret and public-path hygiene checked");
|
|
95
|
+
|
|
96
|
+
const sources = files.filter((path) => path === "index.ts" || (path.startsWith("lib/") && path.endsWith(".ts")));
|
|
97
|
+
const sourceSet = new Set(sources);
|
|
98
|
+
const graph = new Map();
|
|
99
|
+
for (const path of sources) {
|
|
100
|
+
const text = stagedText(path) ?? "";
|
|
101
|
+
const headerEnd = text.indexOf("*/");
|
|
102
|
+
const header = headerEnd >= 0 ? text.slice(0, headerEnd + 2) : "";
|
|
103
|
+
check(
|
|
104
|
+
header.startsWith("/**") && (header.includes("Zones:") || header.includes("Owns:")),
|
|
105
|
+
`missing domain header: ${path}`,
|
|
106
|
+
);
|
|
107
|
+
const edges = sourceImports(text)
|
|
108
|
+
.map((specifier) => resolveLocalImport(path, specifier, sourceSet))
|
|
109
|
+
.filter(Boolean);
|
|
110
|
+
graph.set(path, edges);
|
|
111
|
+
if (path.startsWith("lib/")) {
|
|
112
|
+
check(!edges.includes("index.ts"), `domain imports entrypoint: ${path}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const visiting = new Set();
|
|
116
|
+
const visited = new Set();
|
|
117
|
+
function visit(path, stack = []) {
|
|
118
|
+
if (visiting.has(path)) {
|
|
119
|
+
failures.push(`Domain DAG cycle: ${[...stack, path].join(" -> ")}`);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (visited.has(path)) return;
|
|
123
|
+
visiting.add(path);
|
|
124
|
+
for (const next of graph.get(path) ?? []) visit(next, [...stack, path]);
|
|
125
|
+
visiting.delete(path);
|
|
126
|
+
visited.add(path);
|
|
127
|
+
}
|
|
128
|
+
for (const path of sources) visit(path);
|
|
129
|
+
console.log(`[release] strict Domain DAG: ${sources.length} sources, acyclic`);
|
|
130
|
+
|
|
131
|
+
for (const path of ["README.md", "AGENTS.md", "BACKLOG.md", "CHANGELOG.md", "docs/README.md"]) {
|
|
132
|
+
check(fileSet.has(path), `missing ABCd root/context file: ${path}`);
|
|
133
|
+
}
|
|
134
|
+
const readStaged = (path) => stagedText(path) ?? "";
|
|
135
|
+
const readme = readStaged("README.md");
|
|
136
|
+
for (const path of ["AGENTS.md", "BACKLOG.md", "CHANGELOG.md", "docs/README.md"]) {
|
|
137
|
+
check(readme.includes(path), `README.md does not route to ${path}`);
|
|
138
|
+
}
|
|
139
|
+
for (const path of files.filter((candidate) => candidate.endsWith(".md"))) {
|
|
140
|
+
const text = readStaged(path);
|
|
141
|
+
for (const match of text.matchAll(/\[[^\]]*\]\(([^)]+)\)/gu)) {
|
|
142
|
+
const target = match[1].split("#", 1)[0];
|
|
143
|
+
if (!target || /^(?:https?:|mailto:)/u.test(target)) continue;
|
|
144
|
+
const resolved = normalize(join(dirname(path), decodeURIComponent(target))).replaceAll("\\", "/");
|
|
145
|
+
check(fileSet.has(resolved), `broken Markdown link: ${path} -> ${target}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const docsIndex = readStaged("docs/README.md");
|
|
149
|
+
for (const path of files.filter((candidate) => candidate.startsWith("docs/") && candidate.endsWith(".md") && candidate !== "docs/README.md")) {
|
|
150
|
+
check(docsIndex.includes(relative("docs", path).replaceAll("\\", "/")), `docs/README.md omits ${path}`);
|
|
151
|
+
}
|
|
152
|
+
console.log("[release] ABCd context roots, routing, and Markdown links checked");
|
|
153
|
+
|
|
154
|
+
if (failures.length > 0) {
|
|
155
|
+
for (const failure of failures) console.error(`[FAIL] ${failure}`);
|
|
156
|
+
process.exitCode = 1;
|
|
157
|
+
} else {
|
|
158
|
+
console.log("[release] all supplemental release gates passed");
|
|
159
|
+
}
|
|
160
|
+
} catch (error) {
|
|
161
|
+
console.error(`[FAIL] ${error instanceof Error ? error.message : String(error)}`);
|
|
162
|
+
process.exitCode = 1;
|
|
163
|
+
} finally {
|
|
164
|
+
rmSync(temp, { recursive: true, force: true });
|
|
165
|
+
}
|