@llblab/pi-actors 0.56.1 → 0.57.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 +7 -7
- package/BACKLOG.md +4 -14
- package/CHANGELOG.md +13 -0
- package/README.md +7 -2
- package/dist/lib/execution-output.js +13 -2
- package/dist/lib/observability.js +9 -14
- package/dist/lib/registry.js +82 -117
- package/dist/lib/run-delivery.d.ts +1 -1
- package/dist/lib/run-delivery.js +146 -32
- package/dist/lib/run-ui-runtime.js +18 -27
- package/dist/lib/runs-process-windows-mi.d.ts +71 -0
- package/dist/lib/runs-process-windows-mi.js +146 -3
- package/dist/lib/tools-inspect.js +109 -59
- package/dist/lib/tools-local.js +16 -8
- package/dist/lib/tools-message.js +76 -65
- package/dist/lib/tools-response.d.ts +18 -0
- package/dist/lib/tools-response.js +88 -21
- package/dist/lib/tools-spawn.js +19 -12
- package/dist/skills/actors/SKILL.md +1 -1
- package/dist/skills/actors/references/diagnostics.md +3 -0
- package/dist/skills/actors/references/runs.md +1 -1
- package/docs/README.md +1 -0
- package/docs/actor-inspector.md +2 -2
- package/docs/agent-experience.md +51 -4
- package/docs/coordinator-delivery.md +2 -2
- package/docs/inactive-foundations-inventory.md +73 -0
- package/docs/inspection.md +9 -8
- package/docs/output-projection-map.md +19 -17
- package/docs/windows-process-observation.md +2 -2
- package/lib/execution-output.ts +13 -2
- package/lib/observability.ts +9 -11
- package/lib/registry.ts +89 -124
- package/lib/run-delivery.ts +130 -31
- package/lib/run-ui-runtime.ts +18 -34
- package/lib/runs-process-windows-mi.ts +167 -3
- package/lib/tools-inspect.ts +109 -59
- package/lib/tools-local.ts +26 -15
- package/lib/tools-message.ts +85 -58
- package/lib/tools-response.ts +97 -20
- package/lib/tools-spawn.ts +19 -12
- package/package.json +1 -1
- package/skills/actors/SKILL.md +1 -1
- package/skills/actors/references/diagnostics.md +3 -0
- package/skills/actors/references/runs.md +1 -1
package/dist/lib/run-delivery.js
CHANGED
|
@@ -278,8 +278,11 @@ function parseInventorySource(value, batch) {
|
|
|
278
278
|
function isFrontmatterCompletionLayout(layout) {
|
|
279
279
|
return layout === "frontmatter_v1" || layout === "frontmatter_v2";
|
|
280
280
|
}
|
|
281
|
+
function isYamlCompletionLayout(layout) {
|
|
282
|
+
return layout === "completion_yaml_core_v1" || layout === "completion_yaml_files_v1";
|
|
283
|
+
}
|
|
281
284
|
function isSharedCompletionLayout(layout) {
|
|
282
|
-
return layout === "headings_shared_results" || layout === "headings_shared_kinds" || layout
|
|
285
|
+
return layout === "headings_shared_results" || layout === "headings_shared_kinds" || isYamlCompletionLayout(layout) || isFrontmatterCompletionLayout(layout);
|
|
283
286
|
}
|
|
284
287
|
function parseBatch(value) {
|
|
285
288
|
const record = asRecord(value, "Completion batch");
|
|
@@ -322,7 +325,7 @@ function parseBatch(value) {
|
|
|
322
325
|
throw new Error("Completion shared text budget is invalid");
|
|
323
326
|
}
|
|
324
327
|
const numberingDescriptor = Object.getOwnPropertyDescriptor(record, "numbering");
|
|
325
|
-
if (numberingDescriptor && (resultLayout
|
|
328
|
+
if (numberingDescriptor && (isYamlCompletionLayout(resultLayout) || !Object.hasOwn(numberingDescriptor, "value") || presentationFormat !== "ax_records" || (resultLayout !== "headings" && resultLayout !== "headings_flat_omissions" && resultLayout !== "headings_outcomes" && !isSharedCompletionLayout(resultLayout)))) {
|
|
326
329
|
throw new Error("Completion numbering requires a canonical captured heading layout");
|
|
327
330
|
}
|
|
328
331
|
const numbering = numberingDescriptor ? parseCompletionNumbering(numberingDescriptor.value, members.length) : undefined;
|
|
@@ -685,7 +688,7 @@ export function requestRunCompletionStream(input) {
|
|
|
685
688
|
});
|
|
686
689
|
}
|
|
687
690
|
export function admitRunCompletionBatch(input) {
|
|
688
|
-
return captureCompletionBatch(input, false, "
|
|
691
|
+
return captureCompletionBatch(input, false, "completion_yaml_files_v1");
|
|
689
692
|
}
|
|
690
693
|
/** Kernel-only opt-in; ordinary Pi capture stays fenced until session/lifecycle integration is admitted. */
|
|
691
694
|
export function captureRunCompletionStreamBatch(input) {
|
|
@@ -693,7 +696,7 @@ export function captureRunCompletionStreamBatch(input) {
|
|
|
693
696
|
}
|
|
694
697
|
/** Private ordinary candidate composition; stream capture retains its separately gated codec. */
|
|
695
698
|
export function captureRunCompletionYamlCandidateBatch(input) {
|
|
696
|
-
return captureCompletionBatch(input, false, "
|
|
699
|
+
return captureCompletionBatch(input, false, "completion_yaml_files_v1");
|
|
697
700
|
}
|
|
698
701
|
function captureCompletionBatch(input, numbered, layout = "frontmatter_v2") {
|
|
699
702
|
const now = (input.now ?? new Date()).toISOString();
|
|
@@ -710,7 +713,7 @@ function captureCompletionBatch(input, numbered, layout = "frontmatter_v2") {
|
|
|
710
713
|
members: input.members,
|
|
711
714
|
phase: "pending",
|
|
712
715
|
});
|
|
713
|
-
const draft = layout
|
|
716
|
+
const draft = isYamlCompletionLayout(layout)
|
|
714
717
|
? parseBatch({ ...captured, result_layout: layout }) : { ...captured, result_layout: layout };
|
|
715
718
|
return mutateJournal(input.tempDir, input.ownerId, (journal) => {
|
|
716
719
|
let batch = draft;
|
|
@@ -741,10 +744,10 @@ function captureCompletionBatch(input, numbered, layout = "frontmatter_v2") {
|
|
|
741
744
|
else if (journal.completion_stream) {
|
|
742
745
|
throw new Error("Completion stream capture requires the numbered kernel path; stream state is retained");
|
|
743
746
|
}
|
|
744
|
-
if (layout
|
|
747
|
+
if (!isYamlCompletionLayout(layout))
|
|
745
748
|
batch = retainSharedCompletionTexts(batch);
|
|
746
749
|
if (projectCompletionBatchMessage(batch).membersShown === batch.members.length) {
|
|
747
|
-
if (layout
|
|
750
|
+
if (isYamlCompletionLayout(layout))
|
|
748
751
|
batch = retainSharedCompletionTexts(batch);
|
|
749
752
|
journal.completion_batch = batch;
|
|
750
753
|
return batch;
|
|
@@ -1281,7 +1284,7 @@ function isRoutineCompletionSummary(member) {
|
|
|
1281
1284
|
return member.summary === (member.status === "done" ? "Run completed." : `Run ${member.status}.`);
|
|
1282
1285
|
}
|
|
1283
1286
|
function completionRecordRow(member, index, byIdentity, flat = false, layout, textBudget) {
|
|
1284
|
-
const frontmatter = isFrontmatterCompletionLayout(layout) || layout
|
|
1287
|
+
const frontmatter = isFrontmatterCompletionLayout(layout) || isYamlCompletionLayout(layout);
|
|
1285
1288
|
const simpleKinds = frontmatter || layout === "headings_shared_kinds", shared = isSharedCompletionLayout(layout);
|
|
1286
1289
|
const outcomes = shared || layout === "headings_outcomes", directOmission = outcomes || layout === "headings_flat_omissions";
|
|
1287
1290
|
const run = member.run.startsWith("run:") ? member.run.slice(4) : member.run;
|
|
@@ -1604,8 +1607,97 @@ function completionYamlReferences(row) {
|
|
|
1604
1607
|
})) };
|
|
1605
1608
|
return technical;
|
|
1606
1609
|
}
|
|
1610
|
+
/** Split selected paths without normalize/resolve/relative: dot and symlink routes must not be rewritten. */
|
|
1611
|
+
function rootedCompletionPath(path, runRoot) {
|
|
1612
|
+
if (typeof path !== "string" || !path.isWellFormed())
|
|
1613
|
+
return undefined;
|
|
1614
|
+
const windows = /^(?:[A-Za-z]:[\\/]|\\\\[^\\/]+[\\/][^\\/]+[\\/])/u.test(path);
|
|
1615
|
+
const separator = windows ? /[\\/]/u : /\//u;
|
|
1616
|
+
if (!windows && !path.startsWith("/"))
|
|
1617
|
+
return undefined;
|
|
1618
|
+
if (path.split(separator).some(part => part === "." || part === ".."))
|
|
1619
|
+
return undefined;
|
|
1620
|
+
if (runRoot && path.startsWith(runRoot)) {
|
|
1621
|
+
const tail = path.slice(runRoot.length);
|
|
1622
|
+
if (separator.test(runRoot.slice(-1)) ? tail.length > 0 : separator.test(tail[0] ?? "")) {
|
|
1623
|
+
const relative = separator.test(runRoot.slice(-1)) ? tail : tail.slice(1);
|
|
1624
|
+
if (relative && !separator.test(relative[0]))
|
|
1625
|
+
return { root: runRoot, path: relative };
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
let split = -1;
|
|
1629
|
+
for (let i = 0; i < path.length; i++)
|
|
1630
|
+
if (separator.test(path[i]))
|
|
1631
|
+
split = i;
|
|
1632
|
+
if (split < 0 || split === path.length - 1)
|
|
1633
|
+
return undefined;
|
|
1634
|
+
const end = split === 0 || (windows && split === 2 && path[1] === ":") ? split + 1 : split;
|
|
1635
|
+
return { root: path.slice(0, end), path: path.slice(split + 1) };
|
|
1636
|
+
}
|
|
1637
|
+
/** Body-free, read-free navigation over already selected references. Roots are presentation bases, not containment proof. */
|
|
1638
|
+
function completionRootedFiles(row, member, locations) {
|
|
1639
|
+
const technical = completionYamlReferences(row), files = {};
|
|
1640
|
+
const groups = [];
|
|
1641
|
+
const runRoot = locations?.state === "available" ? locations.directory : undefined;
|
|
1642
|
+
const group = (root) => {
|
|
1643
|
+
const found = groups.findIndex(item => item.root === root);
|
|
1644
|
+
if (found >= 0)
|
|
1645
|
+
return found;
|
|
1646
|
+
groups.push({ root, entries: [] });
|
|
1647
|
+
return groups.length - 1;
|
|
1648
|
+
};
|
|
1649
|
+
let priority = Infinity;
|
|
1650
|
+
const add = (path, entry, rank = Infinity, base) => {
|
|
1651
|
+
const selected = rootedCompletionPath(path, runRoot);
|
|
1652
|
+
const index = group(selected?.root ?? (base === undefined ? null : completionYamlString(base)));
|
|
1653
|
+
const selectedPath = selected?.path ?? path;
|
|
1654
|
+
groups[index].entries.push({ ...entry, path: selectedPath,
|
|
1655
|
+
...(!selected && base === undefined ? { path_basis: "as_recorded", base_unavailable: true } : {}) });
|
|
1656
|
+
if (selected && rank < priority) {
|
|
1657
|
+
priority = rank;
|
|
1658
|
+
files.read_first = { group: index, entry: groups[index].entries.length - 1 };
|
|
1659
|
+
}
|
|
1660
|
+
};
|
|
1661
|
+
for (const [role, purpose, rank] of [
|
|
1662
|
+
["primary", "Full result text", 0], ["diagnostic_metadata", "Recorded error and command metadata", 1],
|
|
1663
|
+
["artifact_inventory", "Declared artifact inventory; target availability unproven", 2],
|
|
1664
|
+
]) {
|
|
1665
|
+
const detail = technical[role];
|
|
1666
|
+
if (!detail)
|
|
1667
|
+
continue;
|
|
1668
|
+
const { source: rawSource, ...facts } = detail;
|
|
1669
|
+
const { path, ...binding } = rawSource;
|
|
1670
|
+
add(path, { role, purpose, ...facts, source: binding }, rank);
|
|
1671
|
+
delete technical[role];
|
|
1672
|
+
}
|
|
1673
|
+
const artifacts = technical.artifacts;
|
|
1674
|
+
if (artifacts) {
|
|
1675
|
+
for (const item of artifacts.declarations)
|
|
1676
|
+
add(item.path, { role: "artifact", purpose: "Declared artifact", name: item.name, scope: artifacts.scope, state: "declared" });
|
|
1677
|
+
delete technical.artifacts;
|
|
1678
|
+
}
|
|
1679
|
+
if (locations?.state === "available") {
|
|
1680
|
+
const { directory, files: selectedFiles, ...observation } = locations;
|
|
1681
|
+
files.observation = { ...observation, run: completionYamlString(locations.run), run_instance_id: completionYamlString(locations.run_instance_id),
|
|
1682
|
+
group: group(completionYamlString(directory)) };
|
|
1683
|
+
for (const item of selectedFiles) {
|
|
1684
|
+
const { path, ...facts } = item;
|
|
1685
|
+
const purpose = item.role === "result" ? "Recorded result metadata" : item.role === "execution" ? "Recorded execution and capture locations" : "Recorded Run file";
|
|
1686
|
+
add(typeof path === "string" ? completionYamlString(path) : null, { ...facts, purpose }, item.state === "present" && item.role === "result" ? 3 : item.state === "present" && item.role === "execution" ? 4 : Infinity, directory);
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
else if (locations)
|
|
1690
|
+
files.observation = { ...locations, run: completionYamlString(member.run), run_instance_id: completionYamlString(member.run_instance_id) };
|
|
1691
|
+
if (groups.length) {
|
|
1692
|
+
files.groups = groups;
|
|
1693
|
+
}
|
|
1694
|
+
if (Object.keys(technical).length)
|
|
1695
|
+
files.notes = technical;
|
|
1696
|
+
return files;
|
|
1697
|
+
}
|
|
1607
1698
|
function prepareCompletionYamlProjection(safe, candidates, references, ordered) {
|
|
1608
1699
|
const packet = { batchId: safe.batch_id, records: [] };
|
|
1700
|
+
const rooted = safe.result_layout === "completion_yaml_files_v1";
|
|
1609
1701
|
if (!safe.batch_id.isWellFormed())
|
|
1610
1702
|
return { state: "unsupported" };
|
|
1611
1703
|
for (const [index, { row, location }] of candidates.entries()) {
|
|
@@ -1630,18 +1722,24 @@ function prepareCompletionYamlProjection(safe, candidates, references, ordered)
|
|
|
1630
1722
|
else
|
|
1631
1723
|
metadata[key] = completionYamlString(value);
|
|
1632
1724
|
}
|
|
1633
|
-
const
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1725
|
+
const navigation = references.has(index) && location
|
|
1726
|
+
? safe.file_navigation.find(item => safe.members[item.member_index] === member).locations : undefined;
|
|
1727
|
+
if (rooted) {
|
|
1728
|
+
const files = completionRootedFiles(row, member, navigation);
|
|
1729
|
+
if (Object.keys(files).length)
|
|
1730
|
+
metadata.files = files;
|
|
1731
|
+
}
|
|
1732
|
+
else {
|
|
1733
|
+
const technical = completionYamlReferences(row);
|
|
1734
|
+
if (Object.keys(technical).length)
|
|
1735
|
+
metadata.references = technical;
|
|
1736
|
+
if (navigation) {
|
|
1737
|
+
metadata.run_files = { run: completionYamlString(member.run), run_instance_id: completionYamlString(member.run_instance_id),
|
|
1738
|
+
locations: navigation.state === "available" ? { ...navigation, run: completionYamlString(navigation.run),
|
|
1739
|
+
run_instance_id: completionYamlString(navigation.run_instance_id), directory: completionYamlString(navigation.directory),
|
|
1740
|
+
files: navigation.files.map(file => ({ ...file, ...(typeof file.path === "string" ? { path: completionYamlString(file.path) } : {}) })) }
|
|
1741
|
+
: { ...navigation } };
|
|
1742
|
+
}
|
|
1645
1743
|
}
|
|
1646
1744
|
const parts = [];
|
|
1647
1745
|
const add = (part) => { if (parts.length)
|
|
@@ -1650,13 +1748,22 @@ function prepareCompletionYamlProjection(safe, candidates, references, ordered)
|
|
|
1650
1748
|
if (piece.block) {
|
|
1651
1749
|
if (!piece.text.isWellFormed())
|
|
1652
1750
|
return { state: "unsupported" };
|
|
1653
|
-
|
|
1751
|
+
if (rooted && piece.block === "marker" && !/^\[[0-9]+ characters omitted\]$/u.test(piece.text)) {
|
|
1752
|
+
metadata.text_warnings ??= [];
|
|
1753
|
+
metadata.text_warnings.push(piece.text);
|
|
1754
|
+
}
|
|
1755
|
+
else
|
|
1756
|
+
add(piece.block === "body" ? { body: piece.text } : { marker: piece.text });
|
|
1654
1757
|
}
|
|
1655
|
-
if (safe.file_navigation !== undefined && !references.has(index))
|
|
1656
|
-
|
|
1758
|
+
if (safe.file_navigation !== undefined && !references.has(index)) {
|
|
1759
|
+
if (rooted)
|
|
1760
|
+
metadata.run_files_omission = { reason: "packet_byte_limit", shown: 0, omitted: 1, total: 1 };
|
|
1761
|
+
else
|
|
1762
|
+
add({ marker: "Run-file reference omitted (packet byte limit)." });
|
|
1763
|
+
}
|
|
1657
1764
|
packet.records.push({ metadata, parts });
|
|
1658
1765
|
}
|
|
1659
|
-
const service = {};
|
|
1766
|
+
const service = rooted ? { header_layout: "rooted_files_v1" } : {};
|
|
1660
1767
|
const omitted = safe.members.length - candidates.length;
|
|
1661
1768
|
if (omitted) {
|
|
1662
1769
|
service.omitted_results = omitted;
|
|
@@ -1676,8 +1783,11 @@ function prepareCompletionYamlProjection(safe, candidates, references, ordered)
|
|
|
1676
1783
|
if (safe.numbering.kind === "unavailable")
|
|
1677
1784
|
service.numbering_unavailable = safe.numbering.reason;
|
|
1678
1785
|
}
|
|
1679
|
-
if (safe.inventory_source)
|
|
1680
|
-
|
|
1786
|
+
if (safe.inventory_source) {
|
|
1787
|
+
const path = rooted ? rootedCompletionPath(safe.inventory_source.path) : undefined;
|
|
1788
|
+
service.result_inventory = { ...safe.inventory_source,
|
|
1789
|
+
...(path ? { root: path.root, path: path.path } : { path: completionYamlString(safe.inventory_source.path) }) };
|
|
1790
|
+
}
|
|
1681
1791
|
if (Object.keys(service).length)
|
|
1682
1792
|
packet.service = service;
|
|
1683
1793
|
const chunks = [];
|
|
@@ -1737,7 +1847,7 @@ export function projectRunCompletionYamlPrototype(batch) {
|
|
|
1737
1847
|
throw error;
|
|
1738
1848
|
}
|
|
1739
1849
|
}
|
|
1740
|
-
function projectCompletionRecords(safe, assembler = safe.result_layout
|
|
1850
|
+
function projectCompletionRecords(safe, assembler = isYamlCompletionLayout(safe.result_layout) ? "yaml" : "legacy") {
|
|
1741
1851
|
const frontmatter = isFrontmatterCompletionLayout(safe.result_layout) || assembler === "yaml";
|
|
1742
1852
|
const shared = isSharedCompletionLayout(safe.result_layout);
|
|
1743
1853
|
const maxBytes = shared ? Limits.RUN_COMPLETION_MODEL_MAX_BYTES : Limits.RUN_DELIVERY_MODEL_MAX_BYTES;
|
|
@@ -2024,7 +2134,7 @@ export function projectRunCompletionBatchPresentation(batch) {
|
|
|
2024
2134
|
if (safe.presentation_format !== "ax_records" && safe.presentation_format !== "ax_records_v3")
|
|
2025
2135
|
return { content: projectCompletionBatchMessage(safe).message };
|
|
2026
2136
|
const result = safe.result_layout !== undefined ? projectCompletionRecords(safe) : projectRetainedRecordCompletionBatch(safe);
|
|
2027
|
-
if (safe.result_layout
|
|
2137
|
+
if (isYamlCompletionLayout(safe.result_layout))
|
|
2028
2138
|
return { content: result.message, presentation: Object.freeze({
|
|
2029
2139
|
format: "completion_yaml_core_v1", content_sha256: createHash("sha256").update(result.message).digest("hex"),
|
|
2030
2140
|
}) };
|
|
@@ -2192,10 +2302,14 @@ export function readRunCompletionPresentation(content, value) {
|
|
|
2192
2302
|
hint.content_sha256 !== createHash("sha256").update(content).digest("hex"))
|
|
2193
2303
|
return full;
|
|
2194
2304
|
const segments = [];
|
|
2305
|
+
const rooted = core.packet.service?.header_layout === "rooted_files_v1";
|
|
2306
|
+
const facts = Object.fromEntries(Object.entries(core.packet.service ?? {}).filter(([key]) => key !== "header_layout"));
|
|
2195
2307
|
for (const [index, record] of core.packet.records.entries()) {
|
|
2196
2308
|
if (index)
|
|
2197
2309
|
segments.push({ kind: "separator", text: "\n" });
|
|
2198
|
-
const metadata = Object.fromEntries(Object.entries(record.metadata).filter(([key]) => key !== "references" && key !== "run_files"));
|
|
2310
|
+
const metadata = Object.fromEntries(Object.entries(record.metadata).filter(([key]) => rooted || (key !== "references" && key !== "run_files")));
|
|
2311
|
+
if (rooted && index === 0 && Object.keys(facts).length)
|
|
2312
|
+
metadata.delivery = facts;
|
|
2199
2313
|
const header = renderYamlMetadata(metadata, completionYamlMaxBytes);
|
|
2200
2314
|
if (header.state !== "available")
|
|
2201
2315
|
return full;
|
|
@@ -2203,13 +2317,13 @@ export function readRunCompletionPresentation(content, value) {
|
|
|
2203
2317
|
for (const part of record.parts)
|
|
2204
2318
|
segments.push("body" in part ? { kind: "body", text: part.body } : { kind: "marker", text: part.marker });
|
|
2205
2319
|
}
|
|
2206
|
-
if (core.packet.service) {
|
|
2207
|
-
const service = renderYamlMetadata(core.packet.service, completionYamlMaxBytes);
|
|
2320
|
+
if (core.packet.service && (!rooted || !core.packet.records.length)) {
|
|
2321
|
+
const service = renderYamlMetadata(rooted ? facts : core.packet.service, completionYamlMaxBytes);
|
|
2208
2322
|
if (service.state !== "available")
|
|
2209
2323
|
return full;
|
|
2210
2324
|
if (segments.length)
|
|
2211
2325
|
segments.push({ kind: "separator", text: "\n" });
|
|
2212
|
-
segments.push({ kind: "service", text: service.text.replace(/\n$/u, "") });
|
|
2326
|
+
segments.push({ kind: rooted ? "header" : "service", text: service.text.replace(/\n$/u, "") });
|
|
2213
2327
|
}
|
|
2214
2328
|
return { text: segments.map(segment => segment.text).join("\n"), compact: true, records: true, yaml: true,
|
|
2215
2329
|
segments: Object.freeze(segments.map(segment => Object.freeze(segment))) };
|
|
@@ -12,26 +12,21 @@ import * as Pi from "./pi.js";
|
|
|
12
12
|
import * as RunDelivery from "./run-delivery.js";
|
|
13
13
|
import * as RunDeliveryLineage from "./run-delivery-lineage.js";
|
|
14
14
|
import * as RunIndex from "./runs-index.js";
|
|
15
|
-
import {
|
|
15
|
+
import { formatYamlNotice } from "./tools-response.js";
|
|
16
16
|
function formatSteerDiagnosticMessage(eventId, stage, message) {
|
|
17
|
-
const fields = [formatMarkdownField("event", eventId), formatMarkdownField("attention", "steer"),
|
|
18
|
-
formatMarkdownField("stage", stage), formatMarkdownField("result", stage === "admission" ? "remains retryable" : "request failed")];
|
|
19
|
-
if (stage === "delivery")
|
|
20
|
-
fields.push(formatMarkdownField("delivery", "unconfirmed"), formatMarkdownField("presentation", "unconfirmed"));
|
|
21
17
|
let end = Math.min(message.length, 240);
|
|
22
18
|
const last = message.charCodeAt(end - 1), next = message.charCodeAt(end);
|
|
23
19
|
if (last >= 0xd800 && last <= 0xdbff && next >= 0xdc00 && next <= 0xdfff)
|
|
24
20
|
end -= 1;
|
|
25
21
|
const preview = message.slice(0, end), clipped = end < message.length;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return fields.join("\n");
|
|
22
|
+
// The 240-pair-aligned preview and its explicit unknown-loss marker are unchanged; only metadata is YAML.
|
|
23
|
+
const diagnostic = Buffer.from(preview).toString("utf8") !== preview
|
|
24
|
+
? { diagnostic: "unavailable", diagnostic_reason: "malformed legacy preview", diagnostic_omission: "[omitted: unknown]" }
|
|
25
|
+
: { diagnostic: preview + (clipped ? "…" : ""), ...(clipped ? { diagnostic_omission: "[omitted: unknown]" } : {}) };
|
|
26
|
+
return formatYamlNotice({ event: eventId, attention: "steer", stage,
|
|
27
|
+
result: stage === "admission" ? "remains retryable" : "request failed",
|
|
28
|
+
...(stage === "delivery" ? { delivery: "unconfirmed", presentation: "unconfirmed" } : {}),
|
|
29
|
+
...diagnostic });
|
|
35
30
|
}
|
|
36
31
|
/** Transcript-only view of the exact delivered content; never re-read a Run or infer body labels. */
|
|
37
32
|
export function registerRunCompletionRenderer(pi) {
|
|
@@ -162,8 +157,7 @@ export function createRunUiRuntime(deps) {
|
|
|
162
157
|
}
|
|
163
158
|
const message = error instanceof Error ? error.message : String(error);
|
|
164
159
|
try {
|
|
165
|
-
expectedContext.ui.notify(
|
|
166
|
-
formatMarkdownField("diagnostic", message)].join("\n"), "error");
|
|
160
|
+
expectedContext.ui.notify(formatYamlNotice({ callback: label, result: "failed", diagnostic: message }), "error");
|
|
167
161
|
}
|
|
168
162
|
catch {
|
|
169
163
|
/* stale context or unavailable UI */
|
|
@@ -327,9 +321,8 @@ export function createRunUiRuntime(deps) {
|
|
|
327
321
|
const key = `${batch.batch_id}:${evidence.status}:${evidence.reason ?? ""}`;
|
|
328
322
|
if (!completionDiagnostics.has(key)) {
|
|
329
323
|
completionDiagnostics.add(key);
|
|
330
|
-
ctx.ui.notify("\n" +
|
|
331
|
-
|
|
332
|
-
formatMarkdownField("reason", evidence.reason ?? "conflicting session evidence")].join("\n"), "warning");
|
|
324
|
+
ctx.ui.notify("\n" + formatYamlNotice({ batch: batch.batch_id, stage: "completion recovery",
|
|
325
|
+
evidence: evidence.status, phase: "pending", reason: evidence.reason ?? "conflicting session evidence" }), "warning");
|
|
333
326
|
}
|
|
334
327
|
return true;
|
|
335
328
|
}
|
|
@@ -413,9 +406,8 @@ export function createRunUiRuntime(deps) {
|
|
|
413
406
|
const key = `${steer.steer_id}:${evidence.status}:${evidence.reason ?? ""}`;
|
|
414
407
|
if (!steerDiagnostics.has(key)) {
|
|
415
408
|
steerDiagnostics.add(key);
|
|
416
|
-
ctx.ui.notify(
|
|
417
|
-
|
|
418
|
-
formatMarkdownField("phase", "queued"), formatMarkdownField("reason", evidence.reason ?? "conflicting session evidence")].join("\n"), "warning");
|
|
409
|
+
ctx.ui.notify(formatYamlNotice({ event: steer.event_id, attention: "steer", stage: "recovery",
|
|
410
|
+
evidence: evidence.status, phase: "queued", reason: evidence.reason ?? "conflicting session evidence" }), "warning");
|
|
419
411
|
}
|
|
420
412
|
continue;
|
|
421
413
|
}
|
|
@@ -681,11 +673,10 @@ export function createRunUiRuntime(deps) {
|
|
|
681
673
|
if (teardown.failed === 0 || !ctx)
|
|
682
674
|
return;
|
|
683
675
|
try {
|
|
684
|
-
ctx.ui.notify(
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
formatMarkdownField("basis", "teardown report; not scope closure")].join("\n"), "warning");
|
|
676
|
+
ctx.ui.notify(formatYamlNotice({ stage: "shutdown teardown", killed: teardown.killed,
|
|
677
|
+
failed: teardown.failed, skipped: teardown.skipped, discovery_failed: teardown.discoveryFailed,
|
|
678
|
+
summary: teardown.summaryPath ?? "unavailable",
|
|
679
|
+
basis: "teardown report; not scope closure" }), "warning");
|
|
689
680
|
}
|
|
690
681
|
catch {
|
|
691
682
|
/* stale shutdown context */
|
|
@@ -43,4 +43,75 @@ export declare function decodeProvenanceBinding(value: unknown): {
|
|
|
43
43
|
readonly reason: 'invalid_binding';
|
|
44
44
|
};
|
|
45
45
|
export declare function decodeResponse(bytes: Uint8Array, profile: unknown, pids: unknown): DecodeOutcome;
|
|
46
|
+
/**
|
|
47
|
+
* Complete-helper manifest claim. Bounded reading, exact-key schema and closed lexemes only; a valid
|
|
48
|
+
* manifest is integrity/provenance input, never executable authenticity, an approved asset or activation.
|
|
49
|
+
*/
|
|
50
|
+
type HelperManifest = {
|
|
51
|
+
readonly helper_version: string;
|
|
52
|
+
readonly architecture: 'windows-x64';
|
|
53
|
+
readonly protocol: 'native-mi-v1';
|
|
54
|
+
readonly profiles: readonly ['scalar', 'shared'];
|
|
55
|
+
readonly native_source_sha256: string;
|
|
56
|
+
readonly executable_bytes: number;
|
|
57
|
+
readonly executable_sha256: string;
|
|
58
|
+
readonly toolchain: {
|
|
59
|
+
readonly compiler: string;
|
|
60
|
+
readonly sdk: string;
|
|
61
|
+
readonly build_arguments: readonly string[];
|
|
62
|
+
};
|
|
63
|
+
readonly dependencies: readonly string[];
|
|
64
|
+
readonly originating_source_commit: string;
|
|
65
|
+
readonly originating_build_job: string;
|
|
66
|
+
};
|
|
67
|
+
type ManifestUnusable = {
|
|
68
|
+
readonly kind: 'unusable';
|
|
69
|
+
readonly reason: 'oversize_manifest' | 'invalid_manifest' | 'unsupported_manifest';
|
|
70
|
+
};
|
|
71
|
+
/** Bounded complete-manifest reader: one strict UTF-8 JSON document with an exact reviewed shape. */
|
|
72
|
+
export declare function readHelperManifest(bytes: Uint8Array): {
|
|
73
|
+
readonly kind: 'manifest_unadmitted';
|
|
74
|
+
readonly manifest: HelperManifest;
|
|
75
|
+
} | ManifestUnusable;
|
|
76
|
+
type HelperAssetLocation = {
|
|
77
|
+
readonly kind: 'located';
|
|
78
|
+
readonly helper_version: string;
|
|
79
|
+
readonly manifest_url: URL;
|
|
80
|
+
readonly executable_url: URL;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Fixed versioned package-relative lookup. Never searches cwd, PATH or caller locations: the only base is
|
|
84
|
+
* the sibling `native/` directory of this module's own `lib/` (source) or `dist/lib/` (compiled) home.
|
|
85
|
+
*/
|
|
86
|
+
export declare function resolveHelperAsset(moduleUrl: unknown, runtime: unknown, helperVersion: unknown): HelperAssetLocation | {
|
|
87
|
+
readonly kind: 'unusable';
|
|
88
|
+
readonly reason: 'unsupported_location' | 'unsupported_target' | 'unsupported_version';
|
|
89
|
+
};
|
|
90
|
+
/** Injected bounded reader: exact bytes for one fixed URL or nothing. Never a path, cwd or PATH lookup. */
|
|
91
|
+
export type HelperAssetReader = (url: URL, maximumBytes: number) => Uint8Array | undefined;
|
|
92
|
+
type HelperAssetOutcome = {
|
|
93
|
+
readonly kind: 'asset_verified_unadmitted';
|
|
94
|
+
readonly manifest: HelperManifest;
|
|
95
|
+
readonly executable_sha256: string;
|
|
96
|
+
readonly executable_bytes: number;
|
|
97
|
+
} | {
|
|
98
|
+
readonly kind: 'unusable';
|
|
99
|
+
readonly reason: 'missing_manifest' | 'oversize_manifest' | 'invalid_manifest' | 'unsupported_manifest' | 'version_mismatch' | 'missing_asset' | 'oversize_asset' | 'size_mismatch' | 'digest_mismatch';
|
|
100
|
+
};
|
|
101
|
+
/** Trusted exact-byte selection: bounded manifest read, exact declared size, then exact SHA-256 match. */
|
|
102
|
+
export declare function loadHelperAsset(location: HelperAssetLocation, read: HelperAssetReader): HelperAssetOutcome;
|
|
103
|
+
type HelperInvocation = {
|
|
104
|
+
readonly kind: 'invocation_unadmitted';
|
|
105
|
+
readonly command: string;
|
|
106
|
+
readonly args: readonly string[];
|
|
107
|
+
readonly options: {
|
|
108
|
+
readonly shell: false;
|
|
109
|
+
readonly windowsHide: true;
|
|
110
|
+
};
|
|
111
|
+
} | {
|
|
112
|
+
readonly kind: 'unusable';
|
|
113
|
+
readonly reason: 'unsupported_executable' | 'invalid_query';
|
|
114
|
+
};
|
|
115
|
+
/** Shell-free invocation spec only: no spawn, retry, fallback or execution-policy bypass. */
|
|
116
|
+
export declare function helperInvocation(executablePath: unknown, profile: unknown, pids: unknown): HelperInvocation;
|
|
46
117
|
export {};
|
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
// Owns: Bounded raw MI wire decoding, query inputs
|
|
2
|
-
//
|
|
3
|
-
//
|
|
1
|
+
// Owns: Bounded raw MI wire decoding, query inputs, unadmitted provenance fragments, and unrouted
|
|
2
|
+
// helper manifest/asset verification plus a shell-free invocation spec, subordinate to process acquisition.
|
|
3
|
+
// Excludes: Identity construction/digest/comparison, status/liveness/lifecycle/publication/admission, fallback,
|
|
4
|
+
// process spawning and package inventory. No production reader imports this owner; decoded data, verified
|
|
5
|
+
// asset bytes and the invocation spec never admit a representation profile, architecture or activation.
|
|
6
|
+
import { createHash } from 'node:crypto';
|
|
7
|
+
const MANIFEST_MAX_BYTES = 32 * 1024;
|
|
8
|
+
const EXECUTABLE_MAX_BYTES = 8 * 1024 * 1024;
|
|
9
|
+
const HELPER_VERSION = /^(?:0|[1-9][0-9]{0,5})\.(?:0|[1-9][0-9]{0,5})\.(?:0|[1-9][0-9]{0,5})$/u;
|
|
10
|
+
const MODULE_FILE = /\/lib\/runs-process-windows-mi\.(?:ts|js)$/u;
|
|
4
11
|
export function queryArguments(profile, pids) {
|
|
5
12
|
if ((profile !== 'scalar' && profile !== 'shared') || !Array.isArray(pids) ||
|
|
6
13
|
pids.length < 1 || pids.length > 64 || (profile === 'scalar' && pids.length !== 1))
|
|
@@ -112,3 +119,139 @@ export function decodeResponse(bytes, profile, pids) {
|
|
|
112
119
|
return { kind: 'unusable', reason: 'unsupported_optional_fields' };
|
|
113
120
|
return { kind: 'decoded_unadmitted', rows };
|
|
114
121
|
}
|
|
122
|
+
function boundedText(bytes, maximumBytes) {
|
|
123
|
+
if (!(bytes instanceof Uint8Array) || bytes.byteLength > maximumBytes)
|
|
124
|
+
return undefined;
|
|
125
|
+
if (bytes[0] === 0xef && bytes[1] === 0xbb && bytes[2] === 0xbf)
|
|
126
|
+
return undefined;
|
|
127
|
+
try {
|
|
128
|
+
return new TextDecoder('utf-8', { fatal: true }).decode(bytes);
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
function boundedLexemes(value, maximum) {
|
|
135
|
+
if (!Array.isArray(value) || value.length > maximum)
|
|
136
|
+
return undefined;
|
|
137
|
+
const selected = [];
|
|
138
|
+
for (const item of value) {
|
|
139
|
+
if (typeof item !== 'string' || item.length === 0 || item.length > 128 || !/^[\x20-\x7e]+$/.test(item) || selected.includes(item))
|
|
140
|
+
return undefined;
|
|
141
|
+
selected.push(item);
|
|
142
|
+
}
|
|
143
|
+
return selected;
|
|
144
|
+
}
|
|
145
|
+
/** Bounded complete-manifest reader: one strict UTF-8 JSON document with an exact reviewed shape. */
|
|
146
|
+
export function readHelperManifest(bytes) {
|
|
147
|
+
const invalid = { kind: 'unusable', reason: 'invalid_manifest' };
|
|
148
|
+
if (!(bytes instanceof Uint8Array) || bytes.byteLength > MANIFEST_MAX_BYTES)
|
|
149
|
+
return { kind: 'unusable', reason: 'oversize_manifest' };
|
|
150
|
+
const text = boundedText(bytes, MANIFEST_MAX_BYTES);
|
|
151
|
+
if (text === undefined)
|
|
152
|
+
return invalid;
|
|
153
|
+
let value;
|
|
154
|
+
try {
|
|
155
|
+
value = JSON.parse(text);
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
return invalid;
|
|
159
|
+
}
|
|
160
|
+
if (!exactKeys(value, ['helper_version', 'architecture', 'protocol', 'profiles', 'native_source_sha256',
|
|
161
|
+
'executable_bytes', 'executable_sha256', 'toolchain', 'dependencies', 'originating_source_commit', 'originating_build_job']))
|
|
162
|
+
return invalid;
|
|
163
|
+
const { helper_version, architecture, protocol, profiles, native_source_sha256, executable_bytes, executable_sha256, toolchain, dependencies, originating_source_commit, originating_build_job } = value;
|
|
164
|
+
if (typeof helper_version !== 'string' || !HELPER_VERSION.test(helper_version) ||
|
|
165
|
+
architecture !== 'windows-x64' || protocol !== 'native-mi-v1' ||
|
|
166
|
+
!Array.isArray(profiles) || profiles.length !== 2 || profiles[0] !== 'scalar' || profiles[1] !== 'shared')
|
|
167
|
+
return { kind: 'unusable', reason: 'unsupported_manifest' };
|
|
168
|
+
if (typeof native_source_sha256 !== 'string' || !/^[0-9a-f]{64}$/.test(native_source_sha256) ||
|
|
169
|
+
typeof executable_sha256 !== 'string' || !/^[0-9a-f]{64}$/.test(executable_sha256) ||
|
|
170
|
+
typeof originating_source_commit !== 'string' || !/^[0-9a-f]{40}$/.test(originating_source_commit) ||
|
|
171
|
+
typeof originating_build_job !== 'string' || !/^[1-9][0-9]*$/.test(originating_build_job) ||
|
|
172
|
+
typeof executable_bytes !== 'number' || !Number.isSafeInteger(executable_bytes) || executable_bytes <= 0)
|
|
173
|
+
return invalid;
|
|
174
|
+
if (executable_bytes > EXECUTABLE_MAX_BYTES)
|
|
175
|
+
return { kind: 'unusable', reason: 'unsupported_manifest' };
|
|
176
|
+
if (!exactKeys(toolchain, ['compiler', 'sdk', 'build_arguments']))
|
|
177
|
+
return invalid;
|
|
178
|
+
const compiler = boundedLexemes([toolchain.compiler], 1)?.[0];
|
|
179
|
+
const sdk = boundedLexemes([toolchain.sdk], 1)?.[0];
|
|
180
|
+
const buildArguments = boundedLexemes(toolchain.build_arguments, 32);
|
|
181
|
+
const dependencyNames = boundedLexemes(dependencies, 32);
|
|
182
|
+
if (compiler === undefined || sdk === undefined || buildArguments === undefined || dependencyNames === undefined)
|
|
183
|
+
return invalid;
|
|
184
|
+
// Fresh primitive claims only; the caller still owns target/version/source-digest policy.
|
|
185
|
+
return {
|
|
186
|
+
kind: 'manifest_unadmitted',
|
|
187
|
+
manifest: {
|
|
188
|
+
helper_version, architecture: 'windows-x64', protocol: 'native-mi-v1', profiles: ['scalar', 'shared'],
|
|
189
|
+
native_source_sha256, executable_bytes, executable_sha256,
|
|
190
|
+
toolchain: { compiler, sdk, build_arguments: buildArguments }, dependencies: dependencyNames,
|
|
191
|
+
originating_source_commit, originating_build_job,
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Fixed versioned package-relative lookup. Never searches cwd, PATH or caller locations: the only base is
|
|
197
|
+
* the sibling `native/` directory of this module's own `lib/` (source) or `dist/lib/` (compiled) home.
|
|
198
|
+
*/
|
|
199
|
+
export function resolveHelperAsset(moduleUrl, runtime, helperVersion) {
|
|
200
|
+
let url;
|
|
201
|
+
try {
|
|
202
|
+
url = new URL(String(moduleUrl));
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
return { kind: 'unusable', reason: 'unsupported_location' };
|
|
206
|
+
}
|
|
207
|
+
if (url.protocol !== 'file:' || !MODULE_FILE.test(url.pathname))
|
|
208
|
+
return { kind: 'unusable', reason: 'unsupported_location' };
|
|
209
|
+
const target = runtime;
|
|
210
|
+
if (target?.platform !== 'win32' || target?.arch !== 'x64')
|
|
211
|
+
return { kind: 'unusable', reason: 'unsupported_target' };
|
|
212
|
+
if (typeof helperVersion !== 'string' || !HELPER_VERSION.test(helperVersion))
|
|
213
|
+
return { kind: 'unusable', reason: 'unsupported_version' };
|
|
214
|
+
const directory = `../native/windows-mi/win32-x64/${helperVersion}/`;
|
|
215
|
+
return {
|
|
216
|
+
kind: 'located', helper_version: helperVersion,
|
|
217
|
+
manifest_url: new URL(`${directory}manifest.json`, url),
|
|
218
|
+
executable_url: new URL(`${directory}helper.exe`, url),
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
/** Trusted exact-byte selection: bounded manifest read, exact declared size, then exact SHA-256 match. */
|
|
222
|
+
export function loadHelperAsset(location, read) {
|
|
223
|
+
const manifestBytes = read(location.manifest_url, MANIFEST_MAX_BYTES + 1);
|
|
224
|
+
if (manifestBytes === undefined)
|
|
225
|
+
return { kind: 'unusable', reason: 'missing_manifest' };
|
|
226
|
+
if (manifestBytes.byteLength > MANIFEST_MAX_BYTES)
|
|
227
|
+
return { kind: 'unusable', reason: 'oversize_manifest' };
|
|
228
|
+
const reading = readHelperManifest(manifestBytes);
|
|
229
|
+
if (reading.kind === 'unusable')
|
|
230
|
+
return { kind: 'unusable', reason: reading.reason };
|
|
231
|
+
const { manifest } = reading;
|
|
232
|
+
if (manifest.helper_version !== location.helper_version)
|
|
233
|
+
return { kind: 'unusable', reason: 'version_mismatch' };
|
|
234
|
+
// One extra byte detects growth; a shorter read can never satisfy the declared size.
|
|
235
|
+
const executableBytes = read(location.executable_url, manifest.executable_bytes + 1);
|
|
236
|
+
if (executableBytes === undefined)
|
|
237
|
+
return { kind: 'unusable', reason: 'missing_asset' };
|
|
238
|
+
if (executableBytes.byteLength > manifest.executable_bytes)
|
|
239
|
+
return { kind: 'unusable', reason: 'oversize_asset' };
|
|
240
|
+
if (executableBytes.byteLength !== manifest.executable_bytes)
|
|
241
|
+
return { kind: 'unusable', reason: 'size_mismatch' };
|
|
242
|
+
const digest = createHash('sha256').update(executableBytes).digest('hex');
|
|
243
|
+
if (digest !== manifest.executable_sha256)
|
|
244
|
+
return { kind: 'unusable', reason: 'digest_mismatch' };
|
|
245
|
+
return { kind: 'asset_verified_unadmitted', manifest, executable_sha256: digest, executable_bytes: executableBytes.byteLength };
|
|
246
|
+
}
|
|
247
|
+
/** Shell-free invocation spec only: no spawn, retry, fallback or execution-policy bypass. */
|
|
248
|
+
export function helperInvocation(executablePath, profile, pids) {
|
|
249
|
+
if (typeof executablePath !== 'string' || executablePath.length === 0 || executablePath.length > 4096 ||
|
|
250
|
+
!/^[A-Za-z]:[\\/][^\x00\r\n"]*\.exe$/.test(executablePath))
|
|
251
|
+
return { kind: 'unusable', reason: 'unsupported_executable' };
|
|
252
|
+
const query = queryArguments(profile, pids);
|
|
253
|
+
if (!query)
|
|
254
|
+
return { kind: 'unusable', reason: 'invalid_query' };
|
|
255
|
+
return { kind: 'invocation_unadmitted', command: executablePath, args: query,
|
|
256
|
+
options: { shell: false, windowsHide: true } };
|
|
257
|
+
}
|