@astrosheep/keiyaku 4.1.0 → 4.1.2

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 (278) hide show
  1. package/README.md +1 -1
  2. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +3 -0
  3. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +3 -0
  4. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +3 -6
  5. package/build/src/akuma/akuma-errors.d.ts +6 -0
  6. package/build/src/akuma/akuma-errors.js +9 -0
  7. package/build/src/akuma/akuma-handle.d.ts +39 -0
  8. package/build/src/akuma/akuma-handle.js +263 -0
  9. package/build/src/akuma/akuma-observe.d.ts +19 -0
  10. package/build/src/akuma/akuma-observe.js +74 -0
  11. package/build/src/akuma/akuma-product-symbols.d.ts +1 -0
  12. package/build/src/akuma/akuma-product-symbols.js +1 -0
  13. package/build/src/akuma/akuma-product.d.ts +18 -0
  14. package/build/src/akuma/akuma-product.js +154 -0
  15. package/build/src/akuma/akuma.d.ts +37 -81
  16. package/build/src/akuma/akuma.js +39 -447
  17. package/build/src/akuma/allowed.d.ts +1 -0
  18. package/build/src/akuma/allowed.js +6 -0
  19. package/build/src/akuma/archetype.js +8 -5
  20. package/build/src/akuma/body-supervisor.d.ts +26 -0
  21. package/build/src/akuma/body-supervisor.js +89 -0
  22. package/build/src/akuma/body.d.ts +45 -4
  23. package/build/src/akuma/body.js +145 -35
  24. package/build/src/akuma/coordinate.js +17 -7
  25. package/build/src/akuma/heart/facts.js +1 -1
  26. package/build/src/akuma/heart/index.d.ts +1 -1
  27. package/build/src/akuma/heart/index.js +2 -4
  28. package/build/src/akuma/heart/request-authority.js +4 -4
  29. package/build/src/akuma/heart/request-rows.js +27 -16
  30. package/build/src/akuma/heart/rows.js +79 -41
  31. package/build/src/akuma/heart/soul.js +1 -1
  32. package/build/src/akuma/heart/storage.d.ts +1 -0
  33. package/build/src/akuma/heart/storage.js +76 -11
  34. package/build/src/akuma/heart/tells.js +35 -18
  35. package/build/src/akuma/heart/timeline.js +19 -11
  36. package/build/src/akuma/identity.d.ts +1 -0
  37. package/build/src/akuma/identity.js +1 -0
  38. package/build/src/akuma/index.d.ts +3 -3
  39. package/build/src/akuma/index.js +1 -1
  40. package/build/src/akuma/nuke.js +106 -14
  41. package/build/src/akuma/projection-read.d.ts +17 -0
  42. package/build/src/akuma/projection-read.js +135 -0
  43. package/build/src/akuma/projection.d.ts +1 -16
  44. package/build/src/akuma/projection.js +77 -154
  45. package/build/src/akuma/provider-recipe.js +11 -5
  46. package/build/src/akuma/provider.js +105 -62
  47. package/build/src/akuma/providers/acp/config.js +5 -6
  48. package/build/src/akuma/providers/acp/core.js +17 -10
  49. package/build/src/akuma/providers/acp/events.js +38 -29
  50. package/build/src/akuma/providers/acp/index.js +13 -9
  51. package/build/src/akuma/providers/claude/events.js +8 -12
  52. package/build/src/akuma/providers/claude/index.d.ts +1 -0
  53. package/build/src/akuma/providers/claude/index.js +55 -26
  54. package/build/src/akuma/providers/claude/input.js +6 -2
  55. package/build/src/akuma/providers/codex-app-server/events.js +61 -36
  56. package/build/src/akuma/providers/codex-app-server/index.js +25 -13
  57. package/build/src/akuma/providers/grok-build/index.js +22 -25
  58. package/build/src/akuma/providers/index.js +2 -18
  59. package/build/src/akuma/providers/opencode-sdk/events.js +32 -18
  60. package/build/src/akuma/providers/opencode-sdk/index.js +26 -17
  61. package/build/src/akuma/providers/opencode-sdk/session.js +21 -5
  62. package/build/src/akuma/providers/pi/events.js +26 -19
  63. package/build/src/akuma/providers/pi/index.js +58 -40
  64. package/build/src/akuma/publication.js +9 -9
  65. package/build/src/akuma/request-execution.d.ts +6 -0
  66. package/build/src/akuma/request-execution.js +86 -0
  67. package/build/src/akuma/request-serve.d.ts +12 -1
  68. package/build/src/akuma/request-serve.js +19 -122
  69. package/build/src/akuma/request-wire.d.ts +2 -2
  70. package/build/src/akuma/request-wire.js +75 -102
  71. package/build/src/akuma/requests.js +2 -2
  72. package/build/src/akuma/{body-turn.d.ts → turn-drive.d.ts} +2 -25
  73. package/build/src/akuma/{body-turn.js → turn-drive.js} +20 -103
  74. package/build/src/akuma-body.js +6 -5
  75. package/build/src/body/amend.d.ts +4 -1
  76. package/build/src/body/amend.js +14 -6
  77. package/build/src/body/decode.js +2 -4
  78. package/build/src/body/envelope.js +2 -4
  79. package/build/src/body/region.d.ts +1 -2
  80. package/build/src/body/region.js +2 -70
  81. package/build/src/body/render.js +17 -12
  82. package/build/src/body/shape.js +1 -7
  83. package/build/src/cli/accepted.d.ts +1 -1
  84. package/build/src/cli/accepted.js +8 -1
  85. package/build/src/cli/commands/akuma-invoke.js +18 -6
  86. package/build/src/cli/commands/akuma.d.ts +0 -6
  87. package/build/src/cli/commands/akuma.js +11 -24
  88. package/build/src/cli/commands/amend.d.ts +2 -2
  89. package/build/src/cli/commands/amend.js +2 -4
  90. package/build/src/cli/commands/bind.d.ts +4 -4
  91. package/build/src/cli/commands/bind.js +16 -6
  92. package/build/src/cli/commands/contract-invoke.d.ts +25 -0
  93. package/build/src/cli/commands/contract-invoke.js +193 -0
  94. package/build/src/cli/commands/contract.d.ts +109 -10
  95. package/build/src/cli/commands/contract.js +339 -16
  96. package/build/src/cli/commands/install.d.ts +1 -1
  97. package/build/src/cli/commands/install.js +23 -15
  98. package/build/src/cli/commands/task-invoke.js +187 -67
  99. package/build/src/cli/commands/task-query.js +48 -24
  100. package/build/src/cli/commands/task.js +150 -38
  101. package/build/src/cli/coordinates.js +2 -4
  102. package/build/src/cli/invoke.js +92 -208
  103. package/build/src/cli/main.js +4 -2
  104. package/build/src/cli/parse.d.ts +3 -90
  105. package/build/src/cli/parse.js +27 -233
  106. package/build/src/cli/render/akuma-activity.d.ts +38 -0
  107. package/build/src/cli/render/akuma-activity.js +362 -0
  108. package/build/src/cli/render/akuma-tool-command.js +6 -7
  109. package/build/src/cli/render/akuma-tool.js +10 -8
  110. package/build/src/cli/render/akuma.d.ts +1 -1
  111. package/build/src/cli/render/akuma.js +61 -353
  112. package/build/src/cli/render/audit.js +5 -15
  113. package/build/src/cli/render/board.d.ts +1 -0
  114. package/build/src/cli/render/board.js +17 -0
  115. package/build/src/cli/render/catalog.js +81 -8
  116. package/build/src/cli/render/contract-history.d.ts +2 -0
  117. package/build/src/cli/render/contract-history.js +83 -0
  118. package/build/src/cli/render/contract-observation.d.ts +10 -0
  119. package/build/src/cli/render/contract-observation.js +58 -0
  120. package/build/src/cli/render/contract.d.ts +1 -2
  121. package/build/src/cli/render/contract.js +41 -142
  122. package/build/src/cli/render/kanshi.js +239 -215
  123. package/build/src/cli/render/receipt.js +2 -9
  124. package/build/src/cli/render/refusal.js +10 -15
  125. package/build/src/cli/render/region.js +22 -6
  126. package/build/src/cli/render/settings.js +4 -4
  127. package/build/src/cli/render/task.js +34 -14
  128. package/build/src/cli/render/terminal.js +16 -5
  129. package/build/src/cli/result.d.ts +3 -3
  130. package/build/src/cli/runtime.d.ts +6 -1
  131. package/build/src/cli/runtime.js +84 -63
  132. package/build/src/cli/selectors.js +2 -4
  133. package/build/src/cli/usage.js +6 -5
  134. package/build/src/contract-worktree.d.ts +1 -23
  135. package/build/src/contract-worktree.js +79 -155
  136. package/build/src/coordination/durable-file.js +7 -3
  137. package/build/src/coordination/sqlite-transaction-lock.js +29 -12
  138. package/build/src/core/facts/codec.js +7 -3
  139. package/build/src/core/facts/types.d.ts +1 -1
  140. package/build/src/core/subject.js +16 -9
  141. package/build/src/core/verbs/abandon.d.ts +1 -1
  142. package/build/src/core/verbs/abandon.js +1 -1
  143. package/build/src/core/verbs/amend.d.ts +1 -1
  144. package/build/src/core/verbs/amend.js +7 -7
  145. package/build/src/core/verbs/attestation.js +4 -1
  146. package/build/src/core/verbs/bind.d.ts +1 -1
  147. package/build/src/core/verbs/bind.js +1 -1
  148. package/build/src/core/verbs/placement.d.ts +1 -1
  149. package/build/src/core/verbs/placement.js +10 -8
  150. package/build/src/dispatch/index.js +8 -6
  151. package/build/src/duration.js +5 -1
  152. package/build/src/git/admission.js +5 -3
  153. package/build/src/git/hooks.d.ts +8 -0
  154. package/build/src/git/hooks.js +63 -14
  155. package/build/src/git/identity.js +1 -4
  156. package/build/src/git/integration.d.ts +2 -3
  157. package/build/src/git/integration.js +32 -39
  158. package/build/src/git/nuke.d.ts +1 -1
  159. package/build/src/git/nuke.js +110 -57
  160. package/build/src/git/observe.d.ts +13 -1
  161. package/build/src/git/observe.js +51 -44
  162. package/build/src/git/process.js +4 -2
  163. package/build/src/git/read-observation.js +10 -8
  164. package/build/src/git/reconcile.d.ts +22 -2
  165. package/build/src/git/reconcile.js +38 -274
  166. package/build/src/git/repository.d.ts +2 -0
  167. package/build/src/git/repository.js +13 -22
  168. package/build/src/git/scratch.d.ts +8 -5
  169. package/build/src/git/scratch.js +29 -2
  170. package/build/src/git/target-placement.d.ts +9 -10
  171. package/build/src/git/target-placement.js +59 -62
  172. package/build/src/git/tender.js +13 -46
  173. package/build/src/git/terminal-reconcile.d.ts +21 -0
  174. package/build/src/git/terminal-reconcile.js +278 -0
  175. package/build/src/git/terminal-seal.js +15 -12
  176. package/build/src/git/tree.js +5 -2
  177. package/build/src/git/workspace.d.ts +24 -2
  178. package/build/src/git/workspace.js +120 -16
  179. package/build/src/identity/coordinates.js +3 -1
  180. package/build/src/identity/selector.js +6 -3
  181. package/build/src/kanshi/fleet.d.ts +6 -0
  182. package/build/src/kanshi/fleet.js +27 -0
  183. package/build/src/kanshi/index.d.ts +1 -1
  184. package/build/src/kanshi/read.js +60 -48
  185. package/build/src/kanshi/report.d.ts +11 -23
  186. package/build/src/kanshi/select.js +25 -24
  187. package/build/src/library/address.d.ts +2 -1
  188. package/build/src/library/address.js +7 -8
  189. package/build/src/library/akuma-creation.js +20 -12
  190. package/build/src/library/audit.d.ts +0 -1
  191. package/build/src/library/audit.js +1 -2
  192. package/build/src/library/bind.d.ts +53 -5
  193. package/build/src/library/bind.js +98 -22
  194. package/build/src/library/catalog.d.ts +1 -0
  195. package/build/src/library/catalog.js +6 -3
  196. package/build/src/library/configuration.js +3 -1
  197. package/build/src/library/continuation.d.ts +15 -4
  198. package/build/src/library/continuation.js +24 -16
  199. package/build/src/library/contract-bind.d.ts +26 -0
  200. package/build/src/library/contract-bind.js +106 -0
  201. package/build/src/library/contract-operations.d.ts +77 -0
  202. package/build/src/library/contract-operations.js +130 -0
  203. package/build/src/library/contract-types.d.ts +95 -0
  204. package/build/src/library/contract-types.js +1 -0
  205. package/build/src/library/contract.d.ts +19 -154
  206. package/build/src/library/contract.js +46 -261
  207. package/build/src/library/fleet.d.ts +1 -1
  208. package/build/src/library/fleet.js +19 -11
  209. package/build/src/library/input.d.ts +2 -0
  210. package/build/src/library/input.js +14 -0
  211. package/build/src/library/keiyaku.d.ts +8 -7
  212. package/build/src/library/keiyaku.js +3 -3
  213. package/build/src/library/nuke.js +14 -5
  214. package/build/src/library/reconcile.d.ts +9 -4
  215. package/build/src/library/reconcile.js +30 -24
  216. package/build/src/library/refusal.d.ts +13 -5
  217. package/build/src/library/refusal.js +7 -0
  218. package/build/src/library/region.d.ts +8 -7
  219. package/build/src/library/region.js +31 -11
  220. package/build/src/library/repo.js +2 -2
  221. package/build/src/markdown/lex.js +9 -2
  222. package/build/src/markdown/parse.js +11 -7
  223. package/build/src/markdown/query.js +4 -2
  224. package/build/src/protocol/amend.js +3 -1
  225. package/build/src/protocol/attempt.d.ts +7 -3
  226. package/build/src/protocol/attempt.js +9 -5
  227. package/build/src/protocol/audit.d.ts +1 -1
  228. package/build/src/protocol/audit.js +4 -14
  229. package/build/src/protocol/bind.d.ts +19 -6
  230. package/build/src/protocol/bind.js +28 -13
  231. package/build/src/protocol/completion.d.ts +0 -1
  232. package/build/src/protocol/completion.js +1 -3
  233. package/build/src/protocol/deliver.d.ts +1 -3
  234. package/build/src/protocol/deliver.js +15 -39
  235. package/build/src/protocol/intent.d.ts +1 -0
  236. package/build/src/protocol/intent.js +9 -7
  237. package/build/src/protocol/operations.d.ts +3 -7
  238. package/build/src/protocol/operations.js +5 -3
  239. package/build/src/protocol/placement.d.ts +0 -1
  240. package/build/src/protocol/placement.js +6 -6
  241. package/build/src/protocol/read/observation.d.ts +11 -0
  242. package/build/src/protocol/read/observation.js +37 -0
  243. package/build/src/protocol/read/status.d.ts +23 -12
  244. package/build/src/protocol/read/status.js +90 -72
  245. package/build/src/protocol/reconcile.d.ts +1 -0
  246. package/build/src/protocol/reconcile.js +2 -3
  247. package/build/src/protocol/reintegrate.js +1 -1
  248. package/build/src/protocol/review.d.ts +0 -1
  249. package/build/src/protocol/review.js +15 -22
  250. package/build/src/protocol/run.d.ts +1 -0
  251. package/build/src/protocol/run.js +4 -5
  252. package/build/src/runtime/proc/launch.d.ts +33 -0
  253. package/build/src/runtime/proc/launch.js +98 -0
  254. package/build/src/runtime/proc/line-rpc.js +9 -3
  255. package/build/src/runtime/proc/run.d.ts +12 -6
  256. package/build/src/runtime/proc/run.js +78 -21
  257. package/build/src/runtime/proc/stdio.js +7 -10
  258. package/build/src/settings.js +6 -2
  259. package/build/src/settlement/holder.js +13 -10
  260. package/build/src/settlement/settle.js +39 -6
  261. package/build/src/task/board.js +12 -6
  262. package/build/src/task/compose.js +85 -22
  263. package/build/src/task/context.js +21 -8
  264. package/build/src/task/document.js +46 -10
  265. package/build/src/task/identity.js +5 -2
  266. package/build/src/task/index.d.ts +0 -1
  267. package/build/src/task/index.js +119 -34
  268. package/build/src/task/input.js +29 -7
  269. package/build/src/task/mutation.js +61 -19
  270. package/build/src/task/operations.d.ts +6 -1
  271. package/build/src/task/operations.js +83 -29
  272. package/build/src/task/query.js +55 -26
  273. package/build/src/task/store.d.ts +3 -1
  274. package/build/src/task/store.js +81 -17
  275. package/build/src/verification/execution.js +14 -11
  276. package/build/src/workspace-place.js +19 -7
  277. package/build/src/world.js +16 -6
  278. package/package.json +8 -3
@@ -1,9 +1,10 @@
1
- import { lstat, readdir, rm } from "node:fs/promises";
1
+ import { lstat, readdir, rm, rmdir } from "node:fs/promises";
2
+ import { dirname, join } from "node:path";
2
3
  import { nukeAliases } from "../alias/index.js";
3
4
  import { CONTROL_RESPONSE_MS } from "./body.js";
4
- import { HeldAkumaLeash, readHeart, readKill, requestStop, } from "./heart/index.js";
5
- import { akuIdFromDirectoryName, akumaPaths, akumaRunRoot, } from "./identity.js";
6
- const POLL_MS = 25;
5
+ import { HeldAkumaLeash, readHeart, readKill, requestStop } from "./heart/index.js";
6
+ import { akuIdFromDirectoryName, akumaPaths, akumaRunRoot } from "./identity.js";
7
+ const POLL_MS = 100;
7
8
  async function hasAkumaCustody(paths) {
8
9
  try {
9
10
  const value = await lstat(paths.heart);
@@ -25,7 +26,7 @@ async function takeLeashUntil(paths, deadline) {
25
26
  return leash;
26
27
  if (performance.now() >= deadline)
27
28
  return null;
28
- await wait(POLL_MS);
29
+ await wait(Math.min(POLL_MS, Math.max(0, deadline - performance.now())));
29
30
  }
30
31
  }
31
32
  async function stopRunningAkuma(entry) {
@@ -36,11 +37,11 @@ async function stopRunningAkuma(entry) {
36
37
  try {
37
38
  if (request.kind === "already-killed" || request.kind === "already-stopped")
38
39
  return leash;
39
- if (await readKill(entry.paths, request.body.sequence) !== null)
40
+ if ((await readKill(entry.paths, request.body.sequence)) !== null)
40
41
  return leash;
41
42
  const current = await readHeart(entry.paths);
42
43
  if (current.latestBody?.sequence !== request.body.sequence) {
43
- if (await readKill(entry.paths, request.body.sequence) !== null)
44
+ if ((await readKill(entry.paths, request.body.sequence)) !== null)
44
45
  return leash;
45
46
  throw new Error(`Akuma ${entry.id} could not be stopped: unavailable`);
46
47
  }
@@ -48,7 +49,7 @@ async function stopRunningAkuma(entry) {
48
49
  await leash.clearStop(entry.paths);
49
50
  throw new Error(`Akuma ${entry.id} could not be stopped: untidy`);
50
51
  }
51
- if (await leash.settleStop(entry.paths, request.body.sequence) === null) {
52
+ if ((await leash.settleStop(entry.paths, request.body.sequence)) === null) {
52
53
  throw new Error(`Akuma ${entry.id} could not be stopped: unavailable`);
53
54
  }
54
55
  return leash;
@@ -58,22 +59,112 @@ async function stopRunningAkuma(entry) {
58
59
  throw error;
59
60
  }
60
61
  }
61
- async function removeRegularFile(path) {
62
+ async function removeKnownRegularFile(path) {
62
63
  try {
63
64
  const value = await lstat(path);
64
- if (value.isFile() && !value.isSymbolicLink())
65
- await rm(path, { force: true });
65
+ if (!value.isFile() || value.isSymbolicLink())
66
+ throw new Error(`Akuma custody is not a regular file: ${path}`);
67
+ await rm(path, { force: true });
66
68
  }
67
69
  catch (error) {
68
70
  if (error.code !== "ENOENT")
69
71
  throw error;
70
72
  }
71
73
  }
74
+ async function removeEmptyDirectory(path) {
75
+ try {
76
+ await rmdir(path);
77
+ }
78
+ catch (error) {
79
+ if (error.code !== "ENOENT" && error.code !== "ENOTEMPTY")
80
+ throw error;
81
+ }
82
+ }
83
+ function isRequestSequenceName(name) {
84
+ const sequence = Number(name);
85
+ return /^\d+$/u.test(name) && Number.isSafeInteger(sequence) && String(sequence) === name;
86
+ }
87
+ const UUID_V4 = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u;
88
+ function isRequestArtifactName(name) {
89
+ const match = /^(.*)\.(?:request|receipt)\.json$/u.exec(name);
90
+ return match !== null && UUID_V4.test(match[1]);
91
+ }
92
+ async function removeKnownRequestFile(path) {
93
+ try {
94
+ const value = await lstat(path);
95
+ if (!value.isFile() || value.isSymbolicLink())
96
+ return;
97
+ await rm(path, { force: true });
98
+ }
99
+ catch (error) {
100
+ if (error.code !== "ENOENT")
101
+ throw error;
102
+ }
103
+ }
104
+ async function removeKnownRequestChannel(path) {
105
+ try {
106
+ const value = await lstat(path);
107
+ if (!value.isDirectory() || value.isSymbolicLink())
108
+ throw new Error(`Akuma custody is not a directory: ${path}`);
109
+ }
110
+ catch (error) {
111
+ if (error.code === "ENOENT")
112
+ return;
113
+ throw error;
114
+ }
115
+ for (const name of await readdir(path)) {
116
+ if (!isRequestSequenceName(name))
117
+ continue;
118
+ const sequenceDir = join(path, name);
119
+ try {
120
+ const sequence = await lstat(sequenceDir);
121
+ if (!sequence.isDirectory() || sequence.isSymbolicLink())
122
+ continue;
123
+ for (const child of await readdir(sequenceDir)) {
124
+ if (isRequestArtifactName(child))
125
+ await removeKnownRequestFile(join(sequenceDir, child));
126
+ }
127
+ await removeEmptyDirectory(sequenceDir);
128
+ }
129
+ catch (error) {
130
+ if (error.code !== "ENOENT")
131
+ throw error;
132
+ }
133
+ }
134
+ await removeEmptyDirectory(path);
135
+ }
72
136
  async function removeAkumaEntry(entry) {
73
- for (const path of [entry.paths.heart, entry.paths.log,
74
- `${entry.paths.heart}-wal`, `${entry.paths.heart}-shm`]) {
75
- await removeRegularFile(path);
137
+ await removeKnownRequestChannel(entry.paths.requests);
138
+ for (const path of [
139
+ entry.paths.log,
140
+ `${entry.paths.heart}-wal`,
141
+ `${entry.paths.heart}-shm`,
142
+ `${entry.paths.leash}-wal`,
143
+ `${entry.paths.leash}-shm`,
144
+ entry.paths.leash,
145
+ entry.paths.heart,
146
+ ]) {
147
+ await removeKnownRegularFile(path);
148
+ }
149
+ await removeEmptyDirectory(entry.paths.directory);
150
+ }
151
+ async function removeEmptyAkumaRunRoot(world) {
152
+ const runRoot = akumaRunRoot(world);
153
+ let names;
154
+ try {
155
+ names = await readdir(runRoot);
156
+ }
157
+ catch (error) {
158
+ if (error.code === "ENOENT")
159
+ return;
160
+ throw error;
76
161
  }
162
+ if (names.some((name) => name !== ".gitignore"))
163
+ return;
164
+ if (names.includes(".gitignore"))
165
+ await removeKnownRegularFile(join(runRoot, ".gitignore"));
166
+ await removeEmptyDirectory(runRoot);
167
+ await removeEmptyDirectory(dirname(runRoot));
77
168
  }
78
169
  async function nukeAkumaEntries(world) {
79
170
  const runRoot = akumaRunRoot(world);
@@ -136,6 +227,7 @@ export async function stopAkuma(world) {
136
227
  await nukeAliases(world);
137
228
  for (const entry of entries)
138
229
  await removeAkumaEntry(entry);
230
+ await removeEmptyAkumaRunRoot(world);
139
231
  }
140
232
  finally {
141
233
  for (const leash of held.reverse())
@@ -0,0 +1,17 @@
1
+ import type { ActivitySnapshot, TurnLedger } from "./projection.js";
2
+ import type { HistoryCursor, HistoryPage } from "./projection.js";
3
+ export declare function ordinarySnapshotBudget(ordinaryBudget?: number): Readonly<{
4
+ tail: number;
5
+ voice: number;
6
+ }>;
7
+ export declare function selectSnapshot(ledger: TurnLedger, input: Readonly<{
8
+ aperture: "monitoring" | "receipt";
9
+ budget?: Readonly<{
10
+ tail: number;
11
+ voice?: number;
12
+ }>;
13
+ }>): Readonly<{
14
+ snapshot: ActivitySnapshot;
15
+ ordinaryCount: number;
16
+ }>;
17
+ export declare function selectHistory(ledger: TurnLedger, cursor: HistoryCursor): HistoryPage;
@@ -0,0 +1,135 @@
1
+ const DEFAULT_TAIL = 3;
2
+ const DEFAULT_VOICE = 3;
3
+ const REPORTED_CHANGE_LIMIT = 5;
4
+ const EMPTY_REPORTED = { reportedChanges: [], reportedChangesOmitted: 0 };
5
+ function assembleEntries(ledger, window, selected) {
6
+ const windowRows = new Set(window);
7
+ const result = [];
8
+ let hidden = 0;
9
+ const flushGap = () => {
10
+ if (hidden === 0)
11
+ return;
12
+ result.push({ kind: "gap", count: hidden });
13
+ hidden = 0;
14
+ };
15
+ for (const row of ledger.rows) {
16
+ if (windowRows.has(row) && !selected.has(row)) {
17
+ hidden += 1;
18
+ continue;
19
+ }
20
+ if (!selected.has(row))
21
+ continue;
22
+ flushGap();
23
+ if (row.kind !== "turn" && row.kind !== "outcome" && !(row.kind === "tool" && row.state === "unsettled")) {
24
+ result.push({ kind: "row", row: row });
25
+ }
26
+ }
27
+ flushGap();
28
+ return result;
29
+ }
30
+ const DEFAULT_ORDINARY = DEFAULT_TAIL + DEFAULT_VOICE;
31
+ export function ordinarySnapshotBudget(ordinaryBudget = DEFAULT_ORDINARY) {
32
+ const tail = Math.min(DEFAULT_TAIL, Math.max(0, ordinaryBudget));
33
+ return { tail, voice: Math.min(DEFAULT_VOICE, Math.max(0, ordinaryBudget) - tail) };
34
+ }
35
+ function isSuccessfulFileChange(row) {
36
+ return (row.kind === "tool" && typeof row.state === "object" && row.state.status === "ok" && row.call.kind === "fileChange");
37
+ }
38
+ function flattenReportedChanges(rows) {
39
+ return rows.flatMap((row) => {
40
+ if (!isSuccessfulFileChange(row))
41
+ return [];
42
+ return row.call.changes.map((change) => ({
43
+ sequence: row.sequence,
44
+ at: row.at,
45
+ op: change.op,
46
+ path: change.path,
47
+ ...(change.diffstat === undefined ? {} : { diffstat: change.diffstat }),
48
+ }));
49
+ });
50
+ }
51
+ function summarizeReportedChanges(rows) {
52
+ const eligible = flattenReportedChanges(rows);
53
+ return {
54
+ reportedChanges: eligible.slice(-REPORTED_CHANGE_LIMIT),
55
+ reportedChangesOmitted: Math.max(0, eligible.length - REPORTED_CHANGE_LIMIT),
56
+ };
57
+ }
58
+ function frontierReportedChanges(ledger) {
59
+ if (ledger.openTurn !== undefined)
60
+ return summarizeReportedChanges(ledger.openTurn.rows);
61
+ const closed = ledger.turns.findLast((turn) => turn.kind === "closed");
62
+ return closed === undefined ? EMPTY_REPORTED : summarizeReportedChanges(closed.rows);
63
+ }
64
+ function isPendingTell(row) {
65
+ return row.kind === "tell" && row.state === "pending";
66
+ }
67
+ function isToldTell(row) {
68
+ return row.kind === "tell" && row.state === "told";
69
+ }
70
+ function isActiveTool(row) {
71
+ return row.kind === "tool" && row.state === "active";
72
+ }
73
+ export function selectSnapshot(ledger, input) {
74
+ const budget = input.budget ?? { tail: DEFAULT_TAIL, voice: DEFAULT_VOICE };
75
+ if (ledger.turns.length === 0 && !ledger.rows.some((row) => row.kind === "tell"))
76
+ return { snapshot: { kind: "unborn", entries: [], omitted: 0, ...EMPTY_REPORTED }, ordinaryCount: 0 };
77
+ const pending = ledger.rows.filter(isPendingTell);
78
+ const latestTold = input.aperture === "monitoring" ? ledger.rows.findLast(isToldTell) : undefined;
79
+ const pins = latestTold === undefined ? pending : [...pending, latestTold];
80
+ const reported = frontierReportedChanges(ledger);
81
+ if (ledger.openTurn !== undefined) {
82
+ const window = ledger.openTurn.rows;
83
+ const pinSet = new Set([...window.filter(isActiveTool), ...pins]);
84
+ const ordinary = window.filter((row) => !pinSet.has(row));
85
+ const tailCount = Math.max(0, budget.tail);
86
+ const tail = tailCount === 0 ? [] : ordinary.slice(-tailCount);
87
+ const tailSet = new Set(tail);
88
+ const voiceCount = Math.max(0, budget.voice ?? DEFAULT_VOICE);
89
+ const voiceCandidates = ordinary.filter((row) => !tailSet.has(row) && (row.kind === "said" || row.kind === "thought"));
90
+ const voice = voiceCount === 0 ? [] : voiceCandidates.slice(-voiceCount);
91
+ const selected = new Set([...pinSet, ...tail, ...voice]);
92
+ const omitted = window.filter((row) => !selected.has(row)).length;
93
+ return {
94
+ snapshot: {
95
+ kind: "open",
96
+ turn: ledger.openTurn.turn,
97
+ entries: assembleEntries(ledger, window, selected),
98
+ omitted,
99
+ ...reported,
100
+ },
101
+ ordinaryCount: tail.length + voice.length,
102
+ };
103
+ }
104
+ return {
105
+ snapshot: {
106
+ kind: "idle",
107
+ ...(ledger.latestOutcome === undefined ? {} : { outcome: ledger.latestOutcome }),
108
+ entries: assembleEntries(ledger, [], new Set(pins)),
109
+ omitted: 0,
110
+ ...reported,
111
+ },
112
+ ordinaryCount: 0,
113
+ };
114
+ }
115
+ export function selectHistory(ledger, cursor) {
116
+ const eligible = ledger.rows.filter((row) => cursor.before !== undefined
117
+ ? row.sequence < cursor.before
118
+ : cursor.since !== undefined
119
+ ? row.sequence > cursor.since
120
+ : true);
121
+ const rows = cursor.since !== undefined ? eligible.slice(0, cursor.limit) : eligible.slice(-cursor.limit);
122
+ const omitted = Math.max(0, eligible.length - rows.length);
123
+ const historyLost = cursor.since !== undefined
124
+ ? ledger.rows[0] !== undefined && ledger.rows[0].sequence > cursor.since + 1
125
+ : ledger.retained.lowestRetained !== null && ledger.retained.lowestRetained > 1 && omitted === 0;
126
+ return {
127
+ rows,
128
+ omitted,
129
+ hasEarlier: cursor.since === undefined && eligible.length > rows.length,
130
+ hasLater: cursor.since !== undefined && eligible.length > rows.length,
131
+ historyLost,
132
+ lowestRetained: ledger.retained.lowestRetained,
133
+ highest: ledger.retained.highest,
134
+ };
135
+ }
@@ -172,19 +172,4 @@ export type TurnLedger = Readonly<{
172
172
  }>;
173
173
  /** Fold the retained fact timeline into Turn-owned rows without inventing provider facts. */
174
174
  export declare function projectTurns(facts: readonly TimelineFact[], retained?: RetainedWindow): TurnLedger;
175
- /** Split one nonnegative ordinary allowance into newest tail first, then pre-tail voice. */
176
- export declare function ordinarySnapshotBudget(ordinaryBudget: number): Readonly<{
177
- tail: number;
178
- voice: number;
179
- }>;
180
- /** Count selected ordinary rows; active tools and pending tells stay outside this total. */
181
- export declare function ordinarySelectedCount(snapshot: ActivitySnapshot): number;
182
- /** Select one current Turn, one latest outcome, or no focus; pending tells stay actionable. */
183
- export declare function selectSnapshot(ledger: TurnLedger, budget?: Readonly<{
184
- tail: number;
185
- voice?: number;
186
- }>): ActivitySnapshot;
187
- /** Apply the one snapshot policy used by every public observation consumer. */
188
- export declare function selectActivitySnapshot(facts: readonly TimelineFact[]): ActivitySnapshot;
189
- export declare function selectHistory(ledger: TurnLedger, cursor: HistoryCursor): HistoryPage;
190
- export {};
175
+ export { ordinarySnapshotBudget, selectHistory, selectSnapshot } from "./projection-read.js";
@@ -1,8 +1,4 @@
1
1
  import { decodeAgentEvent } from "./provider.js";
2
- const DEFAULT_TAIL = 3;
3
- const DEFAULT_VOICE = 3;
4
- const REPORTED_CHANGE_LIMIT = 5;
5
- const EMPTY_REPORTED = { reportedChanges: [], reportedChangesOmitted: 0 };
6
2
  function duration(startedAt, completedAt) {
7
3
  const start = Date.parse(startedAt);
8
4
  const end = Date.parse(completedAt);
@@ -15,12 +11,24 @@ function toolKey(turnSequence, id) {
15
11
  }
16
12
  function narrationRow(fact, event) {
17
13
  if (event.type === "assistant") {
18
- return { kind: "said", sequence: fact.sequence, turnSequence: fact.turnSequence, at: fact.at, text: event.text,
19
- ...(event.truncated === true ? { truncated: true } : {}) };
14
+ return {
15
+ kind: "said",
16
+ sequence: fact.sequence,
17
+ turnSequence: fact.turnSequence,
18
+ at: fact.at,
19
+ text: event.text,
20
+ ...(event.truncated === true ? { truncated: true } : {}),
21
+ };
20
22
  }
21
23
  if (event.type === "thought" || event.type === "note") {
22
- return { kind: event.type, sequence: fact.sequence, turnSequence: fact.turnSequence, at: fact.at, text: event.text,
23
- ...(event.truncated === true ? { truncated: true } : {}) };
24
+ return {
25
+ kind: event.type,
26
+ sequence: fact.sequence,
27
+ turnSequence: fact.turnSequence,
28
+ at: fact.at,
29
+ text: event.text,
30
+ ...(event.truncated === true ? { truncated: true } : {}),
31
+ };
24
32
  }
25
33
  return null;
26
34
  }
@@ -43,7 +51,16 @@ function projectToolEvent(rows, running, fact, event, phase) {
43
51
  const index = running.get(key);
44
52
  const started = index === undefined ? undefined : rows[index];
45
53
  if (started?.kind !== "tool") {
46
- rows.push({ kind: "tool", sequence: fact.sequence, turnSequence: fact.turnSequence, at: fact.at, name: event.name, call: event.call, state: event.result, ...(event.truncated === true ? { truncated: true } : {}) });
54
+ rows.push({
55
+ kind: "tool",
56
+ sequence: fact.sequence,
57
+ turnSequence: fact.turnSequence,
58
+ at: fact.at,
59
+ name: event.name,
60
+ call: event.call,
61
+ state: event.result,
62
+ ...(event.truncated === true ? { truncated: true } : {}),
63
+ });
47
64
  return;
48
65
  }
49
66
  const durationMs = duration(started.at, fact.at);
@@ -103,6 +120,25 @@ function finishTurn(turn) {
103
120
  }
104
121
  return { kind: "open", turn: turn.turn, rows: rows };
105
122
  }
123
+ function finishLedger(state, retained) {
124
+ const turns = state.turns.map(finishTurn);
125
+ const rows = [
126
+ ...turns.flatMap((turn) => [turn.turn, ...turn.rows, ...(turn.kind === "closed" ? [turn.outcome] : [])]),
127
+ ...state.tells,
128
+ ...state.orphanRows,
129
+ ...state.orphanOutcomes,
130
+ ].sort((left, right) => left.sequence - right.sequence);
131
+ const frontier = turns.at(-1);
132
+ const openTurn = frontier?.kind === "open" ? frontier : undefined;
133
+ const latestOutcome = rows.findLast((row) => row.kind === "outcome");
134
+ return {
135
+ rows,
136
+ turns,
137
+ retained,
138
+ ...(openTurn === undefined ? {} : { openTurn }),
139
+ ...(latestOutcome === undefined ? {} : { latestOutcome }),
140
+ };
141
+ }
106
142
  /** Fold the retained fact timeline into Turn-owned rows without inventing provider facts. */
107
143
  export function projectTurns(facts, retained = {
108
144
  lowestRetained: facts[0]?.sequence ?? null,
@@ -118,13 +154,25 @@ export function projectTurns(facts, retained = {
118
154
  };
119
155
  for (const fact of facts) {
120
156
  if (fact.kind === "turn-start") {
121
- const turn = { kind: "turn", sequence: fact.sequence, turnSequence: fact.sequence, bodySequence: fact.bodySequence, at: fact.startedAt };
157
+ const turn = {
158
+ kind: "turn",
159
+ sequence: fact.sequence,
160
+ turnSequence: fact.sequence,
161
+ bodySequence: fact.bodySequence,
162
+ at: fact.startedAt,
163
+ };
122
164
  const projected = { phase: "open", turn, rows: [], running: new Map() };
123
165
  state.turns.push(projected);
124
166
  state.turnsBySequence.set(turn.turnSequence, projected);
125
167
  }
126
168
  else if (fact.kind === "turn-end") {
127
- const outcome = { kind: "outcome", sequence: fact.sequence, turnSequence: fact.turnSequence, at: fact.completedAt, outcome: fact.outcome };
169
+ const outcome = {
170
+ kind: "outcome",
171
+ sequence: fact.sequence,
172
+ turnSequence: fact.turnSequence,
173
+ at: fact.completedAt,
174
+ outcome: fact.outcome,
175
+ };
128
176
  const turn = state.turnsBySequence.get(fact.turnSequence);
129
177
  if (turn === undefined)
130
178
  state.orphanOutcomes.push(outcome);
@@ -135,7 +183,13 @@ export function projectTurns(facts, retained = {
135
183
  }
136
184
  }
137
185
  else if (fact.kind === "call") {
138
- const row = { kind: "call", sequence: fact.sequence, turnSequence: fact.turnSequence, at: fact.at, text: fact.body };
186
+ const row = {
187
+ kind: "call",
188
+ sequence: fact.sequence,
189
+ turnSequence: fact.turnSequence,
190
+ at: fact.at,
191
+ text: fact.body,
192
+ };
139
193
  const turn = state.turnsBySequence.get(fact.turnSequence);
140
194
  if (turn === undefined)
141
195
  state.orphanRows.push(row);
@@ -143,151 +197,20 @@ export function projectTurns(facts, retained = {
143
197
  turn.rows.push(row);
144
198
  }
145
199
  else if (fact.kind === "tell") {
146
- state.tells.push({ kind: "tell", sequence: fact.sequence, at: fact.recordedAt, tellId: fact.id, text: fact.body, state: fact.state, deliveries: fact.deliveries });
200
+ state.tells.push({
201
+ kind: "tell",
202
+ sequence: fact.sequence,
203
+ at: fact.recordedAt,
204
+ tellId: fact.id,
205
+ text: fact.body,
206
+ state: fact.state,
207
+ deliveries: fact.deliveries,
208
+ });
147
209
  }
148
210
  else {
149
211
  projectActivityEvent(state, fact);
150
212
  }
151
213
  }
152
- const turns = state.turns.map(finishTurn);
153
- const rows = [
154
- ...turns.flatMap((turn) => [turn.turn, ...turn.rows, ...(turn.kind === "closed" ? [turn.outcome] : [])]),
155
- ...state.tells,
156
- ...state.orphanRows,
157
- ...state.orphanOutcomes,
158
- ].sort((left, right) => left.sequence - right.sequence);
159
- const frontier = turns.at(-1);
160
- const openTurn = frontier?.kind === "open" ? frontier : undefined;
161
- const latestOutcome = rows.findLast((row) => row.kind === "outcome");
162
- return { rows, turns, retained, ...(openTurn === undefined ? {} : { openTurn }), ...(latestOutcome === undefined ? {} : { latestOutcome }) };
163
- }
164
- function entries(rows) {
165
- return rows.map((row) => ({ kind: "row", row }));
166
- }
167
- function openEntries(ledger, window, selected) {
168
- const windowRows = new Set(window);
169
- const result = [];
170
- let hidden = 0;
171
- const flushGap = () => {
172
- if (hidden === 0)
173
- return;
174
- result.push({ kind: "gap", count: hidden });
175
- hidden = 0;
176
- };
177
- for (const row of ledger.rows) {
178
- if (windowRows.has(row) && !selected.has(row)) {
179
- hidden += 1;
180
- continue;
181
- }
182
- if (!selected.has(row))
183
- continue;
184
- flushGap();
185
- if (row.kind !== "turn" && row.kind !== "outcome" && !(row.kind === "tool" && row.state === "unsettled")) {
186
- result.push({ kind: "row", row });
187
- }
188
- }
189
- flushGap();
190
- return result;
191
- }
192
- /** Split one nonnegative ordinary allowance into newest tail first, then pre-tail voice. */
193
- export function ordinarySnapshotBudget(ordinaryBudget) {
194
- const tail = Math.min(DEFAULT_TAIL, Math.max(0, ordinaryBudget));
195
- return { tail, voice: Math.min(DEFAULT_VOICE, Math.max(0, ordinaryBudget) - tail) };
196
- }
197
- /** Count selected ordinary rows; active tools and pending tells stay outside this total. */
198
- export function ordinarySelectedCount(snapshot) {
199
- return snapshot.entries.reduce((count, entry) => {
200
- if (entry.kind !== "row")
201
- return count;
202
- if (entry.row.kind === "tool" && entry.row.state === "active")
203
- return count;
204
- if (entry.row.kind === "tell" && entry.row.state === "pending")
205
- return count;
206
- return count + 1;
207
- }, 0);
208
- }
209
- function isSuccessfulFileChange(row) {
210
- return row.kind === "tool"
211
- && typeof row.state === "object"
212
- && row.state.status === "ok"
213
- && row.call.kind === "fileChange";
214
- }
215
- function flattenReportedChanges(rows) {
216
- return rows.flatMap((row) => {
217
- if (!isSuccessfulFileChange(row))
218
- return [];
219
- return row.call.changes.map((change) => ({
220
- sequence: row.sequence,
221
- at: row.at,
222
- op: change.op,
223
- path: change.path,
224
- ...(change.diffstat === undefined ? {} : { diffstat: change.diffstat }),
225
- }));
226
- });
227
- }
228
- function summarizeReportedChanges(rows) {
229
- const eligible = flattenReportedChanges(rows);
230
- return {
231
- reportedChanges: eligible.slice(-REPORTED_CHANGE_LIMIT),
232
- reportedChangesOmitted: Math.max(0, eligible.length - REPORTED_CHANGE_LIMIT),
233
- };
234
- }
235
- function frontierReportedChanges(ledger) {
236
- if (ledger.openTurn !== undefined)
237
- return summarizeReportedChanges(ledger.openTurn.rows);
238
- const closed = ledger.turns.findLast((turn) => turn.kind === "closed");
239
- return closed === undefined ? EMPTY_REPORTED : summarizeReportedChanges(closed.rows);
240
- }
241
- /** Select one current Turn, one latest outcome, or no focus; pending tells stay actionable. */
242
- export function selectSnapshot(ledger, budget = { tail: DEFAULT_TAIL, voice: DEFAULT_VOICE }) {
243
- if (ledger.turns.length === 0 && !ledger.rows.some((row) => row.kind === "tell")) {
244
- return { kind: "unborn", entries: [], omitted: 0, ...EMPTY_REPORTED };
245
- }
246
- const pending = ledger.rows.filter((row) => row.kind === "tell" && row.state === "pending");
247
- const reported = frontierReportedChanges(ledger);
248
- if (ledger.openTurn !== undefined) {
249
- const window = ledger.openTurn.rows;
250
- const active = window.filter((row) => row.kind === "tool" && row.state === "active");
251
- const ordinary = window.filter((row) => !(row.kind === "tool" && row.state === "active"));
252
- const tailCount = Math.max(0, budget.tail);
253
- const tail = tailCount === 0 ? [] : ordinary.slice(-tailCount);
254
- const tailSet = new Set(tail);
255
- const voiceCount = Math.max(0, budget.voice ?? DEFAULT_VOICE);
256
- const voiceCandidates = ordinary
257
- .filter((row) => !tailSet.has(row) && (row.kind === "said" || row.kind === "thought"));
258
- const voice = voiceCount === 0 ? [] : voiceCandidates.slice(-voiceCount);
259
- const selected = new Set([...tail, ...voice, ...active, ...pending]);
260
- const omitted = window.filter((row) => !selected.has(row)).length;
261
- return { kind: "open", turn: ledger.openTurn.turn, entries: openEntries(ledger, window, selected), omitted, ...reported };
262
- }
263
- return {
264
- kind: "idle",
265
- ...(ledger.latestOutcome === undefined ? {} : { outcome: ledger.latestOutcome }),
266
- entries: entries(pending),
267
- omitted: 0,
268
- ...reported,
269
- };
270
- }
271
- /** Apply the one snapshot policy used by every public observation consumer. */
272
- export function selectActivitySnapshot(facts) {
273
- return selectSnapshot(projectTurns(facts));
274
- }
275
- export function selectHistory(ledger, cursor) {
276
- const eligible = ledger.rows.filter((row) => cursor.before !== undefined
277
- ? row.sequence < cursor.before
278
- : cursor.since !== undefined ? row.sequence > cursor.since : true);
279
- const rows = cursor.since !== undefined ? eligible.slice(0, cursor.limit) : eligible.slice(-cursor.limit);
280
- const omitted = Math.max(0, eligible.length - rows.length);
281
- const historyLost = cursor.since !== undefined
282
- ? (ledger.rows[0] !== undefined && ledger.rows[0].sequence > cursor.since + 1)
283
- : (ledger.retained.lowestRetained !== null && ledger.retained.lowestRetained > 1 && omitted === 0);
284
- return {
285
- rows,
286
- omitted,
287
- hasEarlier: cursor.since === undefined && eligible.length > rows.length,
288
- hasLater: cursor.since !== undefined && eligible.length > rows.length,
289
- historyLost,
290
- lowestRetained: ledger.retained.lowestRetained,
291
- highest: ledger.retained.highest,
292
- };
214
+ return finishLedger(state, retained);
293
215
  }
216
+ export { ordinarySnapshotBudget, selectHistory, selectSnapshot } from "./projection-read.js";
@@ -59,15 +59,20 @@ export function decodeReadonlyRestraint(value) {
59
59
  if (restraint.enforcement === "native") {
60
60
  return Object.freeze({ enforcement: "native" });
61
61
  }
62
- if (restraint.enforcement === "none"
63
- && typeof restraint.diagnostic === "string" && restraint.diagnostic.trim().length > 0) {
62
+ if (restraint.enforcement === "none" &&
63
+ typeof restraint.diagnostic === "string" &&
64
+ restraint.diagnostic.trim().length > 0) {
64
65
  return Object.freeze({ enforcement: "none", diagnostic: restraint.diagnostic });
65
66
  }
66
67
  throw new TypeError("readonly restraint must be native or none with a diagnostic");
67
68
  }
68
69
  function providerKind(value) {
69
- return value === "acp" || value === "claude-agent-sdk" || value === "codex-app-server" || value === "grok-build"
70
- || value === "opencode-sdk" || value === "pi";
70
+ return (value === "acp" ||
71
+ value === "claude-agent-sdk" ||
72
+ value === "codex-app-server" ||
73
+ value === "grok-build" ||
74
+ value === "opencode-sdk" ||
75
+ value === "pi");
71
76
  }
72
77
  export function decodeProviderRecipe(input) {
73
78
  const value = record(input);
@@ -78,7 +83,8 @@ export function decodeProviderRecipe(input) {
78
83
  }
79
84
  if (!providerKind(value.kind))
80
85
  throw new TypeError("provider execution has unknown kind");
81
- if (value.executable !== undefined && (typeof value.executable !== "string" || value.executable.trim().length === 0)) {
86
+ if (value.executable !== undefined &&
87
+ (typeof value.executable !== "string" || value.executable.trim().length === 0)) {
82
88
  throw new TypeError("provider execution executable must be a nonblank string");
83
89
  }
84
90
  const config = value.config === undefined ? undefined : record(value.config);