@plurnk/plurnk-service 1.8.0 → 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (267) 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 +327 -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 +24 -12
  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 +59 -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/QuestionTool.d.ts +2 -0
  163. package/dist/schemes/QuestionTool.d.ts.map +1 -1
  164. package/dist/schemes/QuestionTool.js +5 -0
  165. package/dist/schemes/QuestionTool.js.map +1 -1
  166. package/dist/schemes/Skill.d.ts.map +1 -1
  167. package/dist/schemes/Skill.js +3 -1
  168. package/dist/schemes/Skill.js.map +1 -1
  169. package/dist/schemes/Worker.d.ts.map +1 -1
  170. package/dist/schemes/Worker.js +95 -49
  171. package/dist/schemes/Worker.js.map +1 -1
  172. package/dist/schemes/_entry-crud.d.ts +5 -5
  173. package/dist/schemes/_entry-crud.d.ts.map +1 -1
  174. package/dist/schemes/_entry-crud.js +22 -17
  175. package/dist/schemes/_entry-crud.js.map +1 -1
  176. package/dist/schemes/_entry-crud.sql +58 -23
  177. package/dist/schemes/_entry-find.d.ts +3 -2
  178. package/dist/schemes/_entry-find.d.ts.map +1 -1
  179. package/dist/schemes/_entry-find.js +26 -25
  180. package/dist/schemes/_entry-find.js.map +1 -1
  181. package/dist/schemes/_entry-find.sql +10 -7
  182. package/dist/schemes/_entry-manifest.d.ts +2 -2
  183. package/dist/schemes/_entry-manifest.d.ts.map +1 -1
  184. package/dist/schemes/_entry-manifest.js +6 -5
  185. package/dist/schemes/_entry-manifest.js.map +1 -1
  186. package/dist/schemes/_entry-ops.d.ts +6 -5
  187. package/dist/schemes/_entry-ops.d.ts.map +1 -1
  188. package/dist/schemes/_entry-ops.js +39 -46
  189. package/dist/schemes/_entry-ops.js.map +1 -1
  190. package/dist/schemes/_entry-ops.sql +9 -6
  191. package/dist/schemes/_entry-send.d.ts +2 -2
  192. package/dist/schemes/_entry-send.d.ts.map +1 -1
  193. package/dist/schemes/_entry-send.js +16 -14
  194. package/dist/schemes/_entry-send.js.map +1 -1
  195. package/dist/schemes/_search-index.d.ts.map +1 -1
  196. package/dist/schemes/_search-index.js +2 -0
  197. package/dist/schemes/_search-index.js.map +1 -1
  198. package/dist/server/Daemon.d.ts +42 -10
  199. package/dist/server/Daemon.d.ts.map +1 -1
  200. package/dist/server/Daemon.js +326 -248
  201. package/dist/server/Daemon.js.map +1 -1
  202. package/dist/server/DaemonModule.d.ts +84 -19
  203. package/dist/server/DaemonModule.d.ts.map +1 -1
  204. package/dist/server/DrainSupervisor.d.ts +3 -1
  205. package/dist/server/DrainSupervisor.d.ts.map +1 -1
  206. package/dist/server/DrainSupervisor.js +11 -9
  207. package/dist/server/DrainSupervisor.js.map +1 -1
  208. package/dist/server/Functionality.d.ts +44 -0
  209. package/dist/server/Functionality.d.ts.map +1 -0
  210. package/dist/server/Functionality.js +422 -0
  211. package/dist/server/Functionality.js.map +1 -0
  212. package/dist/server/FunctionalityManager.d.ts +22 -0
  213. package/dist/server/FunctionalityManager.d.ts.map +1 -0
  214. package/dist/server/FunctionalityManager.js +121 -0
  215. package/dist/server/FunctionalityManager.js.map +1 -0
  216. package/dist/server/SkillsFunctionality.d.ts +61 -0
  217. package/dist/server/SkillsFunctionality.d.ts.map +1 -0
  218. package/dist/server/SkillsFunctionality.js +449 -0
  219. package/dist/server/SkillsFunctionality.js.map +1 -0
  220. package/dist/server/WorkerCapabilities.d.ts +21 -0
  221. package/dist/server/WorkerCapabilities.d.ts.map +1 -0
  222. package/dist/server/WorkerCapabilities.js +214 -0
  223. package/dist/server/WorkerCapabilities.js.map +1 -0
  224. package/dist/server/WorkerResidency.d.ts +40 -0
  225. package/dist/server/WorkerResidency.d.ts.map +1 -0
  226. package/dist/server/WorkerResidency.js +283 -0
  227. package/dist/server/WorkerResidency.js.map +1 -0
  228. package/dist/server/client-input.d.ts +1 -0
  229. package/dist/server/client-input.d.ts.map +1 -1
  230. package/dist/server/client-input.js +22 -1
  231. package/dist/server/client-input.js.map +1 -1
  232. package/dist/server/dispatch-as-plurnk.d.ts +2 -2
  233. package/dist/server/dispatch-as-plurnk.d.ts.map +1 -1
  234. package/dist/server/dispatch-as-plurnk.js +89 -25
  235. package/dist/server/dispatch-as-plurnk.js.map +1 -1
  236. package/dist/server/drain.sql +11 -6
  237. package/dist/server/envelope.d.ts +6 -1
  238. package/dist/server/envelope.d.ts.map +1 -1
  239. package/dist/server/envelope.js +8 -2
  240. package/dist/server/envelope.js.map +1 -1
  241. package/dist/server/envelope.sql +11 -3
  242. package/dist/server/lifecycle-recovery.sql +13 -16
  243. package/dist/server/loopDocs.d.ts +2 -1
  244. package/dist/server/loopDocs.d.ts.map +1 -1
  245. package/dist/server/loopDocs.js +67 -51
  246. package/dist/server/loopDocs.js.map +1 -1
  247. package/dist/server/loopDocs.sql +14 -10
  248. package/dist/server/module-discovery.d.ts +2 -2
  249. package/dist/server/module-discovery.d.ts.map +1 -1
  250. package/dist/server/module-discovery.js +27 -2
  251. package/dist/server/module-discovery.js.map +1 -1
  252. package/dist/server/seam-entry-read.sql +9 -7
  253. package/dist/server/seam-log-read.sql +1 -1
  254. package/dist/server/seam-loop.sql +10 -1
  255. package/dist/server/seam-proposal-list.sql +4 -2
  256. package/dist/server/worker-capabilities.sql +19 -0
  257. package/dist/service.d.ts.map +1 -1
  258. package/dist/service.js +11 -1
  259. package/dist/service.js.map +1 -1
  260. package/migrations/001_schema.sql +486 -136
  261. package/package.json +34 -32
  262. package/dist/server/skillDocs.d.ts +0 -9
  263. package/dist/server/skillDocs.d.ts.map +0 -1
  264. package/dist/server/skillDocs.js +0 -212
  265. package/dist/server/skillDocs.js.map +0 -1
  266. package/dist/server/skillDocs.sql +0 -14
  267. 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,13 @@ class Dispatcher {
735
725
  return operationDenial;
736
726
  const requested = typeof statement.signal === "string" ? statement.signal : "";
737
727
  const runtime = requested === "" ? "sh" : requested;
738
- const targetKind = this.#executors()?.entry(runtime, workspaceId)?.invocation.target?.kind;
728
+ // {§manifest-flag-affinity} — a runtime alias is a registered scheme
729
+ // with its own affinity: the one resolver gates the selected runtime
730
+ // exactly as it gates the exec family (e.g. `question` under noInteraction).
731
+ const runtimeDenial = checkScheme(runtime);
732
+ if (runtimeDenial !== null)
733
+ return runtimeDenial;
734
+ const targetKind = this.#executors()?.entry(runtime, workerId)?.invocation.target?.kind;
739
735
  if (targetKind !== "resource")
740
736
  return null;
741
737
  const sourceScheme = schemeNameOf(statement.target);
@@ -808,17 +804,32 @@ class Dispatcher {
808
804
  }
809
805
  if (statement.op === "FORK") {
810
806
  // Branch the current worker's log into a named sister.
811
- const branchWorkerId = await Fork.fork(this.#db, ctx.workerId, name);
812
- await ctx.injectWorker({ workspaceId: ctx.workspaceId, workerId: branchWorkerId, prompt, flags, parentLoopId: ctx.loopId });
807
+ const branchWorkerId = await Fork.fork(this.#db, ctx.workerId, name, (scheme) => this.#schemes.entryInheritanceForStoredScheme(scheme, ctx.workerId));
808
+ await ctx.injectWorker({
809
+ workspaceId: ctx.workspaceId,
810
+ workerId: branchWorkerId,
811
+ sourceWorkerId: ctx.workerId,
812
+ prompt,
813
+ flags,
814
+ parentLoopId: ctx.loopId,
815
+ });
813
816
  return { status: 200, body: name };
814
817
  }
815
818
  // WORK — a fresh worker sister named <name>.
816
819
  const row = await this.#db.fork_insert_worker.get({
817
820
  workspace_id: ctx.workspaceId, name, parent_worker_id: ctx.workerId, origin: ctx.writer,
821
+ fork_snapshot: 0,
818
822
  });
819
823
  if (row === undefined)
820
824
  throw new Error("worker spawn: worker insert returned no row");
821
- await ctx.injectWorker({ workspaceId: ctx.workspaceId, workerId: row.id, prompt, flags, parentLoopId: ctx.loopId });
825
+ await ctx.injectWorker({
826
+ workspaceId: ctx.workspaceId,
827
+ workerId: row.id,
828
+ sourceWorkerId: ctx.workerId,
829
+ prompt,
830
+ flags,
831
+ parentLoopId: ctx.loopId,
832
+ });
822
833
  return { status: 200, body: name };
823
834
  }
824
835
  // KILL is target-polymorphic. Scheme handlers own the optional numeric code's
@@ -834,20 +845,40 @@ class Dispatcher {
834
845
  if (schemeName === null) {
835
846
  return _a.#failure("kill-target-scheme-required", 400, "KILL target requires a scheme.", {}, { retryable: false });
836
847
  }
848
+ const manifest = this.#schemes.manifestFor(schemeName, ctx.functionalityWorkerId);
849
+ const coordinate = entryCoordinateOf(path, manifest?.authority ?? "namespace");
837
850
  // Log targets use the same killable dispatch as streams; erasure is their
838
851
  // permanent curation operation. {§turn-ops-log-curation}
839
852
  // Process-KILL: any scheme whose handler exposes kill() aborts a live stream — the
840
853
  // exec handler, registered as "exec" + under every runtime tag (sh/node), so a tag-
841
854
  // addressed stream (sh:///l/t/s) routes here, not to deleteEntry. {§exec}
842
- const killable = this.#schemes.get(schemeName, ctx.workspaceId);
855
+ const killable = this.#schemes.get(schemeName, ctx.functionalityWorkerId);
843
856
  if (killable !== undefined && typeof killable.kill === "function") {
844
857
  // Pass the model's OWN scheme so a stream-KILL error answers in the runtime tag the
845
858
  // model addressed (sh:///…), not the internal `exec` ({§fs-answer-in-canon}).
846
- const handlerCtx = this.#handlerContext(schemeName, ctx);
859
+ let handlerCtx;
860
+ if (manifest?.category === "data") {
861
+ const resolved = await this.#resolveDataEntryAddress({
862
+ target: path,
863
+ routedScheme: schemeName,
864
+ handler: killable,
865
+ manifest,
866
+ ctx,
867
+ });
868
+ if (resolved.result !== null)
869
+ return resolved.result;
870
+ if (resolved.address === null) {
871
+ return _a.#failure("entry-not-found", 404, `No entry exists at ${renderAddress({ scheme: schemeName, ...coordinate })}.`);
872
+ }
873
+ handlerCtx = this.#boundEntryContext(schemeName, resolved.address, ctx);
874
+ }
875
+ else {
876
+ handlerCtx = await this.#handlerContext(schemeName, ctx, coordinate.authority);
877
+ }
847
878
  if (handlerCtx === null) {
848
879
  throw new InvalidOperationResultError(`Registered scheme '${schemeName}' has no dispatch context.`);
849
880
  }
850
- return await killable.kill(entryPathnameOf(path), statement.signal, handlerCtx, schemeName);
881
+ return await killable.kill(coordinate.pathname, statement.signal, handlerCtx, schemeName);
851
882
  }
852
883
  if (schemeName === "worker") {
853
884
  // Entry-path present → KILL a private owner-held entry (delete it), self-only —
@@ -856,7 +887,22 @@ class Dispatcher {
856
887
  const entryPath = path.kind === "url" ? (path.pathname ?? "") : "";
857
888
  if (entryPath !== "" && entryPath !== "/") {
858
889
  const workerHandler = this.#schemes.get("worker");
859
- const handlerCtx = this.#handlerContext("worker", ctx);
890
+ if (manifest?.category !== "data") {
891
+ throw new InvalidOperationResultError("Registered scheme 'worker' is not entry-bearing.");
892
+ }
893
+ const resolved = await this.#resolveDataEntryAddress({
894
+ target: path,
895
+ routedScheme: "worker",
896
+ handler: workerHandler,
897
+ manifest,
898
+ ctx,
899
+ });
900
+ if (resolved.result !== null)
901
+ return resolved.result;
902
+ if (resolved.address === null) {
903
+ return _a.#failure("entry-not-found", 404, "The worker entry does not exist.");
904
+ }
905
+ const handlerCtx = this.#boundEntryContext("worker", resolved.address, ctx);
860
906
  if (handlerCtx === null) {
861
907
  throw new InvalidOperationResultError("Registered scheme 'worker' has no dispatch context.");
862
908
  }
@@ -883,12 +929,28 @@ class Dispatcher {
883
929
  await this.#cancelWorker(workerId, "killed via worker:// KILL");
884
930
  return { status: 200 };
885
931
  }
886
- if (!this.#schemes.has(schemeName, ctx.workspaceId)) {
932
+ if (!this.#schemes.has(schemeName, ctx.functionalityWorkerId)) {
887
933
  return _a.#failure("scheme-not-found", 501, `Scheme '${schemeName}' is not registered.`, {}, { scheme: schemeName, retryable: false });
888
934
  }
935
+ const handler = this.#schemes.get(schemeName, ctx.functionalityWorkerId);
936
+ if (handler === undefined || manifest?.category !== "data") {
937
+ return _a.#failure("entry-operation-unsupported", 400, `KILL requires an entry-bearing target; '${schemeName}' does not provide one.`, {}, { scheme: schemeName, retryable: false });
938
+ }
939
+ const resolved = await this.#resolveDataEntryAddress({
940
+ target: path,
941
+ routedScheme: schemeName,
942
+ handler,
943
+ manifest,
944
+ ctx,
945
+ });
946
+ if (resolved.result !== null)
947
+ return resolved.result;
948
+ if (resolved.address === null) {
949
+ return _a.#failure("entry-not-found", 404, "The KILL target does not exist.");
950
+ }
889
951
  // A host-effecting delete (file) returns 202 to PROPOSE — pass its attrs through so the proposal
890
952
  // carries the delete target to review (#isProposal fires on 202). Plurnk-internal deletes execute inline.
891
- return this.#deleteEntry(schemeName, entryPathnameOf(path), ctx);
953
+ return this.#deleteEntry(schemeName, resolved.address, ctx);
892
954
  }
893
955
  async #writeActionlessEntry({ verbatim, workerId, loopId, turnId, sequence, origin, kind, folded, modelCallId = null, attrs = {} }) {
894
956
  const durableAttrs = { ...attrs, kind };
@@ -942,7 +1004,7 @@ class Dispatcher {
942
1004
  },
943
1005
  });
944
1006
  }
945
- // PLAN — the model's intended-goals op. An ordinary op: dispatched like any
1007
+ // PLAN — the model's complete current Plan. An ordinary op: dispatched like any
946
1008
  // other, logged, and broadcast to the client as a log entry — but a pure no-op for
947
1009
  // state (PLAN ∉ MUTATING_OPS); its body serializes into the log row's tx, no effect.
948
1010
  #handlePlan(statement) {
@@ -957,6 +1019,7 @@ class Dispatcher {
957
1019
  Results.assert(result);
958
1020
  const logEntryId = await this.#writeLog({
959
1021
  ...context,
1022
+ functionalityWorkerId: context.functionalityWorkerId ?? context.workerId,
960
1023
  result,
961
1024
  curationPlan: null,
962
1025
  modelCallId,
@@ -983,8 +1046,10 @@ class Dispatcher {
983
1046
  const boundaries = await this.#nextPacketBoundaries(workerId, turnId);
984
1047
  if (boundaries.retrievals)
985
1048
  pending.push("this turn's retrieval results (they land in the NEXT packet's Log)");
986
- if (boundaries.streamTerminations) {
987
- pending.push("completed stream results that land in the NEXT packet's Log");
1049
+ if (boundaries.streamTerminations.length > 0) {
1050
+ // Name the exact streams (bench#5 requiem #9): the model can wait on
1051
+ // or KILL them specifically instead of guessing what remains.
1052
+ pending.push(`completed stream results that land in the NEXT packet's Log: ${boundaries.streamTerminations.join("; ")}`);
988
1053
  }
989
1054
  if (boundaries.childTerminations)
990
1055
  pending.push("worker results that arrived during this turn (they land NEXT turn)");
@@ -996,17 +1061,17 @@ class Dispatcher {
996
1061
  // their distinct contracts: retrievals block explicit completion, whereas FOLD only
997
1062
  // prevents an empty wait from being inferred as completion.
998
1063
  async #nextPacketBoundaries(workerId, turnId) {
999
- const [turnBoundaries, streamTermination, childTermination] = await Promise.all([
1064
+ const [turnBoundaries, streamTerminations, childTermination] = await Promise.all([
1000
1065
  this.#db.engine_turn_packet_boundaries.all({ turn_id: turnId }),
1001
1066
  this.#db.engine_worker_has_undelivered_stream_term
1002
- .get({ worker_id: workerId }),
1067
+ .all({ worker_id: workerId }),
1003
1068
  this.#db.engine_worker_has_undelivered_child_term
1004
1069
  .get({ worker_id: workerId }),
1005
1070
  ]);
1006
1071
  return {
1007
1072
  retrievals: turnBoundaries.some(({ op }) => op !== "FOLD"),
1008
1073
  folds: turnBoundaries.some(({ op }) => op === "FOLD"),
1009
- streamTerminations: streamTermination !== undefined,
1074
+ streamTerminations: streamTerminations.map(({ handle }) => handle),
1010
1075
  childTerminations: childTermination !== undefined,
1011
1076
  };
1012
1077
  }
@@ -1088,7 +1153,7 @@ class Dispatcher {
1088
1153
  // fired, so do not park; continue directly to the packet that
1089
1154
  // materializes them.
1090
1155
  const boundaries = await this.#nextPacketBoundaries(workerId, turnId);
1091
- if (boundaries.retrievals || boundaries.folds || boundaries.streamTerminations || boundaries.childTerminations) {
1156
+ if (boundaries.retrievals || boundaries.folds || boundaries.streamTerminations.length > 0 || boundaries.childTerminations) {
1092
1157
  return { status: 102 };
1093
1158
  }
1094
1159
  const failCount = await this.#unobservedFailureCount(turnId);
@@ -1109,34 +1174,41 @@ class Dispatcher {
1109
1174
  // attempt faithfully (status_rx=409, never erased); the loop stays a continue; the strike
1110
1175
  // couples in runTurn. [499] abandons and cancels the descendant scope.
1111
1176
  if (status === 200) {
1112
- // {§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.", {}, {
1177
+ // Model completion is a claim about the Worker's observed work and
1178
+ // therefore crosses the pending-result rails. A `_plurnk`
1179
+ // maintenance program closes only its own administrative loop; it
1180
+ // must not claim, consume, or be blocked by model work elsewhere in
1181
+ // the same Worker.
1182
+ if (ctx.origin === "model") {
1183
+ // {§send-premature-terminate} — same-turn failures are unobserved
1184
+ // pending results and therefore refuse completion.
1185
+ const failCount = await this.#unobservedFailureCount(turnId);
1186
+ if (failCount > 0)
1187
+ return _a.#unobservedFailures(failCount);
1188
+ const pending = await this.#pendingSet(workerId, turnId);
1189
+ if (pending.length > 0) {
1190
+ // A retrieval-only refusal needs no KILL/park remedy menu: the results simply
1191
+ // arrive in the next packet. Streams and children retain their remedy steer.
1192
+ const retrievalsOnly = pending.every((k) => k.startsWith("this turn's retrieval results"));
1193
+ if (retrievalsOnly) {
1194
+ return _a.#failure("retrieval-results-unobserved", 409, "Last turn both performed retrieval operations and attempted to terminate. Retrieval operations force an additional turn so their results can be reviewed.", {}, {
1195
+ pending: [...pending],
1196
+ stage: "completion",
1197
+ recovery: "Review the results, then use only `# PLAN0` and `## SEND0 [200]` to conclude.",
1198
+ retryable: false,
1199
+ });
1200
+ }
1201
+ return _a.#failure("work-remains", 409, `Completion was refused while work remains: ${pending.join("; ")}.`, {}, {
1124
1202
  pending: [...pending],
1125
1203
  stage: "completion",
1126
- recovery: "Review the results, then use only `# PLAN0` and `## SEND0 [200]` to conclude.",
1204
+ recovery: "Resolve the listed pending work before concluding.",
1127
1205
  retryable: false,
1128
1206
  });
1129
1207
  }
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
- });
1208
+ const branchDenial = await this.#branchCompletionGate?.(workerId) ?? null;
1209
+ if (branchDenial !== null)
1210
+ return branchDenial;
1136
1211
  }
1137
- const branchDenial = await this.#branchCompletionGate?.(workerId) ?? null;
1138
- if (branchDenial !== null)
1139
- return branchDenial;
1140
1212
  const finished = await this.#lifecycle.finish(loopId, TerminalResult.success(raw));
1141
1213
  return _a.#statusResult(finished !== null ? 200 : await this.#lifecycle.status(loopId), "loop-already-terminal", "The loop was already terminal when SEND attempted to conclude it.");
1142
1214
  }
@@ -1180,13 +1252,56 @@ class Dispatcher {
1180
1252
  : {};
1181
1253
  return _a.#failure("target-scheme-required", 400, `${statement.op} requires a target scheme.`, fields, { operation: statement.op, retryable: false });
1182
1254
  }
1183
- if (statement.op === "SEND" && statement.signal === 499 && statement.target?.kind === "url") {
1255
+ const manifest = this.#schemes.manifestFor(schemeName, ctx.functionalityWorkerId);
1256
+ const handler = this.#schemes.get(schemeName, ctx.functionalityWorkerId);
1257
+ if (handler === undefined) {
1258
+ return _a.#failure("scheme-not-found", 501, `Scheme '${schemeName}' is not registered.`, {}, { scheme: schemeName, retryable: false });
1259
+ }
1260
+ const methodName = statement.op.toLowerCase();
1261
+ const method = handler[methodName];
1262
+ const addressedScheme = statement.target?.kind === "url" ? statement.target.scheme : null;
1263
+ if (manifest === undefined)
1264
+ throw new Error(`scheme '${schemeName}' has no manifest`);
1265
+ const publishedChannel = statement.target?.kind === "url"
1266
+ ? statement.target.fragment ?? manifest.defaultChannel
1267
+ : manifest.defaultChannel;
1268
+ const authoredCoordinate = statement.target === null
1269
+ ? { authority: "", pathname: "" }
1270
+ : entryCoordinateOf(statement.target, manifest.authority ?? "namespace");
1271
+ // EXEC's authored target belongs to its declared invocation contract;
1272
+ // a resource target is input to the executor, never the output-stream
1273
+ // address owned by the internal exec scheme. {§exec-target-routing}
1274
+ const addressResolution = manifest.category === "data"
1275
+ && statement.target !== null
1276
+ && statement.op !== "EXEC"
1277
+ ? await this.#resolveDataEntryAddress({
1278
+ target: statement.target,
1279
+ routedScheme: schemeName,
1280
+ handler: handler,
1281
+ manifest,
1282
+ ctx,
1283
+ })
1284
+ : null;
1285
+ if (addressResolution?.result !== null && addressResolution?.result !== undefined) {
1286
+ return addressResolution.result;
1287
+ }
1288
+ const operationAddress = addressResolution?.address ?? null;
1289
+ const schemeCtx = new SchemeCtxImpl(ctx, addressedScheme ?? schemeName, manifest, this.#liveSubscriptions, {
1290
+ authority: operationAddress?.authority ?? authoredCoordinate.authority,
1291
+ ownerId: operationAddress?.ownerId ?? await this.#fixedEntryOwnerId(manifest, ctx),
1292
+ publishedChannel,
1293
+ });
1294
+ if (statement.op === "SEND"
1295
+ && statement.signal === 499
1296
+ && statement.target?.kind === "url"
1297
+ && operationAddress !== null) {
1184
1298
  const addressedScheme = statement.target.scheme;
1185
1299
  const entry = await this.#db.crud_find_workspace_entry.get({
1186
1300
  workspace_id: ctx.workspaceId,
1187
- owner_id: await Owner.commonsId(this.#db, ctx.workspaceId),
1188
- scheme: addressedScheme,
1189
- pathname: entryPathnameOf(statement.target),
1301
+ owner_id: operationAddress.ownerId,
1302
+ scheme: operationAddress.scheme,
1303
+ authority: operationAddress.authority,
1304
+ pathname: operationAddress.pathname,
1190
1305
  });
1191
1306
  if (entry !== undefined) {
1192
1307
  const subscription = await ChannelWrite.findActiveSubscription(this.#db, {
@@ -1202,20 +1317,6 @@ class Dispatcher {
1202
1317
  }
1203
1318
  }
1204
1319
  }
1205
- const handler = this.#schemes.get(schemeName, ctx.workspaceId);
1206
- if (handler === undefined) {
1207
- return _a.#failure("scheme-not-found", 501, `Scheme '${schemeName}' is not registered.`, {}, { scheme: schemeName, retryable: false });
1208
- }
1209
- const methodName = statement.op.toLowerCase();
1210
- const method = handler[methodName];
1211
- const addressedScheme = statement.target?.kind === "url" ? statement.target.scheme : null;
1212
- const manifest = this.#schemes.manifestFor(schemeName, ctx.workspaceId);
1213
- if (manifest === undefined)
1214
- throw new Error(`scheme '${schemeName}' has no manifest`);
1215
- const publishedChannel = statement.target?.kind === "url"
1216
- ? statement.target.fragment ?? manifest.defaultChannel
1217
- : manifest.defaultChannel;
1218
- const schemeCtx = new SchemeCtxImpl(ctx, addressedScheme ?? schemeName, manifest, this.#liveSubscriptions, { publishedChannel });
1219
1320
  if (statement.op === "READ"
1220
1321
  && handler instanceof CoreSchemeAdapterBase
1221
1322
  && typeof handler.resolveCoreRepresentation === "function") {
@@ -1257,8 +1358,8 @@ class Dispatcher {
1257
1358
  handler: handler,
1258
1359
  manifest,
1259
1360
  ctx,
1260
- schemeCtx,
1261
1361
  publishedChannel,
1362
+ ...(addressResolution === null ? {} : { resolved: addressResolution }),
1262
1363
  });
1263
1364
  if (prepared.result !== null)
1264
1365
  return prepared.result;
@@ -1271,9 +1372,10 @@ class Dispatcher {
1271
1372
  }
1272
1373
  const storageScheme = resolved?.scheme ?? manifest.storedScheme ?? addressedScheme ?? schemeName;
1273
1374
  const projected = Results.assertReadResult(await EntryOps.readWorkspaceEntry(statement, ctx, { ...manifest, name: storageScheme, storedScheme: storageScheme }, resolved === null
1274
- ? {}
1375
+ ? null
1275
1376
  : {
1276
1377
  ownerId: resolved.ownerId,
1378
+ authority: resolved.authority,
1277
1379
  pathname: resolved.pathname,
1278
1380
  }));
1279
1381
  return projected;
@@ -1295,7 +1397,7 @@ class Dispatcher {
1295
1397
  && (targetPathname === "" || targetPathname.endsWith("/"));
1296
1398
  const exactTarget = statement.target !== null
1297
1399
  && !collectionTarget
1298
- && !PathSyntax.hasGlob(entryPathnameOf(statement.target));
1400
+ && !PathSyntax.hasGlob(authoredCoordinate.pathname);
1299
1401
  if (exactTarget && statement.target !== null) {
1300
1402
  const prepared = await this.#prepareDataRepresentation({
1301
1403
  target: statement.target,
@@ -1303,8 +1405,8 @@ class Dispatcher {
1303
1405
  handler: handler,
1304
1406
  manifest,
1305
1407
  ctx,
1306
- schemeCtx,
1307
1408
  publishedChannel,
1409
+ ...(addressResolution === null ? {} : { resolved: addressResolution }),
1308
1410
  });
1309
1411
  if (prepared.result !== null)
1310
1412
  return prepared.result;
@@ -1328,6 +1430,7 @@ class Dispatcher {
1328
1430
  ?? schemeName;
1329
1431
  return Results.assert(await EntryFind.findWorkspaceEntries(statement, ctx, { ...manifest, name: storageScheme, storedScheme: storageScheme }, {
1330
1432
  ownerId: prepared.address.ownerId,
1433
+ authority: prepared.address.authority,
1331
1434
  pathname: prepared.address.pathname,
1332
1435
  }));
1333
1436
  }
@@ -1359,7 +1462,7 @@ class Dispatcher {
1359
1462
  if (handler === undefined || manifest === undefined) {
1360
1463
  throw new Error("the core log curation owner is not registered");
1361
1464
  }
1362
- const schemeCtx = new SchemeCtxImpl(ctx, "log", manifest, this.#liveSubscriptions);
1465
+ const schemeCtx = new SchemeCtxImpl(ctx, "log", manifest, this.#liveSubscriptions, { ownerId: null });
1363
1466
  const outcome = await handler.curate(statement, schemeCtx);
1364
1467
  return { result: Results.assert(outcome.result), plan: outcome.plan };
1365
1468
  }
@@ -1370,9 +1473,9 @@ class Dispatcher {
1370
1473
  return false;
1371
1474
  return !(statement.op === "SEND" && statement.target === null);
1372
1475
  }
1373
- async #writeLog({ statement, result, workspaceId, workerId, loopId, turnId, sequence, origin, curationPlan, modelCallId, }) {
1476
+ async #writeLog({ statement, result, workspaceId, workerId, functionalityWorkerId, loopId, turnId, sequence, origin, curationPlan, modelCallId, }) {
1374
1477
  const durableStatement = DurableStatement.project(statement);
1375
- const target = this.#extractTarget(durableStatement.target, workspaceId);
1478
+ const target = this.#extractTarget(durableStatement.target, functionalityWorkerId);
1376
1479
  await this.#canonColumns(target, workspaceId); // {§fs-answer-in-canon}
1377
1480
  const lineMarkerJson = "lineMarker" in durableStatement && durableStatement.lineMarker !== null
1378
1481
  ? JSON.stringify(durableStatement.lineMarker)
@@ -1484,23 +1587,26 @@ class Dispatcher {
1484
1587
  // inputs collapse to scheme=null in log target metadata because both render
1485
1588
  // as bare paths. Addressable file entries separately persist under the
1486
1589
  // reserved `file` identity scheme ({§entry-identity-no-null}).
1487
- #extractTarget(path, workspaceId) {
1590
+ #extractTarget(path, workerId) {
1488
1591
  if (path === null)
1489
1592
  return { scheme: null, username: null, password: null, hostname: null, port: null, pathname: null, query: null, fragment: null };
1490
1593
  // `local` (bare path) carries no URL parts — store the raw text as the pathname for the log record, scheme=null.
1491
1594
  if (path.kind === "local")
1492
1595
  return { scheme: null, username: null, password: null, hostname: null, port: null, pathname: PathSyntax.decodeParens(path.raw), query: null, fragment: null }; // {§path-parentheses}
1493
1596
  const scheme = path.scheme === "file" ? null : path.scheme;
1494
- // {§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.
1597
+ // The registered scheme owns authority disposition. Namespace authority
1598
+ // is path syntax; resource and owner authorities remain explicit in the
1599
+ // durable operation evidence.
1600
+ const routedScheme = schemeNameOf(path);
1601
+ const manifest = routedScheme === null
1602
+ ? undefined
1603
+ : this.#schemes.manifestFor(routedScheme, workerId);
1497
1604
  const foldNs = scheme !== null
1498
- && scheme !== "worker"
1499
- && !NetworkAddress.supports(scheme)
1500
- && this.#schemes.has(scheme, workspaceId);
1605
+ && manifest !== undefined
1606
+ && (manifest.authority ?? "namespace") === "namespace";
1501
1607
  return {
1502
1608
  scheme, username: path.username, password: path.password,
1503
- hostname: foldNs ? null : path.hostname, port: path.port,
1609
+ hostname: foldNs ? null : path.hostname, port: foldNs ? null : path.port,
1504
1610
  pathname: PathSyntax.decodeParens(foldNs ? foldAuthorityIntoPath(path.hostname, path.pathname) : path.pathname), // {§path-parentheses}
1505
1611
  query: path.query, fragment: path.fragment,
1506
1612
  };