@plurnk/plurnk-service 0.60.0 → 0.62.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/.env.example +54 -16
- package/PLURNK_PERSONALITY.md +53 -0
- package/SPEC.md +22 -18
- package/dist/core/Dispatcher.d.ts +61 -0
- package/dist/core/Dispatcher.d.ts.map +1 -0
- package/dist/core/Dispatcher.js +811 -0
- package/dist/core/Dispatcher.js.map +1 -0
- package/dist/core/Engine.d.ts +9 -46
- package/dist/core/Engine.d.ts.map +1 -1
- package/dist/core/Engine.js +209 -1513
- package/dist/core/Engine.js.map +1 -1
- package/dist/core/Engine.sql +7 -2
- package/dist/core/ExecutorRegistry.d.ts +2 -1
- package/dist/core/ExecutorRegistry.d.ts.map +1 -1
- package/dist/core/ExecutorRegistry.js +16 -3
- package/dist/core/ExecutorRegistry.js.map +1 -1
- package/dist/core/PacketBuilder.d.ts +64 -0
- package/dist/core/PacketBuilder.d.ts.map +1 -0
- package/dist/core/PacketBuilder.js +380 -0
- package/dist/core/PacketBuilder.js.map +1 -0
- package/dist/core/ProposalLifecycle.d.ts +56 -0
- package/dist/core/ProposalLifecycle.d.ts.map +1 -0
- package/dist/core/ProposalLifecycle.js +197 -0
- package/dist/core/ProposalLifecycle.js.map +1 -0
- package/dist/core/SchemeRegistry.d.ts +2 -0
- package/dist/core/SchemeRegistry.d.ts.map +1 -1
- package/dist/core/SchemeRegistry.js +20 -15
- package/dist/core/SchemeRegistry.js.map +1 -1
- package/dist/core/StrikeRail.d.ts +27 -0
- package/dist/core/StrikeRail.d.ts.map +1 -0
- package/dist/core/StrikeRail.js +147 -0
- package/dist/core/StrikeRail.js.map +1 -0
- package/dist/core/TelemetryChannel.d.ts +37 -0
- package/dist/core/TelemetryChannel.d.ts.map +1 -0
- package/dist/core/TelemetryChannel.js +77 -0
- package/dist/core/TelemetryChannel.js.map +1 -0
- package/dist/core/fork.d.ts +1 -0
- package/dist/core/fork.d.ts.map +1 -1
- package/dist/core/fork.js +17 -4
- package/dist/core/fork.js.map +1 -1
- package/dist/core/fork.sql +6 -0
- package/dist/core/packet-wire.js +1 -1
- package/dist/core/packet-wire.js.map +1 -1
- package/dist/core/plurnk-uri.d.ts +2 -0
- package/dist/core/plurnk-uri.d.ts.map +1 -1
- package/dist/core/plurnk-uri.js +11 -0
- package/dist/core/plurnk-uri.js.map +1 -1
- package/dist/core/run-cap.js +1 -1
- package/dist/core/run-cap.js.map +1 -1
- package/dist/digest/Digest.d.ts +14 -0
- package/dist/digest/Digest.d.ts.map +1 -0
- package/dist/digest/Digest.js +321 -0
- package/dist/digest/Digest.js.map +1 -0
- package/dist/digest/digest.sql +55 -0
- package/dist/schemes/Exec.d.ts.map +1 -1
- package/dist/schemes/Exec.js +22 -23
- package/dist/schemes/Exec.js.map +1 -1
- package/dist/schemes/File.d.ts.map +1 -1
- package/dist/schemes/File.js +7 -4
- package/dist/schemes/File.js.map +1 -1
- package/dist/schemes/Log.d.ts.map +1 -1
- package/dist/schemes/Log.js +5 -1
- package/dist/schemes/Log.js.map +1 -1
- package/dist/schemes/Run.js +3 -3
- package/dist/schemes/Run.js.map +1 -1
- package/dist/server/ClientConnection.d.ts.map +1 -1
- package/dist/server/ClientConnection.js +48 -2
- package/dist/server/ClientConnection.js.map +1 -1
- package/dist/server/Daemon.d.ts +1 -1
- package/dist/server/Daemon.d.ts.map +1 -1
- package/dist/server/Daemon.js +5 -1
- package/dist/server/Daemon.js.map +1 -1
- package/dist/server/methods/auth.d.ts +6 -0
- package/dist/server/methods/auth.d.ts.map +1 -0
- package/dist/server/methods/auth.js +45 -0
- package/dist/server/methods/auth.js.map +1 -0
- package/dist/server/methods/mcp_install.d.ts +6 -0
- package/dist/server/methods/mcp_install.d.ts.map +1 -0
- package/dist/server/methods/mcp_install.js +66 -0
- package/dist/server/methods/mcp_install.js.map +1 -0
- package/dist/server/methods/op_copy.d.ts.map +1 -1
- package/dist/server/methods/op_copy.js +1 -0
- package/dist/server/methods/op_copy.js.map +1 -1
- package/dist/server/methods/op_dispatch.d.ts.map +1 -1
- package/dist/server/methods/op_dispatch.js +1 -0
- package/dist/server/methods/op_dispatch.js.map +1 -1
- package/dist/server/methods/op_edit.d.ts.map +1 -1
- package/dist/server/methods/op_edit.js +1 -0
- package/dist/server/methods/op_edit.js.map +1 -1
- package/dist/server/methods/op_exec.d.ts.map +1 -1
- package/dist/server/methods/op_exec.js +1 -0
- package/dist/server/methods/op_exec.js.map +1 -1
- package/dist/server/methods/op_move.d.ts.map +1 -1
- package/dist/server/methods/op_move.js +1 -0
- package/dist/server/methods/op_move.js.map +1 -1
- package/dist/server/yolo.js +1 -1
- package/dist/server/yolo.js.map +1 -1
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +31 -2
- package/dist/service.js.map +1 -1
- package/docs/run.md +7 -3
- package/migrations/0000-00-00.01_schema.sql +3 -3
- package/package.json +22 -17
- package/requirements.md +2 -1
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { schemeNameOf } from "./plurnk-uri.js";
|
|
2
|
+
// Resolution timeout — proposed entries auto-cancel if nothing arrives
|
|
3
|
+
// within this window. SPEC.md §engine-rails (proposal lifecycle) + §methods (loop.resolve).
|
|
4
|
+
const PROPOSAL_TIMEOUT_DEFAULT_MS = 300000;
|
|
5
|
+
const readProposalTimeoutMs = () => {
|
|
6
|
+
const raw = process.env.PLURNK_PROPOSAL_TIMEOUT_MS;
|
|
7
|
+
if (raw === undefined || raw.length === 0)
|
|
8
|
+
return PROPOSAL_TIMEOUT_DEFAULT_MS;
|
|
9
|
+
const n = Number(raw);
|
|
10
|
+
if (!Number.isFinite(n) || n <= 0)
|
|
11
|
+
return PROPOSAL_TIMEOUT_DEFAULT_MS;
|
|
12
|
+
return n;
|
|
13
|
+
};
|
|
14
|
+
// The proposal lifecycle (SPEC.md §engine-rails + §methods loop.resolve): a
|
|
15
|
+
// side-effecting op that returns 202 pauses in dispatch until a resolution
|
|
16
|
+
// arrives — from the loop/resolve RPC, the in-tree YOLO listener, or the
|
|
17
|
+
// timeout — then the scheme's applyResolution hook applies the accept.
|
|
18
|
+
export default class ProposalLifecycle {
|
|
19
|
+
#db;
|
|
20
|
+
#schemes;
|
|
21
|
+
#telemetry;
|
|
22
|
+
#streamEventNotify;
|
|
23
|
+
#wakeRunNotify;
|
|
24
|
+
#tokenize;
|
|
25
|
+
// Boot-discovered runtime executors, late-injected on Engine — thunked.
|
|
26
|
+
#executors;
|
|
27
|
+
// Per-loop abort signal, owned by Engine.runLoop — thunked.
|
|
28
|
+
#loopSignal;
|
|
29
|
+
// Proposal lifecycle: pending dispatch pauses waiting for resolution.
|
|
30
|
+
// Dispatch awaits the promise when a scheme returns status 202;
|
|
31
|
+
// Engine.resolveProposal feeds the resolution back in. Map is per-log-
|
|
32
|
+
// entry-id; entries clear on resolution. SPEC.md §engine-rails + §methods (loop.resolve).
|
|
33
|
+
#pending = new Map();
|
|
34
|
+
// External observers of proposal lifecycle events. Daemon subscribes
|
|
35
|
+
// here to push `loop/proposal` notifications when an entry enters
|
|
36
|
+
// pending state. YOLO listener (Phase E.3) subscribes here too. Lean
|
|
37
|
+
// event emitter — no priority, no veto chain at this layer; filter
|
|
38
|
+
// chains come later if a real consumer needs them.
|
|
39
|
+
#listeners = [];
|
|
40
|
+
constructor({ db, schemes, telemetry, streamEventNotify, wakeRunNotify, tokenize, executors, loopSignal }) {
|
|
41
|
+
this.#db = db;
|
|
42
|
+
this.#schemes = schemes;
|
|
43
|
+
this.#telemetry = telemetry;
|
|
44
|
+
this.#streamEventNotify = streamEventNotify;
|
|
45
|
+
this.#wakeRunNotify = wakeRunNotify;
|
|
46
|
+
this.#tokenize = tokenize;
|
|
47
|
+
this.#executors = executors;
|
|
48
|
+
this.#loopSignal = loopSignal;
|
|
49
|
+
}
|
|
50
|
+
// External API to feed a resolution into a pending proposal. Called by
|
|
51
|
+
// the loop/resolve RPC handler (Phase E.2), the in-tree YOLO listener
|
|
52
|
+
// (Phase E.3), or the timeout watcher. Throws when the logEntryId has no
|
|
53
|
+
// pending waiter — duplicate resolutions, IDs for non-proposed entries,
|
|
54
|
+
// or entries already-resolved are caller errors.
|
|
55
|
+
resolve(logEntryId, resolution) {
|
|
56
|
+
const waiter = this.#pending.get(logEntryId);
|
|
57
|
+
if (waiter === undefined) {
|
|
58
|
+
throw new Error(`Engine.resolveProposal: no pending proposal for log_entry ${logEntryId}`);
|
|
59
|
+
}
|
|
60
|
+
clearTimeout(waiter.timeoutHandle);
|
|
61
|
+
this.#pending.delete(logEntryId);
|
|
62
|
+
waiter.resolve(resolution);
|
|
63
|
+
}
|
|
64
|
+
// Snapshot of pending proposals (for diagnostic / RPC listings). Returns
|
|
65
|
+
// the log entry IDs currently awaiting resolution.
|
|
66
|
+
pendingIds() {
|
|
67
|
+
return [...this.#pending.keys()];
|
|
68
|
+
}
|
|
69
|
+
// Subscribe to proposal-pending events. Daemon registers a listener
|
|
70
|
+
// that broadcasts the loop/proposal WS notification; YOLO listener
|
|
71
|
+
// (Phase E.3) registers one that auto-resolves. Listeners fire BEFORE
|
|
72
|
+
// dispatch awaits resolution, so synchronous (or fast-async) handlers
|
|
73
|
+
// can resolve inline.
|
|
74
|
+
onPending(listener) {
|
|
75
|
+
this.#listeners.push(listener);
|
|
76
|
+
}
|
|
77
|
+
notifyPending(event) {
|
|
78
|
+
for (const listener of this.#listeners) {
|
|
79
|
+
try {
|
|
80
|
+
listener(event);
|
|
81
|
+
}
|
|
82
|
+
catch (_) { /* listener errors don't break dispatch */ }
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
awaitResolution(logEntryId) {
|
|
86
|
+
const timeoutMs = readProposalTimeoutMs();
|
|
87
|
+
return new Promise((resolve) => {
|
|
88
|
+
const timeoutHandle = setTimeout(() => {
|
|
89
|
+
// Timeout: synthesize a cancel resolution and feed it back
|
|
90
|
+
// through the same path as any other resolution. State
|
|
91
|
+
// transitions to cancelled with outcome='timeout'.
|
|
92
|
+
if (this.#pending.has(logEntryId)) {
|
|
93
|
+
this.#pending.delete(logEntryId);
|
|
94
|
+
resolve({ decision: "cancel", outcome: "timeout" }); // §proposal-timeout-cancels
|
|
95
|
+
}
|
|
96
|
+
}, timeoutMs);
|
|
97
|
+
this.#pending.set(logEntryId, { resolve, timeoutHandle });
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
// On accept, run the scheme's applyResolution — File writes disk, Exec spawns. §proposal-accept-applies
|
|
101
|
+
async runApply(statement, originalResult, resolution, ids) {
|
|
102
|
+
const { sessionId, runId, loopId, turnId } = ids;
|
|
103
|
+
if (resolution.decision !== "accept")
|
|
104
|
+
return resolution;
|
|
105
|
+
// EXEC routes to the exec scheme regardless of target (cwd, not
|
|
106
|
+
// a scheme address). All other ops resolve their handler from
|
|
107
|
+
// statement.target's scheme.
|
|
108
|
+
// COPY/MOVE write the DEST (statement.body), not the source (target): the
|
|
109
|
+
// accept must reach the dest scheme's applyResolution (File writes disk).
|
|
110
|
+
const schemeName = statement.op === "EXEC" ? "exec"
|
|
111
|
+
: (statement.op === "COPY" || statement.op === "MOVE") ? schemeNameOf(statement.body)
|
|
112
|
+
: schemeNameOf(statement.target);
|
|
113
|
+
if (schemeName === null)
|
|
114
|
+
return resolution;
|
|
115
|
+
const handler = this.#schemes.get(schemeName);
|
|
116
|
+
if (handler === undefined || typeof handler.applyResolution !== "function")
|
|
117
|
+
return resolution;
|
|
118
|
+
try {
|
|
119
|
+
// Build a ctx for the scheme's applyResolution. The proposal
|
|
120
|
+
// was raised inside a specific (session, run, loop, turn);
|
|
121
|
+
// the scheme uses ctx to write the entry that makes the
|
|
122
|
+
// operation's artifact visible in the next packet's index.
|
|
123
|
+
const applyCtx = {
|
|
124
|
+
db: this.#db, sessionId, runId, loopId, turnId,
|
|
125
|
+
writer: "model", signal: this.#loopSignal(loopId),
|
|
126
|
+
streamEventNotify: this.#streamEventNotify,
|
|
127
|
+
wakeRunNotify: this.#wakeRunNotify,
|
|
128
|
+
tokenize: this.#tokenize,
|
|
129
|
+
pushTelemetry: (event) => this.#telemetry.push(sessionId, loopId, event),
|
|
130
|
+
executors: this.#executors(),
|
|
131
|
+
};
|
|
132
|
+
const applyResult = await handler.applyResolution({
|
|
133
|
+
attrs: (originalResult.attrs ?? {}),
|
|
134
|
+
body: resolution.body,
|
|
135
|
+
}, applyCtx);
|
|
136
|
+
if (applyResult.status >= 400) {
|
|
137
|
+
return {
|
|
138
|
+
decision: "reject",
|
|
139
|
+
outcome: applyResult.outcome ?? "apply_failed",
|
|
140
|
+
body: applyResult.body,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
// Propagate applyResolution.outcome onto the accepted resolution
|
|
144
|
+
// (operational metadata, e.g. exec's "exit_N") AND its body — an
|
|
145
|
+
// inline (read/pure) run returns its output as the body, which has
|
|
146
|
+
// to reach the model-facing result this turn, not just stream to
|
|
147
|
+
// the entry. Host accepts carry no body (fire-and-forget).
|
|
148
|
+
const withOutcome = applyResult.outcome !== undefined && resolution.outcome === undefined
|
|
149
|
+
? { ...resolution, outcome: applyResult.outcome }
|
|
150
|
+
: resolution;
|
|
151
|
+
return applyResult.body === undefined ? withOutcome : { ...withOutcome, body: applyResult.body };
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
return {
|
|
155
|
+
decision: "reject",
|
|
156
|
+
outcome: "apply_threw",
|
|
157
|
+
body: err instanceof Error ? err.message : String(err),
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
async applyResolution(logEntryId, resolution) {
|
|
162
|
+
// Map decision → terminal state + HTTP-aligned status:
|
|
163
|
+
// accept → state='resolved', status=200
|
|
164
|
+
// reject → state='failed', status=400, outcome='rejected' (default) §proposal-reject-fails
|
|
165
|
+
// cancel → state='cancelled',status=499, outcome='loop_aborted' (default) §proposal-cancel-aborts
|
|
166
|
+
// resolution.outcome wins over the default when supplied; this is how
|
|
167
|
+
// veto filters (Phase E.2 proposal.accepting) can specify a more
|
|
168
|
+
// precise outcome string like 'policy_veto' or 'timeout'.
|
|
169
|
+
const decision = resolution.decision;
|
|
170
|
+
const state = decision === "accept" ? "resolved"
|
|
171
|
+
: decision === "reject" ? "failed"
|
|
172
|
+
: "cancelled";
|
|
173
|
+
const status = decision === "accept" ? 200
|
|
174
|
+
: decision === "reject" ? 400
|
|
175
|
+
: 499;
|
|
176
|
+
const defaultOutcome = decision === "accept" ? null
|
|
177
|
+
: decision === "reject" ? "rejected"
|
|
178
|
+
: "loop_aborted";
|
|
179
|
+
const outcome = resolution.outcome ?? defaultOutcome;
|
|
180
|
+
// rx is the model-facing operation result. Status always. Body is normally dropped —
|
|
181
|
+
// the propose preview was an input echo — EXCEPT an inline auto-run (read/pure) carries
|
|
182
|
+
// its run output AS the body, the "what happened" the model needs this turn. A non-accept
|
|
183
|
+
// carries the outcome TOKEN as its terse error (write_failed / rejected / timeout — one
|
|
184
|
+
// word, never prose): a bare {"status":400} left the model blind to a mechanically failed
|
|
185
|
+
// apply and parking on a phantom worker (the fan-out digest).
|
|
186
|
+
const rx = (decision === "accept" && resolution.body !== undefined)
|
|
187
|
+
? JSON.stringify({ status, body: resolution.body })
|
|
188
|
+
: decision === "accept"
|
|
189
|
+
? JSON.stringify({ status })
|
|
190
|
+
: JSON.stringify({ status, error: outcome });
|
|
191
|
+
await this.#db.engine_resolve_log_entry.run({
|
|
192
|
+
id: logEntryId, state, outcome, status_rx: status, rx,
|
|
193
|
+
});
|
|
194
|
+
return { status, outcome, body: resolution.body };
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=ProposalLifecycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProposalLifecycle.js","sourceRoot":"","sources":["../../src/core/ProposalLifecycle.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import type { LoopFlags } from "./types.ts";
|
|
2
2
|
import type { PacketSection } from "./packet-wire.ts";
|
|
3
3
|
import type ExecutorRegistry from "./ExecutorRegistry.ts";
|
|
4
|
+
import type { Executor } from "./ExecutorRegistry.ts";
|
|
4
5
|
export default class SchemeRegistry {
|
|
5
6
|
#private;
|
|
6
7
|
constructor();
|
|
7
8
|
register(name: string, handler: object): void;
|
|
8
9
|
registerRuntimeSchemes(executors: ExecutorRegistry): void;
|
|
10
|
+
registerRuntimeScheme(tag: string, executor: Executor): void;
|
|
9
11
|
get(name: string): object | undefined;
|
|
10
12
|
has(name: string): boolean;
|
|
11
13
|
isExternal(name: string): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SchemeRegistry.d.ts","sourceRoot":"","sources":["../../src/core/SchemeRegistry.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAItD,OAAO,KAAK,gBAAgB,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"SchemeRegistry.d.ts","sourceRoot":"","sources":["../../src/core/SchemeRegistry.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAItD,OAAO,KAAK,gBAAgB,MAAM,uBAAuB,CAAC;AAC1D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAiBtD,MAAM,CAAC,OAAO,OAAO,cAAc;;;IA6B/B,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAU7C,sBAAsB,CAAC,SAAS,EAAE,gBAAgB,GAAG,IAAI;IAazD,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,IAAI;IAU5D,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAErC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI1B,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAEjC,IAAI,IAAI,MAAM,EAAE;IAIhB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM;IAehD,KAAK,IAAI,MAAM;IAgBf,IAAI,IAAI,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAkB1C,iBAAiB,CAAC,QAAQ,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAiBtE,gBAAgB,CAAC,GAAG,GAAE,MAAsB,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBlE,YAAY,IAAI,MAAM,EAAE;IAIxB,cAAc,CAAC,KAAK,EAAE,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC;CAGhD"}
|
|
@@ -38,7 +38,7 @@ const SCHEME_DOCS = await (async () => {
|
|
|
38
38
|
export default class SchemeRegistry {
|
|
39
39
|
// Heterogeneous handler store — in-tree schemes take PlurnkSchemeContext, external
|
|
40
40
|
// siblings the DB-free SchemeCtx of the imported SchemeHandler; the common supertype
|
|
41
|
-
// is `object`. Dispatch (
|
|
41
|
+
// is `object`. Dispatch (Dispatcher.#run) borrows SchemeHandler's op-key set, not its ctx.
|
|
42
42
|
#handlers = new Map();
|
|
43
43
|
#external = new Set();
|
|
44
44
|
#attributions = []; // #249 — declared attribution tags of discovered external schemes
|
|
@@ -73,26 +73,31 @@ export default class SchemeRegistry {
|
|
|
73
73
|
// delegated to the shared Exec handler. Minted from the boot ExecutorRegistry; in-tree
|
|
74
74
|
// names take precedence (a tag shadowing a built-in is skipped, never overrides it).
|
|
75
75
|
registerRuntimeSchemes(executors) {
|
|
76
|
-
const exec = this.#handlers.get("exec");
|
|
77
|
-
if (!(exec instanceof Exec))
|
|
78
|
-
throw new Error("registerRuntimeSchemes: the exec handler is not registered");
|
|
79
76
|
for (const tag of executors.availableRuntimes()) {
|
|
80
|
-
if (this.#reserved.has(tag))
|
|
81
|
-
throw new Error(`executor tag '${tag}' collides with a reserved built-in scheme — boot fail-hard (#240)`);
|
|
82
|
-
if (this.#runtimeSchemes.has(tag))
|
|
83
|
-
continue; // idempotent re-scan — already this tag's own runtime scheme
|
|
84
|
-
// #240 cross-family namespace: a tag already claimed by a DIFFERENT scheme (an external
|
|
85
|
-
// scheme sibling) is a collision, not a re-scan — one name, one owner across the exec and
|
|
86
|
-
// scheme families. Fail the boot hard rather than silently first-wins-skipping the tag.
|
|
87
|
-
if (this.#handlers.has(tag))
|
|
88
|
-
throw new Error(`executor tag '${tag}' collides with an already-claimed scheme '${tag}' — one name, one owner across the exec/scheme families (#240)`);
|
|
89
77
|
const entry = executors.entry(tag);
|
|
90
78
|
if (entry === undefined)
|
|
91
79
|
continue;
|
|
92
|
-
this.
|
|
93
|
-
this.#runtimeSchemes.add(tag);
|
|
80
|
+
this.registerRuntimeScheme(tag, entry.executor);
|
|
94
81
|
}
|
|
95
82
|
}
|
|
83
|
+
// Register ONE executor tag's scheme face (the boot loop above + the #289 runtime hotload:
|
|
84
|
+
// an MCP server connected live becomes EXEC[<server>]). The reserved/cross-family arbitration
|
|
85
|
+
// lives HERE so both paths share it — one name, one owner across the exec/scheme families (#240):
|
|
86
|
+
// idempotent on a tag that already has its own runtime scheme (a boot re-scan), fail-hard on a
|
|
87
|
+
// collision with a reserved built-in or a DIFFERENT already-claimed scheme.
|
|
88
|
+
registerRuntimeScheme(tag, executor) {
|
|
89
|
+
const exec = this.#handlers.get("exec");
|
|
90
|
+
if (!(exec instanceof Exec))
|
|
91
|
+
throw new Error("registerRuntimeScheme: the exec handler is not registered");
|
|
92
|
+
if (this.#reserved.has(tag))
|
|
93
|
+
throw new Error(`executor tag '${tag}' collides with a reserved built-in scheme — fail-hard (#240)`);
|
|
94
|
+
if (this.#runtimeSchemes.has(tag))
|
|
95
|
+
return; // idempotent — already this tag's own runtime scheme
|
|
96
|
+
if (this.#handlers.has(tag))
|
|
97
|
+
throw new Error(`executor tag '${tag}' collides with an already-claimed scheme '${tag}' — one name, one owner across the exec/scheme families (#240)`);
|
|
98
|
+
this.register(tag, new ExecOutputScheme(executor, exec));
|
|
99
|
+
this.#runtimeSchemes.add(tag);
|
|
100
|
+
}
|
|
96
101
|
get(name) { return this.#handlers.get(name); }
|
|
97
102
|
has(name) { return this.#handlers.has(name); }
|
|
98
103
|
// True for schemes registered via discoverExternal — they receive the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SchemeRegistry.js","sourceRoot":"","sources":["../../src/core/SchemeRegistry.ts"],"names":[],"mappings":";;;;;;;;AAAA,OAAO,MAAM,MAAM,sBAAsB,CAAC;AAC1C,OAAO,GAAG,MAAM,mBAAmB,CAAC;AACpC,OAAO,IAAI,MAAM,oBAAoB,CAAC;AACtC,OAAO,KAAK,MAAM,qBAAqB,CAAC;AACxC,OAAO,OAAO,MAAM,uBAAuB,CAAC;AAC5C,OAAO,KAAK,MAAM,qBAAqB,CAAC;AACxC,OAAO,IAAI,MAAM,oBAAoB,CAAC;AACtC,OAAO,GAAG,MAAM,mBAAmB,CAAC;AACpC,OAAO,cAAc,MAAM,qBAAqB,CAAC;AAEjD,OAAO,EAAE,eAAe,EAAsB,MAAM,wBAAwB,CAAC;AAG7E,OAAO,iBAAiB,MAAM,yBAAyB,CAAC;AACxD,OAAO,gBAAgB,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"SchemeRegistry.js","sourceRoot":"","sources":["../../src/core/SchemeRegistry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { PlurnkStatement } from "@plurnk/plurnk-grammar";
|
|
2
|
+
export default class StrikeRail {
|
|
3
|
+
#private;
|
|
4
|
+
static fingerprintTurn(ops: ReadonlyArray<PlurnkStatement>): string;
|
|
5
|
+
static detectCycle(history: ReadonlyArray<string>, minCycles: number, maxCyclePeriod: number): {
|
|
6
|
+
detected: false;
|
|
7
|
+
} | {
|
|
8
|
+
detected: true;
|
|
9
|
+
period: number;
|
|
10
|
+
cycles: number;
|
|
11
|
+
};
|
|
12
|
+
assess(loopId: number, turn: {
|
|
13
|
+
fingerprint: string;
|
|
14
|
+
statuses: ReadonlyArray<number>;
|
|
15
|
+
noOps: boolean;
|
|
16
|
+
budgetStruck: boolean;
|
|
17
|
+
steerStruck: boolean;
|
|
18
|
+
minCycles: number;
|
|
19
|
+
maxCyclePeriod: number;
|
|
20
|
+
maxStrikes: number;
|
|
21
|
+
}): {
|
|
22
|
+
cycleDetected: boolean;
|
|
23
|
+
thresholdCrossed: boolean;
|
|
24
|
+
};
|
|
25
|
+
delete(loopId: number): void;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=StrikeRail.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StrikeRail.d.ts","sourceRoot":"","sources":["../../src/core/StrikeRail.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAc,MAAM,wBAAwB,CAAC;AAmE1E,MAAM,CAAC,OAAO,OAAO,UAAU;;IAG3B,MAAM,CAAC,eAAe,CAAC,GAAG,EAAE,aAAa,CAAC,eAAe,CAAC,GAAG,MAAM;IAQnE,MAAM,CAAC,WAAW,CACd,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,EAC9B,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,MAAM,GACvB;QAAE,QAAQ,EAAE,KAAK,CAAA;KAAE,GAAG;QAAE,QAAQ,EAAE,IAAI,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;IA+B3E,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE;QACzB,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;QAChC,KAAK,EAAE,OAAO,CAAC;QACf,YAAY,EAAE,OAAO,CAAC;QACtB,WAAW,EAAE,OAAO,CAAC;QACrB,SAAS,EAAE,MAAM,CAAC;QAClB,cAAc,EAAE,MAAM,CAAC;QACvB,UAAU,EAAE,MAAM,CAAC;KACtB,GAAG;QAAE,aAAa,EAAE,OAAO,CAAC;QAAC,gBAAgB,EAAE,OAAO,CAAA;KAAE;IA8BzD,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;CAG/B"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// Rail #38: action-entry statuses that DON'T accumulate strikes. Model adapted
|
|
2
|
+
// to a finding (not_found, op_not_supported); no penalty. Rummy parallel:
|
|
3
|
+
// SOFT_FAILURE_OUTCOMES = {"not_found", "unparsed"}.
|
|
4
|
+
const SOFT_FAILURE_STATUSES = new Set([404, 501]);
|
|
5
|
+
// Per-op fingerprint: op verb + target URI, plus an op-specific discriminator
|
|
6
|
+
// where the activity isn't fully captured by target alone:
|
|
7
|
+
// - EDIT/COPY/MOVE: body excluded — re-writing the same target with varied
|
|
8
|
+
// content IS cycling (the model is producing different versions of the
|
|
9
|
+
// same artifact instead of progressing).
|
|
10
|
+
// - FIND/READ/OPEN/FOLD: body IS the search/selection pattern; varied
|
|
11
|
+
// matchers on the same target ARE different activities (the model is
|
|
12
|
+
// exploring different queries, not repeating one).
|
|
13
|
+
const fingerprintOp = (stmt) => {
|
|
14
|
+
const path = stmt.target;
|
|
15
|
+
const matcherDiscriminator = () => {
|
|
16
|
+
// For matcher-bearing ops, the body's `raw` (matcher source) plus
|
|
17
|
+
// any lineMarker forms the activity discriminator.
|
|
18
|
+
const parts = [];
|
|
19
|
+
const body = stmt.body;
|
|
20
|
+
if (body !== null && typeof body === "object" && typeof body.raw === "string") {
|
|
21
|
+
parts.push(`body:${body.raw.slice(0, 64)}`);
|
|
22
|
+
}
|
|
23
|
+
const lm = stmt.lineMarker;
|
|
24
|
+
if (lm !== null && lm !== undefined)
|
|
25
|
+
parts.push(`L:${lm.marks.join(",")}`);
|
|
26
|
+
return parts.length > 0 ? `|${parts.join("|")}` : "";
|
|
27
|
+
};
|
|
28
|
+
if (path === null) {
|
|
29
|
+
// Path-less ops need an activity-defining discriminator other
|
|
30
|
+
// than `target`. Picked per op so the cycle detector reflects
|
|
31
|
+
// intent rather than syntax:
|
|
32
|
+
// - EXEC: the command body IS the activity. Without a body
|
|
33
|
+
// digest, varied shell commands (find / ls / wc) collapse to
|
|
34
|
+
// one fingerprint and the detector mislabels exploration
|
|
35
|
+
// as a loop.
|
|
36
|
+
// - SEND: the status code (signal) IS the activity. Different
|
|
37
|
+
// SEND[X] are different intentions; same SEND[X] with
|
|
38
|
+
// different message bodies is the same termination signal.
|
|
39
|
+
if (stmt.op === "EXEC") {
|
|
40
|
+
const body = typeof stmt.body === "string" ? stmt.body : "";
|
|
41
|
+
return `EXEC|(no-path)${body.length > 0 ? `|body:${body.slice(0, 64)}` : ""}`;
|
|
42
|
+
}
|
|
43
|
+
if (stmt.op === "SEND") {
|
|
44
|
+
const signal = typeof stmt.signal === "number" ? stmt.signal : "";
|
|
45
|
+
return `SEND|(no-path)|signal:${signal}`;
|
|
46
|
+
}
|
|
47
|
+
return `${stmt.op}|(no-path)`;
|
|
48
|
+
}
|
|
49
|
+
const base = path.kind === "url"
|
|
50
|
+
? `${stmt.op}|${path.scheme}://${path.pathname}`
|
|
51
|
+
: `${stmt.op}|local:${path.raw}`;
|
|
52
|
+
if (stmt.op === "FIND" || stmt.op === "READ" || stmt.op === "OPEN" || stmt.op === "FOLD") {
|
|
53
|
+
return `${base}${matcherDiscriminator()}`;
|
|
54
|
+
}
|
|
55
|
+
return base;
|
|
56
|
+
};
|
|
57
|
+
// Rails #38 (strikes) + #39 (cycle detection): the per-loop failure-streak
|
|
58
|
+
// accounting that decides abandonment. Strike accounting is engine-internal
|
|
59
|
+
// bookkeeping. Per rummy precedent (plugins/error/error.js#verdict) and SPEC
|
|
60
|
+
// §telemetry policy: model sees errors that happened (parse_error,
|
|
61
|
+
// action_failure), never the engine's accounting about them (strike counts,
|
|
62
|
+
// cycle detection, sudden-death threshold). Surfacing internal state to the
|
|
63
|
+
// model creates a gamification surface — model optimizes for engine metrics
|
|
64
|
+
// rather than task progress.
|
|
65
|
+
export default class StrikeRail {
|
|
66
|
+
// Per-turn fingerprint: sorted set of per-op fingerprints, joined. Order
|
|
67
|
+
// within a turn doesn't matter — we want the SET of activities.
|
|
68
|
+
static fingerprintTurn(ops) {
|
|
69
|
+
return ops.map(fingerprintOp).toSorted().join(",");
|
|
70
|
+
}
|
|
71
|
+
// Rail #39 cycle detector. For each candidate period k in [1, maxCyclePeriod],
|
|
72
|
+
// check whether the last k*minCycles entries form minCycles repetitions of the
|
|
73
|
+
// same length-k pattern. O(maxCyclePeriod × minCycles × max k) ≈ tiny. Rummy
|
|
74
|
+
// parallel: src/plugins/error/error.js detectCycle.
|
|
75
|
+
static detectCycle(history, minCycles, maxCyclePeriod) {
|
|
76
|
+
for (let k = 1; k <= maxCyclePeriod; k++) {
|
|
77
|
+
const needed = k * minCycles;
|
|
78
|
+
if (history.length < needed)
|
|
79
|
+
continue;
|
|
80
|
+
const tail = history.slice(-needed);
|
|
81
|
+
const cycle = tail.slice(0, k);
|
|
82
|
+
let match = true;
|
|
83
|
+
outer: for (let rep = 0; rep < minCycles; rep++) {
|
|
84
|
+
for (let j = 0; j < k; j++) {
|
|
85
|
+
if (tail[rep * k + j] !== cycle[j]) {
|
|
86
|
+
match = false;
|
|
87
|
+
break outer;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (match)
|
|
92
|
+
return { detected: true, period: k, cycles: minCycles };
|
|
93
|
+
}
|
|
94
|
+
return { detected: false };
|
|
95
|
+
}
|
|
96
|
+
// Rail #38 strike state per loop. `streak` = consecutive struck turns;
|
|
97
|
+
// resets on a clean turn. `turnErrors` is bumped by per-turn rails (cycle
|
|
98
|
+
// detection #39, grinder, steer) — read and reset at end of each turn.
|
|
99
|
+
// `history` holds per-turn fingerprints for rail #39 cycle detection.
|
|
100
|
+
#state = new Map();
|
|
101
|
+
// Per-turn strike accounting, run by runLoop after every turn. Three
|
|
102
|
+
// sources strike a turn:
|
|
103
|
+
// 1. recordedFailed — any action-entry at hard failure status
|
|
104
|
+
// (>= 400 and not in SOFT_FAILURE_STATUSES).
|
|
105
|
+
// 2. noOps — the turn emitted no ops at all (per #41).
|
|
106
|
+
// 3. turnErrors — bumped by per-turn rails (#39 cycle, grinder, steer).
|
|
107
|
+
// Struck → streak++; clean → streak = 0. Threshold → thresholdCrossed,
|
|
108
|
+
// and runLoop owns the abandonment.
|
|
109
|
+
assess(loopId, turn) {
|
|
110
|
+
// Rail #39: cycle detection. Push this turn's fingerprint to
|
|
111
|
+
// history, scan for repetition patterns. Detection bumps
|
|
112
|
+
// turnErrors so the strike system handles abandonment
|
|
113
|
+
// naturally — same internal-only role rummy gave it
|
|
114
|
+
// (plugins/error/error.js#verdict). Intentionally NOT a
|
|
115
|
+
// model-facing telemetry kind: model sees the strike pile-up
|
|
116
|
+
// (which IS the actionable signal); cycle is the engine's
|
|
117
|
+
// reason for treating the turn as a failure, not its own alert.
|
|
118
|
+
const state = this.#state.get(loopId) ?? { streak: 0, turnErrors: 0, history: [] };
|
|
119
|
+
state.history.push(turn.fingerprint);
|
|
120
|
+
const cycle = StrikeRail.detectCycle(state.history, turn.minCycles, turn.maxCyclePeriod);
|
|
121
|
+
if (cycle.detected)
|
|
122
|
+
state.turnErrors++;
|
|
123
|
+
// SPEC §grinder: a non-soft grinder fire counts toward the strike streak.
|
|
124
|
+
if (turn.budgetStruck)
|
|
125
|
+
state.turnErrors++; // a grinder fire bumps the strike streak — §grinder-strike-coupling
|
|
126
|
+
if (turn.steerStruck)
|
|
127
|
+
state.turnErrors++; // idle / premature-terminate steer struck — §send the terminal contract
|
|
128
|
+
const recordedFailed = turn.statuses.some((s) => s >= 400 && !SOFT_FAILURE_STATUSES.has(s));
|
|
129
|
+
const struck = turn.noOps || recordedFailed || state.turnErrors > 0;
|
|
130
|
+
let thresholdCrossed = false;
|
|
131
|
+
if (struck) {
|
|
132
|
+
state.streak++;
|
|
133
|
+
if (state.streak >= turn.maxStrikes)
|
|
134
|
+
thresholdCrossed = true;
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
state.streak = 0;
|
|
138
|
+
}
|
|
139
|
+
state.turnErrors = 0;
|
|
140
|
+
this.#state.set(loopId, state);
|
|
141
|
+
return { cycleDetected: cycle.detected, thresholdCrossed };
|
|
142
|
+
}
|
|
143
|
+
delete(loopId) {
|
|
144
|
+
this.#state.delete(loopId);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=StrikeRail.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StrikeRail.js","sourceRoot":"","sources":["../../src/core/StrikeRail.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { TelemetryEvent } from "@plurnk/plurnk-grammar";
|
|
2
|
+
import type { Db } from "./Db.ts";
|
|
3
|
+
import type { TelemetryEventNotify } from "./ChannelWrite.ts";
|
|
4
|
+
declare const ENGINE_ERRORS: Readonly<{
|
|
5
|
+
readonly budget_overflow: {
|
|
6
|
+
readonly status: 413;
|
|
7
|
+
readonly term: "Budget Overflow: newest log items automatically FOLDed";
|
|
8
|
+
};
|
|
9
|
+
readonly max_commands_exceeded: {
|
|
10
|
+
readonly status: 429;
|
|
11
|
+
readonly term: "Max Commands Exceeded";
|
|
12
|
+
};
|
|
13
|
+
readonly idle_turn: {
|
|
14
|
+
readonly status: 409;
|
|
15
|
+
readonly term: "Idle Turn";
|
|
16
|
+
};
|
|
17
|
+
}>;
|
|
18
|
+
export type EngineErrorKind = keyof typeof ENGINE_ERRORS;
|
|
19
|
+
export default class TelemetryChannel {
|
|
20
|
+
#private;
|
|
21
|
+
constructor({ db, notify }: {
|
|
22
|
+
db: Db;
|
|
23
|
+
notify?: TelemetryEventNotify;
|
|
24
|
+
});
|
|
25
|
+
push(sessionId: number, loopId: number, event: TelemetryEvent): void;
|
|
26
|
+
notify(sessionId: number, loopId: number, event: TelemetryEvent): void;
|
|
27
|
+
drain(loopId: number): object[];
|
|
28
|
+
delete(loopId: number): void;
|
|
29
|
+
mintEngineError(kind: EngineErrorKind, { runId, loopId, turnId, sequence }: {
|
|
30
|
+
runId: number;
|
|
31
|
+
loopId: number;
|
|
32
|
+
turnId: number;
|
|
33
|
+
sequence: number;
|
|
34
|
+
}): Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
export {};
|
|
37
|
+
//# sourceMappingURL=TelemetryChannel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TelemetryChannel.d.ts","sourceRoot":"","sources":["../../src/core/TelemetryChannel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,EAAE,EAAc,MAAM,SAAS,CAAC;AAC9C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAM9D,QAAA,MAAM,aAAa;;;;;;;;;;;;;EAOR,CAAC;AACZ,MAAM,MAAM,eAAe,GAAG,MAAM,OAAO,aAAa,CAAC;AAKzD,MAAM,CAAC,OAAO,OAAO,gBAAgB;;gBAarB,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE;QAAE,EAAE,EAAE,EAAE,CAAC;QAAC,MAAM,CAAC,EAAE,oBAAoB,CAAA;KAAE;IAKrE,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,IAAI;IAYpE,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,IAAI;IAKtE,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE;IAO/B,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAQtB,eAAe,CAAC,IAAI,EAAE,eAAe,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAaxK"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// §telemetry — the uniform error channel. Every engine failure is a terse op='error'
|
|
2
|
+
// log row: a status code + the canonical term, no prose (the packet teaches recovery).
|
|
3
|
+
// Each surfaces as a LogCoordinate TelemetryEvent derived from log≥400 — one channel,
|
|
4
|
+
// no per-kind handling. {§telemetry-uniform-error-channel}
|
|
5
|
+
const ENGINE_ERRORS = Object.freeze({
|
|
6
|
+
budget_overflow: { status: 413, term: "Budget Overflow: newest log items automatically FOLDed" },
|
|
7
|
+
max_commands_exceeded: { status: 429, term: "Max Commands Exceeded" },
|
|
8
|
+
// premature-terminate is NOT a terse engine-error: it's a SEND op-result (409 + an actionable
|
|
9
|
+
// outcome, §send-premature-terminate) — the SEND row records the [200] attempt faithfully and
|
|
10
|
+
// auto-surfaces (status≥400) like any op failure, never an erasure to 102.
|
|
11
|
+
idle_turn: { status: 409, term: "Idle Turn" },
|
|
12
|
+
});
|
|
13
|
+
// The model-facing alert channel: a per-loop transient buffer of actionless failures
|
|
14
|
+
// pending surface in the NEXT packet's user.telemetry.errors[], plus the live client
|
|
15
|
+
// fan-out and the uniform op='error' log-row mint. SPEC §telemetry.
|
|
16
|
+
export default class TelemetryChannel {
|
|
17
|
+
#db;
|
|
18
|
+
// Per-loop transient buffer of actionless failures pending surface in the
|
|
19
|
+
// NEXT packet's user.telemetry.errors[]. Drained by PacketBuilder.buildTelemetryErrors.
|
|
20
|
+
// Map<loopId, TelemetryError[]>. SPEC §telemetry.
|
|
21
|
+
#buffer = new Map();
|
|
22
|
+
// Telemetry event fan-out: every TelemetryEvent pushed to the loop's
|
|
23
|
+
// buffer is also broadcast live to the connected client(s) on the
|
|
24
|
+
// session. Without this, the client sees `loop/terminated` with a
|
|
25
|
+
// status code but has no way to surface why the loop degraded.
|
|
26
|
+
// Per-grammar 0.17.0 protocol — see SPEC §telemetry.
|
|
27
|
+
#notify;
|
|
28
|
+
constructor({ db, notify }) {
|
|
29
|
+
this.#db = db;
|
|
30
|
+
this.#notify = notify;
|
|
31
|
+
}
|
|
32
|
+
push(sessionId, loopId, event) {
|
|
33
|
+
const existing = this.#buffer.get(loopId);
|
|
34
|
+
if (existing === undefined)
|
|
35
|
+
this.#buffer.set(loopId, [event]);
|
|
36
|
+
else
|
|
37
|
+
existing.push(event);
|
|
38
|
+
// Live fan-out: client sees the event the moment it lands in the
|
|
39
|
+
// model's buffer (not at the next packet build). Same envelope on
|
|
40
|
+
// both sides per the grammar 0.17.0 TelemetryEvent protocol.
|
|
41
|
+
this.#notify?.(sessionId, { loopId, event });
|
|
42
|
+
}
|
|
43
|
+
// Live fan-out ONLY, never buffered — for work with no loop to drain the
|
|
44
|
+
// buffer (e.g. session-scope derivation warming, loopId 0).
|
|
45
|
+
notify(sessionId, loopId, event) {
|
|
46
|
+
this.#notify?.(sessionId, { loopId, event });
|
|
47
|
+
}
|
|
48
|
+
// Telemetry drains as it's read into the packet — each event surfaces once. §telemetry-drain-on-read
|
|
49
|
+
drain(loopId) {
|
|
50
|
+
const buf = this.#buffer.get(loopId);
|
|
51
|
+
if (buf === undefined)
|
|
52
|
+
return [];
|
|
53
|
+
this.#buffer.delete(loopId);
|
|
54
|
+
return buf;
|
|
55
|
+
}
|
|
56
|
+
delete(loopId) {
|
|
57
|
+
this.#buffer.delete(loopId);
|
|
58
|
+
}
|
|
59
|
+
// Mint an engine failure as a uniform op='error' log row (§telemetry-uniform-error-channel):
|
|
60
|
+
// a terse status + canonical term keyed by `kind` (the packet teaches recovery, not the row),
|
|
61
|
+
// origin engine:rail. The errors section derives its LogCoordinate pointer from log≥400 — one
|
|
62
|
+
// channel, no per-kind handling.
|
|
63
|
+
async mintEngineError(kind, { runId, loopId, turnId, sequence }) {
|
|
64
|
+
const { status, term } = ENGINE_ERRORS[kind];
|
|
65
|
+
await this.#db.engine_insert_log_entry.get({
|
|
66
|
+
run_id: runId, loop_id: loopId, turn_id: turnId, sequence,
|
|
67
|
+
origin: "plurnk", source: "rail", op: "error", suffix: "", signal: null,
|
|
68
|
+
scheme: null, username: null, password: null, hostname: null, port: null,
|
|
69
|
+
pathname: null, params: null, fragment: null, lineMarker: null,
|
|
70
|
+
tx: "", mimetype_tx: "text/plain",
|
|
71
|
+
rx: JSON.stringify({ kind, message: term }),
|
|
72
|
+
mimetype_rx: "application/json",
|
|
73
|
+
status_rx: status, tokens: 0, state: "resolved", outcome: null, attrs: "{}",
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=TelemetryChannel.js.map
|