@plurnk/plurnk-service 1.8.0 → 1.9.1
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 +20 -0
- package/INSTALL.md +14 -16
- package/README.md +1 -1
- package/SPEC.md +635 -296
- package/digest-sql/channel-dispositions/dispositions.sql +2 -2
- package/digest-sql/entry-dispositions/dispositions.sql +2 -2
- package/dist/Paths.d.ts +1 -2
- package/dist/Paths.d.ts.map +1 -1
- package/dist/Paths.js +1 -9
- package/dist/Paths.js.map +1 -1
- package/dist/build-info.json +1 -1
- package/dist/core/ChannelWrite.d.ts +8 -3
- package/dist/core/ChannelWrite.d.ts.map +1 -1
- package/dist/core/ChannelWrite.js +40 -8
- package/dist/core/ChannelWrite.js.map +1 -1
- package/dist/core/ChannelWrite.sql +17 -4
- package/dist/core/CoreSchemeServices.d.ts +7 -3
- package/dist/core/CoreSchemeServices.d.ts.map +1 -1
- package/dist/core/CoreSchemeServices.js +8 -1
- package/dist/core/CoreSchemeServices.js.map +1 -1
- package/dist/core/Dispatcher.d.ts +9 -1
- package/dist/core/Dispatcher.d.ts.map +1 -1
- package/dist/core/Dispatcher.js +327 -221
- package/dist/core/Dispatcher.js.map +1 -1
- package/dist/core/Engine.d.ts +13 -4
- package/dist/core/Engine.d.ts.map +1 -1
- package/dist/core/Engine.js +44 -22
- package/dist/core/Engine.js.map +1 -1
- package/dist/core/Engine.sql +101 -81
- package/dist/core/EntryAddressBinding.d.ts +32 -0
- package/dist/core/EntryAddressBinding.d.ts.map +1 -0
- package/dist/core/EntryAddressBinding.js +93 -0
- package/dist/core/EntryAddressBinding.js.map +1 -0
- package/dist/core/ExecutorRegistry.d.ts +4 -4
- package/dist/core/ExecutorRegistry.d.ts.map +1 -1
- package/dist/core/ExecutorRegistry.js +28 -28
- package/dist/core/ExecutorRegistry.js.map +1 -1
- package/dist/core/LogBody.d.ts.map +1 -1
- package/dist/core/LogBody.js +19 -1
- package/dist/core/LogBody.js.map +1 -1
- package/dist/core/OperatorConfig.d.ts.map +1 -1
- package/dist/core/OperatorConfig.js +5 -2
- package/dist/core/OperatorConfig.js.map +1 -1
- package/dist/core/OverflowTurn.d.ts.map +1 -1
- package/dist/core/OverflowTurn.js +5 -1
- package/dist/core/OverflowTurn.js.map +1 -1
- package/dist/core/Owner.js +1 -1
- package/dist/core/Owner.js.map +1 -1
- package/dist/core/PacketBuilder.d.ts +5 -1
- package/dist/core/PacketBuilder.d.ts.map +1 -1
- package/dist/core/PacketBuilder.js +24 -12
- package/dist/core/PacketBuilder.js.map +1 -1
- package/dist/core/ProposalLifecycle.d.ts +4 -1
- package/dist/core/ProposalLifecycle.d.ts.map +1 -1
- package/dist/core/ProposalLifecycle.js +85 -26
- package/dist/core/ProposalLifecycle.js.map +1 -1
- package/dist/core/ReasoningEvent.d.ts +13 -0
- package/dist/core/ReasoningEvent.d.ts.map +1 -0
- package/dist/core/ReasoningEvent.js +6 -0
- package/dist/core/ReasoningEvent.js.map +1 -0
- package/dist/core/ResourceMutations.d.ts +15 -19
- package/dist/core/ResourceMutations.d.ts.map +1 -1
- package/dist/core/ResourceMutations.js +113 -38
- package/dist/core/ResourceMutations.js.map +1 -1
- package/dist/core/SchemeRegistry.d.ts +12 -11
- package/dist/core/SchemeRegistry.d.ts.map +1 -1
- package/dist/core/SchemeRegistry.js +59 -47
- package/dist/core/SchemeRegistry.js.map +1 -1
- package/dist/core/ToolResources.d.ts.map +1 -1
- package/dist/core/ToolResources.js +20 -19
- package/dist/core/ToolResources.js.map +1 -1
- package/dist/core/Turn.d.ts +1 -1
- package/dist/core/Turn.d.ts.map +1 -1
- package/dist/core/TurnOps.d.ts +2 -2
- package/dist/core/TurnOps.d.ts.map +1 -1
- package/dist/core/TurnOps.js +14 -4
- package/dist/core/TurnOps.js.map +1 -1
- package/dist/core/TurnRunner.d.ts +46 -3
- package/dist/core/TurnRunner.d.ts.map +1 -1
- package/dist/core/TurnRunner.js +165 -78
- package/dist/core/TurnRunner.js.map +1 -1
- package/dist/core/WorkerName.d.ts +1 -0
- package/dist/core/WorkerName.d.ts.map +1 -1
- package/dist/core/WorkerName.js +2 -1
- package/dist/core/WorkerName.js.map +1 -1
- package/dist/core/WorkerName.sql +17 -2
- package/dist/core/WorkspaceGate.js +1 -1
- package/dist/core/WorkspaceGate.js.map +1 -1
- package/dist/core/caps/CapsResolve.d.ts +2 -2
- package/dist/core/caps/CapsResolve.d.ts.map +1 -1
- package/dist/core/caps/CapsResolve.js +5 -7
- package/dist/core/caps/CapsResolve.js.map +1 -1
- package/dist/core/caps/DbChannelCaps.d.ts +1 -1
- package/dist/core/caps/DbChannelCaps.d.ts.map +1 -1
- package/dist/core/caps/DbChannelCaps.js +13 -11
- package/dist/core/caps/DbChannelCaps.js.map +1 -1
- package/dist/core/caps/DbEntryCaps.d.ts +5 -5
- package/dist/core/caps/DbEntryCaps.d.ts.map +1 -1
- package/dist/core/caps/DbEntryCaps.js +27 -30
- package/dist/core/caps/DbEntryCaps.js.map +1 -1
- package/dist/core/caps/DbNotifyCaps.d.ts +1 -1
- package/dist/core/caps/DbNotifyCaps.d.ts.map +1 -1
- package/dist/core/caps/DbNotifyCaps.js +5 -3
- package/dist/core/caps/DbNotifyCaps.js.map +1 -1
- package/dist/core/caps/DbSubscriptionCaps.d.ts +3 -3
- package/dist/core/caps/DbSubscriptionCaps.d.ts.map +1 -1
- package/dist/core/caps/DbSubscriptionCaps.js +16 -26
- package/dist/core/caps/DbSubscriptionCaps.js.map +1 -1
- package/dist/core/caps/SchemeCtxImpl.d.ts +6 -4
- package/dist/core/caps/SchemeCtxImpl.d.ts.map +1 -1
- package/dist/core/caps/SchemeCtxImpl.js +37 -5
- package/dist/core/caps/SchemeCtxImpl.js.map +1 -1
- package/dist/core/file-creation-policy.d.ts +9 -0
- package/dist/core/file-creation-policy.d.ts.map +1 -0
- package/dist/core/file-creation-policy.js +25 -0
- package/dist/core/file-creation-policy.js.map +1 -0
- package/dist/core/fork.d.ts +4 -1
- package/dist/core/fork.d.ts.map +1 -1
- package/dist/core/fork.js +27 -15
- package/dist/core/fork.js.map +1 -1
- package/dist/core/fork.sql +35 -22
- package/dist/core/git-membership.d.ts +19 -1
- package/dist/core/git-membership.d.ts.map +1 -1
- package/dist/core/git-membership.js +225 -50
- package/dist/core/git-membership.js.map +1 -1
- package/dist/core/packet-wire.d.ts.map +1 -1
- package/dist/core/packet-wire.js +28 -19
- package/dist/core/packet-wire.js.map +1 -1
- package/dist/core/plurnk-uri.d.ts +13 -2
- package/dist/core/plurnk-uri.d.ts.map +1 -1
- package/dist/core/plurnk-uri.js +59 -21
- package/dist/core/plurnk-uri.js.map +1 -1
- package/dist/core/scheme-types.d.ts +1 -0
- package/dist/core/scheme-types.d.ts.map +1 -1
- package/dist/core/worker-ops.sql +10 -6
- package/dist/core/worker-settings.d.ts +1 -0
- package/dist/core/worker-settings.d.ts.map +1 -1
- package/dist/core/worker-settings.js +7 -0
- package/dist/core/worker-settings.js.map +1 -1
- package/dist/core/workspace-settings.d.ts +2 -0
- package/dist/core/workspace-settings.d.ts.map +1 -1
- package/dist/core/workspace-settings.js +5 -1
- package/dist/core/workspace-settings.js.map +1 -1
- package/dist/digest/Digest.d.ts.map +1 -1
- package/dist/digest/Digest.js +1 -1
- package/dist/digest/Digest.js.map +1 -1
- package/dist/digest/digest.sql +1 -1
- package/dist/schemes/Exec.d.ts +3 -2
- package/dist/schemes/Exec.d.ts.map +1 -1
- package/dist/schemes/Exec.js +55 -22
- package/dist/schemes/Exec.js.map +1 -1
- package/dist/schemes/ExecOutputScheme.d.ts.map +1 -1
- package/dist/schemes/ExecOutputScheme.js +5 -4
- package/dist/schemes/ExecOutputScheme.js.map +1 -1
- package/dist/schemes/File.d.ts.map +1 -1
- package/dist/schemes/File.js +170 -80
- package/dist/schemes/File.js.map +1 -1
- package/dist/schemes/Prompt.d.ts +2 -3
- package/dist/schemes/Prompt.d.ts.map +1 -1
- package/dist/schemes/Prompt.js +4 -7
- package/dist/schemes/Prompt.js.map +1 -1
- package/dist/schemes/QuestionTool.d.ts +2 -0
- package/dist/schemes/QuestionTool.d.ts.map +1 -1
- package/dist/schemes/QuestionTool.js +5 -0
- package/dist/schemes/QuestionTool.js.map +1 -1
- package/dist/schemes/Skill.d.ts.map +1 -1
- package/dist/schemes/Skill.js +3 -1
- package/dist/schemes/Skill.js.map +1 -1
- package/dist/schemes/Worker.d.ts.map +1 -1
- package/dist/schemes/Worker.js +95 -49
- package/dist/schemes/Worker.js.map +1 -1
- package/dist/schemes/_entry-crud.d.ts +5 -5
- package/dist/schemes/_entry-crud.d.ts.map +1 -1
- package/dist/schemes/_entry-crud.js +22 -17
- package/dist/schemes/_entry-crud.js.map +1 -1
- package/dist/schemes/_entry-crud.sql +58 -23
- package/dist/schemes/_entry-find.d.ts +3 -2
- package/dist/schemes/_entry-find.d.ts.map +1 -1
- package/dist/schemes/_entry-find.js +26 -25
- package/dist/schemes/_entry-find.js.map +1 -1
- package/dist/schemes/_entry-find.sql +10 -7
- package/dist/schemes/_entry-manifest.d.ts +2 -2
- package/dist/schemes/_entry-manifest.d.ts.map +1 -1
- package/dist/schemes/_entry-manifest.js +6 -5
- package/dist/schemes/_entry-manifest.js.map +1 -1
- package/dist/schemes/_entry-ops.d.ts +6 -5
- package/dist/schemes/_entry-ops.d.ts.map +1 -1
- package/dist/schemes/_entry-ops.js +39 -46
- package/dist/schemes/_entry-ops.js.map +1 -1
- package/dist/schemes/_entry-ops.sql +9 -6
- package/dist/schemes/_entry-send.d.ts +2 -2
- package/dist/schemes/_entry-send.d.ts.map +1 -1
- package/dist/schemes/_entry-send.js +16 -14
- package/dist/schemes/_entry-send.js.map +1 -1
- package/dist/schemes/_search-index.d.ts.map +1 -1
- package/dist/schemes/_search-index.js +2 -0
- package/dist/schemes/_search-index.js.map +1 -1
- package/dist/server/Daemon.d.ts +42 -10
- package/dist/server/Daemon.d.ts.map +1 -1
- package/dist/server/Daemon.js +326 -248
- package/dist/server/Daemon.js.map +1 -1
- package/dist/server/DaemonModule.d.ts +84 -19
- package/dist/server/DaemonModule.d.ts.map +1 -1
- package/dist/server/DrainSupervisor.d.ts +3 -1
- package/dist/server/DrainSupervisor.d.ts.map +1 -1
- package/dist/server/DrainSupervisor.js +11 -9
- package/dist/server/DrainSupervisor.js.map +1 -1
- package/dist/server/Functionality.d.ts +44 -0
- package/dist/server/Functionality.d.ts.map +1 -0
- package/dist/server/Functionality.js +422 -0
- package/dist/server/Functionality.js.map +1 -0
- package/dist/server/FunctionalityManager.d.ts +22 -0
- package/dist/server/FunctionalityManager.d.ts.map +1 -0
- package/dist/server/FunctionalityManager.js +121 -0
- package/dist/server/FunctionalityManager.js.map +1 -0
- package/dist/server/SkillsFunctionality.d.ts +61 -0
- package/dist/server/SkillsFunctionality.d.ts.map +1 -0
- package/dist/server/SkillsFunctionality.js +449 -0
- package/dist/server/SkillsFunctionality.js.map +1 -0
- package/dist/server/WorkerCapabilities.d.ts +21 -0
- package/dist/server/WorkerCapabilities.d.ts.map +1 -0
- package/dist/server/WorkerCapabilities.js +214 -0
- package/dist/server/WorkerCapabilities.js.map +1 -0
- package/dist/server/WorkerResidency.d.ts +40 -0
- package/dist/server/WorkerResidency.d.ts.map +1 -0
- package/dist/server/WorkerResidency.js +283 -0
- package/dist/server/WorkerResidency.js.map +1 -0
- package/dist/server/client-input.d.ts +1 -0
- package/dist/server/client-input.d.ts.map +1 -1
- package/dist/server/client-input.js +22 -1
- package/dist/server/client-input.js.map +1 -1
- package/dist/server/dispatch-as-plurnk.d.ts +2 -2
- package/dist/server/dispatch-as-plurnk.d.ts.map +1 -1
- package/dist/server/dispatch-as-plurnk.js +89 -25
- package/dist/server/dispatch-as-plurnk.js.map +1 -1
- package/dist/server/drain.sql +11 -6
- package/dist/server/envelope.d.ts +6 -1
- package/dist/server/envelope.d.ts.map +1 -1
- package/dist/server/envelope.js +8 -2
- package/dist/server/envelope.js.map +1 -1
- package/dist/server/envelope.sql +11 -3
- package/dist/server/lifecycle-recovery.sql +13 -16
- package/dist/server/loopDocs.d.ts +2 -1
- package/dist/server/loopDocs.d.ts.map +1 -1
- package/dist/server/loopDocs.js +67 -51
- package/dist/server/loopDocs.js.map +1 -1
- package/dist/server/loopDocs.sql +14 -10
- package/dist/server/module-discovery.d.ts +2 -2
- package/dist/server/module-discovery.d.ts.map +1 -1
- package/dist/server/module-discovery.js +27 -2
- package/dist/server/module-discovery.js.map +1 -1
- package/dist/server/seam-entry-read.sql +9 -7
- package/dist/server/seam-log-read.sql +1 -1
- package/dist/server/seam-loop.sql +10 -1
- package/dist/server/seam-proposal-list.sql +4 -2
- package/dist/server/worker-capabilities.sql +19 -0
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +11 -1
- package/dist/service.js.map +1 -1
- package/migrations/001_schema.sql +486 -136
- package/package.json +34 -32
- package/dist/server/skillDocs.d.ts +0 -9
- package/dist/server/skillDocs.d.ts.map +0 -1
- package/dist/server/skillDocs.js +0 -212
- package/dist/server/skillDocs.js.map +0 -1
- package/dist/server/skillDocs.sql +0 -14
- package/dist/server/workspace-capabilities.sql +0 -19
package/dist/core/Dispatcher.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
var _a;
|
|
2
|
-
import Owner from "./Owner.js";
|
|
3
2
|
import WorkerName, { WorkerNameError } from "./WorkerName.js";
|
|
4
3
|
import WorkerControlAddress from "./WorkerControlAddress.js";
|
|
5
|
-
import {
|
|
4
|
+
import { entryCoordinateOf, foldAuthorityIntoPath, renderAddress, renderTarget, schemeNameOf } from "./plurnk-uri.js";
|
|
6
5
|
import Fork from "./fork.js";
|
|
7
6
|
import WorkerCap from "./worker-cap.js";
|
|
8
7
|
import { PathSyntax, TagSignal } from "@plurnk/plurnk-contracts";
|
|
@@ -19,8 +18,8 @@ import Results from "./results.js";
|
|
|
19
18
|
import { OperationFailureError } from "./results.js";
|
|
20
19
|
import EffectPolicy from "../schemes/EffectPolicy.js";
|
|
21
20
|
import WorkerSettingsReader from "./worker-settings.js";
|
|
22
|
-
import { CoreSchemeAdapterBase
|
|
23
|
-
import { InvalidOperationResultError,
|
|
21
|
+
import { CoreSchemeAdapterBase } from "./CoreSchemeServices.js";
|
|
22
|
+
import { InvalidOperationResultError, } from "@plurnk/plurnk-schemes";
|
|
24
23
|
import DurableStatement from "./DurableStatement.js";
|
|
25
24
|
import ResourceMutations from "./ResourceMutations.js";
|
|
26
25
|
import LogBody from "./LogBody.js";
|
|
@@ -74,7 +73,8 @@ class Dispatcher {
|
|
|
74
73
|
#liveSubscriptions;
|
|
75
74
|
#lifecycle;
|
|
76
75
|
#resourceMutations;
|
|
77
|
-
|
|
76
|
+
#entryAddresses;
|
|
77
|
+
constructor({ db, schemes, mimetypes, weigh, notices, proposals, interactions, executors, loopSignal, streamEventNotify, wakeWorkerNotify, injectWorker, branchWorker, branchCompletionGate, cancelWorker, cancelDescendants, parkDeadlines, joinTargets, liveSubscriptions, entryAddresses }) {
|
|
78
78
|
this.#db = db;
|
|
79
79
|
this.#schemes = schemes;
|
|
80
80
|
this.#mimetypes = mimetypes;
|
|
@@ -94,45 +94,64 @@ class Dispatcher {
|
|
|
94
94
|
this.#parkDeadlines = parkDeadlines ?? new Map();
|
|
95
95
|
this.#joinTargets = joinTargets ?? new Set();
|
|
96
96
|
this.#liveSubscriptions = liveSubscriptions;
|
|
97
|
+
this.#entryAddresses = entryAddresses;
|
|
97
98
|
this.#lifecycle = new LoopLifecycle(db);
|
|
98
99
|
this.#resourceMutations = new ResourceMutations({
|
|
99
100
|
schemes,
|
|
100
101
|
liveSubscriptions,
|
|
101
102
|
run: (schemeName, statement, ctx) => this.#run(schemeName, statement, ctx),
|
|
102
|
-
checkWritable: (statement, origin,
|
|
103
|
-
checkFlagsGate: (statement, loopId,
|
|
104
|
-
editTargetIdentity: (statement, workspaceId) => this.#editTargetIdentity(statement, workspaceId),
|
|
103
|
+
checkWritable: (statement, origin, workerId) => this.#checkWritable(statement, origin, workerId),
|
|
104
|
+
checkFlagsGate: (statement, loopId, workerId) => this.#checkFlagsGate(statement, loopId, workerId),
|
|
105
|
+
editTargetIdentity: (statement, workspaceId, workerId) => this.#editTargetIdentity(statement, workspaceId, workerId),
|
|
105
106
|
canonicalFilePath: (pathname, workspaceId) => this.#canonicalFilePath(pathname, workspaceId),
|
|
106
107
|
prepareDataRepresentation: (args) => this.#prepareDataRepresentation({
|
|
107
108
|
...args,
|
|
108
109
|
handler: args.handler,
|
|
109
110
|
}),
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
111
|
+
resolveDataEntryAddress: (args) => this.#entryAddresses.resolve(args),
|
|
112
|
+
readEntry: (scheme, address, ctx) => this.#readEntry(scheme, address, ctx),
|
|
113
|
+
writeEntry: (scheme, address, entry, ctx) => this.#writeEntry(scheme, address, entry, ctx),
|
|
114
|
+
deleteChannel: (scheme, address, channel, ctx) => this.#deleteChannel(scheme, address, channel, ctx),
|
|
113
115
|
applyProposal: (statement, result, resolution, ids) => this.#proposals.workerApply(statement, result, resolution, ids),
|
|
114
116
|
});
|
|
115
117
|
}
|
|
116
118
|
// workspace → project_root, memoized: {§fs-namespace} fixes the root immutably at
|
|
117
119
|
// workspace creation, so a process-lifetime cache can never go stale.
|
|
118
120
|
#rootCache = new Map();
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
return manifest === undefined ? null : new SchemeCtxImpl(ctx, scheme, manifest, this.#liveSubscriptions);
|
|
121
|
+
evictWorkspaceCache(workspaceId) {
|
|
122
|
+
this.#rootCache.delete(workspaceId);
|
|
122
123
|
}
|
|
123
|
-
#
|
|
124
|
-
|
|
125
|
-
return this.#schemes.manifestFor(scheme, ctx.workspaceId)?.category === "data" ? handlerCtx : null;
|
|
124
|
+
async #fixedEntryOwnerId(manifest, ctx) {
|
|
125
|
+
return this.#entryAddresses.fixedOwnerId(manifest, ctx);
|
|
126
126
|
}
|
|
127
|
-
#
|
|
128
|
-
const
|
|
127
|
+
async #handlerContext(scheme, ctx, authority = "") {
|
|
128
|
+
const manifest = this.#schemes.manifestFor(scheme, ctx.functionalityWorkerId);
|
|
129
|
+
return manifest === undefined
|
|
130
|
+
? null
|
|
131
|
+
: new SchemeCtxImpl(ctx, scheme, manifest, this.#liveSubscriptions, {
|
|
132
|
+
authority,
|
|
133
|
+
ownerId: await this.#fixedEntryOwnerId(manifest, ctx),
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
#boundEntryContext(routedScheme, address, ctx) {
|
|
137
|
+
const manifest = this.#schemes.manifestFor(routedScheme, ctx.functionalityWorkerId);
|
|
138
|
+
return manifest?.category === "data"
|
|
139
|
+
? new SchemeCtxImpl(ctx, address.scheme, manifest, this.#liveSubscriptions, {
|
|
140
|
+
authority: address.authority,
|
|
141
|
+
ownerId: address.ownerId,
|
|
142
|
+
})
|
|
143
|
+
: null;
|
|
144
|
+
}
|
|
145
|
+
#coreCrud(scheme, workerId) {
|
|
146
|
+
const handler = this.#schemes.get(scheme, workerId);
|
|
129
147
|
return handler instanceof CoreSchemeAdapterBase
|
|
130
148
|
? handler
|
|
131
149
|
: undefined;
|
|
132
150
|
}
|
|
133
|
-
async #readEntry(scheme,
|
|
134
|
-
const
|
|
135
|
-
const
|
|
151
|
+
async #readEntry(scheme, address, ctx) {
|
|
152
|
+
const { pathname } = address;
|
|
153
|
+
const handler = this.#coreCrud(scheme, ctx.functionalityWorkerId);
|
|
154
|
+
const handlerCtx = this.#boundEntryContext(scheme, address, ctx);
|
|
136
155
|
if (typeof handler?.readEntry === "function" && handlerCtx !== null) {
|
|
137
156
|
return Results.assert(await handler.readEntry(pathname, handlerCtx));
|
|
138
157
|
}
|
|
@@ -141,11 +160,11 @@ class Dispatcher {
|
|
|
141
160
|
return _a.#failure("entry-read-not-implemented", 501, `The '${scheme}' scheme does not provide entry reads.`, { entry: null }, {
|
|
142
161
|
stage: "entry-read",
|
|
143
162
|
scheme,
|
|
144
|
-
target: renderAddress(
|
|
163
|
+
target: renderAddress(address),
|
|
145
164
|
retryable: false,
|
|
146
165
|
});
|
|
147
166
|
}
|
|
148
|
-
const result = Results.assert(await caps.read(pathname
|
|
167
|
+
const result = Results.assert(await caps.read(pathname));
|
|
149
168
|
return Results.assert({
|
|
150
169
|
...result,
|
|
151
170
|
status: result.status,
|
|
@@ -159,9 +178,10 @@ class Dispatcher {
|
|
|
159
178
|
},
|
|
160
179
|
});
|
|
161
180
|
}
|
|
162
|
-
async #writeEntry(scheme,
|
|
163
|
-
const
|
|
164
|
-
const
|
|
181
|
+
async #writeEntry(scheme, address, entry, ctx) {
|
|
182
|
+
const { pathname } = address;
|
|
183
|
+
const handler = this.#coreCrud(scheme, ctx.functionalityWorkerId);
|
|
184
|
+
const handlerCtx = this.#boundEntryContext(scheme, address, ctx);
|
|
165
185
|
if (typeof handler?.writeEntry === "function" && handlerCtx !== null) {
|
|
166
186
|
return Results.assert(await handler.writeEntry(pathname, entry, handlerCtx));
|
|
167
187
|
}
|
|
@@ -170,15 +190,16 @@ class Dispatcher {
|
|
|
170
190
|
return _a.#failure("entry-write-not-implemented", 501, `The '${scheme}' scheme does not provide entry writes.`, { created: false, entryId: null }, {
|
|
171
191
|
stage: "entry-write",
|
|
172
192
|
scheme,
|
|
173
|
-
target: renderAddress(
|
|
193
|
+
target: renderAddress(address),
|
|
174
194
|
retryable: false,
|
|
175
195
|
});
|
|
176
196
|
}
|
|
177
|
-
return Results.assert(await caps.write(pathname, entry
|
|
197
|
+
return Results.assert(await caps.write(pathname, entry));
|
|
178
198
|
}
|
|
179
|
-
async #deleteEntry(scheme,
|
|
180
|
-
const
|
|
181
|
-
const
|
|
199
|
+
async #deleteEntry(scheme, address, ctx) {
|
|
200
|
+
const { pathname } = address;
|
|
201
|
+
const handler = this.#coreCrud(scheme, ctx.functionalityWorkerId);
|
|
202
|
+
const handlerCtx = this.#boundEntryContext(scheme, address, ctx);
|
|
182
203
|
if (typeof handler?.deleteEntry === "function" && handlerCtx !== null) {
|
|
183
204
|
return Results.assert(await handler.deleteEntry(pathname, handlerCtx));
|
|
184
205
|
}
|
|
@@ -187,15 +208,16 @@ class Dispatcher {
|
|
|
187
208
|
return _a.#failure("entry-delete-not-implemented", 501, `The '${scheme}' scheme does not provide entry deletion.`, {}, {
|
|
188
209
|
stage: "entry-delete",
|
|
189
210
|
scheme,
|
|
190
|
-
target: renderAddress(
|
|
211
|
+
target: renderAddress(address),
|
|
191
212
|
retryable: false,
|
|
192
213
|
});
|
|
193
214
|
}
|
|
194
|
-
return Results.assert(await caps.delete(pathname
|
|
215
|
+
return Results.assert(await caps.delete(pathname));
|
|
195
216
|
}
|
|
196
|
-
async #deleteChannel(scheme,
|
|
197
|
-
const
|
|
198
|
-
const
|
|
217
|
+
async #deleteChannel(scheme, address, channel, ctx) {
|
|
218
|
+
const { pathname } = address;
|
|
219
|
+
const handler = this.#coreCrud(scheme, ctx.functionalityWorkerId);
|
|
220
|
+
const handlerCtx = this.#boundEntryContext(scheme, address, ctx);
|
|
199
221
|
if (typeof handler?.deleteChannel === "function" && handlerCtx !== null) {
|
|
200
222
|
return Results.assert(await handler.deleteChannel(pathname, channel, handlerCtx));
|
|
201
223
|
}
|
|
@@ -204,12 +226,12 @@ class Dispatcher {
|
|
|
204
226
|
return _a.#failure("channel-delete-not-implemented", 501, `The '${scheme}' scheme does not provide channel deletion.`, {}, {
|
|
205
227
|
stage: "channel-delete",
|
|
206
228
|
scheme,
|
|
207
|
-
target: renderAddress(
|
|
229
|
+
target: renderAddress(address),
|
|
208
230
|
channel,
|
|
209
231
|
retryable: false,
|
|
210
232
|
});
|
|
211
233
|
}
|
|
212
|
-
return Results.assert(await caps.delete(pathname,
|
|
234
|
+
return Results.assert(await caps.delete(pathname, channel));
|
|
213
235
|
}
|
|
214
236
|
async #workspaceRoot(workspaceId) {
|
|
215
237
|
if (this.#rootCache.has(workspaceId))
|
|
@@ -232,8 +254,8 @@ class Dispatcher {
|
|
|
232
254
|
async #canonicalFilePath(pathname, workspaceId) {
|
|
233
255
|
return Namespace.canonicalizeSpelling(pathname, await this.#workspaceRoot(workspaceId));
|
|
234
256
|
}
|
|
235
|
-
async #editTargetIdentity(statement, workspaceId) {
|
|
236
|
-
const target = this.#extractTarget(statement.target,
|
|
257
|
+
async #editTargetIdentity(statement, workspaceId, workerId) {
|
|
258
|
+
const target = this.#extractTarget(statement.target, workerId);
|
|
237
259
|
await this.#canonColumns(target, workspaceId);
|
|
238
260
|
return {
|
|
239
261
|
key: JSON.stringify([
|
|
@@ -249,8 +271,8 @@ class Dispatcher {
|
|
|
249
271
|
async prepareEditBatches(statements, context) {
|
|
250
272
|
for (const statement of statements)
|
|
251
273
|
assertClassifyingSignal(statement); // {§log-tag-signal}
|
|
252
|
-
const { workspaceId, workerId, loopId, turnId, origin } = context;
|
|
253
|
-
const schemeCtx = this.#buildSchemeCtx({ workspaceId, workerId, loopId, turnId, origin });
|
|
274
|
+
const { workspaceId, workerId, functionalityWorkerId, loopId, turnId, origin } = context;
|
|
275
|
+
const schemeCtx = this.#buildSchemeCtx({ workspaceId, workerId, functionalityWorkerId, loopId, turnId, origin });
|
|
254
276
|
await this.#resourceMutations.prepareEditBatches(statements, context, schemeCtx);
|
|
255
277
|
}
|
|
256
278
|
async dispatch(context) {
|
|
@@ -263,12 +285,13 @@ class Dispatcher {
|
|
|
263
285
|
}
|
|
264
286
|
async #dispatchOne(context) {
|
|
265
287
|
const { statement, workspaceId, workerId, loopId, turnId, sequence, origin, onDispatch, } = context;
|
|
266
|
-
const schemeCtx = this.#buildSchemeCtx({ workspaceId, workerId, loopId, turnId, origin });
|
|
288
|
+
const schemeCtx = this.#buildSchemeCtx({ workspaceId, workerId, functionalityWorkerId: context.functionalityWorkerId, loopId, turnId, origin });
|
|
289
|
+
const { functionalityWorkerId } = schemeCtx;
|
|
267
290
|
let result;
|
|
268
291
|
let curationPlan = null;
|
|
269
|
-
let denial = this.#checkWritable(statement, origin,
|
|
292
|
+
let denial = this.#checkWritable(statement, origin, functionalityWorkerId);
|
|
270
293
|
if (denial === null)
|
|
271
|
-
denial = await this.#checkFlagsGate(statement, loopId,
|
|
294
|
+
denial = await this.#checkFlagsGate(statement, loopId, functionalityWorkerId);
|
|
272
295
|
if (denial !== null) {
|
|
273
296
|
result = denial;
|
|
274
297
|
}
|
|
@@ -283,7 +306,14 @@ class Dispatcher {
|
|
|
283
306
|
result = await this.#resourceMutations.preparedEditResult(statement);
|
|
284
307
|
}
|
|
285
308
|
else if (statement.op === "SEND" && statement.target === null) {
|
|
286
|
-
result = await this.#handleSendBroadcast(statement, {
|
|
309
|
+
result = await this.#handleSendBroadcast(statement, {
|
|
310
|
+
workspaceId,
|
|
311
|
+
workerId,
|
|
312
|
+
loopId,
|
|
313
|
+
turnId,
|
|
314
|
+
sequence,
|
|
315
|
+
origin,
|
|
316
|
+
});
|
|
287
317
|
}
|
|
288
318
|
else if (statement.op === "OPEN" || statement.op === "FOLD") {
|
|
289
319
|
const curation = await this.#runLogCuration(statement, schemeCtx);
|
|
@@ -309,8 +339,8 @@ class Dispatcher {
|
|
|
309
339
|
// {§worker-tool-admission} — the question runtime is admitted
|
|
310
340
|
// per worker: a worker whose own rules don't request user input
|
|
311
341
|
// gets the explicit not-available outcome, never a parked loop.
|
|
312
|
-
if (("signal" in statement && statement.signal === "
|
|
313
|
-
&& !(await WorkerSettingsReader.
|
|
342
|
+
if (("signal" in statement && typeof statement.signal === "string")
|
|
343
|
+
&& !(await WorkerSettingsReader.toolAvailable(this.#db, schemeCtx.functionalityWorkerId, statement.signal))) {
|
|
314
344
|
result = _a.#failure("question-tool-unavailable", 404, "The question tool is not available to this worker.", {}, {
|
|
315
345
|
stage: "tool-admission",
|
|
316
346
|
recovery: "This worker does not request user input; continue from the available evidence.",
|
|
@@ -330,15 +360,18 @@ class Dispatcher {
|
|
|
330
360
|
catch (err) { // a scheme exception becomes the op's 500 outcome — {§scheme-surface-exception-500}
|
|
331
361
|
if (err instanceof InvalidOperationResultError)
|
|
332
362
|
throw err;
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
363
|
+
if (err instanceof OperationFailureError) {
|
|
364
|
+
result = err.result;
|
|
365
|
+
}
|
|
366
|
+
else {
|
|
367
|
+
const scheme = schemeNameOf(statement.target);
|
|
368
|
+
console.error(`Scheme '${scheme ?? "unknown"}' ${statement.op} threw outside its operation result contract:`, err);
|
|
369
|
+
result = _a.#failure("scheme-handler-threw", 500, `The '${scheme ?? "unknown"}' scheme did not produce a ${statement.op} result.`, {}, {
|
|
338
370
|
stage: "scheme-dispatch",
|
|
339
371
|
scheme,
|
|
340
372
|
operation: statement.op,
|
|
341
373
|
});
|
|
374
|
+
}
|
|
342
375
|
}
|
|
343
376
|
}
|
|
344
377
|
// {§fold-open-meta-operations} — persist OPEN/FOLD for forensics;
|
|
@@ -350,6 +383,7 @@ class Dispatcher {
|
|
|
350
383
|
const logEntryId = await this.#writeLog({
|
|
351
384
|
statement,
|
|
352
385
|
result,
|
|
386
|
+
functionalityWorkerId,
|
|
353
387
|
workspaceId,
|
|
354
388
|
workerId,
|
|
355
389
|
loopId,
|
|
@@ -380,13 +414,13 @@ class Dispatcher {
|
|
|
380
414
|
autoAccept = EffectPolicy.decide(effect) === "auto";
|
|
381
415
|
}
|
|
382
416
|
if (autoAccept) {
|
|
383
|
-
const initialSettlement = await this.#proposals.workerApply(statement, result, { decision: "accept" }, { workspaceId, workerId, loopId, turnId });
|
|
417
|
+
const initialSettlement = await this.#proposals.workerApply(statement, result, { decision: "accept" }, { workspaceId, workerId, functionalityWorkerId, loopId, turnId });
|
|
384
418
|
const effective = await this.#resourceMutations.settleProposal({
|
|
385
419
|
statement,
|
|
386
420
|
result,
|
|
387
421
|
settlement: initialSettlement,
|
|
388
422
|
ctx: schemeCtx,
|
|
389
|
-
ids: { workspaceId, workerId, loopId, turnId },
|
|
423
|
+
ids: { workspaceId, workerId, functionalityWorkerId, loopId, turnId },
|
|
390
424
|
});
|
|
391
425
|
return this.#proposals.applyResolution(logEntryId, effective);
|
|
392
426
|
}
|
|
@@ -415,13 +449,13 @@ class Dispatcher {
|
|
|
415
449
|
// file, spawns the process, etc.). Its operation result is
|
|
416
450
|
// preserved: an apply failure keeps its original status and
|
|
417
451
|
// Problem Details instead of masquerading as a client rejection.
|
|
418
|
-
const initialSettlement = await this.#proposals.workerApply(statement, result, resolution, { workspaceId, workerId, loopId, turnId });
|
|
452
|
+
const initialSettlement = await this.#proposals.workerApply(statement, result, resolution, { workspaceId, workerId, functionalityWorkerId, loopId, turnId });
|
|
419
453
|
const effective = await this.#resourceMutations.settleProposal({
|
|
420
454
|
statement,
|
|
421
455
|
result,
|
|
422
456
|
settlement: initialSettlement,
|
|
423
457
|
ctx: schemeCtx,
|
|
424
|
-
ids: { workspaceId, workerId, loopId, turnId },
|
|
458
|
+
ids: { workspaceId, workerId, functionalityWorkerId, loopId, turnId },
|
|
425
459
|
});
|
|
426
460
|
const post = await this.#proposals.applyResolution(logEntryId, effective);
|
|
427
461
|
return post;
|
|
@@ -438,8 +472,8 @@ class Dispatcher {
|
|
|
438
472
|
if (statement.op !== "READ")
|
|
439
473
|
throw new Error(`look resolves READ only; got ${statement.op}`);
|
|
440
474
|
// turnId is a write-time FK only — a look writes no row, so 0 (no turn) is inert.
|
|
441
|
-
const schemeCtx = this.#buildSchemeCtx({ workspaceId, workerId, loopId, turnId: 0, origin });
|
|
442
|
-
const denial = await this.#checkFlagsGate(statement, loopId,
|
|
475
|
+
const schemeCtx = this.#buildSchemeCtx({ workspaceId, workerId, functionalityWorkerId: context.functionalityWorkerId, loopId, turnId: 0, origin });
|
|
476
|
+
const denial = await this.#checkFlagsGate(statement, loopId, schemeCtx.functionalityWorkerId);
|
|
443
477
|
if (denial !== null)
|
|
444
478
|
return denial;
|
|
445
479
|
return this.#run(schemeNameOf(statement.target), statement, schemeCtx);
|
|
@@ -449,30 +483,17 @@ class Dispatcher {
|
|
|
449
483
|
// schemes may return the already-authorized principal key.
|
|
450
484
|
async resolveEntryAddress(context) {
|
|
451
485
|
const { target, workspaceId, workerId } = context;
|
|
452
|
-
const routedScheme = schemeNameOf(target);
|
|
453
|
-
if (routedScheme === null)
|
|
454
|
-
return null;
|
|
455
|
-
const handler = this.#schemes.get(routedScheme, workspaceId);
|
|
456
|
-
const manifest = this.#schemes.manifestFor(routedScheme, workspaceId);
|
|
457
|
-
if (handler === undefined || manifest?.category !== "data")
|
|
458
|
-
return null;
|
|
459
|
-
const addressedScheme = target.kind === "url" ? target.scheme : routedScheme;
|
|
460
486
|
const coreCtx = this.#buildSchemeCtx({
|
|
461
487
|
workspaceId,
|
|
462
488
|
workerId,
|
|
489
|
+
functionalityWorkerId: context.functionalityWorkerId,
|
|
463
490
|
loopId: 0,
|
|
464
491
|
turnId: 0,
|
|
465
492
|
origin: "client",
|
|
466
493
|
});
|
|
467
|
-
const
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
routedScheme,
|
|
471
|
-
handler,
|
|
472
|
-
manifest,
|
|
473
|
-
ctx: coreCtx,
|
|
474
|
-
schemeCtx,
|
|
475
|
-
});
|
|
494
|
+
const resolved = await this.bindEntryAddress(target, coreCtx);
|
|
495
|
+
if (resolved === null)
|
|
496
|
+
return null;
|
|
476
497
|
if (resolved.address === null)
|
|
477
498
|
return null;
|
|
478
499
|
const rendered = target.kind === "url"
|
|
@@ -483,70 +504,30 @@ class Dispatcher {
|
|
|
483
504
|
return {
|
|
484
505
|
ownerId: resolved.address.ownerId,
|
|
485
506
|
scheme: resolved.address.scheme,
|
|
507
|
+
authority: resolved.address.authority,
|
|
486
508
|
pathname: resolved.address.pathname,
|
|
487
509
|
target: rendered,
|
|
488
510
|
};
|
|
489
511
|
}
|
|
490
|
-
async
|
|
491
|
-
const
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
return { address: null, result: null };
|
|
504
|
-
if ("status" in resolved) {
|
|
505
|
-
const result = Results.assert(resolved);
|
|
506
|
-
if (result.status < 300) {
|
|
507
|
-
throw new InvalidOperationResultError(`Scheme '${routedScheme}' returned a successful operation result instead of an entry address.`);
|
|
508
|
-
}
|
|
509
|
-
return { address: null, result };
|
|
510
|
-
}
|
|
511
|
-
if (typeof resolved.pathname !== "string") {
|
|
512
|
-
throw new TypeError(`Scheme '${routedScheme}' returned an invalid entry pathname.`);
|
|
513
|
-
}
|
|
514
|
-
let ownerId;
|
|
515
|
-
if ("ownerId" in resolved) {
|
|
516
|
-
if (!(handler instanceof CoreSchemeAdapterBase)) {
|
|
517
|
-
throw new TypeError(`Scheme '${routedScheme}' returned a core-only entry owner id.`);
|
|
518
|
-
}
|
|
519
|
-
ownerId = resolved.ownerId;
|
|
520
|
-
}
|
|
521
|
-
else if (resolved.owner === "worker") {
|
|
522
|
-
ownerId = ctx.workerId;
|
|
523
|
-
}
|
|
524
|
-
else if (resolved.owner === "commons") {
|
|
525
|
-
ownerId = await Owner.commonsId(this.#db, ctx.workspaceId);
|
|
526
|
-
}
|
|
527
|
-
else {
|
|
528
|
-
throw new TypeError(`Scheme '${routedScheme}' returned an invalid entry owner.`);
|
|
529
|
-
}
|
|
530
|
-
if (!Number.isSafeInteger(ownerId) || ownerId < 1) {
|
|
531
|
-
throw new TypeError(`Scheme '${routedScheme}' returned an invalid entry owner id.`);
|
|
512
|
+
async bindEntryAddress(target, ctx) {
|
|
513
|
+
const routedScheme = schemeNameOf(target);
|
|
514
|
+
if (routedScheme === null)
|
|
515
|
+
return null;
|
|
516
|
+
const handler = this.#schemes.get(routedScheme, ctx.functionalityWorkerId);
|
|
517
|
+
const manifest = this.#schemes.manifestFor(routedScheme, ctx.functionalityWorkerId);
|
|
518
|
+
if (handler === undefined || manifest?.category !== "data")
|
|
519
|
+
return null;
|
|
520
|
+
return this.#resolveDataEntryAddress({ target, routedScheme, handler, manifest, ctx });
|
|
521
|
+
}
|
|
522
|
+
async #resolveDataEntryAddress({ target, routedScheme, handler, manifest, ctx, }) {
|
|
523
|
+
if (manifest.category !== "data") {
|
|
524
|
+
throw new TypeError(`Scheme '${routedScheme}' is not entry-bearing.`);
|
|
532
525
|
}
|
|
533
|
-
return {
|
|
534
|
-
address: {
|
|
535
|
-
ownerId,
|
|
536
|
-
scheme: manifest.storedScheme ?? addressedScheme,
|
|
537
|
-
pathname: resolved.pathname,
|
|
538
|
-
},
|
|
539
|
-
result: null,
|
|
540
|
-
};
|
|
526
|
+
return this.#entryAddresses.resolve({ target, routedScheme, handler, manifest, ctx });
|
|
541
527
|
}
|
|
542
|
-
async #prepareDataRepresentation({ target, routedScheme, handler, manifest, ctx,
|
|
543
|
-
const resolved = await this.#resolveDataEntryAddress({
|
|
544
|
-
target,
|
|
545
|
-
routedScheme,
|
|
546
|
-
handler,
|
|
547
|
-
manifest,
|
|
548
|
-
ctx,
|
|
549
|
-
schemeCtx,
|
|
528
|
+
async #prepareDataRepresentation({ target, routedScheme, handler, manifest, ctx, publishedChannel, resolved: priorResolution, }) {
|
|
529
|
+
const resolved = priorResolution ?? await this.#resolveDataEntryAddress({
|
|
530
|
+
target, routedScheme, handler, manifest, ctx,
|
|
550
531
|
});
|
|
551
532
|
if (resolved.address === null
|
|
552
533
|
|| resolved.result !== null
|
|
@@ -562,11 +543,13 @@ class Dispatcher {
|
|
|
562
543
|
}
|
|
563
544
|
: target;
|
|
564
545
|
const preparationCtx = new SchemeCtxImpl(ctx, target.kind === "url" ? target.scheme : routedScheme, manifest, this.#liveSubscriptions, {
|
|
546
|
+
authority: address.authority,
|
|
565
547
|
ownerId: address.ownerId,
|
|
566
548
|
publishedChannel,
|
|
567
549
|
});
|
|
568
550
|
const prepared = Results.assertRepresentationPreparation(await handler.prepareRepresentation({
|
|
569
551
|
target: selectionNeutralTarget,
|
|
552
|
+
authority: address.authority,
|
|
570
553
|
pathname: address.pathname,
|
|
571
554
|
}, preparationCtx));
|
|
572
555
|
return {
|
|
@@ -578,7 +561,7 @@ class Dispatcher {
|
|
|
578
561
|
// handler and addressed context as an authored READ. {§exec-target-routing}
|
|
579
562
|
async readExecSource(statement, ctx) {
|
|
580
563
|
const schemeName = schemeNameOf(statement.target);
|
|
581
|
-
const manifest = schemeName === null ? undefined : this.#schemes.manifestFor(schemeName, ctx.
|
|
564
|
+
const manifest = schemeName === null ? undefined : this.#schemes.manifestFor(schemeName, ctx.functionalityWorkerId);
|
|
582
565
|
if (manifest !== undefined && manifest.category !== "data") {
|
|
583
566
|
return _a.#failure("exec-source-not-data", 501, `Scheme '${schemeName}' is not a data source for EXEC.`, {}, {
|
|
584
567
|
scheme: schemeName,
|
|
@@ -588,11 +571,16 @@ class Dispatcher {
|
|
|
588
571
|
}
|
|
589
572
|
return Results.assertReadResult(await this.#run(schemeName, statement, ctx));
|
|
590
573
|
}
|
|
574
|
+
// The one place per-dispatch coordinates are built; a caller that carries no
|
|
575
|
+
// explicit Functionality coordinate acts in its own Worker's
|
|
576
|
+
// ({§actor-boundary-attached-functionality}). Consumers read the built
|
|
577
|
+
// PlurnkSchemeContext and never re-derive.
|
|
591
578
|
#buildSchemeCtx(ids) {
|
|
592
579
|
const { workspaceId, workerId, loopId, turnId, origin } = ids;
|
|
580
|
+
const functionalityWorkerId = ids.functionalityWorkerId ?? workerId;
|
|
593
581
|
return {
|
|
594
582
|
db: this.#db,
|
|
595
|
-
workspaceId, workerId, loopId, turnId,
|
|
583
|
+
workspaceId, workerId, functionalityWorkerId, loopId, turnId,
|
|
596
584
|
writer: origin,
|
|
597
585
|
signal: this.#loopSignal(loopId),
|
|
598
586
|
streamEventNotify: this.#streamEventNotify,
|
|
@@ -611,7 +599,8 @@ class Dispatcher {
|
|
|
611
599
|
// - SEND broadcast (path=null) has no target scheme; not gated.
|
|
612
600
|
// - COPY: dst scheme writableBy applies.
|
|
613
601
|
// - MOVE: both src (delete) and dst (write) schemes' writableBy apply.
|
|
614
|
-
#checkWritable(statement, origin,
|
|
602
|
+
#checkWritable(statement, origin, functionalityWorkerId) {
|
|
603
|
+
const workerId = functionalityWorkerId;
|
|
615
604
|
if (!MUTATING_OPS.has(statement.op))
|
|
616
605
|
return null;
|
|
617
606
|
if (statement.op === "SEND" && statement.target === null)
|
|
@@ -619,23 +608,23 @@ class Dispatcher {
|
|
|
619
608
|
// EXEC's operation authority always belongs to the exec scheme;
|
|
620
609
|
// runtime-specific resource authority is gated separately below.
|
|
621
610
|
if (statement.op === "EXEC") {
|
|
622
|
-
return this.#denyIfDisallowed("exec", origin,
|
|
611
|
+
return this.#denyIfDisallowed("exec", origin, workerId);
|
|
623
612
|
}
|
|
624
613
|
// Worker control (FORK/WORK → worker://<name>, spawn or fork) is gated by worker://'s writableBy — its
|
|
625
614
|
// body is a seed prompt, not a dst path, so the entry-COPY dst-parse below doesn't apply.
|
|
626
615
|
// {§machine-processes}
|
|
627
616
|
if (this.#isWorkerControl(statement))
|
|
628
|
-
return this.#denyIfDisallowed("worker", origin,
|
|
617
|
+
return this.#denyIfDisallowed("worker", origin, workerId);
|
|
629
618
|
if (statement.op === "COPY" || statement.op === "MOVE") {
|
|
630
619
|
const dst = statement.body?.target ?? null;
|
|
631
620
|
const dstScheme = schemeNameOf(dst);
|
|
632
|
-
const dstDenial = this.#denyIfDisallowed(dstScheme, origin,
|
|
621
|
+
const dstDenial = this.#denyIfDisallowed(dstScheme, origin, workerId);
|
|
633
622
|
if (dstDenial !== null)
|
|
634
623
|
return dstDenial;
|
|
635
624
|
if (statement.op === "MOVE") {
|
|
636
625
|
const srcScheme = schemeNameOf(statement.target);
|
|
637
626
|
if (srcScheme !== dstScheme) {
|
|
638
|
-
const srcDenial = this.#denyIfDisallowed(srcScheme, origin,
|
|
627
|
+
const srcDenial = this.#denyIfDisallowed(srcScheme, origin, workerId);
|
|
639
628
|
if (srcDenial !== null)
|
|
640
629
|
return srcDenial;
|
|
641
630
|
}
|
|
@@ -643,15 +632,15 @@ class Dispatcher {
|
|
|
643
632
|
return null;
|
|
644
633
|
}
|
|
645
634
|
const target = schemeNameOf(statement.target);
|
|
646
|
-
return this.#denyIfDisallowed(target, origin,
|
|
635
|
+
return this.#denyIfDisallowed(target, origin, workerId);
|
|
647
636
|
}
|
|
648
|
-
#denyIfDisallowed(schemeName, origin,
|
|
637
|
+
#denyIfDisallowed(schemeName, origin, workerId) {
|
|
649
638
|
if (schemeName === null)
|
|
650
639
|
return null;
|
|
651
|
-
const handler = this.#schemes.get(schemeName,
|
|
640
|
+
const handler = this.#schemes.get(schemeName, workerId);
|
|
652
641
|
if (handler === undefined)
|
|
653
642
|
return null;
|
|
654
|
-
const manifest = this.#schemes.manifestFor(schemeName,
|
|
643
|
+
const manifest = this.#schemes.manifestFor(schemeName, workerId);
|
|
655
644
|
if (manifest === undefined)
|
|
656
645
|
throw new Error(`registered scheme '${schemeName}' has no manifest`);
|
|
657
646
|
if (manifest.writableBy.includes(origin))
|
|
@@ -669,7 +658,8 @@ class Dispatcher {
|
|
|
669
658
|
// active set under the loop's persisted flags. A registered scheme outside
|
|
670
659
|
// that set returns 403; unknown names continue to their operation owner for
|
|
671
660
|
// the ordinary registration failure. Action-entry-as-outcome carries either.
|
|
672
|
-
async #checkFlagsGate(statement, loopId,
|
|
661
|
+
async #checkFlagsGate(statement, loopId, functionalityWorkerId) {
|
|
662
|
+
const workerId = functionalityWorkerId;
|
|
673
663
|
// Broadcast SEND has no scheme to gate.
|
|
674
664
|
if (statement.op === "SEND" && statement.target === null)
|
|
675
665
|
return null;
|
|
@@ -703,7 +693,7 @@ class Dispatcher {
|
|
|
703
693
|
});
|
|
704
694
|
}
|
|
705
695
|
}
|
|
706
|
-
const active = this.#schemes.resolveForLoop(flags,
|
|
696
|
+
const active = this.#schemes.resolveForLoop(flags, workerId);
|
|
707
697
|
// {§mode-ask-read-only}: name the non-retryable restriction so the model changes course.
|
|
708
698
|
const restriction = flags.mode === "ask"
|
|
709
699
|
? "this is an ask-mode (read-only) loop — you cannot run commands or take host actions here"
|
|
@@ -711,7 +701,7 @@ class Dispatcher {
|
|
|
711
701
|
: flags.noWeb ? "web access is disabled for this loop"
|
|
712
702
|
: "interaction is disabled for this loop";
|
|
713
703
|
const checkScheme = (scheme) => {
|
|
714
|
-
if (scheme === null || !this.#schemes.has(scheme,
|
|
704
|
+
if (scheme === null || !this.#schemes.has(scheme, workerId))
|
|
715
705
|
return null;
|
|
716
706
|
if (active.has(scheme))
|
|
717
707
|
return null;
|
|
@@ -735,7 +725,13 @@ class Dispatcher {
|
|
|
735
725
|
return operationDenial;
|
|
736
726
|
const requested = typeof statement.signal === "string" ? statement.signal : "";
|
|
737
727
|
const runtime = requested === "" ? "sh" : requested;
|
|
738
|
-
|
|
728
|
+
// {§manifest-flag-affinity} — a runtime alias is a registered scheme
|
|
729
|
+
// with its own affinity: the one resolver gates the selected runtime
|
|
730
|
+
// exactly as it gates the exec family (e.g. `question` under noInteraction).
|
|
731
|
+
const runtimeDenial = checkScheme(runtime);
|
|
732
|
+
if (runtimeDenial !== null)
|
|
733
|
+
return runtimeDenial;
|
|
734
|
+
const targetKind = this.#executors()?.entry(runtime, workerId)?.invocation.target?.kind;
|
|
739
735
|
if (targetKind !== "resource")
|
|
740
736
|
return null;
|
|
741
737
|
const sourceScheme = schemeNameOf(statement.target);
|
|
@@ -808,17 +804,32 @@ class Dispatcher {
|
|
|
808
804
|
}
|
|
809
805
|
if (statement.op === "FORK") {
|
|
810
806
|
// Branch the current worker's log into a named sister.
|
|
811
|
-
const branchWorkerId = await Fork.fork(this.#db, ctx.workerId, name);
|
|
812
|
-
await ctx.injectWorker({
|
|
807
|
+
const branchWorkerId = await Fork.fork(this.#db, ctx.workerId, name, (scheme) => this.#schemes.entryInheritanceForStoredScheme(scheme, ctx.workerId));
|
|
808
|
+
await ctx.injectWorker({
|
|
809
|
+
workspaceId: ctx.workspaceId,
|
|
810
|
+
workerId: branchWorkerId,
|
|
811
|
+
sourceWorkerId: ctx.workerId,
|
|
812
|
+
prompt,
|
|
813
|
+
flags,
|
|
814
|
+
parentLoopId: ctx.loopId,
|
|
815
|
+
});
|
|
813
816
|
return { status: 200, body: name };
|
|
814
817
|
}
|
|
815
818
|
// WORK — a fresh worker sister named <name>.
|
|
816
819
|
const row = await this.#db.fork_insert_worker.get({
|
|
817
820
|
workspace_id: ctx.workspaceId, name, parent_worker_id: ctx.workerId, origin: ctx.writer,
|
|
821
|
+
fork_snapshot: 0,
|
|
818
822
|
});
|
|
819
823
|
if (row === undefined)
|
|
820
824
|
throw new Error("worker spawn: worker insert returned no row");
|
|
821
|
-
await ctx.injectWorker({
|
|
825
|
+
await ctx.injectWorker({
|
|
826
|
+
workspaceId: ctx.workspaceId,
|
|
827
|
+
workerId: row.id,
|
|
828
|
+
sourceWorkerId: ctx.workerId,
|
|
829
|
+
prompt,
|
|
830
|
+
flags,
|
|
831
|
+
parentLoopId: ctx.loopId,
|
|
832
|
+
});
|
|
822
833
|
return { status: 200, body: name };
|
|
823
834
|
}
|
|
824
835
|
// KILL is target-polymorphic. Scheme handlers own the optional numeric code's
|
|
@@ -834,20 +845,40 @@ class Dispatcher {
|
|
|
834
845
|
if (schemeName === null) {
|
|
835
846
|
return _a.#failure("kill-target-scheme-required", 400, "KILL target requires a scheme.", {}, { retryable: false });
|
|
836
847
|
}
|
|
848
|
+
const manifest = this.#schemes.manifestFor(schemeName, ctx.functionalityWorkerId);
|
|
849
|
+
const coordinate = entryCoordinateOf(path, manifest?.authority ?? "namespace");
|
|
837
850
|
// Log targets use the same killable dispatch as streams; erasure is their
|
|
838
851
|
// permanent curation operation. {§turn-ops-log-curation}
|
|
839
852
|
// Process-KILL: any scheme whose handler exposes kill() aborts a live stream — the
|
|
840
853
|
// exec handler, registered as "exec" + under every runtime tag (sh/node), so a tag-
|
|
841
854
|
// addressed stream (sh:///l/t/s) routes here, not to deleteEntry. {§exec}
|
|
842
|
-
const killable = this.#schemes.get(schemeName, ctx.
|
|
855
|
+
const killable = this.#schemes.get(schemeName, ctx.functionalityWorkerId);
|
|
843
856
|
if (killable !== undefined && typeof killable.kill === "function") {
|
|
844
857
|
// Pass the model's OWN scheme so a stream-KILL error answers in the runtime tag the
|
|
845
858
|
// model addressed (sh:///…), not the internal `exec` ({§fs-answer-in-canon}).
|
|
846
|
-
|
|
859
|
+
let handlerCtx;
|
|
860
|
+
if (manifest?.category === "data") {
|
|
861
|
+
const resolved = await this.#resolveDataEntryAddress({
|
|
862
|
+
target: path,
|
|
863
|
+
routedScheme: schemeName,
|
|
864
|
+
handler: killable,
|
|
865
|
+
manifest,
|
|
866
|
+
ctx,
|
|
867
|
+
});
|
|
868
|
+
if (resolved.result !== null)
|
|
869
|
+
return resolved.result;
|
|
870
|
+
if (resolved.address === null) {
|
|
871
|
+
return _a.#failure("entry-not-found", 404, `No entry exists at ${renderAddress({ scheme: schemeName, ...coordinate })}.`);
|
|
872
|
+
}
|
|
873
|
+
handlerCtx = this.#boundEntryContext(schemeName, resolved.address, ctx);
|
|
874
|
+
}
|
|
875
|
+
else {
|
|
876
|
+
handlerCtx = await this.#handlerContext(schemeName, ctx, coordinate.authority);
|
|
877
|
+
}
|
|
847
878
|
if (handlerCtx === null) {
|
|
848
879
|
throw new InvalidOperationResultError(`Registered scheme '${schemeName}' has no dispatch context.`);
|
|
849
880
|
}
|
|
850
|
-
return await killable.kill(
|
|
881
|
+
return await killable.kill(coordinate.pathname, statement.signal, handlerCtx, schemeName);
|
|
851
882
|
}
|
|
852
883
|
if (schemeName === "worker") {
|
|
853
884
|
// Entry-path present → KILL a private owner-held entry (delete it), self-only —
|
|
@@ -856,7 +887,22 @@ class Dispatcher {
|
|
|
856
887
|
const entryPath = path.kind === "url" ? (path.pathname ?? "") : "";
|
|
857
888
|
if (entryPath !== "" && entryPath !== "/") {
|
|
858
889
|
const workerHandler = this.#schemes.get("worker");
|
|
859
|
-
|
|
890
|
+
if (manifest?.category !== "data") {
|
|
891
|
+
throw new InvalidOperationResultError("Registered scheme 'worker' is not entry-bearing.");
|
|
892
|
+
}
|
|
893
|
+
const resolved = await this.#resolveDataEntryAddress({
|
|
894
|
+
target: path,
|
|
895
|
+
routedScheme: "worker",
|
|
896
|
+
handler: workerHandler,
|
|
897
|
+
manifest,
|
|
898
|
+
ctx,
|
|
899
|
+
});
|
|
900
|
+
if (resolved.result !== null)
|
|
901
|
+
return resolved.result;
|
|
902
|
+
if (resolved.address === null) {
|
|
903
|
+
return _a.#failure("entry-not-found", 404, "The worker entry does not exist.");
|
|
904
|
+
}
|
|
905
|
+
const handlerCtx = this.#boundEntryContext("worker", resolved.address, ctx);
|
|
860
906
|
if (handlerCtx === null) {
|
|
861
907
|
throw new InvalidOperationResultError("Registered scheme 'worker' has no dispatch context.");
|
|
862
908
|
}
|
|
@@ -883,12 +929,28 @@ class Dispatcher {
|
|
|
883
929
|
await this.#cancelWorker(workerId, "killed via worker:// KILL");
|
|
884
930
|
return { status: 200 };
|
|
885
931
|
}
|
|
886
|
-
if (!this.#schemes.has(schemeName, ctx.
|
|
932
|
+
if (!this.#schemes.has(schemeName, ctx.functionalityWorkerId)) {
|
|
887
933
|
return _a.#failure("scheme-not-found", 501, `Scheme '${schemeName}' is not registered.`, {}, { scheme: schemeName, retryable: false });
|
|
888
934
|
}
|
|
935
|
+
const handler = this.#schemes.get(schemeName, ctx.functionalityWorkerId);
|
|
936
|
+
if (handler === undefined || manifest?.category !== "data") {
|
|
937
|
+
return _a.#failure("entry-operation-unsupported", 400, `KILL requires an entry-bearing target; '${schemeName}' does not provide one.`, {}, { scheme: schemeName, retryable: false });
|
|
938
|
+
}
|
|
939
|
+
const resolved = await this.#resolveDataEntryAddress({
|
|
940
|
+
target: path,
|
|
941
|
+
routedScheme: schemeName,
|
|
942
|
+
handler,
|
|
943
|
+
manifest,
|
|
944
|
+
ctx,
|
|
945
|
+
});
|
|
946
|
+
if (resolved.result !== null)
|
|
947
|
+
return resolved.result;
|
|
948
|
+
if (resolved.address === null) {
|
|
949
|
+
return _a.#failure("entry-not-found", 404, "The KILL target does not exist.");
|
|
950
|
+
}
|
|
889
951
|
// A host-effecting delete (file) returns 202 to PROPOSE — pass its attrs through so the proposal
|
|
890
952
|
// carries the delete target to review (#isProposal fires on 202). Plurnk-internal deletes execute inline.
|
|
891
|
-
return this.#deleteEntry(schemeName,
|
|
953
|
+
return this.#deleteEntry(schemeName, resolved.address, ctx);
|
|
892
954
|
}
|
|
893
955
|
async #writeActionlessEntry({ verbatim, workerId, loopId, turnId, sequence, origin, kind, folded, modelCallId = null, attrs = {} }) {
|
|
894
956
|
const durableAttrs = { ...attrs, kind };
|
|
@@ -942,7 +1004,7 @@ class Dispatcher {
|
|
|
942
1004
|
},
|
|
943
1005
|
});
|
|
944
1006
|
}
|
|
945
|
-
// PLAN — the model's
|
|
1007
|
+
// PLAN — the model's complete current Plan. An ordinary op: dispatched like any
|
|
946
1008
|
// other, logged, and broadcast to the client as a log entry — but a pure no-op for
|
|
947
1009
|
// state (PLAN ∉ MUTATING_OPS); its body serializes into the log row's tx, no effect.
|
|
948
1010
|
#handlePlan(statement) {
|
|
@@ -957,6 +1019,7 @@ class Dispatcher {
|
|
|
957
1019
|
Results.assert(result);
|
|
958
1020
|
const logEntryId = await this.#writeLog({
|
|
959
1021
|
...context,
|
|
1022
|
+
functionalityWorkerId: context.functionalityWorkerId ?? context.workerId,
|
|
960
1023
|
result,
|
|
961
1024
|
curationPlan: null,
|
|
962
1025
|
modelCallId,
|
|
@@ -983,8 +1046,10 @@ class Dispatcher {
|
|
|
983
1046
|
const boundaries = await this.#nextPacketBoundaries(workerId, turnId);
|
|
984
1047
|
if (boundaries.retrievals)
|
|
985
1048
|
pending.push("this turn's retrieval results (they land in the NEXT packet's Log)");
|
|
986
|
-
if (boundaries.streamTerminations) {
|
|
987
|
-
|
|
1049
|
+
if (boundaries.streamTerminations.length > 0) {
|
|
1050
|
+
// Name the exact streams (bench#5 requiem #9): the model can wait on
|
|
1051
|
+
// or KILL them specifically instead of guessing what remains.
|
|
1052
|
+
pending.push(`completed stream results that land in the NEXT packet's Log: ${boundaries.streamTerminations.join("; ")}`);
|
|
988
1053
|
}
|
|
989
1054
|
if (boundaries.childTerminations)
|
|
990
1055
|
pending.push("worker results that arrived during this turn (they land NEXT turn)");
|
|
@@ -996,17 +1061,17 @@ class Dispatcher {
|
|
|
996
1061
|
// their distinct contracts: retrievals block explicit completion, whereas FOLD only
|
|
997
1062
|
// prevents an empty wait from being inferred as completion.
|
|
998
1063
|
async #nextPacketBoundaries(workerId, turnId) {
|
|
999
|
-
const [turnBoundaries,
|
|
1064
|
+
const [turnBoundaries, streamTerminations, childTermination] = await Promise.all([
|
|
1000
1065
|
this.#db.engine_turn_packet_boundaries.all({ turn_id: turnId }),
|
|
1001
1066
|
this.#db.engine_worker_has_undelivered_stream_term
|
|
1002
|
-
.
|
|
1067
|
+
.all({ worker_id: workerId }),
|
|
1003
1068
|
this.#db.engine_worker_has_undelivered_child_term
|
|
1004
1069
|
.get({ worker_id: workerId }),
|
|
1005
1070
|
]);
|
|
1006
1071
|
return {
|
|
1007
1072
|
retrievals: turnBoundaries.some(({ op }) => op !== "FOLD"),
|
|
1008
1073
|
folds: turnBoundaries.some(({ op }) => op === "FOLD"),
|
|
1009
|
-
streamTerminations:
|
|
1074
|
+
streamTerminations: streamTerminations.map(({ handle }) => handle),
|
|
1010
1075
|
childTerminations: childTermination !== undefined,
|
|
1011
1076
|
};
|
|
1012
1077
|
}
|
|
@@ -1088,7 +1153,7 @@ class Dispatcher {
|
|
|
1088
1153
|
// fired, so do not park; continue directly to the packet that
|
|
1089
1154
|
// materializes them.
|
|
1090
1155
|
const boundaries = await this.#nextPacketBoundaries(workerId, turnId);
|
|
1091
|
-
if (boundaries.retrievals || boundaries.folds || boundaries.streamTerminations || boundaries.childTerminations) {
|
|
1156
|
+
if (boundaries.retrievals || boundaries.folds || boundaries.streamTerminations.length > 0 || boundaries.childTerminations) {
|
|
1092
1157
|
return { status: 102 };
|
|
1093
1158
|
}
|
|
1094
1159
|
const failCount = await this.#unobservedFailureCount(turnId);
|
|
@@ -1109,34 +1174,41 @@ class Dispatcher {
|
|
|
1109
1174
|
// attempt faithfully (status_rx=409, never erased); the loop stays a continue; the strike
|
|
1110
1175
|
// couples in runTurn. [499] abandons and cancels the descendant scope.
|
|
1111
1176
|
if (status === 200) {
|
|
1112
|
-
//
|
|
1113
|
-
//
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
//
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1177
|
+
// Model completion is a claim about the Worker's observed work and
|
|
1178
|
+
// therefore crosses the pending-result rails. A `_plurnk`
|
|
1179
|
+
// maintenance program closes only its own administrative loop; it
|
|
1180
|
+
// must not claim, consume, or be blocked by model work elsewhere in
|
|
1181
|
+
// the same Worker.
|
|
1182
|
+
if (ctx.origin === "model") {
|
|
1183
|
+
// {§send-premature-terminate} — same-turn failures are unobserved
|
|
1184
|
+
// pending results and therefore refuse completion.
|
|
1185
|
+
const failCount = await this.#unobservedFailureCount(turnId);
|
|
1186
|
+
if (failCount > 0)
|
|
1187
|
+
return _a.#unobservedFailures(failCount);
|
|
1188
|
+
const pending = await this.#pendingSet(workerId, turnId);
|
|
1189
|
+
if (pending.length > 0) {
|
|
1190
|
+
// A retrieval-only refusal needs no KILL/park remedy menu: the results simply
|
|
1191
|
+
// arrive in the next packet. Streams and children retain their remedy steer.
|
|
1192
|
+
const retrievalsOnly = pending.every((k) => k.startsWith("this turn's retrieval results"));
|
|
1193
|
+
if (retrievalsOnly) {
|
|
1194
|
+
return _a.#failure("retrieval-results-unobserved", 409, "Last turn both performed retrieval operations and attempted to terminate. Retrieval operations force an additional turn so their results can be reviewed.", {}, {
|
|
1195
|
+
pending: [...pending],
|
|
1196
|
+
stage: "completion",
|
|
1197
|
+
recovery: "Review the results, then use only `# PLAN0` and `## SEND0 [200]` to conclude.",
|
|
1198
|
+
retryable: false,
|
|
1199
|
+
});
|
|
1200
|
+
}
|
|
1201
|
+
return _a.#failure("work-remains", 409, `Completion was refused while work remains: ${pending.join("; ")}.`, {}, {
|
|
1124
1202
|
pending: [...pending],
|
|
1125
1203
|
stage: "completion",
|
|
1126
|
-
recovery: "
|
|
1204
|
+
recovery: "Resolve the listed pending work before concluding.",
|
|
1127
1205
|
retryable: false,
|
|
1128
1206
|
});
|
|
1129
1207
|
}
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
recovery: "Resolve the listed pending work before concluding.",
|
|
1134
|
-
retryable: false,
|
|
1135
|
-
});
|
|
1208
|
+
const branchDenial = await this.#branchCompletionGate?.(workerId) ?? null;
|
|
1209
|
+
if (branchDenial !== null)
|
|
1210
|
+
return branchDenial;
|
|
1136
1211
|
}
|
|
1137
|
-
const branchDenial = await this.#branchCompletionGate?.(workerId) ?? null;
|
|
1138
|
-
if (branchDenial !== null)
|
|
1139
|
-
return branchDenial;
|
|
1140
1212
|
const finished = await this.#lifecycle.finish(loopId, TerminalResult.success(raw));
|
|
1141
1213
|
return _a.#statusResult(finished !== null ? 200 : await this.#lifecycle.status(loopId), "loop-already-terminal", "The loop was already terminal when SEND attempted to conclude it.");
|
|
1142
1214
|
}
|
|
@@ -1180,13 +1252,56 @@ class Dispatcher {
|
|
|
1180
1252
|
: {};
|
|
1181
1253
|
return _a.#failure("target-scheme-required", 400, `${statement.op} requires a target scheme.`, fields, { operation: statement.op, retryable: false });
|
|
1182
1254
|
}
|
|
1183
|
-
|
|
1255
|
+
const manifest = this.#schemes.manifestFor(schemeName, ctx.functionalityWorkerId);
|
|
1256
|
+
const handler = this.#schemes.get(schemeName, ctx.functionalityWorkerId);
|
|
1257
|
+
if (handler === undefined) {
|
|
1258
|
+
return _a.#failure("scheme-not-found", 501, `Scheme '${schemeName}' is not registered.`, {}, { scheme: schemeName, retryable: false });
|
|
1259
|
+
}
|
|
1260
|
+
const methodName = statement.op.toLowerCase();
|
|
1261
|
+
const method = handler[methodName];
|
|
1262
|
+
const addressedScheme = statement.target?.kind === "url" ? statement.target.scheme : null;
|
|
1263
|
+
if (manifest === undefined)
|
|
1264
|
+
throw new Error(`scheme '${schemeName}' has no manifest`);
|
|
1265
|
+
const publishedChannel = statement.target?.kind === "url"
|
|
1266
|
+
? statement.target.fragment ?? manifest.defaultChannel
|
|
1267
|
+
: manifest.defaultChannel;
|
|
1268
|
+
const authoredCoordinate = statement.target === null
|
|
1269
|
+
? { authority: "", pathname: "" }
|
|
1270
|
+
: entryCoordinateOf(statement.target, manifest.authority ?? "namespace");
|
|
1271
|
+
// EXEC's authored target belongs to its declared invocation contract;
|
|
1272
|
+
// a resource target is input to the executor, never the output-stream
|
|
1273
|
+
// address owned by the internal exec scheme. {§exec-target-routing}
|
|
1274
|
+
const addressResolution = manifest.category === "data"
|
|
1275
|
+
&& statement.target !== null
|
|
1276
|
+
&& statement.op !== "EXEC"
|
|
1277
|
+
? await this.#resolveDataEntryAddress({
|
|
1278
|
+
target: statement.target,
|
|
1279
|
+
routedScheme: schemeName,
|
|
1280
|
+
handler: handler,
|
|
1281
|
+
manifest,
|
|
1282
|
+
ctx,
|
|
1283
|
+
})
|
|
1284
|
+
: null;
|
|
1285
|
+
if (addressResolution?.result !== null && addressResolution?.result !== undefined) {
|
|
1286
|
+
return addressResolution.result;
|
|
1287
|
+
}
|
|
1288
|
+
const operationAddress = addressResolution?.address ?? null;
|
|
1289
|
+
const schemeCtx = new SchemeCtxImpl(ctx, addressedScheme ?? schemeName, manifest, this.#liveSubscriptions, {
|
|
1290
|
+
authority: operationAddress?.authority ?? authoredCoordinate.authority,
|
|
1291
|
+
ownerId: operationAddress?.ownerId ?? await this.#fixedEntryOwnerId(manifest, ctx),
|
|
1292
|
+
publishedChannel,
|
|
1293
|
+
});
|
|
1294
|
+
if (statement.op === "SEND"
|
|
1295
|
+
&& statement.signal === 499
|
|
1296
|
+
&& statement.target?.kind === "url"
|
|
1297
|
+
&& operationAddress !== null) {
|
|
1184
1298
|
const addressedScheme = statement.target.scheme;
|
|
1185
1299
|
const entry = await this.#db.crud_find_workspace_entry.get({
|
|
1186
1300
|
workspace_id: ctx.workspaceId,
|
|
1187
|
-
owner_id:
|
|
1188
|
-
scheme:
|
|
1189
|
-
|
|
1301
|
+
owner_id: operationAddress.ownerId,
|
|
1302
|
+
scheme: operationAddress.scheme,
|
|
1303
|
+
authority: operationAddress.authority,
|
|
1304
|
+
pathname: operationAddress.pathname,
|
|
1190
1305
|
});
|
|
1191
1306
|
if (entry !== undefined) {
|
|
1192
1307
|
const subscription = await ChannelWrite.findActiveSubscription(this.#db, {
|
|
@@ -1202,20 +1317,6 @@ class Dispatcher {
|
|
|
1202
1317
|
}
|
|
1203
1318
|
}
|
|
1204
1319
|
}
|
|
1205
|
-
const handler = this.#schemes.get(schemeName, ctx.workspaceId);
|
|
1206
|
-
if (handler === undefined) {
|
|
1207
|
-
return _a.#failure("scheme-not-found", 501, `Scheme '${schemeName}' is not registered.`, {}, { scheme: schemeName, retryable: false });
|
|
1208
|
-
}
|
|
1209
|
-
const methodName = statement.op.toLowerCase();
|
|
1210
|
-
const method = handler[methodName];
|
|
1211
|
-
const addressedScheme = statement.target?.kind === "url" ? statement.target.scheme : null;
|
|
1212
|
-
const manifest = this.#schemes.manifestFor(schemeName, ctx.workspaceId);
|
|
1213
|
-
if (manifest === undefined)
|
|
1214
|
-
throw new Error(`scheme '${schemeName}' has no manifest`);
|
|
1215
|
-
const publishedChannel = statement.target?.kind === "url"
|
|
1216
|
-
? statement.target.fragment ?? manifest.defaultChannel
|
|
1217
|
-
: manifest.defaultChannel;
|
|
1218
|
-
const schemeCtx = new SchemeCtxImpl(ctx, addressedScheme ?? schemeName, manifest, this.#liveSubscriptions, { publishedChannel });
|
|
1219
1320
|
if (statement.op === "READ"
|
|
1220
1321
|
&& handler instanceof CoreSchemeAdapterBase
|
|
1221
1322
|
&& typeof handler.resolveCoreRepresentation === "function") {
|
|
@@ -1257,8 +1358,8 @@ class Dispatcher {
|
|
|
1257
1358
|
handler: handler,
|
|
1258
1359
|
manifest,
|
|
1259
1360
|
ctx,
|
|
1260
|
-
schemeCtx,
|
|
1261
1361
|
publishedChannel,
|
|
1362
|
+
...(addressResolution === null ? {} : { resolved: addressResolution }),
|
|
1262
1363
|
});
|
|
1263
1364
|
if (prepared.result !== null)
|
|
1264
1365
|
return prepared.result;
|
|
@@ -1271,9 +1372,10 @@ class Dispatcher {
|
|
|
1271
1372
|
}
|
|
1272
1373
|
const storageScheme = resolved?.scheme ?? manifest.storedScheme ?? addressedScheme ?? schemeName;
|
|
1273
1374
|
const projected = Results.assertReadResult(await EntryOps.readWorkspaceEntry(statement, ctx, { ...manifest, name: storageScheme, storedScheme: storageScheme }, resolved === null
|
|
1274
|
-
?
|
|
1375
|
+
? null
|
|
1275
1376
|
: {
|
|
1276
1377
|
ownerId: resolved.ownerId,
|
|
1378
|
+
authority: resolved.authority,
|
|
1277
1379
|
pathname: resolved.pathname,
|
|
1278
1380
|
}));
|
|
1279
1381
|
return projected;
|
|
@@ -1295,7 +1397,7 @@ class Dispatcher {
|
|
|
1295
1397
|
&& (targetPathname === "" || targetPathname.endsWith("/"));
|
|
1296
1398
|
const exactTarget = statement.target !== null
|
|
1297
1399
|
&& !collectionTarget
|
|
1298
|
-
&& !PathSyntax.hasGlob(
|
|
1400
|
+
&& !PathSyntax.hasGlob(authoredCoordinate.pathname);
|
|
1299
1401
|
if (exactTarget && statement.target !== null) {
|
|
1300
1402
|
const prepared = await this.#prepareDataRepresentation({
|
|
1301
1403
|
target: statement.target,
|
|
@@ -1303,8 +1405,8 @@ class Dispatcher {
|
|
|
1303
1405
|
handler: handler,
|
|
1304
1406
|
manifest,
|
|
1305
1407
|
ctx,
|
|
1306
|
-
schemeCtx,
|
|
1307
1408
|
publishedChannel,
|
|
1409
|
+
...(addressResolution === null ? {} : { resolved: addressResolution }),
|
|
1308
1410
|
});
|
|
1309
1411
|
if (prepared.result !== null)
|
|
1310
1412
|
return prepared.result;
|
|
@@ -1328,6 +1430,7 @@ class Dispatcher {
|
|
|
1328
1430
|
?? schemeName;
|
|
1329
1431
|
return Results.assert(await EntryFind.findWorkspaceEntries(statement, ctx, { ...manifest, name: storageScheme, storedScheme: storageScheme }, {
|
|
1330
1432
|
ownerId: prepared.address.ownerId,
|
|
1433
|
+
authority: prepared.address.authority,
|
|
1331
1434
|
pathname: prepared.address.pathname,
|
|
1332
1435
|
}));
|
|
1333
1436
|
}
|
|
@@ -1359,7 +1462,7 @@ class Dispatcher {
|
|
|
1359
1462
|
if (handler === undefined || manifest === undefined) {
|
|
1360
1463
|
throw new Error("the core log curation owner is not registered");
|
|
1361
1464
|
}
|
|
1362
|
-
const schemeCtx = new SchemeCtxImpl(ctx, "log", manifest, this.#liveSubscriptions);
|
|
1465
|
+
const schemeCtx = new SchemeCtxImpl(ctx, "log", manifest, this.#liveSubscriptions, { ownerId: null });
|
|
1363
1466
|
const outcome = await handler.curate(statement, schemeCtx);
|
|
1364
1467
|
return { result: Results.assert(outcome.result), plan: outcome.plan };
|
|
1365
1468
|
}
|
|
@@ -1370,9 +1473,9 @@ class Dispatcher {
|
|
|
1370
1473
|
return false;
|
|
1371
1474
|
return !(statement.op === "SEND" && statement.target === null);
|
|
1372
1475
|
}
|
|
1373
|
-
async #writeLog({ statement, result, workspaceId, workerId, loopId, turnId, sequence, origin, curationPlan, modelCallId, }) {
|
|
1476
|
+
async #writeLog({ statement, result, workspaceId, workerId, functionalityWorkerId, loopId, turnId, sequence, origin, curationPlan, modelCallId, }) {
|
|
1374
1477
|
const durableStatement = DurableStatement.project(statement);
|
|
1375
|
-
const target = this.#extractTarget(durableStatement.target,
|
|
1478
|
+
const target = this.#extractTarget(durableStatement.target, functionalityWorkerId);
|
|
1376
1479
|
await this.#canonColumns(target, workspaceId); // {§fs-answer-in-canon}
|
|
1377
1480
|
const lineMarkerJson = "lineMarker" in durableStatement && durableStatement.lineMarker !== null
|
|
1378
1481
|
? JSON.stringify(durableStatement.lineMarker)
|
|
@@ -1484,23 +1587,26 @@ class Dispatcher {
|
|
|
1484
1587
|
// inputs collapse to scheme=null in log target metadata because both render
|
|
1485
1588
|
// as bare paths. Addressable file entries separately persist under the
|
|
1486
1589
|
// reserved `file` identity scheme ({§entry-identity-no-null}).
|
|
1487
|
-
#extractTarget(path,
|
|
1590
|
+
#extractTarget(path, workerId) {
|
|
1488
1591
|
if (path === null)
|
|
1489
1592
|
return { scheme: null, username: null, password: null, hostname: null, port: null, pathname: null, query: null, fragment: null };
|
|
1490
1593
|
// `local` (bare path) carries no URL parts — store the raw text as the pathname for the log record, scheme=null.
|
|
1491
1594
|
if (path.kind === "local")
|
|
1492
1595
|
return { scheme: null, username: null, password: null, hostname: null, port: null, pathname: PathSyntax.decodeParens(path.raw), query: null, fragment: null }; // {§path-parentheses}
|
|
1493
1596
|
const scheme = path.scheme === "file" ? null : path.scheme;
|
|
1494
|
-
//
|
|
1495
|
-
//
|
|
1496
|
-
//
|
|
1597
|
+
// The registered scheme owns authority disposition. Namespace authority
|
|
1598
|
+
// is path syntax; resource and owner authorities remain explicit in the
|
|
1599
|
+
// durable operation evidence.
|
|
1600
|
+
const routedScheme = schemeNameOf(path);
|
|
1601
|
+
const manifest = routedScheme === null
|
|
1602
|
+
? undefined
|
|
1603
|
+
: this.#schemes.manifestFor(routedScheme, workerId);
|
|
1497
1604
|
const foldNs = scheme !== null
|
|
1498
|
-
&&
|
|
1499
|
-
&&
|
|
1500
|
-
&& this.#schemes.has(scheme, workspaceId);
|
|
1605
|
+
&& manifest !== undefined
|
|
1606
|
+
&& (manifest.authority ?? "namespace") === "namespace";
|
|
1501
1607
|
return {
|
|
1502
1608
|
scheme, username: path.username, password: path.password,
|
|
1503
|
-
hostname: foldNs ? null : path.hostname, port: path.port,
|
|
1609
|
+
hostname: foldNs ? null : path.hostname, port: foldNs ? null : path.port,
|
|
1504
1610
|
pathname: PathSyntax.decodeParens(foldNs ? foldAuthorityIntoPath(path.hostname, path.pathname) : path.pathname), // {§path-parentheses}
|
|
1505
1611
|
query: path.query, fragment: path.fragment,
|
|
1506
1612
|
};
|