@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.
Files changed (263) hide show
  1. package/.env.defaults +20 -0
  2. package/INSTALL.md +14 -16
  3. package/README.md +1 -1
  4. package/SPEC.md +635 -296
  5. package/digest-sql/channel-dispositions/dispositions.sql +2 -2
  6. package/digest-sql/entry-dispositions/dispositions.sql +2 -2
  7. package/dist/Paths.d.ts +1 -2
  8. package/dist/Paths.d.ts.map +1 -1
  9. package/dist/Paths.js +1 -9
  10. package/dist/Paths.js.map +1 -1
  11. package/dist/build-info.json +1 -1
  12. package/dist/core/ChannelWrite.d.ts +8 -3
  13. package/dist/core/ChannelWrite.d.ts.map +1 -1
  14. package/dist/core/ChannelWrite.js +40 -8
  15. package/dist/core/ChannelWrite.js.map +1 -1
  16. package/dist/core/ChannelWrite.sql +17 -4
  17. package/dist/core/CoreSchemeServices.d.ts +7 -3
  18. package/dist/core/CoreSchemeServices.d.ts.map +1 -1
  19. package/dist/core/CoreSchemeServices.js +8 -1
  20. package/dist/core/CoreSchemeServices.js.map +1 -1
  21. package/dist/core/Dispatcher.d.ts +9 -1
  22. package/dist/core/Dispatcher.d.ts.map +1 -1
  23. package/dist/core/Dispatcher.js +321 -221
  24. package/dist/core/Dispatcher.js.map +1 -1
  25. package/dist/core/Engine.d.ts +13 -4
  26. package/dist/core/Engine.d.ts.map +1 -1
  27. package/dist/core/Engine.js +44 -22
  28. package/dist/core/Engine.js.map +1 -1
  29. package/dist/core/Engine.sql +101 -81
  30. package/dist/core/EntryAddressBinding.d.ts +32 -0
  31. package/dist/core/EntryAddressBinding.d.ts.map +1 -0
  32. package/dist/core/EntryAddressBinding.js +93 -0
  33. package/dist/core/EntryAddressBinding.js.map +1 -0
  34. package/dist/core/ExecutorRegistry.d.ts +4 -4
  35. package/dist/core/ExecutorRegistry.d.ts.map +1 -1
  36. package/dist/core/ExecutorRegistry.js +28 -28
  37. package/dist/core/ExecutorRegistry.js.map +1 -1
  38. package/dist/core/LogBody.d.ts.map +1 -1
  39. package/dist/core/LogBody.js +19 -1
  40. package/dist/core/LogBody.js.map +1 -1
  41. package/dist/core/OperatorConfig.d.ts.map +1 -1
  42. package/dist/core/OperatorConfig.js +5 -2
  43. package/dist/core/OperatorConfig.js.map +1 -1
  44. package/dist/core/OverflowTurn.d.ts.map +1 -1
  45. package/dist/core/OverflowTurn.js +5 -1
  46. package/dist/core/OverflowTurn.js.map +1 -1
  47. package/dist/core/Owner.js +1 -1
  48. package/dist/core/Owner.js.map +1 -1
  49. package/dist/core/PacketBuilder.d.ts +5 -1
  50. package/dist/core/PacketBuilder.d.ts.map +1 -1
  51. package/dist/core/PacketBuilder.js +21 -11
  52. package/dist/core/PacketBuilder.js.map +1 -1
  53. package/dist/core/ProposalLifecycle.d.ts +4 -1
  54. package/dist/core/ProposalLifecycle.d.ts.map +1 -1
  55. package/dist/core/ProposalLifecycle.js +85 -26
  56. package/dist/core/ProposalLifecycle.js.map +1 -1
  57. package/dist/core/ReasoningEvent.d.ts +13 -0
  58. package/dist/core/ReasoningEvent.d.ts.map +1 -0
  59. package/dist/core/ReasoningEvent.js +6 -0
  60. package/dist/core/ReasoningEvent.js.map +1 -0
  61. package/dist/core/ResourceMutations.d.ts +15 -19
  62. package/dist/core/ResourceMutations.d.ts.map +1 -1
  63. package/dist/core/ResourceMutations.js +113 -38
  64. package/dist/core/ResourceMutations.js.map +1 -1
  65. package/dist/core/SchemeRegistry.d.ts +12 -11
  66. package/dist/core/SchemeRegistry.d.ts.map +1 -1
  67. package/dist/core/SchemeRegistry.js +52 -47
  68. package/dist/core/SchemeRegistry.js.map +1 -1
  69. package/dist/core/ToolResources.d.ts.map +1 -1
  70. package/dist/core/ToolResources.js +20 -19
  71. package/dist/core/ToolResources.js.map +1 -1
  72. package/dist/core/Turn.d.ts +1 -1
  73. package/dist/core/Turn.d.ts.map +1 -1
  74. package/dist/core/TurnOps.d.ts +2 -2
  75. package/dist/core/TurnOps.d.ts.map +1 -1
  76. package/dist/core/TurnOps.js +14 -4
  77. package/dist/core/TurnOps.js.map +1 -1
  78. package/dist/core/TurnRunner.d.ts +46 -3
  79. package/dist/core/TurnRunner.d.ts.map +1 -1
  80. package/dist/core/TurnRunner.js +165 -78
  81. package/dist/core/TurnRunner.js.map +1 -1
  82. package/dist/core/WorkerName.d.ts +1 -0
  83. package/dist/core/WorkerName.d.ts.map +1 -1
  84. package/dist/core/WorkerName.js +2 -1
  85. package/dist/core/WorkerName.js.map +1 -1
  86. package/dist/core/WorkerName.sql +17 -2
  87. package/dist/core/WorkspaceGate.js +1 -1
  88. package/dist/core/WorkspaceGate.js.map +1 -1
  89. package/dist/core/caps/CapsResolve.d.ts +2 -2
  90. package/dist/core/caps/CapsResolve.d.ts.map +1 -1
  91. package/dist/core/caps/CapsResolve.js +5 -7
  92. package/dist/core/caps/CapsResolve.js.map +1 -1
  93. package/dist/core/caps/DbChannelCaps.d.ts +1 -1
  94. package/dist/core/caps/DbChannelCaps.d.ts.map +1 -1
  95. package/dist/core/caps/DbChannelCaps.js +13 -11
  96. package/dist/core/caps/DbChannelCaps.js.map +1 -1
  97. package/dist/core/caps/DbEntryCaps.d.ts +5 -5
  98. package/dist/core/caps/DbEntryCaps.d.ts.map +1 -1
  99. package/dist/core/caps/DbEntryCaps.js +27 -30
  100. package/dist/core/caps/DbEntryCaps.js.map +1 -1
  101. package/dist/core/caps/DbNotifyCaps.d.ts +1 -1
  102. package/dist/core/caps/DbNotifyCaps.d.ts.map +1 -1
  103. package/dist/core/caps/DbNotifyCaps.js +5 -3
  104. package/dist/core/caps/DbNotifyCaps.js.map +1 -1
  105. package/dist/core/caps/DbSubscriptionCaps.d.ts +3 -3
  106. package/dist/core/caps/DbSubscriptionCaps.d.ts.map +1 -1
  107. package/dist/core/caps/DbSubscriptionCaps.js +16 -26
  108. package/dist/core/caps/DbSubscriptionCaps.js.map +1 -1
  109. package/dist/core/caps/SchemeCtxImpl.d.ts +6 -4
  110. package/dist/core/caps/SchemeCtxImpl.d.ts.map +1 -1
  111. package/dist/core/caps/SchemeCtxImpl.js +37 -5
  112. package/dist/core/caps/SchemeCtxImpl.js.map +1 -1
  113. package/dist/core/file-creation-policy.d.ts +9 -0
  114. package/dist/core/file-creation-policy.d.ts.map +1 -0
  115. package/dist/core/file-creation-policy.js +25 -0
  116. package/dist/core/file-creation-policy.js.map +1 -0
  117. package/dist/core/fork.d.ts +4 -1
  118. package/dist/core/fork.d.ts.map +1 -1
  119. package/dist/core/fork.js +27 -15
  120. package/dist/core/fork.js.map +1 -1
  121. package/dist/core/fork.sql +35 -22
  122. package/dist/core/git-membership.d.ts +19 -1
  123. package/dist/core/git-membership.d.ts.map +1 -1
  124. package/dist/core/git-membership.js +225 -50
  125. package/dist/core/git-membership.js.map +1 -1
  126. package/dist/core/packet-wire.d.ts.map +1 -1
  127. package/dist/core/packet-wire.js +28 -19
  128. package/dist/core/packet-wire.js.map +1 -1
  129. package/dist/core/plurnk-uri.d.ts +13 -2
  130. package/dist/core/plurnk-uri.d.ts.map +1 -1
  131. package/dist/core/plurnk-uri.js +59 -21
  132. package/dist/core/plurnk-uri.js.map +1 -1
  133. package/dist/core/scheme-types.d.ts +1 -0
  134. package/dist/core/scheme-types.d.ts.map +1 -1
  135. package/dist/core/worker-ops.sql +10 -6
  136. package/dist/core/worker-settings.d.ts +1 -0
  137. package/dist/core/worker-settings.d.ts.map +1 -1
  138. package/dist/core/worker-settings.js +7 -0
  139. package/dist/core/worker-settings.js.map +1 -1
  140. package/dist/core/workspace-settings.d.ts +2 -0
  141. package/dist/core/workspace-settings.d.ts.map +1 -1
  142. package/dist/core/workspace-settings.js +5 -1
  143. package/dist/core/workspace-settings.js.map +1 -1
  144. package/dist/digest/Digest.d.ts.map +1 -1
  145. package/dist/digest/Digest.js +1 -1
  146. package/dist/digest/Digest.js.map +1 -1
  147. package/dist/digest/digest.sql +1 -1
  148. package/dist/schemes/Exec.d.ts +3 -2
  149. package/dist/schemes/Exec.d.ts.map +1 -1
  150. package/dist/schemes/Exec.js +55 -22
  151. package/dist/schemes/Exec.js.map +1 -1
  152. package/dist/schemes/ExecOutputScheme.d.ts.map +1 -1
  153. package/dist/schemes/ExecOutputScheme.js +5 -4
  154. package/dist/schemes/ExecOutputScheme.js.map +1 -1
  155. package/dist/schemes/File.d.ts.map +1 -1
  156. package/dist/schemes/File.js +170 -80
  157. package/dist/schemes/File.js.map +1 -1
  158. package/dist/schemes/Prompt.d.ts +2 -3
  159. package/dist/schemes/Prompt.d.ts.map +1 -1
  160. package/dist/schemes/Prompt.js +4 -7
  161. package/dist/schemes/Prompt.js.map +1 -1
  162. package/dist/schemes/Skill.d.ts.map +1 -1
  163. package/dist/schemes/Skill.js +3 -1
  164. package/dist/schemes/Skill.js.map +1 -1
  165. package/dist/schemes/Worker.d.ts.map +1 -1
  166. package/dist/schemes/Worker.js +95 -49
  167. package/dist/schemes/Worker.js.map +1 -1
  168. package/dist/schemes/_entry-crud.d.ts +5 -5
  169. package/dist/schemes/_entry-crud.d.ts.map +1 -1
  170. package/dist/schemes/_entry-crud.js +22 -17
  171. package/dist/schemes/_entry-crud.js.map +1 -1
  172. package/dist/schemes/_entry-crud.sql +58 -23
  173. package/dist/schemes/_entry-find.d.ts +3 -2
  174. package/dist/schemes/_entry-find.d.ts.map +1 -1
  175. package/dist/schemes/_entry-find.js +26 -25
  176. package/dist/schemes/_entry-find.js.map +1 -1
  177. package/dist/schemes/_entry-find.sql +10 -7
  178. package/dist/schemes/_entry-manifest.d.ts +2 -2
  179. package/dist/schemes/_entry-manifest.d.ts.map +1 -1
  180. package/dist/schemes/_entry-manifest.js +6 -5
  181. package/dist/schemes/_entry-manifest.js.map +1 -1
  182. package/dist/schemes/_entry-ops.d.ts +6 -5
  183. package/dist/schemes/_entry-ops.d.ts.map +1 -1
  184. package/dist/schemes/_entry-ops.js +39 -46
  185. package/dist/schemes/_entry-ops.js.map +1 -1
  186. package/dist/schemes/_entry-ops.sql +9 -6
  187. package/dist/schemes/_entry-send.d.ts +2 -2
  188. package/dist/schemes/_entry-send.d.ts.map +1 -1
  189. package/dist/schemes/_entry-send.js +16 -14
  190. package/dist/schemes/_entry-send.js.map +1 -1
  191. package/dist/schemes/_search-index.d.ts.map +1 -1
  192. package/dist/schemes/_search-index.js +2 -0
  193. package/dist/schemes/_search-index.js.map +1 -1
  194. package/dist/server/Daemon.d.ts +42 -10
  195. package/dist/server/Daemon.d.ts.map +1 -1
  196. package/dist/server/Daemon.js +326 -248
  197. package/dist/server/Daemon.js.map +1 -1
  198. package/dist/server/DaemonModule.d.ts +84 -19
  199. package/dist/server/DaemonModule.d.ts.map +1 -1
  200. package/dist/server/DrainSupervisor.d.ts +3 -1
  201. package/dist/server/DrainSupervisor.d.ts.map +1 -1
  202. package/dist/server/DrainSupervisor.js +11 -9
  203. package/dist/server/DrainSupervisor.js.map +1 -1
  204. package/dist/server/Functionality.d.ts +44 -0
  205. package/dist/server/Functionality.d.ts.map +1 -0
  206. package/dist/server/Functionality.js +422 -0
  207. package/dist/server/Functionality.js.map +1 -0
  208. package/dist/server/FunctionalityManager.d.ts +22 -0
  209. package/dist/server/FunctionalityManager.d.ts.map +1 -0
  210. package/dist/server/FunctionalityManager.js +121 -0
  211. package/dist/server/FunctionalityManager.js.map +1 -0
  212. package/dist/server/SkillsFunctionality.d.ts +61 -0
  213. package/dist/server/SkillsFunctionality.d.ts.map +1 -0
  214. package/dist/server/SkillsFunctionality.js +449 -0
  215. package/dist/server/SkillsFunctionality.js.map +1 -0
  216. package/dist/server/WorkerCapabilities.d.ts +21 -0
  217. package/dist/server/WorkerCapabilities.d.ts.map +1 -0
  218. package/dist/server/WorkerCapabilities.js +214 -0
  219. package/dist/server/WorkerCapabilities.js.map +1 -0
  220. package/dist/server/WorkerResidency.d.ts +40 -0
  221. package/dist/server/WorkerResidency.d.ts.map +1 -0
  222. package/dist/server/WorkerResidency.js +283 -0
  223. package/dist/server/WorkerResidency.js.map +1 -0
  224. package/dist/server/client-input.d.ts +1 -0
  225. package/dist/server/client-input.d.ts.map +1 -1
  226. package/dist/server/client-input.js +22 -1
  227. package/dist/server/client-input.js.map +1 -1
  228. package/dist/server/dispatch-as-plurnk.d.ts +2 -2
  229. package/dist/server/dispatch-as-plurnk.d.ts.map +1 -1
  230. package/dist/server/dispatch-as-plurnk.js +89 -25
  231. package/dist/server/dispatch-as-plurnk.js.map +1 -1
  232. package/dist/server/drain.sql +11 -6
  233. package/dist/server/envelope.d.ts +6 -1
  234. package/dist/server/envelope.d.ts.map +1 -1
  235. package/dist/server/envelope.js +8 -2
  236. package/dist/server/envelope.js.map +1 -1
  237. package/dist/server/envelope.sql +11 -3
  238. package/dist/server/lifecycle-recovery.sql +13 -16
  239. package/dist/server/loopDocs.d.ts +2 -1
  240. package/dist/server/loopDocs.d.ts.map +1 -1
  241. package/dist/server/loopDocs.js +67 -51
  242. package/dist/server/loopDocs.js.map +1 -1
  243. package/dist/server/loopDocs.sql +14 -10
  244. package/dist/server/module-discovery.d.ts +2 -2
  245. package/dist/server/module-discovery.d.ts.map +1 -1
  246. package/dist/server/module-discovery.js +27 -2
  247. package/dist/server/module-discovery.js.map +1 -1
  248. package/dist/server/seam-entry-read.sql +9 -7
  249. package/dist/server/seam-log-read.sql +1 -1
  250. package/dist/server/seam-loop.sql +10 -1
  251. package/dist/server/seam-proposal-list.sql +4 -2
  252. package/dist/server/worker-capabilities.sql +19 -0
  253. package/dist/service.d.ts.map +1 -1
  254. package/dist/service.js +11 -1
  255. package/dist/service.js.map +1 -1
  256. package/migrations/001_schema.sql +486 -136
  257. package/package.json +34 -32
  258. package/dist/server/skillDocs.d.ts +0 -9
  259. package/dist/server/skillDocs.d.ts.map +0 -1
  260. package/dist/server/skillDocs.js +0 -212
  261. package/dist/server/skillDocs.js.map +0 -1
  262. package/dist/server/skillDocs.sql +0 -14
  263. package/dist/server/workspace-capabilities.sql +0 -19
@@ -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 { entryPathnameOf, foldAuthorityIntoPath, renderAddress, renderTarget, schemeNameOf } from "./plurnk-uri.js";
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, } from "./CoreSchemeServices.js";
23
- import { InvalidOperationResultError, NetworkAddress, } from "@plurnk/plurnk-schemes";
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
- constructor({ db, schemes, mimetypes, weigh, notices, proposals, interactions, executors, loopSignal, streamEventNotify, wakeWorkerNotify, injectWorker, branchWorker, branchCompletionGate, cancelWorker, cancelDescendants, parkDeadlines, joinTargets, liveSubscriptions }) {
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, workspaceId) => this.#checkWritable(statement, origin, workspaceId),
103
- checkFlagsGate: (statement, loopId, workspaceId) => this.#checkFlagsGate(statement, loopId, workspaceId),
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
- readEntry: (scheme, pathname, ctx) => this.#readEntry(scheme, pathname, ctx),
111
- writeEntry: (scheme, pathname, entry, ctx) => this.#writeEntry(scheme, pathname, entry, ctx),
112
- deleteChannel: (scheme, pathname, channel, ctx) => this.#deleteChannel(scheme, pathname, channel, ctx),
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
- #handlerContext(scheme, ctx) {
120
- const manifest = this.#schemes.manifestFor(scheme, ctx.workspaceId);
121
- return manifest === undefined ? null : new SchemeCtxImpl(ctx, scheme, manifest, this.#liveSubscriptions);
121
+ evictWorkspaceCache(workspaceId) {
122
+ this.#rootCache.delete(workspaceId);
122
123
  }
123
- #entryContext(scheme, ctx) {
124
- const handlerCtx = this.#handlerContext(scheme, ctx);
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
- #coreCrud(scheme, workspaceId) {
128
- const handler = this.#schemes.get(scheme, workspaceId);
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, pathname, ctx) {
134
- const handler = this.#coreCrud(scheme, ctx.workspaceId);
135
- const handlerCtx = this.#entryContext(scheme, ctx);
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(scheme, pathname),
163
+ target: renderAddress(address),
145
164
  retryable: false,
146
165
  });
147
166
  }
148
- const result = Results.assert(await caps.read(pathname, scheme === "prompt" ? "worker" : "commons"));
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, pathname, entry, ctx) {
163
- const handler = this.#coreCrud(scheme, ctx.workspaceId);
164
- const handlerCtx = this.#entryContext(scheme, ctx);
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(scheme, pathname),
193
+ target: renderAddress(address),
174
194
  retryable: false,
175
195
  });
176
196
  }
177
- return Results.assert(await caps.write(pathname, entry, scheme === "prompt" ? "worker" : "commons"));
197
+ return Results.assert(await caps.write(pathname, entry));
178
198
  }
179
- async #deleteEntry(scheme, pathname, ctx) {
180
- const handler = this.#coreCrud(scheme, ctx.workspaceId);
181
- const handlerCtx = this.#entryContext(scheme, ctx);
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(scheme, pathname),
211
+ target: renderAddress(address),
191
212
  retryable: false,
192
213
  });
193
214
  }
194
- return Results.assert(await caps.delete(pathname, scheme === "prompt" ? "worker" : "commons"));
215
+ return Results.assert(await caps.delete(pathname));
195
216
  }
196
- async #deleteChannel(scheme, pathname, channel, ctx) {
197
- const handler = this.#coreCrud(scheme, ctx.workspaceId);
198
- const handlerCtx = this.#entryContext(scheme, ctx);
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(scheme, pathname),
229
+ target: renderAddress(address),
208
230
  channel,
209
231
  retryable: false,
210
232
  });
211
233
  }
212
- return Results.assert(await caps.delete(pathname, scheme === "prompt" ? "worker" : "commons", channel));
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, workspaceId);
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, workspaceId);
292
+ let denial = this.#checkWritable(statement, origin, functionalityWorkerId);
270
293
  if (denial === null)
271
- denial = await this.#checkFlagsGate(statement, loopId, workspaceId);
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, { workspaceId, workerId, loopId, turnId, sequence });
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 === "question")
313
- && !(await WorkerSettingsReader.requestUserInputEnabled(this.#db, schemeCtx.workerId))) {
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
- const scheme = schemeNameOf(statement.target);
334
- console.error(`Scheme '${scheme ?? "unknown"}' ${statement.op} threw outside its operation result contract:`, err);
335
- result = err instanceof OperationFailureError
336
- ? err.result
337
- : _a.#failure("scheme-handler-threw", 500, `The '${scheme ?? "unknown"}' scheme did not produce a ${statement.op} result.`, {}, {
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, workspaceId);
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 schemeCtx = new SchemeCtxImpl(coreCtx, addressedScheme, manifest, this.#liveSubscriptions);
468
- const resolved = await this.#resolveDataEntryAddress({
469
- target,
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 #resolveDataEntryAddress({ target, routedScheme, handler, manifest, ctx, schemeCtx, }) {
491
- const addressedScheme = target.kind === "url" ? target.scheme : routedScheme;
492
- const identityTarget = target.kind === "url"
493
- ? {
494
- ...target,
495
- pathname: PathSyntax.decodeParens(target.pathname),
496
- fragment: null,
497
- }
498
- : { ...target, raw: PathSyntax.decodeParens(target.raw) };
499
- const resolved = handler.resolveEntryAddress === undefined
500
- ? { pathname: entryPathnameOf(identityTarget), owner: "commons" }
501
- : await handler.resolveEntryAddress(identityTarget, schemeCtx);
502
- if (resolved === null)
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, schemeCtx, publishedChannel, }) {
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.workspaceId);
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, workspaceId) {
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, workspaceId);
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, workspaceId);
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, workspaceId);
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, workspaceId);
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, workspaceId);
635
+ return this.#denyIfDisallowed(target, origin, workerId);
647
636
  }
648
- #denyIfDisallowed(schemeName, origin, workspaceId) {
637
+ #denyIfDisallowed(schemeName, origin, workerId) {
649
638
  if (schemeName === null)
650
639
  return null;
651
- const handler = this.#schemes.get(schemeName, workspaceId);
640
+ const handler = this.#schemes.get(schemeName, workerId);
652
641
  if (handler === undefined)
653
642
  return null;
654
- const manifest = this.#schemes.manifestFor(schemeName, workspaceId);
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, workspaceId) {
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, workspaceId);
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, workspaceId))
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, workspaceId)?.invocation.target?.kind;
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({ workspaceId: ctx.workspaceId, workerId: branchWorkerId, prompt, flags, parentLoopId: ctx.loopId });
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({ workspaceId: ctx.workspaceId, workerId: row.id, prompt, flags, parentLoopId: ctx.loopId });
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.workspaceId);
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
- const handlerCtx = this.#handlerContext(schemeName, ctx);
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(entryPathnameOf(path), statement.signal, handlerCtx, schemeName);
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
- const handlerCtx = this.#handlerContext("worker", ctx);
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.workspaceId)) {
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, entryPathnameOf(path), ctx);
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 intended-goals op. An ordinary op: dispatched like any
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
- pending.push("completed stream results that land in the NEXT packet's Log");
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, streamTermination, childTermination] = await Promise.all([
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
- .get({ worker_id: workerId }),
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: streamTermination !== undefined,
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
- // {§send-premature-terminate} — same-turn failures are unobserved
1113
- // pending results and therefore refuse completion.
1114
- const failCount = await this.#unobservedFailureCount(turnId);
1115
- if (failCount > 0)
1116
- return _a.#unobservedFailures(failCount);
1117
- const pending = await this.#pendingSet(workerId, turnId);
1118
- if (pending.length > 0) {
1119
- // A retrieval-only refusal needs no KILL/park remedy menu: the results simply
1120
- // arrive in the next packet. Streams and children retain their remedy steer.
1121
- const retrievalsOnly = pending.every((k) => k.startsWith("this turn's retrieval results"));
1122
- if (retrievalsOnly) {
1123
- 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.", {}, {
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: "Review the results, then use only `# PLAN0` and `## SEND0 [200]` to conclude.",
1198
+ recovery: "Resolve the listed pending work before concluding.",
1127
1199
  retryable: false,
1128
1200
  });
1129
1201
  }
1130
- return _a.#failure("work-remains", 409, `Completion was refused while work remains: ${pending.join("; ")}.`, {}, {
1131
- pending: [...pending],
1132
- stage: "completion",
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
- if (statement.op === "SEND" && statement.signal === 499 && statement.target?.kind === "url") {
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: await Owner.commonsId(this.#db, ctx.workspaceId),
1188
- scheme: addressedScheme,
1189
- pathname: entryPathnameOf(statement.target),
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(entryPathnameOf(statement.target));
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, workspaceId);
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, workspaceId) {
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
- // {§scheme-address-namespace-fold} — a registered non-network scheme folds its
1495
- // namespace authority into the canonical pathname. Network authorities remain hosts;
1496
- // worker:// is the registered exception because its authority selects the owner.
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
- && scheme !== "worker"
1499
- && !NetworkAddress.supports(scheme)
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
  };