@plurnk/plurnk-service 1.9.1 → 1.10.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.defaults +3 -5
- package/SPEC.md +69 -32
- package/dist/build-info.json +1 -1
- package/dist/core/BranchReceipt.d.ts.map +1 -1
- package/dist/core/BranchReceipt.js +9 -4
- package/dist/core/BranchReceipt.js.map +1 -1
- package/dist/core/CoreSchemeServices.d.ts +1 -0
- package/dist/core/CoreSchemeServices.d.ts.map +1 -1
- package/dist/core/CoreSchemeServices.js +1 -0
- package/dist/core/CoreSchemeServices.js.map +1 -1
- package/dist/core/Dispatcher.d.ts +2 -1
- package/dist/core/Dispatcher.d.ts.map +1 -1
- package/dist/core/Dispatcher.js +49 -13
- package/dist/core/Dispatcher.js.map +1 -1
- package/dist/core/Engine.d.ts +3 -1
- package/dist/core/Engine.d.ts.map +1 -1
- package/dist/core/Engine.js +18 -17
- package/dist/core/Engine.js.map +1 -1
- package/dist/core/Engine.sql +34 -10
- package/dist/core/LoopPacket.d.ts +3 -0
- package/dist/core/LoopPacket.d.ts.map +1 -0
- package/dist/core/LoopPacket.js +2 -0
- package/dist/core/LoopPacket.js.map +1 -0
- package/dist/core/NoticeChannel.d.ts.map +1 -1
- package/dist/core/NoticeChannel.js +14 -2
- package/dist/core/NoticeChannel.js.map +1 -1
- package/dist/core/OverflowTurn.d.ts.map +1 -1
- package/dist/core/OverflowTurn.js +0 -1
- package/dist/core/OverflowTurn.js.map +1 -1
- package/dist/core/PacketBuilder.d.ts.map +1 -1
- package/dist/core/PacketBuilder.js +9 -2
- package/dist/core/PacketBuilder.js.map +1 -1
- package/dist/core/ResourceMutations.d.ts.map +1 -1
- package/dist/core/ResourceMutations.js +21 -3
- package/dist/core/ResourceMutations.js.map +1 -1
- package/dist/core/TerminalResult.d.ts.map +1 -1
- package/dist/core/TerminalResult.js +4 -1
- package/dist/core/TerminalResult.js.map +1 -1
- package/dist/core/ToolResources.js +4 -4
- package/dist/core/ToolResources.js.map +1 -1
- package/dist/core/TurnOps.d.ts.map +1 -1
- package/dist/core/TurnOps.js +3 -0
- package/dist/core/TurnOps.js.map +1 -1
- package/dist/core/TurnRunner.d.ts +3 -1
- package/dist/core/TurnRunner.d.ts.map +1 -1
- package/dist/core/TurnRunner.js +153 -160
- package/dist/core/TurnRunner.js.map +1 -1
- package/dist/core/packet-wire.d.ts.map +1 -1
- package/dist/core/packet-wire.js +5 -1
- package/dist/core/packet-wire.js.map +1 -1
- package/dist/core/scheme-types.d.ts +1 -0
- package/dist/core/scheme-types.d.ts.map +1 -1
- package/dist/schemes/Exec.d.ts.map +1 -1
- package/dist/schemes/Exec.js +26 -16
- package/dist/schemes/Exec.js.map +1 -1
- package/dist/schemes/_entry-find.d.ts.map +1 -1
- package/dist/schemes/_entry-find.js +24 -4
- package/dist/schemes/_entry-find.js.map +1 -1
- package/dist/schemes/_entry-graph.js +1 -1
- package/dist/schemes/_entry-graph.js.map +1 -1
- package/dist/schemes/_entry-send.d.ts.map +1 -1
- package/dist/schemes/_entry-send.js +3 -1
- package/dist/schemes/_entry-send.js.map +1 -1
- package/dist/schemes/_search-index.d.ts +1 -1
- package/dist/schemes/_search-index.d.ts.map +1 -1
- package/dist/schemes/_search-index.js +39 -35
- package/dist/schemes/_search-index.js.map +1 -1
- package/dist/server/Daemon.d.ts.map +1 -1
- package/dist/server/Daemon.js +4 -0
- package/dist/server/Daemon.js.map +1 -1
- package/dist/server/branch-batch.sql +4 -2
- package/dist/server/dispatch-as-plurnk.js +1 -1
- package/dist/server/dispatch-as-plurnk.js.map +1 -1
- package/dist/server/seam-loop.sql +5 -1
- package/migrations/001_schema.sql +4 -1
- package/package.json +32 -32
package/dist/core/TurnRunner.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
var _a;
|
|
2
|
-
import {
|
|
2
|
+
import { PlurnkParser, PlurnkParseError, UNKNOWN_POSITION } from "@plurnk/plurnk-contracts";
|
|
3
3
|
const TERMINAL_SEND_SIGNALS = new Set([102, 200, 202, 499]);
|
|
4
4
|
const comparePosition = (a, b) => a.line - b.line || a.column - b.column;
|
|
5
5
|
import Meta from "@plurnk/plurnk-meta";
|
|
@@ -8,7 +8,8 @@ import GitMembership from "./git-membership.js";
|
|
|
8
8
|
import GitState from "./git-state.js";
|
|
9
9
|
import WorkspaceSettings from "./workspace-settings.js";
|
|
10
10
|
import { editedSpan } from "../content/index.js";
|
|
11
|
-
import
|
|
11
|
+
import ReadResolve from "../content/read-resolve.js";
|
|
12
|
+
import { authorityParts, generatedPathname, promptPathname, promptLoopPrefix } from "./plurnk-uri.js";
|
|
12
13
|
import { readFile } from "node:fs/promises";
|
|
13
14
|
// {§grammar-rail-registration} — bare names are the built-in rail namespace;
|
|
14
15
|
// everything else is an import specifier resolved through the Node chain.
|
|
@@ -43,18 +44,6 @@ import { readOptimisticSettlementMs } from "./optimistic-settlement.js";
|
|
|
43
44
|
import ModelCall, { ModelCallPersistenceError, ProviderAccountingIntegrityError, } from "./ModelCall.js";
|
|
44
45
|
import OverflowTurn from "./OverflowTurn.js";
|
|
45
46
|
import TurnOps from "./TurnOps.js";
|
|
46
|
-
const RECORD_STREAM_MIMETYPES = new Set(["application/jsonl", "application/x-ndjson"]);
|
|
47
|
-
const baseMimetype = (mimetype) => mimetype.split(";", 1)[0].trim().toLowerCase();
|
|
48
|
-
// {§exec-stream} Active streams publish only independently meaningful units.
|
|
49
|
-
// Atomic documents wait for close; JSONL stops after its last complete record.
|
|
50
|
-
const streamPublicationEnd = (content, mimetype, cursor, closed) => {
|
|
51
|
-
const type = baseMimetype(mimetype);
|
|
52
|
-
if (closed || type.startsWith("text/"))
|
|
53
|
-
return content.length;
|
|
54
|
-
if (!RECORD_STREAM_MIMETYPES.has(type))
|
|
55
|
-
return cursor;
|
|
56
|
-
return Math.max(cursor, content.lastIndexOf("\n") + 1);
|
|
57
|
-
};
|
|
58
47
|
const ENGINE_PROBLEMS = Object.freeze({
|
|
59
48
|
max_commands_exceeded: {
|
|
60
49
|
status: 429,
|
|
@@ -182,6 +171,7 @@ class TurnRunner {
|
|
|
182
171
|
#liveSubscriptions;
|
|
183
172
|
#streamEventNotify;
|
|
184
173
|
#reasoningEventNotify;
|
|
174
|
+
#loopPacketNotify;
|
|
185
175
|
#wakeWorkerNotify;
|
|
186
176
|
#executors;
|
|
187
177
|
#loopSignal;
|
|
@@ -190,7 +180,7 @@ class TurnRunner {
|
|
|
190
180
|
#dispatch;
|
|
191
181
|
#resolveWorkerProviderIdentity;
|
|
192
182
|
#gbnfCache = new Map();
|
|
193
|
-
constructor({ db, schemes, mimetypes, weigh, notices, problems, strikes, packets, dispatcher, liveSubscriptions, streamEventNotify, reasoningEventNotify, wakeWorkerNotify, executors, loopSignal, interactions, warmWorkspace, dispatch, resolveWorkerProviderIdentity, }) {
|
|
183
|
+
constructor({ db, schemes, mimetypes, weigh, notices, problems, strikes, packets, dispatcher, liveSubscriptions, streamEventNotify, reasoningEventNotify, loopPacketNotify, wakeWorkerNotify, executors, loopSignal, interactions, warmWorkspace, dispatch, resolveWorkerProviderIdentity, }) {
|
|
194
184
|
this.#db = db;
|
|
195
185
|
this.#schemes = schemes;
|
|
196
186
|
this.#mimetypes = mimetypes;
|
|
@@ -203,6 +193,7 @@ class TurnRunner {
|
|
|
203
193
|
this.#liveSubscriptions = liveSubscriptions;
|
|
204
194
|
this.#streamEventNotify = streamEventNotify;
|
|
205
195
|
this.#reasoningEventNotify = reasoningEventNotify;
|
|
196
|
+
this.#loopPacketNotify = loopPacketNotify;
|
|
206
197
|
this.#wakeWorkerNotify = wakeWorkerNotify;
|
|
207
198
|
this.#executors = executors;
|
|
208
199
|
this.#loopSignal = loopSignal;
|
|
@@ -211,6 +202,19 @@ class TurnRunner {
|
|
|
211
202
|
this.#dispatch = dispatch;
|
|
212
203
|
this.#resolveWorkerProviderIdentity = resolveWorkerProviderIdentity;
|
|
213
204
|
}
|
|
205
|
+
async #recordInference(args) {
|
|
206
|
+
await Turn.recordInference(this.#db, args.turnId, args.evidence);
|
|
207
|
+
if (this.#loopPacketNotify === undefined)
|
|
208
|
+
return;
|
|
209
|
+
const packetCount = await this.#db.engine_loop_packet_count.get({ loop_id: args.loopId });
|
|
210
|
+
if (packetCount === undefined)
|
|
211
|
+
throw new Error(`loop ${args.loopId}: packet count row missing`);
|
|
212
|
+
this.#loopPacketNotify(args.workspaceId, {
|
|
213
|
+
workerId: args.workerId,
|
|
214
|
+
loopId: args.loopId,
|
|
215
|
+
packetCount: packetCount.count,
|
|
216
|
+
});
|
|
217
|
+
}
|
|
214
218
|
// {§rail-truth-engine-verdict} — the verify GAP (a configured grammar @plurnk/gbnf can't
|
|
215
219
|
// parse): warn once per message, never per turn; the turn records railsVerdict "unverifiable".
|
|
216
220
|
static #railGapWarned = new Set();
|
|
@@ -653,6 +657,29 @@ class TurnRunner {
|
|
|
653
657
|
requestInteraction: (request) => this.#interactions.request(request, { workspaceId, workerId, loopId, turnId: contextTurnId }, this.#loopSignal(loopId)),
|
|
654
658
|
});
|
|
655
659
|
let systemCtx = systemContext(initializationTurn?.id ?? modelTurn.id);
|
|
660
|
+
// {§prompt-entry} — the prompt entry exists before any turn of the loop
|
|
661
|
+
// runs, so the initialization COPY archives a real source; its `prompt`
|
|
662
|
+
// log row is published to the model turn below (one durable publication
|
|
663
|
+
// per loop, decided by that row).
|
|
664
|
+
const promptPublication = turnNumber === 1 && loopRow?.prompt_published === 0
|
|
665
|
+
&& typeof loopRow.prompt === "string" && loopRow.prompt.length > 0
|
|
666
|
+
? {
|
|
667
|
+
content: loopRow.prompt,
|
|
668
|
+
source: loopRow.prompt_source,
|
|
669
|
+
path: promptTarget(loopRow.sequence, 1),
|
|
670
|
+
openPaths: assertOpenPaths(JSON.parse(loopRow.open_paths), `Loop ${loopId} open_paths`),
|
|
671
|
+
}
|
|
672
|
+
: null;
|
|
673
|
+
if (promptPublication !== null) {
|
|
674
|
+
const entry = {
|
|
675
|
+
channels: { body: { content: promptPublication.content, mimetype: "text/markdown" } },
|
|
676
|
+
attributes: {
|
|
677
|
+
openPaths: promptPublication.openPaths,
|
|
678
|
+
...(promptPublication.source === null ? {} : { source: promptPublication.source }),
|
|
679
|
+
},
|
|
680
|
+
};
|
|
681
|
+
await EntryCrud.writeEntry({ authority: "", pathname: promptPublication.path.pathname }, entry, systemCtx, "prompt", workerId);
|
|
682
|
+
}
|
|
656
683
|
const initializationStatements = [];
|
|
657
684
|
// {§worker-initialization-entry} — the worker's first turn is the worked
|
|
658
685
|
// example itself: an ordinary PLAN, the actual orienting operations,
|
|
@@ -663,12 +690,28 @@ class TurnRunner {
|
|
|
663
690
|
signal: null, target: null, lineMarker: null,
|
|
664
691
|
body: [{
|
|
665
692
|
content: "Discover the tooling available and survey the workspace file root.",
|
|
666
|
-
priority: "medium",
|
|
667
693
|
status: "in_progress",
|
|
668
694
|
}],
|
|
669
695
|
position: UNKNOWN_POSITION,
|
|
670
696
|
};
|
|
671
697
|
initializationStatements.push(plan);
|
|
698
|
+
// {§worker-initialization-entry} — the prompt is archived into the
|
|
699
|
+
// worker's private space: the worked COPY specimen, and the private
|
|
700
|
+
// space shown as scratch. An append onto an absent entry creates it.
|
|
701
|
+
// Authored first among the operations because it executes first
|
|
702
|
+
// ({§op-mode-phases}: Mutate precedes Observe).
|
|
703
|
+
if (promptPublication !== null) {
|
|
704
|
+
const archive = {
|
|
705
|
+
op: "COPY", delimiter: "", annotation: "append latest prompt", signal: ["+_plurnk", "+backup"],
|
|
706
|
+
target: promptPublication.path, lineMarker: null,
|
|
707
|
+
body: {
|
|
708
|
+
target: { kind: "url", raw: "worker://~/prompts.md", scheme: "worker", username: null, password: null, hostname: "~", port: null, pathname: "/prompts.md", query: null, fragment: null },
|
|
709
|
+
lineMarker: { marks: [-1] },
|
|
710
|
+
},
|
|
711
|
+
position: UNKNOWN_POSITION,
|
|
712
|
+
};
|
|
713
|
+
initializationStatements.push(archive);
|
|
714
|
+
}
|
|
672
715
|
// {§turn0-agents-stunt} — the project AGENTS.md (materialized by LoopDocs as
|
|
673
716
|
// worker://~/_plurnk/agents.md) gets one foisted READ on the worker's first
|
|
674
717
|
// loop, so local repo guidance is visible turn-0 content. Global policy
|
|
@@ -738,11 +781,11 @@ class TurnRunner {
|
|
|
738
781
|
const catalogSchemes = await this.#db.engine_scheme_catalog_summary.all({ workspace_id: workspaceId });
|
|
739
782
|
const fileItems = catalogSchemes.find(({ scheme }) => scheme === "file")?.shallow_items ?? 0;
|
|
740
783
|
const fileCap = filesItems > 0 && fileItems > 0 ? Math.min(filesItems, fileItems) : null;
|
|
741
|
-
// {§tools-resource-materialization} — PLURNK_MCP_EXPANDED
|
|
742
|
-
//
|
|
743
|
-
//
|
|
744
|
-
//
|
|
745
|
-
//
|
|
784
|
+
// {§tools-resource-materialization} — a PLURNK_MCP_EXPANDED server
|
|
785
|
+
// contributes one FIND over its family document matching the
|
|
786
|
+
// `## EXEC0` headings: one row per tool — heading, annotation,
|
|
787
|
+
// signature — paged like every survey. No document is delivered
|
|
788
|
+
// unasked; per-target child documents do not exist.
|
|
746
789
|
const registry = this.#executors();
|
|
747
790
|
const toolExpansions = [];
|
|
748
791
|
for (const tag of registry?.availableRuntimes(workerId) ?? []) {
|
|
@@ -751,9 +794,10 @@ class TurnRunner {
|
|
|
751
794
|
continue;
|
|
752
795
|
toolExpansions.push({
|
|
753
796
|
statement: {
|
|
754
|
-
op: "
|
|
797
|
+
op: "FIND", delimiter: "", annotation: `enabled tools: ${tag}`, signal: ["+_plurnk", "+init", "+tools"],
|
|
755
798
|
target: { kind: "url", raw: `worker://~/_plurnk/tools/${tag}.md`, scheme: "worker", username: null, password: null, hostname: "~", port: null, pathname: generatedPathname(`/tools/${tag}.md`), query: null, fragment: null },
|
|
756
|
-
body:
|
|
799
|
+
body: { dialect: "regex", raw: "/^## EXEC0 .*\\n.*$/m", pattern: "^## EXEC0 .*\\n.*$", flags: "m" },
|
|
800
|
+
lineMarker: null, position: UNKNOWN_POSITION,
|
|
757
801
|
},
|
|
758
802
|
});
|
|
759
803
|
}
|
|
@@ -864,32 +908,18 @@ class TurnRunner {
|
|
|
864
908
|
// Model operations continue the same turn sequence after these rows.
|
|
865
909
|
let nextActionIndex = 1;
|
|
866
910
|
const turnOpenPaths = [];
|
|
867
|
-
if (
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
};
|
|
880
|
-
await EntryCrud.writeEntry({ authority: "", pathname: promptPath.pathname }, entry, systemCtx, "prompt", workerId);
|
|
881
|
-
turnOpenPaths.push(...openPaths);
|
|
882
|
-
const promptLogId = await this.#writePromptLog({
|
|
883
|
-
workerId,
|
|
884
|
-
loopId,
|
|
885
|
-
turnId,
|
|
886
|
-
sequence: nextActionIndex++,
|
|
887
|
-
target: promptPath,
|
|
888
|
-
content: promptRow.prompt,
|
|
889
|
-
source: promptRow.prompt_source,
|
|
890
|
-
});
|
|
891
|
-
onDispatch?.(promptLogId);
|
|
892
|
-
}
|
|
911
|
+
if (promptPublication !== null) { // {§prompt-entry} — one durable publication per loop
|
|
912
|
+
turnOpenPaths.push(...promptPublication.openPaths);
|
|
913
|
+
const promptLogId = await this.#writePromptLog({
|
|
914
|
+
workerId,
|
|
915
|
+
loopId,
|
|
916
|
+
turnId,
|
|
917
|
+
sequence: nextActionIndex++,
|
|
918
|
+
target: promptPublication.path,
|
|
919
|
+
content: promptPublication.content,
|
|
920
|
+
source: promptPublication.source,
|
|
921
|
+
});
|
|
922
|
+
onDispatch?.(promptLogId);
|
|
893
923
|
}
|
|
894
924
|
// {§prompt-loop-containment}: the loop contains every prompt that arrived
|
|
895
925
|
// while it ran. Publish each undelivered frame oldest-first exactly once.
|
|
@@ -1270,12 +1300,15 @@ class TurnRunner {
|
|
|
1270
1300
|
// runLoop/Daemon settle the exact 504/499 loop result.
|
|
1271
1301
|
if (providerSignal?.aborted) {
|
|
1272
1302
|
const status = providerSignal.reason === LOOP_TIMEOUT_REASON ? 504 : 499;
|
|
1273
|
-
await
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1303
|
+
await this.#recordInference({
|
|
1304
|
+
workspaceId, workerId, loopId, turnId,
|
|
1305
|
+
evidence: {
|
|
1306
|
+
packet: StoredPacket.stringify(requestPacket),
|
|
1307
|
+
usageCurationBudget: this.#packets.curationBudgetFor(provider),
|
|
1308
|
+
finishReason: splitResponse?.callMetadata.finishReason ?? null,
|
|
1309
|
+
model: splitResponse?.callMetadata.model ?? provider.model,
|
|
1310
|
+
meta: JSON.stringify(response?.meta ?? {}),
|
|
1311
|
+
},
|
|
1279
1312
|
});
|
|
1280
1313
|
await Turn.complete(this.#db, turnId, status);
|
|
1281
1314
|
throw err;
|
|
@@ -1292,12 +1325,15 @@ class TurnRunner {
|
|
|
1292
1325
|
// The provider call was attempted, but no completed exchange exists.
|
|
1293
1326
|
// Persist the exact request half and failure status; omitting assistant
|
|
1294
1327
|
// is materially different from fabricating an empty model turn.
|
|
1295
|
-
await
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1328
|
+
await this.#recordInference({
|
|
1329
|
+
workspaceId, workerId, loopId, turnId,
|
|
1330
|
+
evidence: {
|
|
1331
|
+
packet: StoredPacket.stringify(requestPacket),
|
|
1332
|
+
usageCurationBudget: this.#packets.curationBudgetFor(provider),
|
|
1333
|
+
finishReason: splitResponse?.callMetadata.finishReason ?? null,
|
|
1334
|
+
model: splitResponse?.callMetadata.model ?? provider.model,
|
|
1335
|
+
meta: JSON.stringify(response?.meta ?? {}),
|
|
1336
|
+
},
|
|
1301
1337
|
});
|
|
1302
1338
|
await Turn.complete(this.#db, turnId, recorded.result.status);
|
|
1303
1339
|
if (capacityFailure) {
|
|
@@ -1338,20 +1374,31 @@ class TurnRunner {
|
|
|
1338
1374
|
? { reasoningItems: response.assistant.reasoningEncrypted }
|
|
1339
1375
|
: {}),
|
|
1340
1376
|
});
|
|
1377
|
+
// {§invalid-emission-attempts} — the informed turn carries the parser's own
|
|
1378
|
+
// diagnostic and position: the model sees WHY, not only that it was refused.
|
|
1379
|
+
const diagnostic = splitResponse.parseErrors[0];
|
|
1341
1380
|
this.#notices.push(workspaceId, loopId, {
|
|
1342
1381
|
source: "engine:grammar",
|
|
1343
1382
|
kind: "invalid_emission",
|
|
1344
1383
|
level: "error",
|
|
1345
|
-
message:
|
|
1384
|
+
message: diagnostic === undefined
|
|
1385
|
+
? INVALID_EMISSION_RECOVERY_MESSAGE
|
|
1386
|
+
: `${INVALID_EMISSION_RECOVERY_MESSAGE} Parser: ${diagnostic.message}`,
|
|
1387
|
+
...(diagnostic !== undefined && diagnostic.line > 0
|
|
1388
|
+
? { position: { type: "content-offset", line: diagnostic.line, column: diagnostic.column } }
|
|
1389
|
+
: {}),
|
|
1346
1390
|
});
|
|
1347
1391
|
}
|
|
1348
1392
|
const status = allowInvalidEmissionRecovery ? TURN_STATUS_IMPLICIT_CONTINUE : 500;
|
|
1349
|
-
await
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1393
|
+
await this.#recordInference({
|
|
1394
|
+
workspaceId, workerId, loopId, turnId,
|
|
1395
|
+
evidence: {
|
|
1396
|
+
packet: StoredPacket.stringify(requestPacket),
|
|
1397
|
+
usageCurationBudget: this.#packets.curationBudgetFor(provider),
|
|
1398
|
+
finishReason: splitResponse.callMetadata.finishReason,
|
|
1399
|
+
model: splitResponse.callMetadata.model,
|
|
1400
|
+
meta: JSON.stringify(response.meta ?? {}),
|
|
1401
|
+
},
|
|
1355
1402
|
});
|
|
1356
1403
|
await Turn.complete(this.#db, turnId, status);
|
|
1357
1404
|
return {
|
|
@@ -1436,14 +1483,17 @@ class TurnRunner {
|
|
|
1436
1483
|
// the producer-neutral admitted-turn executor settles every operation
|
|
1437
1484
|
// and its exact source artifact.
|
|
1438
1485
|
const packet = StoredPacket.admit(requestPacket, packetAssistant, response.assistantRaw);
|
|
1439
|
-
await
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1486
|
+
await this.#recordInference({
|
|
1487
|
+
workspaceId, workerId, loopId, turnId,
|
|
1488
|
+
evidence: {
|
|
1489
|
+
packet: StoredPacket.stringify(packet),
|
|
1490
|
+
usageCurationBudget: this.#packets.curationBudgetFor(provider), // {§tokenomics-client-gauge}
|
|
1491
|
+
finishReason: callMetadata.finishReason,
|
|
1492
|
+
model: callMetadata.model,
|
|
1493
|
+
// Opaque provider metadata plus engine-authored rail keys.
|
|
1494
|
+
// {§meta-passthrough}, {§rail-truth-engine-verdict}
|
|
1495
|
+
meta: JSON.stringify({ ...(response.meta ?? {}), ...(railKeys ?? {}) }),
|
|
1496
|
+
},
|
|
1447
1497
|
});
|
|
1448
1498
|
// {§operator-config-workspace-max-commands} — workspace maxCommands
|
|
1449
1499
|
// narrows the operator ceiling before the admitted program reaches the
|
|
@@ -1736,90 +1786,33 @@ class TurnRunner {
|
|
|
1736
1786
|
&& ch.channel === this.#schemes.defaultChannelFor(ch.runtime, workerId)
|
|
1737
1787
|
? null
|
|
1738
1788
|
: ch.channel;
|
|
1739
|
-
const streamBase = renderAddress({
|
|
1740
|
-
scheme: ch.runtime,
|
|
1741
|
-
authority: ch.authority,
|
|
1742
|
-
pathname: ch.coord,
|
|
1743
|
-
});
|
|
1744
|
-
const streamTarget = visibleFragment === null
|
|
1745
|
-
? streamBase
|
|
1746
|
-
: `${streamBase}#${PathSyntax.escapeTarget(visibleFragment)}`;
|
|
1747
1789
|
const targetParts = authorityParts(ch.authority);
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
const terminalResult = async (fields, sequence) => {
|
|
1754
|
-
if (terminal === null)
|
|
1755
|
-
throw new Error(`closed subscription ${ch.subscription_id} has no terminal result`);
|
|
1756
|
-
const result = Results.assert({
|
|
1757
|
-
...terminal,
|
|
1758
|
-
...(terminal.problem === undefined ? {} : { problem: { ...terminal.problem } }),
|
|
1759
|
-
...fields,
|
|
1760
|
-
});
|
|
1761
|
-
if (result.problem !== undefined) {
|
|
1762
|
-
const seqs = await this.#db.engine_loop_turn_seqs.get({
|
|
1763
|
-
loop_id: loopId,
|
|
1764
|
-
turn_id: turnId,
|
|
1765
|
-
});
|
|
1766
|
-
if (seqs === undefined)
|
|
1767
|
-
throw new Error(`stream delta has no log coordinate for loop=${loopId} turn=${turnId}`);
|
|
1768
|
-
Results.attachInstance(result, `log:///${seqs.loop_seq}/${seqs.turn_seq}/${sequence}/READ`);
|
|
1769
|
-
}
|
|
1770
|
-
return result;
|
|
1771
|
-
};
|
|
1772
|
-
const publishEnd = streamPublicationEnd(ch.content, ch.mimetype, cursor, closed);
|
|
1773
|
-
if (publishEnd <= cursor) {
|
|
1774
|
-
// The cursor-terminal race: a channel written in one final
|
|
1775
|
-
// burst gets fully shown FOLDED while still active; the close then has zero new
|
|
1776
|
-
// content and the auto-OPEN terminal observation never fired — the model was never shown
|
|
1777
|
-
// the conclusion of a stream whose result it already holds folded. The same
|
|
1778
|
-
// observation is required when the channel produced no publishable content:
|
|
1779
|
-
// completion is information independently of payload. A text stream that delivered
|
|
1780
|
-
// content retains its terse revisit marker; an empty text stream and every structured
|
|
1781
|
-
// channel emit a bodyless typed conclusion.
|
|
1782
|
-
if (terminal !== null) {
|
|
1783
|
-
const sequence = fromSequence + written;
|
|
1784
|
-
const content = cursor > 0 && baseMimetype(ch.mimetype).startsWith("text/")
|
|
1785
|
-
? `[ stream closed (${terminal.status}) - full output already delivered above; READ ${streamTarget} to revisit ]`
|
|
1786
|
-
: "";
|
|
1787
|
-
const rx = JSON.stringify(await terminalResult({
|
|
1788
|
-
content,
|
|
1789
|
-
mimetype: ch.mimetype,
|
|
1790
|
-
}, sequence));
|
|
1791
|
-
await this.#db.engine_insert_stream_delta.run({
|
|
1792
|
-
worker_id: workerId, loop_id: loopId, turn_id: turnId, sequence,
|
|
1793
|
-
subscription_publication_id: ch.publication_id,
|
|
1794
|
-
scheme: ch.runtime, hostname: targetParts.hostname, port: targetParts.port,
|
|
1795
|
-
pathname: ch.coord, fragment: visibleFragment,
|
|
1796
|
-
rx,
|
|
1797
|
-
weight: LogBody.weight({
|
|
1798
|
-
op: "READ",
|
|
1799
|
-
attrs: {},
|
|
1800
|
-
tx: "",
|
|
1801
|
-
rx,
|
|
1802
|
-
mimetypeTx: "text/plain",
|
|
1803
|
-
mimetypeRx: "application/json",
|
|
1804
|
-
}, this.#weighContent),
|
|
1805
|
-
status: terminal?.status ?? 200,
|
|
1806
|
-
attrs: JSON.stringify({ streamEnd: ch.content.length, terminal: true }),
|
|
1807
|
-
folded: LogVisibility.serialize(LogVisibility.OPEN),
|
|
1808
|
-
});
|
|
1809
|
-
written++;
|
|
1810
|
-
}
|
|
1790
|
+
// {§exec-stream} — nothing publishes while a stream is active: the Child Streams
|
|
1791
|
+
// section reports its size and growth ({§child-orientation}); the model READs any
|
|
1792
|
+
// range it wants. At close, ONE foisted READ that is exactly a markerless READ —
|
|
1793
|
+
// the first page, the extent, the terminal status and Problem — born OPEN. {§exec-stream-page}
|
|
1794
|
+
if (ch.state !== "closed" && ch.state !== "errored")
|
|
1811
1795
|
continue;
|
|
1812
|
-
|
|
1813
|
-
// startLine continues the line count across turns: a multi-turn stream's deltas number
|
|
1814
|
-
// into one sequence (lines N..M, then M+1..), not N independent "1:" restarts. {§exec-stream}
|
|
1815
|
-
const startLine = (ch.content.slice(0, cursor).match(/\n/g)?.length ?? 0) + 1;
|
|
1796
|
+
const terminal = Results.assert(JSON.parse(ch.producer_result ?? "null"));
|
|
1816
1797
|
const sequence = fromSequence + written;
|
|
1817
|
-
const
|
|
1818
|
-
const
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
:
|
|
1798
|
+
const page = await ReadResolve.resolve({ content: ch.content, mimetype: ch.mimetype, lineMarker: null });
|
|
1799
|
+
const result = Results.assert({
|
|
1800
|
+
...terminal,
|
|
1801
|
+
...(terminal.problem === undefined ? {} : { problem: { ...terminal.problem } }),
|
|
1802
|
+
content: page.content ?? "",
|
|
1803
|
+
mimetype: page.mimetype,
|
|
1804
|
+
...(page.startLine === undefined || page.startLine === null ? {} : { startLine: page.startLine }),
|
|
1805
|
+
...(page.range === undefined ? {} : { range: page.range }),
|
|
1806
|
+
});
|
|
1807
|
+
if (result.problem !== undefined) {
|
|
1808
|
+
const seqs = await this.#db.engine_loop_turn_seqs.get({
|
|
1809
|
+
loop_id: loopId,
|
|
1810
|
+
turn_id: turnId,
|
|
1811
|
+
});
|
|
1812
|
+
if (seqs === undefined)
|
|
1813
|
+
throw new Error(`stream delta has no log coordinate for loop=${loopId} turn=${turnId}`);
|
|
1814
|
+
Results.attachInstance(result, `log:///${seqs.loop_seq}/${seqs.turn_seq}/${sequence}/READ`);
|
|
1815
|
+
}
|
|
1823
1816
|
const rx = JSON.stringify(result);
|
|
1824
1817
|
await this.#db.engine_insert_stream_delta.run({
|
|
1825
1818
|
worker_id: workerId, loop_id: loopId, turn_id: turnId, sequence,
|
|
@@ -1835,9 +1828,9 @@ class TurnRunner {
|
|
|
1835
1828
|
mimetypeTx: "text/plain",
|
|
1836
1829
|
mimetypeRx: "application/json",
|
|
1837
1830
|
}, this.#weighContent),
|
|
1838
|
-
status:
|
|
1839
|
-
attrs: JSON.stringify({ streamEnd:
|
|
1840
|
-
folded: LogVisibility.serialize(
|
|
1831
|
+
status: terminal.status,
|
|
1832
|
+
attrs: JSON.stringify({ streamEnd: ch.content.length, terminal: true }),
|
|
1833
|
+
folded: LogVisibility.serialize(LogVisibility.OPEN), // {§exec-stream} — the conclusion is born OPEN
|
|
1841
1834
|
});
|
|
1842
1835
|
written++;
|
|
1843
1836
|
}
|