@plurnk/plurnk-service 1.27.0 → 1.29.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 +17 -19
- package/SPEC.md +105 -149
- package/dist/build-info.json +1 -1
- package/dist/content/body-preview.d.ts +5 -0
- package/dist/content/body-preview.d.ts.map +1 -1
- package/dist/content/body-preview.js +16 -0
- package/dist/content/body-preview.js.map +1 -1
- package/dist/content/line-marker.d.ts +1 -0
- package/dist/content/line-marker.d.ts.map +1 -1
- package/dist/content/line-marker.js +5 -0
- package/dist/content/line-marker.js.map +1 -1
- package/dist/content/read-projector.js +3 -3
- package/dist/content/read-projector.js.map +1 -1
- package/dist/content/read-resolve.d.ts.map +1 -1
- package/dist/content/read-resolve.js +7 -4
- package/dist/content/read-resolve.js.map +1 -1
- package/dist/core/AdmittedTurnExecutor.d.ts +3 -1
- package/dist/core/AdmittedTurnExecutor.d.ts.map +1 -1
- package/dist/core/AdmittedTurnExecutor.js +19 -4
- package/dist/core/AdmittedTurnExecutor.js.map +1 -1
- package/dist/core/BudgetReadout.d.ts +4 -4
- package/dist/core/BudgetReadout.d.ts.map +1 -1
- package/dist/core/BudgetReadout.js +26 -41
- package/dist/core/BudgetReadout.js.map +1 -1
- package/dist/core/ChannelWrite.d.ts +5 -0
- package/dist/core/ChannelWrite.d.ts.map +1 -1
- package/dist/core/ChannelWrite.js +5 -0
- package/dist/core/ChannelWrite.js.map +1 -1
- package/dist/core/ChannelWrite.sql +10 -0
- package/dist/core/ContextFit.d.ts +26 -0
- package/dist/core/ContextFit.d.ts.map +1 -0
- package/dist/core/ContextFit.js +56 -0
- package/dist/core/ContextFit.js.map +1 -0
- package/dist/core/DataStatementRunner.d.ts.map +1 -1
- package/dist/core/DataStatementRunner.js +8 -3
- package/dist/core/DataStatementRunner.js.map +1 -1
- package/dist/core/Dispatcher.d.ts +13 -0
- package/dist/core/Dispatcher.d.ts.map +1 -1
- package/dist/core/Dispatcher.js +155 -5
- package/dist/core/Dispatcher.js.map +1 -1
- package/dist/core/EmissionHead.d.ts +8 -0
- package/dist/core/EmissionHead.d.ts.map +1 -0
- package/dist/core/EmissionHead.js +29 -0
- package/dist/core/EmissionHead.js.map +1 -0
- package/dist/core/KillHandler.d.ts.map +1 -1
- package/dist/core/KillHandler.js +9 -3
- package/dist/core/KillHandler.js.map +1 -1
- package/dist/core/LogEntryProjection.d.ts +1 -0
- package/dist/core/LogEntryProjection.d.ts.map +1 -1
- package/dist/core/LogEntryProjection.js +9 -0
- package/dist/core/LogEntryProjection.js.map +1 -1
- package/dist/core/LogWriter.d.ts +42 -0
- package/dist/core/LogWriter.d.ts.map +1 -1
- package/dist/core/LogWriter.js +11 -5
- package/dist/core/LogWriter.js.map +1 -1
- package/dist/core/PacketBuilder.d.ts +17 -4
- package/dist/core/PacketBuilder.d.ts.map +1 -1
- package/dist/core/PacketBuilder.js +76 -117
- package/dist/core/PacketBuilder.js.map +1 -1
- package/dist/core/PacketBuilder.sql +0 -7
- package/dist/core/ResourceSelector.d.ts.map +1 -1
- package/dist/core/ResourceSelector.js +4 -2
- package/dist/core/ResourceSelector.js.map +1 -1
- package/dist/core/ResourceTransfers.d.ts.map +1 -1
- package/dist/core/ResourceTransfers.js +2 -2
- package/dist/core/ResourceTransfers.js.map +1 -1
- package/dist/core/StrikeRail.d.ts.map +1 -1
- package/dist/core/StrikeRail.js +5 -1
- package/dist/core/StrikeRail.js.map +1 -1
- package/dist/core/TurnMaterialization.d.ts +6 -2
- package/dist/core/TurnMaterialization.d.ts.map +1 -1
- package/dist/core/TurnMaterialization.js +64 -16
- package/dist/core/TurnMaterialization.js.map +1 -1
- package/dist/core/TurnOps.d.ts.map +1 -1
- package/dist/core/TurnOps.js +7 -7
- package/dist/core/TurnOps.js.map +1 -1
- package/dist/core/TurnRunner.d.ts +1 -0
- package/dist/core/TurnRunner.d.ts.map +1 -1
- package/dist/core/TurnRunner.js +141 -139
- package/dist/core/TurnRunner.js.map +1 -1
- package/dist/core/TurnRunner.sql +2 -2
- package/dist/core/WorkerControlHandler.d.ts.map +1 -1
- package/dist/core/WorkerControlHandler.js +5 -1
- package/dist/core/WorkerControlHandler.js.map +1 -1
- package/dist/core/env-catalog.js +2 -2
- package/dist/core/env-catalog.js.map +1 -1
- package/dist/core/fork.sql +4 -10
- package/dist/core/git-env.d.ts +1 -1
- package/dist/core/git-env.d.ts.map +1 -1
- package/dist/core/git-env.js +19 -12
- package/dist/core/git-env.js.map +1 -1
- package/dist/core/git-membership.d.ts.map +1 -1
- package/dist/core/git-membership.js +11 -5
- package/dist/core/git-membership.js.map +1 -1
- package/dist/core/git-state.js +1 -1
- package/dist/core/git-state.js.map +1 -1
- package/dist/core/operation-target-groups.d.ts +1 -1
- package/dist/core/operation-target-groups.d.ts.map +1 -1
- package/dist/core/operation-target-groups.js +27 -5
- package/dist/core/operation-target-groups.js.map +1 -1
- package/dist/core/packet-wire.d.ts +80 -10
- package/dist/core/packet-wire.d.ts.map +1 -1
- package/dist/core/packet-wire.js +181 -226
- package/dist/core/packet-wire.js.map +1 -1
- package/dist/core/plurnk-uri.d.ts +7 -0
- package/dist/core/plurnk-uri.d.ts.map +1 -1
- package/dist/core/plurnk-uri.js +21 -0
- package/dist/core/plurnk-uri.js.map +1 -1
- package/dist/core/unregistered-scheme.d.ts +4 -0
- package/dist/core/unregistered-scheme.d.ts.map +1 -0
- package/dist/core/unregistered-scheme.js +14 -0
- package/dist/core/unregistered-scheme.js.map +1 -0
- package/dist/digest/Digest.d.ts.map +1 -1
- package/dist/digest/Digest.js +3 -2
- package/dist/digest/Digest.js.map +1 -1
- package/dist/digest/DigestRender.d.ts.map +1 -1
- package/dist/digest/DigestRender.js +88 -42
- package/dist/digest/DigestRender.js.map +1 -1
- package/dist/digest/digest-rows.d.ts +7 -0
- package/dist/digest/digest-rows.d.ts.map +1 -1
- package/dist/digest/digest-rows.js.map +1 -1
- package/dist/digest/digest.sql +15 -2
- package/dist/schemes/Exec.d.ts.map +1 -1
- package/dist/schemes/Exec.js +14 -3
- package/dist/schemes/Exec.js.map +1 -1
- package/dist/schemes/File.d.ts.map +1 -1
- package/dist/schemes/File.js +10 -5
- package/dist/schemes/File.js.map +1 -1
- package/dist/schemes/Log.d.ts.map +1 -1
- package/dist/schemes/Log.js +46 -5
- package/dist/schemes/Log.js.map +1 -1
- package/dist/schemes/_entry-crud.js +9 -9
- package/dist/schemes/_entry-crud.js.map +1 -1
- package/dist/schemes/_entry-find.d.ts.map +1 -1
- package/dist/schemes/_entry-find.js +5 -3
- package/dist/schemes/_entry-find.js.map +1 -1
- package/dist/schemes/_entry-fts.d.ts.map +1 -1
- package/dist/schemes/_entry-fts.js +7 -3
- package/dist/schemes/_entry-fts.js.map +1 -1
- package/dist/schemes/_entry-manifest.d.ts.map +1 -1
- package/dist/schemes/_entry-manifest.js +2 -4
- package/dist/schemes/_entry-manifest.js.map +1 -1
- package/dist/schemes/_entry-ops.js +4 -4
- package/dist/schemes/_entry-ops.js.map +1 -1
- package/dist/server/Daemon.d.ts.map +1 -1
- package/dist/server/Daemon.js +11 -2
- package/dist/server/Daemon.js.map +1 -1
- package/migrations/013_graph_regions.sql +1 -0
- package/migrations/014_stream_timing.sql +1 -0
- package/migrations/015_request_evidence.sql +1 -0
- package/package.json +34 -34
- package/dist/core/KnownToxins.d.ts +0 -4
- package/dist/core/KnownToxins.d.ts.map +0 -1
- package/dist/core/KnownToxins.js +0 -22
- package/dist/core/KnownToxins.js.map +0 -1
- package/dist/core/ReasoningView.d.ts +0 -12
- package/dist/core/ReasoningView.d.ts.map +0 -1
- package/dist/core/ReasoningView.js +0 -40
- package/dist/core/ReasoningView.js.map +0 -1
package/dist/core/TurnRunner.js
CHANGED
|
@@ -31,10 +31,11 @@ export const resolveOperatorGrammarPath = (value) => {
|
|
|
31
31
|
// projection and digest projection are structurally one function — no
|
|
32
32
|
// drift between wire and digest possible.
|
|
33
33
|
import PacketWire from "./packet-wire.js";
|
|
34
|
-
import
|
|
34
|
+
import { RECEIPT_RESERVE, reserved } from "./ContextFit.js";
|
|
35
35
|
import Results, { OperationFailureError } from "./results.js";
|
|
36
36
|
import Turn from "./Turn.js";
|
|
37
37
|
import ProviderRecovery from "./ProviderRecovery.js";
|
|
38
|
+
import PacketBuilder from "./PacketBuilder.js";
|
|
38
39
|
import StoredPacket from "./StoredPacket.js";
|
|
39
40
|
import { observed, observedSync } from "../observe/spans.js";
|
|
40
41
|
import { genAiRequestName, genAiRequestOptions, settleGenAiResponse } from "../observe/genai.js";
|
|
@@ -93,15 +94,16 @@ import { ProviderError, scopeEnvToAlias } from "@plurnk/plurnk-providers";
|
|
|
93
94
|
import ProviderInstantiate from "./ProviderInstantiate.js";
|
|
94
95
|
import TurnMaterialization from "./TurnMaterialization.js";
|
|
95
96
|
import BareBatchRunner from "./BareBatchRunner.js";
|
|
96
|
-
import KnownToxins from "./KnownToxins.js";
|
|
97
97
|
import { TURN_STATUS_IMPLICIT_CONTINUE } from "./turn-signals.js";
|
|
98
98
|
import AdmittedTurnExecutor from "./AdmittedTurnExecutor.js";
|
|
99
|
-
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
})
|
|
99
|
+
// {§context-wall} — the window's one terminal: the packet cannot fit the model's window even as receipts.
|
|
100
|
+
const WINDOW_OVERFLOW_DETAIL = "Context window overflow: the packet cannot fit the model's window even as receipts.";
|
|
101
|
+
const windowOverflowFailure = (overflow) => Results.failure("engine:context", "window-overflow", 413, WINDOW_OVERFLOW_DETAIL, {}, { tokens: overflow.tokens, budget: overflow.budget, wall: overflow.wall, excess: overflow.excess });
|
|
102
|
+
// {§context-over-budget-row} — over budget is a row and a request: the row is how the model hears it.
|
|
103
|
+
// {§context-over-budget-row} — the row is the mandate; the gauge is the one home for the numbers
|
|
104
|
+
// ({§context-gauge}), which the row's own charge would otherwise put out of agreement.
|
|
105
|
+
const OVER_BUDGET_DETAIL = "Context exceeds budget. YOU MUST ONLY KILL, MOVE or NOTE this turn.";
|
|
106
|
+
const overBudgetFailure = () => Results.failure("engine:context", "packet-exceeds-budget", 413, OVER_BUDGET_DETAIL);
|
|
105
107
|
const INVALID_EMISSION_RECOVERY_MESSAGE = "Response rejected before dispatch; no operations were performed.";
|
|
106
108
|
// {§output-allowance-notice} — THE single derivation of a ceiling cut: a
|
|
107
109
|
// `length` finish is the engine's own allowance ending the emission, and it
|
|
@@ -298,14 +300,18 @@ class TurnRunner {
|
|
|
298
300
|
const createdTurnIds = [];
|
|
299
301
|
const turnSignal = this.#loopSignal(loopId) ?? signal;
|
|
300
302
|
try {
|
|
303
|
+
// {§configuration-repair-path} — a retired packet knob refuses inference before any turn opens.
|
|
304
|
+
PacketBuilder.assertConfiguration();
|
|
301
305
|
const container = await this.#openTurnContainer(args, createdTurnIds);
|
|
302
306
|
const gitStatus = await this.#deriveWorkspace(args, container.systemCtx);
|
|
303
307
|
if (container.initializationTurn !== null)
|
|
304
|
-
await this.#runInitializationTurn(args, container, container.initializationTurn);
|
|
305
|
-
const
|
|
306
|
-
const
|
|
307
|
-
|
|
308
|
-
|
|
308
|
+
await this.#runInitializationTurn(args, container, container.initializationTurn, gitStatus);
|
|
309
|
+
const composed = await this.#composeRequest(args, container, gitStatus);
|
|
310
|
+
const admitted = await this.#admitPacket(args, composed);
|
|
311
|
+
// {§context-wall} — only the window ends a loop.
|
|
312
|
+
if (admitted.overflow !== null)
|
|
313
|
+
return await this.#failCuration(admitted.request, admitted.overflow);
|
|
314
|
+
const request = admitted.request;
|
|
309
315
|
const attempts = await this.#prepareProviderAttempts(args, request);
|
|
310
316
|
let emission;
|
|
311
317
|
try {
|
|
@@ -386,16 +392,12 @@ class TurnRunner {
|
|
|
386
392
|
const systemCtx = this.#schemeContext(args, initializationTurn?.id ?? modelTurn.id);
|
|
387
393
|
const initializationStatements = [];
|
|
388
394
|
// {§worker-initialization-entry} — the worker's first turn is the worked
|
|
389
|
-
// example itself: the actual orienting operations
|
|
395
|
+
// example itself: the actual orienting operations.
|
|
390
396
|
// {§turn0-agents-stunt} — the project AGENTS.md (materialized by LoopDocs as
|
|
391
397
|
// worker:///_plurnk/AGENTS.md) gets one foisted READ on the worker's first
|
|
392
398
|
// loop, so local repo guidance is visible turn-0 content. Global policy
|
|
393
399
|
// stays in the system prompt; nothing else is force-read.
|
|
394
400
|
if (initializationTurn !== null) {
|
|
395
|
-
initializationStatements.push({
|
|
396
|
-
op: "NOTE", aside: null, metadata: null, target: null, lineMarker: null,
|
|
397
|
-
body: `This turn surveys tooling and environment. The log records results; reasoning://${workerName}/${loopSequence}/${initializationTurn.sequence} contains the submitted OPs.`, position: UNKNOWN_POSITION,
|
|
398
|
-
});
|
|
399
401
|
const agentsEntry = await this.#db.crud_find_workspace_entry.get({
|
|
400
402
|
workspace_id: workspaceId,
|
|
401
403
|
scheme: "worker",
|
|
@@ -474,13 +476,13 @@ class TurnRunner {
|
|
|
474
476
|
// {§worker-initialization-entry} — the worker's first turn is the worked example
|
|
475
477
|
// itself: the actual orienting operations and ordinary NOTEs, executed as a
|
|
476
478
|
// complete turn before the model boundary.
|
|
477
|
-
async #runInitializationTurn(args, container, initializationTurn) {
|
|
478
|
-
const {
|
|
479
|
-
const {
|
|
479
|
+
async #runInitializationTurn(args, container, initializationTurn, gitStatus) {
|
|
480
|
+
const { workspaceId, workerId, loopId, onDispatch, onSettled } = args;
|
|
481
|
+
const { initializationStatements, initializationPolicies } = container;
|
|
480
482
|
// Turn-0 catalog preview (PLURNK_SERVICE_FILES_ITEMS, {§actor-boundary-catalog-preview}):
|
|
481
|
-
//
|
|
483
|
+
// Nine bodyless FIND surveys in the worker's packetless initialization turn establish the Agent
|
|
482
484
|
// Skills, the plurnk references, the enabled tools, agents, and members, then the project, commons,
|
|
483
|
-
//
|
|
485
|
+
// named scratch, and the worker's own reasoning, in that order.
|
|
484
486
|
// Their `init` classification lets the model curate this opening survey as one log set.
|
|
485
487
|
// {§operator-config-workspace-files-items} — workspace filesItems replaces the env default.
|
|
486
488
|
const { filesItems: workspaceMI } = await WorkspaceSettings.read(this.#db, workspaceId);
|
|
@@ -488,20 +490,23 @@ class TurnRunner {
|
|
|
488
490
|
if (filesItems !== null) { // {§actor-boundary-catalog-preview} — once per worker
|
|
489
491
|
initializationStatements.push(...await this.#catalogSurveys(args, container, filesItems));
|
|
490
492
|
}
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
// {§worker-initialization-entry} — publish the complete source before its own READ executes.
|
|
495
|
-
// {§emission-row} — reasoning-only work has receipts, never a fabricated content emission.
|
|
493
|
+
// {§worker-initialization-entry} — the survey is an ordinary operation program: recorded as the
|
|
494
|
+
// turn's ops source and executed through the same path as a model's program; it authors no reasoning.
|
|
495
|
+
// {§emission-row} — a `_plurnk` turn has receipts, never a fabricated assistant emission.
|
|
496
496
|
// {§message-arrival} — the message reaches the model as an inbound SEND in the first
|
|
497
497
|
// model turn; initialization does not READ it a second time.
|
|
498
498
|
const admittedInitializationStatements = initializationStatements.filter((statement) => this.#capabilities.allowsAcross(statement, workspaceId, initializationPolicies));
|
|
499
|
-
|
|
500
|
-
|
|
499
|
+
if (admittedInitializationStatements.length === 0) {
|
|
500
|
+
// Nothing to survey (the preview off, no project guidance): the turn opens and completes empty.
|
|
501
|
+
await Turn.complete(this.#db, initializationTurn.id, 200);
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
const program = TurnOps.renderInternal(admittedInitializationStatements);
|
|
505
|
+
const admitted = PlurnkParser.parseStatements(program).items.flatMap((item) => item.kind === "statement" ? [item.statement] : []);
|
|
501
506
|
if (admitted.length !== admittedInitializationStatements.length) {
|
|
502
|
-
throw new Error("initialization
|
|
507
|
+
throw new Error("initialization program did not preserve every authored operation");
|
|
503
508
|
}
|
|
504
|
-
await Turn.recordSource(this.#db, initializationTurn.id, "
|
|
509
|
+
await Turn.recordSource(this.#db, initializationTurn.id, "ops", program);
|
|
505
510
|
const result = await this.executeAdmittedTurn({
|
|
506
511
|
statements: admitted,
|
|
507
512
|
source: null,
|
|
@@ -513,6 +518,7 @@ class TurnRunner {
|
|
|
513
518
|
turnId: initializationTurn.id,
|
|
514
519
|
fromSequence: 1,
|
|
515
520
|
failOnOperationError: true,
|
|
521
|
+
fit: this.#fitFor(args, { turnId: initializationTurn.id, seq: initializationTurn.sequence, gitStatus, notices: [], transientOpenLogEntryId: container.transientOpenLogEntryId, bodiless: new Set() }),
|
|
516
522
|
signal: this.#loopSignal(loopId),
|
|
517
523
|
onDispatch,
|
|
518
524
|
onSettled,
|
|
@@ -592,6 +598,14 @@ class TurnRunner {
|
|
|
592
598
|
metadata: null,
|
|
593
599
|
matcher: null, body: null, lineMarker: null, position: UNKNOWN_POSITION,
|
|
594
600
|
},
|
|
601
|
+
{
|
|
602
|
+
// {§worker-initialization-entry} — the worker's own reasoning is an addressable, searchable
|
|
603
|
+
// space from the first packet: surveyed by name, never read back.
|
|
604
|
+
op: "FIND", aside: "this worker's reasoning, by loop and turn",
|
|
605
|
+
target: { kind: "url", raw: `reasoning://${workerName}/**`, scheme: "reasoning", username: null, password: null, hostname: workerName, port: null, pathname: "/**", query: null, fragment: null },
|
|
606
|
+
metadata: null,
|
|
607
|
+
matcher: null, body: null, lineMarker: null, position: UNKNOWN_POSITION,
|
|
608
|
+
},
|
|
595
609
|
];
|
|
596
610
|
return surveys.filter(({ target }) => this.#schemes.get(target?.kind === "url" ? target.scheme : "file", workspaceId) !== undefined);
|
|
597
611
|
}
|
|
@@ -636,8 +650,12 @@ class TurnRunner {
|
|
|
636
650
|
container.createdTurnIds.push(modelTurn.id);
|
|
637
651
|
const { id: turnId, sequence: seq } = modelTurn;
|
|
638
652
|
const systemCtx = this.#schemeContext(args, turnId);
|
|
639
|
-
|
|
640
|
-
|
|
653
|
+
// {§context-fit} — one measure for everything this turn lands: the budget less the packet as it
|
|
654
|
+
// would render now. The drained notices join the same facts below.
|
|
655
|
+
const facts = { turnId, seq, gitStatus, notices: [], transientOpenLogEntryId: container.transientOpenLogEntryId, bodiless: new Set() };
|
|
656
|
+
const fit = this.#fitFor(args, facts);
|
|
657
|
+
const messages = await this.#publishMessages(args, turnId, fit);
|
|
658
|
+
let nextActionIndex = await this.#readOpenPaths(args, turnId, messages.openPaths, messages.nextActionIndex, fit);
|
|
641
659
|
// {§env-delta-log-pull} — materialize ambient observations before packet
|
|
642
660
|
// composition and reserve their action indices. {§exec-stream} owns the
|
|
643
661
|
// distinct byte-cursor path for this worker's streams.
|
|
@@ -647,7 +665,7 @@ class TurnRunner {
|
|
|
647
665
|
await this.#reapTurnScopedStreams(workerId);
|
|
648
666
|
const ambientEntries = await this.#materialization.materializeEnvironmentDeltas({ workspaceId, workerId, loopId, turnId, fromSequence: nextActionIndex });
|
|
649
667
|
nextActionIndex += ambientEntries.length;
|
|
650
|
-
const streamEntries = await this.#materialization.materializeStreamDeltas({ workspaceId, workerId, loopId, turnId, fromSequence: nextActionIndex });
|
|
668
|
+
const streamEntries = await this.#materialization.materializeStreamDeltas({ workspaceId, workerId, loopId, turnId, fromSequence: nextActionIndex, fit });
|
|
651
669
|
nextActionIndex += streamEntries.length;
|
|
652
670
|
await Turn.observeCompletions(this.#db, turnId);
|
|
653
671
|
// {§notifications-log-entry-notify}: materialized observations are
|
|
@@ -660,18 +678,13 @@ class TurnRunner {
|
|
|
660
678
|
// and every budget rebuild; overflow never shells again.
|
|
661
679
|
// Notices are non-terminal observations, never operation-failure truth.
|
|
662
680
|
// Drain once and thread the same set through every overflow rebuild.
|
|
663
|
-
|
|
664
|
-
.filter((event) => event.level !== "info");
|
|
681
|
+
facts.notices.push(...this.#notices.drain(loopId)
|
|
682
|
+
.filter((event) => event.level !== "info"));
|
|
665
683
|
// Build the model request packet ({§packet-stored-shape}). The log build
|
|
666
684
|
// queries log_entries scoped to the worker, including this turn's newly
|
|
667
685
|
// published message arrivals.
|
|
668
|
-
const
|
|
669
|
-
|
|
670
|
-
// {§context-output-admission} — output admission changes no operation
|
|
671
|
-
// outcome, authored memory, or turn identity.
|
|
672
|
-
if (await this.#packets.admitOutput(packet, turnId))
|
|
673
|
-
packet = await this.#buildPacket(args, facts);
|
|
674
|
-
return { ...facts, createdTurnIds: container.createdTurnIds, workerName: container.workerName, loopSeq: container.loopSequence, systemCtx, nextActionIndex, packet };
|
|
686
|
+
const packet = await this.#buildPacket(args, facts);
|
|
687
|
+
return { ...facts, fit, createdTurnIds: container.createdTurnIds, workerName: container.workerName, loopSeq: container.loopSequence, systemCtx, nextActionIndex, packet };
|
|
675
688
|
}
|
|
676
689
|
// Pre-model writes. Each message the model has not seen yet becomes an inbound `SEND`
|
|
677
690
|
// row; model operations continue the same turn sequence after these rows. Returns the
|
|
@@ -679,15 +692,18 @@ class TurnRunner {
|
|
|
679
692
|
// {§message-loop-containment}: the loop contains every message that arrived before this
|
|
680
693
|
// boundary. Publish each unpublished inbox row oldest-first, exactly once, as an inbound
|
|
681
694
|
// SEND row ({§message-arrival}); its selected paths ride along for the READs that follow.
|
|
682
|
-
async #publishMessages({ workerId, loopId, onDispatch, onSettled }, turnId) {
|
|
695
|
+
async #publishMessages({ workspaceId, workerId, loopId, onDispatch, onSettled }, turnId, fit) {
|
|
683
696
|
let nextActionIndex = 1;
|
|
684
697
|
const openPaths = [];
|
|
685
698
|
const unpublished = await this.#db.drain_unpublished_messages_for_loop.all({ loop_id: loopId });
|
|
686
|
-
|
|
687
|
-
|
|
699
|
+
const selected = unpublished.map((message) => assertOpenPaths(JSON.parse(message.open_paths), `Message ${message.id} open_paths`));
|
|
700
|
+
const pendingPaths = selected.reduce((count, paths) => count + paths.length, 0);
|
|
701
|
+
for (const [index, message] of unpublished.entries()) {
|
|
702
|
+
openPaths.push(...selected[index]);
|
|
688
703
|
const logEntryId = await this.#materialization.writeArrivalLog({
|
|
689
|
-
workerId, loopId, turnId, sequence: nextActionIndex++, body: message.body, source: message.source, resource: message.path,
|
|
704
|
+
workspaceId, workerId, loopId, turnId, sequence: nextActionIndex++, body: message.body, source: message.source, resource: message.path,
|
|
690
705
|
selfAddressed: message.source !== null && message.source === message.durable_path,
|
|
706
|
+
fit: reserved(fit, unpublished.length - index - 1 + pendingPaths),
|
|
691
707
|
});
|
|
692
708
|
const published = await this.#db.drain_publish_message.get({ id: message.id, log_entry_id: logEntryId });
|
|
693
709
|
if (published === undefined)
|
|
@@ -700,9 +716,9 @@ class TurnRunner {
|
|
|
700
716
|
// {§methods-loop-run-open-paths}: selected workspace paths belong to the message.
|
|
701
717
|
// Publish it, then dispatch ordinary core READs in that same turn;
|
|
702
718
|
// missing/non-member paths retain their normal 4xx. Returns the next action index.
|
|
703
|
-
async #readOpenPaths({ workspaceId, workerId, loopId, onDispatch, onSettled }, turnId, openPaths, fromSequence) {
|
|
719
|
+
async #readOpenPaths({ workspaceId, workerId, loopId, onDispatch, onSettled }, turnId, openPaths, fromSequence, fit) {
|
|
704
720
|
let nextActionIndex = fromSequence;
|
|
705
|
-
for (const raw of openPaths) {
|
|
721
|
+
for (const [index, raw] of openPaths.entries()) {
|
|
706
722
|
const fileRead = {
|
|
707
723
|
op: "READ", aside: null, lineMarker: null, matcher: null,
|
|
708
724
|
target: { kind: "local", raw },
|
|
@@ -712,6 +728,7 @@ class TurnRunner {
|
|
|
712
728
|
await this.#dispatch({
|
|
713
729
|
statement: fileRead, workspaceId, workerId, loopId, turnId,
|
|
714
730
|
sequence: nextActionIndex, origin: "_plurnk", onDispatch, onSettled,
|
|
731
|
+
fit: reserved(fit, openPaths.length - index - 1),
|
|
715
732
|
});
|
|
716
733
|
nextActionIndex++;
|
|
717
734
|
}
|
|
@@ -729,14 +746,74 @@ class TurnRunner {
|
|
|
729
746
|
gitStatus: facts.gitStatus,
|
|
730
747
|
notices: facts.notices,
|
|
731
748
|
transientOpenLogEntryId: facts.transientOpenLogEntryId,
|
|
732
|
-
promptProjection: facts.promptProjection,
|
|
733
749
|
turnId: facts.turnId,
|
|
750
|
+
bodiless: facts.bodiless,
|
|
734
751
|
});
|
|
735
752
|
}
|
|
736
|
-
//
|
|
737
|
-
//
|
|
753
|
+
// {§context-fit} — the budget left for one more row, measured by building the packet as it would
|
|
754
|
+
// render now: every row landed so far, every KILL already honoured. Null without a budget.
|
|
755
|
+
#fitFor(args, facts) {
|
|
756
|
+
return {
|
|
757
|
+
remaining: async () => {
|
|
758
|
+
const packet = await this.#buildPacket(args, facts);
|
|
759
|
+
const budget = this.#packets.curationBudgetFor(packet);
|
|
760
|
+
return budget === null ? null : Math.max(0, budget - packet.weight - RECEIPT_RESERVE);
|
|
761
|
+
},
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
// {§context-over-budget-row} — one `_plurnk` error row at the head of the turn's own rows, then the
|
|
765
|
+
// packet as it renders with it; the row recurs on every packet that is over.
|
|
766
|
+
async #noteOverBudget(args, request) {
|
|
767
|
+
const { workerId, loopId, onDispatch, onSettled } = args;
|
|
768
|
+
const minted = await this.#problems.record({
|
|
769
|
+
workerId, loopId, turnId: request.turnId, sequence: request.nextActionIndex, origin: "_plurnk", source: "context",
|
|
770
|
+
result: overBudgetFailure(),
|
|
771
|
+
});
|
|
772
|
+
onDispatch?.(minted.id);
|
|
773
|
+
await onSettled?.(minted.id);
|
|
774
|
+
const packet = await this.#buildPacket(args, request);
|
|
775
|
+
return { ...request, nextActionIndex: request.nextActionIndex + 1, packet };
|
|
776
|
+
}
|
|
777
|
+
// The packet against the wall and the budget: the newest rows go bodiless until it fits the window
|
|
778
|
+
// ({§context-own-rows-fit}); a packet still over budget then says so in a row ({§context-over-budget-row}),
|
|
779
|
+
// whose own weight takes the same wall. The overflow is returned only when the packet cannot fit even
|
|
780
|
+
// as receipts.
|
|
781
|
+
async #admitPacket(args, request) {
|
|
782
|
+
const fitted = await this.#fitOwnRows(args, request);
|
|
783
|
+
if (fitted.overflow !== null)
|
|
784
|
+
return fitted;
|
|
785
|
+
if (this.#packets.curationOverflow(fitted.request.packet) === null)
|
|
786
|
+
return fitted;
|
|
787
|
+
return await this.#fitOwnRows(args, await this.#noteOverBudget(args, fitted.request));
|
|
788
|
+
}
|
|
789
|
+
// {§context-own-rows-fit} — while the packet is over the wall ({§context-wall}), the newest rows still
|
|
790
|
+
// carrying a body are taken, enough of them by their rendered tokens to shed the excess, and the packet
|
|
791
|
+
// is rebuilt with them bodiless; every body stays stored. The rebuilt packet is measured again until
|
|
792
|
+
// it fits, or until no row is left to take.
|
|
793
|
+
async #fitOwnRows(args, request) {
|
|
794
|
+
let current = request;
|
|
795
|
+
for (;;) {
|
|
796
|
+
const overflow = this.#packets.windowOverflow(current.packet, args.provider);
|
|
797
|
+
if (overflow === null)
|
|
798
|
+
return { request: current, overflow: null };
|
|
799
|
+
let shed = 0;
|
|
800
|
+
let taken = 0;
|
|
801
|
+
for (const { id, tokens } of this.#packets.bodiedRowsOf(current.packet).toReversed()) {
|
|
802
|
+
if (shed >= overflow.excessWeight)
|
|
803
|
+
break;
|
|
804
|
+
current.bodiless.add(id);
|
|
805
|
+
shed += tokens;
|
|
806
|
+
taken += 1;
|
|
807
|
+
}
|
|
808
|
+
if (taken === 0)
|
|
809
|
+
return { request: current, overflow };
|
|
810
|
+
current = { ...current, packet: await this.#buildPacket(args, current) };
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
// {§context-wall} — the packet cannot fit the window even as receipts: the turn completes on the
|
|
814
|
+
// overflow and the loop rules the terminal; no provider I/O.
|
|
738
815
|
async #failCuration(request, overflow) {
|
|
739
|
-
const curationFailure =
|
|
816
|
+
const curationFailure = windowOverflowFailure(overflow);
|
|
740
817
|
await Turn.complete(this.#db, request.turnId, curationFailure.status);
|
|
741
818
|
return turnResult(request, curationFailure.status, { curationFailure });
|
|
742
819
|
}
|
|
@@ -882,14 +959,9 @@ class TurnRunner {
|
|
|
882
959
|
await this.#recoverProviderFailure(args, request, attempts, modelCall, attemptRow.id, error);
|
|
883
960
|
return "reissued";
|
|
884
961
|
}
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|| !await this.#recoverCapacityPacket(args, request, attempts)) {
|
|
889
|
-
throw error;
|
|
890
|
-
}
|
|
891
|
-
await this.#resendForCapacity(args, request, attempts, modelCall, error);
|
|
892
|
-
return "reissued";
|
|
962
|
+
// {§context-wall} — a provider capacity rejection after the provider's own retries is this
|
|
963
|
+
// turn's failure; nothing is withheld to make the request smaller.
|
|
964
|
+
throw error;
|
|
893
965
|
}
|
|
894
966
|
finally {
|
|
895
967
|
reasoning.end();
|
|
@@ -1049,42 +1121,6 @@ class TurnRunner {
|
|
|
1049
1121
|
// durable, but recursively materializing them would mutate and cache-bust
|
|
1050
1122
|
// the request being recovered. {§provider-recovery}
|
|
1051
1123
|
}
|
|
1052
|
-
// The first capacity rejection withholds the automatic prompt projection; the
|
|
1053
|
-
// rebuilt request is resent only when it actually changed the wire messages.
|
|
1054
|
-
async #recoverCapacityPacket(args, request, attempts) {
|
|
1055
|
-
if (request.promptProjection !== "automatic")
|
|
1056
|
-
return false;
|
|
1057
|
-
request.promptProjection = "withheld";
|
|
1058
|
-
const candidate = await this.#buildPacket(args, request);
|
|
1059
|
-
const candidateRequest = await this.#wireMessages(candidate, request.systemCtx, args.provider);
|
|
1060
|
-
if (JSON.stringify(candidateRequest.messages) === JSON.stringify(attempts.wire.messages))
|
|
1061
|
-
return false;
|
|
1062
|
-
request.packet = candidate;
|
|
1063
|
-
attempts.wire = candidateRequest;
|
|
1064
|
-
return true;
|
|
1065
|
-
}
|
|
1066
|
-
// The capacity rejection is durable as a failed call and a problem row; the
|
|
1067
|
-
// replacement request is rebuilt to include that recovery signal while keeping
|
|
1068
|
-
// the selected projection.
|
|
1069
|
-
async #resendForCapacity(args, request, attempts, modelCall, error) {
|
|
1070
|
-
const { workerId, loopId, provider } = args;
|
|
1071
|
-
const failure = _a.#providerFailure(error, attempts.signal);
|
|
1072
|
-
await modelCall.fail(failure, error.capacity ?? null);
|
|
1073
|
-
attempts.callInFlight = false;
|
|
1074
|
-
await this.#problems.record({
|
|
1075
|
-
workerId,
|
|
1076
|
-
loopId,
|
|
1077
|
-
turnId: request.turnId,
|
|
1078
|
-
sequence: request.nextActionIndex++,
|
|
1079
|
-
origin: "_plurnk",
|
|
1080
|
-
source: "provider",
|
|
1081
|
-
result: failure,
|
|
1082
|
-
});
|
|
1083
|
-
// Include the durable recovery signal in the replacement
|
|
1084
|
-
// request while preserving the selected recovery posture.
|
|
1085
|
-
request.packet = await this.#buildPacket(args, request);
|
|
1086
|
-
attempts.wire = await this.#wireMessages(request.packet, request.systemCtx, provider);
|
|
1087
|
-
}
|
|
1088
1124
|
// The attempt row's verdict: accepted, or rejected with the parser's errors. The
|
|
1089
1125
|
// classified emission attempt is the turn's spent count.
|
|
1090
1126
|
async #classifyProviderAttempt(attempts, id, split, emissionAttempt, accepted) {
|
|
@@ -1351,6 +1387,7 @@ class TurnRunner {
|
|
|
1351
1387
|
emission: split.emissionStatements === null ? null : {
|
|
1352
1388
|
content: TurnOps.renderEmission(split.emissionStatements, `ops://${request.workerName}/${request.loopSeq}/${request.seq}`),
|
|
1353
1389
|
workerName: request.workerName, loopSeq: request.loopSeq, turnSeq: request.seq,
|
|
1390
|
+
reasoning: split.packetAssistant.reasoning ?? null, // {§reasoning-row}
|
|
1354
1391
|
},
|
|
1355
1392
|
sourceModelCallId: emission.modelCallId,
|
|
1356
1393
|
origin: "model",
|
|
@@ -1360,6 +1397,7 @@ class TurnRunner {
|
|
|
1360
1397
|
turnId: request.turnId,
|
|
1361
1398
|
fromSequence: request.nextActionIndex,
|
|
1362
1399
|
maxCommands,
|
|
1400
|
+
fit: request.fit,
|
|
1363
1401
|
recoverableParseErrors: split.recoverableParseErrors,
|
|
1364
1402
|
emptyTurn: split.emptyTurn,
|
|
1365
1403
|
finalResponse: split.finalResponse,
|
|
@@ -1373,8 +1411,6 @@ class TurnRunner {
|
|
|
1373
1411
|
onDispatch,
|
|
1374
1412
|
onSettled,
|
|
1375
1413
|
});
|
|
1376
|
-
if (executed.emptyTurn)
|
|
1377
|
-
await this.#readEmptyTurnReasoning(args, request, split.packetAssistant);
|
|
1378
1414
|
return turnResult(request, executed.status, {
|
|
1379
1415
|
outcomes: executed.outcomes,
|
|
1380
1416
|
progressed: executed.progressed,
|
|
@@ -1383,40 +1419,6 @@ class TurnRunner {
|
|
|
1383
1419
|
emissionAttempts: emission.emissionAttempts,
|
|
1384
1420
|
});
|
|
1385
1421
|
}
|
|
1386
|
-
// {§reasoning-empty-turn-read} — after a turn with no operation, one runtime turn READs that
|
|
1387
|
-
// turn's reasoning back to the model. A trace or emission carrying a foreign tool-call grammar
|
|
1388
|
-
// is not echoed (`KnownToxins`); a turn without reasoning has nothing to read.
|
|
1389
|
-
async #readEmptyTurnReasoning(args, request, { content, reasoning, ops }) {
|
|
1390
|
-
const { provider, workspaceId, workerId, loopId, onDispatch, onSettled } = args;
|
|
1391
|
-
if (ops.length > 0 || !reasoning?.length || KnownToxins.match(content) !== null || KnownToxins.match(reasoning) !== null)
|
|
1392
|
-
return;
|
|
1393
|
-
const statement = ReasoningView.emptyTurnRead(provider, request.workerName, request.loopSeq, request.seq);
|
|
1394
|
-
if (statement === null)
|
|
1395
|
-
return;
|
|
1396
|
-
const policies = (await CapabilityPolicies.layers(this.#db, workspaceId, loopId)).map((layer) => layer.policy);
|
|
1397
|
-
if (!this.#capabilities.allowsAcross(statement, workspaceId, policies))
|
|
1398
|
-
return;
|
|
1399
|
-
const turn = await Turn.open(this.#db, { loopId, producer: "_plurnk", kind: "operation" });
|
|
1400
|
-
request.createdTurnIds.push(turn.id);
|
|
1401
|
-
const source = TurnOps.renderInternal([statement]);
|
|
1402
|
-
const result = await this.executeAdmittedTurn({
|
|
1403
|
-
statements: TurnOps.parseInternal(source),
|
|
1404
|
-
source,
|
|
1405
|
-
emission: null,
|
|
1406
|
-
origin: "_plurnk",
|
|
1407
|
-
workspaceId,
|
|
1408
|
-
workerId,
|
|
1409
|
-
loopId,
|
|
1410
|
-
turnId: turn.id,
|
|
1411
|
-
fromSequence: 1,
|
|
1412
|
-
failOnOperationError: true,
|
|
1413
|
-
signal: this.#loopSignal(loopId),
|
|
1414
|
-
onDispatch,
|
|
1415
|
-
onSettled,
|
|
1416
|
-
});
|
|
1417
|
-
if (result.status !== 200)
|
|
1418
|
-
throw new Error(`empty-turn reasoning read returned ${result.status}; expected 200`);
|
|
1419
|
-
}
|
|
1420
1422
|
// Split the wire-level ProviderResponse into the two destinations:
|
|
1421
1423
|
// packet.assistant gets the model's emission (content, ops, reasoning);
|
|
1422
1424
|
// Turn columns get accepted-call metadata (finishReason, model). Physical
|
|
@@ -1553,10 +1555,10 @@ class TurnRunner {
|
|
|
1553
1555
|
parserSource: warning.source,
|
|
1554
1556
|
});
|
|
1555
1557
|
});
|
|
1556
|
-
const
|
|
1557
|
-
|
|
1558
|
-
//
|
|
1559
|
-
if (
|
|
1558
|
+
const operationCount = contentStatementCount + reasoningOps.length;
|
|
1559
|
+
// {§reasoning-operations}: an admitted reasoning operation is the turn's work, a NOTE as much as a
|
|
1560
|
+
// FIND or READ, with or without a content program.
|
|
1561
|
+
if (reasoningOps.length > 0) {
|
|
1560
1562
|
for (let i = parseErrors.length - 1; i >= 0; i--) {
|
|
1561
1563
|
if (parseErrors[i].message === PlurnkParser.NO_VALID_OPERATION)
|
|
1562
1564
|
parseErrors.splice(i, 1);
|
|
@@ -1569,7 +1571,7 @@ class TurnRunner {
|
|
|
1569
1571
|
&& !hasUnparsedTail && parseErrors.length === 0
|
|
1570
1572
|
&& assistant.finishReason !== "length";
|
|
1571
1573
|
const emptyTurn = preParsedOps === undefined && operationCount === 0 && !hasUnparsedTail;
|
|
1572
|
-
// {§unparsed-tail-boundary}: outside text
|
|
1574
|
+
// {§unparsed-tail-boundary}: outside text is not operational work.
|
|
1573
1575
|
const emissionValid = fabrications.length === 0 && (preParsedOps !== undefined
|
|
1574
1576
|
|| emptyTurn
|
|
1575
1577
|
|| operationCount > 0);
|