@llblab/pi-actors 0.58.1 → 0.58.3
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 +3 -3
- package/BACKLOG.md +2 -2
- package/CHANGELOG.md +12 -0
- package/README.md +38 -36
- package/dist/lib/draft-sleep.js +3 -3
- package/dist/lib/extension-runtime.js +7 -2
- package/dist/lib/prompts.d.ts +3 -3
- package/dist/lib/prompts.js +10 -9
- package/dist/lib/recipes-references.js +7 -7
- package/dist/lib/registry.js +27 -24
- package/dist/lib/review-control.js +1 -1
- package/dist/lib/run-delivery.d.ts +1 -1
- package/dist/lib/run-delivery.js +41 -17
- package/dist/lib/run-evidence-policy.js +2 -2
- package/dist/lib/runtime.d.ts +3 -0
- package/dist/lib/runtime.js +24 -10
- package/dist/lib/tool-review-scheduler.js +7 -7
- package/dist/lib/tools-access.js +1 -1
- package/dist/lib/tools-inspect.js +10 -10
- package/dist/lib/tools-message.d.ts +1 -1
- package/dist/lib/tools-message.js +10 -10
- package/dist/lib/tools-register.d.ts +3 -3
- package/dist/lib/tools-register.js +4 -4
- package/dist/lib/tools-response.d.ts +1 -1
- package/dist/lib/tools-response.js +42 -20
- package/dist/lib/tools-spawn.js +1 -1
- package/dist/lib/tools.d.ts +7 -0
- package/dist/lib/tools.js +25 -0
- package/dist/skills/actors/SKILL.md +29 -21
- package/dist/skills/actors/references/diagnostics.md +11 -11
- package/dist/skills/actors/references/persistent-tools.md +19 -15
- package/dist/skills/actors/references/recipes.md +1 -1
- package/dist/skills/actors/references/runs.md +10 -10
- package/dist/skills/music-player/SKILL.md +4 -4
- package/dist/skills/music-player/scripts/playback.mjs +1 -1
- package/dist/skills/recipe-memory/SKILL.md +7 -7
- package/docs/README.md +2 -2
- package/docs/actor-inspector.md +1 -1
- package/docs/agent-experience.md +8 -8
- package/docs/async-runs.md +13 -13
- package/docs/capability-memory.md +6 -6
- package/docs/coordinator-delivery.md +2 -2
- package/docs/inspection.md +21 -21
- package/docs/output-projection-map.md +1 -1
- package/docs/run-supervision.md +21 -21
- package/docs/tool-registry.md +27 -19
- package/docs/windows-process-observation.md +1 -1
- package/lib/draft-sleep.ts +3 -3
- package/lib/extension-runtime.ts +7 -5
- package/lib/prompts.ts +10 -9
- package/lib/recipes-references.ts +7 -7
- package/lib/registry.ts +26 -24
- package/lib/review-control.ts +1 -1
- package/lib/run-delivery.ts +34 -17
- package/lib/run-evidence-policy.ts +2 -2
- package/lib/runtime.ts +26 -11
- package/lib/tool-review-scheduler.ts +7 -7
- package/lib/tools-access.ts +1 -1
- package/lib/tools-inspect.ts +10 -10
- package/lib/tools-message.ts +10 -10
- package/lib/tools-register.ts +4 -4
- package/lib/tools-response.ts +39 -20
- package/lib/tools-spawn.ts +1 -1
- package/lib/tools.ts +28 -0
- package/package.json +1 -1
- package/skills/actors/SKILL.md +29 -21
- package/skills/actors/references/diagnostics.md +11 -11
- package/skills/actors/references/persistent-tools.md +19 -15
- package/skills/actors/references/recipes.md +1 -1
- package/skills/actors/references/runs.md +10 -10
- package/skills/music-player/SKILL.md +4 -4
- package/skills/music-player/scripts/playback.mjs +1 -1
- package/skills/recipe-memory/SKILL.md +7 -7
package/lib/run-delivery.ts
CHANGED
|
@@ -69,7 +69,7 @@ export interface RunCompletionBatch {
|
|
|
69
69
|
/** Retained singleton capture; absence preserves older indexed bytes. Never a new-capture selector. */
|
|
70
70
|
readonly single_result?: true;
|
|
71
71
|
/** Captured record-list shape; no public format/version choice or source authority. */
|
|
72
|
-
readonly result_layout?: "records" | "run_headers" | "headings" | "headings_flat_omissions" | "headings_outcomes" | "headings_shared_results" | "headings_shared_kinds" | "frontmatter_v1" | "frontmatter_v2" | "completion_yaml_core_v1" | "completion_yaml_files_v1" | "completion_yaml_files_v2";
|
|
72
|
+
readonly result_layout?: "records" | "run_headers" | "headings" | "headings_flat_omissions" | "headings_outcomes" | "headings_shared_results" | "headings_shared_kinds" | "frontmatter_v1" | "frontmatter_v2" | "completion_yaml_core_v1" | "completion_yaml_files_v1" | "completion_yaml_files_v2" | "completion_yaml_files_v3";
|
|
73
73
|
/** Immutable scope/order/range data, not a caller-selected format or allocation authority. */
|
|
74
74
|
readonly numbering?: RunCompletionNumbering;
|
|
75
75
|
/** Formatting version/data only; no recipient ownership, read or publication capability. */
|
|
@@ -468,7 +468,7 @@ function isFrontmatterCompletionLayout(layout: unknown): boolean {
|
|
|
468
468
|
}
|
|
469
469
|
|
|
470
470
|
function isRootedCompletionLayout(layout: unknown): boolean {
|
|
471
|
-
return layout === "completion_yaml_files_v1" || layout === "completion_yaml_files_v2";
|
|
471
|
+
return layout === "completion_yaml_files_v1" || layout === "completion_yaml_files_v2" || layout === "completion_yaml_files_v3";
|
|
472
472
|
}
|
|
473
473
|
|
|
474
474
|
function isYamlCompletionLayout(layout: unknown): boolean {
|
|
@@ -899,7 +899,7 @@ export function requestRunCompletionStream(input: { tempDir: string; ownerId: st
|
|
|
899
899
|
}
|
|
900
900
|
|
|
901
901
|
export function admitRunCompletionBatch(input: AdmitRunCompletionBatchInput): RunCompletionBatch {
|
|
902
|
-
return captureCompletionBatch(input, false, "
|
|
902
|
+
return captureCompletionBatch(input, false, "completion_yaml_files_v3");
|
|
903
903
|
}
|
|
904
904
|
|
|
905
905
|
/** Kernel-only opt-in; ordinary Pi capture stays fenced until session/lifecycle integration is admitted. */
|
|
@@ -909,10 +909,10 @@ export function captureRunCompletionStreamBatch(input: AdmitRunCompletionBatchIn
|
|
|
909
909
|
|
|
910
910
|
/** Private ordinary candidate composition; stream capture retains its separately gated codec. */
|
|
911
911
|
export function captureRunCompletionYamlCandidateBatch(input: AdmitRunCompletionBatchInput): RunCompletionBatch {
|
|
912
|
-
return captureCompletionBatch(input, false, "
|
|
912
|
+
return captureCompletionBatch(input, false, "completion_yaml_files_v3");
|
|
913
913
|
}
|
|
914
914
|
|
|
915
|
-
function captureCompletionBatch(input: AdmitRunCompletionBatchInput, numbered: boolean, layout: "frontmatter_v2" | "completion_yaml_core_v1" | "completion_yaml_files_v1" | "completion_yaml_files_v2" = "frontmatter_v2"): RunCompletionBatch {
|
|
915
|
+
function captureCompletionBatch(input: AdmitRunCompletionBatchInput, numbered: boolean, layout: "frontmatter_v2" | "completion_yaml_core_v1" | "completion_yaml_files_v1" | "completion_yaml_files_v2" | "completion_yaml_files_v3" = "frontmatter_v2"): RunCompletionBatch {
|
|
916
916
|
const now = (input.now ?? new Date()).toISOString();
|
|
917
917
|
if (Object.hasOwn(input, "presentationFormat")) throw new Error("Completion presentation selection is not supported; new batches use AX.");
|
|
918
918
|
if (Object.hasOwn(input, "numbering")) throw new Error("Completion numbering is assigned only by journal capture");
|
|
@@ -1810,8 +1810,9 @@ function rootedCompletionPath(path: CompletionYamlData, runRoot?: string): { roo
|
|
|
1810
1810
|
}
|
|
1811
1811
|
|
|
1812
1812
|
/** Body-free, read-free navigation over already selected references. Roots are presentation bases, not containment proof. */
|
|
1813
|
-
function completionRootedFiles(row: CompletionRow, member: RunCompletionBatchMember, locations?: RunDirectoryNavigation): Record<string, CompletionYamlData> {
|
|
1813
|
+
function completionRootedFiles(row: CompletionRow, member: RunCompletionBatchMember, locations?: RunDirectoryNavigation, compact = false): Record<string, CompletionYamlData> {
|
|
1814
1814
|
const technical = completionYamlReferences(row), files: Record<string, CompletionYamlData> = {};
|
|
1815
|
+
const status: string[] = [];
|
|
1815
1816
|
const groups: Array<{ root: CompletionYamlData; entries: Record<string, CompletionYamlData>[] }> = [];
|
|
1816
1817
|
const runRoot = locations?.state === "available" ? locations.directory : undefined;
|
|
1817
1818
|
const group = (root: CompletionYamlData): number => {
|
|
@@ -1838,26 +1839,41 @@ function completionRootedFiles(row: CompletionRow, member: RunCompletionBatchMem
|
|
|
1838
1839
|
if (!detail) continue;
|
|
1839
1840
|
const { source: rawSource, ...facts } = detail;
|
|
1840
1841
|
const { path, ...binding } = rawSource as Record<string, CompletionYamlData>;
|
|
1841
|
-
add(path!, { role,
|
|
1842
|
+
add(path!, compact ? { role, ...(role === "primary" ? { status: facts.text_basis === "actor_response" ? "Actor response; admission privacy" : "Exported text; admission privacy, not complete Actor output" }
|
|
1843
|
+
: role === "diagnostic_metadata" ? { status: "Recorded diagnostic metadata only; admission privacy",
|
|
1844
|
+
...(facts.recorded_fields_not_shown ? { recorded_fields_not_shown: facts.recorded_fields_not_shown } : {}) }
|
|
1845
|
+
: { status: "Declarations only; target availability/production unproven" }) } : { role, purpose, ...facts, source: binding }, rank);
|
|
1842
1846
|
delete technical[role];
|
|
1843
1847
|
}
|
|
1844
1848
|
const artifacts = technical.artifacts as { scope: string; declarations: { name: CompletionYamlData; path: CompletionYamlData }[] } | undefined;
|
|
1845
1849
|
if (artifacts) {
|
|
1846
1850
|
for (const item of artifacts.declarations)
|
|
1847
|
-
add(item.path, { role: "artifact", purpose: "Declared artifact", name: item.name, scope: artifacts.scope, state: "declared" });
|
|
1851
|
+
add(item.path, compact ? { role: "artifact", name: item.name } : { role: "artifact", purpose: "Declared artifact", name: item.name, scope: artifacts.scope, state: "declared" });
|
|
1852
|
+
if (compact) status.push("Declared artifact paths; existence/production unproven");
|
|
1848
1853
|
delete technical.artifacts;
|
|
1849
1854
|
}
|
|
1850
1855
|
if (locations?.state === "available") {
|
|
1851
1856
|
const { directory, files: selectedFiles, ...observation } = locations;
|
|
1852
|
-
files.observation = { ...observation, run: completionYamlString(locations.run), run_instance_id: completionYamlString(locations.run_instance_id),
|
|
1857
|
+
if (!compact) files.observation = { ...observation, run: completionYamlString(locations.run), run_instance_id: completionYamlString(locations.run_instance_id),
|
|
1853
1858
|
group: group(completionYamlString(directory)) };
|
|
1854
|
-
|
|
1859
|
+
if (compact) {
|
|
1860
|
+
const unavailable = selectedFiles.filter(item => item.state === "unavailable").map(item => item.role);
|
|
1861
|
+
if (unavailable.length) status.push(`Unavailable locations: ${unavailable.join(", ")}`);
|
|
1862
|
+
if (selectedFiles.some(item => item.role === "result" && item.state === "missing")) status.push("Recorded result location missing");
|
|
1863
|
+
}
|
|
1864
|
+
const useful = selectedFiles.filter(item => item.state === "present" && ["context_index", "result", "execution"].includes(item.role));
|
|
1865
|
+
if (!useful.length) useful.push(...selectedFiles.filter(item => item.state === "present" && item.role === "metadata"));
|
|
1866
|
+
for (const item of compact ? useful : selectedFiles) {
|
|
1855
1867
|
const { path, ...facts } = item;
|
|
1856
1868
|
const purpose = item.role === "result" ? "Recorded result metadata" : item.role === "execution" ? "Recorded execution and capture locations" : "Recorded Run file";
|
|
1857
|
-
add(typeof path === "string" ? completionYamlString(path) : null, { ...facts, purpose },
|
|
1869
|
+
add(typeof path === "string" ? completionYamlString(path) : null, compact ? { role: item.role } : { ...facts, purpose },
|
|
1858
1870
|
item.state === "present" && item.role === "result" ? 3 : item.state === "present" && item.role === "execution" ? 4 : Infinity, directory);
|
|
1859
1871
|
}
|
|
1860
|
-
} else if (locations)
|
|
1872
|
+
} else if (locations) {
|
|
1873
|
+
if (compact) status.push(`Run locations unavailable: ${locations.reason}`);
|
|
1874
|
+
else files.observation = { ...locations, run: completionYamlString(member.run), run_instance_id: completionYamlString(member.run_instance_id) };
|
|
1875
|
+
}
|
|
1876
|
+
if (status.length) files.status = status.join("; ");
|
|
1861
1877
|
if (groups.length) {
|
|
1862
1878
|
files.groups = groups;
|
|
1863
1879
|
}
|
|
@@ -1874,7 +1890,8 @@ function prepareCompletionYamlProjection(safe: RunCompletionBatch, candidates: r
|
|
|
1874
1890
|
references: ReadonlySet<number>, ordered: readonly RunCompletionBatchMember[]): PreparedCompletionYamlProjection {
|
|
1875
1891
|
const packet: CompletionYamlPacket = { batchId: safe.batch_id, records: [] };
|
|
1876
1892
|
const rooted = isRootedCompletionLayout(safe.result_layout);
|
|
1877
|
-
const
|
|
1893
|
+
const compact = safe.result_layout === "completion_yaml_files_v3";
|
|
1894
|
+
const localSingleton = (compact || safe.result_layout === "completion_yaml_files_v2") && !safe.numbering && candidates.length === 1;
|
|
1878
1895
|
if (!safe.batch_id.isWellFormed()) return { state: "unsupported" };
|
|
1879
1896
|
for (const [index, { row, location }] of candidates.entries()) {
|
|
1880
1897
|
const member = ordered[index]!;
|
|
@@ -1894,7 +1911,7 @@ function prepareCompletionYamlProjection(safe: RunCompletionBatch, candidates: r
|
|
|
1894
1911
|
const navigation = references.has(index) && location
|
|
1895
1912
|
? safe.file_navigation!.find(item => safe.members[item.member_index] === member)!.locations : undefined;
|
|
1896
1913
|
if (rooted) {
|
|
1897
|
-
const files = completionRootedFiles(row, member, navigation);
|
|
1914
|
+
const files = completionRootedFiles(row, member, navigation, compact);
|
|
1898
1915
|
if (Object.keys(files).length) metadata.files = files;
|
|
1899
1916
|
} else {
|
|
1900
1917
|
const technical = completionYamlReferences(row);
|
|
@@ -1923,7 +1940,7 @@ function prepareCompletionYamlProjection(safe: RunCompletionBatch, candidates: r
|
|
|
1923
1940
|
packet.records.push({ metadata, parts });
|
|
1924
1941
|
}
|
|
1925
1942
|
const service: Record<string, CompletionYamlData> = rooted ? {
|
|
1926
|
-
header_layout: safe.result_layout === "completion_yaml_files_v2" ? "rooted_files_v2" : "rooted_files_v1",
|
|
1943
|
+
header_layout: compact ? "rooted_files_v3" : safe.result_layout === "completion_yaml_files_v2" ? "rooted_files_v2" : "rooted_files_v1",
|
|
1927
1944
|
} : {};
|
|
1928
1945
|
const omitted = safe.members.length - candidates.length;
|
|
1929
1946
|
if (omitted) {
|
|
@@ -2356,7 +2373,7 @@ export function readRunCompletionPresentation(content: string, value: unknown):
|
|
|
2356
2373
|
!completionYamlKeys(hint, ["format", "content_sha256"]) || hint.format !== completionYamlCodec ||
|
|
2357
2374
|
hint.content_sha256 !== createHash("sha256").update(content).digest("hex")) return full;
|
|
2358
2375
|
const segments: NonNullable<RunCompletionPresentationView["segments"]>[number][] = [];
|
|
2359
|
-
const rooted = core.packet.service?.header_layout === "rooted_files_v1" || core.packet.service?.header_layout === "rooted_files_v2";
|
|
2376
|
+
const rooted = core.packet.service?.header_layout === "rooted_files_v1" || core.packet.service?.header_layout === "rooted_files_v2" || core.packet.service?.header_layout === "rooted_files_v3";
|
|
2360
2377
|
const facts = Object.fromEntries(Object.entries(core.packet.service ?? {}).filter(([key]) => key !== "header_layout"));
|
|
2361
2378
|
for (const [index, record] of core.packet.records.entries()) {
|
|
2362
2379
|
if (index) segments.push({ kind: "separator", text: "\n" });
|
|
@@ -2589,7 +2606,7 @@ export function encodeCompletionYamlCarrier(input: unknown): { state: "available
|
|
|
2589
2606
|
(Object.hasOwn(packet, "service") && (!completionYamlMapping(packet.service) || !Object.keys(packet.service).length)) ||
|
|
2590
2607
|
(!packet.records.length && !Object.hasOwn(packet, "service"))) return unavailable;
|
|
2591
2608
|
const localSingleton = packet.records.length === 1 && completionYamlMapping(packet.service) &&
|
|
2592
|
-
packet.service.header_layout === "rooted_files_v2" && !Object.hasOwn(packet.service, "numbering");
|
|
2609
|
+
(packet.service.header_layout === "rooted_files_v2" || packet.service.header_layout === "rooted_files_v3") && !Object.hasOwn(packet.service, "numbering");
|
|
2593
2610
|
const rows: string[] = [];
|
|
2594
2611
|
for (let index = 0; index < Math.max(1, packet.records.length); index++) {
|
|
2595
2612
|
const row: CompletionYamlData | undefined = packet.records[index];
|
|
@@ -69,8 +69,8 @@ export type RunControlAdmissionInput = {
|
|
|
69
69
|
const PENDING_CONTROL_STATUSES = new Set<RunControlStatus>(["queued", "delivered", "claimed"]);
|
|
70
70
|
const TERMINAL_CONTROL_STATUSES = new Set<RunControlStatus>(["handled", "failed"]);
|
|
71
71
|
const CONTROL_ADMISSION_NEXT_ACTIONS = [
|
|
72
|
-
"
|
|
73
|
-
"
|
|
72
|
+
"actors_inspect target=run:<id> view=control",
|
|
73
|
+
"actors_inspect target=runtime view=triage",
|
|
74
74
|
];
|
|
75
75
|
|
|
76
76
|
function isCount(value: number): boolean {
|
package/lib/runtime.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { existsSync, watch, type FSWatcher } from "node:fs";
|
|
|
8
8
|
import { basename, dirname } from "node:path";
|
|
9
9
|
|
|
10
10
|
import * as Config from "./config.ts";
|
|
11
|
+
import * as Identity from "./identity.ts";
|
|
11
12
|
import type { RegisteredToolExec } from "./execution.ts";
|
|
12
13
|
import * as Paths from "./paths.ts";
|
|
13
14
|
import type { RecipeResolutionContext } from "./recipes-context.ts";
|
|
@@ -36,7 +37,7 @@ export interface ToolRegistryRuntimeDeps {
|
|
|
36
37
|
exec: RegisteredToolExec;
|
|
37
38
|
recipeRoot?: string;
|
|
38
39
|
getActiveTools?: () => string[];
|
|
39
|
-
getAllTools?: () => Array<{ name: string }>;
|
|
40
|
+
getAllTools?: () => Array<{ name: string; sourceInfo?: { path: string } }>;
|
|
40
41
|
registerTool: (
|
|
41
42
|
definition: ReturnType<typeof ToolsLocal.createRuntimeToolDefinition>,
|
|
42
43
|
) => void;
|
|
@@ -83,6 +84,8 @@ export function createAutoToolsRuntime(
|
|
|
83
84
|
const tools = new Map<string, Config.RegisteredTool>();
|
|
84
85
|
const runtimeToolFingerprints = new Map<string, string>();
|
|
85
86
|
const runtimeTools = new Set<string>();
|
|
87
|
+
// Host declarations survive deactivation; retain their observed provenance separately.
|
|
88
|
+
const runtimeToolSources = new Map<string, string | undefined>();
|
|
86
89
|
const recipeRoot = deps.recipeRoot ?? Paths.getRecipeRoot();
|
|
87
90
|
const status: RecipeRegistryStatus = {
|
|
88
91
|
active_tool_count: 0,
|
|
@@ -99,9 +102,14 @@ export function createAutoToolsRuntime(
|
|
|
99
102
|
if (ctx.hasUI) ctx.ui.notify(message, type);
|
|
100
103
|
}
|
|
101
104
|
function getToolNameBlocker(name: string): string | undefined {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
:
|
|
105
|
+
const normalized = Identity.normalizeToolName(name);
|
|
106
|
+
if ([...deps.reservedToolNames].some((reserved) => Identity.normalizeToolName(reserved) === normalized))
|
|
107
|
+
return `Reserved tool name: ${name}`;
|
|
108
|
+
const foreign = deps.getAllTools?.().find((tool) =>
|
|
109
|
+
Identity.normalizeToolName(tool.name) === normalized &&
|
|
110
|
+
!(tool.name === name && runtimeToolSources.has(name) &&
|
|
111
|
+
tool.sourceInfo?.path === runtimeToolSources.get(name)));
|
|
112
|
+
return foreign ? `Host tool name collision: ${name} conflicts with ${foreign.name}` : undefined;
|
|
105
113
|
}
|
|
106
114
|
function getToolFingerprint(cfg: Config.RegisteredTool): string {
|
|
107
115
|
return JSON.stringify({
|
|
@@ -135,12 +143,15 @@ export function createAutoToolsRuntime(
|
|
|
135
143
|
function deactivateMissingRuntimeTools(activeNames: Set<string>): void {
|
|
136
144
|
const stale = [...runtimeTools].filter((name) => !activeNames.has(name));
|
|
137
145
|
if (stale.length === 0) return;
|
|
146
|
+
const hostTools = deps.getAllTools?.();
|
|
147
|
+
const staleOwned = stale.filter((name) => !hostTools || hostTools.some((tool) =>
|
|
148
|
+
tool.name === name && tool.sourceInfo?.path === runtimeToolSources.get(name)));
|
|
138
149
|
for (const name of stale) {
|
|
139
150
|
runtimeTools.delete(name);
|
|
140
151
|
runtimeToolFingerprints.delete(name);
|
|
141
152
|
}
|
|
142
153
|
if (!deps.getActiveTools || !deps.setActiveTools) return;
|
|
143
|
-
const staleSet = new Set(
|
|
154
|
+
const staleSet = new Set(staleOwned);
|
|
144
155
|
deps.setActiveTools(
|
|
145
156
|
deps.getActiveTools().filter((name) => !staleSet.has(name)),
|
|
146
157
|
);
|
|
@@ -157,10 +168,13 @@ export function createAutoToolsRuntime(
|
|
|
157
168
|
};
|
|
158
169
|
}
|
|
159
170
|
function registerRuntimeTool(cfg: Config.RegisteredTool): RuntimeToolActivation {
|
|
171
|
+
const blocker = getToolNameBlocker(cfg.name);
|
|
172
|
+
if (blocker) throw new Error(blocker);
|
|
160
173
|
const fingerprint = getToolFingerprint(cfg);
|
|
161
174
|
if (runtimeToolFingerprints.get(cfg.name) !== fingerprint) {
|
|
162
175
|
deps.registerTool(ToolsLocal.createRuntimeToolDefinition(cfg, deps.exec));
|
|
163
176
|
runtimeTools.add(cfg.name);
|
|
177
|
+
runtimeToolSources.set(cfg.name, deps.getAllTools?.().find((tool) => tool.name === cfg.name)?.sourceInfo?.path);
|
|
164
178
|
runtimeToolFingerprints.set(cfg.name, fingerprint);
|
|
165
179
|
}
|
|
166
180
|
if (deps.getActiveTools && deps.setActiveTools) {
|
|
@@ -215,7 +229,7 @@ export function createAutoToolsRuntime(
|
|
|
215
229
|
lines.push(...other.map((warning) => `• ${warning}`));
|
|
216
230
|
groups[groups.length] = ["other_diagnostics", start, lines.length];
|
|
217
231
|
}
|
|
218
|
-
lines.push("Next:
|
|
232
|
+
lines.push("Next: actors_inspect target=recipes view=doctor");
|
|
219
233
|
// Preserve the original layout join/coercion and its faults before projecting owned warning slots.
|
|
220
234
|
void `${lines.join("\n")}\n`;
|
|
221
235
|
let text = ToolsResponse.formatMarkdownField("report", "pi-actors recipe registry warning");
|
|
@@ -227,7 +241,7 @@ export function createAutoToolsRuntime(
|
|
|
227
241
|
: " - warning:\n - result: unavailable\n - reason: malformed_selected_text");
|
|
228
242
|
}
|
|
229
243
|
}
|
|
230
|
-
return text + "\n" + ToolsResponse.formatMarkdownField("instruction", "Next:
|
|
244
|
+
return text + "\n" + ToolsResponse.formatMarkdownField("instruction", "Next: actors_inspect target=recipes view=doctor") + "\n";
|
|
231
245
|
}
|
|
232
246
|
function loadTools(
|
|
233
247
|
ctx: RuntimeContext,
|
|
@@ -259,6 +273,7 @@ export function createAutoToolsRuntime(
|
|
|
259
273
|
const blocker = getToolNameBlocker(cfg.name);
|
|
260
274
|
if (blocker) {
|
|
261
275
|
warnings.push(blocker);
|
|
276
|
+
tools.delete(cfg.name);
|
|
262
277
|
continue;
|
|
263
278
|
}
|
|
264
279
|
registerRuntimeTool(cfg);
|
|
@@ -284,7 +299,7 @@ export function createAutoToolsRuntime(
|
|
|
284
299
|
}),
|
|
285
300
|
getToolStatus: (name) => {
|
|
286
301
|
const cfg = tools.get(name);
|
|
287
|
-
if (!cfg) return undefined;
|
|
302
|
+
if (!cfg || getToolNameBlocker(name)) return undefined;
|
|
288
303
|
const usage = cfg.sourcePath
|
|
289
304
|
? RecipesUsage.readRecipeUsage(cfg.sourcePath)
|
|
290
305
|
: undefined;
|
|
@@ -305,8 +320,8 @@ export function createAutoToolsRuntime(
|
|
|
305
320
|
next_actions: activation.callable_now
|
|
306
321
|
? [`call tool ${name}`]
|
|
307
322
|
: [
|
|
308
|
-
`
|
|
309
|
-
`
|
|
323
|
+
`actors_tool_register name=${name} update=true`,
|
|
324
|
+
`actors_inspect target=tool:${name} view=status`,
|
|
310
325
|
],
|
|
311
326
|
...(usage?.launch_kind ? { launch_kind: usage.launch_kind } : {}),
|
|
312
327
|
spawn_calls: Number(usage?.spawn_calls ?? 0),
|
|
@@ -372,7 +387,7 @@ export function createRecipeToolReloadWatcher(
|
|
|
372
387
|
setWatchStatus("failed");
|
|
373
388
|
try {
|
|
374
389
|
ctx.ui.notify(
|
|
375
|
-
"- report: Recipe live reload watcher failed\n- instruction: restart the session or use
|
|
390
|
+
"- report: Recipe live reload watcher failed\n- instruction: restart the session or use actors_tool_register again to refresh recipe tools.",
|
|
376
391
|
"warning",
|
|
377
392
|
);
|
|
378
393
|
} catch (error) {
|
|
@@ -404,7 +404,7 @@ export function applyApprovedToolReviewAtSessionBoundary(
|
|
|
404
404
|
...state,
|
|
405
405
|
failedStage: "approval_state",
|
|
406
406
|
lastError: "approved review path is unavailable",
|
|
407
|
-
nextAction: "
|
|
407
|
+
nextAction: "actors_control_apply target=runtime action=review.retry input={\"scope\":\"tool\"}",
|
|
408
408
|
phase: "processing_failed" as const,
|
|
409
409
|
updatedAt: (options.now ?? (() => new Date()))().toISOString(),
|
|
410
410
|
};
|
|
@@ -427,7 +427,7 @@ export function applyApprovedToolReviewAtSessionBoundary(
|
|
|
427
427
|
...state,
|
|
428
428
|
failedStage: "transaction",
|
|
429
429
|
lastError: `tool review transaction remained ${transaction.phase}`,
|
|
430
|
-
nextAction: "
|
|
430
|
+
nextAction: "actors_control_apply target=runtime action=review.retry input={\"scope\":\"tool\"}",
|
|
431
431
|
phase: "processing_failed" as const,
|
|
432
432
|
updatedAt: (options.now ?? (() => new Date()))().toISOString(),
|
|
433
433
|
};
|
|
@@ -458,7 +458,7 @@ export function applyApprovedToolReviewAtSessionBoundary(
|
|
|
458
458
|
...pending,
|
|
459
459
|
failedStage: "lineage",
|
|
460
460
|
lastError: error instanceof Error ? error.message : String(error),
|
|
461
|
-
nextAction: "
|
|
461
|
+
nextAction: "actors_control_apply target=runtime action=review.retry input={\"scope\":\"tool\"}",
|
|
462
462
|
};
|
|
463
463
|
writeJsonAtomic(statePath, failedPending);
|
|
464
464
|
return { outcome: "lineage_pending", state: failedPending };
|
|
@@ -478,7 +478,7 @@ export function applyApprovedToolReviewAtSessionBoundary(
|
|
|
478
478
|
...state,
|
|
479
479
|
failedStage: "transaction",
|
|
480
480
|
lastError: error instanceof Error ? error.message : String(error),
|
|
481
|
-
nextAction: "
|
|
481
|
+
nextAction: "actors_control_apply target=runtime action=review.retry input={\"scope\":\"tool\"}",
|
|
482
482
|
phase: "processing_failed" as const,
|
|
483
483
|
updatedAt: (options.now ?? (() => new Date()))().toISOString(),
|
|
484
484
|
};
|
|
@@ -541,7 +541,7 @@ export function createToolReviewScheduler(
|
|
|
541
541
|
...existing,
|
|
542
542
|
failedStage: processed.stage ?? "review_result",
|
|
543
543
|
lastError: processed.error ?? "automatic tool reviewer failed",
|
|
544
|
-
nextAction: "
|
|
544
|
+
nextAction: "actors_control_apply target=runtime action=review.retry input={\"scope\":\"tool\"}",
|
|
545
545
|
phase: processed.outcome === "processing_failed"
|
|
546
546
|
? "processing_failed"
|
|
547
547
|
: "failed",
|
|
@@ -577,7 +577,7 @@ export function createToolReviewScheduler(
|
|
|
577
577
|
attempts: existing.attempts + 1,
|
|
578
578
|
failedStage: "review_launch",
|
|
579
579
|
lastError: error instanceof Error ? error.message : String(error),
|
|
580
|
-
nextAction: "
|
|
580
|
+
nextAction: "actors_control_apply target=runtime action=review.retry input={\"scope\":\"tool\"}",
|
|
581
581
|
phase: "failed",
|
|
582
582
|
updatedAt: now().toISOString(),
|
|
583
583
|
} satisfies ToolReviewAdmissionState);
|
|
@@ -612,7 +612,7 @@ export function createToolReviewScheduler(
|
|
|
612
612
|
failedStage: "review_launch",
|
|
613
613
|
inputPath: batch.inputPath,
|
|
614
614
|
lastError: error instanceof Error ? error.message : String(error),
|
|
615
|
-
nextAction: "
|
|
615
|
+
nextAction: "actors_control_apply target=runtime action=review.retry input={\"scope\":\"tool\"}",
|
|
616
616
|
phase: "failed",
|
|
617
617
|
reviewerInputPath: batch.reviewerInputPath,
|
|
618
618
|
reviewId: batch.reviewId,
|
package/lib/tools-access.ts
CHANGED
|
@@ -55,7 +55,7 @@ export function sessionMismatchError(input: {
|
|
|
55
55
|
),
|
|
56
56
|
{
|
|
57
57
|
current_session: input.currentSession,
|
|
58
|
-
hint: "
|
|
58
|
+
hint: "actors_inspect target=runtime view=runs",
|
|
59
59
|
owner_session: input.expectedSession,
|
|
60
60
|
reason: "session_mismatch",
|
|
61
61
|
run: input.run,
|
package/lib/tools-inspect.ts
CHANGED
|
@@ -171,13 +171,13 @@ function runtimeTriage(
|
|
|
171
171
|
trace_diagnostic_count: traceDiagnostics.length, trace_diagnostics: traceDiagnostics.slice(0, 40),
|
|
172
172
|
attention_events: attentionEvents.slice(-40).reverse(),
|
|
173
173
|
next_actions: [
|
|
174
|
-
...(failed.length ? ["
|
|
174
|
+
...(failed.length ? ["actors_inspect target=runtime view=runs status=failed"] : []),
|
|
175
175
|
...new Set([...staleControls, ...backpressuredRuns].slice(0, 3).map((entry) =>
|
|
176
|
-
`
|
|
176
|
+
`actors_inspect target=run:${entry.run} view=control`,
|
|
177
177
|
)),
|
|
178
178
|
...(attentionEvents.length
|
|
179
179
|
? attentionEvents.slice(-3).map((entry) =>
|
|
180
|
-
`
|
|
180
|
+
`actors_inspect target=run:${String(entry.run)} view=trace source=runtime`,
|
|
181
181
|
)
|
|
182
182
|
: []),
|
|
183
183
|
],
|
|
@@ -247,7 +247,7 @@ function formatInspectFailure(
|
|
|
247
247
|
return { omittable, full, selected };
|
|
248
248
|
});
|
|
249
249
|
// Only producer-known read-only inspect literals become a typed next, never a parsed action.
|
|
250
|
-
const typedNext = next ? { next: { tool: "
|
|
250
|
+
const typedNext = next ? { next: { tool: "actors_inspect", target: next[0], view: next[1] } } : undefined;
|
|
251
251
|
const nextFallback = { next_action: omitted };
|
|
252
252
|
const document = () => ({ ...Object.assign({}, ...parts.map((part) => part.selected)),
|
|
253
253
|
...(typedNext ?? nextFallback) });
|
|
@@ -412,15 +412,15 @@ function inspectRecipeIdentity(
|
|
|
412
412
|
...(rejectedReason ? { rejected_reason: rejectedReason } : {}),
|
|
413
413
|
next_actions: resolvable
|
|
414
414
|
? [
|
|
415
|
-
`
|
|
416
|
-
`
|
|
415
|
+
`actors_run_spawn recipe=${identity}`,
|
|
416
|
+
`actors_tool_register name=<tool-name> from=${identity}`,
|
|
417
417
|
]
|
|
418
418
|
: roots.length === 0 && skill
|
|
419
419
|
? [
|
|
420
420
|
`activate Skill ${skill}`,
|
|
421
|
-
`
|
|
421
|
+
`actors_inspect target=recipes view=doctor identity=${identity}`,
|
|
422
422
|
]
|
|
423
|
-
: ["
|
|
423
|
+
: ["actors_inspect target=recipes view=imports verbose=true"],
|
|
424
424
|
};
|
|
425
425
|
}
|
|
426
426
|
|
|
@@ -707,7 +707,7 @@ function compactResult(target: string, view: string, details: Record<string, unk
|
|
|
707
707
|
? String(details.next_actions[0])
|
|
708
708
|
: details.callable_now === true
|
|
709
709
|
? `call tool ${name}`
|
|
710
|
-
: `
|
|
710
|
+
: `actors_tool_register name=${name} update=true`;
|
|
711
711
|
const hint = ToolsResponse.nextActionMetadata(next, Limits.RUN_DELIVERY_MODEL_MAX_BYTES);
|
|
712
712
|
const required = compactArguments("required_args", details.required_args);
|
|
713
713
|
const optional = compactArguments("optional_args", details.optional_args);
|
|
@@ -727,7 +727,7 @@ export function createInspectToolDefinition<TContext = unknown>(
|
|
|
727
727
|
deps: InspectToolDeps<TContext> = {},
|
|
728
728
|
): any {
|
|
729
729
|
return {
|
|
730
|
-
name: "
|
|
730
|
+
name: "actors_inspect",
|
|
731
731
|
label: "Inspect",
|
|
732
732
|
description:
|
|
733
733
|
"Inspect a Run's Recipe, Trace, or Control evidence, or runtime/recipe/tool diagnostics.",
|
package/lib/tools-message.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Public Control facade under the stable `
|
|
2
|
+
* Public Control facade under the stable `actors_control_apply` tool name.
|
|
3
3
|
* Zones: Control request validation, Run/runtime dispatch, recipient-scoped receipts
|
|
4
4
|
* Owns public Control execution; journaling, delivery, and lifecycle mutation stay in Run domains.
|
|
5
5
|
*/
|
|
@@ -52,9 +52,9 @@ function controlDocument(render: () => ControlDocument, order: readonly ControlP
|
|
|
52
52
|
|
|
53
53
|
/** Only producer-known read-only inspect literals become typed next; other advice stays opaque content. */
|
|
54
54
|
function controlNextPart(action: string): ControlPart {
|
|
55
|
-
const inspect = /^
|
|
55
|
+
const inspect = /^actors_inspect target=([^\s]+) view=([^\s]+)$/u.exec(action);
|
|
56
56
|
if (inspect) {
|
|
57
|
-
return ToolsResponse.selectYamlNode("next", { tool: "
|
|
57
|
+
return ToolsResponse.selectYamlNode("next", { tool: "actors_inspect", target: inspect[1], view: inspect[2] }, CONTROL_BUDGET);
|
|
58
58
|
}
|
|
59
59
|
const wellFormed = action.isWellFormed();
|
|
60
60
|
const omitted: ControlDocument = { next: { instruction: wellFormed ? "[omitted]" : "[unavailable]",
|
|
@@ -123,15 +123,15 @@ export function compactControlResult(
|
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
function runNextActions(run: string, result: Record<string, unknown>): string[] {
|
|
126
|
-
if (result.stopped === true) return [`
|
|
126
|
+
if (result.stopped === true) return [`actors_inspect target=run:${run} view=trace`];
|
|
127
127
|
if (result.stopped === false || result.archived === false || result.pruned === false) {
|
|
128
128
|
if (result.reason === "ownership changed" || result.reason === "run generation changed") {
|
|
129
|
-
return ["
|
|
129
|
+
return ["actors_inspect target=runtime view=runs"];
|
|
130
130
|
}
|
|
131
|
-
if (result.stopped === false && result.reason !== "not running") return [`
|
|
131
|
+
if (result.stopped === false && result.reason !== "not running") return [`actors_inspect target=run:${run} view=trace`];
|
|
132
132
|
}
|
|
133
133
|
if (result.delivery === "queued" || result.delivery === "delivered") {
|
|
134
|
-
return [`
|
|
134
|
+
return [`actors_inspect target=run:${run} view=control`];
|
|
135
135
|
}
|
|
136
136
|
return [];
|
|
137
137
|
}
|
|
@@ -148,10 +148,10 @@ export function createControlToolDefinition<TContext = unknown>(
|
|
|
148
148
|
deps: ControlToolDeps = {},
|
|
149
149
|
): any {
|
|
150
150
|
return {
|
|
151
|
-
name: "
|
|
151
|
+
name: "actors_control_apply",
|
|
152
152
|
label: "Control",
|
|
153
153
|
description:
|
|
154
|
-
"Apply one Control action
|
|
154
|
+
"Apply one Run/runtime Control action. This is not chat or peer messaging.",
|
|
155
155
|
parameters: Schema.objectSchema(
|
|
156
156
|
{
|
|
157
157
|
target: Schema.stringSchema("Run target run:<id>, or literal runtime."),
|
|
@@ -260,7 +260,7 @@ export function createControlToolDefinition<TContext = unknown>(
|
|
|
260
260
|
// Admission-era correlation is not current reply-scope proof after an awaited dispatch.
|
|
261
261
|
// Display one whole caller-owned id or an explicit whole-id omission; the warning stays literal.
|
|
262
262
|
const identity = controlSelected("run", run);
|
|
263
|
-
const next = controlNextPart("
|
|
263
|
+
const next = controlNextPart("actors_inspect target=runtime view=runs");
|
|
264
264
|
const render = (): ControlDocument => ({ ...identity.selected, action: request.action,
|
|
265
265
|
result: "request failed", delivery: "unconfirmed", handling: "unconfirmed",
|
|
266
266
|
warning: "Effects may already exist.\nDo not repeat automatically.", ...next.selected });
|
package/lib/tools-register.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Public
|
|
3
|
-
* Zones: runtime tool registration, tool registry mutation,
|
|
4
|
-
* Owns the public
|
|
2
|
+
* Public actors_tool_register behavior
|
|
3
|
+
* Zones: runtime tool registration, tool registry mutation, actors_tool_register schema
|
|
4
|
+
* Owns the public actors_tool_register definition that persists local agent capabilities
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import * as Prompts from "./prompts.ts";
|
|
@@ -24,7 +24,7 @@ export function createRegisterToolDefinition<TContext>(
|
|
|
24
24
|
deps: RegisterToolRuntimeDeps<TContext>,
|
|
25
25
|
) {
|
|
26
26
|
return {
|
|
27
|
-
name: "
|
|
27
|
+
name: "actors_tool_register",
|
|
28
28
|
label: "Register Tool",
|
|
29
29
|
description: Prompts.REGISTER_TOOL_DESCRIPTION,
|
|
30
30
|
promptSnippet: Prompts.REGISTER_TOOL_PROMPT_SNIPPET,
|