@plurnk/plurnk-service 1.4.0 → 1.6.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 (302) hide show
  1. package/.env.defaults +37 -31
  2. package/INSTALL.md +16 -11
  3. package/README.md +1 -0
  4. package/SPEC.md +631 -397
  5. package/dist/Paths.d.ts.map +1 -1
  6. package/dist/Paths.js +2 -3
  7. package/dist/Paths.js.map +1 -1
  8. package/dist/build-info.json +1 -1
  9. package/dist/content/edit-collision.d.ts +5 -0
  10. package/dist/content/edit-collision.d.ts.map +1 -0
  11. package/dist/content/edit-collision.js +14 -0
  12. package/dist/content/edit-collision.js.map +1 -0
  13. package/dist/content/edit-receipt.d.ts +3 -2
  14. package/dist/content/edit-receipt.d.ts.map +1 -1
  15. package/dist/content/edit-receipt.js +58 -13
  16. package/dist/content/edit-receipt.js.map +1 -1
  17. package/dist/content/index.d.ts +5 -1
  18. package/dist/content/index.d.ts.map +1 -1
  19. package/dist/content/index.js +4 -1
  20. package/dist/content/index.js.map +1 -1
  21. package/dist/content/line-anchors.d.ts +41 -0
  22. package/dist/content/line-anchors.d.ts.map +1 -0
  23. package/dist/content/line-anchors.js +191 -0
  24. package/dist/content/line-anchors.js.map +1 -0
  25. package/dist/content/line-marker.d.ts +7 -3
  26. package/dist/content/line-marker.d.ts.map +1 -1
  27. package/dist/content/line-marker.js +2 -1
  28. package/dist/content/line-marker.js.map +1 -1
  29. package/dist/content/matcher.d.ts +1 -1
  30. package/dist/content/matcher.d.ts.map +1 -1
  31. package/dist/content/matcher.js +2 -2
  32. package/dist/content/matcher.js.map +1 -1
  33. package/dist/content/read-projector.d.ts +20 -0
  34. package/dist/content/read-projector.d.ts.map +1 -0
  35. package/dist/content/read-projector.js +122 -0
  36. package/dist/content/read-projector.js.map +1 -0
  37. package/dist/content/read-resolve.d.ts +2 -7
  38. package/dist/content/read-resolve.d.ts.map +1 -1
  39. package/dist/content/read-resolve.js +32 -107
  40. package/dist/content/read-resolve.js.map +1 -1
  41. package/dist/core/BranchReceipt.d.ts +0 -1
  42. package/dist/core/BranchReceipt.d.ts.map +1 -1
  43. package/dist/core/BranchReceipt.js +0 -3
  44. package/dist/core/BranchReceipt.js.map +1 -1
  45. package/dist/core/BudgetReadout.d.ts.map +1 -1
  46. package/dist/core/BudgetReadout.js +9 -2
  47. package/dist/core/BudgetReadout.js.map +1 -1
  48. package/dist/core/ChannelWrite.d.ts +3 -0
  49. package/dist/core/ChannelWrite.d.ts.map +1 -1
  50. package/dist/core/ChannelWrite.js +3 -3
  51. package/dist/core/ChannelWrite.js.map +1 -1
  52. package/dist/core/ChannelWrite.sql +4 -3
  53. package/dist/core/CoreSchemeServices.d.ts +10 -2
  54. package/dist/core/CoreSchemeServices.d.ts.map +1 -1
  55. package/dist/core/CoreSchemeServices.js.map +1 -1
  56. package/dist/core/Dispatcher.d.ts +14 -8
  57. package/dist/core/Dispatcher.d.ts.map +1 -1
  58. package/dist/core/Dispatcher.js +656 -312
  59. package/dist/core/Dispatcher.js.map +1 -1
  60. package/dist/core/DurableStatement.d.ts.map +1 -1
  61. package/dist/core/DurableStatement.js +2 -0
  62. package/dist/core/DurableStatement.js.map +1 -1
  63. package/dist/core/Engine.d.ts +24 -18
  64. package/dist/core/Engine.d.ts.map +1 -1
  65. package/dist/core/Engine.js +620 -392
  66. package/dist/core/Engine.js.map +1 -1
  67. package/dist/core/Engine.sql +188 -119
  68. package/dist/core/ExecutableTools.d.ts +10 -0
  69. package/dist/core/ExecutableTools.d.ts.map +1 -0
  70. package/dist/core/ExecutableTools.js +41 -0
  71. package/dist/core/ExecutableTools.js.map +1 -0
  72. package/dist/core/ExecutorRegistry.d.ts +3 -3
  73. package/dist/core/ExecutorRegistry.d.ts.map +1 -1
  74. package/dist/core/ExecutorRegistry.js +2 -2
  75. package/dist/core/ExecutorRegistry.js.map +1 -1
  76. package/dist/core/LogBody.d.ts +4 -1
  77. package/dist/core/LogBody.d.ts.map +1 -1
  78. package/dist/core/LogBody.js +46 -2
  79. package/dist/core/LogBody.js.map +1 -1
  80. package/dist/core/LogEntryProjection.d.ts +14 -0
  81. package/dist/core/LogEntryProjection.d.ts.map +1 -0
  82. package/dist/core/LogEntryProjection.js +38 -0
  83. package/dist/core/LogEntryProjection.js.map +1 -0
  84. package/dist/core/LoopLifecycle.d.ts +1 -2
  85. package/dist/core/LoopLifecycle.d.ts.map +1 -1
  86. package/dist/core/LoopLifecycle.js +1 -4
  87. package/dist/core/LoopLifecycle.js.map +1 -1
  88. package/dist/core/LoopLifecycle.sql +1 -4
  89. package/dist/core/ModelCall.d.ts +27 -0
  90. package/dist/core/ModelCall.d.ts.map +1 -0
  91. package/dist/core/ModelCall.js +125 -0
  92. package/dist/core/ModelCall.js.map +1 -0
  93. package/dist/core/PacketBuilder.d.ts +13 -12
  94. package/dist/core/PacketBuilder.d.ts.map +1 -1
  95. package/dist/core/PacketBuilder.js +146 -118
  96. package/dist/core/PacketBuilder.js.map +1 -1
  97. package/dist/core/ProblemLog.d.ts.map +1 -1
  98. package/dist/core/ProblemLog.js +1 -0
  99. package/dist/core/ProblemLog.js.map +1 -1
  100. package/dist/core/ProposalLifecycle.d.ts.map +1 -1
  101. package/dist/core/ProposalLifecycle.js +3 -4
  102. package/dist/core/ProposalLifecycle.js.map +1 -1
  103. package/dist/core/ProviderInstantiate.d.ts.map +1 -1
  104. package/dist/core/ProviderInstantiate.js +23 -6
  105. package/dist/core/ProviderInstantiate.js.map +1 -1
  106. package/dist/core/SchemeRegistry.js +7 -7
  107. package/dist/core/SchemeRegistry.js.map +1 -1
  108. package/dist/core/StrikeRail.d.ts +5 -2
  109. package/dist/core/StrikeRail.d.ts.map +1 -1
  110. package/dist/core/StrikeRail.js +10 -7
  111. package/dist/core/StrikeRail.js.map +1 -1
  112. package/dist/core/TerminalResult.d.ts +16 -0
  113. package/dist/core/TerminalResult.d.ts.map +1 -0
  114. package/dist/core/TerminalResult.js +49 -0
  115. package/dist/core/TerminalResult.js.map +1 -0
  116. package/dist/core/caps/CapsResolve.d.ts +5 -1
  117. package/dist/core/caps/CapsResolve.d.ts.map +1 -1
  118. package/dist/core/caps/CapsResolve.js +9 -5
  119. package/dist/core/caps/CapsResolve.js.map +1 -1
  120. package/dist/core/caps/DbChannelCaps.d.ts +1 -1
  121. package/dist/core/caps/DbChannelCaps.d.ts.map +1 -1
  122. package/dist/core/caps/DbChannelCaps.js +6 -4
  123. package/dist/core/caps/DbChannelCaps.js.map +1 -1
  124. package/dist/core/caps/DbEntryCaps.d.ts +2 -1
  125. package/dist/core/caps/DbEntryCaps.d.ts.map +1 -1
  126. package/dist/core/caps/DbEntryCaps.js +21 -8
  127. package/dist/core/caps/DbEntryCaps.js.map +1 -1
  128. package/dist/core/caps/DbNotifyCaps.d.ts +1 -1
  129. package/dist/core/caps/DbNotifyCaps.d.ts.map +1 -1
  130. package/dist/core/caps/DbNotifyCaps.js +6 -4
  131. package/dist/core/caps/DbNotifyCaps.js.map +1 -1
  132. package/dist/core/caps/DbProjectionCaps.d.ts +1 -0
  133. package/dist/core/caps/DbProjectionCaps.d.ts.map +1 -1
  134. package/dist/core/caps/DbProjectionCaps.js +29 -0
  135. package/dist/core/caps/DbProjectionCaps.js.map +1 -1
  136. package/dist/core/caps/DbSubscriptionCaps.d.ts +4 -6
  137. package/dist/core/caps/DbSubscriptionCaps.d.ts.map +1 -1
  138. package/dist/core/caps/DbSubscriptionCaps.js +27 -13
  139. package/dist/core/caps/DbSubscriptionCaps.js.map +1 -1
  140. package/dist/core/caps/SchemeCtxImpl.d.ts +11 -3
  141. package/dist/core/caps/SchemeCtxImpl.d.ts.map +1 -1
  142. package/dist/core/caps/SchemeCtxImpl.js +10 -8
  143. package/dist/core/caps/SchemeCtxImpl.js.map +1 -1
  144. package/dist/core/fork.d.ts.map +1 -1
  145. package/dist/core/fork.js +23 -7
  146. package/dist/core/fork.js.map +1 -1
  147. package/dist/core/fork.sql +35 -14
  148. package/dist/core/git-iso.d.ts +3 -2
  149. package/dist/core/git-iso.d.ts.map +1 -1
  150. package/dist/core/git-iso.js +27 -12
  151. package/dist/core/git-iso.js.map +1 -1
  152. package/dist/core/git-membership.d.ts +1 -0
  153. package/dist/core/git-membership.d.ts.map +1 -1
  154. package/dist/core/git-membership.js +87 -26
  155. package/dist/core/git-membership.js.map +1 -1
  156. package/dist/core/git-state.d.ts +8 -1
  157. package/dist/core/git-state.d.ts.map +1 -1
  158. package/dist/core/git-state.js +36 -17
  159. package/dist/core/git-state.js.map +1 -1
  160. package/dist/core/namespace.d.ts +1 -0
  161. package/dist/core/namespace.d.ts.map +1 -1
  162. package/dist/core/namespace.js +20 -0
  163. package/dist/core/namespace.js.map +1 -1
  164. package/dist/core/optimistic-settlement.d.ts +2 -0
  165. package/dist/core/optimistic-settlement.d.ts.map +1 -0
  166. package/dist/core/optimistic-settlement.js +14 -0
  167. package/dist/core/optimistic-settlement.js.map +1 -0
  168. package/dist/core/packet-wire.d.ts +5 -2
  169. package/dist/core/packet-wire.d.ts.map +1 -1
  170. package/dist/core/packet-wire.js +318 -114
  171. package/dist/core/packet-wire.js.map +1 -1
  172. package/dist/core/provider-accounting.d.ts +24 -0
  173. package/dist/core/provider-accounting.d.ts.map +1 -0
  174. package/dist/core/provider-accounting.js +76 -0
  175. package/dist/core/provider-accounting.js.map +1 -0
  176. package/dist/core/results.d.ts +4 -2
  177. package/dist/core/results.d.ts.map +1 -1
  178. package/dist/core/results.js +6 -0
  179. package/dist/core/results.js.map +1 -1
  180. package/dist/core/scheme-types.d.ts +0 -12
  181. package/dist/core/scheme-types.d.ts.map +1 -1
  182. package/dist/core/scheme-types.js.map +1 -1
  183. package/dist/core/search-gate.d.ts +2 -2
  184. package/dist/core/search-gate.d.ts.map +1 -1
  185. package/dist/core/search-gate.js +8 -8
  186. package/dist/core/search-gate.js.map +1 -1
  187. package/dist/core/token-ruler.js +1 -1
  188. package/dist/core/token-ruler.js.map +1 -1
  189. package/dist/core/turn-scheduler.js +1 -1
  190. package/dist/core/turn-scheduler.js.map +1 -1
  191. package/dist/core/worker-ops.sql +4 -4
  192. package/dist/core/workspace-settings.js +1 -1
  193. package/dist/core/workspace-settings.js.map +1 -1
  194. package/dist/digest/Digest.d.ts.map +1 -1
  195. package/dist/digest/Digest.js +349 -76
  196. package/dist/digest/Digest.js.map +1 -1
  197. package/dist/digest/digest.sql +42 -16
  198. package/dist/schemes/Exec.d.ts +4 -4
  199. package/dist/schemes/Exec.d.ts.map +1 -1
  200. package/dist/schemes/Exec.js +167 -134
  201. package/dist/schemes/Exec.js.map +1 -1
  202. package/dist/schemes/ExecOutputScheme.d.ts +4 -5
  203. package/dist/schemes/ExecOutputScheme.d.ts.map +1 -1
  204. package/dist/schemes/ExecOutputScheme.js +12 -20
  205. package/dist/schemes/ExecOutputScheme.js.map +1 -1
  206. package/dist/schemes/File.d.ts +5 -6
  207. package/dist/schemes/File.d.ts.map +1 -1
  208. package/dist/schemes/File.js +35 -41
  209. package/dist/schemes/File.js.map +1 -1
  210. package/dist/schemes/Log.d.ts +16 -5
  211. package/dist/schemes/Log.d.ts.map +1 -1
  212. package/dist/schemes/Log.js +229 -262
  213. package/dist/schemes/Log.js.map +1 -1
  214. package/dist/schemes/Log.sql +50 -27
  215. package/dist/schemes/Prompt.d.ts +3 -4
  216. package/dist/schemes/Prompt.d.ts.map +1 -1
  217. package/dist/schemes/Prompt.js +5 -5
  218. package/dist/schemes/Prompt.js.map +1 -1
  219. package/dist/schemes/Skill.d.ts +4 -5
  220. package/dist/schemes/Skill.d.ts.map +1 -1
  221. package/dist/schemes/Skill.js +3 -3
  222. package/dist/schemes/Skill.js.map +1 -1
  223. package/dist/schemes/Worker.d.ts +7 -7
  224. package/dist/schemes/Worker.d.ts.map +1 -1
  225. package/dist/schemes/Worker.js +99 -84
  226. package/dist/schemes/Worker.js.map +1 -1
  227. package/dist/schemes/_entry-crud.d.ts +2 -2
  228. package/dist/schemes/_entry-crud.d.ts.map +1 -1
  229. package/dist/schemes/_entry-crud.js +29 -10
  230. package/dist/schemes/_entry-crud.js.map +1 -1
  231. package/dist/schemes/_entry-crud.sql +11 -17
  232. package/dist/schemes/_entry-find.d.ts +41 -14
  233. package/dist/schemes/_entry-find.d.ts.map +1 -1
  234. package/dist/schemes/_entry-find.js +159 -166
  235. package/dist/schemes/_entry-find.js.map +1 -1
  236. package/dist/schemes/_entry-find.sql +2 -21
  237. package/dist/schemes/_entry-graph.sql +1 -1
  238. package/dist/schemes/_entry-manifest.d.ts +5 -17
  239. package/dist/schemes/_entry-manifest.d.ts.map +1 -1
  240. package/dist/schemes/_entry-manifest.js +28 -24
  241. package/dist/schemes/_entry-manifest.js.map +1 -1
  242. package/dist/schemes/_entry-ops.d.ts +13 -8
  243. package/dist/schemes/_entry-ops.d.ts.map +1 -1
  244. package/dist/schemes/_entry-ops.js +98 -119
  245. package/dist/schemes/_entry-ops.js.map +1 -1
  246. package/dist/schemes/_entry-ops.sql +31 -6
  247. package/dist/schemes/_entry-semantic.d.ts +5 -7
  248. package/dist/schemes/_entry-semantic.d.ts.map +1 -1
  249. package/dist/schemes/_entry-semantic.js +10 -31
  250. package/dist/schemes/_entry-semantic.js.map +1 -1
  251. package/dist/schemes/_entry-semantic.sql +2 -2
  252. package/dist/schemes/_entry-send.js +4 -4
  253. package/dist/schemes/_entry-send.js.map +1 -1
  254. package/dist/schemes/_search-index.d.ts.map +1 -1
  255. package/dist/schemes/_search-index.js +16 -6
  256. package/dist/schemes/_search-index.js.map +1 -1
  257. package/dist/schemes/exec-abort.js +2 -2
  258. package/dist/schemes/exec-abort.js.map +1 -1
  259. package/dist/server/BranchBatches.d.ts +1 -0
  260. package/dist/server/BranchBatches.d.ts.map +1 -1
  261. package/dist/server/BranchBatches.js +1 -0
  262. package/dist/server/BranchBatches.js.map +1 -1
  263. package/dist/server/Daemon.d.ts +6 -8
  264. package/dist/server/Daemon.d.ts.map +1 -1
  265. package/dist/server/Daemon.js +238 -68
  266. package/dist/server/Daemon.js.map +1 -1
  267. package/dist/server/DaemonModule.d.ts +3 -3
  268. package/dist/server/DaemonModule.d.ts.map +1 -1
  269. package/dist/server/branch-batch.sql +3 -1
  270. package/dist/server/client-input.d.ts +2 -1
  271. package/dist/server/client-input.d.ts.map +1 -1
  272. package/dist/server/client-input.js +7 -1
  273. package/dist/server/client-input.js.map +1 -1
  274. package/dist/server/drain.sql +8 -7
  275. package/dist/server/envelope.d.ts +0 -2
  276. package/dist/server/envelope.d.ts.map +1 -1
  277. package/dist/server/envelope.js +0 -1
  278. package/dist/server/envelope.js.map +1 -1
  279. package/dist/server/envelope.sql +3 -4
  280. package/dist/server/lifecycle-recovery.sql +44 -1
  281. package/dist/server/logEntry.d.ts +2 -1
  282. package/dist/server/logEntry.d.ts.map +1 -1
  283. package/dist/server/logEntry.js +15 -2
  284. package/dist/server/logEntry.js.map +1 -1
  285. package/dist/server/logEntry.sql +5 -1
  286. package/dist/service.d.ts.map +1 -1
  287. package/dist/service.js +13 -1
  288. package/dist/service.js.map +1 -1
  289. package/migrations/001_schema.sql +730 -221
  290. package/package.json +53 -46
  291. package/dist/core/BudgetOverflow.d.ts +0 -23
  292. package/dist/core/BudgetOverflow.d.ts.map +0 -1
  293. package/dist/core/BudgetOverflow.js +0 -61
  294. package/dist/core/BudgetOverflow.js.map +0 -1
  295. package/dist/core/caps/DbTagCaps.d.ts +0 -10
  296. package/dist/core/caps/DbTagCaps.d.ts.map +0 -1
  297. package/dist/core/caps/DbTagCaps.js +0 -44
  298. package/dist/core/caps/DbTagCaps.js.map +0 -1
  299. package/dist/matrix/live-specimen.d.ts +0 -9
  300. package/dist/matrix/live-specimen.d.ts.map +0 -1
  301. package/dist/matrix/live-specimen.js +0 -37
  302. package/dist/matrix/live-specimen.js.map +0 -1
@@ -1,9 +1,8 @@
1
- import { parsePath } from "@plurnk/plurnk-contracts";
1
+ import { parsePath, TagSignal } from "@plurnk/plurnk-contracts";
2
2
  import { Policy } from "@plurnk/plurnk-execs";
3
3
  import { WebFetcher, WebMaterializationError, } from "@plurnk/plurnk-schemes-http";
4
4
  import WorkspaceSettings from "../core/workspace-settings.js";
5
5
  import EffectPolicy from "./EffectPolicy.js";
6
- import EntryOps from "./_entry-ops.js";
7
6
  import EntryCrud from "./_entry-crud.js";
8
7
  import Owner from "../core/Owner.js";
9
8
  import EntryFind from "./_entry-find.js";
@@ -13,7 +12,7 @@ import ExecAbort from "./exec-abort.js";
13
12
  import { entryPathnameOf, renderAddress } from "../core/plurnk-uri.js";
14
13
  import { writeFile, unlink, stat } from "node:fs/promises";
15
14
  import { tmpdir } from "node:os";
16
- import { join, resolve } from "node:path";
15
+ import { isAbsolute, join, resolve } from "node:path";
17
16
  import { CoreSchemeAdapterBase } from "../core/CoreSchemeServices.js";
18
17
  import ErrorDetail from "../core/ErrorDetail.js";
19
18
  import Results, { OperationFailureError } from "../core/results.js";
@@ -21,11 +20,13 @@ import { InvalidOperationResultError, NetworkAddress } from "@plurnk/plurnk-sche
21
20
  import DbProjectionCaps from "../core/caps/DbProjectionCaps.js";
22
21
  import WorkerControlAddress from "../core/WorkerControlAddress.js";
23
22
  import JournalTurn from "../core/JournalTurn.js";
23
+ import LogEntryProjection from "../core/LogEntryProjection.js";
24
+ import { setTimeout as delay } from "node:timers/promises";
24
25
  // Executors are discovered + probed at boot into ExecutorRegistry and reach
25
26
  // the scheme through ctx.executors ({§exec-registry-resolves}). Each runtime tag
26
27
  // resolves to its sibling executor; the scheme itself stays runtime-agnostic.
27
28
  // Extract the local arm of {§exec-target-routing}; non-file schemes are
28
- // classified separately by schemeSourceOf.
29
+ // classified separately by resourceSourceOf.
29
30
  const localPathFromTarget = (target) => {
30
31
  if (target === null)
31
32
  return null;
@@ -39,21 +40,13 @@ const localPathFromTarget = (target) => {
39
40
  // A non-file scheme target is distinct from the local path handled above. The
40
41
  // consumer resolves its content at apply time; executors stay scheme-blind
41
42
  // ({§executor-role}).
42
- const schemeSourceOf = (target) => {
43
+ const resourceSourceOf = (target) => {
43
44
  if (target === null || target.kind !== "url")
44
45
  return null;
45
46
  if (target.scheme === null || target.scheme === "file")
46
47
  return null;
47
48
  return target.raw;
48
49
  };
49
- // {§exec-target-routing} — effect classifies the invocation core is admitting,
50
- // before a scheme-backed source can be materialized. A scheme target with no
51
- // body becomes the command, so the executor has no target. With a body, the
52
- // scheme content becomes a target at run time; its authored address is the
53
- // stable, opaque target-present identity used only for the one effect call.
54
- const effectTargetOf = (authoredTarget, routedTarget, schemeSource, command) => schemeSource !== null && command.length > 0
55
- ? authoredTarget?.raw ?? null
56
- : routedTarget;
57
50
  // EXEC's pathname is <runtime>/<loop_seq>/<turn_seq>/<sequence> (stamped by
58
51
  // Dispatcher.#writeLog). Exec owns this convention, so it — not the client — turns
59
52
  // the pathname into the entry's coordinate, mirrored onto stream payloads so
@@ -96,27 +89,22 @@ export default class Exec extends CoreSchemeAdapterBase {
96
89
  writableBy: ["model", "client"],
97
90
  volatile: true,
98
91
  modelVisible: true,
99
- example: "<<EXEC[sqlite]:SELECT 22.0 / 7.0:EXEC",
100
- documentation: "Runs a command in a runtime — `<<EXEC[runtime](target):command:EXEC` — output streams into the worker's `<runtime>:///<loop>/<turn>/<seq>` entry on the runtime's own channels (a subprocess → stdout/stderr; a computational runtime like sqlite/jq → a JSON `results` channel). A host-effecting command proposes for review before it runs; a read-only/pure one runs ungated. Either way you never fetch the output: the engine surfaces each turn's new stream bytes to you automatically — folded while the command runs, opened when it finishes.",
92
+ documentation: "Runs a registered executable tool — `## EXEC0 [executor] (target) <timeout,poll>\nbody` — using the tool's table-declared target and body contract. Output streams into the worker's `<executor>:///<loop>/<turn>/<seq>` entry on that tool's own channels. A host-effecting invocation proposes for review before it runs; a read-only or pure one runs ungated. Either way you never fetch the output: the engine surfaces each turn's new stream bytes automatically — folded while it runs, opened when it finishes.",
101
93
  flags: {
102
94
  excludedInAsk: true,
103
95
  },
104
96
  };
105
- // The web-fetch the entry sink calls on content:null ({§exec-entry-sink}). Default = schemes-http's
106
- // checked WebFetcher over one warm-Chromium pool shared across this handler's
107
- // fallback renders; injectable so tests substitute the network.
97
+ // The web-fetch the entry sink calls on content:null ({§exec-entry-sink}).
98
+ // Default = schemes-http's checked WebFetcher; injectable for tests.
108
99
  #fetchWeb;
109
- #closeWebFetcher;
110
100
  constructor(fetchWeb) {
111
101
  super();
112
- const webFetcher = new WebFetcher();
113
102
  if (fetchWeb === undefined) {
103
+ const webFetcher = new WebFetcher();
114
104
  this.#fetchWeb = (url, opts) => webFetcher.fetch(url, opts);
115
- this.#closeWebFetcher = () => webFetcher.close();
116
105
  }
117
106
  else {
118
107
  this.#fetchWeb = fetchWeb;
119
- this.#closeWebFetcher = async () => { };
120
108
  }
121
109
  }
122
110
  #activeAborts = new Map();
@@ -124,11 +112,9 @@ export default class Exec extends CoreSchemeAdapterBase {
124
112
  async idle() {
125
113
  await Promise.allSettled([...this.#activeSpawns.values()]);
126
114
  }
127
- // {§handler-lifecycle} — idle is the streaming drain barrier; the handler
128
- // hook owns release of its separate checked-fetch browser pool.
115
+ // {§handler-lifecycle} — idle is the streaming drain barrier.
129
116
  async close() {
130
117
  await this.idle();
131
- await this.#closeWebFetcher();
132
118
  }
133
119
  // Whether the worker has an in-flight spawn (a background exec). The daemon
134
120
  // reads this only for loop.cancel's cancelled=true/false answer — the
@@ -140,6 +126,29 @@ export default class Exec extends CoreSchemeAdapterBase {
140
126
  return true;
141
127
  return false;
142
128
  }
129
+ // {§worker-optimistic-settlement} — wait on exactly the spawns initiated by
130
+ // this turn, ending immediately when all settle and never extending the
131
+ // opportunity to streams inherited from an earlier turn.
132
+ async settleTurnSpawns(workerId, turnId, timeoutMs, signal) {
133
+ signal?.throwIfAborted();
134
+ const pending = [...this.#activeAborts.entries()]
135
+ .filter(([, active]) => active.workerId === workerId && active.turnId === turnId)
136
+ .map(([subscriptionId]) => {
137
+ const spawn = this.#activeSpawns.get(subscriptionId);
138
+ if (spawn === undefined) {
139
+ throw new Error(`Active EXEC subscription ${subscriptionId} has no spawn settlement promise.`);
140
+ }
141
+ return spawn;
142
+ });
143
+ if (pending.length === 0)
144
+ return true;
145
+ if (timeoutMs === 0)
146
+ return false;
147
+ return await Promise.race([
148
+ Promise.allSettled(pending).then(() => true),
149
+ delay(timeoutMs, false, { signal, ref: false }),
150
+ ]);
151
+ }
143
152
  // {§exec-hold-until-concluded} — match active spawns against the
144
153
  // operator's runtime or runtime:effect hold selectors.
145
154
  hasActiveHoldSpawns(workerId, holdSet) {
@@ -185,51 +194,15 @@ export default class Exec extends CoreSchemeAdapterBase {
185
194
  });
186
195
  }
187
196
  // EXEC op handler — the actual model-facing entry point per plurnk.md.
188
- // `<<EXEC[runtime](target):command:EXEC` →
189
- // signal=runtime, target=optional source/program/cwd, body=command.
197
+ // `## EXEC0 [runtime] (target)\nbody` → runtime-owned invocation buckets.
190
198
  //
191
- // Proposes (status=202) with attrs={runtime, cwd, command, pathname}.
199
+ // Proposes (status=202) with attrs={runtime, cwd, body, pathname}.
192
200
  // applyResolution spawns the subprocess; output streams into the
193
201
  // coordinate-stamped <runtime>:///<pathname> entry's stdout/stderr channels
194
202
  // (e.g. sh:///1/1/2, {§exec}). The model READs that entry on a subsequent turn.
195
203
  async exec(statement, ctx) {
196
204
  const core = this.coreContext(ctx);
197
- let command = statement.body ?? "";
198
- // Non-file data sources resolve after acceptance. {§exec-target-routing}
199
- const schemeSource = schemeSourceOf(statement.target);
200
- // A local directory overrides cwd; every other local path remains the
201
- // executor target. {§exec-target-routing}
202
- const workspaceRow = await core.db.envelope_get_workspace.get({ id: core.workspaceId });
203
- const projectRoot = workspaceRow?.project_root ?? null;
204
- const localTarget = localPathFromTarget(statement.target);
205
- let routedTarget = localTarget;
206
- let cwd = projectRoot;
207
- if (localTarget !== null) {
208
- const abs = projectRoot !== null ? resolve(projectRoot, localTarget) : localTarget;
209
- try {
210
- if ((await stat(abs)).isDirectory()) {
211
- cwd = abs;
212
- routedTarget = null;
213
- }
214
- }
215
- catch (cause) {
216
- if (cause?.code !== "ENOENT") {
217
- console.error(`EXEC target classification failed for '${abs}':`, cause);
218
- return Results.failure("scheme:exec", "target-classification-failed", 500, `EXEC target '${localTarget}' could not be inspected: ${ErrorDetail.preview(cause)}`, {}, {
219
- target: localTarget,
220
- stage: "target-classification",
221
- });
222
- }
223
- }
224
- }
225
- // An empty body requires a file/program or scheme command source.
226
- // {§exec-target-routing}
227
- if (command.length === 0 && schemeSource === null && routedTarget === null) {
228
- return Results.failure("scheme:exec", "command-required", 400, "EXEC requires a command body or an executable target.", {}, {
229
- recovery: "Provide a command body or executable target.",
230
- retryable: false,
231
- });
232
- }
205
+ const body = statement.body ?? "";
233
206
  const requested = typeof statement.signal === "string" ? statement.signal : "";
234
207
  const runtime = requested === "" ? "sh" : requested; // empty signal = default shell
235
208
  if (core.executors === undefined)
@@ -276,14 +249,76 @@ export default class Exec extends CoreSchemeAdapterBase {
276
249
  retryable: false,
277
250
  });
278
251
  }
279
- // The local (target) realization the executor receives — its data source
280
- // or program. A directory routes to cwd and a scheme source is resolved
281
- // only after acceptance, so either is null here.
282
- const target = routedTarget;
283
- // Derive one effect from the canonical logical target before any policy
284
- // consumes it, then preserve that fact with the invocation (#107,
285
- // {§executor-effect}). Leaves never receive authored command text.
286
- const effectTarget = effectTargetOf(statement.target, target, schemeSource, command);
252
+ const invocation = resolved.invocation;
253
+ const hasBody = body.length > 0;
254
+ const hasTarget = statement.target !== null;
255
+ const refuse = (code, detail, recovery, extensions = {}) => Results.failure("scheme:exec", code, 400, detail, {}, { runtime, recovery, retryable: false, ...extensions });
256
+ if (invocation.body.required && !hasBody) {
257
+ return refuse("body-required", `Executable tool '${runtime}' requires a ${invocation.body.role} body.`, `Provide the ${invocation.body.role} in the EXEC body.`);
258
+ }
259
+ if (invocation.target === undefined && hasTarget) {
260
+ return refuse("target-not-supported", `Executable tool '${runtime}' does not accept a target.`, `Remove the target and provide the ${invocation.body.role} in the body.`);
261
+ }
262
+ if (invocation.target?.required === true && !hasTarget) {
263
+ return refuse("target-required", `Executable tool '${runtime}' requires a ${invocation.target.role} target.`, `Provide the ${invocation.target.role} in (target).`);
264
+ }
265
+ if (!hasBody && !hasTarget) {
266
+ return refuse("input-required", `Executable tool '${runtime}' requires a body or target.`, `Provide ${invocation.target === undefined ? "a body" : "a body or target"}.`);
267
+ }
268
+ if (invocation.exclusive === true && hasBody && hasTarget) {
269
+ return refuse("input-conflict", `Executable tool '${runtime}' accepts its ${invocation.body.role} body and ${invocation.target?.role ?? "target"} target only as alternatives.`, "Provide either the body or target, not both.");
270
+ }
271
+ const workspaceRow = await core.db.envelope_get_workspace.get({ id: core.workspaceId });
272
+ const projectRoot = workspaceRow?.project_root ?? null;
273
+ let cwd = projectRoot;
274
+ let target = null;
275
+ let resourceSource = null;
276
+ const targetDecl = invocation.target;
277
+ if (statement.target !== null && targetDecl !== undefined) {
278
+ if (targetDecl.kind === "literal") {
279
+ target = statement.target.raw;
280
+ }
281
+ else {
282
+ const localTarget = localPathFromTarget(statement.target);
283
+ if (localTarget !== null) {
284
+ target = localTarget;
285
+ }
286
+ else if (targetDecl.kind === "resource") {
287
+ resourceSource = resourceSourceOf(statement.target);
288
+ if (resourceSource === null) {
289
+ throw new Error(`EXEC '${runtime}' resource target could not be classified`);
290
+ }
291
+ }
292
+ else {
293
+ return refuse("target-kind-invalid", `Executable tool '${runtime}' accepts only a local or file:// ${targetDecl.role} target.`, `Use a local or file:// ${targetDecl.role} target.`, { target: statement.target.raw, targetKind: targetDecl.kind });
294
+ }
295
+ }
296
+ }
297
+ if (target !== null && targetDecl?.directory === "cwd") {
298
+ const inspected = isAbsolute(target)
299
+ ? target
300
+ : projectRoot === null ? null : resolve(projectRoot, target);
301
+ if (inspected !== null) {
302
+ try {
303
+ if ((await stat(inspected)).isDirectory()) {
304
+ cwd = inspected;
305
+ target = null;
306
+ }
307
+ }
308
+ catch (cause) {
309
+ if (cause?.code !== "ENOENT") {
310
+ console.error(`EXEC target classification failed for '${inspected}':`, cause);
311
+ return Results.failure("scheme:exec", "target-classification-failed", 500, `EXEC target '${target}' could not be inspected: ${ErrorDetail.preview(cause)}`, {}, { target, stage: "target-classification" });
312
+ }
313
+ }
314
+ }
315
+ }
316
+ if (!hasBody && target === null && resourceSource === null) {
317
+ return refuse("input-required", `Executable tool '${runtime}' has no executable body or realized target.`, `Provide a ${invocation.body.role} body or a non-directory target.`);
318
+ }
319
+ // One logical target derives one effect fact before policy consumes it.
320
+ // A non-file resource keeps its exact authored address until materialization.
321
+ const effectTarget = resourceSource ?? target;
287
322
  const effect = resolved.executor.effect(effectTarget);
288
323
  // cwd is the workspace project_root unless target routing selected a
289
324
  // directory override. {§exec-target-routing}, {§executor-sinks}
@@ -300,21 +335,20 @@ export default class Exec extends CoreSchemeAdapterBase {
300
335
  const turnScoped = typeof marks?.[0] === "number" && marks[0] === 0;
301
336
  const pollSec = typeof marks?.[1] === "number" && marks[1] >= 0 ? Math.floor(marks[1]) : undefined;
302
337
  const attrs = {
303
- runtime, cwd, command, target, pathname: "", effect,
304
- ...(schemeSource !== null ? { schemeSource } : {}),
338
+ runtime, cwd, body, target, pathname: "", effect,
339
+ ...(resourceSource !== null ? { resourceSource } : {}),
305
340
  ...(timeoutSec !== undefined ? { timeoutSec } : {}),
306
341
  ...(turnScoped ? { turnScoped: true } : {}),
307
342
  ...(pollSec !== undefined ? { pollSec } : {}),
308
343
  };
309
- // Body shown to client during proposal review — `$ command` is the
310
- // most-readable summary regardless of runtime.
311
- const preview = runtime !== "" ? `[${runtime}] ${command}` : `$ ${command}`;
344
+ const previewInput = body !== "" ? body : statement.target?.raw ?? "";
345
+ const preview = runtime !== "" ? `[${runtime}] ${previewInput}` : `$ ${previewInput}`;
312
346
  return { status: 202, body: preview, attrs }; // host runtime proposes with 202 — {§exec-host-proposes}
313
347
  }
314
348
  async applyResolution(args, ctx) {
315
349
  const core = this.coreContext(ctx);
316
350
  const attrs = args.attrs;
317
- let command = typeof attrs.command === "string" ? attrs.command : "";
351
+ const body = typeof attrs.body === "string" ? attrs.body : "";
318
352
  const pathname = attrs.pathname;
319
353
  const runtime = (typeof attrs.runtime === "string" && attrs.runtime !== "") ? attrs.runtime : "sh";
320
354
  const cwd = (typeof attrs.cwd === "string" && attrs.cwd.length > 0) ? attrs.cwd : null;
@@ -326,12 +360,11 @@ export default class Exec extends CoreSchemeAdapterBase {
326
360
  if (!EffectPolicy.isEffect(effect)) {
327
361
  throw new InvalidOperationResultError("The accepted EXEC proposal is missing its canonical effect fact.");
328
362
  }
329
- // Resolve a scheme target after acceptance: empty body uses the content
330
- // as the command; otherwise a temporary file becomes the data target.
331
- // {§exec-target-routing}
363
+ // Every non-file resource becomes a temporary executor target after
364
+ // acceptance; body presence never changes the target's role.
332
365
  let tempPath = null;
333
- if (attrs.schemeSource !== undefined) {
334
- const sourceTarget = parsePath(attrs.schemeSource);
366
+ if (attrs.resourceSource !== undefined) {
367
+ const sourceTarget = parsePath(attrs.resourceSource);
335
368
  if (sourceTarget?.kind !== "url" || sourceTarget.scheme === null || sourceTarget.scheme === "file") {
336
369
  throw new InvalidOperationResultError("The accepted EXEC proposal has an invalid scheme source address.");
337
370
  }
@@ -340,7 +373,7 @@ export default class Exec extends CoreSchemeAdapterBase {
340
373
  suffix: "",
341
374
  signal: null,
342
375
  target: sourceTarget,
343
- lineMarker: null,
376
+ lineMarker: { marks: [1, -1] },
344
377
  body: null,
345
378
  position: { line: 0, column: 0 },
346
379
  }, core);
@@ -355,22 +388,12 @@ export default class Exec extends CoreSchemeAdapterBase {
355
388
  retryable: false,
356
389
  });
357
390
  }
358
- if (command.length === 0) {
359
- command = content;
360
- }
361
- else {
362
- tempPath = join(tmpdir(), `plurnk-exec-${core.workspaceId}-${pathname.replace(/[^a-zA-Z0-9]/g, "-")}`);
363
- await writeFile(tempPath, content, "utf8");
364
- target = tempPath;
365
- }
391
+ tempPath = join(tmpdir(), `plurnk-exec-${core.workspaceId}-${pathname.replace(/[^a-zA-Z0-9]/g, "-")}`);
392
+ await writeFile(tempPath, content, "utf8");
393
+ target = tempPath;
366
394
  }
367
- // {§exec-target-routing} — a local file target is itself the program, so
368
- // its body may be empty; no target and no command remains invalid.
369
- if (command.length === 0 && target === null) {
370
- if (attrs.schemeSource !== undefined) {
371
- return Results.failure("scheme:exec", "source-command-empty", 422, "The EXEC source resolved to an empty command.", { outcome: "scheme_source_command_empty" }, { retryable: false });
372
- }
373
- throw new InvalidOperationResultError("The accepted EXEC proposal has neither a command nor a target.");
395
+ if (body.length === 0 && target === null) {
396
+ throw new InvalidOperationResultError("The accepted EXEC proposal has neither a body nor a realized target.");
374
397
  }
375
398
  // Resolve the runtime's executor from the boot registry, then seed
376
399
  // channels from its declared topology ({§executor-channels}). Each executor declares its own
@@ -392,7 +415,7 @@ export default class Exec extends CoreSchemeAdapterBase {
392
415
  state: decl.defaultState ?? "active",
393
416
  };
394
417
  }
395
- const seed = { channels: seedChannels, tags: [] };
418
+ const seed = { channels: seedChannels };
396
419
  // {§exec} — the stream entry's scheme IS the runtime tag (sh/node), so it addresses by
397
420
  // tag authority (sh:///l/t/s). The engine registers each runtime tag → this handler.
398
421
  const { entryId } = await EntryCrud.writeEntry(pathname, seed, core, runtime, core.workerId);
@@ -407,7 +430,7 @@ export default class Exec extends CoreSchemeAdapterBase {
407
430
  }
408
431
  const subscriptionId = await ChannelWrite.openSubscription(core.db, {
409
432
  workerId: core.workerId, entryId, scheme: runtime,
410
- handle: runtime !== "" ? `${runtime}: ${command}` : command,
433
+ handle: runtime !== "" ? `${runtime}: ${body !== "" ? body : target ?? ""}` : body,
411
434
  pollSeconds: typeof attrs.pollSec === "number" ? attrs.pollSec : null, // {§exec-poll} — hibernation wake cadence
412
435
  turnScoped: attrs.turnScoped === true, // {§exec-poll} — `<0>` reaped at the next pre-turn
413
436
  });
@@ -429,13 +452,13 @@ export default class Exec extends CoreSchemeAdapterBase {
429
452
  if (parent.aborted)
430
453
  controller.abort(ExecAbort.teardownReason());
431
454
  }
432
- this.#activeAborts.set(subscriptionId, { workerId: core.workerId, pathname, runtime, effect, controller, unlink });
455
+ this.#activeAborts.set(subscriptionId, { workerId: core.workerId, turnId: core.turnId, pathname, runtime, effect, controller, unlink });
433
456
  this.liveSubscriptions().register(subscriptionId, {
434
457
  cancel: () => controller.abort(ExecAbort.teardownReason()),
435
458
  });
436
459
  const tail = this.#runExecutor({
437
460
  executor: resolved.executor,
438
- runtime, command, cwd, target, ctx: core, pathname,
461
+ runtime, body, cwd, target, ctx: core, pathname,
439
462
  entryId, subscriptionId, signal: controller.signal, controller, tempPath,
440
463
  timeoutSec: typeof attrs.timeoutSec === "number" ? attrs.timeoutSec : null,
441
464
  });
@@ -459,7 +482,7 @@ export default class Exec extends CoreSchemeAdapterBase {
459
482
  // notify's reported state to "closed" before the chunk event fires
460
483
  // as "active." Chain through a single promise queue to serialize.
461
484
  async #runExecutor(opts) {
462
- const { executor, runtime, command, cwd, target, ctx, pathname, entryId, subscriptionId, signal, controller, timeoutSec, tempPath } = opts;
485
+ const { executor, runtime, body, cwd, target, ctx, pathname, entryId, subscriptionId, signal, controller, timeoutSec, tempPath } = opts;
463
486
  const db = ctx.db;
464
487
  const coordinate = coordinateFromPathname(pathname);
465
488
  // grammar 0.74.20 EXEC `<T>` — kill the spawn after T seconds. unref'd so a pending timer never
@@ -520,7 +543,12 @@ export default class Exec extends CoreSchemeAdapterBase {
520
543
  materialized = this.#fetchWeb(fetchAddress.url, { signal });
521
544
  }
522
545
  else {
523
- materialized = Promise.resolve({ body: content, mimetype: opts.mimetype });
546
+ materialized = Promise.resolve({
547
+ url: fetchAddress?.url ?? "http://localhost",
548
+ body: content,
549
+ mimetype: opts.mimetype,
550
+ allowTavily: false,
551
+ });
524
552
  }
525
553
  const op = async () => {
526
554
  const fetched = await materialized;
@@ -538,19 +566,21 @@ export default class Exec extends CoreSchemeAdapterBase {
538
566
  }
539
567
  if (web === null)
540
568
  throw new Error(`entry(): '${path.slice(0, 80)}' has no readable projection`);
541
- const prior = await EntryCrud.readEntry(pathname, ctx, scheme);
542
- const tags = [...new Set([...(prior.entry?.tags ?? []), ...opts.tags])];
569
+ if (web.body === undefined) {
570
+ throw new Error(web.bodyOutcome.failure?.detail
571
+ ?? `entry(): '${path.slice(0, 80)}' produced no readable body`);
572
+ }
573
+ const tags = TagSignal.applied(opts.tags.map((tag) => `+${tag}`)).add;
574
+ const tagSignal = tags.map((tag) => `+${tag}`);
543
575
  // {§exec-entry-sink}/{§html-materialization} The shared
544
576
  // materializer owns the decisive projection and its provenance.
545
- const channels = {
546
- body: web.body,
547
- ...(web.html === undefined ? {} : { html: web.html }),
548
- ...(web.header === undefined ? {} : { header: { content: web.header, mimetype: "text/plain" } }),
549
- };
577
+ const channels = WebFetcher.materializedChannels(web, content === null && fetchAddress !== null
578
+ ? { url: fetchAddress.url, method: "GET" }
579
+ : undefined);
550
580
  const decisive = web.body.content;
551
581
  const source = web.html?.content ?? decisive;
552
582
  const causalSource = await resolveCallerSource();
553
- const written = Results.assert(await EntryCrud.writeEntry(pathname, { channels, tags }, ctx, scheme));
583
+ const written = Results.assert(await EntryCrud.writeEntry(pathname, { channels }, ctx, scheme));
554
584
  if (narration === null) {
555
585
  const worker = await db.envelope_get_worker_by_name.get({ workspace_id: ctx.workspaceId, name: "plurnk" })
556
586
  ?? await db.envelope_insert_worker.get({ workspace_id: ctx.workspaceId, name: "plurnk", origin: "plurnk" });
@@ -570,21 +600,23 @@ export default class Exec extends CoreSchemeAdapterBase {
570
600
  };
571
601
  }
572
602
  const sequence = narration.seq++;
603
+ const narrationAttrs = { kind: "entry_materialized" };
573
604
  if (written.problem !== undefined) {
574
- Results.attachInstance(written, `log:///${narration.loopSeq}/${narration.turnSeq}/${sequence}/EDIT`);
605
+ const coordinate = LogEntryProjection.coordinate(`${narration.loopSeq}/${narration.turnSeq}/${sequence}`, { origin: "plurnk", op: "EDIT", attrs: narrationAttrs });
606
+ Results.attachInstance(written, `log:///${coordinate}`);
575
607
  }
576
- await db.engine_insert_log_entry.get({
608
+ const logRow = await db.engine_insert_log_entry.get({
577
609
  worker_id: narration.workerId, loop_id: narration.loopId, turn_id: narration.turnId, sequence,
578
- // signal carries the tags — the SAME slot a model's EDIT[tags] uses, so the
610
+ // signal carries additive tag terms through the same slot a model's EDIT uses, so the
579
611
  // ambient row renders its tags natively everywhere (packet meta line, digest).
580
- origin: "plurnk", source: causalSource, op: "EDIT", suffix: "", signal: JSON.stringify(tags),
612
+ origin: "plurnk", source: causalSource, model_call_id: null,
613
+ op: "EDIT", suffix: "", signal: JSON.stringify(tagSignal),
581
614
  scheme, username: null, password: null, hostname: null, port: null,
582
615
  pathname, query: null, fragment: null, lineMarker: null,
583
616
  tx: JSON.stringify({ op: "EDIT", body: source }), mimetype_tx: "application/json",
584
617
  rx: JSON.stringify(written.problem === undefined
585
618
  ? {
586
619
  ...written,
587
- tags,
588
620
  span: decisive.split("\n").map((l, n) => `${n + 1}:${l}`).join("\n"),
589
621
  }
590
622
  : written),
@@ -592,8 +624,10 @@ export default class Exec extends CoreSchemeAdapterBase {
592
624
  status_rx: written.status, tokens: ctx.tokenize?.(decisive) ?? 0, state: "resolved", outcome: null,
593
625
  // Durable provenance for clients/forensics. This is machine
594
626
  // ambience, not a human/model action waterfall item.
595
- attrs: JSON.stringify({ tags, kind: "entry_materialized" }),
627
+ attrs: JSON.stringify(narrationAttrs),
596
628
  });
629
+ if (logRow === undefined)
630
+ throw new Error("entry(): log insert returned no row");
597
631
  if (written.problem !== undefined)
598
632
  throw new OperationFailureError(written);
599
633
  return renderAddress(scheme, pathname);
@@ -605,11 +639,11 @@ export default class Exec extends CoreSchemeAdapterBase {
605
639
  try {
606
640
  try {
607
641
  const reported = await executor.run({
608
- runtime, command, cwd, target, signal,
642
+ runtime, body, cwd, target, signal,
609
643
  entry: entrySink,
610
644
  env: ExecEnv.scoped(), // SPEC {§exec} {§exec-env-scoped} — never plurnk's own secrets
611
- write: (channel, chunk) => enqueue(() => ChannelWrite.appendToChannel(db, {
612
- entryId, channel, chunk, notify: ctx.streamEventNotify, coordinate,
645
+ write: (channel, chunk, mimetype) => enqueue(() => ChannelWrite.appendToChannel(db, {
646
+ entryId, channel, chunk, mimetype, notify: ctx.streamEventNotify, coordinate,
613
647
  })),
614
648
  setState: (channel, state) => enqueue(() => ChannelWrite.setChannelState(db, {
615
649
  entryId, channel, state, notify: ctx.streamEventNotify, coordinate,
@@ -695,7 +729,7 @@ export default class Exec extends CoreSchemeAdapterBase {
695
729
  if (ctx.wakeWorkerNotify !== undefined) {
696
730
  ctx.wakeWorkerNotify({
697
731
  workspaceId: ctx.workspaceId, workerId: ctx.workerId,
698
- entryId, target: `${runtime}://${pathname}`, subscriptionId, result,
732
+ entryOwnerId: ctx.workerId, entryId, target: `${runtime}://${pathname}`, subscriptionId, result,
699
733
  scheme: runtime,
700
734
  summary: `${runtime}://${pathname} completed (${exitLabel}); stdout=${stdoutLength} bytes, stderr=${stderrLength} bytes`,
701
735
  ...coordinate,
@@ -704,25 +738,24 @@ export default class Exec extends CoreSchemeAdapterBase {
704
738
  }
705
739
  return result;
706
740
  }
707
- async read(statement, ctx) {
708
- const core = this.coreContext(ctx);
709
- const owner = await resolveStreamStatement(statement, core);
710
- if (owner === null) {
711
- return Results.failure("scheme:exec", "stream-not-found", 404, "No visible stream exists at the requested address.", {
712
- content: null, mimetype: null, channel: null,
713
- });
714
- }
715
- return EntryOps.readWorkspaceEntry(owner.statement, core, Exec.manifest, owner.ownerId);
741
+ async resolveEntryAddress(target, ctx) {
742
+ if (target.kind !== "url")
743
+ return null;
744
+ const ownerId = await Owner.resolveStreamOwner(target.hostname, this.coreContext(ctx));
745
+ return ownerId === null
746
+ ? Results.failure("scheme:exec", "stream-not-found", 404, "No visible stream exists at the requested address.")
747
+ : { pathname: target.pathname, ownerId };
716
748
  }
717
749
  async find(statement, ctx) {
718
750
  const core = this.coreContext(ctx);
719
751
  const owner = await resolveStreamStatement(statement, core);
720
752
  if (owner === null) {
721
753
  return Results.failure("scheme:exec", "stream-not-found", 404, "No visible stream exists at the requested address.", {
722
- content: null, mimetype: null, results: [], itemsTokenTotal: 0, pathnames: [], matches: [],
754
+ content: null, mimetype: null, results: [], itemsTokenTotal: 0, returnedItemsTokenTotal: 0,
755
+ matchingPathCount: 0, matchLocationCount: 0,
723
756
  });
724
757
  }
725
- return EntryFind.findWorkspaceEntries(owner.statement, core, Exec.manifest, owner.ownerId);
758
+ return EntryFind.findWorkspaceEntries(owner.statement, core, Exec.manifest, { ownerId: owner.ownerId });
726
759
  }
727
760
  async readEntry(pathname, ctx) {
728
761
  const core = this.coreContext(ctx);