@plurnk/plurnk-service 1.8.0 → 1.9.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 +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 +321 -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 +21 -11
- 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 +52 -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/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,7 @@ class Dispatcher {
|
|
|
735
725
|
return operationDenial;
|
|
736
726
|
const requested = typeof statement.signal === "string" ? statement.signal : "";
|
|
737
727
|
const runtime = requested === "" ? "sh" : requested;
|
|
738
|
-
const targetKind = this.#executors()?.entry(runtime,
|
|
728
|
+
const targetKind = this.#executors()?.entry(runtime, workerId)?.invocation.target?.kind;
|
|
739
729
|
if (targetKind !== "resource")
|
|
740
730
|
return null;
|
|
741
731
|
const sourceScheme = schemeNameOf(statement.target);
|
|
@@ -808,17 +798,32 @@ class Dispatcher {
|
|
|
808
798
|
}
|
|
809
799
|
if (statement.op === "FORK") {
|
|
810
800
|
// 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({
|
|
801
|
+
const branchWorkerId = await Fork.fork(this.#db, ctx.workerId, name, (scheme) => this.#schemes.entryInheritanceForStoredScheme(scheme, ctx.workerId));
|
|
802
|
+
await ctx.injectWorker({
|
|
803
|
+
workspaceId: ctx.workspaceId,
|
|
804
|
+
workerId: branchWorkerId,
|
|
805
|
+
sourceWorkerId: ctx.workerId,
|
|
806
|
+
prompt,
|
|
807
|
+
flags,
|
|
808
|
+
parentLoopId: ctx.loopId,
|
|
809
|
+
});
|
|
813
810
|
return { status: 200, body: name };
|
|
814
811
|
}
|
|
815
812
|
// WORK — a fresh worker sister named <name>.
|
|
816
813
|
const row = await this.#db.fork_insert_worker.get({
|
|
817
814
|
workspace_id: ctx.workspaceId, name, parent_worker_id: ctx.workerId, origin: ctx.writer,
|
|
815
|
+
fork_snapshot: 0,
|
|
818
816
|
});
|
|
819
817
|
if (row === undefined)
|
|
820
818
|
throw new Error("worker spawn: worker insert returned no row");
|
|
821
|
-
await ctx.injectWorker({
|
|
819
|
+
await ctx.injectWorker({
|
|
820
|
+
workspaceId: ctx.workspaceId,
|
|
821
|
+
workerId: row.id,
|
|
822
|
+
sourceWorkerId: ctx.workerId,
|
|
823
|
+
prompt,
|
|
824
|
+
flags,
|
|
825
|
+
parentLoopId: ctx.loopId,
|
|
826
|
+
});
|
|
822
827
|
return { status: 200, body: name };
|
|
823
828
|
}
|
|
824
829
|
// KILL is target-polymorphic. Scheme handlers own the optional numeric code's
|
|
@@ -834,20 +839,40 @@ class Dispatcher {
|
|
|
834
839
|
if (schemeName === null) {
|
|
835
840
|
return _a.#failure("kill-target-scheme-required", 400, "KILL target requires a scheme.", {}, { retryable: false });
|
|
836
841
|
}
|
|
842
|
+
const manifest = this.#schemes.manifestFor(schemeName, ctx.functionalityWorkerId);
|
|
843
|
+
const coordinate = entryCoordinateOf(path, manifest?.authority ?? "namespace");
|
|
837
844
|
// Log targets use the same killable dispatch as streams; erasure is their
|
|
838
845
|
// permanent curation operation. {§turn-ops-log-curation}
|
|
839
846
|
// Process-KILL: any scheme whose handler exposes kill() aborts a live stream — the
|
|
840
847
|
// exec handler, registered as "exec" + under every runtime tag (sh/node), so a tag-
|
|
841
848
|
// addressed stream (sh:///l/t/s) routes here, not to deleteEntry. {§exec}
|
|
842
|
-
const killable = this.#schemes.get(schemeName, ctx.
|
|
849
|
+
const killable = this.#schemes.get(schemeName, ctx.functionalityWorkerId);
|
|
843
850
|
if (killable !== undefined && typeof killable.kill === "function") {
|
|
844
851
|
// Pass the model's OWN scheme so a stream-KILL error answers in the runtime tag the
|
|
845
852
|
// model addressed (sh:///…), not the internal `exec` ({§fs-answer-in-canon}).
|
|
846
|
-
|
|
853
|
+
let handlerCtx;
|
|
854
|
+
if (manifest?.category === "data") {
|
|
855
|
+
const resolved = await this.#resolveDataEntryAddress({
|
|
856
|
+
target: path,
|
|
857
|
+
routedScheme: schemeName,
|
|
858
|
+
handler: killable,
|
|
859
|
+
manifest,
|
|
860
|
+
ctx,
|
|
861
|
+
});
|
|
862
|
+
if (resolved.result !== null)
|
|
863
|
+
return resolved.result;
|
|
864
|
+
if (resolved.address === null) {
|
|
865
|
+
return _a.#failure("entry-not-found", 404, `No entry exists at ${renderAddress({ scheme: schemeName, ...coordinate })}.`);
|
|
866
|
+
}
|
|
867
|
+
handlerCtx = this.#boundEntryContext(schemeName, resolved.address, ctx);
|
|
868
|
+
}
|
|
869
|
+
else {
|
|
870
|
+
handlerCtx = await this.#handlerContext(schemeName, ctx, coordinate.authority);
|
|
871
|
+
}
|
|
847
872
|
if (handlerCtx === null) {
|
|
848
873
|
throw new InvalidOperationResultError(`Registered scheme '${schemeName}' has no dispatch context.`);
|
|
849
874
|
}
|
|
850
|
-
return await killable.kill(
|
|
875
|
+
return await killable.kill(coordinate.pathname, statement.signal, handlerCtx, schemeName);
|
|
851
876
|
}
|
|
852
877
|
if (schemeName === "worker") {
|
|
853
878
|
// Entry-path present → KILL a private owner-held entry (delete it), self-only —
|
|
@@ -856,7 +881,22 @@ class Dispatcher {
|
|
|
856
881
|
const entryPath = path.kind === "url" ? (path.pathname ?? "") : "";
|
|
857
882
|
if (entryPath !== "" && entryPath !== "/") {
|
|
858
883
|
const workerHandler = this.#schemes.get("worker");
|
|
859
|
-
|
|
884
|
+
if (manifest?.category !== "data") {
|
|
885
|
+
throw new InvalidOperationResultError("Registered scheme 'worker' is not entry-bearing.");
|
|
886
|
+
}
|
|
887
|
+
const resolved = await this.#resolveDataEntryAddress({
|
|
888
|
+
target: path,
|
|
889
|
+
routedScheme: "worker",
|
|
890
|
+
handler: workerHandler,
|
|
891
|
+
manifest,
|
|
892
|
+
ctx,
|
|
893
|
+
});
|
|
894
|
+
if (resolved.result !== null)
|
|
895
|
+
return resolved.result;
|
|
896
|
+
if (resolved.address === null) {
|
|
897
|
+
return _a.#failure("entry-not-found", 404, "The worker entry does not exist.");
|
|
898
|
+
}
|
|
899
|
+
const handlerCtx = this.#boundEntryContext("worker", resolved.address, ctx);
|
|
860
900
|
if (handlerCtx === null) {
|
|
861
901
|
throw new InvalidOperationResultError("Registered scheme 'worker' has no dispatch context.");
|
|
862
902
|
}
|
|
@@ -883,12 +923,28 @@ class Dispatcher {
|
|
|
883
923
|
await this.#cancelWorker(workerId, "killed via worker:// KILL");
|
|
884
924
|
return { status: 200 };
|
|
885
925
|
}
|
|
886
|
-
if (!this.#schemes.has(schemeName, ctx.
|
|
926
|
+
if (!this.#schemes.has(schemeName, ctx.functionalityWorkerId)) {
|
|
887
927
|
return _a.#failure("scheme-not-found", 501, `Scheme '${schemeName}' is not registered.`, {}, { scheme: schemeName, retryable: false });
|
|
888
928
|
}
|
|
929
|
+
const handler = this.#schemes.get(schemeName, ctx.functionalityWorkerId);
|
|
930
|
+
if (handler === undefined || manifest?.category !== "data") {
|
|
931
|
+
return _a.#failure("entry-operation-unsupported", 400, `KILL requires an entry-bearing target; '${schemeName}' does not provide one.`, {}, { scheme: schemeName, retryable: false });
|
|
932
|
+
}
|
|
933
|
+
const resolved = await this.#resolveDataEntryAddress({
|
|
934
|
+
target: path,
|
|
935
|
+
routedScheme: schemeName,
|
|
936
|
+
handler,
|
|
937
|
+
manifest,
|
|
938
|
+
ctx,
|
|
939
|
+
});
|
|
940
|
+
if (resolved.result !== null)
|
|
941
|
+
return resolved.result;
|
|
942
|
+
if (resolved.address === null) {
|
|
943
|
+
return _a.#failure("entry-not-found", 404, "The KILL target does not exist.");
|
|
944
|
+
}
|
|
889
945
|
// A host-effecting delete (file) returns 202 to PROPOSE — pass its attrs through so the proposal
|
|
890
946
|
// carries the delete target to review (#isProposal fires on 202). Plurnk-internal deletes execute inline.
|
|
891
|
-
return this.#deleteEntry(schemeName,
|
|
947
|
+
return this.#deleteEntry(schemeName, resolved.address, ctx);
|
|
892
948
|
}
|
|
893
949
|
async #writeActionlessEntry({ verbatim, workerId, loopId, turnId, sequence, origin, kind, folded, modelCallId = null, attrs = {} }) {
|
|
894
950
|
const durableAttrs = { ...attrs, kind };
|
|
@@ -942,7 +998,7 @@ class Dispatcher {
|
|
|
942
998
|
},
|
|
943
999
|
});
|
|
944
1000
|
}
|
|
945
|
-
// PLAN — the model's
|
|
1001
|
+
// PLAN — the model's complete current Plan. An ordinary op: dispatched like any
|
|
946
1002
|
// other, logged, and broadcast to the client as a log entry — but a pure no-op for
|
|
947
1003
|
// state (PLAN ∉ MUTATING_OPS); its body serializes into the log row's tx, no effect.
|
|
948
1004
|
#handlePlan(statement) {
|
|
@@ -957,6 +1013,7 @@ class Dispatcher {
|
|
|
957
1013
|
Results.assert(result);
|
|
958
1014
|
const logEntryId = await this.#writeLog({
|
|
959
1015
|
...context,
|
|
1016
|
+
functionalityWorkerId: context.functionalityWorkerId ?? context.workerId,
|
|
960
1017
|
result,
|
|
961
1018
|
curationPlan: null,
|
|
962
1019
|
modelCallId,
|
|
@@ -983,8 +1040,10 @@ class Dispatcher {
|
|
|
983
1040
|
const boundaries = await this.#nextPacketBoundaries(workerId, turnId);
|
|
984
1041
|
if (boundaries.retrievals)
|
|
985
1042
|
pending.push("this turn's retrieval results (they land in the NEXT packet's Log)");
|
|
986
|
-
if (boundaries.streamTerminations) {
|
|
987
|
-
|
|
1043
|
+
if (boundaries.streamTerminations.length > 0) {
|
|
1044
|
+
// Name the exact streams (bench#5 requiem #9): the model can wait on
|
|
1045
|
+
// or KILL them specifically instead of guessing what remains.
|
|
1046
|
+
pending.push(`completed stream results that land in the NEXT packet's Log: ${boundaries.streamTerminations.join("; ")}`);
|
|
988
1047
|
}
|
|
989
1048
|
if (boundaries.childTerminations)
|
|
990
1049
|
pending.push("worker results that arrived during this turn (they land NEXT turn)");
|
|
@@ -996,17 +1055,17 @@ class Dispatcher {
|
|
|
996
1055
|
// their distinct contracts: retrievals block explicit completion, whereas FOLD only
|
|
997
1056
|
// prevents an empty wait from being inferred as completion.
|
|
998
1057
|
async #nextPacketBoundaries(workerId, turnId) {
|
|
999
|
-
const [turnBoundaries,
|
|
1058
|
+
const [turnBoundaries, streamTerminations, childTermination] = await Promise.all([
|
|
1000
1059
|
this.#db.engine_turn_packet_boundaries.all({ turn_id: turnId }),
|
|
1001
1060
|
this.#db.engine_worker_has_undelivered_stream_term
|
|
1002
|
-
.
|
|
1061
|
+
.all({ worker_id: workerId }),
|
|
1003
1062
|
this.#db.engine_worker_has_undelivered_child_term
|
|
1004
1063
|
.get({ worker_id: workerId }),
|
|
1005
1064
|
]);
|
|
1006
1065
|
return {
|
|
1007
1066
|
retrievals: turnBoundaries.some(({ op }) => op !== "FOLD"),
|
|
1008
1067
|
folds: turnBoundaries.some(({ op }) => op === "FOLD"),
|
|
1009
|
-
streamTerminations:
|
|
1068
|
+
streamTerminations: streamTerminations.map(({ handle }) => handle),
|
|
1010
1069
|
childTerminations: childTermination !== undefined,
|
|
1011
1070
|
};
|
|
1012
1071
|
}
|
|
@@ -1088,7 +1147,7 @@ class Dispatcher {
|
|
|
1088
1147
|
// fired, so do not park; continue directly to the packet that
|
|
1089
1148
|
// materializes them.
|
|
1090
1149
|
const boundaries = await this.#nextPacketBoundaries(workerId, turnId);
|
|
1091
|
-
if (boundaries.retrievals || boundaries.folds || boundaries.streamTerminations || boundaries.childTerminations) {
|
|
1150
|
+
if (boundaries.retrievals || boundaries.folds || boundaries.streamTerminations.length > 0 || boundaries.childTerminations) {
|
|
1092
1151
|
return { status: 102 };
|
|
1093
1152
|
}
|
|
1094
1153
|
const failCount = await this.#unobservedFailureCount(turnId);
|
|
@@ -1109,34 +1168,41 @@ class Dispatcher {
|
|
|
1109
1168
|
// attempt faithfully (status_rx=409, never erased); the loop stays a continue; the strike
|
|
1110
1169
|
// couples in runTurn. [499] abandons and cancels the descendant scope.
|
|
1111
1170
|
if (status === 200) {
|
|
1112
|
-
//
|
|
1113
|
-
//
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
//
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1171
|
+
// Model completion is a claim about the Worker's observed work and
|
|
1172
|
+
// therefore crosses the pending-result rails. A `_plurnk`
|
|
1173
|
+
// maintenance program closes only its own administrative loop; it
|
|
1174
|
+
// must not claim, consume, or be blocked by model work elsewhere in
|
|
1175
|
+
// the same Worker.
|
|
1176
|
+
if (ctx.origin === "model") {
|
|
1177
|
+
// {§send-premature-terminate} — same-turn failures are unobserved
|
|
1178
|
+
// pending results and therefore refuse completion.
|
|
1179
|
+
const failCount = await this.#unobservedFailureCount(turnId);
|
|
1180
|
+
if (failCount > 0)
|
|
1181
|
+
return _a.#unobservedFailures(failCount);
|
|
1182
|
+
const pending = await this.#pendingSet(workerId, turnId);
|
|
1183
|
+
if (pending.length > 0) {
|
|
1184
|
+
// A retrieval-only refusal needs no KILL/park remedy menu: the results simply
|
|
1185
|
+
// arrive in the next packet. Streams and children retain their remedy steer.
|
|
1186
|
+
const retrievalsOnly = pending.every((k) => k.startsWith("this turn's retrieval results"));
|
|
1187
|
+
if (retrievalsOnly) {
|
|
1188
|
+
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.", {}, {
|
|
1189
|
+
pending: [...pending],
|
|
1190
|
+
stage: "completion",
|
|
1191
|
+
recovery: "Review the results, then use only `# PLAN0` and `## SEND0 [200]` to conclude.",
|
|
1192
|
+
retryable: false,
|
|
1193
|
+
});
|
|
1194
|
+
}
|
|
1195
|
+
return _a.#failure("work-remains", 409, `Completion was refused while work remains: ${pending.join("; ")}.`, {}, {
|
|
1124
1196
|
pending: [...pending],
|
|
1125
1197
|
stage: "completion",
|
|
1126
|
-
recovery: "
|
|
1198
|
+
recovery: "Resolve the listed pending work before concluding.",
|
|
1127
1199
|
retryable: false,
|
|
1128
1200
|
});
|
|
1129
1201
|
}
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
recovery: "Resolve the listed pending work before concluding.",
|
|
1134
|
-
retryable: false,
|
|
1135
|
-
});
|
|
1202
|
+
const branchDenial = await this.#branchCompletionGate?.(workerId) ?? null;
|
|
1203
|
+
if (branchDenial !== null)
|
|
1204
|
+
return branchDenial;
|
|
1136
1205
|
}
|
|
1137
|
-
const branchDenial = await this.#branchCompletionGate?.(workerId) ?? null;
|
|
1138
|
-
if (branchDenial !== null)
|
|
1139
|
-
return branchDenial;
|
|
1140
1206
|
const finished = await this.#lifecycle.finish(loopId, TerminalResult.success(raw));
|
|
1141
1207
|
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
1208
|
}
|
|
@@ -1180,13 +1246,56 @@ class Dispatcher {
|
|
|
1180
1246
|
: {};
|
|
1181
1247
|
return _a.#failure("target-scheme-required", 400, `${statement.op} requires a target scheme.`, fields, { operation: statement.op, retryable: false });
|
|
1182
1248
|
}
|
|
1183
|
-
|
|
1249
|
+
const manifest = this.#schemes.manifestFor(schemeName, ctx.functionalityWorkerId);
|
|
1250
|
+
const handler = this.#schemes.get(schemeName, ctx.functionalityWorkerId);
|
|
1251
|
+
if (handler === undefined) {
|
|
1252
|
+
return _a.#failure("scheme-not-found", 501, `Scheme '${schemeName}' is not registered.`, {}, { scheme: schemeName, retryable: false });
|
|
1253
|
+
}
|
|
1254
|
+
const methodName = statement.op.toLowerCase();
|
|
1255
|
+
const method = handler[methodName];
|
|
1256
|
+
const addressedScheme = statement.target?.kind === "url" ? statement.target.scheme : null;
|
|
1257
|
+
if (manifest === undefined)
|
|
1258
|
+
throw new Error(`scheme '${schemeName}' has no manifest`);
|
|
1259
|
+
const publishedChannel = statement.target?.kind === "url"
|
|
1260
|
+
? statement.target.fragment ?? manifest.defaultChannel
|
|
1261
|
+
: manifest.defaultChannel;
|
|
1262
|
+
const authoredCoordinate = statement.target === null
|
|
1263
|
+
? { authority: "", pathname: "" }
|
|
1264
|
+
: entryCoordinateOf(statement.target, manifest.authority ?? "namespace");
|
|
1265
|
+
// EXEC's authored target belongs to its declared invocation contract;
|
|
1266
|
+
// a resource target is input to the executor, never the output-stream
|
|
1267
|
+
// address owned by the internal exec scheme. {§exec-target-routing}
|
|
1268
|
+
const addressResolution = manifest.category === "data"
|
|
1269
|
+
&& statement.target !== null
|
|
1270
|
+
&& statement.op !== "EXEC"
|
|
1271
|
+
? await this.#resolveDataEntryAddress({
|
|
1272
|
+
target: statement.target,
|
|
1273
|
+
routedScheme: schemeName,
|
|
1274
|
+
handler: handler,
|
|
1275
|
+
manifest,
|
|
1276
|
+
ctx,
|
|
1277
|
+
})
|
|
1278
|
+
: null;
|
|
1279
|
+
if (addressResolution?.result !== null && addressResolution?.result !== undefined) {
|
|
1280
|
+
return addressResolution.result;
|
|
1281
|
+
}
|
|
1282
|
+
const operationAddress = addressResolution?.address ?? null;
|
|
1283
|
+
const schemeCtx = new SchemeCtxImpl(ctx, addressedScheme ?? schemeName, manifest, this.#liveSubscriptions, {
|
|
1284
|
+
authority: operationAddress?.authority ?? authoredCoordinate.authority,
|
|
1285
|
+
ownerId: operationAddress?.ownerId ?? await this.#fixedEntryOwnerId(manifest, ctx),
|
|
1286
|
+
publishedChannel,
|
|
1287
|
+
});
|
|
1288
|
+
if (statement.op === "SEND"
|
|
1289
|
+
&& statement.signal === 499
|
|
1290
|
+
&& statement.target?.kind === "url"
|
|
1291
|
+
&& operationAddress !== null) {
|
|
1184
1292
|
const addressedScheme = statement.target.scheme;
|
|
1185
1293
|
const entry = await this.#db.crud_find_workspace_entry.get({
|
|
1186
1294
|
workspace_id: ctx.workspaceId,
|
|
1187
|
-
owner_id:
|
|
1188
|
-
scheme:
|
|
1189
|
-
|
|
1295
|
+
owner_id: operationAddress.ownerId,
|
|
1296
|
+
scheme: operationAddress.scheme,
|
|
1297
|
+
authority: operationAddress.authority,
|
|
1298
|
+
pathname: operationAddress.pathname,
|
|
1190
1299
|
});
|
|
1191
1300
|
if (entry !== undefined) {
|
|
1192
1301
|
const subscription = await ChannelWrite.findActiveSubscription(this.#db, {
|
|
@@ -1202,20 +1311,6 @@ class Dispatcher {
|
|
|
1202
1311
|
}
|
|
1203
1312
|
}
|
|
1204
1313
|
}
|
|
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
1314
|
if (statement.op === "READ"
|
|
1220
1315
|
&& handler instanceof CoreSchemeAdapterBase
|
|
1221
1316
|
&& typeof handler.resolveCoreRepresentation === "function") {
|
|
@@ -1257,8 +1352,8 @@ class Dispatcher {
|
|
|
1257
1352
|
handler: handler,
|
|
1258
1353
|
manifest,
|
|
1259
1354
|
ctx,
|
|
1260
|
-
schemeCtx,
|
|
1261
1355
|
publishedChannel,
|
|
1356
|
+
...(addressResolution === null ? {} : { resolved: addressResolution }),
|
|
1262
1357
|
});
|
|
1263
1358
|
if (prepared.result !== null)
|
|
1264
1359
|
return prepared.result;
|
|
@@ -1271,9 +1366,10 @@ class Dispatcher {
|
|
|
1271
1366
|
}
|
|
1272
1367
|
const storageScheme = resolved?.scheme ?? manifest.storedScheme ?? addressedScheme ?? schemeName;
|
|
1273
1368
|
const projected = Results.assertReadResult(await EntryOps.readWorkspaceEntry(statement, ctx, { ...manifest, name: storageScheme, storedScheme: storageScheme }, resolved === null
|
|
1274
|
-
?
|
|
1369
|
+
? null
|
|
1275
1370
|
: {
|
|
1276
1371
|
ownerId: resolved.ownerId,
|
|
1372
|
+
authority: resolved.authority,
|
|
1277
1373
|
pathname: resolved.pathname,
|
|
1278
1374
|
}));
|
|
1279
1375
|
return projected;
|
|
@@ -1295,7 +1391,7 @@ class Dispatcher {
|
|
|
1295
1391
|
&& (targetPathname === "" || targetPathname.endsWith("/"));
|
|
1296
1392
|
const exactTarget = statement.target !== null
|
|
1297
1393
|
&& !collectionTarget
|
|
1298
|
-
&& !PathSyntax.hasGlob(
|
|
1394
|
+
&& !PathSyntax.hasGlob(authoredCoordinate.pathname);
|
|
1299
1395
|
if (exactTarget && statement.target !== null) {
|
|
1300
1396
|
const prepared = await this.#prepareDataRepresentation({
|
|
1301
1397
|
target: statement.target,
|
|
@@ -1303,8 +1399,8 @@ class Dispatcher {
|
|
|
1303
1399
|
handler: handler,
|
|
1304
1400
|
manifest,
|
|
1305
1401
|
ctx,
|
|
1306
|
-
schemeCtx,
|
|
1307
1402
|
publishedChannel,
|
|
1403
|
+
...(addressResolution === null ? {} : { resolved: addressResolution }),
|
|
1308
1404
|
});
|
|
1309
1405
|
if (prepared.result !== null)
|
|
1310
1406
|
return prepared.result;
|
|
@@ -1328,6 +1424,7 @@ class Dispatcher {
|
|
|
1328
1424
|
?? schemeName;
|
|
1329
1425
|
return Results.assert(await EntryFind.findWorkspaceEntries(statement, ctx, { ...manifest, name: storageScheme, storedScheme: storageScheme }, {
|
|
1330
1426
|
ownerId: prepared.address.ownerId,
|
|
1427
|
+
authority: prepared.address.authority,
|
|
1331
1428
|
pathname: prepared.address.pathname,
|
|
1332
1429
|
}));
|
|
1333
1430
|
}
|
|
@@ -1359,7 +1456,7 @@ class Dispatcher {
|
|
|
1359
1456
|
if (handler === undefined || manifest === undefined) {
|
|
1360
1457
|
throw new Error("the core log curation owner is not registered");
|
|
1361
1458
|
}
|
|
1362
|
-
const schemeCtx = new SchemeCtxImpl(ctx, "log", manifest, this.#liveSubscriptions);
|
|
1459
|
+
const schemeCtx = new SchemeCtxImpl(ctx, "log", manifest, this.#liveSubscriptions, { ownerId: null });
|
|
1363
1460
|
const outcome = await handler.curate(statement, schemeCtx);
|
|
1364
1461
|
return { result: Results.assert(outcome.result), plan: outcome.plan };
|
|
1365
1462
|
}
|
|
@@ -1370,9 +1467,9 @@ class Dispatcher {
|
|
|
1370
1467
|
return false;
|
|
1371
1468
|
return !(statement.op === "SEND" && statement.target === null);
|
|
1372
1469
|
}
|
|
1373
|
-
async #writeLog({ statement, result, workspaceId, workerId, loopId, turnId, sequence, origin, curationPlan, modelCallId, }) {
|
|
1470
|
+
async #writeLog({ statement, result, workspaceId, workerId, functionalityWorkerId, loopId, turnId, sequence, origin, curationPlan, modelCallId, }) {
|
|
1374
1471
|
const durableStatement = DurableStatement.project(statement);
|
|
1375
|
-
const target = this.#extractTarget(durableStatement.target,
|
|
1472
|
+
const target = this.#extractTarget(durableStatement.target, functionalityWorkerId);
|
|
1376
1473
|
await this.#canonColumns(target, workspaceId); // {§fs-answer-in-canon}
|
|
1377
1474
|
const lineMarkerJson = "lineMarker" in durableStatement && durableStatement.lineMarker !== null
|
|
1378
1475
|
? JSON.stringify(durableStatement.lineMarker)
|
|
@@ -1484,23 +1581,26 @@ class Dispatcher {
|
|
|
1484
1581
|
// inputs collapse to scheme=null in log target metadata because both render
|
|
1485
1582
|
// as bare paths. Addressable file entries separately persist under the
|
|
1486
1583
|
// reserved `file` identity scheme ({§entry-identity-no-null}).
|
|
1487
|
-
#extractTarget(path,
|
|
1584
|
+
#extractTarget(path, workerId) {
|
|
1488
1585
|
if (path === null)
|
|
1489
1586
|
return { scheme: null, username: null, password: null, hostname: null, port: null, pathname: null, query: null, fragment: null };
|
|
1490
1587
|
// `local` (bare path) carries no URL parts — store the raw text as the pathname for the log record, scheme=null.
|
|
1491
1588
|
if (path.kind === "local")
|
|
1492
1589
|
return { scheme: null, username: null, password: null, hostname: null, port: null, pathname: PathSyntax.decodeParens(path.raw), query: null, fragment: null }; // {§path-parentheses}
|
|
1493
1590
|
const scheme = path.scheme === "file" ? null : path.scheme;
|
|
1494
|
-
//
|
|
1495
|
-
//
|
|
1496
|
-
//
|
|
1591
|
+
// The registered scheme owns authority disposition. Namespace authority
|
|
1592
|
+
// is path syntax; resource and owner authorities remain explicit in the
|
|
1593
|
+
// durable operation evidence.
|
|
1594
|
+
const routedScheme = schemeNameOf(path);
|
|
1595
|
+
const manifest = routedScheme === null
|
|
1596
|
+
? undefined
|
|
1597
|
+
: this.#schemes.manifestFor(routedScheme, workerId);
|
|
1497
1598
|
const foldNs = scheme !== null
|
|
1498
|
-
&&
|
|
1499
|
-
&&
|
|
1500
|
-
&& this.#schemes.has(scheme, workspaceId);
|
|
1599
|
+
&& manifest !== undefined
|
|
1600
|
+
&& (manifest.authority ?? "namespace") === "namespace";
|
|
1501
1601
|
return {
|
|
1502
1602
|
scheme, username: path.username, password: path.password,
|
|
1503
|
-
hostname: foldNs ? null : path.hostname, port: path.port,
|
|
1603
|
+
hostname: foldNs ? null : path.hostname, port: foldNs ? null : path.port,
|
|
1504
1604
|
pathname: PathSyntax.decodeParens(foldNs ? foldAuthorityIntoPath(path.hostname, path.pathname) : path.pathname), // {§path-parentheses}
|
|
1505
1605
|
query: path.query, fragment: path.fragment,
|
|
1506
1606
|
};
|