@plurnk/plurnk-service 0.4.0 → 0.5.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/README.md +13 -44
- package/SPEC.md +548 -445
- package/bin/plurnk-service.js +2 -1
- package/dist/core/ChannelWrite.d.ts +5 -0
- package/dist/core/ChannelWrite.d.ts.map +1 -1
- package/dist/core/ChannelWrite.js.map +1 -1
- package/dist/core/Engine.d.ts +8 -5
- package/dist/core/Engine.d.ts.map +1 -1
- package/dist/core/Engine.js +169 -73
- package/dist/core/Engine.js.map +1 -1
- package/dist/core/PluginLoader.js +6 -6
- package/dist/core/PluginLoader.js.map +1 -1
- package/dist/core/ProviderInstantiate.d.ts +4 -0
- package/dist/core/ProviderInstantiate.d.ts.map +1 -0
- package/dist/core/ProviderInstantiate.js +41 -0
- package/dist/core/ProviderInstantiate.js.map +1 -0
- package/dist/core/ProviderRegistry.d.ts.map +1 -1
- package/dist/core/ProviderRegistry.js +1 -1
- package/dist/core/ProviderRegistry.js.map +1 -1
- package/dist/core/SchemeRegistry.d.ts +1 -1
- package/dist/core/SchemeRegistry.d.ts.map +1 -1
- package/dist/core/SchemeRegistry.js +4 -20
- package/dist/core/SchemeRegistry.js.map +1 -1
- package/dist/core/line-marker.js +3 -3
- package/dist/core/line-marker.js.map +1 -1
- package/dist/core/matcher.d.ts +4 -6
- package/dist/core/matcher.d.ts.map +1 -1
- package/dist/core/matcher.js +61 -184
- package/dist/core/matcher.js.map +1 -1
- package/dist/core/mimetype-binary.js +1 -1
- package/dist/core/mimetype-binary.js.map +1 -1
- package/dist/core/packet-wire.d.ts.map +1 -1
- package/dist/core/packet-wire.js +130 -30
- package/dist/core/packet-wire.js.map +1 -1
- package/dist/core/scheme-types.d.ts +3 -26
- package/dist/core/scheme-types.d.ts.map +1 -1
- package/dist/core/scheme-types.js +5 -8
- package/dist/core/scheme-types.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -1
- package/dist/index.js.map +1 -1
- package/dist/schemes/Exec.d.ts.map +1 -1
- package/dist/schemes/Exec.js +12 -24
- package/dist/schemes/Exec.js.map +1 -1
- package/dist/schemes/File.d.ts +1 -0
- package/dist/schemes/File.d.ts.map +1 -1
- package/dist/schemes/File.js +11 -5
- package/dist/schemes/File.js.map +1 -1
- package/dist/schemes/Log.d.ts +1 -0
- package/dist/schemes/Log.d.ts.map +1 -1
- package/dist/schemes/Log.js +10 -4
- package/dist/schemes/Log.js.map +1 -1
- package/dist/schemes/_entry-ops.d.ts +1 -1
- package/dist/schemes/_entry-ops.d.ts.map +1 -1
- package/dist/schemes/_entry-ops.js +15 -20
- package/dist/schemes/_entry-ops.js.map +1 -1
- package/dist/server/Daemon.d.ts +11 -0
- package/dist/server/Daemon.d.ts.map +1 -1
- package/dist/server/Daemon.js +18 -0
- package/dist/server/Daemon.js.map +1 -1
- package/dist/server/MethodRegistry.d.ts +2 -2
- package/dist/server/MethodRegistry.d.ts.map +1 -1
- package/dist/server/methods/loop_resolve.js +2 -2
- package/dist/server/methods/loop_resolve.js.map +1 -1
- package/dist/server/methods/loop_run.d.ts.map +1 -1
- package/dist/server/methods/loop_run.js +2 -1
- package/dist/server/methods/loop_run.js.map +1 -1
- package/dist/server/methods/providers_list.js +1 -1
- package/dist/server/methods/providers_list.js.map +1 -1
- package/package.json +16 -12
package/bin/plurnk-service.js
CHANGED
|
@@ -7,7 +7,8 @@ import { fileURLToPath } from "node:url";
|
|
|
7
7
|
import { dirname, resolve } from "node:path";
|
|
8
8
|
import Daemon from "../src/server/Daemon.ts";
|
|
9
9
|
import { parseEnvExample, formatFlagsHelp } from "../src/core/EnvFlags.ts";
|
|
10
|
-
import { loadActiveProvider
|
|
10
|
+
import { loadActiveProvider } from "../src/core/ProviderInstantiate.ts";
|
|
11
|
+
import { resolveActiveAlias } from "@plurnk/plurnk-providers";
|
|
11
12
|
|
|
12
13
|
const die = (code, message) => {
|
|
13
14
|
process.stderr.write(`${message}\n`);
|
|
@@ -17,6 +17,11 @@ export interface WakeRunPayload {
|
|
|
17
17
|
summary: string;
|
|
18
18
|
}
|
|
19
19
|
export type WakeRunNotify = (payload: WakeRunPayload) => void;
|
|
20
|
+
export interface TelemetryEventPayload {
|
|
21
|
+
loopId: number;
|
|
22
|
+
event: object;
|
|
23
|
+
}
|
|
24
|
+
export type TelemetryEventNotify = (sessionId: number, payload: TelemetryEventPayload) => void;
|
|
20
25
|
export declare const appendToChannel: (db: Db, { entryId, channel, chunk, notify }: {
|
|
21
26
|
entryId: number;
|
|
22
27
|
channel: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChannelWrite.d.ts","sourceRoot":"","sources":["../../src/core/ChannelWrite.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,EAAE,EAAc,MAAM,SAAS,CAAC;AAE9C,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEtE,MAAM,WAAW,kBAAkB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,YAAY,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,iBAAiB,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,KAAK,IAAI,CAAC;AAQvF,MAAM,WAAW,cAAc;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"ChannelWrite.d.ts","sourceRoot":"","sources":["../../src/core/ChannelWrite.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,EAAE,EAAc,MAAM,SAAS,CAAC;AAE9C,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEtE,MAAM,WAAW,kBAAkB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,YAAY,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,iBAAiB,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,KAAK,IAAI,CAAC;AAQvF,MAAM,WAAW,cAAc;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;AAQ9D,MAAM,WAAW,qBAAqB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,oBAAoB,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,qBAAqB,KAAK,IAAI,CAAC;AAe/F,eAAO,MAAM,eAAe,GACxB,IAAI,EAAE,EACN,qCAAqC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,iBAAiB,CAAA;CAAE,KACrH,OAAO,CAAC,IAAI,CAOd,CAAC;AAEF,eAAO,MAAM,eAAe,GACxB,IAAI,EAAE,EACN,qCAAqC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,YAAY,CAAC;IAAC,MAAM,CAAC,EAAE,iBAAiB,CAAA;CAAE,KAC3H,OAAO,CAAC,IAAI,CAOd,CAAC;AAEF,eAAO,MAAM,gBAAgB,GACzB,IAAI,EAAE,EACN,oCAAoC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,KACvG,OAAO,CAAC,MAAM,CAIhB,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC1B,IAAI,EAAE,EACN,4BAA4B;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,KACvE,OAAO,CAAC,IAAI,CAEd,CAAC;AAEF,eAAO,MAAM,sBAAsB,GAC/B,IAAI,EAAE,EACN,oBAAoB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,KACvD,OAAO,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAG/D,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChannelWrite.js","sourceRoot":"","sources":["../../src/core/ChannelWrite.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,mEAAmE;AACnE,EAAE;AACF,+EAA+E;AAC/E,8EAA8E;AAC9E,2EAA2E;AAC3E,gCAAgC;
|
|
1
|
+
{"version":3,"file":"ChannelWrite.js","sourceRoot":"","sources":["../../src/core/ChannelWrite.ts"],"names":[],"mappings":"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"}
|
package/dist/core/Engine.d.ts
CHANGED
|
@@ -3,13 +3,13 @@ import type SchemeRegistry from "./SchemeRegistry.ts";
|
|
|
3
3
|
import { Mimetypes } from "@plurnk/plurnk-mimetypes";
|
|
4
4
|
import type { Db } from "./Db.ts";
|
|
5
5
|
import type { LoopFlags } from "./scheme-types.ts";
|
|
6
|
-
import type { StreamEventNotify, WakeRunNotify } from "./ChannelWrite.ts";
|
|
6
|
+
import type { StreamEventNotify, TelemetryEventNotify, WakeRunNotify } from "./ChannelWrite.ts";
|
|
7
7
|
type Origin = "model" | "client" | "system" | "plugin";
|
|
8
8
|
type ChatMessage = {
|
|
9
9
|
role: "system" | "user" | "assistant";
|
|
10
10
|
content: string;
|
|
11
11
|
};
|
|
12
|
-
import type { Provider } from "
|
|
12
|
+
import type { Provider } from "@plurnk/plurnk-providers";
|
|
13
13
|
type DispatchContext = {
|
|
14
14
|
statement: PlurnkStatement;
|
|
15
15
|
sessionId: number;
|
|
@@ -56,17 +56,19 @@ export declare const detectCycle: (history: ReadonlyArray<string>, minCycles: nu
|
|
|
56
56
|
};
|
|
57
57
|
export default class Engine {
|
|
58
58
|
#private;
|
|
59
|
-
constructor({ db, schemes, mimetypes, streamEventNotify, wakeRunNotify }: {
|
|
59
|
+
constructor({ db, schemes, mimetypes, streamEventNotify, wakeRunNotify, telemetryEventNotify }: {
|
|
60
60
|
db: Db;
|
|
61
61
|
schemes: SchemeRegistry;
|
|
62
62
|
mimetypes?: Mimetypes;
|
|
63
63
|
streamEventNotify?: StreamEventNotify;
|
|
64
64
|
wakeRunNotify?: WakeRunNotify;
|
|
65
|
+
telemetryEventNotify?: TelemetryEventNotify;
|
|
65
66
|
});
|
|
66
|
-
runLoop({ provider, messages, persona, sessionId, runId, loopId, maxTurns, maxStrikes, minCycles, maxCyclePeriod, origin, signal, onDispatch, }: {
|
|
67
|
+
runLoop({ provider, messages, persona, requirements, sessionId, runId, loopId, maxTurns, maxStrikes, minCycles, maxCyclePeriod, origin, signal, onDispatch, }: {
|
|
67
68
|
provider: Provider;
|
|
68
69
|
messages: ChatMessage[];
|
|
69
70
|
persona?: string;
|
|
71
|
+
requirements?: string;
|
|
70
72
|
sessionId: number;
|
|
71
73
|
runId: number;
|
|
72
74
|
loopId: number;
|
|
@@ -83,10 +85,11 @@ export default class Engine {
|
|
|
83
85
|
hitMaxTurns: boolean;
|
|
84
86
|
reason: "max_turns" | "strike_threshold" | "external" | null;
|
|
85
87
|
}>;
|
|
86
|
-
runTurn({ provider, messages, persona, sessionId, runId, loopId, origin, signal, onDispatch, turnNumber, maxTurns, }: {
|
|
88
|
+
runTurn({ provider, messages, persona, requirements, sessionId, runId, loopId, origin, signal, onDispatch, turnNumber, maxTurns, }: {
|
|
87
89
|
provider: Provider;
|
|
88
90
|
messages: ChatMessage[];
|
|
89
91
|
persona?: string;
|
|
92
|
+
requirements?: string;
|
|
90
93
|
sessionId: number;
|
|
91
94
|
runId: number;
|
|
92
95
|
loopId: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Engine.d.ts","sourceRoot":"","sources":["../../src/core/Engine.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAA4D,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"Engine.d.ts","sourceRoot":"","sources":["../../src/core/Engine.ts"],"names":[],"mappings":"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"}
|
package/dist/core/Engine.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { PlurnkParser } from "@plurnk/plurnk-grammar";
|
|
1
|
+
import { PlurnkParser, PlurnkParseError } from "@plurnk/plurnk-grammar";
|
|
2
2
|
import { Mimetypes, emptyRegistry } from "@plurnk/plurnk-mimetypes";
|
|
3
3
|
import { writeEntry } from "../schemes/_entry-crud.js";
|
|
4
4
|
import { DEFAULT_LOOP_FLAGS } from "./scheme-types.js";
|
|
5
|
-
import { sliceLinesRaw } from "
|
|
6
|
-
import { isBinaryMimetype } from "./mimetype-binary.js";
|
|
5
|
+
import { sliceLinesRaw, isBinaryMimetype } from "@plurnk/plurnk-schemes";
|
|
7
6
|
// Plain JS module shared with bin/digest.js so wire projection and
|
|
8
7
|
// digest projection are structurally one function. tsconfig.build.json
|
|
9
8
|
// has allowJs:true so this gets copied through to dist/.
|
|
10
9
|
import { packetToWireMessages } from "./packet-wire.js";
|
|
11
|
-
//
|
|
10
|
+
// SPEC §3.6: writer must be in target scheme's manifest.writableBy.
|
|
12
11
|
// SHOW/HIDE/READ/FIND are not gated — they touch visibility metadata or read.
|
|
13
12
|
const MUTATING_OPS = new Set(["EDIT", "SEND", "COPY", "MOVE", "EXEC"]);
|
|
14
13
|
const DEFAULT_PREVIEW_BUDGET = 256;
|
|
15
14
|
const DEFAULT_MAX_STRIKES = 3;
|
|
15
|
+
const DEFAULT_MAX_COMMANDS = 99;
|
|
16
16
|
const readBudget = () => {
|
|
17
17
|
const raw = process.env.PLURNK_ENTRY_SIZE_DEFAULT_TOKENS;
|
|
18
18
|
if (raw === undefined || raw.length === 0)
|
|
@@ -31,8 +31,17 @@ const readMaxStrikes = () => {
|
|
|
31
31
|
return DEFAULT_MAX_STRIKES;
|
|
32
32
|
return n;
|
|
33
33
|
};
|
|
34
|
+
const readMaxCommands = () => {
|
|
35
|
+
const raw = process.env.PLURNK_MAX_COMMANDS;
|
|
36
|
+
if (raw === undefined || raw.length === 0)
|
|
37
|
+
return DEFAULT_MAX_COMMANDS;
|
|
38
|
+
const n = Number.parseInt(raw, 10);
|
|
39
|
+
if (!Number.isFinite(n) || n < 1)
|
|
40
|
+
return DEFAULT_MAX_COMMANDS;
|
|
41
|
+
return n;
|
|
42
|
+
};
|
|
34
43
|
// Resolution timeout — proposed entries auto-cancel if nothing arrives
|
|
35
|
-
// within this window.
|
|
44
|
+
// within this window. SPEC.md §0.5 (proposal lifecycle) + §13.5 (loop.resolve).
|
|
36
45
|
const PROPOSAL_TIMEOUT_DEFAULT_MS = 300000;
|
|
37
46
|
const readProposalTimeoutMs = () => {
|
|
38
47
|
const raw = process.env.PLURNK_PROPOSAL_TIMEOUT_MS;
|
|
@@ -166,11 +175,10 @@ export default class Engine {
|
|
|
166
175
|
// rails (cycle detection #39, etc.) — read and reset at end of each turn.
|
|
167
176
|
// `history` holds per-turn fingerprints for rail #39 cycle detection.
|
|
168
177
|
#strikeState = new Map();
|
|
169
|
-
// Proposal lifecycle
|
|
170
|
-
//
|
|
171
|
-
//
|
|
172
|
-
//
|
|
173
|
-
// §Phase E for the broader lifecycle plan.
|
|
178
|
+
// Proposal lifecycle: pending dispatch pauses waiting for resolution.
|
|
179
|
+
// Engine.runTurn awaits the promise when a scheme returns status 202;
|
|
180
|
+
// Engine.resolveProposal feeds the resolution back in. Map is per-log-
|
|
181
|
+
// entry-id; entries clear on resolution. SPEC.md §0.5 + §13.5 (loop.resolve).
|
|
174
182
|
#pendingProposals = new Map();
|
|
175
183
|
// External observers of proposal lifecycle events. Daemon subscribes
|
|
176
184
|
// here to push `loop/proposal` notifications when an entry enters
|
|
@@ -186,11 +194,18 @@ export default class Engine {
|
|
|
186
194
|
#loopAborts = new Map();
|
|
187
195
|
#streamEventNotify;
|
|
188
196
|
#wakeRunNotify;
|
|
189
|
-
|
|
197
|
+
// Telemetry event fan-out: every TelemetryEvent pushed to the loop's
|
|
198
|
+
// buffer is also broadcast live to the connected client(s) on the
|
|
199
|
+
// session. Without this, the client sees `loop/terminated` with a
|
|
200
|
+
// status code but has no way to surface why the loop degraded.
|
|
201
|
+
// Per-grammar 0.17.0 protocol — see SPEC §15.1.
|
|
202
|
+
#telemetryEventNotify;
|
|
203
|
+
constructor({ db, schemes, mimetypes, streamEventNotify, wakeRunNotify, telemetryEventNotify }) {
|
|
190
204
|
this.#db = db;
|
|
191
205
|
this.#schemes = schemes;
|
|
192
206
|
this.#streamEventNotify = streamEventNotify;
|
|
193
207
|
this.#wakeRunNotify = wakeRunNotify;
|
|
208
|
+
this.#telemetryEventNotify = telemetryEventNotify;
|
|
194
209
|
// Default to empty discovery — standalone Engine construction (in
|
|
195
210
|
// tests) gets no handlers, and content flows through the framework's
|
|
196
211
|
// raw-content fitContent fallback. Daemon-managed Engine receives a
|
|
@@ -200,12 +215,16 @@ export default class Engine {
|
|
|
200
215
|
});
|
|
201
216
|
this.#previewBudget = readBudget();
|
|
202
217
|
}
|
|
203
|
-
#pushTelemetry(loopId,
|
|
218
|
+
#pushTelemetry(sessionId, loopId, event) {
|
|
204
219
|
const existing = this.#telemetryBuffer.get(loopId);
|
|
205
220
|
if (existing === undefined)
|
|
206
|
-
this.#telemetryBuffer.set(loopId, [
|
|
221
|
+
this.#telemetryBuffer.set(loopId, [event]);
|
|
207
222
|
else
|
|
208
|
-
existing.push(
|
|
223
|
+
existing.push(event);
|
|
224
|
+
// Live fan-out: client sees the event the moment it lands in the
|
|
225
|
+
// model's buffer (not at the next packet build). Same envelope on
|
|
226
|
+
// both sides per the grammar 0.17.0 TelemetryEvent protocol.
|
|
227
|
+
this.#telemetryEventNotify?.(sessionId, { loopId, event });
|
|
209
228
|
}
|
|
210
229
|
#drainTelemetry(loopId) {
|
|
211
230
|
const buf = this.#telemetryBuffer.get(loopId);
|
|
@@ -214,7 +233,28 @@ export default class Engine {
|
|
|
214
233
|
this.#telemetryBuffer.delete(loopId);
|
|
215
234
|
return buf;
|
|
216
235
|
}
|
|
217
|
-
|
|
236
|
+
// Pull a ±windowLines context block around `targetLine` (1-based) from
|
|
237
|
+
// `content`, formatted with N:\t prefixes — same shape the model
|
|
238
|
+
// already knows from READ output and numbered Index entries. Used to
|
|
239
|
+
// give parse_error telemetry concrete locality: the model sees what
|
|
240
|
+
// it wrote on the offending line, not just an abstract error message.
|
|
241
|
+
//
|
|
242
|
+
// Lenient: targetLine ≤ 0 clamps to 1; targetLine beyond content
|
|
243
|
+
// returns whatever overlap exists; empty content returns "".
|
|
244
|
+
#extractSnippet(content, targetLine, windowLines) {
|
|
245
|
+
if (content.length === 0)
|
|
246
|
+
return "";
|
|
247
|
+
const lines = content.split("\n");
|
|
248
|
+
const target = Math.max(1, targetLine);
|
|
249
|
+
const start = Math.max(1, target - windowLines);
|
|
250
|
+
const end = Math.min(lines.length, target + windowLines);
|
|
251
|
+
const slice = [];
|
|
252
|
+
for (let i = start; i <= end; i++) {
|
|
253
|
+
slice.push(`${i}:\t${lines[i - 1] ?? ""}`);
|
|
254
|
+
}
|
|
255
|
+
return slice.join("\n");
|
|
256
|
+
}
|
|
257
|
+
async runLoop({ provider, messages, persona = "", requirements = "", sessionId, runId, loopId, maxTurns = 50, maxStrikes = readMaxStrikes(), minCycles = readPositiveInt("PLURNK_MIN_CYCLES", DEFAULT_MIN_CYCLES), maxCyclePeriod = readPositiveInt("PLURNK_MAX_CYCLE_PERIOD", DEFAULT_MAX_CYCLE_PERIOD), origin = "model", signal, onDispatch, }) {
|
|
218
258
|
const turnIds = [];
|
|
219
259
|
const suddenDeathThreshold = maxTurns - maxStrikes;
|
|
220
260
|
// Per-loop AbortController for scheme-side cancellation propagation.
|
|
@@ -261,7 +301,7 @@ export default class Engine {
|
|
|
261
301
|
return { turnIds, finalStatus: 499, hitMaxTurns: true, reason: "max_turns" };
|
|
262
302
|
}
|
|
263
303
|
const turn = await this.runTurn({
|
|
264
|
-
provider, messages, persona, sessionId, runId, loopId, origin, signal, onDispatch,
|
|
304
|
+
provider, messages, persona, requirements, sessionId, runId, loopId, origin, signal, onDispatch,
|
|
265
305
|
turnNumber: turnIds.length + 1, maxTurns,
|
|
266
306
|
});
|
|
267
307
|
turnIds.push(turn.turnId);
|
|
@@ -273,11 +313,10 @@ export default class Engine {
|
|
|
273
313
|
const cycle = detectCycle(state.history, minCycles, maxCyclePeriod);
|
|
274
314
|
if (cycle.detected) {
|
|
275
315
|
state.turnErrors++;
|
|
276
|
-
this.#pushTelemetry(loopId, {
|
|
316
|
+
this.#pushTelemetry(sessionId, loopId, {
|
|
277
317
|
kind: "cycle",
|
|
278
318
|
period: cycle.period,
|
|
279
319
|
cycles: cycle.cycles,
|
|
280
|
-
message: "Loop detected",
|
|
281
320
|
});
|
|
282
321
|
}
|
|
283
322
|
this.#strikeState.set(loopId, state);
|
|
@@ -293,12 +332,11 @@ export default class Engine {
|
|
|
293
332
|
if (struck) {
|
|
294
333
|
state.streak++;
|
|
295
334
|
const reason = noOps ? "no_ops" : recordedFailed ? "recorded_failure" : "rail";
|
|
296
|
-
this.#pushTelemetry(loopId, {
|
|
335
|
+
this.#pushTelemetry(sessionId, loopId, {
|
|
297
336
|
kind: "strike",
|
|
298
337
|
streak: state.streak,
|
|
299
338
|
maxStrikes,
|
|
300
339
|
reason,
|
|
301
|
-
message: `strike ${state.streak}/${maxStrikes} (${reason}); ${maxStrikes - state.streak} before abandonment`,
|
|
302
340
|
});
|
|
303
341
|
if (state.streak >= maxStrikes) {
|
|
304
342
|
await this.#db.engine_loop_cancel.run({ loop_id: loopId });
|
|
@@ -316,15 +354,14 @@ export default class Engine {
|
|
|
316
354
|
// each turn so the model can wrap up before the hard cancel.
|
|
317
355
|
// Soft: no strike, no loop-status change. SPEC §15.1.
|
|
318
356
|
if (turnIds.length >= suddenDeathThreshold && turnIds.length < maxTurns) {
|
|
319
|
-
this.#pushTelemetry(loopId, {
|
|
357
|
+
this.#pushTelemetry(sessionId, loopId, {
|
|
320
358
|
kind: "sudden_death",
|
|
321
|
-
message: `${turnIds.length} of ${maxTurns}`,
|
|
322
359
|
remaining: maxTurns - turnIds.length,
|
|
323
360
|
});
|
|
324
361
|
}
|
|
325
362
|
}
|
|
326
363
|
}
|
|
327
|
-
async runTurn({ provider, messages, persona = "", sessionId, runId, loopId, origin = "model", signal, onDispatch, turnNumber = 1, maxTurns = 50, }) {
|
|
364
|
+
async runTurn({ provider, messages, persona = "", requirements = "", sessionId, runId, loopId, origin = "model", signal, onDispatch, turnNumber = 1, maxTurns = 50, }) {
|
|
328
365
|
// === Turn-as-container model ===
|
|
329
366
|
//
|
|
330
367
|
// Turn rows are created at runTurn OPEN (status=102, placeholder
|
|
@@ -380,8 +417,8 @@ export default class Engine {
|
|
|
380
417
|
// queries log_entries scoped to the run — the prompt entry just
|
|
381
418
|
// written (if turn 1) is part of that query result.
|
|
382
419
|
const requestPacket = await this.#buildRequestPacket({
|
|
383
|
-
initialMessages: messages, persona, runId, loopId,
|
|
384
|
-
|
|
420
|
+
initialMessages: messages, persona, requirements, runId, loopId,
|
|
421
|
+
currentTurnSeq: seq, provider,
|
|
385
422
|
});
|
|
386
423
|
const modelMessages = this.#packetToWireMessages(requestPacket);
|
|
387
424
|
const response = await provider.generate({ messages: modelMessages, signal });
|
|
@@ -395,8 +432,23 @@ export default class Engine {
|
|
|
395
432
|
// body starting with `//` being interpreted as xpath) silently
|
|
396
433
|
// drop, the model sees zero ops dispatched, strike-rail fires,
|
|
397
434
|
// model has no feedback on WHY its emission didn't take effect.
|
|
398
|
-
|
|
399
|
-
|
|
435
|
+
//
|
|
436
|
+
// Envelope per @plurnk/plurnk-grammar 0.17.0 TelemetryEvent:
|
|
437
|
+
// { source, kind, message, position: { type: "content-offset", line, column } }
|
|
438
|
+
// Plus a `snippet` field (additionalProperties) carrying ±N lines
|
|
439
|
+
// of the assistant's own content around the error line. Without
|
|
440
|
+
// the snippet, the model sees "invalid xpath at 1:0" but can't
|
|
441
|
+
// connect that to what IT wrote — and tends to regenerate the
|
|
442
|
+
// same broken emission. See edit-todo demo for the canonical case.
|
|
443
|
+
for (const { message, line, column, source } of parseErrors ?? []) {
|
|
444
|
+
this.#pushTelemetry(sessionId, loopId, {
|
|
445
|
+
source: "grammar",
|
|
446
|
+
kind: "parse_error",
|
|
447
|
+
message,
|
|
448
|
+
position: { type: "content-offset", line, column },
|
|
449
|
+
snippet: this.#extractSnippet(packetAssistant.content, line, 2),
|
|
450
|
+
parserSource: source,
|
|
451
|
+
});
|
|
400
452
|
}
|
|
401
453
|
const opsCount = packetAssistant.ops.length;
|
|
402
454
|
const sendOp = packetAssistant.ops.findLast((op) => op.op === "SEND" && typeof op.signal === "number");
|
|
@@ -422,8 +474,19 @@ export default class Engine {
|
|
|
422
474
|
});
|
|
423
475
|
// Dispatch model ops starting at nextActionIndex (continues the
|
|
424
476
|
// turn's running counter after any pre-model writes).
|
|
477
|
+
//
|
|
478
|
+
// Max-commands cap: a single emission with more than `maxCommands`
|
|
479
|
+
// ops is the runaway-loop fingerprint observed in pathological cases
|
|
480
|
+
// (html-attrs demo: 635 ops in one turn). Cap dispatches at the
|
|
481
|
+
// configured limit; overflow ops are dropped without per-op log
|
|
482
|
+
// entries (avoids bloating forensics with hundreds of identical refusals)
|
|
483
|
+
// and the model gets a single telemetry signal next packet so it knows
|
|
484
|
+
// its emission was truncated.
|
|
485
|
+
const maxCommands = readMaxCommands();
|
|
486
|
+
const opsToDispatch = packetAssistant.ops.slice(0, maxCommands);
|
|
487
|
+
const droppedCount = opsCount - opsToDispatch.length;
|
|
425
488
|
const statuses = [];
|
|
426
|
-
for (const [i, statement] of
|
|
489
|
+
for (const [i, statement] of opsToDispatch.entries()) {
|
|
427
490
|
const result = await this.dispatch({
|
|
428
491
|
statement, sessionId, runId, loopId, turnId,
|
|
429
492
|
sequence: nextActionIndex + i,
|
|
@@ -431,14 +494,21 @@ export default class Engine {
|
|
|
431
494
|
});
|
|
432
495
|
statuses.push(result.status);
|
|
433
496
|
}
|
|
497
|
+
if (droppedCount > 0) {
|
|
498
|
+
this.#pushTelemetry(sessionId, loopId, {
|
|
499
|
+
kind: "max_commands_exceeded",
|
|
500
|
+
emitted: opsCount,
|
|
501
|
+
cap: maxCommands,
|
|
502
|
+
dropped: droppedCount,
|
|
503
|
+
});
|
|
504
|
+
}
|
|
434
505
|
if (opsCount === 0) {
|
|
435
506
|
// Rail #41 (revised): per-turn requirement is "emit at least one
|
|
436
507
|
// op." Zero ops = actionless failure. SEND specifically is not
|
|
437
508
|
// required — any of the 9 grammar ops satisfies. Pushed AFTER
|
|
438
509
|
// #buildPacket so this turn's drain doesn't consume it.
|
|
439
|
-
this.#pushTelemetry(loopId, {
|
|
510
|
+
this.#pushTelemetry(sessionId, loopId, {
|
|
440
511
|
kind: "no_ops",
|
|
441
|
-
message: "turn ended without emitting any op; emit at least one operation per turn",
|
|
442
512
|
});
|
|
443
513
|
}
|
|
444
514
|
return { turnId, status: turnStatus, statuses, fingerprint: fingerprintTurn(packetAssistant.ops) };
|
|
@@ -446,8 +516,9 @@ export default class Engine {
|
|
|
446
516
|
// Split the wire-level ProviderResponse into the two destinations:
|
|
447
517
|
// packet.assistant gets the model's emission (content, ops, reasoning);
|
|
448
518
|
// Turn columns get the call-metadata (usage, finishReason, model).
|
|
449
|
-
//
|
|
450
|
-
// owns the parse and the scraping rule, providers stay
|
|
519
|
+
// SPEC §2.1 / plurnk-providers#1: text-fragment scraping policy lives
|
|
520
|
+
// here — engine owns the parse and the scraping rule, providers stay
|
|
521
|
+
// grammar-unaware.
|
|
451
522
|
//
|
|
452
523
|
// Test-fixture escape hatch: the Mock provider may pre-supply `ops` on
|
|
453
524
|
// its assistant payload to skip the parse roundtrip. The wire Provider
|
|
@@ -458,6 +529,10 @@ export default class Engine {
|
|
|
458
529
|
const preParsedOps = assistant.ops;
|
|
459
530
|
const ops = [];
|
|
460
531
|
const textFragments = [];
|
|
532
|
+
// Full PlurnkParseError context (line/column/source) is preserved
|
|
533
|
+
// here so runTurn can build TelemetryEvent envelopes per the
|
|
534
|
+
// grammar 0.17.0 protocol — model needs position info to locate
|
|
535
|
+
// its own offending content on the next turn.
|
|
461
536
|
const parseErrors = [];
|
|
462
537
|
if (preParsedOps !== undefined) {
|
|
463
538
|
ops.push(...preParsedOps);
|
|
@@ -474,8 +549,13 @@ export default class Engine {
|
|
|
474
549
|
}
|
|
475
550
|
else if (item.kind === "error") {
|
|
476
551
|
const err = item.error;
|
|
477
|
-
|
|
478
|
-
|
|
552
|
+
if (err instanceof PlurnkParseError) {
|
|
553
|
+
parseErrors.push({ message: err.message, line: err.line, column: err.column, source: err.source });
|
|
554
|
+
}
|
|
555
|
+
else {
|
|
556
|
+
const msg = err?.message ?? "parse error";
|
|
557
|
+
parseErrors.push({ message: msg, line: 0, column: 0, source: "parser" });
|
|
558
|
+
}
|
|
479
559
|
}
|
|
480
560
|
}
|
|
481
561
|
}
|
|
@@ -493,10 +573,7 @@ export default class Engine {
|
|
|
493
573
|
// and §user) BEFORE the provider call. The same packet object is then
|
|
494
574
|
// completed with assistant + assistantRaw after the model responds, so
|
|
495
575
|
// the stored packet and the wire payload share one source of truth.
|
|
496
|
-
|
|
497
|
-
// a continuation turn"; the turn-N-of-M continuation marker rides on
|
|
498
|
-
// user.system_requirements (per-turn rules), NOT a mutated prompt.
|
|
499
|
-
async #buildRequestPacket({ initialMessages, persona: defaultPersona, runId, loopId, turnNumber, currentTurnSeq, maxTurns, provider, }) {
|
|
576
|
+
async #buildRequestPacket({ initialMessages, persona: defaultPersona, requirements, runId, loopId, currentTurnSeq, provider, }) {
|
|
500
577
|
const byRole = (role) => initialMessages.filter((m) => m.role === role).map((m) => m.content).join("\n\n");
|
|
501
578
|
const system_definition = byRole("system");
|
|
502
579
|
// user.prompt sources from the loop's most recent prompt entry first
|
|
@@ -516,13 +593,6 @@ export default class Engine {
|
|
|
516
593
|
const index = await this.#buildIndex(runId, loopId);
|
|
517
594
|
const log = await this.#buildLog(runId);
|
|
518
595
|
const telemetryErrors = await this.#buildTelemetryErrors(loopId, currentTurnSeq);
|
|
519
|
-
// Rummy AgentLoop.js #buildContinuationPrompt: literally
|
|
520
|
-
// `Turn ${turn}/${maxTurns}`. That's the whole string. The model
|
|
521
|
-
// can read the action log to see what it already did; it does
|
|
522
|
-
// not need editorial instructions from us about not repeating.
|
|
523
|
-
const system_requirements = turnNumber > 1
|
|
524
|
-
? `Turn ${turnNumber}/${maxTurns}`
|
|
525
|
-
: "";
|
|
526
596
|
// Per-section render-cost subtotals via provider's tokenizer.
|
|
527
597
|
// Engine approximates each section by tokenizing its serialized
|
|
528
598
|
// form — wire-payload tokens may differ slightly because chat-
|
|
@@ -533,13 +603,13 @@ export default class Engine {
|
|
|
533
603
|
provider.countTokens(JSON.stringify(index)) +
|
|
534
604
|
provider.countTokens(JSON.stringify(log));
|
|
535
605
|
// user.telemetry.budget — shimmed with section-aggregate table.
|
|
536
|
-
// Real per-scheme breakdown is
|
|
537
|
-
//
|
|
538
|
-
const budget = this.#renderBudgetShim(systemTokens, provider, prompt,
|
|
606
|
+
// Real per-scheme breakdown is the tokenomics chapter (SPEC.md §14.2);
|
|
607
|
+
// depends on provider.getContextSize and write-time per-row tokens.
|
|
608
|
+
const budget = this.#renderBudgetShim(systemTokens, provider, prompt, telemetryErrors);
|
|
539
609
|
const userTokens = provider.countTokens(prompt) +
|
|
540
|
-
provider.countTokens(system_requirements) +
|
|
541
610
|
provider.countTokens(JSON.stringify(telemetryErrors)) +
|
|
542
|
-
provider.countTokens(budget)
|
|
611
|
+
provider.countTokens(budget) +
|
|
612
|
+
provider.countTokens(requirements);
|
|
543
613
|
return {
|
|
544
614
|
system: {
|
|
545
615
|
tokens: systemTokens,
|
|
@@ -552,20 +622,19 @@ export default class Engine {
|
|
|
552
622
|
tokens: userTokens,
|
|
553
623
|
prompt,
|
|
554
624
|
telemetry: { budget, errors: telemetryErrors },
|
|
555
|
-
system_requirements,
|
|
625
|
+
system_requirements: requirements,
|
|
556
626
|
},
|
|
557
627
|
};
|
|
558
628
|
}
|
|
559
|
-
// user.telemetry.budget — SHIM.
|
|
560
|
-
//
|
|
561
|
-
//
|
|
562
|
-
// aggregate counts we already compute so the wire's
|
|
563
|
-
// Budget` section is non-empty for picking-apart purposes.
|
|
564
|
-
#renderBudgetShim(systemTokens, provider, prompt,
|
|
629
|
+
// user.telemetry.budget — SHIM. Real per-scheme breakdown +
|
|
630
|
+
// context-window "free/percent-of-total" is SPEC.md §14.2; depends on
|
|
631
|
+
// provider.getContextSize and write-time per-row tokens. Until then,
|
|
632
|
+
// render the section-aggregate counts we already compute so the wire's
|
|
633
|
+
// `# Plurnk System Budget` section is non-empty for picking-apart purposes.
|
|
634
|
+
#renderBudgetShim(systemTokens, provider, prompt, telemetryErrors) {
|
|
565
635
|
const userPromptTokens = provider.countTokens(prompt);
|
|
566
|
-
const userReqTokens = provider.countTokens(system_requirements);
|
|
567
636
|
const userErrTokens = provider.countTokens(JSON.stringify(telemetryErrors));
|
|
568
|
-
const userTokens = userPromptTokens +
|
|
637
|
+
const userTokens = userPromptTokens + userErrTokens;
|
|
569
638
|
const total = systemTokens + userTokens;
|
|
570
639
|
const pct = (n) => total === 0 ? "0.0%" : `${((n / total) * 100).toFixed(1)}%`;
|
|
571
640
|
return [
|
|
@@ -618,7 +687,7 @@ export default class Engine {
|
|
|
618
687
|
op: r.op,
|
|
619
688
|
target,
|
|
620
689
|
status: r.status_rx,
|
|
621
|
-
|
|
690
|
+
error: typeof parsedRx === "object" && parsedRx !== null && "error" in parsedRx
|
|
622
691
|
? parsedRx.error
|
|
623
692
|
: typeof parsedRx === "string" ? parsedRx : "",
|
|
624
693
|
};
|
|
@@ -738,7 +807,7 @@ export default class Engine {
|
|
|
738
807
|
result = denial;
|
|
739
808
|
}
|
|
740
809
|
else {
|
|
741
|
-
//
|
|
810
|
+
// SPEC §3.6 + plurnk-schemes#1: action-entry-as-outcome. Scheme-handler
|
|
742
811
|
// exceptions become the action-entry's outcome (status 500), not a
|
|
743
812
|
// thrown bubble. The log_entry is the durable record; engine never
|
|
744
813
|
// skips it. Logging failures (#writeLog throws) are NOT caught —
|
|
@@ -773,13 +842,13 @@ export default class Engine {
|
|
|
773
842
|
}
|
|
774
843
|
const logEntryId = await this.#writeLog({ statement, result, runId, loopId, turnId, sequence, origin });
|
|
775
844
|
onDispatch?.(logEntryId);
|
|
776
|
-
// Proposal lifecycle (
|
|
777
|
-
// returns status 202, the entry is written as state='proposed';
|
|
845
|
+
// Proposal lifecycle (SPEC.md §0.5 + §13.5 loop.resolve). When a
|
|
846
|
+
// scheme returns status 202, the entry is written as state='proposed';
|
|
778
847
|
// dispatch then PAUSES on a per-entry waiter until resolution
|
|
779
848
|
// arrives via Engine.resolveProposal (from the loop/resolve RPC,
|
|
780
|
-
// YOLO listener, or timeout
|
|
781
|
-
//
|
|
782
|
-
//
|
|
849
|
+
// YOLO listener, or timeout). The post-resolution status replaces
|
|
850
|
+
// 202 in the result the caller sees, so runTurn never branches on
|
|
851
|
+
// a pending state.
|
|
783
852
|
if (result.status === 202) {
|
|
784
853
|
// Register the resolution waiter SYNCHRONOUSLY before any await
|
|
785
854
|
// yields. A same-tick resolveProposal() (e.g. from a test that
|
|
@@ -1007,8 +1076,8 @@ export default class Engine {
|
|
|
1007
1076
|
});
|
|
1008
1077
|
return { status, outcome, body: resolution.body };
|
|
1009
1078
|
}
|
|
1010
|
-
//
|
|
1011
|
-
//
|
|
1079
|
+
// SPEC §3.6: engine rejects writes whose origin is outside the target
|
|
1080
|
+
// scheme's manifest.writableBy.
|
|
1012
1081
|
// - Read-side ops (READ, FIND, SHOW, HIDE) are not gated.
|
|
1013
1082
|
// - SEND broadcast (path=null) has no target scheme; not gated.
|
|
1014
1083
|
// - COPY: dst scheme writableBy applies.
|
|
@@ -1154,8 +1223,8 @@ export default class Engine {
|
|
|
1154
1223
|
return { status: 415, error: `mimetype mismatch on channel '${channelName}': ${channelData.mimetype} vs ${expectedMimetype}` };
|
|
1155
1224
|
}
|
|
1156
1225
|
}
|
|
1157
|
-
// `<L>` source range slicing per
|
|
1158
|
-
//
|
|
1226
|
+
// `<L>` source range slicing per SPEC.md §16.9 (symmetric with READ
|
|
1227
|
+
// `<L>` — source range, no line-number prefix).
|
|
1159
1228
|
// Applied to every channel of the source entry. Binary channels return
|
|
1160
1229
|
// 415 since line semantics don't apply.
|
|
1161
1230
|
const lineMarker = statement.lineMarker ?? null;
|
|
@@ -1225,9 +1294,36 @@ export default class Engine {
|
|
|
1225
1294
|
// consumes on accept. All other statuses are terminal — state =
|
|
1226
1295
|
// 'resolved' for the common case.
|
|
1227
1296
|
const isProposed = result.status === 202;
|
|
1228
|
-
|
|
1229
|
-
?
|
|
1230
|
-
:
|
|
1297
|
+
let attrsObj = (result.attrs !== undefined && result.attrs !== null)
|
|
1298
|
+
? { ...result.attrs }
|
|
1299
|
+
: {};
|
|
1300
|
+
// EXEC pathname is coordinate-based — the stream entry lives at
|
|
1301
|
+
// exec://<loop_seq>/<turn_seq>/<sequence>/EXEC, mirroring the log
|
|
1302
|
+
// row's own URI at log://<...>/EXEC. The model can correlate the
|
|
1303
|
+
// log meta to its stream output by structural parallelism — no
|
|
1304
|
+
// random slug injection. Coordinate is resolved here because both
|
|
1305
|
+
// log row write and applyResolution need it.
|
|
1306
|
+
if (statement.op === "EXEC") {
|
|
1307
|
+
const seqs = await this.#db.engine_loop_turn_seqs.get({
|
|
1308
|
+
loop_id: loopId, turn_id: turnId,
|
|
1309
|
+
});
|
|
1310
|
+
if (seqs === undefined)
|
|
1311
|
+
throw new Error(`Engine.#writeLog: loop_turn_seqs returned no row for loop=${loopId} turn=${turnId}`);
|
|
1312
|
+
const coordPathname = `${seqs.loop_seq}/${seqs.turn_seq}/${sequence}/EXEC`;
|
|
1313
|
+
target.scheme = "exec";
|
|
1314
|
+
target.pathname = coordPathname;
|
|
1315
|
+
attrsObj.pathname = coordPathname;
|
|
1316
|
+
// Mutate the in-memory result.attrs too: the dispatch path
|
|
1317
|
+
// hands originalResult.attrs to handler.applyResolution after
|
|
1318
|
+
// proposal accept (see #acceptResolution). Both views — the
|
|
1319
|
+
// stored row AND the in-memory proposal — need the same
|
|
1320
|
+
// coordinate-based pathname so applyResolution writes the
|
|
1321
|
+
// entry at exec://<coord>/EXEC.
|
|
1322
|
+
if (result.attrs !== undefined && result.attrs !== null) {
|
|
1323
|
+
result.attrs.pathname = coordPathname;
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
const attrs = JSON.stringify(attrsObj);
|
|
1231
1327
|
const row = await this.#db.engine_insert_log_entry.get({
|
|
1232
1328
|
run_id: runId,
|
|
1233
1329
|
loop_id: loopId,
|