@llblab/pi-actors 0.57.0 → 0.58.0
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 +9 -1
- package/BACKLOG.md +14 -1
- package/CHANGELOG.md +11 -0
- package/README.md +9 -1
- package/dist/lib/file-state.d.ts +1 -0
- package/dist/lib/file-state.js +8 -2
- package/dist/lib/prompts.d.ts +2 -1
- package/dist/lib/prompts.js +6 -2
- package/dist/lib/recipes-discovery.d.ts +4 -0
- package/dist/lib/recipes-discovery.js +22 -0
- package/dist/lib/recipes-references.d.ts +1 -0
- package/dist/lib/recipes-references.js +1 -1
- package/dist/lib/recipes-usage.js +12 -9
- package/dist/lib/registry.d.ts +5 -1
- package/dist/lib/registry.js +199 -7
- package/dist/lib/run-delivery.d.ts +1 -1
- package/dist/lib/run-delivery.js +146 -32
- package/dist/lib/tools-inspect.js +6 -3
- package/dist/lib/tools-register.js +1 -0
- package/dist/lib/tools-response.js +6 -1
- package/dist/skills/actors/SKILL.md +11 -2
- package/dist/skills/actors/references/persistent-tools.md +20 -2
- package/dist/skills/actors/references/runs.md +1 -1
- package/dist/skills/recipe-memory/SKILL.md +6 -0
- package/docs/README.md +1 -0
- package/docs/agent-experience.md +42 -3
- package/docs/capability-memory.md +166 -0
- package/docs/coordinator-delivery.md +2 -2
- package/docs/inspection.md +3 -1
- package/docs/output-projection-map.md +4 -4
- package/docs/releasing.md +4 -0
- package/docs/tool-registry.md +20 -1
- package/lib/file-state.ts +7 -3
- package/lib/prompts.ts +7 -2
- package/lib/recipes-discovery.ts +26 -0
- package/lib/recipes-references.ts +1 -1
- package/lib/recipes-usage.ts +11 -8
- package/lib/registry.ts +195 -10
- package/lib/run-delivery.ts +130 -31
- package/lib/tools-inspect.ts +6 -3
- package/lib/tools-register.ts +1 -0
- package/lib/tools-response.ts +8 -1
- package/package.json +1 -1
- package/skills/actors/SKILL.md +11 -2
- package/skills/actors/references/persistent-tools.md +20 -2
- package/skills/actors/references/runs.md +1 -1
- package/skills/recipe-memory/SKILL.md +6 -0
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";
|
|
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";
|
|
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. */
|
|
@@ -467,8 +467,12 @@ function isFrontmatterCompletionLayout(layout: unknown): boolean {
|
|
|
467
467
|
return layout === "frontmatter_v1" || layout === "frontmatter_v2";
|
|
468
468
|
}
|
|
469
469
|
|
|
470
|
+
function isYamlCompletionLayout(layout: unknown): boolean {
|
|
471
|
+
return layout === "completion_yaml_core_v1" || layout === "completion_yaml_files_v1";
|
|
472
|
+
}
|
|
473
|
+
|
|
470
474
|
function isSharedCompletionLayout(layout: unknown): boolean {
|
|
471
|
-
return layout === "headings_shared_results" || layout === "headings_shared_kinds" || layout
|
|
475
|
+
return layout === "headings_shared_results" || layout === "headings_shared_kinds" || isYamlCompletionLayout(layout) || isFrontmatterCompletionLayout(layout);
|
|
472
476
|
}
|
|
473
477
|
|
|
474
478
|
function parseBatch(value: unknown): RunCompletionBatch {
|
|
@@ -512,7 +516,7 @@ function parseBatch(value: unknown): RunCompletionBatch {
|
|
|
512
516
|
throw new Error("Completion shared text budget is invalid");
|
|
513
517
|
}
|
|
514
518
|
const numberingDescriptor = Object.getOwnPropertyDescriptor(record, "numbering");
|
|
515
|
-
if (numberingDescriptor && (resultLayout
|
|
519
|
+
if (numberingDescriptor && (isYamlCompletionLayout(resultLayout) || !Object.hasOwn(numberingDescriptor, "value") || presentationFormat !== "ax_records" || (resultLayout !== "headings" && resultLayout !== "headings_flat_omissions" && resultLayout !== "headings_outcomes" && !isSharedCompletionLayout(resultLayout)))) {
|
|
516
520
|
throw new Error("Completion numbering requires a canonical captured heading layout");
|
|
517
521
|
}
|
|
518
522
|
const numbering = numberingDescriptor ? parseCompletionNumbering(numberingDescriptor.value, members.length) : undefined;
|
|
@@ -891,7 +895,7 @@ export function requestRunCompletionStream(input: { tempDir: string; ownerId: st
|
|
|
891
895
|
}
|
|
892
896
|
|
|
893
897
|
export function admitRunCompletionBatch(input: AdmitRunCompletionBatchInput): RunCompletionBatch {
|
|
894
|
-
return captureCompletionBatch(input, false, "
|
|
898
|
+
return captureCompletionBatch(input, false, "completion_yaml_files_v1");
|
|
895
899
|
}
|
|
896
900
|
|
|
897
901
|
/** Kernel-only opt-in; ordinary Pi capture stays fenced until session/lifecycle integration is admitted. */
|
|
@@ -901,10 +905,10 @@ export function captureRunCompletionStreamBatch(input: AdmitRunCompletionBatchIn
|
|
|
901
905
|
|
|
902
906
|
/** Private ordinary candidate composition; stream capture retains its separately gated codec. */
|
|
903
907
|
export function captureRunCompletionYamlCandidateBatch(input: AdmitRunCompletionBatchInput): RunCompletionBatch {
|
|
904
|
-
return captureCompletionBatch(input, false, "
|
|
908
|
+
return captureCompletionBatch(input, false, "completion_yaml_files_v1");
|
|
905
909
|
}
|
|
906
910
|
|
|
907
|
-
function captureCompletionBatch(input: AdmitRunCompletionBatchInput, numbered: boolean, layout: "frontmatter_v2" | "completion_yaml_core_v1" = "frontmatter_v2"): RunCompletionBatch {
|
|
911
|
+
function captureCompletionBatch(input: AdmitRunCompletionBatchInput, numbered: boolean, layout: "frontmatter_v2" | "completion_yaml_core_v1" | "completion_yaml_files_v1" = "frontmatter_v2"): RunCompletionBatch {
|
|
908
912
|
const now = (input.now ?? new Date()).toISOString();
|
|
909
913
|
if (Object.hasOwn(input, "presentationFormat")) throw new Error("Completion presentation selection is not supported; new batches use AX.");
|
|
910
914
|
if (Object.hasOwn(input, "numbering")) throw new Error("Completion numbering is assigned only by journal capture");
|
|
@@ -917,7 +921,7 @@ function captureCompletionBatch(input: AdmitRunCompletionBatchInput, numbered: b
|
|
|
917
921
|
members: input.members,
|
|
918
922
|
phase: "pending",
|
|
919
923
|
});
|
|
920
|
-
const draft: RunCompletionBatch = layout
|
|
924
|
+
const draft: RunCompletionBatch = isYamlCompletionLayout(layout)
|
|
921
925
|
? parseBatch({ ...captured, result_layout: layout }) : { ...captured, result_layout: layout };
|
|
922
926
|
return mutateJournal(input.tempDir, input.ownerId, (journal) => {
|
|
923
927
|
let batch = draft;
|
|
@@ -946,9 +950,9 @@ function captureCompletionBatch(input: AdmitRunCompletionBatchInput, numbered: b
|
|
|
946
950
|
} else if (journal.completion_stream) {
|
|
947
951
|
throw new Error("Completion stream capture requires the numbered kernel path; stream state is retained");
|
|
948
952
|
}
|
|
949
|
-
if (layout
|
|
953
|
+
if (!isYamlCompletionLayout(layout)) batch = retainSharedCompletionTexts(batch);
|
|
950
954
|
if (projectCompletionBatchMessage(batch).membersShown === batch.members.length) {
|
|
951
|
-
if (layout
|
|
955
|
+
if (isYamlCompletionLayout(layout)) batch = retainSharedCompletionTexts(batch);
|
|
952
956
|
journal.completion_batch = batch;
|
|
953
957
|
return batch;
|
|
954
958
|
}
|
|
@@ -1515,7 +1519,7 @@ function isRoutineCompletionSummary(member: RunCompletionBatchMember): boolean {
|
|
|
1515
1519
|
}
|
|
1516
1520
|
|
|
1517
1521
|
function completionRecordRow(member: RunCompletionBatchMember, index: number, byIdentity: Map<string, RunCompletionBatchMember>, flat = false, layout?: RunCompletionBatch["result_layout"], textBudget?: number): CompletionRow {
|
|
1518
|
-
const frontmatter = isFrontmatterCompletionLayout(layout) || layout
|
|
1522
|
+
const frontmatter = isFrontmatterCompletionLayout(layout) || isYamlCompletionLayout(layout);
|
|
1519
1523
|
const simpleKinds = frontmatter || layout === "headings_shared_kinds", shared = isSharedCompletionLayout(layout);
|
|
1520
1524
|
const outcomes = shared || layout === "headings_outcomes", directOmission = outcomes || layout === "headings_flat_omissions";
|
|
1521
1525
|
const run = member.run.startsWith("run:") ? member.run.slice(4) : member.run;
|
|
@@ -1780,6 +1784,83 @@ function completionYamlReferences(row: CompletionRow): Record<string, Completion
|
|
|
1780
1784
|
return technical;
|
|
1781
1785
|
}
|
|
1782
1786
|
|
|
1787
|
+
/** Split selected paths without normalize/resolve/relative: dot and symlink routes must not be rewritten. */
|
|
1788
|
+
function rootedCompletionPath(path: CompletionYamlData, runRoot?: string): { root: string; path: string } | undefined {
|
|
1789
|
+
if (typeof path !== "string" || !path.isWellFormed()) return undefined;
|
|
1790
|
+
const windows = /^(?:[A-Za-z]:[\\/]|\\\\[^\\/]+[\\/][^\\/]+[\\/])/u.test(path);
|
|
1791
|
+
const separator = windows ? /[\\/]/u : /\//u;
|
|
1792
|
+
if (!windows && !path.startsWith("/")) return undefined;
|
|
1793
|
+
if (path.split(separator).some(part => part === "." || part === "..")) return undefined;
|
|
1794
|
+
if (runRoot && path.startsWith(runRoot)) {
|
|
1795
|
+
const tail = path.slice(runRoot.length);
|
|
1796
|
+
if (separator.test(runRoot.slice(-1)) ? tail.length > 0 : separator.test(tail[0] ?? "")) {
|
|
1797
|
+
const relative = separator.test(runRoot.slice(-1)) ? tail : tail.slice(1);
|
|
1798
|
+
if (relative && !separator.test(relative[0]!)) return { root: runRoot, path: relative };
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
let split = -1;
|
|
1802
|
+
for (let i = 0; i < path.length; i++) if (separator.test(path[i]!)) split = i;
|
|
1803
|
+
if (split < 0 || split === path.length - 1) return undefined;
|
|
1804
|
+
const end = split === 0 || (windows && split === 2 && path[1] === ":") ? split + 1 : split;
|
|
1805
|
+
return { root: path.slice(0, end), path: path.slice(split + 1) };
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
/** Body-free, read-free navigation over already selected references. Roots are presentation bases, not containment proof. */
|
|
1809
|
+
function completionRootedFiles(row: CompletionRow, member: RunCompletionBatchMember, locations?: RunDirectoryNavigation): Record<string, CompletionYamlData> {
|
|
1810
|
+
const technical = completionYamlReferences(row), files: Record<string, CompletionYamlData> = {};
|
|
1811
|
+
const groups: Array<{ root: CompletionYamlData; entries: Record<string, CompletionYamlData>[] }> = [];
|
|
1812
|
+
const runRoot = locations?.state === "available" ? locations.directory : undefined;
|
|
1813
|
+
const group = (root: CompletionYamlData): number => {
|
|
1814
|
+
const found = groups.findIndex(item => item.root === root);
|
|
1815
|
+
if (found >= 0) return found;
|
|
1816
|
+
groups.push({ root, entries: [] }); return groups.length - 1;
|
|
1817
|
+
};
|
|
1818
|
+
let priority = Infinity;
|
|
1819
|
+
const add = (path: CompletionYamlData, entry: Record<string, CompletionYamlData>, rank = Infinity, base?: string): void => {
|
|
1820
|
+
const selected = rootedCompletionPath(path, runRoot);
|
|
1821
|
+
const index = group(selected?.root ?? (base === undefined ? null : completionYamlString(base)));
|
|
1822
|
+
const selectedPath = selected?.path ?? path;
|
|
1823
|
+
groups[index]!.entries.push({ ...entry, path: selectedPath,
|
|
1824
|
+
...(!selected && base === undefined ? { path_basis: "as_recorded", base_unavailable: true } : {}) });
|
|
1825
|
+
if (selected && rank < priority) {
|
|
1826
|
+
priority = rank; files.read_first = { group: index, entry: groups[index]!.entries.length - 1 };
|
|
1827
|
+
}
|
|
1828
|
+
};
|
|
1829
|
+
for (const [role, purpose, rank] of [
|
|
1830
|
+
["primary", "Full result text", 0], ["diagnostic_metadata", "Recorded error and command metadata", 1],
|
|
1831
|
+
["artifact_inventory", "Declared artifact inventory; target availability unproven", 2],
|
|
1832
|
+
] as const) {
|
|
1833
|
+
const detail = technical[role] as Record<string, CompletionYamlData> | undefined;
|
|
1834
|
+
if (!detail) continue;
|
|
1835
|
+
const { source: rawSource, ...facts } = detail;
|
|
1836
|
+
const { path, ...binding } = rawSource as Record<string, CompletionYamlData>;
|
|
1837
|
+
add(path!, { role, purpose, ...facts, source: binding }, rank);
|
|
1838
|
+
delete technical[role];
|
|
1839
|
+
}
|
|
1840
|
+
const artifacts = technical.artifacts as { scope: string; declarations: { name: CompletionYamlData; path: CompletionYamlData }[] } | undefined;
|
|
1841
|
+
if (artifacts) {
|
|
1842
|
+
for (const item of artifacts.declarations)
|
|
1843
|
+
add(item.path, { role: "artifact", purpose: "Declared artifact", name: item.name, scope: artifacts.scope, state: "declared" });
|
|
1844
|
+
delete technical.artifacts;
|
|
1845
|
+
}
|
|
1846
|
+
if (locations?.state === "available") {
|
|
1847
|
+
const { directory, files: selectedFiles, ...observation } = locations;
|
|
1848
|
+
files.observation = { ...observation, run: completionYamlString(locations.run), run_instance_id: completionYamlString(locations.run_instance_id),
|
|
1849
|
+
group: group(completionYamlString(directory)) };
|
|
1850
|
+
for (const item of selectedFiles) {
|
|
1851
|
+
const { path, ...facts } = item;
|
|
1852
|
+
const purpose = item.role === "result" ? "Recorded result metadata" : item.role === "execution" ? "Recorded execution and capture locations" : "Recorded Run file";
|
|
1853
|
+
add(typeof path === "string" ? completionYamlString(path) : null, { ...facts, purpose },
|
|
1854
|
+
item.state === "present" && item.role === "result" ? 3 : item.state === "present" && item.role === "execution" ? 4 : Infinity, directory);
|
|
1855
|
+
}
|
|
1856
|
+
} else if (locations) files.observation = { ...locations, run: completionYamlString(member.run), run_instance_id: completionYamlString(member.run_instance_id) };
|
|
1857
|
+
if (groups.length) {
|
|
1858
|
+
files.groups = groups;
|
|
1859
|
+
}
|
|
1860
|
+
if (Object.keys(technical).length) files.notes = technical;
|
|
1861
|
+
return files;
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1783
1864
|
type PreparedCompletionYamlProjection =
|
|
1784
1865
|
| { state: "unsupported" }
|
|
1785
1866
|
| { state: "overbudget" }
|
|
@@ -1788,6 +1869,7 @@ type PreparedCompletionYamlProjection =
|
|
|
1788
1869
|
function prepareCompletionYamlProjection(safe: RunCompletionBatch, candidates: readonly { row: CompletionRow; location?: CompletionPiece }[],
|
|
1789
1870
|
references: ReadonlySet<number>, ordered: readonly RunCompletionBatchMember[]): PreparedCompletionYamlProjection {
|
|
1790
1871
|
const packet: CompletionYamlPacket = { batchId: safe.batch_id, records: [] };
|
|
1872
|
+
const rooted = safe.result_layout === "completion_yaml_files_v1";
|
|
1791
1873
|
if (!safe.batch_id.isWellFormed()) return { state: "unsupported" };
|
|
1792
1874
|
for (const [index, { row, location }] of candidates.entries()) {
|
|
1793
1875
|
const member = ordered[index]!;
|
|
@@ -1804,28 +1886,38 @@ function prepareCompletionYamlProjection(safe: RunCompletionBatch, candidates: r
|
|
|
1804
1886
|
else if (key === "artifact_declarations") metadata[key] = member.artifact_inventory?.declarations_total ?? Object.keys(member.artifacts ?? {}).length;
|
|
1805
1887
|
else metadata[key] = completionYamlString(value);
|
|
1806
1888
|
}
|
|
1807
|
-
const
|
|
1808
|
-
|
|
1809
|
-
if (
|
|
1810
|
-
const
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
: { ...
|
|
1889
|
+
const navigation = references.has(index) && location
|
|
1890
|
+
? safe.file_navigation!.find(item => safe.members[item.member_index] === member)!.locations : undefined;
|
|
1891
|
+
if (rooted) {
|
|
1892
|
+
const files = completionRootedFiles(row, member, navigation);
|
|
1893
|
+
if (Object.keys(files).length) metadata.files = files;
|
|
1894
|
+
} else {
|
|
1895
|
+
const technical = completionYamlReferences(row);
|
|
1896
|
+
if (Object.keys(technical).length) metadata.references = technical;
|
|
1897
|
+
if (navigation) {
|
|
1898
|
+
metadata.run_files = { run: completionYamlString(member.run), run_instance_id: completionYamlString(member.run_instance_id),
|
|
1899
|
+
locations: navigation.state === "available" ? { ...navigation, run: completionYamlString(navigation.run),
|
|
1900
|
+
run_instance_id: completionYamlString(navigation.run_instance_id), directory: completionYamlString(navigation.directory),
|
|
1901
|
+
files: navigation.files.map(file => ({ ...file, ...(typeof file.path === "string" ? { path: completionYamlString(file.path) } : {}) })) } as CompletionYamlData
|
|
1902
|
+
: { ...navigation } };
|
|
1903
|
+
}
|
|
1818
1904
|
}
|
|
1819
1905
|
const parts: CompletionYamlPart[] = [];
|
|
1820
1906
|
const add = (part: CompletionYamlPart) => { if (parts.length) parts.push({ marker: "\n" }); parts.push(part); };
|
|
1821
1907
|
for (const piece of row.pieces) if (piece.block) {
|
|
1822
1908
|
if (!piece.text.isWellFormed()) return { state: "unsupported" };
|
|
1823
|
-
|
|
1909
|
+
if (rooted && piece.block === "marker" && !/^\[[0-9]+ characters omitted\]$/u.test(piece.text)) {
|
|
1910
|
+
metadata.text_warnings ??= [];
|
|
1911
|
+
(metadata.text_warnings as CompletionYamlData[]).push(piece.text);
|
|
1912
|
+
} else add(piece.block === "body" ? { body: piece.text } : { marker: piece.text });
|
|
1913
|
+
}
|
|
1914
|
+
if (safe.file_navigation !== undefined && !references.has(index)) {
|
|
1915
|
+
if (rooted) metadata.run_files_omission = { reason: "packet_byte_limit", shown: 0, omitted: 1, total: 1 };
|
|
1916
|
+
else add({ marker: "Run-file reference omitted (packet byte limit)." });
|
|
1824
1917
|
}
|
|
1825
|
-
if (safe.file_navigation !== undefined && !references.has(index)) add({ marker: "Run-file reference omitted (packet byte limit)." });
|
|
1826
1918
|
packet.records.push({ metadata, parts });
|
|
1827
1919
|
}
|
|
1828
|
-
const service: Record<string, CompletionYamlData> = {};
|
|
1920
|
+
const service: Record<string, CompletionYamlData> = rooted ? { header_layout: "rooted_files_v1" } : {};
|
|
1829
1921
|
const omitted = safe.members.length - candidates.length;
|
|
1830
1922
|
if (omitted) {
|
|
1831
1923
|
service.omitted_results = omitted;
|
|
@@ -1841,7 +1933,11 @@ function prepareCompletionYamlProjection(safe: RunCompletionBatch, candidates: r
|
|
|
1841
1933
|
service.numbering = { ...safe.numbering, session_id: completionYamlString(safe.numbering.session_id), member_order: [...safe.numbering.member_order] };
|
|
1842
1934
|
if (safe.numbering.kind === "unavailable") service.numbering_unavailable = safe.numbering.reason;
|
|
1843
1935
|
}
|
|
1844
|
-
if (safe.inventory_source)
|
|
1936
|
+
if (safe.inventory_source) {
|
|
1937
|
+
const path = rooted ? rootedCompletionPath(safe.inventory_source.path) : undefined;
|
|
1938
|
+
service.result_inventory = { ...safe.inventory_source,
|
|
1939
|
+
...(path ? { root: path.root, path: path.path } : { path: completionYamlString(safe.inventory_source.path) }) };
|
|
1940
|
+
}
|
|
1845
1941
|
if (Object.keys(service).length) packet.service = service;
|
|
1846
1942
|
const chunks: string[] = [];
|
|
1847
1943
|
let bytes = 0;
|
|
@@ -1890,7 +1986,7 @@ export function projectRunCompletionYamlPrototype(batch: unknown):
|
|
|
1890
1986
|
}
|
|
1891
1987
|
}
|
|
1892
1988
|
|
|
1893
|
-
function projectCompletionRecords(safe: RunCompletionBatch, assembler: "legacy" | "yaml" = safe.result_layout
|
|
1989
|
+
function projectCompletionRecords(safe: RunCompletionBatch, assembler: "legacy" | "yaml" = isYamlCompletionLayout(safe.result_layout) ? "yaml" : "legacy"): { message: string; membersShown: number; compact_ranges: [number, number][]; textBudget?: number; blocks?: readonly CompletionBlock[] } {
|
|
1894
1990
|
const frontmatter = isFrontmatterCompletionLayout(safe.result_layout) || assembler === "yaml";
|
|
1895
1991
|
const shared = isSharedCompletionLayout(safe.result_layout);
|
|
1896
1992
|
const maxBytes = shared ? Limits.RUN_COMPLETION_MODEL_MAX_BYTES : Limits.RUN_DELIVERY_MODEL_MAX_BYTES;
|
|
@@ -2118,7 +2214,7 @@ export function projectRunCompletionBatchPresentation(batch: RunCompletionBatch)
|
|
|
2118
2214
|
const safe = parseBatch(batch);
|
|
2119
2215
|
if (safe.presentation_format !== "ax_records" && safe.presentation_format !== "ax_records_v3") return { content: projectCompletionBatchMessage(safe).message };
|
|
2120
2216
|
const result = safe.result_layout !== undefined ? projectCompletionRecords(safe) : projectRetainedRecordCompletionBatch(safe);
|
|
2121
|
-
if (safe.result_layout
|
|
2217
|
+
if (isYamlCompletionLayout(safe.result_layout)) return { content: result.message, presentation: Object.freeze({
|
|
2122
2218
|
format: "completion_yaml_core_v1", content_sha256: createHash("sha256").update(result.message).digest("hex"),
|
|
2123
2219
|
}) };
|
|
2124
2220
|
return { content: result.message, presentation: Object.freeze({ format: isFrontmatterCompletionLayout(safe.result_layout) ? "completion_frontmatter_v1" : safe.presentation_format, content_sha256: createHash("sha256").update(result.message).digest("hex"),
|
|
@@ -2253,19 +2349,22 @@ export function readRunCompletionPresentation(content: string, value: unknown):
|
|
|
2253
2349
|
!completionYamlKeys(hint, ["format", "content_sha256"]) || hint.format !== completionYamlCodec ||
|
|
2254
2350
|
hint.content_sha256 !== createHash("sha256").update(content).digest("hex")) return full;
|
|
2255
2351
|
const segments: NonNullable<RunCompletionPresentationView["segments"]>[number][] = [];
|
|
2352
|
+
const rooted = core.packet.service?.header_layout === "rooted_files_v1";
|
|
2353
|
+
const facts = Object.fromEntries(Object.entries(core.packet.service ?? {}).filter(([key]) => key !== "header_layout"));
|
|
2256
2354
|
for (const [index, record] of core.packet.records.entries()) {
|
|
2257
2355
|
if (index) segments.push({ kind: "separator", text: "\n" });
|
|
2258
|
-
const metadata = Object.fromEntries(Object.entries(record.metadata).filter(([key]) => key !== "references" && key !== "run_files"));
|
|
2356
|
+
const metadata = Object.fromEntries(Object.entries(record.metadata).filter(([key]) => rooted || (key !== "references" && key !== "run_files")));
|
|
2357
|
+
if (rooted && index === 0 && Object.keys(facts).length) metadata.delivery = facts;
|
|
2259
2358
|
const header = renderYamlMetadata(metadata, completionYamlMaxBytes);
|
|
2260
2359
|
if (header.state !== "available") return full;
|
|
2261
2360
|
segments.push({ kind: "header", text: record.parts.length ? `---\n${header.text}---` : header.text.replace(/\n$/u, "") });
|
|
2262
2361
|
for (const part of record.parts) segments.push("body" in part ? { kind: "body", text: part.body } : { kind: "marker", text: part.marker });
|
|
2263
2362
|
}
|
|
2264
|
-
if (core.packet.service) {
|
|
2265
|
-
const service = renderYamlMetadata(core.packet.service, completionYamlMaxBytes);
|
|
2363
|
+
if (core.packet.service && (!rooted || !core.packet.records.length)) {
|
|
2364
|
+
const service = renderYamlMetadata(rooted ? facts : core.packet.service, completionYamlMaxBytes);
|
|
2266
2365
|
if (service.state !== "available") return full;
|
|
2267
2366
|
if (segments.length) segments.push({ kind: "separator", text: "\n" });
|
|
2268
|
-
segments.push({ kind: "service", text: service.text.replace(/\n$/u, "") });
|
|
2367
|
+
segments.push({ kind: rooted ? "header" : "service", text: service.text.replace(/\n$/u, "") });
|
|
2269
2368
|
}
|
|
2270
2369
|
return { text: segments.map(segment => segment.text).join("\n"), compact: true, records: true, yaml: true,
|
|
2271
2370
|
segments: Object.freeze(segments.map(segment => Object.freeze(segment))) };
|
package/lib/tools-inspect.ts
CHANGED
|
@@ -448,9 +448,6 @@ function inspectRecipes(
|
|
|
448
448
|
if (view === "reviews") {
|
|
449
449
|
return ReviewDiagnostics.readAutomaticReviewDiagnostics({ recipeRoot });
|
|
450
450
|
}
|
|
451
|
-
const discovered = RecipesDiscovery.discoverRecipeSources([
|
|
452
|
-
{ root: recipeRoot, defaultTool: true, mutableUsage: true },
|
|
453
|
-
]);
|
|
454
451
|
const resolutionContext =
|
|
455
452
|
context && typeof context === "object" && "recipeResolutionContext" in context
|
|
456
453
|
? (context as {
|
|
@@ -506,7 +503,11 @@ function inspectRecipes(
|
|
|
506
503
|
...(diagnostic.stem ? { stem: diagnostic.stem } : {}),
|
|
507
504
|
}),
|
|
508
505
|
);
|
|
506
|
+
const discovered = RecipesDiscovery.discoverRecipeSources([
|
|
507
|
+
{ root: recipeRoot, defaultTool: true, mutableUsage: true, resolutionContext },
|
|
508
|
+
]);
|
|
509
509
|
const discoverySummary = RecipesDiscovery.summarizeDiscovery(discovered);
|
|
510
|
+
const retained = RecipesDiscovery.inventoryRetainedRecipes(recipeRoot, resolutionContext);
|
|
510
511
|
const summary = {
|
|
511
512
|
...discoverySummary,
|
|
512
513
|
...(registryStatus
|
|
@@ -532,6 +533,8 @@ function inspectRecipes(
|
|
|
532
533
|
error: `Ambiguous active Skill Recipe namespace ${name}`,
|
|
533
534
|
})),
|
|
534
535
|
drafts: RecipesDiscovery.listDraftRecipes(join(recipeRoot, "drafts")),
|
|
536
|
+
retained: retained.recipes,
|
|
537
|
+
retained_diagnostics: retained.diagnostics,
|
|
535
538
|
};
|
|
536
539
|
return {
|
|
537
540
|
...summary,
|
package/lib/tools-register.ts
CHANGED
|
@@ -40,6 +40,7 @@ export function createRegisterToolDefinition<TContext>(
|
|
|
40
40
|
Prompts.REGISTER_TOOL_PARAM_DESCRIPTIONS.defaults,
|
|
41
41
|
),
|
|
42
42
|
draft: stringSchema(Prompts.REGISTER_TOOL_PARAM_DESCRIPTIONS.draft),
|
|
43
|
+
expose: booleanSchema(Prompts.REGISTER_TOOL_PARAM_DESCRIPTIONS.expose),
|
|
43
44
|
from: stringSchema(Prompts.REGISTER_TOOL_PARAM_DESCRIPTIONS.from),
|
|
44
45
|
name: stringSchema(Prompts.REGISTER_TOOL_PARAM_DESCRIPTIONS.name),
|
|
45
46
|
template: unionSchema([
|
package/lib/tools-response.ts
CHANGED
|
@@ -517,6 +517,12 @@ export function recipeRegistryNextActions(
|
|
|
517
517
|
if (summary.skill_recipe_catalog_partial === true) {
|
|
518
518
|
actions.push("inspect target=recipes view=imports verbose=true");
|
|
519
519
|
}
|
|
520
|
+
if (
|
|
521
|
+
(Array.isArray(summary.retained) && summary.retained.length > 0) ||
|
|
522
|
+
(Array.isArray(summary.retained_diagnostics) && summary.retained_diagnostics.length > 0)
|
|
523
|
+
) {
|
|
524
|
+
actions.push("inspect target=recipes view=summary verbose=true");
|
|
525
|
+
}
|
|
520
526
|
if (drafts.length > 0) {
|
|
521
527
|
actions.push("inspect target=recipes view=summary verbose=true");
|
|
522
528
|
const firstPath =
|
|
@@ -563,6 +569,7 @@ export function compactRecipeRegistry(
|
|
|
563
569
|
const active = count("active", summary.active), shadowed = count("shadowed", summary.shadowed);
|
|
564
570
|
const invalid = count("invalid", summary.invalid), disabled = count("disabled", summary.disabled);
|
|
565
571
|
const diagnostics = count("diagnostics", summary.diagnostics), drafts = count("drafts", summary.drafts);
|
|
572
|
+
const retained = count("retained", summary.retained);
|
|
566
573
|
const recommendations = count("recommendations", summary.recommendations);
|
|
567
574
|
const skillCatalogPartial = summary.skill_recipe_catalog_partial === true;
|
|
568
575
|
const currentPolicy = Array.isArray(summary.active)
|
|
@@ -574,7 +581,7 @@ export function compactRecipeRegistry(
|
|
|
574
581
|
? (summary.next_actions as string[])
|
|
575
582
|
: [];
|
|
576
583
|
const hint = nextActions.length ? nextActionMetadata(nextActions[0]!, Limits.RUN_DELIVERY_MODEL_MAX_BYTES) : undefined;
|
|
577
|
-
const render = (): Record<string, unknown> => ({ recipes: "registry", active, drafts, shadowed, invalid, disabled,
|
|
584
|
+
const render = (): Record<string, unknown> => ({ recipes: "registry", active, drafts, retained, shadowed, invalid, disabled,
|
|
578
585
|
current_policy: currentPolicy, skill_catalog_partial: skillCatalogPartial, recommendations, diagnostics,
|
|
579
586
|
...(hint ? hint.selected : {}) });
|
|
580
587
|
return hint
|
package/package.json
CHANGED
package/skills/actors/SKILL.md
CHANGED
|
@@ -19,6 +19,9 @@ Make a maintained capability a persistent agent-callable tool
|
|
|
19
19
|
Keep the same capability but narrow caller defaults
|
|
20
20
|
→ use register_tool from=<skill>/<recipe> defaults={...}
|
|
21
21
|
|
|
22
|
+
Retain a new named capability without exposing a tool
|
|
23
|
+
→ use register_tool expose=false with from or template
|
|
24
|
+
|
|
22
25
|
Register a trusted command directly
|
|
23
26
|
→ use register_tool template="..."
|
|
24
27
|
|
|
@@ -54,6 +57,12 @@ Run Control ≠ actor chat
|
|
|
54
57
|
|
|
55
58
|
A Skill Recipe is a maintained component addressed by `<skill>/<recipe>`. `spawn` creates a Run from a Recipe. `register_tool from=<skill>/<recipe>` persists compact logical delegation and activates from the resolved effective contract without copying, symlinking, or ambient re-resolution. A tool is callable in the current session only when activation evidence says `callable_now: true`.
|
|
56
59
|
|
|
60
|
+
Sync/async is an execution contract, not a usefulness or tool-exposure rule. A sync tool awaits its result; an async tool returns a Run launch receipt, with the eventual result delivered separately. A subagent-backed tool is a capability launcher, not a persisted live subagent. Register a stable, meaningfully parameterized capability when its name and contract save repeated planning; prefer one-off `spawn` over a redundant generic launcher or a new tool for every mission.
|
|
61
|
+
|
|
62
|
+
Use `register_tool expose=false` with a new name and one `template` or `from` source to retain a capability without requesting tool activation. Its `kind: retained_recipe`, `activation: not_requested` receipt is deliberate retention, not failed registration; it omits global callability flags because a same-named tool may already exist. Reuse the exact returned Recipe path through `spawn`, or deliberately expose it later with ordinary `register_tool from=<exact path>`. Retained update, delete and draft promotion are unsupported; do not hand-edit the live registry to bypass those guards.
|
|
63
|
+
|
|
64
|
+
Automatic retained lifecycle is not implemented. Current user-root tool discovery and automatic review actions are unchanged. `inspect target=recipes view=summary` distinguishes retained definitions under `recipes/library` from root tool candidates; verbose detail gives exact paths, resolved mode and invalid/disabled diagnostics. Storage is not proof of review, callability or a running Actor. `expose` is registration input, not a Recipe field or automatic-review action; do not invent retain/defer review commands. See [persistent tools](./references/persistent-tools.md) for current selection and activation proof.
|
|
65
|
+
|
|
57
66
|
`actors` owns generic Recipe/tool/Run mechanics. The owning capability Skill owns capability-specific selection and constraints. `swarm` owns multi-actor decomposition and integration methodology.
|
|
58
67
|
|
|
59
68
|
## Presentation Direction and Current Output
|
|
@@ -151,7 +160,7 @@ Run = Recipe + Trace + Control
|
|
|
151
160
|
5. Send `message` only for an action declared and consumed by that controlled Recipe.
|
|
152
161
|
6. Use terminal state, result, declared artifacts, and execution evidence to prove completion. When an outcome includes a retained primary-source, declaration-inventory or diagnostic-metadata reference, use that exact reference for further reading, not guessed captures or sibling files. Literal previews may be clipped; their field/range/counts describe what was shown. Diagnostic sources contain recorded command/error/failure-reason metadata only, not complete primary output; other recorded fields may be referenced without being shown. Artifact inventories list declarations only: they do not assert that target files exist or were produced, or grant body-read permission. Explicit unavailability has no guessed legacy body/path fallback.
|
|
153
162
|
|
|
154
|
-
Compact launch receipts include `run_files` as nested block YAML containing the exact selected observed location values, labeled observed locations/mutable for the exact launched Run generation when the current session and physical identity agree; otherwise they report unavailability or whole-row omission. Machine `file_navigation` details stay JSON-compatible; saved terminal messages retain their own original encodings. Selectively ordinary-read those starting locations when needed; layout/size hints are not body, privacy, terminal or native proof. No evidence bodies or session-wide catalog map are read to build this row. New terminal packets also freeze bounded location rows for captured completion members at fresh current-session admission. Each shown result keeps its whole Run-file row or explicit per-record omission; wholly omitted results stay covered by result omission/inventory. This describes Run-row coverage, not a complete file/tree inventory. Older captured formats retain their original shown/omitted/total counters; unavailability remains explicit. Ordinary-read selected paths only when needed. Packet reload/render never refreshes sources, and old absent/request-only formats keep their text. Source/npm SDK verifies an explicitly requested ordinary read from a delivered terminal result path; this is research, never routine completion polling or new primary/privacy/clock/native authority. Use the request only for file investigation, not routine completion/status polling. Copied locations/hints remain data, never transferred scope; older batches keep their original text.
|
|
163
|
+
Compact launch receipts include `run_files` as nested block YAML containing the exact selected observed location values, labeled observed locations/mutable for the exact launched Run generation when the current session and physical identity agree; otherwise they report unavailability or whole-row omission. Machine `file_navigation` details stay JSON-compatible; saved terminal messages retain their own original encodings. Selectively ordinary-read those starting locations when needed; layout/size hints are not body, privacy, terminal or native proof. No evidence bodies or session-wide catalog map are read to build this row. New terminal packets also freeze bounded location rows for captured completion members at fresh current-session admission. Fresh rooted completion headers group paths under literal roots with relative entries, English purposes and a read_first group/entry pointer; no cwd guess, source refresh or automatic read follows. Unknown bases stay explicit. Runtime navigation/omission/inventory facts remain above the Actor Markdown, not in a footer; retained packets keep their original projection. Each shown result keeps its whole Run-file row or explicit per-record omission; wholly omitted results stay covered by result omission/inventory. This describes Run-row coverage, not a complete file/tree inventory. Older captured formats retain their original shown/omitted/total counters; unavailability remains explicit. Ordinary-read selected paths only when needed. Packet reload/render never refreshes sources, and old absent/request-only formats keep their text. Source/npm SDK verifies an explicitly requested ordinary read from a delivered terminal result path; this is research, never routine completion polling or new primary/privacy/clock/native authority. Use the request only for file investigation, not routine completion/status polling. Copied locations/hints remain data, never transferred scope; older batches keep their original text.
|
|
155
164
|
|
|
156
165
|
For selective investigation of already cataloged files, use `inspect target=runtime view=runs verbose=true`, then ordinary file tools on its current-session root-first navigation. Ordinary maps keep latest-registration order between roots and prioritize known failed subtrees within each root. For a failed investigation add status=failed: inventory is filtered, while the full owned map prioritizes observed failed subtrees across roots, always ancestors first. Priority uses only exact-generation matched inventory observations, not complete terminal truth. Launches and compatible singleton attachments populate the catalog; reads do not backfill missing locations. A `catalog: unavailable` launch warning is not workload failure: do not spawn again merely to repair navigation. The starting map's format hints do not certify syntax, encoding or complete output. See [Runs](./references/runs.md) for selective recorded-capture research and resume boundaries.
|
|
157
166
|
|
|
@@ -168,7 +177,7 @@ When a pi-actors operation fails:
|
|
|
168
177
|
3. Report resolver, registry, activation, Run, Trace, or Control truth exactly.
|
|
169
178
|
4. Retry only after the owning state is healthy.
|
|
170
179
|
|
|
171
|
-
Stop if spawn and registry resolve the same Recipe differently.
|
|
180
|
+
Stop if spawn and registry resolve the same Recipe differently. When tool exposure was requested, stop if registration persists but is not callable. A `retained_recipe` receipt with `activation: not_requested` instead permits ordinary Recipe reuse, not a claim of tool invocation. Stop if an operation cannot be proven through pi-actors surfaces.
|
|
172
181
|
|
|
173
182
|
Never recover by copying maintained Recipe args, defaults, Control, artifacts, or helper commands. Never hard-code a `{skill_dir}` replacement path. Never introduce `bash -lc`, `eval`, direct bundled-helper execution, or shell backgrounding to bypass resolution. Never call `spawn` and claim a tool call. Use [diagnostics](./references/diagnostics.md) for the safe next action.
|
|
174
183
|
|
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
Use a persistent tool when the agent should call a trusted capability by name across turns or sessions. Use `spawn` instead for one-off Recipe execution.
|
|
4
4
|
|
|
5
|
+
## Select the capability, not the execution mode
|
|
6
|
+
|
|
7
|
+
Both sync and async Recipes can be tools. Sync invocation awaits the result; async invocation returns a launch receipt and the eventual result arrives separately. Neither synchrony nor a model-backed implementation establishes usefulness. A subagent-backed tool launches a capability, not a saved live subagent. Synchronous tools execute inline; non-singleton async calls launch Run executions, while singleton attachment follows its existing compatibility contract.
|
|
8
|
+
|
|
9
|
+
Prefer a stable purpose/name, caller-owned parameterized inputs, a clear result/acceptance boundary, understood effects and failure/cancellation limits, and meaningful leverage over existing tools or one-off `spawn`. Do not register every transient mission or a generic arbitrary-prompt alias merely to add a name. A successful registration is not permission for new effects, and a launch receipt is not successful task completion.
|
|
10
|
+
|
|
11
|
+
Current user-root Recipes are tool candidates by location; Skill components do not become tools automatically. Explicit create-only retention is supported through `register_tool expose=false`; it is separate from tool exposure. Automatic retained lifecycle is not implemented. Read-only `inspect target=recipes view=summary` reports retained counts for `recipes/library`; use `verbose=true` for exact `retained` paths, resolved mode, invalid/disabled state and diagnostics. Reuse a valid enabled entry through `spawn recipe=<exact path>` with required caller values, or deliberately register it with `from=<exact path>`. A stored definition is not review, activation or Run evidence; use `tool:<name>` for activation and `runtime view=runs` for running instances. A `library/<stem>` usage-lineage label is private accounting identity, not a callable name or Skill Recipe reference; reuse the exact file path from inspection. Do not populate or migrate the live registry manually. `expose` is a registration input, not a Recipe field; do not invent review actions or bypass unsupported retained mutations. Existing explicit `register_tool` works for both modes; automatic review remains under its existing policy. Launch counts and value-free structural review alone do not establish semantic purpose, a good name, or accepted useful reuse.
|
|
12
|
+
|
|
5
13
|
## Choose one source mode
|
|
6
14
|
|
|
7
15
|
```text
|
|
@@ -32,9 +40,19 @@ Resolution uses Pi's authoritative active-session Skill snapshot across every Sk
|
|
|
32
40
|
|
|
33
41
|
Use `description` to narrow agent-facing intent when useful. Every supplied default must name a caller-owned source arg and satisfy its type or enum. Never default runtime-owned origins.
|
|
34
42
|
|
|
43
|
+
## Retain without exposing a tool
|
|
44
|
+
|
|
45
|
+
Use `register_tool name=<name> expose=false from=<skill>/<recipe>` or a trusted `template` source to create `recipes/library/<name>.json`. A description is still required for authored command templates; source defaults/args/origins still belong to the maintained Recipe. This is create-only: retained `update=true`, null/empty deletion, `draft` mode and direct Recipe dependencies on managed disposable drafts are rejected, including Recipe-valued template leaves. A duplicate retained JSON/Markdown stem is a conflict; no older file is overwritten.
|
|
46
|
+
|
|
47
|
+
The `kind: retained_recipe`, `activation: not_requested` receipt confirms persistence/admission, not host activation or review. It returns the exact Recipe path and required/optional argument names, without global callability flags or default values. A same-named tool is left untouched. Use the exact returned path with `spawn`; if compact output omits the whole path, follow its read-only verbose Recipe inspection hint rather than guessing it. Retention records no launch and makes no self-contained-backup promise for referenced sources.
|
|
48
|
+
|
|
49
|
+
To expose a retained capability deliberately, call ordinary `register_tool name=<tool> from=<exact retained path>`, prove activation below, then call the actual tool. Deleting that admitted wrapper through the ordinary deletion path leaves the retained source; it does not stop active Runs. This is not a migration or safe-retention rule for old standalone tools.
|
|
50
|
+
|
|
51
|
+
Publication uses a complete atomic no-overwrite file link; unsupported filesystems refuse without an overwrite fallback. A storage, admission or lock-release failure can follow committed bytes. Inspect the retained inventory and preserve the original cause; do not blindly repeat the request or delete an uncertain file.
|
|
52
|
+
|
|
35
53
|
## Prove registration
|
|
36
54
|
|
|
37
|
-
|
|
55
|
+
When tool exposure was requested, read registration as a state transition, not one success word:
|
|
38
56
|
|
|
39
57
|
```text
|
|
40
58
|
resolved
|
|
@@ -46,7 +64,7 @@ resolved
|
|
|
46
64
|
→ callable_now
|
|
47
65
|
```
|
|
48
66
|
|
|
49
|
-
|
|
67
|
+
For tool exposure, when `callable_now` is true, call the actual generated tool and verify `launch_kind: "tool"` when provenance matters. When false, stop at the reported activation boundary and use tool/Recipe diagnosis. A Recipe spawn is not an activation test or tool invocation substitute.
|
|
50
68
|
|
|
51
69
|
Use:
|
|
52
70
|
|
|
@@ -24,7 +24,7 @@ inspect target=run:<id> view=trace
|
|
|
24
24
|
inspect target=run:<id> view=control
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
Fresh root completions use index-first YAML frontmatter (index, run, state, admitted kind), followed by raw left-edge Markdown. No external #N heading, result:/preview: wrapper, list nesting, whole-body fence, Batch caption, members wrapper or routine outer counter is added. Primary/final Actor response uses kind:primary; explicit error/failure diagnostic text uses kind:diagnostic, never a launch command. Untracked fallback has no kind and keeps unknown original completeness/privacy explicit outside YAML. Known cut loss is [N characters omitted] at the cut, with no extra ellipsis or body clipping counters/flags in metadata. Unknown upstream loss remains separately unknown and does not assert a known positive omission. Exact source/generation/parent/location references remain in complete model content, with whole references or explicit omission, captured inventory recovery and non-done omitted states. The compact TUI independently renders literal headers/markers and native Markdown bodies from validated content-bound blocks; only whole header reference/location rows and framing are hidden. Expansion and invalid/missing hints reveal original text. Hashes/layout/markers establish consistency/correlation only, never authentication, read/action permission, acceptance or native truth. Authored YAML/next/tool/batch/omission lookalikes stay body content. Fresh packets share48KiB UTF-8 including service data, equally between up to64candidate rows, with256captured members and no redistribution of unused shares or fixed16KiB/2KiB text cap. Full admitted text fitting its share without upstream uncertainty has no cut marker; otherwise use the scalar/CRLF-safe prefix and exact retained-source loss. Allocation/text is frozen at admission; rendering/recovery does not reread sources or reallocate. Unsupported Pi event/control output is withheld. Source basis stays in reference metadata; retained structured coverage facts are not exposed as YAML clipping fields. Pending/queued/presented older formats—including #N records and the first frontmatter draft—keep exact bytes, hints and budgets. Encoding names are storage replay data, not public product choices, source permissions or work authority. No standing full-map instruction or automatic research/read follows; use selective same-session investigation when needed.
|
|
27
|
+
Fresh root completions use index-first YAML frontmatter (index, run, state, admitted kind), followed by raw left-edge Markdown. New source captures put all runtime metadata above that body, with no service/file footer. files.groups gives one literal root plus relative entry paths; files.read_first names zero-based group/entry indices for the first useful source, not an automatic read. role/purpose guidance is English; Actor Markdown and path/name bytes are unchanged. External paths use separate roots; unknown bases remain explicit rather than guessed from cwd or Run id. Known cut counts alone may follow the cut body; unknown-loss warnings and packet omission/inventory facts stay in the header. Retained captures preserve their old bytes and presentation. No external #N heading, result:/preview: wrapper, list nesting, whole-body fence, Batch caption, members wrapper or routine outer counter is added. Primary/final Actor response uses kind:primary; explicit error/failure diagnostic text uses kind:diagnostic, never a launch command. Untracked fallback has no kind and keeps unknown original completeness/privacy explicit outside YAML. Known cut loss is [N characters omitted] at the cut, with no extra ellipsis or body clipping counters/flags in metadata. Unknown upstream loss remains separately unknown and does not assert a known positive omission. Exact source/generation/parent/location references remain in complete model content, with whole references or explicit omission, captured inventory recovery and non-done omitted states. The compact TUI independently renders literal headers/markers and native Markdown bodies from validated content-bound blocks; only whole header reference/location rows and framing are hidden. Expansion and invalid/missing hints reveal original text. Hashes/layout/markers establish consistency/correlation only, never authentication, read/action permission, acceptance or native truth. Authored YAML/next/tool/batch/omission lookalikes stay body content. Fresh packets share48KiB UTF-8 including service data, equally between up to64candidate rows, with256captured members and no redistribution of unused shares or fixed16KiB/2KiB text cap. Full admitted text fitting its share without upstream uncertainty has no cut marker; otherwise use the scalar/CRLF-safe prefix and exact retained-source loss. Allocation/text is frozen at admission; rendering/recovery does not reread sources or reallocate. Unsupported Pi event/control output is withheld. Source basis stays in reference metadata; retained structured coverage facts are not exposed as YAML clipping fields. Pending/queued/presented older formats—including #N records and the first frontmatter draft—keep exact bytes, hints and budgets. Encoding names are storage replay data, not public product choices, source permissions or work authority. No standing full-map instruction or automatic research/read follows; use selective same-session investigation when needed.
|
|
28
28
|
|
|
29
29
|
Current completion index values restart within each packet; they are not session-wide historical references. Completion streams are adopted but not enabled, and no start-stream command is available yet. Do not infer an active stream or reset a counter from missing metadata or a pending target, and never relabel older saved packets. The kernel-only allocator now commits fixed scope/order/indices, the counter and first-packet target activation together, and an internal fresh-session bridge can use it after current manager/generation admission. The default runtime does not opt in; no public action exposes it. Event reason or a matching ID alone is insufficient. Old callbacks/wrappers cannot act after replacement; fresh wrappers must belong to the current original manager. Startup/reload/resume and ambiguous imports remain closed to stream capture/ACK, without changing saved packets or inferring a counter from history. Its whole-range exhaustion or admitted counter unavailability yields a frozen unknown index (quoted "?" in fresh YAML; #? in older saved layouts), never partial indices or an automatic fresh stream. Old packets retain their original scope while a pending switch waits; uncertainty does not permit reallocation. Stream state outlives packet cleanup/receipt rotation. Malformed/missing state remains refused, not a zero counter or proof of non-connection.
|
|
30
30
|
|
|
@@ -7,6 +7,12 @@ description: Use only for internal automatic Recipe-memory review, diagnosis, or
|
|
|
7
7
|
|
|
8
8
|
Use this Skill only when automatic draft-memory or active-tool review needs diagnosis or bounded recovery. Normal agents must not spawn, register, specialize, or invoke `recipe-memory/draft-review` or `recipe-memory/tool-review`; they are package-owned internal reviewer components.
|
|
9
9
|
|
|
10
|
+
## Evidence limits
|
|
11
|
+
|
|
12
|
+
Automatic reviewers receive value-free contract shape, risk labels, usage counts, and batch-local equality groups, not executable content, descriptions, paths, or canonical names. Structural evidence and launch counts alone do not establish semantic purpose, good tool names, successful reuse, or accepted outcomes. Promotion preserves unchanged captured executable content; it does not generalize a one-off mission.
|
|
13
|
+
|
|
14
|
+
Explicit create-only retention is owned by `actors` through `register_tool expose=false`, not by these internal reviewers. Automatic retained lifecycle is not implemented. Current draft promote/discard and tool-review behavior are unchanged. Missing evidence is unknown; this guidance adds no retain/defer action, retry permission, automatic rewrite, or live migration. Do not bypass confidentiality or edit review state to simulate the future policy.
|
|
15
|
+
|
|
10
16
|
## Diagnose first
|
|
11
17
|
|
|
12
18
|
```text
|