@plurnk/plurnk-service 1.10.0 → 1.11.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 +41 -17
- package/INSTALL.md +1 -1
- package/README.md +1 -1
- package/SPEC.md +487 -233
- package/digest-sql/curation/curation.sql +1 -0
- package/dist/Paths.d.ts +3 -3
- package/dist/Paths.d.ts.map +1 -1
- package/dist/Paths.js +9 -9
- package/dist/Paths.js.map +1 -1
- package/dist/build-info.json +1 -1
- package/dist/content/edit-collision.js +3 -3
- package/dist/content/edit-collision.js.map +1 -1
- package/dist/content/edit-receipt.d.ts +6 -5
- package/dist/content/edit-receipt.d.ts.map +1 -1
- package/dist/content/edit-receipt.js +13 -2
- package/dist/content/edit-receipt.js.map +1 -1
- package/dist/content/index.d.ts +2 -2
- package/dist/content/index.d.ts.map +1 -1
- package/dist/content/index.js +1 -1
- package/dist/content/index.js.map +1 -1
- package/dist/content/line-anchors.d.ts +2 -0
- package/dist/content/line-anchors.d.ts.map +1 -1
- package/dist/content/line-anchors.js +2 -0
- package/dist/content/line-anchors.js.map +1 -1
- package/dist/content/matcher.d.ts.map +1 -1
- package/dist/content/matcher.js +3 -2
- package/dist/content/matcher.js.map +1 -1
- package/dist/content/read-projector.js +3 -3
- package/dist/content/read-projector.js.map +1 -1
- package/dist/core/BudgetReadout.d.ts +6 -1
- package/dist/core/BudgetReadout.d.ts.map +1 -1
- package/dist/core/BudgetReadout.js +38 -2
- package/dist/core/BudgetReadout.js.map +1 -1
- package/dist/core/CapabilityPolicies.d.ts +15 -0
- package/dist/core/CapabilityPolicies.d.ts.map +1 -0
- package/dist/core/CapabilityPolicies.js +60 -0
- package/dist/core/CapabilityPolicies.js.map +1 -0
- package/dist/core/CapabilityResolver.d.ts +24 -0
- package/dist/core/CapabilityResolver.d.ts.map +1 -0
- package/dist/core/CapabilityResolver.js +180 -0
- package/dist/core/CapabilityResolver.js.map +1 -0
- package/dist/core/ChannelWrite.d.ts +4 -3
- package/dist/core/ChannelWrite.d.ts.map +1 -1
- package/dist/core/CoreSchemeServices.d.ts +1 -1
- package/dist/core/CoreSchemeServices.d.ts.map +1 -1
- package/dist/core/CoreSchemeServices.js +1 -1
- package/dist/core/CoreSchemeServices.js.map +1 -1
- package/dist/core/Dispatcher.d.ts +3 -1
- package/dist/core/Dispatcher.d.ts.map +1 -1
- package/dist/core/Dispatcher.js +148 -160
- package/dist/core/Dispatcher.js.map +1 -1
- package/dist/core/DurableStatement.d.ts.map +1 -1
- package/dist/core/DurableStatement.js +19 -15
- package/dist/core/DurableStatement.js.map +1 -1
- package/dist/core/Engine.d.ts +6 -4
- package/dist/core/Engine.d.ts.map +1 -1
- package/dist/core/Engine.js +18 -7
- package/dist/core/Engine.js.map +1 -1
- package/dist/core/Engine.sql +30 -25
- package/dist/core/ExecutorRegistry.d.ts +3 -3
- package/dist/core/ExecutorRegistry.d.ts.map +1 -1
- package/dist/core/ExecutorRegistry.js +10 -6
- package/dist/core/ExecutorRegistry.js.map +1 -1
- package/dist/core/GitBranch.d.ts +4 -1
- package/dist/core/GitBranch.d.ts.map +1 -1
- package/dist/core/GitBranch.js +12 -2
- package/dist/core/GitBranch.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 +12 -4
- package/dist/core/LogEntryProjection.js.map +1 -1
- package/dist/core/LoopPolicyReader.d.ts +7 -0
- package/dist/core/LoopPolicyReader.d.ts.map +1 -0
- package/dist/core/LoopPolicyReader.js +33 -0
- package/dist/core/LoopPolicyReader.js.map +1 -0
- package/dist/core/ModelCall.d.ts +1 -0
- package/dist/core/ModelCall.d.ts.map +1 -1
- package/dist/core/ModelCall.js +3 -0
- package/dist/core/ModelCall.js.map +1 -1
- package/dist/core/NoticeChannel.d.ts +2 -2
- package/dist/core/NoticeChannel.d.ts.map +1 -1
- package/dist/core/NoticeChannel.js +18 -9
- package/dist/core/NoticeChannel.js.map +1 -1
- package/dist/core/OperatorConfig.d.ts.map +1 -1
- package/dist/core/OperatorConfig.js +4 -0
- package/dist/core/OperatorConfig.js.map +1 -1
- package/dist/core/OverflowTurn.d.ts.map +1 -1
- package/dist/core/OverflowTurn.js +4 -3
- package/dist/core/OverflowTurn.js.map +1 -1
- package/dist/core/Owner.js +5 -5
- package/dist/core/Owner.js.map +1 -1
- package/dist/core/PacketBuilder.d.ts +2 -2
- package/dist/core/PacketBuilder.d.ts.map +1 -1
- package/dist/core/PacketBuilder.js +53 -35
- package/dist/core/PacketBuilder.js.map +1 -1
- package/dist/core/ProblemLog.d.ts.map +1 -1
- package/dist/core/ProblemLog.js +1 -0
- package/dist/core/ProblemLog.js.map +1 -1
- package/dist/core/ProposalLifecycle.d.ts.map +1 -1
- package/dist/core/ProposalLifecycle.js +16 -13
- package/dist/core/ProposalLifecycle.js.map +1 -1
- package/dist/core/ReasoningEvent.d.ts +1 -0
- package/dist/core/ReasoningEvent.d.ts.map +1 -1
- package/dist/core/ResourceMutations.d.ts +5 -4
- package/dist/core/ResourceMutations.d.ts.map +1 -1
- package/dist/core/ResourceMutations.js +71 -48
- package/dist/core/ResourceMutations.js.map +1 -1
- package/dist/core/SchemeRegistry.d.ts +6 -3
- package/dist/core/SchemeRegistry.d.ts.map +1 -1
- package/dist/core/SchemeRegistry.js +14 -17
- package/dist/core/SchemeRegistry.js.map +1 -1
- package/dist/core/ServiceTeardown.d.ts +1 -1
- package/dist/core/ServiceTeardown.d.ts.map +1 -1
- package/dist/core/ServiceTeardown.js +4 -8
- package/dist/core/ServiceTeardown.js.map +1 -1
- package/dist/core/StrikeRail.js +1 -1
- package/dist/core/StrikeRail.js.map +1 -1
- package/dist/core/ToolResources.d.ts +2 -2
- package/dist/core/ToolResources.d.ts.map +1 -1
- package/dist/core/ToolResources.js +18 -8
- package/dist/core/ToolResources.js.map +1 -1
- package/dist/core/TurnOps.d.ts.map +1 -1
- package/dist/core/TurnOps.js +22 -8
- package/dist/core/TurnOps.js.map +1 -1
- package/dist/core/TurnRunner.d.ts +7 -3
- package/dist/core/TurnRunner.d.ts.map +1 -1
- package/dist/core/TurnRunner.js +315 -71
- package/dist/core/TurnRunner.js.map +1 -1
- package/dist/core/WorkerControlAddress.d.ts.map +1 -1
- package/dist/core/WorkerControlAddress.js +1 -2
- package/dist/core/WorkerControlAddress.js.map +1 -1
- package/dist/core/WorkerName.d.ts +2 -0
- package/dist/core/WorkerName.d.ts.map +1 -1
- package/dist/core/WorkerName.js +3 -2
- package/dist/core/WorkerName.js.map +1 -1
- package/dist/core/WorkerName.sql +2 -1
- package/dist/core/caps/DbProjectionCaps.d.ts +2 -1
- package/dist/core/caps/DbProjectionCaps.d.ts.map +1 -1
- package/dist/core/caps/DbProjectionCaps.js +12 -1
- package/dist/core/caps/DbProjectionCaps.js.map +1 -1
- package/dist/core/file-materialization.d.ts +22 -0
- package/dist/core/file-materialization.d.ts.map +1 -0
- package/dist/core/file-materialization.js +77 -0
- package/dist/core/file-materialization.js.map +1 -0
- package/dist/core/fork.d.ts +2 -1
- package/dist/core/fork.d.ts.map +1 -1
- package/dist/core/fork.js +13 -4
- package/dist/core/fork.js.map +1 -1
- package/dist/core/fork.sql +27 -11
- package/dist/core/git-membership.d.ts +25 -14
- package/dist/core/git-membership.d.ts.map +1 -1
- package/dist/core/git-membership.js +260 -164
- package/dist/core/git-membership.js.map +1 -1
- package/dist/core/git-state.d.ts +2 -0
- package/dist/core/git-state.d.ts.map +1 -1
- package/dist/core/git-state.js +27 -1
- package/dist/core/git-state.js.map +1 -1
- package/dist/core/operation-target-groups.d.ts.map +1 -1
- package/dist/core/operation-target-groups.js +8 -7
- package/dist/core/operation-target-groups.js.map +1 -1
- package/dist/core/owner.sql +7 -10
- package/dist/core/packet-wire.d.ts +10 -0
- package/dist/core/packet-wire.d.ts.map +1 -1
- package/dist/core/packet-wire.js +78 -29
- package/dist/core/packet-wire.js.map +1 -1
- package/dist/core/scheme-types.d.ts +2 -2
- package/dist/core/scheme-types.d.ts.map +1 -1
- package/dist/core/scheme-types.js +1 -1
- package/dist/core/scheme-types.js.map +1 -1
- package/dist/core/types.d.ts +3 -2
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/core/worker-settings.d.ts +4 -3
- package/dist/core/worker-settings.d.ts.map +1 -1
- package/dist/core/worker-settings.js +40 -19
- package/dist/core/worker-settings.js.map +1 -1
- package/dist/core/workspace-settings.d.ts +3 -1
- package/dist/core/workspace-settings.d.ts.map +1 -1
- package/dist/core/workspace-settings.js +6 -2
- package/dist/core/workspace-settings.js.map +1 -1
- package/dist/digest/Digest.d.ts.map +1 -1
- package/dist/digest/Digest.js +93 -26
- package/dist/digest/Digest.js.map +1 -1
- package/dist/digest/digest.sql +13 -3
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/schemes/Exec.d.ts +3 -5
- package/dist/schemes/Exec.d.ts.map +1 -1
- package/dist/schemes/Exec.js +98 -94
- package/dist/schemes/Exec.js.map +1 -1
- package/dist/schemes/ExecOutputScheme.d.ts.map +1 -1
- package/dist/schemes/ExecOutputScheme.js +24 -6
- package/dist/schemes/ExecOutputScheme.js.map +1 -1
- package/dist/schemes/ExecScheduler.d.ts +15 -0
- package/dist/schemes/ExecScheduler.d.ts.map +1 -0
- package/dist/schemes/ExecScheduler.js +95 -0
- package/dist/schemes/ExecScheduler.js.map +1 -0
- package/dist/schemes/File.d.ts.map +1 -1
- package/dist/schemes/File.js +16 -15
- package/dist/schemes/File.js.map +1 -1
- package/dist/schemes/Log.d.ts +6 -4
- package/dist/schemes/Log.d.ts.map +1 -1
- package/dist/schemes/Log.js +131 -38
- package/dist/schemes/Log.js.map +1 -1
- package/dist/schemes/Log.sql +23 -18
- package/dist/schemes/QuestionTool.d.ts +17 -4
- package/dist/schemes/QuestionTool.d.ts.map +1 -1
- package/dist/schemes/QuestionTool.js +2 -4
- package/dist/schemes/QuestionTool.js.map +1 -1
- package/dist/schemes/Worker.d.ts.map +1 -1
- package/dist/schemes/Worker.js +20 -20
- package/dist/schemes/Worker.js.map +1 -1
- package/dist/schemes/_entry-chunk.js +1 -1
- package/dist/schemes/_entry-chunk.js.map +1 -1
- package/dist/schemes/_entry-crud.sql +18 -17
- package/dist/schemes/_entry-find.d.ts.map +1 -1
- package/dist/schemes/_entry-find.js +7 -9
- package/dist/schemes/_entry-find.js.map +1 -1
- package/dist/schemes/_entry-graph.js +12 -12
- package/dist/schemes/_entry-graph.js.map +1 -1
- package/dist/schemes/_entry-graph.sql +5 -5
- package/dist/schemes/_entry-ops.d.ts.map +1 -1
- package/dist/schemes/_entry-ops.js +3 -3
- package/dist/schemes/_entry-ops.js.map +1 -1
- package/dist/schemes/_entry-semantic.js +1 -1
- package/dist/schemes/_entry-semantic.js.map +1 -1
- package/dist/server/BranchBatches.d.ts +3 -3
- package/dist/server/BranchBatches.d.ts.map +1 -1
- package/dist/server/BranchBatches.js +9 -2
- package/dist/server/BranchBatches.js.map +1 -1
- package/dist/server/Daemon.d.ts +12 -41
- package/dist/server/Daemon.d.ts.map +1 -1
- package/dist/server/Daemon.js +59 -93
- package/dist/server/Daemon.js.map +1 -1
- package/dist/server/DaemonModule.d.ts +10 -1
- package/dist/server/DaemonModule.d.ts.map +1 -1
- package/dist/server/DrainSupervisor.d.ts +6 -5
- package/dist/server/DrainSupervisor.d.ts.map +1 -1
- package/dist/server/DrainSupervisor.js +14 -11
- package/dist/server/DrainSupervisor.js.map +1 -1
- package/dist/server/Functionality.d.ts +2 -2
- package/dist/server/Functionality.d.ts.map +1 -1
- package/dist/server/Functionality.js +8 -5
- package/dist/server/Functionality.js.map +1 -1
- package/dist/server/FunctionalityManager.d.ts +13 -1
- package/dist/server/FunctionalityManager.d.ts.map +1 -1
- package/dist/server/FunctionalityManager.js +82 -17
- package/dist/server/FunctionalityManager.js.map +1 -1
- package/dist/server/MembersFunctionality.d.ts +56 -0
- package/dist/server/MembersFunctionality.d.ts.map +1 -0
- package/dist/server/MembersFunctionality.js +350 -0
- package/dist/server/MembersFunctionality.js.map +1 -0
- package/dist/server/SkillsFunctionality.d.ts +12 -1
- package/dist/server/SkillsFunctionality.d.ts.map +1 -1
- package/dist/server/SkillsFunctionality.js +6 -1
- package/dist/server/SkillsFunctionality.js.map +1 -1
- package/dist/server/client-input.d.ts +5 -8
- package/dist/server/client-input.d.ts.map +1 -1
- package/dist/server/client-input.js +49 -108
- package/dist/server/client-input.js.map +1 -1
- package/dist/server/dispatch-as-plurnk.d.ts.map +1 -1
- package/dist/server/dispatch-as-plurnk.js +3 -0
- package/dist/server/dispatch-as-plurnk.js.map +1 -1
- package/dist/server/drain.sql +4 -4
- package/dist/server/envelope.d.ts +1 -1
- package/dist/server/envelope.d.ts.map +1 -1
- package/dist/server/envelope.js +11 -10
- package/dist/server/envelope.js.map +1 -1
- package/dist/server/envelope.sql +1 -1
- package/dist/server/loopDocs.d.ts.map +1 -1
- package/dist/server/loopDocs.js +12 -5
- package/dist/server/loopDocs.js.map +1 -1
- package/dist/server/seam-proposal-list.sql +2 -2
- package/dist/server/worker-capabilities.sql +9 -0
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +16 -21
- package/dist/service.js.map +1 -1
- package/migrations/001_schema.sql +217 -64
- package/package.json +30 -32
- package/dist/core/LoopFlagsReader.d.ts +0 -7
- package/dist/core/LoopFlagsReader.d.ts.map +0 -1
- package/dist/core/LoopFlagsReader.js +0 -33
- package/dist/core/LoopFlagsReader.js.map +0 -1
- package/dist/core/resolveForLoop.d.ts +0 -5
- package/dist/core/resolveForLoop.d.ts.map +0 -1
- package/dist/core/resolveForLoop.js +0 -12
- package/dist/core/resolveForLoop.js.map +0 -1
package/dist/core/Dispatcher.js
CHANGED
|
@@ -6,7 +6,9 @@ import Fork from "./fork.js";
|
|
|
6
6
|
import WorkerCap from "./worker-cap.js";
|
|
7
7
|
import { PathSyntax, TagSignal } from "@plurnk/plurnk-contracts";
|
|
8
8
|
import Namespace from "./namespace.js";
|
|
9
|
-
import
|
|
9
|
+
import LoopPolicyReader from "./LoopPolicyReader.js";
|
|
10
|
+
import CapabilityResolver from "./CapabilityResolver.js";
|
|
11
|
+
import CapabilityPolicies from "./CapabilityPolicies.js";
|
|
10
12
|
import ChannelWrite from "./ChannelWrite.js";
|
|
11
13
|
import { ReadProjector } from "../content/index.js";
|
|
12
14
|
import SchemeCtxImpl from "./caps/SchemeCtxImpl.js";
|
|
@@ -17,12 +19,13 @@ import TerminalResult from "./TerminalResult.js";
|
|
|
17
19
|
import Results from "./results.js";
|
|
18
20
|
import { OperationFailureError } from "./results.js";
|
|
19
21
|
import EffectPolicy from "../schemes/EffectPolicy.js";
|
|
20
|
-
import WorkerSettingsReader from "./worker-settings.js";
|
|
21
22
|
import { CoreSchemeAdapterBase } from "./CoreSchemeServices.js";
|
|
22
23
|
import { InvalidOperationResultError, } from "@plurnk/plurnk-schemes";
|
|
23
24
|
import DurableStatement from "./DurableStatement.js";
|
|
24
25
|
import ResourceMutations from "./ResourceMutations.js";
|
|
25
26
|
import LogBody from "./LogBody.js";
|
|
27
|
+
import LogVisibility from "./LogVisibility.js";
|
|
28
|
+
import ErrorDetail from "./ErrorDetail.js";
|
|
26
29
|
// SPEC {§scheme-surface}: writer must be in target scheme's manifest.writableBy.
|
|
27
30
|
// OPEN/FOLD/READ/FIND are not gated — they curate the log or read, never mutating an entry.
|
|
28
31
|
const MUTATING_OPS = new Set(["EDIT", "SEND", "COPY", "MOVE", "EXEC", "KILL", "FORK", "WORK"]);
|
|
@@ -38,6 +41,8 @@ const assertClassifyingSignal = (statement) => {
|
|
|
38
41
|
if (statement.op === "EXEC")
|
|
39
42
|
TagSignal.applied(statement.tags ?? null); // {§exec-tag-signal}
|
|
40
43
|
};
|
|
44
|
+
const primaryTargetOf = (statement) => statement.op === "COPY" || statement.op === "MOVE" ? statement.source.target : statement.target;
|
|
45
|
+
const primaryLineMarkerOf = (statement) => statement.op === "COPY" || statement.op === "MOVE" ? statement.source.lineMarker : statement.lineMarker;
|
|
41
46
|
// Op dispatch ({§op-methods-op-dispatch}): admission, operation-owner routing,
|
|
42
47
|
// durable log writing, and proposal lifecycle.
|
|
43
48
|
class Dispatcher {
|
|
@@ -79,6 +84,7 @@ class Dispatcher {
|
|
|
79
84
|
#lifecycle;
|
|
80
85
|
#resourceMutations;
|
|
81
86
|
#entryAddresses;
|
|
87
|
+
#capabilities;
|
|
82
88
|
constructor({ db, schemes, mimetypes, weigh, notices, proposals, interactions, executors, loopSignal, settleDerivations, streamEventNotify, wakeWorkerNotify, injectWorker, branchWorker, branchCompletionGate, cancelWorker, cancelDescendants, parkDeadlines, joinTargets, liveSubscriptions, entryAddresses }) {
|
|
83
89
|
this.#db = db;
|
|
84
90
|
this.#schemes = schemes;
|
|
@@ -101,13 +107,14 @@ class Dispatcher {
|
|
|
101
107
|
this.#joinTargets = joinTargets ?? new Set();
|
|
102
108
|
this.#liveSubscriptions = liveSubscriptions;
|
|
103
109
|
this.#entryAddresses = entryAddresses;
|
|
110
|
+
this.#capabilities = new CapabilityResolver(db, schemes, executors);
|
|
104
111
|
this.#lifecycle = new LoopLifecycle(db);
|
|
105
112
|
this.#resourceMutations = new ResourceMutations({
|
|
106
113
|
schemes,
|
|
107
114
|
liveSubscriptions,
|
|
108
115
|
run: (schemeName, statement, ctx) => this.#run(schemeName, statement, ctx),
|
|
109
116
|
checkWritable: (statement, origin, workerId) => this.#checkWritable(statement, origin, workerId),
|
|
110
|
-
|
|
117
|
+
checkCapabilities: (statement, workspaceId, loopId, workerId) => this.#checkCapabilities(statement, workspaceId, loopId, workerId),
|
|
111
118
|
editTargetIdentity: (statement, workspaceId, workerId) => this.#editTargetIdentity(statement, workspaceId, workerId),
|
|
112
119
|
canonicalFilePath: (pathname, workspaceId) => this.#canonicalFilePath(pathname, workspaceId),
|
|
113
120
|
prepareDataRepresentation: (args) => this.#prepareDataRepresentation({
|
|
@@ -290,14 +297,14 @@ class Dispatcher {
|
|
|
290
297
|
return result;
|
|
291
298
|
}
|
|
292
299
|
async #dispatchOne(context) {
|
|
293
|
-
const { statement, workspaceId, workerId, loopId, turnId, sequence, origin, onDispatch, } = context;
|
|
300
|
+
const { statement, workspaceId, workerId, loopId, turnId, sequence, origin, onDispatch, onSettled, } = context;
|
|
294
301
|
const schemeCtx = this.#buildSchemeCtx({ workspaceId, workerId, functionalityWorkerId: context.functionalityWorkerId, loopId, turnId, origin });
|
|
295
302
|
const { functionalityWorkerId } = schemeCtx;
|
|
296
303
|
let result;
|
|
297
304
|
let curationPlan = null;
|
|
298
305
|
let denial = this.#checkWritable(statement, origin, functionalityWorkerId);
|
|
299
306
|
if (denial === null)
|
|
300
|
-
denial = await this.#
|
|
307
|
+
denial = await this.#checkCapabilities(statement, workspaceId, loopId, functionalityWorkerId);
|
|
301
308
|
if (denial !== null) {
|
|
302
309
|
result = denial;
|
|
303
310
|
}
|
|
@@ -321,7 +328,9 @@ class Dispatcher {
|
|
|
321
328
|
origin,
|
|
322
329
|
});
|
|
323
330
|
}
|
|
324
|
-
else if (statement.op === "OPEN"
|
|
331
|
+
else if (statement.op === "OPEN"
|
|
332
|
+
|| statement.op === "FOLD"
|
|
333
|
+
|| (statement.op === "KILL" && schemeNameOf(statement.target) === "log")) {
|
|
325
334
|
const curation = await this.#runLogCuration(statement, schemeCtx);
|
|
326
335
|
result = curation.result;
|
|
327
336
|
curationPlan = curation.plan;
|
|
@@ -342,22 +351,9 @@ class Dispatcher {
|
|
|
342
351
|
result = this.#handlePlan(statement);
|
|
343
352
|
}
|
|
344
353
|
else if (statement.op === "EXEC") {
|
|
345
|
-
//
|
|
346
|
-
//
|
|
347
|
-
|
|
348
|
-
if (("signal" in statement && typeof statement.signal === "string")
|
|
349
|
-
&& !(await WorkerSettingsReader.toolAvailable(this.#db, schemeCtx.functionalityWorkerId, statement.signal))) {
|
|
350
|
-
result = _a.#failure("question-tool-unavailable", 404, "The question tool is not available to this worker.", {}, {
|
|
351
|
-
stage: "tool-admission",
|
|
352
|
-
recovery: "This worker does not request user input; continue from the available evidence.",
|
|
353
|
-
retryable: false,
|
|
354
|
-
});
|
|
355
|
-
}
|
|
356
|
-
else {
|
|
357
|
-
// EXEC routes unconditionally to its operation owner. The
|
|
358
|
-
// resolved runtime declaration owns body/target semantics.
|
|
359
|
-
result = await this.#run("exec", statement, schemeCtx);
|
|
360
|
-
}
|
|
354
|
+
// EXEC routes unconditionally to its operation owner after
|
|
355
|
+
// the shared capability resolver admits its runtime/tool.
|
|
356
|
+
result = await this.#run("exec", statement, schemeCtx);
|
|
361
357
|
}
|
|
362
358
|
else {
|
|
363
359
|
result = await this.#run(schemeNameOf(statement.target), statement, schemeCtx); // {§op-methods-op-dispatch}
|
|
@@ -370,7 +366,7 @@ class Dispatcher {
|
|
|
370
366
|
result = err.result;
|
|
371
367
|
}
|
|
372
368
|
else {
|
|
373
|
-
const scheme = schemeNameOf(statement
|
|
369
|
+
const scheme = schemeNameOf(primaryTargetOf(statement));
|
|
374
370
|
console.error(`Scheme '${scheme ?? "unknown"}' ${statement.op} threw outside its operation result contract:`, err);
|
|
375
371
|
result = _a.#failure("scheme-handler-threw", 500, `The '${scheme ?? "unknown"}' scheme did not produce a ${statement.op} result.`, {}, {
|
|
376
372
|
stage: "scheme-dispatch",
|
|
@@ -380,8 +376,8 @@ class Dispatcher {
|
|
|
380
376
|
}
|
|
381
377
|
}
|
|
382
378
|
}
|
|
383
|
-
//
|
|
384
|
-
//
|
|
379
|
+
// Persist log curation for forensics; packet rendering suppresses its
|
|
380
|
+
// successful receipts while the exact state effects remain durable.
|
|
385
381
|
// A running-worker READ arms this turn's blocking collect.
|
|
386
382
|
// {§join-blocking-collect}
|
|
387
383
|
if (typeof result.awaitWorker === "string")
|
|
@@ -428,7 +424,9 @@ class Dispatcher {
|
|
|
428
424
|
ctx: schemeCtx,
|
|
429
425
|
ids: { workspaceId, workerId, functionalityWorkerId, loopId, turnId },
|
|
430
426
|
});
|
|
431
|
-
|
|
427
|
+
const post = await this.#proposals.applyResolution(logEntryId, effective);
|
|
428
|
+
await onSettled?.(logEntryId);
|
|
429
|
+
return post;
|
|
432
430
|
}
|
|
433
431
|
// Register the resolution waiter SYNCHRONOUSLY before any await
|
|
434
432
|
// yields. A same-tick resolveProposal() (e.g. from a test that
|
|
@@ -448,6 +446,7 @@ class Dispatcher {
|
|
|
448
446
|
}
|
|
449
447
|
catch (cause) {
|
|
450
448
|
await this.#proposals.failPreparation(logEntryId, cause);
|
|
449
|
+
await onSettled?.(logEntryId);
|
|
451
450
|
throw cause;
|
|
452
451
|
}
|
|
453
452
|
const resolution = await resolutionPromise;
|
|
@@ -464,8 +463,10 @@ class Dispatcher {
|
|
|
464
463
|
ids: { workspaceId, workerId, functionalityWorkerId, loopId, turnId },
|
|
465
464
|
});
|
|
466
465
|
const post = await this.#proposals.applyResolution(logEntryId, effective);
|
|
466
|
+
await onSettled?.(logEntryId);
|
|
467
467
|
return post;
|
|
468
468
|
}
|
|
469
|
+
await onSettled?.(logEntryId);
|
|
469
470
|
return result;
|
|
470
471
|
}
|
|
471
472
|
// {§op-look}: resolve a READ and return its content without writing a
|
|
@@ -479,11 +480,14 @@ class Dispatcher {
|
|
|
479
480
|
throw new Error(`look resolves READ only; got ${statement.op}`);
|
|
480
481
|
// turnId is a write-time FK only — a look writes no row, so 0 (no turn) is inert.
|
|
481
482
|
const schemeCtx = this.#buildSchemeCtx({ workspaceId, workerId, functionalityWorkerId: context.functionalityWorkerId, loopId, turnId: 0, origin });
|
|
482
|
-
const denial = await this.#
|
|
483
|
+
const denial = await this.#checkCapabilities(statement, schemeCtx.workspaceId, loopId, schemeCtx.functionalityWorkerId);
|
|
483
484
|
if (denial !== null)
|
|
484
485
|
return denial;
|
|
485
486
|
return this.#run(schemeNameOf(statement.target), statement, schemeCtx);
|
|
486
487
|
}
|
|
488
|
+
capabilityProjection(workspaceId, workerId) {
|
|
489
|
+
return this.#capabilities.projection(workspaceId, workerId);
|
|
490
|
+
}
|
|
487
491
|
// Resolve the client selector through the owning scheme before persistence
|
|
488
492
|
// is consulted. Public schemes choose a semantic owner; core-owned authority
|
|
489
493
|
// schemes may return the already-authorized principal key.
|
|
@@ -531,7 +535,13 @@ class Dispatcher {
|
|
|
531
535
|
}
|
|
532
536
|
return this.#entryAddresses.resolve({ target, routedScheme, handler, manifest, ctx });
|
|
533
537
|
}
|
|
534
|
-
async #prepareDataRepresentation({ target, routedScheme, handler, manifest, ctx, publishedChannel, resolved: priorResolution, }) {
|
|
538
|
+
async #prepareDataRepresentation({ target, metadata, routedScheme, handler, manifest, ctx, publishedChannel, resolved: priorResolution, }) {
|
|
539
|
+
if (metadata !== null && manifest.metadataModifier !== true) {
|
|
540
|
+
return {
|
|
541
|
+
address: null,
|
|
542
|
+
result: _a.#failure("scheme-metadata-unsupported", 400, `Scheme '${routedScheme}' does not accept the {metadata} modifier.`, {}, { scheme: routedScheme, retryable: false }),
|
|
543
|
+
};
|
|
544
|
+
}
|
|
535
545
|
const resolved = priorResolution ?? await this.#resolveDataEntryAddress({
|
|
536
546
|
target, routedScheme, handler, manifest, ctx,
|
|
537
547
|
});
|
|
@@ -555,6 +565,7 @@ class Dispatcher {
|
|
|
555
565
|
});
|
|
556
566
|
const prepared = Results.assertRepresentationPreparation(await handler.prepareRepresentation({
|
|
557
567
|
target: selectionNeutralTarget,
|
|
568
|
+
metadata,
|
|
558
569
|
authority: address.authority,
|
|
559
570
|
pathname: address.pathname,
|
|
560
571
|
}, preparationCtx));
|
|
@@ -598,7 +609,7 @@ class Dispatcher {
|
|
|
598
609
|
// catalog fallback `body`; resolved through the same registry the writable gate reads.
|
|
599
610
|
defaultChannelFor: (scheme) => this.#schemes.defaultChannelFor(scheme, functionalityWorkerId),
|
|
600
611
|
settleDerivations: () => this.#settleDerivations(workspaceId),
|
|
601
|
-
pushNotice: (notice) => this.#notices.push(workspaceId, loopId, notice),
|
|
612
|
+
pushNotice: (notice) => this.#notices.push(workspaceId, workerId, loopId, notice),
|
|
602
613
|
requestInteraction: (request) => this.#interactions.request(request, { workspaceId, workerId, loopId, turnId }, this.#loopSignal(loopId)),
|
|
603
614
|
executors: this.#executors(),
|
|
604
615
|
};
|
|
@@ -620,19 +631,27 @@ class Dispatcher {
|
|
|
620
631
|
if (statement.op === "EXEC") {
|
|
621
632
|
return this.#denyIfDisallowed("exec", origin, workerId);
|
|
622
633
|
}
|
|
634
|
+
// {§stream-control} — a KILL of a runtime stream terminates the caller's own process; it
|
|
635
|
+
// writes nothing into that read-only output scheme (writableBy plugin), so the writer rule
|
|
636
|
+
// does not speak. Exec.kill scopes the stream to the caller ({§stream-owner-scoped}).
|
|
637
|
+
if (statement.op === "KILL") {
|
|
638
|
+
const target = schemeNameOf(statement.target);
|
|
639
|
+
if (target !== null && this.#schemes.isRuntimeScheme(target, workerId))
|
|
640
|
+
return null;
|
|
641
|
+
}
|
|
623
642
|
// Worker control (FORK/WORK → worker://<name>, spawn or fork) is gated by worker://'s writableBy — its
|
|
624
643
|
// body is a seed prompt, not a dst path, so the entry-COPY dst-parse below doesn't apply.
|
|
625
644
|
// {§machine-processes}
|
|
626
645
|
if (this.#isWorkerControl(statement))
|
|
627
646
|
return this.#denyIfDisallowed("worker", origin, workerId);
|
|
628
647
|
if (statement.op === "COPY" || statement.op === "MOVE") {
|
|
629
|
-
const dst = statement.
|
|
648
|
+
const dst = statement.destination.target;
|
|
630
649
|
const dstScheme = schemeNameOf(dst);
|
|
631
650
|
const dstDenial = this.#denyIfDisallowed(dstScheme, origin, workerId);
|
|
632
651
|
if (dstDenial !== null)
|
|
633
652
|
return dstDenial;
|
|
634
653
|
if (statement.op === "MOVE") {
|
|
635
|
-
const srcScheme = schemeNameOf(statement.target);
|
|
654
|
+
const srcScheme = schemeNameOf(statement.source.target);
|
|
636
655
|
if (srcScheme !== dstScheme) {
|
|
637
656
|
const srcDenial = this.#denyIfDisallowed(srcScheme, origin, workerId);
|
|
638
657
|
if (srcDenial !== null)
|
|
@@ -643,14 +662,14 @@ class Dispatcher {
|
|
|
643
662
|
}
|
|
644
663
|
const target = schemeNameOf(statement.target);
|
|
645
664
|
const denial = this.#denyIfDisallowed(target, origin, workerId);
|
|
646
|
-
// {§send-target-recipient} —
|
|
647
|
-
//
|
|
648
|
-
//
|
|
665
|
+
// {§send-target-recipient} — SEND addresses recipients, not otherwise
|
|
666
|
+
// read-only resources. State that boundary without guessing whether the
|
|
667
|
+
// model intended a reply, deletion, or directed message.
|
|
649
668
|
if (denial !== null && statement.op === "SEND" && origin === "model") {
|
|
650
|
-
return _a.#failure("send-target-not-a-recipient", 400, "
|
|
669
|
+
return _a.#failure("send-target-not-a-recipient", 400, "The addressed scheme is not a SEND recipient.", {}, {
|
|
651
670
|
target: statement.target?.raw ?? String(target),
|
|
652
671
|
stage: "dispatch",
|
|
653
|
-
recovery: "A SEND
|
|
672
|
+
recovery: "A targetless SEND answers the active prompt; a directed SEND requires a recipient that implements SEND.",
|
|
654
673
|
retryable: false,
|
|
655
674
|
});
|
|
656
675
|
}
|
|
@@ -674,92 +693,25 @@ class Dispatcher {
|
|
|
674
693
|
retryable: false,
|
|
675
694
|
}); // {§scheme-surface-writableby-403}
|
|
676
695
|
}
|
|
677
|
-
//
|
|
678
|
-
//
|
|
679
|
-
//
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
async #checkFlagsGate(statement, loopId, functionalityWorkerId) {
|
|
684
|
-
const workerId = functionalityWorkerId;
|
|
685
|
-
// Broadcast SEND has no scheme to gate.
|
|
686
|
-
if (statement.op === "SEND" && statement.target === null)
|
|
687
|
-
return null;
|
|
688
|
-
const flags = await LoopFlagsReader.read(this.#db, loopId);
|
|
689
|
-
// Fast path: default flags gate nothing. (auto never gates.)
|
|
690
|
-
if (!flags.noWeb && !flags.noInteraction && flags.mode === "act")
|
|
696
|
+
// {§capability-admission} — one resolver gates every operation route before
|
|
697
|
+
// execution or proposal handling. Unknown routes continue to their ordinary
|
|
698
|
+
// owner; a known policy denial is one factual, non-presumptuous 403.
|
|
699
|
+
async #checkCapabilities(statement, workspaceId, loopId, functionalityWorkerId) {
|
|
700
|
+
const denied = await this.#capabilities.denial(statement, workspaceId, functionalityWorkerId, loopId);
|
|
701
|
+
if (denied === null)
|
|
691
702
|
return null;
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
writesFilesystem = isFile(statement.target);
|
|
705
|
-
else if (statement.op === "COPY")
|
|
706
|
-
writesFilesystem = isFile(statement.body?.target ?? null);
|
|
707
|
-
else if (statement.op === "MOVE")
|
|
708
|
-
writesFilesystem = isFile(statement.target) || isFile(statement.body?.target ?? null);
|
|
709
|
-
if (writesFilesystem) {
|
|
710
|
-
return _a.#failure("ask-mode-read-only", 403, `${statement.op} cannot change the filesystem in an ask-mode loop.`, {}, {
|
|
711
|
-
mode: flags.mode,
|
|
712
|
-
operation: statement.op,
|
|
713
|
-
recovery: "Answer or advise the user without changing the filesystem.",
|
|
714
|
-
retryable: false,
|
|
715
|
-
});
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
const active = this.#schemes.resolveForLoop(flags, workerId);
|
|
719
|
-
// {§mode-ask-read-only}: name the non-retryable restriction so the model changes course.
|
|
720
|
-
const restriction = flags.mode === "ask"
|
|
721
|
-
? "this is an ask-mode (read-only) loop — you cannot run commands or take host actions here"
|
|
722
|
-
: flags.noWeb && flags.noInteraction ? "web and interaction are disabled for this loop"
|
|
723
|
-
: flags.noWeb ? "web access is disabled for this loop"
|
|
724
|
-
: "interaction is disabled for this loop";
|
|
725
|
-
const checkScheme = (scheme) => {
|
|
726
|
-
if (scheme === null || !this.#schemes.has(scheme, workerId))
|
|
727
|
-
return null;
|
|
728
|
-
if (active.has(scheme))
|
|
729
|
-
return null;
|
|
730
|
-
return _a.#failure("scheme-unavailable", 403, `Scheme '${scheme}' is unavailable because ${restriction}.`, {}, {
|
|
731
|
-
scheme,
|
|
732
|
-
recovery: "Answer or advise the user without using the unavailable scheme.",
|
|
733
|
-
retryable: false,
|
|
734
|
-
});
|
|
735
|
-
};
|
|
736
|
-
const check = (target) => checkScheme(schemeNameOf(target));
|
|
737
|
-
if (this.#isWorkerControl(statement))
|
|
738
|
-
return check(statement.target); // body is a spawn/fork task, not a dst path
|
|
739
|
-
if (statement.op === "COPY" || statement.op === "MOVE") {
|
|
740
|
-
return check(statement.target) ?? check(statement.body?.target ?? null);
|
|
741
|
-
}
|
|
742
|
-
// {§exec-target-routing} — only a runtime-declared resource target adds
|
|
743
|
-
// source authority. Literal identifiers and path targets stay executor-local.
|
|
744
|
-
if (statement.op === "EXEC") {
|
|
745
|
-
const operationDenial = checkScheme("exec");
|
|
746
|
-
if (operationDenial !== null)
|
|
747
|
-
return operationDenial;
|
|
748
|
-
const requested = typeof statement.signal === "string" ? statement.signal : "";
|
|
749
|
-
const runtime = requested === "" ? "sh" : requested;
|
|
750
|
-
// {§manifest-flag-affinity} — a runtime alias is a registered scheme
|
|
751
|
-
// with its own affinity: the one resolver gates the selected runtime
|
|
752
|
-
// exactly as it gates the exec family (e.g. `question` under noInteraction).
|
|
753
|
-
const runtimeDenial = checkScheme(runtime);
|
|
754
|
-
if (runtimeDenial !== null)
|
|
755
|
-
return runtimeDenial;
|
|
756
|
-
const targetKind = this.#executors()?.entry(runtime, workerId)?.invocation.target?.kind;
|
|
757
|
-
if (targetKind !== "resource")
|
|
758
|
-
return null;
|
|
759
|
-
const sourceScheme = schemeNameOf(statement.target);
|
|
760
|
-
return sourceScheme === null || sourceScheme === "file" ? null : checkScheme(sourceScheme);
|
|
761
|
-
}
|
|
762
|
-
return check(statement.target);
|
|
703
|
+
const { descriptor, scope } = denied;
|
|
704
|
+
const route = [
|
|
705
|
+
descriptor.operation,
|
|
706
|
+
descriptor.scheme,
|
|
707
|
+
descriptor.runtime,
|
|
708
|
+
descriptor.tool,
|
|
709
|
+
].filter((part) => part !== undefined).join("/");
|
|
710
|
+
return _a.#failure("capability-denied", 403, `Capability '${route}' is denied by ${scope} policy.`, {}, {
|
|
711
|
+
...descriptor,
|
|
712
|
+
policyScope: scope,
|
|
713
|
+
retryable: false,
|
|
714
|
+
});
|
|
763
715
|
}
|
|
764
716
|
// Worker control is FORK/WORK (grammar 0.74.55), not COPY — its body
|
|
765
717
|
// is the new worker's seed prompt, not a destination path. The COPY gates and ResourceMutations.handleCopy
|
|
@@ -793,10 +745,15 @@ class Dispatcher {
|
|
|
793
745
|
if (denied !== null)
|
|
794
746
|
return denied;
|
|
795
747
|
const prompt = statement.body;
|
|
796
|
-
// {§worker-delegation-inherits-
|
|
797
|
-
//
|
|
798
|
-
//
|
|
799
|
-
const
|
|
748
|
+
// {§worker-delegation-inherits-policy} — authority flows down the
|
|
749
|
+
// delegation edge. The child receives the delegator's complete loop
|
|
750
|
+
// policy and an immutable snapshot of its effective capability bound.
|
|
751
|
+
const policy = await LoopPolicyReader.read(this.#db, ctx.loopId);
|
|
752
|
+
const capabilityBound = await CapabilityPolicies.delegationBound(this.#db, ctx.workspaceId, ctx.workerId, policy);
|
|
753
|
+
const delegationPolicy = {
|
|
754
|
+
...policy,
|
|
755
|
+
capabilities: capabilityBound,
|
|
756
|
+
};
|
|
800
757
|
// A name is frozen per worker but reclaimable across time ({§machine-processes-worker-origin}): a LIVE
|
|
801
758
|
// sister holding it is a 409 (legible, never a raw UNIQUE 500); a free/terminated name reclaims.
|
|
802
759
|
const live = await this.#db.worker_live_by_name.get({ workspace_id: ctx.workspaceId, name });
|
|
@@ -804,6 +761,16 @@ class Dispatcher {
|
|
|
804
761
|
return _a.#failure("worker-already-running", 409, `Worker '${name}' is already running.`, {}, { worker: name, retryable: false });
|
|
805
762
|
}
|
|
806
763
|
if (typeof statement.signal === "string") {
|
|
764
|
+
// {§branch-delegation-disabled} — off the model-facing surface until it is specified and
|
|
765
|
+
// witnessed end to end (#396); the batches stay behind the operator knob.
|
|
766
|
+
if (process.env.PLURNK_SERVICE_BRANCH_DELEGATION !== "1") {
|
|
767
|
+
return _a.#failure("branch-delegation-disabled", 501, `${statement.op} takes no signal; branch delegation is not offered.`, {}, {
|
|
768
|
+
worker: name,
|
|
769
|
+
signal: statement.signal,
|
|
770
|
+
recovery: `Spawn without a signal: \`## ${statement.op}0 (worker://${name})\` with the prompt as the body.`,
|
|
771
|
+
retryable: false,
|
|
772
|
+
});
|
|
773
|
+
}
|
|
807
774
|
if (this.#branchWorker === undefined)
|
|
808
775
|
throw new Error("branch worker control: branchWorker capability absent");
|
|
809
776
|
const child = await this.#branchWorker({
|
|
@@ -815,7 +782,7 @@ class Dispatcher {
|
|
|
815
782
|
name,
|
|
816
783
|
branch: statement.signal,
|
|
817
784
|
prompt,
|
|
818
|
-
|
|
785
|
+
policy: delegationPolicy,
|
|
819
786
|
origin: ctx.writer,
|
|
820
787
|
});
|
|
821
788
|
return {
|
|
@@ -826,13 +793,13 @@ class Dispatcher {
|
|
|
826
793
|
}
|
|
827
794
|
if (statement.op === "FORK") {
|
|
828
795
|
// Branch the current worker's log into a named sister.
|
|
829
|
-
const branchWorkerId = await Fork.fork(this.#db, ctx.workerId, name, (scheme) => this.#schemes.entryInheritanceForStoredScheme(scheme, ctx.workerId));
|
|
796
|
+
const branchWorkerId = await Fork.fork(this.#db, ctx.workerId, name, capabilityBound, (scheme) => this.#schemes.entryInheritanceForStoredScheme(scheme, ctx.workerId));
|
|
830
797
|
await ctx.injectWorker({
|
|
831
798
|
workspaceId: ctx.workspaceId,
|
|
832
799
|
workerId: branchWorkerId,
|
|
833
800
|
sourceWorkerId: ctx.workerId,
|
|
834
801
|
prompt,
|
|
835
|
-
|
|
802
|
+
freshLoopPolicy: delegationPolicy,
|
|
836
803
|
parentLoopId: ctx.loopId,
|
|
837
804
|
});
|
|
838
805
|
return { status: 200, body: name };
|
|
@@ -841,6 +808,7 @@ class Dispatcher {
|
|
|
841
808
|
const row = await this.#db.fork_insert_worker.get({
|
|
842
809
|
workspace_id: ctx.workspaceId, name, parent_worker_id: ctx.workerId, origin: ctx.writer,
|
|
843
810
|
fork_snapshot: 0,
|
|
811
|
+
capability_bound: JSON.stringify(capabilityBound),
|
|
844
812
|
});
|
|
845
813
|
if (row === undefined)
|
|
846
814
|
throw new Error("worker spawn: worker insert returned no row");
|
|
@@ -849,7 +817,7 @@ class Dispatcher {
|
|
|
849
817
|
workerId: row.id,
|
|
850
818
|
sourceWorkerId: ctx.workerId,
|
|
851
819
|
prompt,
|
|
852
|
-
|
|
820
|
+
freshLoopPolicy: delegationPolicy,
|
|
853
821
|
parentLoopId: ctx.loopId,
|
|
854
822
|
});
|
|
855
823
|
return { status: 200, body: name };
|
|
@@ -869,8 +837,8 @@ class Dispatcher {
|
|
|
869
837
|
}
|
|
870
838
|
const manifest = this.#schemes.manifestFor(schemeName, ctx.functionalityWorkerId);
|
|
871
839
|
const coordinate = entryCoordinateOf(path, manifest?.authority ?? "namespace");
|
|
872
|
-
//
|
|
873
|
-
//
|
|
840
|
+
// log:/// KILL has already gone through the projection-curation owner.
|
|
841
|
+
// This path owns scheme-specific world and process KILL semantics.
|
|
874
842
|
// Process-KILL: any scheme whose handler exposes kill() aborts a live stream — the
|
|
875
843
|
// exec handler, registered as "exec" + under every runtime tag (sh/node), so a tag-
|
|
876
844
|
// addressed stream (sh:///l/t/s) routes here, not to deleteEntry. {§exec}
|
|
@@ -997,11 +965,10 @@ class Dispatcher {
|
|
|
997
965
|
}, this.#weighContent),
|
|
998
966
|
state: "resolved", outcome: null,
|
|
999
967
|
attrs: JSON.stringify(durableAttrs),
|
|
968
|
+
initial_folded: LogVisibility.serialize(folded ? LogVisibility.FOLDED : LogVisibility.OPEN),
|
|
1000
969
|
});
|
|
1001
970
|
if (row === undefined)
|
|
1002
971
|
throw new Error("Dispatcher.#writeActionlessEntry: insert returned no row");
|
|
1003
|
-
if (folded)
|
|
1004
|
-
await this.#db.engine_fold_log_entry.run({ id: row.id });
|
|
1005
972
|
return row.id;
|
|
1006
973
|
}
|
|
1007
974
|
// {§turn-ops-entry} — preserve exact admitted source beside, never instead
|
|
@@ -1026,7 +993,7 @@ class Dispatcher {
|
|
|
1026
993
|
},
|
|
1027
994
|
});
|
|
1028
995
|
}
|
|
1029
|
-
// PLAN — the model's
|
|
996
|
+
// PLAN — one installment of the model's running work journal. An ordinary op: dispatched like any
|
|
1030
997
|
// other, logged, and broadcast to the client as a log entry — but a pure no-op for
|
|
1031
998
|
// state (PLAN ∉ MUTATING_OPS); its body serializes into the log row's tx, no effect.
|
|
1032
999
|
#handlePlan(statement) {
|
|
@@ -1047,6 +1014,7 @@ class Dispatcher {
|
|
|
1047
1014
|
modelCallId,
|
|
1048
1015
|
});
|
|
1049
1016
|
context.onDispatch?.(logEntryId);
|
|
1017
|
+
await context.onSettled?.(logEntryId);
|
|
1050
1018
|
return result;
|
|
1051
1019
|
}
|
|
1052
1020
|
// {§send-premature-terminate} — the unified PENDING SET, judged at the terminal's OWN dispatch
|
|
@@ -1061,23 +1029,20 @@ class Dispatcher {
|
|
|
1061
1029
|
const openSubs = await this.#db.find_open_subscriptions_for_worker.all({ worker_id: workerId });
|
|
1062
1030
|
const execHandler = this.#schemes.get("exec");
|
|
1063
1031
|
if (openSubs.length > 0 || execHandler?.hasActiveSpawns?.(workerId) === true)
|
|
1064
|
-
pending.push("
|
|
1032
|
+
pending.push("streams");
|
|
1065
1033
|
const liveChild = await this.#db.engine_worker_has_live_child.get({ worker_id: workerId });
|
|
1066
1034
|
if (liveChild !== undefined)
|
|
1067
|
-
pending.push("
|
|
1035
|
+
pending.push("workers");
|
|
1068
1036
|
const boundaries = await this.#nextPacketBoundaries(workerId, turnId);
|
|
1069
1037
|
if (boundaries.retrievals)
|
|
1070
|
-
pending.push("
|
|
1038
|
+
pending.push("receipts");
|
|
1071
1039
|
// A stream that closed successfully is banked, not pending: concluding on its own
|
|
1072
1040
|
// success is legitimate and its output stays in the Log. A failed close is an unseen
|
|
1073
1041
|
// failure — named exactly (bench#5 requiem #9) so the model reads it, not guesses.
|
|
1074
|
-
|
|
1075
|
-
.
|
|
1076
|
-
.map(({ handle }) => handle);
|
|
1077
|
-
if (failedStreams.length > 0)
|
|
1078
|
-
pending.push(`failed stream results that land in the NEXT packet's Log: ${failedStreams.join("; ")}`);
|
|
1042
|
+
if (boundaries.streamTerminations.some(({ closeStatus }) => closeStatus >= 400))
|
|
1043
|
+
pending.push("failed-stream-results");
|
|
1079
1044
|
if (boundaries.childTerminations)
|
|
1080
|
-
pending.push("worker
|
|
1045
|
+
pending.push("worker-results");
|
|
1081
1046
|
return pending;
|
|
1082
1047
|
}
|
|
1083
1048
|
// Results cross an observation boundary only when they have appeared in a packet;
|
|
@@ -1109,10 +1074,9 @@ class Dispatcher {
|
|
|
1109
1074
|
return failedRows.length;
|
|
1110
1075
|
}
|
|
1111
1076
|
static #unobservedFailures(failCount) {
|
|
1112
|
-
return _a.#failure("unobserved-failures", 409, `
|
|
1077
|
+
return _a.#failure("unobserved-failures", 409, `This turn produced ${failCount} failed operation result(s) that have not yet entered a packet.`, {}, {
|
|
1113
1078
|
failures: failCount,
|
|
1114
1079
|
stage: "completion",
|
|
1115
|
-
recovery: "Review the failed log items before concluding.",
|
|
1116
1080
|
retryable: false,
|
|
1117
1081
|
});
|
|
1118
1082
|
}
|
|
@@ -1165,7 +1129,8 @@ class Dispatcher {
|
|
|
1165
1129
|
// retrieval (R) just lands next turn, so the wait continues.
|
|
1166
1130
|
if (status === 202) {
|
|
1167
1131
|
const marks = statement.lineMarker?.marks[0];
|
|
1168
|
-
|
|
1132
|
+
// `<T>` is MINUTES, held in seconds; bare 202 / absent T = indefinite, bounded by the join.
|
|
1133
|
+
const seconds = typeof marks === "number" ? (marks > 0 ? marks * 60 : marks) : -1;
|
|
1169
1134
|
if (await this.#hasLiveWork(workerId)) {
|
|
1170
1135
|
if (!await this.#lifecycle.park(loopId)) {
|
|
1171
1136
|
return _a.#statusResult(await this.#lifecycle.status(loopId), "loop-already-terminal", "The loop was already terminal when SEND attempted to wait.");
|
|
@@ -1214,19 +1179,17 @@ class Dispatcher {
|
|
|
1214
1179
|
if (pending.length > 0) {
|
|
1215
1180
|
// A receipts-only refusal needs no KILL/park remedy menu: the results simply
|
|
1216
1181
|
// arrive in the next packet. Streams and children retain their remedy steer.
|
|
1217
|
-
const receiptsOnly = pending.every((
|
|
1182
|
+
const receiptsOnly = pending.every((kind) => kind === "receipts");
|
|
1218
1183
|
if (receiptsOnly) {
|
|
1219
|
-
return _a.#failure("retrieval-results-unobserved", 409, "
|
|
1184
|
+
return _a.#failure("retrieval-results-unobserved", 409, "Completion preceded this turn's operation results; they enter the next packet.", {}, {
|
|
1220
1185
|
pending: [...pending],
|
|
1221
1186
|
stage: "completion",
|
|
1222
|
-
recovery: "Review the results, then use only `# PLAN0` and `## SEND0 [200]` to conclude.",
|
|
1223
1187
|
retryable: false,
|
|
1224
1188
|
});
|
|
1225
1189
|
}
|
|
1226
|
-
return _a.#failure("work-remains", 409,
|
|
1190
|
+
return _a.#failure("work-remains", 409, "Completion encountered pending work or results.", {}, {
|
|
1227
1191
|
pending: [...pending],
|
|
1228
1192
|
stage: "completion",
|
|
1229
|
-
recovery: "Resolve the listed pending work before concluding.",
|
|
1230
1193
|
retryable: false,
|
|
1231
1194
|
});
|
|
1232
1195
|
}
|
|
@@ -1241,7 +1204,11 @@ class Dispatcher {
|
|
|
1241
1204
|
const branchDenial = await this.#branchCompletionGate?.(workerId) ?? null;
|
|
1242
1205
|
if (branchDenial !== null)
|
|
1243
1206
|
return branchDenial;
|
|
1244
|
-
const
|
|
1207
|
+
const reason = raw === "" ? null : ErrorDetail.preview(raw);
|
|
1208
|
+
const failure = _a.#failure("scope-abandoned", 499, "The worker ended its scope with SEND[499].", {}, {
|
|
1209
|
+
...(reason === null ? {} : { reason }),
|
|
1210
|
+
retryable: false,
|
|
1211
|
+
});
|
|
1245
1212
|
const seqs = await this.#db.engine_loop_turn_seqs.get({
|
|
1246
1213
|
loop_id: loopId,
|
|
1247
1214
|
turn_id: turnId,
|
|
@@ -1253,7 +1220,7 @@ class Dispatcher {
|
|
|
1253
1220
|
const finished = await this.#lifecycle.finish(loopId, failure);
|
|
1254
1221
|
if (finished === null)
|
|
1255
1222
|
return _a.#statusResult(await this.#lifecycle.status(loopId), "loop-already-terminal", "The loop was already terminal when SEND attempted to abandon it.");
|
|
1256
|
-
await this.#cancelDescendants?.(workerId,
|
|
1223
|
+
await this.#cancelDescendants?.(workerId, reason ?? "parent worker ended its scope with SEND[499]");
|
|
1257
1224
|
return failure;
|
|
1258
1225
|
}
|
|
1259
1226
|
// Every other signal — 102 bare, 202 (retired as a terminal; now ordinary mid-comms), 1xx —
|
|
@@ -1287,6 +1254,27 @@ class Dispatcher {
|
|
|
1287
1254
|
const addressedScheme = statement.target?.kind === "url" ? statement.target.scheme : null;
|
|
1288
1255
|
if (manifest === undefined)
|
|
1289
1256
|
throw new Error(`scheme '${schemeName}' has no manifest`);
|
|
1257
|
+
// Metadata belongs to the handler that receives it. EXEC over a
|
|
1258
|
+
// non-file resource is the one split route: exec hosts the operation,
|
|
1259
|
+
// while the canonically routed source scheme receives the metadata.
|
|
1260
|
+
const metadataScheme = schemeName === "exec"
|
|
1261
|
+
&& statement.target?.kind === "url"
|
|
1262
|
+
&& statement.target.scheme !== "file"
|
|
1263
|
+
? schemeNameOf(statement.target) ?? schemeName
|
|
1264
|
+
: schemeName;
|
|
1265
|
+
const metadataManifest = metadataScheme === schemeName
|
|
1266
|
+
? manifest
|
|
1267
|
+
: this.#schemes.manifestFor(metadataScheme, ctx.functionalityWorkerId);
|
|
1268
|
+
if (statement.metadata !== null && metadataManifest === undefined) {
|
|
1269
|
+
return _a.#failure("scheme-not-found", 501, `Scheme '${metadataScheme}' is not registered.`, {}, { scheme: metadataScheme, retryable: false });
|
|
1270
|
+
}
|
|
1271
|
+
if (statement.metadata !== null && metadataManifest?.metadataModifier !== true) {
|
|
1272
|
+
return _a.#failure("scheme-metadata-unsupported", 400, `Scheme '${metadataScheme}' does not accept the {metadata} modifier.`, {}, {
|
|
1273
|
+
scheme: metadataScheme,
|
|
1274
|
+
operation: statement.op,
|
|
1275
|
+
retryable: false,
|
|
1276
|
+
});
|
|
1277
|
+
}
|
|
1290
1278
|
const publishedChannel = statement.target?.kind === "url"
|
|
1291
1279
|
? statement.target.fragment ?? manifest.defaultChannel
|
|
1292
1280
|
: manifest.defaultChannel;
|
|
@@ -1379,6 +1367,7 @@ class Dispatcher {
|
|
|
1379
1367
|
? { address: null, result: null }
|
|
1380
1368
|
: await this.#prepareDataRepresentation({
|
|
1381
1369
|
target: statement.target,
|
|
1370
|
+
metadata: statement.metadata,
|
|
1382
1371
|
routedScheme: schemeName,
|
|
1383
1372
|
handler: handler,
|
|
1384
1373
|
manifest,
|
|
@@ -1412,10 +1401,6 @@ class Dispatcher {
|
|
|
1412
1401
|
return _a.#failure("operation-not-implemented", 501, `Scheme '${schemeName}' does not implement ${statement.op}.`, {}, {
|
|
1413
1402
|
scheme: schemeName,
|
|
1414
1403
|
operation: statement.op,
|
|
1415
|
-
// {§send-target-recipient} — a model's SEND to a file path was meant as the reply.
|
|
1416
|
-
...(statement.op === "SEND"
|
|
1417
|
-
? { recovery: "`## SEND0 [200]` answers the active prompt with no target. A SEND target is a recipient — `## SEND0 (worker://<name>)` — or, with `[410]`, a resource to delete." }
|
|
1418
|
-
: {}),
|
|
1419
1404
|
retryable: false,
|
|
1420
1405
|
});
|
|
1421
1406
|
}
|
|
@@ -1430,6 +1415,7 @@ class Dispatcher {
|
|
|
1430
1415
|
if (exactTarget && statement.target !== null) {
|
|
1431
1416
|
const prepared = await this.#prepareDataRepresentation({
|
|
1432
1417
|
target: statement.target,
|
|
1418
|
+
metadata: statement.metadata,
|
|
1433
1419
|
routedScheme: schemeName,
|
|
1434
1420
|
handler: handler,
|
|
1435
1421
|
manifest,
|
|
@@ -1504,10 +1490,11 @@ class Dispatcher {
|
|
|
1504
1490
|
}
|
|
1505
1491
|
async #writeLog({ statement, result, workspaceId, workerId, functionalityWorkerId, loopId, turnId, sequence, origin, curationPlan, modelCallId, }) {
|
|
1506
1492
|
const durableStatement = DurableStatement.project(statement);
|
|
1507
|
-
const target = this.#extractTarget(durableStatement
|
|
1493
|
+
const target = this.#extractTarget(primaryTargetOf(durableStatement), functionalityWorkerId);
|
|
1508
1494
|
await this.#canonColumns(target, workspaceId); // {§fs-answer-in-canon}
|
|
1509
|
-
const
|
|
1510
|
-
|
|
1495
|
+
const lineMarker = primaryLineMarkerOf(durableStatement);
|
|
1496
|
+
const lineMarkerJson = lineMarker !== null
|
|
1497
|
+
? JSON.stringify(lineMarker)
|
|
1511
1498
|
: null;
|
|
1512
1499
|
// A proposal (status 202 from a side-effecting op) is written to the log in
|
|
1513
1500
|
// state='proposed' until the proposal lifecycle resolves it; attrs holds the
|
|
@@ -1607,6 +1594,7 @@ class Dispatcher {
|
|
|
1607
1594
|
state: isProposed ? "proposed" : "resolved",
|
|
1608
1595
|
outcome: null,
|
|
1609
1596
|
attrs,
|
|
1597
|
+
initial_folded: LogVisibility.serialize(LogVisibility.OPEN),
|
|
1610
1598
|
});
|
|
1611
1599
|
// {§exec-tag-signal} — an EXEC row's signal is its runtime; its tag signal classifies
|
|
1612
1600
|
// through the same idempotent primitive the log-write trigger uses for tag operations.
|