@llblab/pi-actors 0.45.0 → 0.45.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 +2 -0
- package/CHANGELOG.md +6 -0
- package/README.md +2 -0
- package/dist/index.d.ts +1 -2
- package/dist/index.js +10 -110
- package/dist/lib/extension-runtime.d.ts +21 -0
- package/dist/lib/extension-runtime.js +116 -0
- package/dist/scripts/async-runner.mjs +0 -16
- package/dist/scripts/executable-block-style.d.mts +1 -0
- package/dist/scripts/executable-block-style.mjs +73 -0
- package/dist/scripts/locker.mjs +0 -21
- package/dist/scripts/recipe-utils.mjs +0 -1
- package/dist/scripts/release-gates.mjs +14 -11
- package/dist/scripts/validate-recipe.mjs +0 -1
- package/index.ts +17 -112
- package/lib/extension-runtime.ts +139 -0
- package/package.json +2 -2
- package/scripts/async-runner.mjs +0 -16
- package/scripts/executable-block-style.d.mts +1 -0
- package/scripts/executable-block-style.mjs +73 -0
- package/scripts/locker.mjs +0 -21
- package/scripts/recipe-utils.mjs +0 -1
- package/scripts/release-gates.mjs +14 -11
- package/scripts/validate-recipe.mjs +0 -1
|
@@ -7,6 +7,8 @@ import { dirname, extname, join, normalize, relative, resolve } from "node:path"
|
|
|
7
7
|
import { spawnSync } from "node:child_process";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
9
|
|
|
10
|
+
import { executableBlockBlankLines } from "./executable-block-style.mjs";
|
|
11
|
+
|
|
10
12
|
const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
11
13
|
const temp = mkdtempSync(join(tmpdir(), "pi-actors-release-index-"));
|
|
12
14
|
const indexPath = join(temp, "index");
|
|
@@ -71,7 +73,6 @@ try {
|
|
|
71
73
|
.filter(Boolean);
|
|
72
74
|
const fileSet = new Set(files);
|
|
73
75
|
console.log(`[release] exact temporary index: ${files.length} files`);
|
|
74
|
-
|
|
75
76
|
const secretPatterns = [
|
|
76
77
|
[/-----BEGIN(?: [A-Z0-9]+)? PRIVATE KEY-----[\s\S]{80,}?-----END(?: [A-Z0-9]+)? PRIVATE KEY-----/u, "private-key block"],
|
|
77
78
|
[/\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/u, "AWS access key"],
|
|
@@ -92,7 +93,17 @@ try {
|
|
|
92
93
|
}
|
|
93
94
|
}
|
|
94
95
|
console.log("[release] bounded secret and public-path hygiene checked");
|
|
95
|
-
|
|
96
|
+
const executableSources = files.filter((path) =>
|
|
97
|
+
/\.(?:[cm]?js|ts)$/u.test(path) && !path.startsWith("dist/"),
|
|
98
|
+
);
|
|
99
|
+
for (const path of executableSources) {
|
|
100
|
+
const blankLines = executableBlockBlankLines(stagedText(path) ?? "");
|
|
101
|
+
check(
|
|
102
|
+
blankLines.length === 0,
|
|
103
|
+
`blank lines inside executable blocks: ${path}:${blankLines.join(",")}`,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
console.log("[release] executable blocks contain no blank lines");
|
|
96
107
|
const removedPaths = [
|
|
97
108
|
"lib/messages.ts",
|
|
98
109
|
"lib/rooms.ts",
|
|
@@ -113,7 +124,6 @@ try {
|
|
|
113
124
|
"lib/run-inspector-overlay.ts",
|
|
114
125
|
];
|
|
115
126
|
for (const path of removedPaths) check(!fileSet.has(path), `removed surface returned: ${path}`);
|
|
116
|
-
|
|
117
127
|
const currentSurface = /^(?:lib\/|scripts\/|recipes\/|fixtures\/|skills\/|docs\/|README\.md|AGENTS\.md)/u;
|
|
118
128
|
const forbiddenPatterns = [
|
|
119
129
|
[/room:</u, "room address"],
|
|
@@ -144,7 +154,6 @@ try {
|
|
|
144
154
|
}
|
|
145
155
|
}
|
|
146
156
|
console.log("[release] removed-surface and legacy-fallback allowlists checked");
|
|
147
|
-
|
|
148
157
|
const directTraceWrite = /(?:appendFileSync|writeFileSync|writeText(?:Atomic)?)\s*\(\s*[A-Za-z0-9_.]*?(?:trace|event)(?:Path|File)/iu;
|
|
149
158
|
for (const path of files.filter((candidate) =>
|
|
150
159
|
(candidate.startsWith("lib/") && candidate.endsWith(".ts")) ||
|
|
@@ -172,7 +181,6 @@ try {
|
|
|
172
181
|
);
|
|
173
182
|
}
|
|
174
183
|
console.log("[release] canonical Trace and Control writer residue checked");
|
|
175
|
-
|
|
176
184
|
// owner | class | lock | record bound | byte bound | recovery
|
|
177
185
|
const appendOnlyOwners = new Set([
|
|
178
186
|
"lib/runs-trace.ts", // canonical Run evidence | token lock | 2048 | 4 MiB | compact valid suffix
|
|
@@ -189,7 +197,6 @@ try {
|
|
|
189
197
|
check(appendOnlyOwners.has(path), `unregistered shipped append-only writer: ${path}`);
|
|
190
198
|
for (const path of appendOnlyOwners) check(fileSet.has(path), `stale append-only owner inventory: ${path}`);
|
|
191
199
|
console.log(`[release] append-only owner inventory checked: ${appendOnlyOwners.size} source owners`);
|
|
192
|
-
|
|
193
200
|
check(
|
|
194
201
|
(stagedText("scripts/validate-recipe.mjs") ?? "").includes(
|
|
195
202
|
"qaReport.diagnostics.length === 0 && qaReport.warnings.length === 0",
|
|
@@ -197,15 +204,13 @@ try {
|
|
|
197
204
|
"Recipe QA warnings are not release-blocking",
|
|
198
205
|
);
|
|
199
206
|
console.log("[release] zero-warning Recipe QA gate checked");
|
|
200
|
-
|
|
201
|
-
const maximumShippedLines = 29_767;
|
|
207
|
+
const maximumShippedLines = 32_000;
|
|
202
208
|
const shippedPath = /^(?:lib\/|scripts\/|recipes\/|docs\/|skills\/)/u;
|
|
203
209
|
const shippedLines = files
|
|
204
210
|
.filter((path) => shippedPath.test(path))
|
|
205
211
|
.reduce((total, path) => total + (((stagedText(path) ?? "").match(/\n/gu) ?? []).length + 1), 0);
|
|
206
212
|
check(shippedLines <= maximumShippedLines, `shipped lines ${shippedLines} exceed release maximum ${maximumShippedLines}`);
|
|
207
213
|
console.log(`[release] shipped lines ${shippedLines} <= release maximum ${maximumShippedLines}`);
|
|
208
|
-
|
|
209
214
|
const sources = files.filter((path) => path === "index.ts" || (path.startsWith("lib/") && path.endsWith(".ts")));
|
|
210
215
|
const sourceSet = new Set(sources);
|
|
211
216
|
const graph = new Map();
|
|
@@ -240,7 +245,6 @@ try {
|
|
|
240
245
|
}
|
|
241
246
|
for (const path of sources) visit(path);
|
|
242
247
|
console.log(`[release] strict Domain DAG: ${sources.length} sources, acyclic`);
|
|
243
|
-
|
|
244
248
|
for (const path of ["README.md", "AGENTS.md", "BACKLOG.md", "CHANGELOG.md", "docs/README.md"]) {
|
|
245
249
|
check(fileSet.has(path), `missing ABCd root/context file: ${path}`);
|
|
246
250
|
}
|
|
@@ -263,7 +267,6 @@ try {
|
|
|
263
267
|
check(docsIndex.includes(relative("docs", path).replaceAll("\\", "/")), `docs/README.md omits ${path}`);
|
|
264
268
|
}
|
|
265
269
|
console.log("[release] ABCd context roots, routing, and Markdown links checked");
|
|
266
|
-
|
|
267
270
|
if (failures.length > 0) {
|
|
268
271
|
for (const failure of failures) console.error(`[FAIL] ${failure}`);
|
|
269
272
|
process.exitCode = 1;
|
|
@@ -187,7 +187,6 @@ export function validateRecipes(argv) {
|
|
|
187
187
|
if (!targetArg || argv.includes("--help") || argv.includes("-h")) {
|
|
188
188
|
return { help: true, ok: Boolean(targetArg), usage: validateRecipeUsage() };
|
|
189
189
|
}
|
|
190
|
-
|
|
191
190
|
const files = recipeFiles(expandPath(targetArg), all);
|
|
192
191
|
const results = files.map((file) => validateFile(file, qa));
|
|
193
192
|
const failed = results.filter((result) => !result.ok).length;
|
package/index.ts
CHANGED
|
@@ -1,124 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* pi-actors — actor runtime and persistent local tool registry for pi.
|
|
3
3
|
* Zones: composition root, pi agent, actor runtime
|
|
4
|
-
*
|
|
5
|
-
* Wraps command templates as callable pi tools, stores durable user tools as recipe files, and exposes actor orchestration across reloads and sessions.
|
|
4
|
+
* Owns extension composition and Pi event registration, not domain behavior.
|
|
6
5
|
*/
|
|
7
6
|
|
|
8
|
-
import * as
|
|
9
|
-
import * as CommandTemplates from "./lib/command-templates.ts";
|
|
7
|
+
import * as ExtensionRuntime from "./lib/extension-runtime.ts";
|
|
10
8
|
import * as InspectorCommand from "./lib/inspector-command.ts";
|
|
11
|
-
import * as Paths from "./lib/paths.ts";
|
|
12
9
|
import * as Pi from "./lib/pi.ts";
|
|
13
|
-
import * as Prompts from "./lib/prompts.ts";
|
|
14
|
-
import * as RecipesReferences from "./lib/recipes-references.ts";
|
|
15
|
-
import * as RunUiRuntime from "./lib/run-ui-runtime.ts";
|
|
16
|
-
import * as Runtime from "./lib/runtime.ts";
|
|
17
|
-
import * as Temp from "./lib/temp.ts";
|
|
18
|
-
import * as Tools from "./lib/tools.ts";
|
|
19
|
-
import * as ToolsResponse from "./lib/tools-response.ts";
|
|
20
10
|
|
|
21
11
|
export default function toolRegistryExtension(pi: Pi.ExtensionAPI) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
});
|
|
35
|
-
const actorToolDefinitions = new Map<string, Tools.ActorToolDefinition>();
|
|
36
|
-
const withCurrentThinkingContext = <T extends Tools.ActorToolDefinition>(
|
|
37
|
-
definition: T,
|
|
38
|
-
): T => {
|
|
39
|
-
if (typeof definition.execute !== "function") return definition;
|
|
40
|
-
const execute = definition.execute as (...args: unknown[]) => unknown;
|
|
41
|
-
return {
|
|
42
|
-
...definition,
|
|
43
|
-
execute: async (...args: unknown[]) => {
|
|
44
|
-
const nextArgs = [...args];
|
|
45
|
-
const ctx = nextArgs[4];
|
|
46
|
-
if (ctx && typeof ctx === "object") {
|
|
47
|
-
nextArgs[4] = {
|
|
48
|
-
...(ctx as Record<string, unknown>),
|
|
49
|
-
getThinkingLevel: () => pi.getThinkingLevel(),
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
try {
|
|
53
|
-
return ToolsResponse.spaceToolResult(await execute(...nextArgs));
|
|
54
|
-
} catch (error) {
|
|
55
|
-
throw ToolsResponse.spaceToolError(error);
|
|
56
|
-
}
|
|
57
|
-
},
|
|
58
|
-
} as T;
|
|
59
|
-
};
|
|
60
|
-
const runtime = Runtime.createAutoToolsRuntime({
|
|
61
|
-
configPath: Paths.EXTENSION_RUNTIME_PATHS.configPath,
|
|
62
|
-
exec: CommandTemplates.execCommandTemplate,
|
|
63
|
-
getActiveTools: () => pi.getActiveTools(),
|
|
64
|
-
registerTool: (definition) => {
|
|
65
|
-
const wrapped = withCurrentThinkingContext(definition);
|
|
66
|
-
actorToolDefinitions.set(wrapped.name, wrapped);
|
|
67
|
-
pi.registerTool(wrapped);
|
|
68
|
-
},
|
|
69
|
-
reservedToolNames: Tools.RESERVED_TOOL_NAMES,
|
|
70
|
-
setActiveTools: (toolNames) => pi.setActiveTools(toolNames),
|
|
71
|
-
});
|
|
72
|
-
const recipeReload = Runtime.createRecipeToolReloadWatcher(runtime);
|
|
73
|
-
pi.on("resources_discover", async () => {
|
|
74
|
-
const skillPaths = Paths.getExistingExtensionSkillPaths(import.meta.url);
|
|
75
|
-
if (skillPaths.length === 0) return;
|
|
76
|
-
return { skillPaths };
|
|
77
|
-
});
|
|
78
|
-
pi.on("session_start", async (_event, ctx) => {
|
|
79
|
-
// Clear the pre-overlay widget after hot reloads from older pi-actors builds.
|
|
80
|
-
ctx.ui.setWidget("zz-pi-actors-comms", undefined);
|
|
81
|
-
activeRunContext = ctx;
|
|
82
|
-
runUiRuntime.close();
|
|
83
|
-
automaticReview.close();
|
|
84
|
-
recipeReload.close();
|
|
85
|
-
await Temp.prepareExtensionTempDir(Paths.EXTENSION_RUNTIME_PATHS.tempDir);
|
|
86
|
-
if (activeRunContext !== ctx) return;
|
|
87
|
-
automaticReview.start(ctx);
|
|
88
|
-
runtime.loadTools(ctx);
|
|
89
|
-
runUiRuntime.start(ctx);
|
|
90
|
-
recipeReload.watch(ctx);
|
|
91
|
-
});
|
|
92
|
-
pi.on("agent_end", async (_event, ctx) => {
|
|
93
|
-
if (activeRunContext === ctx) automaticReview.schedule();
|
|
94
|
-
});
|
|
95
|
-
pi.on("session_shutdown", async (event, ctx) => {
|
|
96
|
-
activeRunContext = undefined;
|
|
97
|
-
automaticReview.close();
|
|
98
|
-
recipeReload.close();
|
|
99
|
-
runUiRuntime.shutdown(event.reason, ctx);
|
|
100
|
-
});
|
|
101
|
-
InspectorCommand.registerActorInspectorCommand(pi, getRunOwnerId);
|
|
102
|
-
pi.on("before_agent_start", async (event) => {
|
|
103
|
-
RecipesReferences.setActiveSkillRecipeSources(
|
|
12
|
+
const runtime = ExtensionRuntime.createActorExtensionRuntime(pi);
|
|
13
|
+
pi.on("resources_discover", async () =>
|
|
14
|
+
runtime.discoverResources(import.meta.url),
|
|
15
|
+
);
|
|
16
|
+
pi.on("session_start", async (_event, ctx) => runtime.onSessionStart(ctx));
|
|
17
|
+
pi.on("agent_end", async (_event, ctx) => runtime.onAgentEnd(ctx));
|
|
18
|
+
pi.on("session_shutdown", async (event, ctx) =>
|
|
19
|
+
runtime.onSessionShutdown(event.reason, ctx),
|
|
20
|
+
);
|
|
21
|
+
pi.on("before_agent_start", async (event) =>
|
|
22
|
+
runtime.beforeAgentStart(
|
|
23
|
+
event.systemPrompt,
|
|
104
24
|
event.systemPromptOptions.skills ?? [],
|
|
105
|
-
)
|
|
106
|
-
return {
|
|
107
|
-
systemPrompt: `${event.systemPrompt}\n\n${Prompts.ONBOARDING_SYSTEM_PROMPT}`,
|
|
108
|
-
};
|
|
109
|
-
});
|
|
110
|
-
Pi.registerToolDefinitions(
|
|
111
|
-
pi,
|
|
112
|
-
Tools.createCoreActorToolDefinitions<Pi.ExtensionContext>({
|
|
113
|
-
configPath: Paths.EXTENSION_RUNTIME_PATHS.configPath,
|
|
114
|
-
getActiveTools: () => pi.getActiveTools(),
|
|
115
|
-
getRuntimeTool: (name) =>
|
|
116
|
-
Tools.resolveActiveRuntimeTool(name, runtime.getTools(), (activeName) =>
|
|
117
|
-
actorToolDefinitions.get(activeName),
|
|
118
|
-
),
|
|
119
|
-
handleRuntimeControl: automaticReview.handleControl,
|
|
120
|
-
registryRuntime: runtime,
|
|
121
|
-
setActiveTools: (toolNames) => pi.setActiveTools(toolNames),
|
|
122
|
-
}).map(withCurrentThinkingContext),
|
|
25
|
+
),
|
|
123
26
|
);
|
|
27
|
+
InspectorCommand.registerActorInspectorCommand(pi, runtime.getRunOwnerId);
|
|
28
|
+
runtime.registerCoreTools();
|
|
124
29
|
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pi actor extension runtime coordinator.
|
|
3
|
+
* Zones: extension session lifecycle, host tool adaptation, runtime service composition
|
|
4
|
+
* Owns low-level Pi lifecycle effects and tool wrapping without owning event registration.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as AutomaticReviewRuntime from "./automatic-review-runtime.ts";
|
|
8
|
+
import * as CommandTemplates from "./command-templates.ts";
|
|
9
|
+
import * as Paths from "./paths.ts";
|
|
10
|
+
import * as Pi from "./pi.ts";
|
|
11
|
+
import * as Prompts from "./prompts.ts";
|
|
12
|
+
import * as RecipesReferences from "./recipes-references.ts";
|
|
13
|
+
import * as RunUiRuntime from "./run-ui-runtime.ts";
|
|
14
|
+
import * as Runtime from "./runtime.ts";
|
|
15
|
+
import * as Temp from "./temp.ts";
|
|
16
|
+
import * as Tools from "./tools.ts";
|
|
17
|
+
import * as ToolsResponse from "./tools-response.ts";
|
|
18
|
+
|
|
19
|
+
export interface ActorExtensionRuntime {
|
|
20
|
+
beforeAgentStart(
|
|
21
|
+
systemPrompt: string,
|
|
22
|
+
skills: RecipesReferences.ActiveSkillRecipeSource[],
|
|
23
|
+
): { systemPrompt: string };
|
|
24
|
+
discoverResources(metaUrl: string): { skillPaths: string[] } | undefined;
|
|
25
|
+
getRunOwnerId(ctx: Pi.ExtensionContext): string;
|
|
26
|
+
onAgentEnd(ctx: Pi.ExtensionContext): void;
|
|
27
|
+
onSessionShutdown(reason: string, ctx: Pi.ExtensionContext): void;
|
|
28
|
+
onSessionStart(ctx: Pi.ExtensionContext): Promise<void>;
|
|
29
|
+
registerCoreTools(): void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function createActorExtensionRuntime(
|
|
33
|
+
pi: Pi.ExtensionAPI,
|
|
34
|
+
): ActorExtensionRuntime {
|
|
35
|
+
let activeRunContext: Pi.ExtensionContext | undefined;
|
|
36
|
+
const getRunOwnerId = Pi.getSessionId;
|
|
37
|
+
const automaticReview = AutomaticReviewRuntime.createAutomaticReviewRuntime({
|
|
38
|
+
getActiveContext: () => activeRunContext,
|
|
39
|
+
getRunOwnerId,
|
|
40
|
+
getThinkingLevel: () => pi.getThinkingLevel(),
|
|
41
|
+
});
|
|
42
|
+
const runUiRuntime = RunUiRuntime.createRunUiRuntime({
|
|
43
|
+
getActiveContext: () => activeRunContext,
|
|
44
|
+
getRunOwnerId,
|
|
45
|
+
onRunEvent: automaticReview.schedule,
|
|
46
|
+
pi,
|
|
47
|
+
});
|
|
48
|
+
const actorToolDefinitions = new Map<string, Tools.ActorToolDefinition>();
|
|
49
|
+
const withCurrentThinkingContext = <T extends Tools.ActorToolDefinition>(
|
|
50
|
+
definition: T,
|
|
51
|
+
): T => {
|
|
52
|
+
if (typeof definition.execute !== "function") return definition;
|
|
53
|
+
const execute = definition.execute as (...args: unknown[]) => unknown;
|
|
54
|
+
return {
|
|
55
|
+
...definition,
|
|
56
|
+
execute: async (...args: unknown[]) => {
|
|
57
|
+
const nextArgs = [...args];
|
|
58
|
+
const ctx = nextArgs[4];
|
|
59
|
+
if (ctx && typeof ctx === "object") {
|
|
60
|
+
nextArgs[4] = {
|
|
61
|
+
...(ctx as Record<string, unknown>),
|
|
62
|
+
getThinkingLevel: () => pi.getThinkingLevel(),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
return ToolsResponse.spaceToolResult(await execute(...nextArgs));
|
|
67
|
+
} catch (error) {
|
|
68
|
+
throw ToolsResponse.spaceToolError(error);
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
} as T;
|
|
72
|
+
};
|
|
73
|
+
const runtime = Runtime.createAutoToolsRuntime({
|
|
74
|
+
configPath: Paths.EXTENSION_RUNTIME_PATHS.configPath,
|
|
75
|
+
exec: CommandTemplates.execCommandTemplate,
|
|
76
|
+
getActiveTools: () => pi.getActiveTools(),
|
|
77
|
+
registerTool: (definition) => {
|
|
78
|
+
const wrapped = withCurrentThinkingContext(definition);
|
|
79
|
+
actorToolDefinitions.set(wrapped.name, wrapped);
|
|
80
|
+
pi.registerTool(wrapped);
|
|
81
|
+
},
|
|
82
|
+
reservedToolNames: Tools.RESERVED_TOOL_NAMES,
|
|
83
|
+
setActiveTools: (toolNames) => pi.setActiveTools(toolNames),
|
|
84
|
+
});
|
|
85
|
+
const recipeReload = Runtime.createRecipeToolReloadWatcher(runtime);
|
|
86
|
+
return {
|
|
87
|
+
beforeAgentStart(systemPrompt, skills) {
|
|
88
|
+
RecipesReferences.setActiveSkillRecipeSources(skills);
|
|
89
|
+
return {
|
|
90
|
+
systemPrompt: `${systemPrompt}\n\n${Prompts.ONBOARDING_SYSTEM_PROMPT}`,
|
|
91
|
+
};
|
|
92
|
+
},
|
|
93
|
+
discoverResources(metaUrl) {
|
|
94
|
+
const skillPaths = Paths.getExistingExtensionSkillPaths(metaUrl);
|
|
95
|
+
return skillPaths.length > 0 ? { skillPaths } : undefined;
|
|
96
|
+
},
|
|
97
|
+
getRunOwnerId,
|
|
98
|
+
onAgentEnd(ctx) {
|
|
99
|
+
if (activeRunContext === ctx) automaticReview.schedule();
|
|
100
|
+
},
|
|
101
|
+
onSessionShutdown(reason, ctx) {
|
|
102
|
+
activeRunContext = undefined;
|
|
103
|
+
automaticReview.close();
|
|
104
|
+
recipeReload.close();
|
|
105
|
+
runUiRuntime.shutdown(reason, ctx);
|
|
106
|
+
},
|
|
107
|
+
async onSessionStart(ctx) {
|
|
108
|
+
ctx.ui.setWidget("zz-pi-actors-comms", undefined);
|
|
109
|
+
activeRunContext = ctx;
|
|
110
|
+
runUiRuntime.close();
|
|
111
|
+
automaticReview.close();
|
|
112
|
+
recipeReload.close();
|
|
113
|
+
await Temp.prepareExtensionTempDir(Paths.EXTENSION_RUNTIME_PATHS.tempDir);
|
|
114
|
+
if (activeRunContext !== ctx) return;
|
|
115
|
+
automaticReview.start(ctx);
|
|
116
|
+
runtime.loadTools(ctx);
|
|
117
|
+
runUiRuntime.start(ctx);
|
|
118
|
+
recipeReload.watch(ctx);
|
|
119
|
+
},
|
|
120
|
+
registerCoreTools() {
|
|
121
|
+
Pi.registerToolDefinitions(
|
|
122
|
+
pi,
|
|
123
|
+
Tools.createCoreActorToolDefinitions<Pi.ExtensionContext>({
|
|
124
|
+
configPath: Paths.EXTENSION_RUNTIME_PATHS.configPath,
|
|
125
|
+
getActiveTools: () => pi.getActiveTools(),
|
|
126
|
+
getRuntimeTool: (name) =>
|
|
127
|
+
Tools.resolveActiveRuntimeTool(
|
|
128
|
+
name,
|
|
129
|
+
runtime.getTools(),
|
|
130
|
+
(activeName) => actorToolDefinitions.get(activeName),
|
|
131
|
+
),
|
|
132
|
+
handleRuntimeControl: automaticReview.handleControl,
|
|
133
|
+
registryRuntime: runtime,
|
|
134
|
+
setActiveTools: (toolNames) => pi.setActiveTools(toolNames),
|
|
135
|
+
}).map(withCurrentThinkingContext),
|
|
136
|
+
);
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llblab/pi-actors",
|
|
3
|
-
"version": "0.45.
|
|
3
|
+
"version": "0.45.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Local Actor Kernel for Pi",
|
|
6
6
|
"keywords": [
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"sourceSkills": [
|
|
66
66
|
"./skills"
|
|
67
67
|
],
|
|
68
|
-
"image": "https://
|
|
68
|
+
"image": "https://raw.githubusercontent.com/llblab/pi-actors/main/banner.jpg"
|
|
69
69
|
},
|
|
70
70
|
"peerDependencies": {
|
|
71
71
|
"@earendil-works/pi-coding-agent": "*",
|
package/scripts/async-runner.mjs
CHANGED
|
@@ -74,7 +74,6 @@ function summarizeCommandDetail(commandDetail) {
|
|
|
74
74
|
|
|
75
75
|
export async function runAsyncRunner(stateDir = process.argv[2]) {
|
|
76
76
|
if (!stateDir) throw new Error("missing state dir");
|
|
77
|
-
|
|
78
77
|
const runPath = join(stateDir, "run.json");
|
|
79
78
|
const progressPath = join(stateDir, "progress.json");
|
|
80
79
|
const resultPath = join(stateDir, "result.json");
|
|
@@ -83,11 +82,9 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
|
|
|
83
82
|
const stderrPath = join(stateDir, "stderr.log");
|
|
84
83
|
const executionPath = join(stateDir, "execution.json");
|
|
85
84
|
const meta = JSON.parse(readFileSync(runPath, "utf8"));
|
|
86
|
-
|
|
87
85
|
function event(kind, data = {}) {
|
|
88
86
|
appendRunTraceEvent(stateDir, { kind, data });
|
|
89
87
|
}
|
|
90
|
-
|
|
91
88
|
function observation(kind, summary, data = {}, delivery = "log", level = "info") {
|
|
92
89
|
appendRunTraceEvent(stateDir, {
|
|
93
90
|
kind,
|
|
@@ -97,7 +94,6 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
|
|
|
97
94
|
...(level === "error" ? { level: "error" } : {}),
|
|
98
95
|
});
|
|
99
96
|
}
|
|
100
|
-
|
|
101
97
|
function progress(phase, extra = {}) {
|
|
102
98
|
writeJsonAtomic(progressPath, {
|
|
103
99
|
...(meta.model_policy ? { model_policy: meta.model_policy } : {}),
|
|
@@ -106,7 +102,6 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
|
|
|
106
102
|
...extra,
|
|
107
103
|
});
|
|
108
104
|
}
|
|
109
|
-
|
|
110
105
|
let activeSubagents = 0;
|
|
111
106
|
let completedSubagents = 0;
|
|
112
107
|
let promptCounter = 0;
|
|
@@ -115,7 +110,6 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
|
|
|
115
110
|
const evidenceRecords = [];
|
|
116
111
|
const stageOccurrences = new Map();
|
|
117
112
|
let reportEvidence;
|
|
118
|
-
|
|
119
113
|
function writeExecutionManifest(status) {
|
|
120
114
|
writeJsonAtomic(executionPath, {
|
|
121
115
|
version: 1,
|
|
@@ -129,7 +123,6 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
|
|
|
129
123
|
updated_at: new Date().toISOString(),
|
|
130
124
|
});
|
|
131
125
|
}
|
|
132
|
-
|
|
133
126
|
function commandSessionFiles(sessionDir) {
|
|
134
127
|
if (!sessionDir) return [];
|
|
135
128
|
try {
|
|
@@ -141,7 +134,6 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
|
|
|
141
134
|
return [];
|
|
142
135
|
}
|
|
143
136
|
}
|
|
144
|
-
|
|
145
137
|
function commandEvidenceStartRecord({
|
|
146
138
|
commandDetail,
|
|
147
139
|
commandId,
|
|
@@ -181,7 +173,6 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
|
|
|
181
173
|
: "not_required",
|
|
182
174
|
};
|
|
183
175
|
}
|
|
184
|
-
|
|
185
176
|
function commandEvidenceRecord({
|
|
186
177
|
captureDir,
|
|
187
178
|
commandDetail,
|
|
@@ -273,7 +264,6 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
|
|
|
273
264
|
: "rejected",
|
|
274
265
|
};
|
|
275
266
|
}
|
|
276
|
-
|
|
277
267
|
function auditReviewReport(text) {
|
|
278
268
|
const required = evidenceRecords
|
|
279
269
|
.filter((record) =>
|
|
@@ -294,11 +284,9 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
|
|
|
294
284
|
complete_allowed: missing.length === 0,
|
|
295
285
|
};
|
|
296
286
|
}
|
|
297
|
-
|
|
298
287
|
function getCommandDoneDelivery(result) {
|
|
299
288
|
return result.code !== 0 || activeSubagents > 0 ? "followup" : "log";
|
|
300
289
|
}
|
|
301
|
-
|
|
302
290
|
function progressRunning() {
|
|
303
291
|
progress("running", {
|
|
304
292
|
activeSubagents,
|
|
@@ -306,14 +294,12 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
|
|
|
306
294
|
failures: subagentFailures,
|
|
307
295
|
});
|
|
308
296
|
}
|
|
309
|
-
|
|
310
297
|
function promptFilePath() {
|
|
311
298
|
promptCounter += 1;
|
|
312
299
|
const dir = join(stateDir, "prompts");
|
|
313
300
|
mkdirSync(dir, { recursive: true });
|
|
314
301
|
return join(dir, `command-${String(promptCounter).padStart(3, "0")}.md`);
|
|
315
302
|
}
|
|
316
|
-
|
|
317
303
|
function readPromptText(promptFile) {
|
|
318
304
|
if (!promptFile) return undefined;
|
|
319
305
|
try {
|
|
@@ -322,7 +308,6 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
|
|
|
322
308
|
return undefined;
|
|
323
309
|
}
|
|
324
310
|
}
|
|
325
|
-
|
|
326
311
|
async function observedExec(command, args, options) {
|
|
327
312
|
captureCounter += 1;
|
|
328
313
|
const commandId = `command-${String(captureCounter).padStart(3, "0")}`;
|
|
@@ -485,7 +470,6 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
|
|
|
485
470
|
progressRunning();
|
|
486
471
|
return result;
|
|
487
472
|
}
|
|
488
|
-
|
|
489
473
|
try {
|
|
490
474
|
event("run.runner.start", { pid: process.pid });
|
|
491
475
|
progressRunning();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function executableBlockBlankLines(source: string): number[];
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/** Detect blank lines inside executable JavaScript and TypeScript blocks. */
|
|
2
|
+
|
|
3
|
+
export function executableBlockBlankLines(source) {
|
|
4
|
+
const lines = source.split(/\r?\n/u);
|
|
5
|
+
const blanks = [];
|
|
6
|
+
const stack = [];
|
|
7
|
+
let blockComment = false;
|
|
8
|
+
let currentExecutableDepth = 0;
|
|
9
|
+
let previousCode = "";
|
|
10
|
+
let quote;
|
|
11
|
+
for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
|
|
12
|
+
const line = lines[lineIndex];
|
|
13
|
+
if (
|
|
14
|
+
/^\s*$/u.test(line) &&
|
|
15
|
+
currentExecutableDepth > 0 &&
|
|
16
|
+
quote !== "`"
|
|
17
|
+
)
|
|
18
|
+
blanks.push(lineIndex + 1);
|
|
19
|
+
let code = "";
|
|
20
|
+
let regularExpression = false;
|
|
21
|
+
for (let index = 0; index < line.length; index += 1) {
|
|
22
|
+
const char = line[index];
|
|
23
|
+
const next = line[index + 1];
|
|
24
|
+
if (blockComment) {
|
|
25
|
+
if (char === "*" && next === "/") {
|
|
26
|
+
blockComment = false;
|
|
27
|
+
index += 1;
|
|
28
|
+
}
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (quote) {
|
|
32
|
+
if (char === "\\") index += 1;
|
|
33
|
+
else if (char === quote) quote = undefined;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (regularExpression) {
|
|
37
|
+
if (char === "\\") index += 1;
|
|
38
|
+
else if (char === "/") regularExpression = false;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (char === "/" && next === "*") {
|
|
42
|
+
blockComment = true;
|
|
43
|
+
index += 1;
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (char === "/" && next === "/") break;
|
|
47
|
+
if (
|
|
48
|
+
char === "/" &&
|
|
49
|
+
/^(?:|.*(?:[=(,:;!&|?{}]|\b(?:return|case|throw)))\s*$/u.test(code)
|
|
50
|
+
) {
|
|
51
|
+
regularExpression = true;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (char === '"' || char === "'" || char === "`") {
|
|
55
|
+
quote = char;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (char === "{") {
|
|
59
|
+
const prefix = code.trimEnd() || previousCode;
|
|
60
|
+
const executable =
|
|
61
|
+
/\)\s*$|=>\s*$|\b(?:else|try|finally|do)\s*$/u.test(prefix);
|
|
62
|
+
stack.push(executable);
|
|
63
|
+
if (executable) currentExecutableDepth += 1;
|
|
64
|
+
} else if (char === "}") {
|
|
65
|
+
if (stack.pop()) currentExecutableDepth -= 1;
|
|
66
|
+
}
|
|
67
|
+
code += char;
|
|
68
|
+
}
|
|
69
|
+
if (quote !== "`") quote = undefined;
|
|
70
|
+
if (code.trim()) previousCode = code.trim();
|
|
71
|
+
}
|
|
72
|
+
return blanks;
|
|
73
|
+
}
|