@llblab/pi-actors 0.43.1 → 0.44.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 +6 -6
- package/BACKLOG.md +1 -189
- package/CHANGELOG.md +412 -402
- package/README.md +5 -5
- package/dist/lib/async-runs.js +8 -30
- package/dist/lib/file-state.d.ts +3 -1
- package/dist/lib/file-state.js +25 -7
- package/dist/lib/inspector-overlay.js +46 -30
- package/dist/lib/limits.d.ts +10 -0
- package/dist/lib/limits.js +10 -0
- package/dist/lib/observability.d.ts +4 -2
- package/dist/lib/observability.js +43 -36
- package/dist/lib/run-evidence-policy.d.ts +95 -0
- package/dist/lib/run-evidence-policy.js +177 -0
- package/dist/lib/run-ui-runtime.js +2 -0
- package/dist/lib/runs-controls.d.ts +7 -5
- package/dist/lib/runs-controls.js +186 -49
- package/dist/lib/runs-retention.js +27 -14
- package/dist/lib/runs-trace.d.ts +25 -1
- package/dist/lib/runs-trace.js +410 -21
- package/dist/lib/runtime-triage.js +6 -22
- package/dist/lib/tools-inspect.js +48 -14
- package/dist/lib/trace-projection.js +90 -44
- package/dist/scripts/conformance.mjs +5 -0
- package/dist/scripts/locker.mjs +31 -74
- package/dist/scripts/music-player.mjs +41 -129
- package/dist/scripts/release-gates.mjs +26 -4
- package/dist/skills/actors/SKILL.md +7 -6
- package/dist/skills/swarm/SKILL.md +2 -2
- package/docs/README.md +0 -1
- package/docs/actor-inspector.md +3 -2
- package/docs/async-runs.md +10 -8
- package/docs/recipe-library.md +2 -2
- package/lib/async-runs.ts +8 -45
- package/lib/file-state.ts +28 -7
- package/lib/inspector-overlay.ts +29 -17
- package/lib/limits.ts +10 -0
- package/lib/observability.ts +55 -57
- package/lib/run-evidence-policy.ts +242 -0
- package/lib/run-ui-runtime.ts +2 -0
- package/lib/runs-controls.ts +177 -108
- package/lib/runs-retention.ts +28 -20
- package/lib/runs-trace.ts +496 -21
- package/lib/runtime-triage.ts +11 -25
- package/lib/tools-inspect.ts +45 -15
- package/lib/trace-projection.ts +137 -75
- package/package.json +1 -1
- package/scripts/conformance.mjs +5 -0
- package/scripts/locker.mjs +31 -74
- package/scripts/music-player.mjs +41 -129
- package/scripts/release-gates.mjs +26 -4
- package/skills/actors/SKILL.md +7 -6
- package/skills/swarm/SKILL.md +2 -2
- package/dist/lib/runtime-notifier.d.ts +0 -48
- package/dist/lib/runtime-notifier.js +0 -138
- package/docs/0.43-baseline.md +0 -39
- package/lib/runtime-notifier.ts +0 -211
package/lib/runtime-notifier.ts
DELETED
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Runtime wake notifications for actor state.
|
|
3
|
-
* Zones: advisory wake layer, file-backed runtime state, cross-platform notification boundary
|
|
4
|
-
* Owns best-effort live wake signals while durable Run state remains canonical.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { randomUUID } from "node:crypto";
|
|
8
|
-
import {
|
|
9
|
-
appendFileSync,
|
|
10
|
-
existsSync,
|
|
11
|
-
mkdirSync,
|
|
12
|
-
readFileSync,
|
|
13
|
-
statSync,
|
|
14
|
-
watch,
|
|
15
|
-
type FSWatcher,
|
|
16
|
-
} from "node:fs";
|
|
17
|
-
import { basename, dirname, join } from "node:path";
|
|
18
|
-
|
|
19
|
-
import { readJsonlFileResilient } from "./state-readers.ts";
|
|
20
|
-
|
|
21
|
-
export interface RuntimeWakeEvent {
|
|
22
|
-
actor: string;
|
|
23
|
-
id: string;
|
|
24
|
-
metadata?: Record<string, unknown>;
|
|
25
|
-
reason: string;
|
|
26
|
-
state_dir: string;
|
|
27
|
-
ts: string;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface RuntimeNotifierSubscription {
|
|
31
|
-
close(): void;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export type RuntimeReconcileReason = "initial" | "poll" | "wake";
|
|
35
|
-
|
|
36
|
-
export interface RuntimeReconcileEvent {
|
|
37
|
-
actor: string;
|
|
38
|
-
reason: RuntimeReconcileReason;
|
|
39
|
-
state_dir: string;
|
|
40
|
-
ts: string;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export interface RuntimeNotifierSubscribeOptions {
|
|
44
|
-
onReconcile?: (event: RuntimeReconcileEvent) => void;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export interface FileRuntimeNotifierOptions {
|
|
48
|
-
pollIntervalMs?: number;
|
|
49
|
-
replay?: boolean;
|
|
50
|
-
watch?: boolean;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export interface RuntimeNotifier {
|
|
54
|
-
notify(event: { actor: string; metadata?: Record<string, unknown>; reason: string }): RuntimeWakeEvent;
|
|
55
|
-
subscribe(
|
|
56
|
-
actor: string,
|
|
57
|
-
onWake: (event: RuntimeWakeEvent) => void,
|
|
58
|
-
options?: RuntimeNotifierSubscribeOptions,
|
|
59
|
-
): RuntimeNotifierSubscription;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const DEFAULT_POLL_INTERVAL_MS = 1000;
|
|
63
|
-
|
|
64
|
-
export function runtimeWakeFile(stateDir: string): string {
|
|
65
|
-
return join(stateDir, "wake.jsonl");
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function normalizeWakeEvent(
|
|
69
|
-
stateDir: string,
|
|
70
|
-
event: { actor: string; metadata?: Record<string, unknown>; reason: string },
|
|
71
|
-
): RuntimeWakeEvent {
|
|
72
|
-
const actor = event.actor.trim();
|
|
73
|
-
const reason = event.reason.trim();
|
|
74
|
-
if (!actor) throw new Error("Runtime wake event requires actor.");
|
|
75
|
-
if (!reason) throw new Error("Runtime wake event requires reason.");
|
|
76
|
-
return {
|
|
77
|
-
actor,
|
|
78
|
-
id: randomUUID(),
|
|
79
|
-
...(event.metadata ? { metadata: event.metadata } : {}),
|
|
80
|
-
reason,
|
|
81
|
-
state_dir: stateDir,
|
|
82
|
-
ts: new Date().toISOString(),
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export function notifyRuntimeWake(
|
|
87
|
-
stateDir: string,
|
|
88
|
-
event: { actor: string; metadata?: Record<string, unknown>; reason: string },
|
|
89
|
-
): RuntimeWakeEvent {
|
|
90
|
-
const normalized = normalizeWakeEvent(stateDir, event);
|
|
91
|
-
const file = runtimeWakeFile(stateDir);
|
|
92
|
-
mkdirSync(dirname(file), { recursive: true });
|
|
93
|
-
appendFileSync(file, `${JSON.stringify(normalized)}\n`, "utf8");
|
|
94
|
-
return normalized;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export function parseRuntimeWakeEventLine(
|
|
98
|
-
line: string,
|
|
99
|
-
): RuntimeWakeEvent | undefined {
|
|
100
|
-
try {
|
|
101
|
-
const record = JSON.parse(line) as Record<string, unknown>;
|
|
102
|
-
if (
|
|
103
|
-
typeof record.actor !== "string" ||
|
|
104
|
-
typeof record.id !== "string" ||
|
|
105
|
-
typeof record.reason !== "string" ||
|
|
106
|
-
typeof record.state_dir !== "string" ||
|
|
107
|
-
typeof record.ts !== "string"
|
|
108
|
-
) {
|
|
109
|
-
return undefined;
|
|
110
|
-
}
|
|
111
|
-
return {
|
|
112
|
-
actor: record.actor,
|
|
113
|
-
id: record.id,
|
|
114
|
-
...(record.metadata &&
|
|
115
|
-
typeof record.metadata === "object" &&
|
|
116
|
-
!Array.isArray(record.metadata)
|
|
117
|
-
? { metadata: record.metadata as Record<string, unknown> }
|
|
118
|
-
: {}),
|
|
119
|
-
reason: record.reason,
|
|
120
|
-
state_dir: record.state_dir,
|
|
121
|
-
ts: record.ts,
|
|
122
|
-
};
|
|
123
|
-
} catch {
|
|
124
|
-
return undefined;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
export function readRuntimeWakeEvents(stateDir: string): RuntimeWakeEvent[] {
|
|
129
|
-
return readJsonlFileResilient<Record<string, unknown>>(runtimeWakeFile(stateDir))
|
|
130
|
-
.records.map((record) => parseRuntimeWakeEventLine(JSON.stringify(record)))
|
|
131
|
-
.filter((event): event is RuntimeWakeEvent => Boolean(event));
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
export function createFileRuntimeNotifier(
|
|
135
|
-
stateDir: string,
|
|
136
|
-
options: FileRuntimeNotifierOptions = {},
|
|
137
|
-
): RuntimeNotifier {
|
|
138
|
-
const file = runtimeWakeFile(stateDir);
|
|
139
|
-
const pollIntervalMs = Math.max(
|
|
140
|
-
25,
|
|
141
|
-
Number(options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS),
|
|
142
|
-
);
|
|
143
|
-
|
|
144
|
-
return {
|
|
145
|
-
notify: (event) => notifyRuntimeWake(stateDir, event),
|
|
146
|
-
subscribe: (actor, onWake, subscribeOptions = {}) => {
|
|
147
|
-
mkdirSync(dirname(file), { recursive: true });
|
|
148
|
-
let position =
|
|
149
|
-
options.replay || !existsSync(file) ? 0 : statSync(file).size;
|
|
150
|
-
let pending = "";
|
|
151
|
-
let closed = false;
|
|
152
|
-
const reconcile = (reason: RuntimeReconcileReason): void => {
|
|
153
|
-
if (closed) return;
|
|
154
|
-
subscribeOptions.onReconcile?.({
|
|
155
|
-
actor,
|
|
156
|
-
reason,
|
|
157
|
-
state_dir: stateDir,
|
|
158
|
-
ts: new Date().toISOString(),
|
|
159
|
-
});
|
|
160
|
-
};
|
|
161
|
-
const drain = (): void => {
|
|
162
|
-
if (closed || !existsSync(file)) return;
|
|
163
|
-
const buffer = readFileSync(file);
|
|
164
|
-
if (position > buffer.length) {
|
|
165
|
-
position = 0;
|
|
166
|
-
pending = "";
|
|
167
|
-
}
|
|
168
|
-
const chunk = pending + buffer.subarray(position).toString("utf8");
|
|
169
|
-
position = buffer.length;
|
|
170
|
-
const lines = chunk.split("\n");
|
|
171
|
-
pending = chunk.endsWith("\n") ? "" : (lines.pop() ?? "");
|
|
172
|
-
for (const line of lines) {
|
|
173
|
-
if (!line.trim()) continue;
|
|
174
|
-
const event = parseRuntimeWakeEventLine(line);
|
|
175
|
-
if (event && event.actor === actor) {
|
|
176
|
-
onWake(event);
|
|
177
|
-
reconcile("wake");
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
let watcher: FSWatcher | undefined;
|
|
183
|
-
if (options.watch !== false) {
|
|
184
|
-
try {
|
|
185
|
-
watcher = watch(
|
|
186
|
-
dirname(file),
|
|
187
|
-
{ persistent: false },
|
|
188
|
-
(_eventType, changedFile) => {
|
|
189
|
-
if (!changedFile || String(changedFile) === basename(file)) drain();
|
|
190
|
-
},
|
|
191
|
-
);
|
|
192
|
-
} catch {
|
|
193
|
-
// fs.watch availability varies by platform/filesystem; polling below is the fallback.
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
reconcile("initial");
|
|
197
|
-
const timer = setInterval(() => {
|
|
198
|
-
drain();
|
|
199
|
-
reconcile("poll");
|
|
200
|
-
}, pollIntervalMs);
|
|
201
|
-
timer.unref?.();
|
|
202
|
-
return {
|
|
203
|
-
close: () => {
|
|
204
|
-
closed = true;
|
|
205
|
-
clearInterval(timer);
|
|
206
|
-
watcher?.close();
|
|
207
|
-
},
|
|
208
|
-
};
|
|
209
|
-
},
|
|
210
|
-
};
|
|
211
|
-
}
|