@plurnk/plurnk-service 1.10.1 → 1.12.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 +4 -4
- package/README.md +2 -2
- package/SPEC.md +573 -273
- 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.d.ts +1 -1
- package/dist/content/edit-collision.d.ts.map +1 -1
- package/dist/content/edit-collision.js +5 -4
- 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 +28 -8
- 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 +15 -6
- package/dist/content/line-anchors.js.map +1 -1
- package/dist/content/line-marker.d.ts +0 -1
- package/dist/content/line-marker.d.ts.map +1 -1
- package/dist/content/line-marker.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 +2 -2
- package/dist/core/CoreSchemeServices.d.ts.map +1 -1
- package/dist/core/CoreSchemeServices.js +2 -2
- package/dist/core/CoreSchemeServices.js.map +1 -1
- package/dist/core/Dispatcher.d.ts +5 -2
- package/dist/core/Dispatcher.d.ts.map +1 -1
- package/dist/core/Dispatcher.js +157 -165
- 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/EmbeddingCall.d.ts +23 -0
- package/dist/core/EmbeddingCall.d.ts.map +1 -0
- package/dist/core/EmbeddingCall.js +149 -0
- package/dist/core/EmbeddingCall.js.map +1 -0
- package/dist/core/Engine.d.ts +6 -4
- package/dist/core/Engine.d.ts.map +1 -1
- package/dist/core/Engine.js +20 -9
- package/dist/core/Engine.js.map +1 -1
- package/dist/core/Engine.sql +119 -56
- 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/InferenceCall.d.ts +18 -0
- package/dist/core/InferenceCall.d.ts.map +1 -0
- package/dist/core/InferenceCall.js +86 -0
- package/dist/core/InferenceCall.js.map +1 -0
- 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 +4 -12
- package/dist/core/ModelCall.d.ts.map +1 -1
- package/dist/core/ModelCall.js +10 -74
- 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 +55 -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 +6 -4
- package/dist/core/ResourceMutations.d.ts.map +1 -1
- package/dist/core/ResourceMutations.js +209 -63
- 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.d.ts +1 -0
- package/dist/core/StrikeRail.d.ts.map +1 -1
- package/dist/core/StrikeRail.js +12 -3
- 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/Turn.sql +6 -3
- 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 -4
- package/dist/core/TurnRunner.d.ts.map +1 -1
- package/dist/core/TurnRunner.js +345 -83
- 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 +11 -0
- package/dist/core/packet-wire.d.ts.map +1 -1
- package/dist/core/packet-wire.js +98 -34
- 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 +151 -42
- package/dist/digest/Digest.js.map +1 -1
- package/dist/digest/digest.sql +48 -20
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/schemes/Exec.d.ts +4 -5
- package/dist/schemes/Exec.d.ts.map +1 -1
- package/dist/schemes/Exec.js +111 -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 +26 -19
- 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 +132 -39
- 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.d.ts.map +1 -1
- package/dist/schemes/_entry-chunk.js +6 -2
- 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 +9 -14
- 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 +15 -5
- package/dist/schemes/_entry-ops.js.map +1 -1
- package/dist/schemes/_entry-semantic.d.ts +5 -3
- package/dist/schemes/_entry-semantic.d.ts.map +1 -1
- package/dist/schemes/_entry-semantic.js +21 -9
- package/dist/schemes/_entry-semantic.js.map +1 -1
- package/dist/schemes/_path-scope.d.ts.map +1 -1
- package/dist/schemes/_path-scope.js +6 -1
- package/dist/schemes/_path-scope.js.map +1 -1
- package/dist/schemes/_search-index.d.ts.map +1 -1
- package/dist/schemes/_search-index.js +14 -2
- package/dist/schemes/_search-index.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 +60 -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/lifecycle-recovery.sql +14 -20
- 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 +18 -23
- package/dist/service.js.map +1 -1
- package/migrations/001_schema.sql +477 -131
- package/package.json +31 -33
- 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({
|
|
@@ -283,21 +290,24 @@ class Dispatcher {
|
|
|
283
290
|
}
|
|
284
291
|
async dispatch(context) {
|
|
285
292
|
assertClassifyingSignal(context.statement); // {§log-tag-signal}
|
|
286
|
-
|
|
293
|
+
let result = await this.#dispatchOne(context);
|
|
287
294
|
if (context.statement.op === "EDIT") {
|
|
295
|
+
// {§edit-batch-merges} — a proposal's apply result replaces the projected one; the
|
|
296
|
+
// statement's merge facts ride every EDIT result, whichever route produced it.
|
|
297
|
+
result = this.#resourceMutations.withMergeFacts(context.statement, result);
|
|
288
298
|
this.#resourceMutations.settleEdit(context.statement, result);
|
|
289
299
|
}
|
|
290
300
|
return result;
|
|
291
301
|
}
|
|
292
302
|
async #dispatchOne(context) {
|
|
293
|
-
const { statement, workspaceId, workerId, loopId, turnId, sequence, origin, onDispatch, } = context;
|
|
303
|
+
const { statement, workspaceId, workerId, loopId, turnId, sequence, origin, onDispatch, onSettled, } = context;
|
|
294
304
|
const schemeCtx = this.#buildSchemeCtx({ workspaceId, workerId, functionalityWorkerId: context.functionalityWorkerId, loopId, turnId, origin });
|
|
295
305
|
const { functionalityWorkerId } = schemeCtx;
|
|
296
306
|
let result;
|
|
297
307
|
let curationPlan = null;
|
|
298
308
|
let denial = this.#checkWritable(statement, origin, functionalityWorkerId);
|
|
299
309
|
if (denial === null)
|
|
300
|
-
denial = await this.#
|
|
310
|
+
denial = await this.#checkCapabilities(statement, workspaceId, loopId, functionalityWorkerId);
|
|
301
311
|
if (denial !== null) {
|
|
302
312
|
result = denial;
|
|
303
313
|
}
|
|
@@ -321,7 +331,9 @@ class Dispatcher {
|
|
|
321
331
|
origin,
|
|
322
332
|
});
|
|
323
333
|
}
|
|
324
|
-
else if (statement.op === "OPEN"
|
|
334
|
+
else if (statement.op === "OPEN"
|
|
335
|
+
|| statement.op === "FOLD"
|
|
336
|
+
|| (statement.op === "KILL" && schemeNameOf(statement.target) === "log")) {
|
|
325
337
|
const curation = await this.#runLogCuration(statement, schemeCtx);
|
|
326
338
|
result = curation.result;
|
|
327
339
|
curationPlan = curation.plan;
|
|
@@ -342,22 +354,9 @@ class Dispatcher {
|
|
|
342
354
|
result = this.#handlePlan(statement);
|
|
343
355
|
}
|
|
344
356
|
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
|
-
}
|
|
357
|
+
// EXEC routes unconditionally to its operation owner after
|
|
358
|
+
// the shared capability resolver admits its runtime/tool.
|
|
359
|
+
result = await this.#run("exec", statement, schemeCtx);
|
|
361
360
|
}
|
|
362
361
|
else {
|
|
363
362
|
result = await this.#run(schemeNameOf(statement.target), statement, schemeCtx); // {§op-methods-op-dispatch}
|
|
@@ -370,7 +369,7 @@ class Dispatcher {
|
|
|
370
369
|
result = err.result;
|
|
371
370
|
}
|
|
372
371
|
else {
|
|
373
|
-
const scheme = schemeNameOf(statement
|
|
372
|
+
const scheme = schemeNameOf(primaryTargetOf(statement));
|
|
374
373
|
console.error(`Scheme '${scheme ?? "unknown"}' ${statement.op} threw outside its operation result contract:`, err);
|
|
375
374
|
result = _a.#failure("scheme-handler-threw", 500, `The '${scheme ?? "unknown"}' scheme did not produce a ${statement.op} result.`, {}, {
|
|
376
375
|
stage: "scheme-dispatch",
|
|
@@ -380,8 +379,8 @@ class Dispatcher {
|
|
|
380
379
|
}
|
|
381
380
|
}
|
|
382
381
|
}
|
|
383
|
-
//
|
|
384
|
-
//
|
|
382
|
+
// Persist log curation for forensics; packet rendering suppresses its
|
|
383
|
+
// successful receipts while the exact state effects remain durable.
|
|
385
384
|
// A running-worker READ arms this turn's blocking collect.
|
|
386
385
|
// {§join-blocking-collect}
|
|
387
386
|
if (typeof result.awaitWorker === "string")
|
|
@@ -428,7 +427,9 @@ class Dispatcher {
|
|
|
428
427
|
ctx: schemeCtx,
|
|
429
428
|
ids: { workspaceId, workerId, functionalityWorkerId, loopId, turnId },
|
|
430
429
|
});
|
|
431
|
-
|
|
430
|
+
const post = await this.#proposals.applyResolution(logEntryId, effective);
|
|
431
|
+
await onSettled?.(logEntryId);
|
|
432
|
+
return post;
|
|
432
433
|
}
|
|
433
434
|
// Register the resolution waiter SYNCHRONOUSLY before any await
|
|
434
435
|
// yields. A same-tick resolveProposal() (e.g. from a test that
|
|
@@ -448,6 +449,7 @@ class Dispatcher {
|
|
|
448
449
|
}
|
|
449
450
|
catch (cause) {
|
|
450
451
|
await this.#proposals.failPreparation(logEntryId, cause);
|
|
452
|
+
await onSettled?.(logEntryId);
|
|
451
453
|
throw cause;
|
|
452
454
|
}
|
|
453
455
|
const resolution = await resolutionPromise;
|
|
@@ -464,8 +466,10 @@ class Dispatcher {
|
|
|
464
466
|
ids: { workspaceId, workerId, functionalityWorkerId, loopId, turnId },
|
|
465
467
|
});
|
|
466
468
|
const post = await this.#proposals.applyResolution(logEntryId, effective);
|
|
469
|
+
await onSettled?.(logEntryId);
|
|
467
470
|
return post;
|
|
468
471
|
}
|
|
472
|
+
await onSettled?.(logEntryId);
|
|
469
473
|
return result;
|
|
470
474
|
}
|
|
471
475
|
// {§op-look}: resolve a READ and return its content without writing a
|
|
@@ -479,11 +483,14 @@ class Dispatcher {
|
|
|
479
483
|
throw new Error(`look resolves READ only; got ${statement.op}`);
|
|
480
484
|
// turnId is a write-time FK only — a look writes no row, so 0 (no turn) is inert.
|
|
481
485
|
const schemeCtx = this.#buildSchemeCtx({ workspaceId, workerId, functionalityWorkerId: context.functionalityWorkerId, loopId, turnId: 0, origin });
|
|
482
|
-
const denial = await this.#
|
|
486
|
+
const denial = await this.#checkCapabilities(statement, schemeCtx.workspaceId, loopId, schemeCtx.functionalityWorkerId);
|
|
483
487
|
if (denial !== null)
|
|
484
488
|
return denial;
|
|
485
489
|
return this.#run(schemeNameOf(statement.target), statement, schemeCtx);
|
|
486
490
|
}
|
|
491
|
+
capabilityProjection(workspaceId, workerId) {
|
|
492
|
+
return this.#capabilities.projection(workspaceId, workerId);
|
|
493
|
+
}
|
|
487
494
|
// Resolve the client selector through the owning scheme before persistence
|
|
488
495
|
// is consulted. Public schemes choose a semantic owner; core-owned authority
|
|
489
496
|
// schemes may return the already-authorized principal key.
|
|
@@ -531,7 +538,13 @@ class Dispatcher {
|
|
|
531
538
|
}
|
|
532
539
|
return this.#entryAddresses.resolve({ target, routedScheme, handler, manifest, ctx });
|
|
533
540
|
}
|
|
534
|
-
async #prepareDataRepresentation({ target, routedScheme, handler, manifest, ctx, publishedChannel, resolved: priorResolution, }) {
|
|
541
|
+
async #prepareDataRepresentation({ target, metadata, routedScheme, handler, manifest, ctx, publishedChannel, resolved: priorResolution, }) {
|
|
542
|
+
if (metadata !== null && manifest.metadataModifier !== true) {
|
|
543
|
+
return {
|
|
544
|
+
address: null,
|
|
545
|
+
result: _a.#failure("scheme-metadata-unsupported", 400, `Scheme '${routedScheme}' does not accept the {metadata} modifier.`, {}, { scheme: routedScheme, retryable: false }),
|
|
546
|
+
};
|
|
547
|
+
}
|
|
535
548
|
const resolved = priorResolution ?? await this.#resolveDataEntryAddress({
|
|
536
549
|
target, routedScheme, handler, manifest, ctx,
|
|
537
550
|
});
|
|
@@ -555,6 +568,7 @@ class Dispatcher {
|
|
|
555
568
|
});
|
|
556
569
|
const prepared = Results.assertRepresentationPreparation(await handler.prepareRepresentation({
|
|
557
570
|
target: selectionNeutralTarget,
|
|
571
|
+
metadata,
|
|
558
572
|
authority: address.authority,
|
|
559
573
|
pathname: address.pathname,
|
|
560
574
|
}, preparationCtx));
|
|
@@ -584,7 +598,7 @@ class Dispatcher {
|
|
|
584
598
|
#buildSchemeCtx(ids) {
|
|
585
599
|
const { workspaceId, workerId, loopId, turnId, origin } = ids;
|
|
586
600
|
const functionalityWorkerId = ids.functionalityWorkerId ?? workerId;
|
|
587
|
-
|
|
601
|
+
const context = {
|
|
588
602
|
db: this.#db,
|
|
589
603
|
workspaceId, workerId, functionalityWorkerId, loopId, turnId,
|
|
590
604
|
writer: origin,
|
|
@@ -597,11 +611,12 @@ class Dispatcher {
|
|
|
597
611
|
// {§exec-stream} — a runtime scheme's default channel is its own (stdout), never the
|
|
598
612
|
// catalog fallback `body`; resolved through the same registry the writable gate reads.
|
|
599
613
|
defaultChannelFor: (scheme) => this.#schemes.defaultChannelFor(scheme, functionalityWorkerId),
|
|
600
|
-
settleDerivations: () => this.#settleDerivations(
|
|
601
|
-
pushNotice: (notice) => this.#notices.push(workspaceId, loopId, notice),
|
|
614
|
+
settleDerivations: () => this.#settleDerivations(context),
|
|
615
|
+
pushNotice: (notice) => this.#notices.push(workspaceId, workerId, loopId, notice),
|
|
602
616
|
requestInteraction: (request) => this.#interactions.request(request, { workspaceId, workerId, loopId, turnId }, this.#loopSignal(loopId)),
|
|
603
617
|
executors: this.#executors(),
|
|
604
618
|
};
|
|
619
|
+
return context;
|
|
605
620
|
}
|
|
606
621
|
// SPEC {§scheme-surface}: engine rejects writes whose origin is outside the target
|
|
607
622
|
// scheme's manifest.writableBy.
|
|
@@ -620,19 +635,27 @@ class Dispatcher {
|
|
|
620
635
|
if (statement.op === "EXEC") {
|
|
621
636
|
return this.#denyIfDisallowed("exec", origin, workerId);
|
|
622
637
|
}
|
|
638
|
+
// {§stream-control} — a KILL of a runtime stream terminates the caller's own process; it
|
|
639
|
+
// writes nothing into that read-only output scheme (writableBy plugin), so the writer rule
|
|
640
|
+
// does not speak. Exec.kill scopes the stream to the caller ({§stream-owner-scoped}).
|
|
641
|
+
if (statement.op === "KILL") {
|
|
642
|
+
const target = schemeNameOf(statement.target);
|
|
643
|
+
if (target !== null && this.#schemes.isRuntimeScheme(target, workerId))
|
|
644
|
+
return null;
|
|
645
|
+
}
|
|
623
646
|
// Worker control (FORK/WORK → worker://<name>, spawn or fork) is gated by worker://'s writableBy — its
|
|
624
647
|
// body is a seed prompt, not a dst path, so the entry-COPY dst-parse below doesn't apply.
|
|
625
648
|
// {§machine-processes}
|
|
626
649
|
if (this.#isWorkerControl(statement))
|
|
627
650
|
return this.#denyIfDisallowed("worker", origin, workerId);
|
|
628
651
|
if (statement.op === "COPY" || statement.op === "MOVE") {
|
|
629
|
-
const dst = statement.
|
|
652
|
+
const dst = statement.destination.target;
|
|
630
653
|
const dstScheme = schemeNameOf(dst);
|
|
631
654
|
const dstDenial = this.#denyIfDisallowed(dstScheme, origin, workerId);
|
|
632
655
|
if (dstDenial !== null)
|
|
633
656
|
return dstDenial;
|
|
634
657
|
if (statement.op === "MOVE") {
|
|
635
|
-
const srcScheme = schemeNameOf(statement.target);
|
|
658
|
+
const srcScheme = schemeNameOf(statement.source.target);
|
|
636
659
|
if (srcScheme !== dstScheme) {
|
|
637
660
|
const srcDenial = this.#denyIfDisallowed(srcScheme, origin, workerId);
|
|
638
661
|
if (srcDenial !== null)
|
|
@@ -643,14 +666,14 @@ class Dispatcher {
|
|
|
643
666
|
}
|
|
644
667
|
const target = schemeNameOf(statement.target);
|
|
645
668
|
const denial = this.#denyIfDisallowed(target, origin, workerId);
|
|
646
|
-
// {§send-target-recipient} —
|
|
647
|
-
//
|
|
648
|
-
//
|
|
669
|
+
// {§send-target-recipient} — SEND addresses recipients, not otherwise
|
|
670
|
+
// read-only resources. State that boundary without guessing whether the
|
|
671
|
+
// model intended a reply, deletion, or directed message.
|
|
649
672
|
if (denial !== null && statement.op === "SEND" && origin === "model") {
|
|
650
|
-
return _a.#failure("send-target-not-a-recipient", 400, "
|
|
673
|
+
return _a.#failure("send-target-not-a-recipient", 400, "The addressed scheme is not a SEND recipient.", {}, {
|
|
651
674
|
target: statement.target?.raw ?? String(target),
|
|
652
675
|
stage: "dispatch",
|
|
653
|
-
recovery: "A SEND
|
|
676
|
+
recovery: "A targetless SEND answers the active prompt; a directed SEND requires a recipient that implements SEND.",
|
|
654
677
|
retryable: false,
|
|
655
678
|
});
|
|
656
679
|
}
|
|
@@ -674,92 +697,25 @@ class Dispatcher {
|
|
|
674
697
|
retryable: false,
|
|
675
698
|
}); // {§scheme-surface-writableby-403}
|
|
676
699
|
}
|
|
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")
|
|
700
|
+
// {§capability-admission} — one resolver gates every operation route before
|
|
701
|
+
// execution or proposal handling. Unknown routes continue to their ordinary
|
|
702
|
+
// owner; a known policy denial is one factual, non-presumptuous 403.
|
|
703
|
+
async #checkCapabilities(statement, workspaceId, loopId, functionalityWorkerId) {
|
|
704
|
+
const denied = await this.#capabilities.denial(statement, workspaceId, functionalityWorkerId, loopId);
|
|
705
|
+
if (denied === null)
|
|
691
706
|
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);
|
|
707
|
+
const { descriptor, scope } = denied;
|
|
708
|
+
const route = [
|
|
709
|
+
descriptor.operation,
|
|
710
|
+
descriptor.scheme,
|
|
711
|
+
descriptor.runtime,
|
|
712
|
+
descriptor.tool,
|
|
713
|
+
].filter((part) => part !== undefined).join("/");
|
|
714
|
+
return _a.#failure("capability-denied", 403, `Capability '${route}' is denied by ${scope} policy.`, {}, {
|
|
715
|
+
...descriptor,
|
|
716
|
+
policyScope: scope,
|
|
717
|
+
retryable: false,
|
|
718
|
+
});
|
|
763
719
|
}
|
|
764
720
|
// Worker control is FORK/WORK (grammar 0.74.55), not COPY — its body
|
|
765
721
|
// is the new worker's seed prompt, not a destination path. The COPY gates and ResourceMutations.handleCopy
|
|
@@ -793,10 +749,15 @@ class Dispatcher {
|
|
|
793
749
|
if (denied !== null)
|
|
794
750
|
return denied;
|
|
795
751
|
const prompt = statement.body;
|
|
796
|
-
// {§worker-delegation-inherits-
|
|
797
|
-
//
|
|
798
|
-
//
|
|
799
|
-
const
|
|
752
|
+
// {§worker-delegation-inherits-policy} — authority flows down the
|
|
753
|
+
// delegation edge. The child receives the delegator's complete loop
|
|
754
|
+
// policy and an immutable snapshot of its effective capability bound.
|
|
755
|
+
const policy = await LoopPolicyReader.read(this.#db, ctx.loopId);
|
|
756
|
+
const capabilityBound = await CapabilityPolicies.delegationBound(this.#db, ctx.workspaceId, ctx.workerId, policy);
|
|
757
|
+
const delegationPolicy = {
|
|
758
|
+
...policy,
|
|
759
|
+
capabilities: capabilityBound,
|
|
760
|
+
};
|
|
800
761
|
// A name is frozen per worker but reclaimable across time ({§machine-processes-worker-origin}): a LIVE
|
|
801
762
|
// sister holding it is a 409 (legible, never a raw UNIQUE 500); a free/terminated name reclaims.
|
|
802
763
|
const live = await this.#db.worker_live_by_name.get({ workspace_id: ctx.workspaceId, name });
|
|
@@ -804,6 +765,16 @@ class Dispatcher {
|
|
|
804
765
|
return _a.#failure("worker-already-running", 409, `Worker '${name}' is already running.`, {}, { worker: name, retryable: false });
|
|
805
766
|
}
|
|
806
767
|
if (typeof statement.signal === "string") {
|
|
768
|
+
// {§branch-delegation-disabled} — off the model-facing surface until it is specified and
|
|
769
|
+
// witnessed end to end (#396); the batches stay behind the operator knob.
|
|
770
|
+
if (process.env.PLURNK_SERVICE_BRANCH_DELEGATION !== "1") {
|
|
771
|
+
return _a.#failure("branch-delegation-disabled", 501, `${statement.op} takes no signal; branch delegation is not offered.`, {}, {
|
|
772
|
+
worker: name,
|
|
773
|
+
signal: statement.signal,
|
|
774
|
+
recovery: `Spawn without a signal: \`## ${statement.op}0 (worker://${name})\` with the prompt as the body.`,
|
|
775
|
+
retryable: false,
|
|
776
|
+
});
|
|
777
|
+
}
|
|
807
778
|
if (this.#branchWorker === undefined)
|
|
808
779
|
throw new Error("branch worker control: branchWorker capability absent");
|
|
809
780
|
const child = await this.#branchWorker({
|
|
@@ -815,7 +786,7 @@ class Dispatcher {
|
|
|
815
786
|
name,
|
|
816
787
|
branch: statement.signal,
|
|
817
788
|
prompt,
|
|
818
|
-
|
|
789
|
+
policy: delegationPolicy,
|
|
819
790
|
origin: ctx.writer,
|
|
820
791
|
});
|
|
821
792
|
return {
|
|
@@ -826,13 +797,13 @@ class Dispatcher {
|
|
|
826
797
|
}
|
|
827
798
|
if (statement.op === "FORK") {
|
|
828
799
|
// 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));
|
|
800
|
+
const branchWorkerId = await Fork.fork(this.#db, ctx.workerId, name, capabilityBound, (scheme) => this.#schemes.entryInheritanceForStoredScheme(scheme, ctx.workerId));
|
|
830
801
|
await ctx.injectWorker({
|
|
831
802
|
workspaceId: ctx.workspaceId,
|
|
832
803
|
workerId: branchWorkerId,
|
|
833
804
|
sourceWorkerId: ctx.workerId,
|
|
834
805
|
prompt,
|
|
835
|
-
|
|
806
|
+
freshLoopPolicy: delegationPolicy,
|
|
836
807
|
parentLoopId: ctx.loopId,
|
|
837
808
|
});
|
|
838
809
|
return { status: 200, body: name };
|
|
@@ -841,6 +812,7 @@ class Dispatcher {
|
|
|
841
812
|
const row = await this.#db.fork_insert_worker.get({
|
|
842
813
|
workspace_id: ctx.workspaceId, name, parent_worker_id: ctx.workerId, origin: ctx.writer,
|
|
843
814
|
fork_snapshot: 0,
|
|
815
|
+
capability_bound: JSON.stringify(capabilityBound),
|
|
844
816
|
});
|
|
845
817
|
if (row === undefined)
|
|
846
818
|
throw new Error("worker spawn: worker insert returned no row");
|
|
@@ -849,7 +821,7 @@ class Dispatcher {
|
|
|
849
821
|
workerId: row.id,
|
|
850
822
|
sourceWorkerId: ctx.workerId,
|
|
851
823
|
prompt,
|
|
852
|
-
|
|
824
|
+
freshLoopPolicy: delegationPolicy,
|
|
853
825
|
parentLoopId: ctx.loopId,
|
|
854
826
|
});
|
|
855
827
|
return { status: 200, body: name };
|
|
@@ -869,8 +841,8 @@ class Dispatcher {
|
|
|
869
841
|
}
|
|
870
842
|
const manifest = this.#schemes.manifestFor(schemeName, ctx.functionalityWorkerId);
|
|
871
843
|
const coordinate = entryCoordinateOf(path, manifest?.authority ?? "namespace");
|
|
872
|
-
//
|
|
873
|
-
//
|
|
844
|
+
// log:/// KILL has already gone through the projection-curation owner.
|
|
845
|
+
// This path owns scheme-specific world and process KILL semantics.
|
|
874
846
|
// Process-KILL: any scheme whose handler exposes kill() aborts a live stream — the
|
|
875
847
|
// exec handler, registered as "exec" + under every runtime tag (sh/node), so a tag-
|
|
876
848
|
// addressed stream (sh:///l/t/s) routes here, not to deleteEntry. {§exec}
|
|
@@ -997,11 +969,10 @@ class Dispatcher {
|
|
|
997
969
|
}, this.#weighContent),
|
|
998
970
|
state: "resolved", outcome: null,
|
|
999
971
|
attrs: JSON.stringify(durableAttrs),
|
|
972
|
+
initial_folded: LogVisibility.serialize(folded ? LogVisibility.FOLDED : LogVisibility.OPEN),
|
|
1000
973
|
});
|
|
1001
974
|
if (row === undefined)
|
|
1002
975
|
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
976
|
return row.id;
|
|
1006
977
|
}
|
|
1007
978
|
// {§turn-ops-entry} — preserve exact admitted source beside, never instead
|
|
@@ -1026,7 +997,7 @@ class Dispatcher {
|
|
|
1026
997
|
},
|
|
1027
998
|
});
|
|
1028
999
|
}
|
|
1029
|
-
// PLAN — the model's
|
|
1000
|
+
// PLAN — one installment of the model's running work journal. An ordinary op: dispatched like any
|
|
1030
1001
|
// other, logged, and broadcast to the client as a log entry — but a pure no-op for
|
|
1031
1002
|
// state (PLAN ∉ MUTATING_OPS); its body serializes into the log row's tx, no effect.
|
|
1032
1003
|
#handlePlan(statement) {
|
|
@@ -1047,6 +1018,7 @@ class Dispatcher {
|
|
|
1047
1018
|
modelCallId,
|
|
1048
1019
|
});
|
|
1049
1020
|
context.onDispatch?.(logEntryId);
|
|
1021
|
+
await context.onSettled?.(logEntryId);
|
|
1050
1022
|
return result;
|
|
1051
1023
|
}
|
|
1052
1024
|
// {§send-premature-terminate} — the unified PENDING SET, judged at the terminal's OWN dispatch
|
|
@@ -1061,23 +1033,20 @@ class Dispatcher {
|
|
|
1061
1033
|
const openSubs = await this.#db.find_open_subscriptions_for_worker.all({ worker_id: workerId });
|
|
1062
1034
|
const execHandler = this.#schemes.get("exec");
|
|
1063
1035
|
if (openSubs.length > 0 || execHandler?.hasActiveSpawns?.(workerId) === true)
|
|
1064
|
-
pending.push("
|
|
1036
|
+
pending.push("streams");
|
|
1065
1037
|
const liveChild = await this.#db.engine_worker_has_live_child.get({ worker_id: workerId });
|
|
1066
1038
|
if (liveChild !== undefined)
|
|
1067
|
-
pending.push("
|
|
1039
|
+
pending.push("workers");
|
|
1068
1040
|
const boundaries = await this.#nextPacketBoundaries(workerId, turnId);
|
|
1069
1041
|
if (boundaries.retrievals)
|
|
1070
|
-
pending.push("
|
|
1042
|
+
pending.push("receipts");
|
|
1071
1043
|
// A stream that closed successfully is banked, not pending: concluding on its own
|
|
1072
1044
|
// success is legitimate and its output stays in the Log. A failed close is an unseen
|
|
1073
1045
|
// 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("; ")}`);
|
|
1046
|
+
if (boundaries.streamTerminations.some(({ closeStatus }) => closeStatus >= 400))
|
|
1047
|
+
pending.push("failed-stream-results");
|
|
1079
1048
|
if (boundaries.childTerminations)
|
|
1080
|
-
pending.push("worker
|
|
1049
|
+
pending.push("worker-results");
|
|
1081
1050
|
return pending;
|
|
1082
1051
|
}
|
|
1083
1052
|
// Results cross an observation boundary only when they have appeared in a packet;
|
|
@@ -1109,16 +1078,15 @@ class Dispatcher {
|
|
|
1109
1078
|
return failedRows.length;
|
|
1110
1079
|
}
|
|
1111
1080
|
static #unobservedFailures(failCount) {
|
|
1112
|
-
return _a.#failure("unobserved-failures", 409, `
|
|
1081
|
+
return _a.#failure("unobserved-failures", 409, `This turn produced ${failCount} failed operation result(s) that have not yet entered a packet.`, {}, {
|
|
1113
1082
|
failures: failCount,
|
|
1114
1083
|
stage: "completion",
|
|
1115
|
-
recovery: "Review the failed log items before concluding.",
|
|
1116
1084
|
retryable: false,
|
|
1117
1085
|
});
|
|
1118
1086
|
}
|
|
1119
1087
|
// J — a live obligation to WAIT on: a spawned child or an open stream (NOT retrievals, which land
|
|
1120
1088
|
// next turn regardless). The wait-side twin of #pendingSet's stream+child legs ({§wait-obligation-matrix}).
|
|
1121
|
-
async
|
|
1089
|
+
async hasLiveWork(workerId) {
|
|
1122
1090
|
const openSubs = await this.#db.find_open_subscriptions_for_worker.all({ worker_id: workerId });
|
|
1123
1091
|
if (openSubs.length > 0)
|
|
1124
1092
|
return true;
|
|
@@ -1165,8 +1133,9 @@ class Dispatcher {
|
|
|
1165
1133
|
// retrieval (R) just lands next turn, so the wait continues.
|
|
1166
1134
|
if (status === 202) {
|
|
1167
1135
|
const marks = statement.lineMarker?.marks[0];
|
|
1168
|
-
|
|
1169
|
-
|
|
1136
|
+
// `<T>` is MINUTES, held in seconds; bare 202 / absent T = indefinite, bounded by the join.
|
|
1137
|
+
const seconds = typeof marks === "number" ? (marks > 0 ? marks * 60 : marks) : -1;
|
|
1138
|
+
if (await this.hasLiveWork(workerId)) {
|
|
1170
1139
|
if (!await this.#lifecycle.park(loopId)) {
|
|
1171
1140
|
return _a.#statusResult(await this.#lifecycle.status(loopId), "loop-already-terminal", "The loop was already terminal when SEND attempted to wait.");
|
|
1172
1141
|
}
|
|
@@ -1214,19 +1183,17 @@ class Dispatcher {
|
|
|
1214
1183
|
if (pending.length > 0) {
|
|
1215
1184
|
// A receipts-only refusal needs no KILL/park remedy menu: the results simply
|
|
1216
1185
|
// arrive in the next packet. Streams and children retain their remedy steer.
|
|
1217
|
-
const receiptsOnly = pending.every((
|
|
1186
|
+
const receiptsOnly = pending.every((kind) => kind === "receipts");
|
|
1218
1187
|
if (receiptsOnly) {
|
|
1219
|
-
return _a.#failure("retrieval-results-unobserved", 409, "
|
|
1188
|
+
return _a.#failure("retrieval-results-unobserved", 409, "Completion preceded this turn's operation results; they enter the next packet.", {}, {
|
|
1220
1189
|
pending: [...pending],
|
|
1221
1190
|
stage: "completion",
|
|
1222
|
-
recovery: "Review the results, then use only `# PLAN0` and `## SEND0 [200]` to conclude.",
|
|
1223
1191
|
retryable: false,
|
|
1224
1192
|
});
|
|
1225
1193
|
}
|
|
1226
|
-
return _a.#failure("work-remains", 409,
|
|
1194
|
+
return _a.#failure("work-remains", 409, "Completion encountered pending work or results.", {}, {
|
|
1227
1195
|
pending: [...pending],
|
|
1228
1196
|
stage: "completion",
|
|
1229
|
-
recovery: "Resolve the listed pending work before concluding.",
|
|
1230
1197
|
retryable: false,
|
|
1231
1198
|
});
|
|
1232
1199
|
}
|
|
@@ -1241,7 +1208,11 @@ class Dispatcher {
|
|
|
1241
1208
|
const branchDenial = await this.#branchCompletionGate?.(workerId) ?? null;
|
|
1242
1209
|
if (branchDenial !== null)
|
|
1243
1210
|
return branchDenial;
|
|
1244
|
-
const
|
|
1211
|
+
const reason = raw === "" ? null : ErrorDetail.preview(raw);
|
|
1212
|
+
const failure = _a.#failure("scope-abandoned", 499, "The worker ended its scope with SEND[499].", {}, {
|
|
1213
|
+
...(reason === null ? {} : { reason }),
|
|
1214
|
+
retryable: false,
|
|
1215
|
+
});
|
|
1245
1216
|
const seqs = await this.#db.engine_loop_turn_seqs.get({
|
|
1246
1217
|
loop_id: loopId,
|
|
1247
1218
|
turn_id: turnId,
|
|
@@ -1253,7 +1224,7 @@ class Dispatcher {
|
|
|
1253
1224
|
const finished = await this.#lifecycle.finish(loopId, failure);
|
|
1254
1225
|
if (finished === null)
|
|
1255
1226
|
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,
|
|
1227
|
+
await this.#cancelDescendants?.(workerId, reason ?? "parent worker ended its scope with SEND[499]");
|
|
1257
1228
|
return failure;
|
|
1258
1229
|
}
|
|
1259
1230
|
// Every other signal — 102 bare, 202 (retired as a terminal; now ordinary mid-comms), 1xx —
|
|
@@ -1287,6 +1258,27 @@ class Dispatcher {
|
|
|
1287
1258
|
const addressedScheme = statement.target?.kind === "url" ? statement.target.scheme : null;
|
|
1288
1259
|
if (manifest === undefined)
|
|
1289
1260
|
throw new Error(`scheme '${schemeName}' has no manifest`);
|
|
1261
|
+
// Metadata belongs to the handler that receives it. EXEC over a
|
|
1262
|
+
// non-file resource is the one split route: exec hosts the operation,
|
|
1263
|
+
// while the canonically routed source scheme receives the metadata.
|
|
1264
|
+
const metadataScheme = schemeName === "exec"
|
|
1265
|
+
&& statement.target?.kind === "url"
|
|
1266
|
+
&& statement.target.scheme !== "file"
|
|
1267
|
+
? schemeNameOf(statement.target) ?? schemeName
|
|
1268
|
+
: schemeName;
|
|
1269
|
+
const metadataManifest = metadataScheme === schemeName
|
|
1270
|
+
? manifest
|
|
1271
|
+
: this.#schemes.manifestFor(metadataScheme, ctx.functionalityWorkerId);
|
|
1272
|
+
if (statement.metadata !== null && metadataManifest === undefined) {
|
|
1273
|
+
return _a.#failure("scheme-not-found", 501, `Scheme '${metadataScheme}' is not registered.`, {}, { scheme: metadataScheme, retryable: false });
|
|
1274
|
+
}
|
|
1275
|
+
if (statement.metadata !== null && metadataManifest?.metadataModifier !== true) {
|
|
1276
|
+
return _a.#failure("scheme-metadata-unsupported", 400, `Scheme '${metadataScheme}' does not accept the {metadata} modifier.`, {}, {
|
|
1277
|
+
scheme: metadataScheme,
|
|
1278
|
+
operation: statement.op,
|
|
1279
|
+
retryable: false,
|
|
1280
|
+
});
|
|
1281
|
+
}
|
|
1290
1282
|
const publishedChannel = statement.target?.kind === "url"
|
|
1291
1283
|
? statement.target.fragment ?? manifest.defaultChannel
|
|
1292
1284
|
: manifest.defaultChannel;
|
|
@@ -1379,6 +1371,7 @@ class Dispatcher {
|
|
|
1379
1371
|
? { address: null, result: null }
|
|
1380
1372
|
: await this.#prepareDataRepresentation({
|
|
1381
1373
|
target: statement.target,
|
|
1374
|
+
metadata: statement.metadata,
|
|
1382
1375
|
routedScheme: schemeName,
|
|
1383
1376
|
handler: handler,
|
|
1384
1377
|
manifest,
|
|
@@ -1412,10 +1405,6 @@ class Dispatcher {
|
|
|
1412
1405
|
return _a.#failure("operation-not-implemented", 501, `Scheme '${schemeName}' does not implement ${statement.op}.`, {}, {
|
|
1413
1406
|
scheme: schemeName,
|
|
1414
1407
|
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
1408
|
retryable: false,
|
|
1420
1409
|
});
|
|
1421
1410
|
}
|
|
@@ -1430,6 +1419,7 @@ class Dispatcher {
|
|
|
1430
1419
|
if (exactTarget && statement.target !== null) {
|
|
1431
1420
|
const prepared = await this.#prepareDataRepresentation({
|
|
1432
1421
|
target: statement.target,
|
|
1422
|
+
metadata: statement.metadata,
|
|
1433
1423
|
routedScheme: schemeName,
|
|
1434
1424
|
handler: handler,
|
|
1435
1425
|
manifest,
|
|
@@ -1504,10 +1494,11 @@ class Dispatcher {
|
|
|
1504
1494
|
}
|
|
1505
1495
|
async #writeLog({ statement, result, workspaceId, workerId, functionalityWorkerId, loopId, turnId, sequence, origin, curationPlan, modelCallId, }) {
|
|
1506
1496
|
const durableStatement = DurableStatement.project(statement);
|
|
1507
|
-
const target = this.#extractTarget(durableStatement
|
|
1497
|
+
const target = this.#extractTarget(primaryTargetOf(durableStatement), functionalityWorkerId);
|
|
1508
1498
|
await this.#canonColumns(target, workspaceId); // {§fs-answer-in-canon}
|
|
1509
|
-
const
|
|
1510
|
-
|
|
1499
|
+
const lineMarker = primaryLineMarkerOf(durableStatement);
|
|
1500
|
+
const lineMarkerJson = lineMarker !== null
|
|
1501
|
+
? JSON.stringify(lineMarker)
|
|
1511
1502
|
: null;
|
|
1512
1503
|
// A proposal (status 202 from a side-effecting op) is written to the log in
|
|
1513
1504
|
// state='proposed' until the proposal lifecycle resolves it; attrs holds the
|
|
@@ -1607,6 +1598,7 @@ class Dispatcher {
|
|
|
1607
1598
|
state: isProposed ? "proposed" : "resolved",
|
|
1608
1599
|
outcome: null,
|
|
1609
1600
|
attrs,
|
|
1601
|
+
initial_folded: LogVisibility.serialize(LogVisibility.OPEN),
|
|
1610
1602
|
});
|
|
1611
1603
|
// {§exec-tag-signal} — an EXEC row's signal is its runtime; its tag signal classifies
|
|
1612
1604
|
// through the same idempotent primitive the log-write trigger uses for tag operations.
|