@llblab/pi-actors 0.55.0 → 0.56.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.
Files changed (188) hide show
  1. package/AGENTS.md +108 -20
  2. package/BACKLOG.md +198 -4
  3. package/CHANGELOG.md +17 -0
  4. package/README.md +31 -1
  5. package/dist/index.js +1 -1
  6. package/dist/lib/async-runs.d.ts +37 -0
  7. package/dist/lib/async-runs.js +131 -62
  8. package/dist/lib/automatic-review-runtime.d.ts +2 -0
  9. package/dist/lib/automatic-review-runtime.js +3 -2
  10. package/dist/lib/command-templates.d.ts +37 -0
  11. package/dist/lib/command-templates.js +263 -129
  12. package/dist/lib/execution-files.d.ts +32 -0
  13. package/dist/lib/execution-files.js +128 -0
  14. package/dist/lib/execution-output.js +5 -4
  15. package/dist/lib/execution-sessions.d.ts +19 -1
  16. package/dist/lib/execution-sessions.js +62 -26
  17. package/dist/lib/execution.d.ts +4 -1
  18. package/dist/lib/execution.js +6 -1
  19. package/dist/lib/extension-runtime.d.ts +9 -2
  20. package/dist/lib/extension-runtime.js +92 -14
  21. package/dist/lib/file-state.js +21 -2
  22. package/dist/lib/inspector-command.js +2 -2
  23. package/dist/lib/inspector-overlay.d.ts +25 -7
  24. package/dist/lib/inspector-overlay.js +562 -262
  25. package/dist/lib/inspector.d.ts +20 -10
  26. package/dist/lib/inspector.js +80 -91
  27. package/dist/lib/limits.d.ts +1 -0
  28. package/dist/lib/limits.js +1 -0
  29. package/dist/lib/observability.d.ts +15 -1
  30. package/dist/lib/observability.js +216 -99
  31. package/dist/lib/pi.d.ts +5 -4
  32. package/dist/lib/pi.js +64 -10
  33. package/dist/lib/recipe-projection.d.ts +18 -0
  34. package/dist/lib/recipe-projection.js +220 -0
  35. package/dist/lib/recipes-context.d.ts +2 -0
  36. package/dist/lib/recipes-context.js +30 -6
  37. package/dist/lib/registry.js +280 -36
  38. package/dist/lib/run-delivery.d.ts +213 -5
  39. package/dist/lib/run-delivery.js +1909 -40
  40. package/dist/lib/run-supervision.d.ts +107 -0
  41. package/dist/lib/run-supervision.js +412 -0
  42. package/dist/lib/run-ui-runtime.d.ts +9 -2
  43. package/dist/lib/run-ui-runtime.js +283 -60
  44. package/dist/lib/runs-artifacts.d.ts +26 -1
  45. package/dist/lib/runs-artifacts.js +68 -2
  46. package/dist/lib/runs-context-exports.d.ts +451 -0
  47. package/dist/lib/runs-context-exports.js +2315 -0
  48. package/dist/lib/runs-context-index.d.ts +97 -0
  49. package/dist/lib/runs-context-index.js +368 -0
  50. package/dist/lib/runs-control-delivery.js +46 -7
  51. package/dist/lib/runs-control.d.ts +78 -2
  52. package/dist/lib/runs-control.js +334 -2
  53. package/dist/lib/runs-controls.js +4 -1
  54. package/dist/lib/runs-descendant-launches.d.ts +80 -0
  55. package/dist/lib/runs-descendant-launches.js +287 -0
  56. package/dist/lib/runs-execution-evidence.d.ts +110 -0
  57. package/dist/lib/runs-execution-evidence.js +282 -0
  58. package/dist/lib/runs-index.d.ts +66 -1
  59. package/dist/lib/runs-index.js +349 -13
  60. package/dist/lib/runs-native-launches.d.ts +73 -0
  61. package/dist/lib/runs-native-launches.js +227 -0
  62. package/dist/lib/runs-outcomes.d.ts +141 -0
  63. package/dist/lib/runs-outcomes.js +292 -0
  64. package/dist/lib/runs-ownership.d.ts +10 -1
  65. package/dist/lib/runs-ownership.js +36 -4
  66. package/dist/lib/runs-process-windows-mi.d.ts +46 -0
  67. package/dist/lib/runs-process-windows-mi.js +114 -0
  68. package/dist/lib/runs-process.d.ts +34 -1
  69. package/dist/lib/runs-process.js +158 -26
  70. package/dist/lib/runs-results.d.ts +116 -0
  71. package/dist/lib/runs-results.js +518 -0
  72. package/dist/lib/runs-retention.d.ts +15 -2
  73. package/dist/lib/runs-retention.js +148 -9
  74. package/dist/lib/runs-session-exports.d.ts +95 -0
  75. package/dist/lib/runs-session-exports.js +470 -0
  76. package/dist/lib/runs-snapshot.d.ts +58 -0
  77. package/dist/lib/runs-snapshot.js +92 -0
  78. package/dist/lib/runs-start.d.ts +2 -0
  79. package/dist/lib/runs-start.js +44 -3
  80. package/dist/lib/runs-status.d.ts +15 -1
  81. package/dist/lib/runs-status.js +337 -13
  82. package/dist/lib/runs-supervision.d.ts +105 -0
  83. package/dist/lib/runs-supervision.js +501 -0
  84. package/dist/lib/runs-trace.d.ts +1 -0
  85. package/dist/lib/runs-trace.js +14 -3
  86. package/dist/lib/runtime.js +22 -3
  87. package/dist/lib/session-evidence.d.ts +37 -0
  88. package/dist/lib/session-evidence.js +59 -3
  89. package/dist/lib/state-readers.d.ts +4 -1
  90. package/dist/lib/state-readers.js +14 -8
  91. package/dist/lib/temp.d.ts +2 -4
  92. package/dist/lib/temp.js +4 -56
  93. package/dist/lib/tools-access.d.ts +3 -0
  94. package/dist/lib/tools-access.js +13 -0
  95. package/dist/lib/tools-inspect.d.ts +1 -0
  96. package/dist/lib/tools-inspect.js +110 -43
  97. package/dist/lib/tools-local.d.ts +3 -0
  98. package/dist/lib/tools-local.js +18 -6
  99. package/dist/lib/tools-message.d.ts +4 -1
  100. package/dist/lib/tools-message.js +126 -31
  101. package/dist/lib/tools-response.d.ts +31 -3
  102. package/dist/lib/tools-response.js +311 -47
  103. package/dist/lib/tools-spawn.d.ts +3 -0
  104. package/dist/lib/tools-spawn.js +29 -7
  105. package/dist/lib/trace-projection.d.ts +20 -0
  106. package/dist/lib/trace-projection.js +192 -43
  107. package/dist/scripts/async-runner.mjs +272 -194
  108. package/dist/scripts/conformance.mjs +14 -2
  109. package/dist/skills/actors/SKILL.md +46 -2
  110. package/dist/skills/actors/references/diagnostics.md +40 -0
  111. package/dist/skills/actors/references/runs.md +55 -1
  112. package/docs/README.md +9 -1
  113. package/docs/actor-inspector.md +84 -15
  114. package/docs/agent-experience.md +216 -0
  115. package/docs/async-runs.md +18 -2
  116. package/docs/composition-transport.md +103 -0
  117. package/docs/coordinator-delivery.md +204 -4
  118. package/docs/inspection.md +38 -2
  119. package/docs/output-projection-map.md +130 -0
  120. package/docs/releasing.md +19 -2
  121. package/docs/run-supervision.md +1622 -0
  122. package/docs/template-recipes.md +4 -0
  123. package/docs/windows-process-observation.md +248 -0
  124. package/index.ts +1 -1
  125. package/lib/async-runs.ts +164 -73
  126. package/lib/automatic-review-runtime.ts +4 -2
  127. package/lib/command-templates.ts +287 -138
  128. package/lib/execution-files.ts +118 -0
  129. package/lib/execution-output.ts +4 -4
  130. package/lib/execution-sessions.ts +57 -27
  131. package/lib/execution.ts +8 -0
  132. package/lib/extension-runtime.ts +102 -12
  133. package/lib/file-state.ts +16 -2
  134. package/lib/inspector-command.ts +2 -2
  135. package/lib/inspector-overlay.ts +524 -261
  136. package/lib/inspector.ts +101 -130
  137. package/lib/limits.ts +1 -0
  138. package/lib/observability.ts +235 -116
  139. package/lib/pi.ts +59 -11
  140. package/lib/recipe-projection.ts +232 -0
  141. package/lib/recipes-context.ts +27 -6
  142. package/lib/registry.ts +275 -36
  143. package/lib/run-delivery.ts +1737 -49
  144. package/lib/run-supervision.ts +411 -0
  145. package/lib/run-ui-runtime.ts +237 -73
  146. package/lib/runs-artifacts.ts +73 -1
  147. package/lib/runs-context-exports.ts +1871 -0
  148. package/lib/runs-context-index.ts +372 -0
  149. package/lib/runs-control-delivery.ts +36 -18
  150. package/lib/runs-control.ts +297 -1
  151. package/lib/runs-controls.ts +3 -1
  152. package/lib/runs-descendant-launches.ts +338 -0
  153. package/lib/runs-execution-evidence.ts +302 -0
  154. package/lib/runs-index.ts +345 -17
  155. package/lib/runs-native-launches.ts +270 -0
  156. package/lib/runs-outcomes.ts +377 -0
  157. package/lib/runs-ownership.ts +37 -4
  158. package/lib/runs-process-windows-mi.ts +113 -0
  159. package/lib/runs-process.ts +210 -34
  160. package/lib/runs-results.ts +601 -0
  161. package/lib/runs-retention.ts +134 -10
  162. package/lib/runs-session-exports.ts +406 -0
  163. package/lib/runs-snapshot.ts +143 -0
  164. package/lib/runs-start.ts +44 -2
  165. package/lib/runs-status.ts +252 -10
  166. package/lib/runs-supervision.ts +394 -0
  167. package/lib/runs-trace.ts +14 -3
  168. package/lib/runtime.ts +22 -3
  169. package/lib/session-evidence.ts +76 -4
  170. package/lib/state-readers.ts +16 -7
  171. package/lib/temp.ts +3 -56
  172. package/lib/tools-access.ts +13 -0
  173. package/lib/tools-inspect.ts +110 -51
  174. package/lib/tools-local.ts +37 -24
  175. package/lib/tools-message.ts +120 -37
  176. package/lib/tools-response.ts +273 -50
  177. package/lib/tools-spawn.ts +31 -10
  178. package/lib/trace-projection.ts +165 -42
  179. package/package.json +10 -6
  180. package/scripts/async-runner.mjs +272 -194
  181. package/scripts/conformance.mjs +14 -2
  182. package/skills/actors/SKILL.md +46 -2
  183. package/skills/actors/references/diagnostics.md +40 -0
  184. package/skills/actors/references/runs.md +55 -1
  185. package/banner.jpg +0 -0
  186. package/dist/lib/inspector-actions.d.ts +0 -16
  187. package/dist/lib/inspector-actions.js +0 -57
  188. package/lib/inspector-actions.ts +0 -79
@@ -4,17 +4,21 @@
4
4
  * Owns public Trace items; source journals and session parsing remain in their domains.
5
5
  */
6
6
 
7
+ import { createHash } from "node:crypto";
7
8
  import { existsSync, statSync } from "node:fs";
8
9
  import { join, relative } from "node:path";
9
10
 
10
11
  import * as ControlProjection from "./control-projection.ts";
11
12
  import * as ExecutionSessions from "./execution-sessions.ts";
12
13
  import * as Limits from "./limits.ts";
14
+ import { createRunRecipeEvidenceRedactor } from "./recipe-projection.ts";
13
15
  import {
14
- resolveArtifactManifest,
16
+ describeRunArtifactReferences,
17
+ observeRunArtifactReference,
15
18
  type RunArtifactDeclaration,
16
19
  } from "./runs-artifacts.ts";
17
20
  import * as RunsControls from "./runs-controls.ts";
21
+ import { isRunStopResultCapture } from "./runs-outcomes.ts";
18
22
  import type { RunControlRecord } from "./runs-controls.ts";
19
23
  import { tailFile } from "./runs-status.ts";
20
24
  import * as RunsTrace from "./runs-trace.ts";
@@ -35,11 +39,17 @@ export type TraceSource =
35
39
  export type TraceSourceFilter = TraceSource | "all";
36
40
 
37
41
  export const TRACE_ITEM_SEQUENCE = Symbol("trace-item-sequence");
42
+ export const TRACE_ITEM_CHRONOLOGY = Symbol("trace-item-chronology");
43
+ export type TraceChronologyReason = "history_incomplete" | "control_history_unproven" | "metadata_unavailable" | "metadata_private" | "metadata_too_large" | "metadata_ambiguous" | "timestamp_unavailable";
44
+ export type TraceChronology = Readonly<{ basis: "recorded_observations" } & (
45
+ { state: "available"; ordinal: number; total: number } | { state: "unavailable"; reason: TraceChronologyReason }
46
+ )>;
38
47
 
39
48
  export interface TraceItem {
40
49
  id: string;
41
50
  ts: string;
42
51
  [TRACE_ITEM_SEQUENCE]?: number;
52
+ [TRACE_ITEM_CHRONOLOGY]?: TraceChronology;
43
53
  source: TraceSource;
44
54
  kind: string;
45
55
  summary: string;
@@ -47,11 +57,12 @@ export interface TraceItem {
47
57
  detail: unknown;
48
58
  }
49
59
 
50
- interface OrderedTraceItem {
51
- item: TraceItem;
60
+ interface OrderedTraceKey {
61
+ item: Pick<TraceItem, "id" | "ts" | "source">;
52
62
  ordinal: number;
53
63
  sourceKey: string;
54
64
  }
65
+ interface OrderedTraceItem extends OrderedTraceKey { item: TraceItem }
55
66
 
56
67
  const LIFECYCLE_PREFIXES = ["run.", "command."];
57
68
  const SOURCE_LIMIT = 100;
@@ -84,7 +95,7 @@ function ordered(
84
95
  return { item, ordinal, sourceKey };
85
96
  }
86
97
 
87
- function compareTraceItems(left: OrderedTraceItem, right: OrderedTraceItem): number {
98
+ function compareTraceItems(left: OrderedTraceKey, right: OrderedTraceKey): number {
88
99
  const timestamp = right.item.ts.localeCompare(left.item.ts);
89
100
  if (timestamp) return timestamp;
90
101
  if (left.sourceKey === right.sourceKey) {
@@ -95,6 +106,52 @@ function compareTraceItems(left: OrderedTraceItem, right: OrderedTraceItem): num
95
106
  right.item.id.localeCompare(left.item.id);
96
107
  }
97
108
 
109
+ // Read-local metadata only; availability is not an original-history, issuer or work capability.
110
+ class TraceChronologyBuilder {
111
+ private entries: OrderedTraceKey[] = [];
112
+ private bytes = 2;
113
+ private reason?: TraceChronologyReason;
114
+ unavailable(reason: TraceChronologyReason): void { this.reason ??= reason; this.entries.length = 0; }
115
+ add(item: Pick<TraceItem, "id" | "ts" | "source">, sourceKey: string, ordinal: number): void {
116
+ if (this.reason) return;
117
+ if (typeof item.id !== "string" || !item.id) { this.unavailable("metadata_unavailable"); return; }
118
+ const projected = redact([item.id, item.ts]) as unknown[];
119
+ if (projected[0] !== item.id || projected[1] !== item.ts) { this.unavailable("metadata_private"); return; }
120
+ if (typeof item.ts !== "string" || item.ts.length !== 24 || !Number.isFinite(Date.parse(item.ts)) || new Date(item.ts).toISOString() !== item.ts) {
121
+ this.unavailable("timestamp_unavailable"); return;
122
+ }
123
+ const entry = { item: { id: item.id, ts: item.ts, source: item.source }, sourceKey, ordinal };
124
+ this.bytes += Buffer.byteLength(JSON.stringify(entry)) + Number(this.entries.length > 0);
125
+ if (this.bytes > Limits.TRACE_JOURNAL_MAX_BYTES) { this.unavailable("metadata_too_large"); return; }
126
+ this.entries.push(entry);
127
+ }
128
+ finish(items: OrderedTraceItem[]): void {
129
+ const ranks = new Map<string, { key: OrderedTraceKey; ordinal: number }>();
130
+ if (!this.reason) {
131
+ this.entries.sort(compareTraceItems);
132
+ for (const [index, key] of this.entries.entries()) {
133
+ if (ranks.has(key.item.id)) { this.unavailable("metadata_ambiguous"); break; }
134
+ ranks.set(key.item.id, { key, ordinal: this.entries.length - index });
135
+ }
136
+ }
137
+ if (!this.reason && items.some(({ item, sourceKey }) => {
138
+ const entry = ranks.get(item.id);
139
+ return !entry || entry.key.sourceKey !== sourceKey || entry.key.item.ts !== item.ts || entry.key.item.source !== item.source;
140
+ })) this.unavailable("metadata_unavailable");
141
+ // The legacy same-source ordinal/cross-source rank tie rule is not always a total order.
142
+ if (!this.reason) for (const source of ["all", ...Object.keys(SOURCE_RANK)]) {
143
+ let previous = Infinity;
144
+ const view = items.filter(({ item }) => source === "all" || item.source === source).sort(compareTraceItems);
145
+ if (view.some(({ item }) => { const rank = ranks.get(item.id)!.ordinal; const mismatch = rank >= previous; previous = rank; return mismatch; })) {
146
+ this.unavailable("metadata_ambiguous"); break;
147
+ }
148
+ }
149
+ for (const { item } of items) Object.defineProperty(item, TRACE_ITEM_CHRONOLOGY, { value: Object.freeze(this.reason
150
+ ? { state: "unavailable", basis: "recorded_observations", reason: this.reason }
151
+ : { state: "available", basis: "recorded_observations", ordinal: ranks.get(item.id)!.ordinal, total: ranks.size }) });
152
+ }
153
+ }
154
+
98
155
  function latestControlTimestamp(control: RunControlRecord): string {
99
156
  return (
100
157
  control.failed_at ??
@@ -136,9 +193,15 @@ function controlDiagnosticItems(
136
193
  }));
137
194
  }
138
195
 
139
- function traceEventItems(stateDir: string): OrderedTraceItem[] {
140
- const read = RunsTrace.readRunTraceJournal(stateDir);
196
+ function traceEventItems(read: RunsTrace.RunTraceJournalRead, recipe?: Record<string, unknown>,
197
+ projectEvidence: (value: unknown) => unknown = recipe ? createRunRecipeEvidenceRedactor(recipe) : redact,
198
+ chronology?: TraceChronologyBuilder): OrderedTraceItem[] {
141
199
  const summary = RunsTrace.summarizeRunTraceJournal(read);
200
+ if (!summary.history_complete) chronology?.unavailable("history_incomplete");
201
+ if (chronology) for (const { event, ordinal } of read.events) {
202
+ if (redact(event.kind) !== event.kind) chronology.unavailable("metadata_private");
203
+ chronology.add({ id: event.id, ts: event.ts, source: sourceForEvent(event) }, "trace", ordinal);
204
+ }
142
205
  const sequenceOffset = summary.dropped_events + Math.max(0, summary.compactions_total - 1);
143
206
  const retained = read.events.slice(-SOURCE_LIMIT);
144
207
  const projected = retained.map(({ event, ordinal }) => ordered({
@@ -146,9 +209,9 @@ function traceEventItems(stateDir: string): OrderedTraceItem[] {
146
209
  ts: event.ts,
147
210
  source: sourceForEvent(event),
148
211
  kind: event.kind,
149
- summary: event.summary ?? event.kind,
212
+ summary: projectEvidence(event.summary ?? event.kind) as string,
150
213
  ...(event.level ? { level: event.level } : {}),
151
- detail: redact({
214
+ detail: projectEvidence({
152
215
  ...(event.attention ? { attention: event.attention } : {}),
153
216
  ...(event.data !== undefined ? { data: event.data } : {}),
154
217
  }),
@@ -177,8 +240,24 @@ function traceEventItems(stateDir: string): OrderedTraceItem[] {
177
240
  return [...projected, ...diagnostics];
178
241
  }
179
242
 
180
- function controlItems(stateDir: string): OrderedTraceItem[] {
243
+ function controlItems(stateDir: string, chronology?: TraceChronologyBuilder): OrderedTraceItem[] {
181
244
  const read = RunsControls.readRunControlJournalFromStateDir(stateDir);
245
+ if (read.diagnostics.length) chronology?.unavailable("metadata_unavailable");
246
+ let terminal = 0;
247
+ if (chronology) for (const [index, raw] of read.records.entries()) {
248
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) { chronology.unavailable("metadata_unavailable"); continue; }
249
+ const record = raw as RunControlRecord;
250
+ if (![record.id, record.action, record.run_instance_id, record.queued_at].every(value => typeof value === "string") || !record.id || !record.run_instance_id ||
251
+ !["queued", "delivered", "claimed", "handled", "failed"].includes(record.status)) { chronology.unavailable("metadata_unavailable"); continue; }
252
+ if (record.status === "handled" || record.status === "failed") terminal++;
253
+ const metadata = { id: record.id, action: record.action, run_instance_id: record.run_instance_id, queued_at: record.queued_at, status: record.status,
254
+ ...Object.fromEntries(["failed_at", "handled_at", "claimed_at", "delivered_at"].flatMap(field =>
255
+ typeof (raw as Record<string, unknown>)[field] === "string" ? [[field, (raw as Record<string, unknown>)[field]]] : [])) };
256
+ const value = ControlProjection.projectRunControl(metadata);
257
+ if (value.id !== record.id || latestControlTimestamp(value) !== latestControlTimestamp(record)) chronology.unavailable("metadata_unavailable");
258
+ chronology.add({ id: `control:${value.id}`, ts: latestControlTimestamp(value), source: "control" }, "control", index + 1);
259
+ }
260
+ if (terminal >= Limits.RUN_CONTROL_TERMINAL_LIMIT) chronology?.unavailable("control_history_unproven");
182
261
  const start = Math.max(0, read.records.length - SOURCE_LIMIT);
183
262
  const projected = read.records.slice(start).map((control, index) => {
184
263
  const value = ControlProjection.projectRunControl(control as RunControlRecord);
@@ -200,14 +279,39 @@ function controlItems(stateDir: string): OrderedTraceItem[] {
200
279
  ];
201
280
  }
202
281
 
203
- function agentItems(stateDir: string): OrderedTraceItem[] {
204
- return ExecutionSessions.readExecutionTurns(stateDir)
205
- .slice(-SOURCE_LIMIT)
206
- .map((turn) => {
282
+ function agentIdentity(commandId: string, sessionFile: string, index: number,
283
+ admitted: SessionEvidence.SessionEvidenceTurn[typeof SessionEvidence.SESSION_TURN_IDENTITY]): string {
284
+ const namespace = redact([commandId, sessionFile]) as unknown[];
285
+ const identity = namespace[0] === commandId && namespace[1] === sessionFile ? admitted : undefined;
286
+ const basis = identity?.basis ?? "legacy";
287
+ const key = createHash("sha256").update(JSON.stringify([
288
+ "agent-turn-v1", ...namespace, identity ?? { basis, ordinal: index },
289
+ ])).digest("hex");
290
+ return `agent:${basis}:${key}`;
291
+ }
292
+
293
+ function agentItems(stateDir: string, chronology?: TraceChronologyBuilder): OrderedTraceItem[] {
294
+ const turns = ExecutionSessions.readExecutionTurns(stateDir);
295
+ const metadata = turns[ExecutionSessions.EXECUTION_CHRONOLOGY];
296
+ if (!metadata || metadata.state !== "available") chronology?.unavailable("metadata_unavailable");
297
+ else for (const entry of metadata.entries) chronology?.add({
298
+ id: agentIdentity(entry.commandId, entry.sessionFile, entry.ordinal, entry.identity), ts: entry.timestamp, source: "agent",
299
+ }, "agent", entry.ordinal);
300
+ return turns.slice(-SOURCE_LIMIT).map((turn) => {
207
301
  const text = turn.assistantText ?? turn.userText ?? "Agent turn";
208
- const summary = text.replaceAll(/\s+/g, " ").trim().slice(0, 160);
302
+ const compact = text.replaceAll(/\s+/g, " ").trim();
303
+ let summary = compact;
304
+ if (Buffer.from(compact).toString("utf8") !== compact) {
305
+ summary = "Agent turn [omitted: unknown] (malformed retained text)";
306
+ } else if (compact.length > 160) {
307
+ const last = compact.charCodeAt(159), next = compact.charCodeAt(160);
308
+ const end = last >= 0xd800 && last <= 0xdbff && next >= 0xdc00 && next <= 0xdfff ? 159 : 160;
309
+ // Only this normalized retained suffix supplies a count; upstream source loss stays unknown.
310
+ const omitted = Array.from(compact.slice(end)).length;
311
+ summary = `${compact.slice(0, end)}… [omitted: ${omitted} chars] from normalized retained turn; upstream [omitted: unknown]`;
312
+ }
209
313
  return ordered({
210
- id: `agent:${turn.commandId}:${turn.sessionFile}:${turn.index}`,
314
+ id: agentIdentity(turn.commandId, turn.sessionFile, turn.index, turn[SessionEvidence.SESSION_TURN_IDENTITY]),
211
315
  ts: turn.timestamp ?? new Date(0).toISOString(),
212
316
  source: "agent",
213
317
  kind: "agent.turn",
@@ -247,14 +351,15 @@ function resultItems(stateDir: string): OrderedTraceItem[] {
247
351
  }
248
352
  const stat = statSync(path);
249
353
  const failed = Number(read.value.code ?? 0) !== 0;
354
+ const captured = isRunStopResultCapture(read.value);
250
355
  return [ordered({
251
356
  id: "process:result",
252
357
  ts: typeof read.value.completedAt === "string"
253
358
  ? read.value.completedAt
254
359
  : stat.mtime.toISOString(),
255
360
  source: "process",
256
- kind: failed ? "process.failed" : "process.result",
257
- summary: failed ? "Run process failed" : "Run process completed",
361
+ kind: captured ? "process.result_captured" : failed ? "process.failed" : "process.result",
362
+ summary: captured ? "Workload result captured while stopping" : failed ? "Run process failed" : "Run process completed",
258
363
  ...(failed ? { level: "error" as const } : {}),
259
364
  detail: redact(read.value),
260
365
  }, "process:result", 1, 2), ...diagnosticItems(read.diagnostics).map(
@@ -265,27 +370,41 @@ function resultItems(stateDir: string): OrderedTraceItem[] {
265
370
  function artifactItems(
266
371
  stateDir: string,
267
372
  declarations: Record<string, RunArtifactDeclaration> | undefined,
373
+ chronology?: TraceChronologyBuilder,
268
374
  ): OrderedTraceItem[] {
269
- const manifest = resolveArtifactManifest(declarations);
270
- if (!manifest) return [];
271
- return Object.entries(manifest).slice(0, SOURCE_LIMIT).map(
272
- ([name, artifact], index) => ordered({
273
- id: `artifact:${name}`,
274
- ts: artifact.exists && existsSync(artifact.path)
275
- ? statSync(artifact.path).mtime.toISOString()
276
- : new Date(0).toISOString(),
375
+ const described = describeRunArtifactReferences(declarations, process.cwd());
376
+ if (described.state !== "available") { if (declarations) chronology?.unavailable("metadata_unavailable"); return []; }
377
+ if (described.references.length > SOURCE_LIMIT) chronology?.unavailable("metadata_unavailable");
378
+ return described.references.slice(0, SOURCE_LIMIT).map((reference, index) => {
379
+ const artifact = observeRunArtifactReference(reference);
380
+ const exists = artifact.availability === "available";
381
+ if (!artifact.modified_at) chronology?.unavailable("timestamp_unavailable");
382
+ return ordered({
383
+ id: `artifact:${artifact.name}`,
384
+ ts: artifact.modified_at ?? new Date(0).toISOString(),
277
385
  source: "artifact",
278
- kind: artifact.exists ? "artifact.ready" : "artifact.missing",
279
- summary: `${name}: ${artifact.exists ? "ready" : "missing"}`,
280
- ...(artifact.required && !artifact.exists ? { level: "warning" as const } : {}),
386
+ kind: exists ? "artifact.ready" : artifact.availability === "missing" ? "artifact.missing" : "artifact.unavailable",
387
+ summary: redact(`${artifact.name}: ${exists ? "ready" : artifact.availability}`) as string,
388
+ ...(artifact.required && !exists ? { level: "warning" as const } : {}),
281
389
  detail: redact({
282
- exists: artifact.exists,
390
+ ...(artifact.availability !== "unavailable" ? { exists } : {}),
391
+ availability: artifact.availability,
283
392
  path: relative(stateDir, artifact.path).replaceAll("\\", "/"),
284
393
  required: artifact.required,
285
- ...(artifact.size !== undefined ? { size: artifact.size } : {}),
394
+ ...(artifact.bytes_total !== undefined ? { size: artifact.bytes_total } : {}),
395
+ ...(artifact.reason ? { reason: artifact.reason } : {}),
286
396
  }),
287
- }, "artifact", index + 1, index),
288
- );
397
+ }, "artifact", index + 1, index);
398
+ });
399
+ }
400
+
401
+ /** Data-only canonical-journal projection; captured Recipe context supplies privacy, never lifecycle/work authority. */
402
+ export function projectRunTraceJournal(
403
+ read: RunsTrace.RunTraceJournalRead,
404
+ options: { limit?: number; recipe?: Record<string, unknown>; redactEvidence?: (value: unknown) => unknown } = {},
405
+ ): TraceItem[] {
406
+ return traceEventItems(read, options.recipe, options.redactEvidence).sort(compareTraceItems)
407
+ .slice(0, Math.max(1, Math.min(options.limit ?? 100, 200))).map(({ item }) => item);
289
408
  }
290
409
 
291
410
  export function projectRunTrace(
@@ -297,16 +416,20 @@ export function projectRunTrace(
297
416
  } = {},
298
417
  ): TraceItem[] {
299
418
  const source = options.source ?? "all";
300
- const items = [
301
- ...traceEventItems(stateDir),
302
- ...controlItems(stateDir),
303
- ...agentItems(stateDir),
304
- ...processItems(stateDir),
305
- ...resultItems(stateDir),
306
- ...artifactItems(stateDir, options.artifacts),
307
- ]
308
- .filter(({ item }) => source === "all" || item.source === source)
309
- .sort(compareTraceItems);
419
+ const chronology = new TraceChronologyBuilder();
420
+ const entries = [
421
+ ...traceEventItems(RunsTrace.readRunTraceJournal(stateDir), undefined, undefined, chronology),
422
+ ...controlItems(stateDir, chronology),
423
+ ...agentItems(stateDir, chronology),
424
+ ];
425
+ const snapshots = [...processItems(stateDir), ...resultItems(stateDir), ...artifactItems(stateDir, options.artifacts, chronology)];
426
+ for (const entry of snapshots) {
427
+ if (entry.item.kind === "state.read_error") chronology.unavailable("metadata_unavailable");
428
+ chronology.add(entry.item, entry.sourceKey, entry.ordinal);
429
+ }
430
+ const all = [...entries, ...snapshots];
431
+ chronology.finish(all);
432
+ const items = all.filter(({ item }) => source === "all" || item.source === source).sort(compareTraceItems);
310
433
  return items
311
434
  .slice(0, Math.max(1, Math.min(options.limit ?? 100, 200)))
312
435
  .map(({ item }) => item);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llblab/pi-actors",
3
- "version": "0.55.0",
3
+ "version": "0.56.1",
4
4
  "private": false,
5
5
  "description": "Local Actor Kernel for Pi",
6
6
  "keywords": [
@@ -27,11 +27,13 @@
27
27
  "scripts": {
28
28
  "check": "node --experimental-strip-types -e \"await import('./index.ts'); console.log('pi-actors: extension import ok')\"",
29
29
  "conformance": "node scripts/conformance.mjs",
30
- "test": "node --experimental-strip-types --test --test-concurrency=1 tests/*.test.ts",
31
- "test:preservation": "node --experimental-strip-types --test --test-concurrency=1 tests/async-runs.test.ts tests/observability.test.ts tests/session-evidence.test.ts tests/inspector-security.test.ts tests/inspector-overlay.test.ts tests/review-control.test.ts",
30
+ "test": "node --import ./tests/test-environment.mjs --experimental-strip-types --test --test-concurrency=1 tests/*.test.ts",
31
+ "test:shard": "node .github/scripts/test-shards.mjs",
32
+ "test:preservation": "node --import ./tests/test-environment.mjs --experimental-strip-types --test --test-concurrency=1 tests/async-runs.test.ts tests/runs-retention.test.ts tests/observability.test.ts tests/session-evidence.test.ts tests/inspector.test.ts tests/inspector-overlay.test.ts tests/review-control.test.ts",
32
33
  "pack:dry": "npm pack --dry-run",
33
34
  "recipes:qa": "node skills/actors/scripts/validate-recipe.mjs skills --skills --qa --summary",
34
- "validate": "npx tsc --noEmit && npm run recipes:qa && npm run build:check && npm run check && npm test && npm run conformance && npm run pack:dry",
35
+ "validate:preflight": "npx tsc --noEmit && npm run recipes:qa && npm run build:check && npm run check",
36
+ "validate": "npm run validate:preflight && npm test && npm run pack:dry",
35
37
  "audit:dependencies": "npm audit --audit-level=high --omit=peer",
36
38
  "build": "node scripts/build-dist.mjs",
37
39
  "build:check": "node scripts/build-dist.mjs --check",
@@ -49,8 +51,7 @@
49
51
  "BACKLOG.md",
50
52
  "CHANGELOG.md",
51
53
  "docs",
52
- "fixtures",
53
- "banner.jpg"
54
+ "fixtures"
54
55
  ],
55
56
  "pi": {
56
57
  "extensions": [
@@ -68,5 +69,8 @@
68
69
  "devDependencies": {
69
70
  "@types/node": "latest",
70
71
  "typescript": "latest"
72
+ },
73
+ "dependencies": {
74
+ "yaml": "2.9.0"
71
75
  }
72
76
  }