@astrosheep/keiyaku 4.0.6 → 4.1.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 (251) 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.d.ts +17 -14
  6. package/build/src/akuma/akuma.js +122 -114
  7. package/build/src/akuma/allowed.d.ts +1 -0
  8. package/build/src/akuma/allowed.js +6 -0
  9. package/build/src/akuma/archetype.js +12 -2
  10. package/build/src/akuma/body-turn.js +27 -15
  11. package/build/src/akuma/body.d.ts +44 -3
  12. package/build/src/akuma/body.js +142 -33
  13. package/build/src/akuma/coordinate.js +17 -7
  14. package/build/src/akuma/heart/facts.js +1 -1
  15. package/build/src/akuma/heart/index.d.ts +1 -1
  16. package/build/src/akuma/heart/index.js +2 -4
  17. package/build/src/akuma/heart/request-authority.js +4 -4
  18. package/build/src/akuma/heart/request-rows.js +27 -16
  19. package/build/src/akuma/heart/rows.js +79 -41
  20. package/build/src/akuma/heart/soul.js +1 -1
  21. package/build/src/akuma/heart/storage.d.ts +1 -0
  22. package/build/src/akuma/heart/storage.js +76 -11
  23. package/build/src/akuma/heart/tells.js +35 -18
  24. package/build/src/akuma/heart/timeline.js +19 -11
  25. package/build/src/akuma/identity.d.ts +1 -0
  26. package/build/src/akuma/identity.js +1 -0
  27. package/build/src/akuma/index.d.ts +3 -3
  28. package/build/src/akuma/index.js +1 -1
  29. package/build/src/akuma/nuke.js +106 -14
  30. package/build/src/akuma/projection.d.ts +11 -9
  31. package/build/src/akuma/projection.js +110 -53
  32. package/build/src/akuma/provider-recipe.d.ts +2 -0
  33. package/build/src/akuma/provider-recipe.js +21 -5
  34. package/build/src/akuma/provider.js +105 -62
  35. package/build/src/akuma/providers/acp/config.d.ts +2 -0
  36. package/build/src/akuma/providers/acp/config.js +13 -6
  37. package/build/src/akuma/providers/acp/core.d.ts +5 -2
  38. package/build/src/akuma/providers/acp/core.js +45 -25
  39. package/build/src/akuma/providers/acp/events.js +38 -29
  40. package/build/src/akuma/providers/acp/index.js +18 -8
  41. package/build/src/akuma/providers/claude/events.js +8 -12
  42. package/build/src/akuma/providers/claude/index.d.ts +2 -1
  43. package/build/src/akuma/providers/claude/index.js +59 -26
  44. package/build/src/akuma/providers/claude/input.js +6 -2
  45. package/build/src/akuma/providers/codex-app-server/events.js +61 -36
  46. package/build/src/akuma/providers/codex-app-server/index.js +29 -14
  47. package/build/src/akuma/providers/grok-build/index.js +28 -17
  48. package/build/src/akuma/providers/index.js +3 -19
  49. package/build/src/akuma/providers/opencode-sdk/events.js +32 -18
  50. package/build/src/akuma/providers/opencode-sdk/index.js +29 -17
  51. package/build/src/akuma/providers/opencode-sdk/session.js +21 -5
  52. package/build/src/akuma/providers/pi/events.js +26 -19
  53. package/build/src/akuma/providers/pi/index.js +58 -38
  54. package/build/src/akuma/publication.js +9 -9
  55. package/build/src/akuma/request-serve.js +37 -40
  56. package/build/src/akuma/request-wire.d.ts +2 -2
  57. package/build/src/akuma/request-wire.js +75 -102
  58. package/build/src/akuma/requests.js +2 -2
  59. package/build/src/akuma-body.js +6 -5
  60. package/build/src/body/amend.d.ts +4 -1
  61. package/build/src/body/amend.js +14 -15
  62. package/build/src/body/decode.js +2 -4
  63. package/build/src/body/envelope.js +2 -4
  64. package/build/src/body/region.d.ts +1 -2
  65. package/build/src/body/region.js +4 -72
  66. package/build/src/body/render.js +17 -12
  67. package/build/src/body/shape.js +1 -7
  68. package/build/src/cli/accepted.d.ts +1 -1
  69. package/build/src/cli/accepted.js +10 -1
  70. package/build/src/cli/commands/akuma-invoke.js +18 -6
  71. package/build/src/cli/commands/akuma.js +10 -13
  72. package/build/src/cli/commands/amend.d.ts +2 -2
  73. package/build/src/cli/commands/amend.js +2 -4
  74. package/build/src/cli/commands/bind.d.ts +4 -4
  75. package/build/src/cli/commands/bind.js +16 -6
  76. package/build/src/cli/commands/contract.d.ts +99 -10
  77. package/build/src/cli/commands/contract.js +128 -24
  78. package/build/src/cli/commands/install.d.ts +1 -1
  79. package/build/src/cli/commands/install.js +23 -15
  80. package/build/src/cli/commands/task-invoke.d.ts +4 -1
  81. package/build/src/cli/commands/task-invoke.js +191 -68
  82. package/build/src/cli/commands/task-query.js +48 -24
  83. package/build/src/cli/commands/task.js +150 -30
  84. package/build/src/cli/coordinates.js +2 -4
  85. package/build/src/cli/invoke.js +88 -44
  86. package/build/src/cli/main.js +4 -2
  87. package/build/src/cli/parse.d.ts +2 -89
  88. package/build/src/cli/parse.js +87 -79
  89. package/build/src/cli/render/akuma-tool-command.js +6 -7
  90. package/build/src/cli/render/akuma-tool.js +10 -8
  91. package/build/src/cli/render/akuma.js +149 -53
  92. package/build/src/cli/render/audit.js +5 -15
  93. package/build/src/cli/render/board.d.ts +1 -0
  94. package/build/src/cli/render/board.js +17 -0
  95. package/build/src/cli/render/catalog.js +84 -7
  96. package/build/src/cli/render/contract-observation.d.ts +10 -0
  97. package/build/src/cli/render/contract-observation.js +58 -0
  98. package/build/src/cli/render/contract.js +57 -54
  99. package/build/src/cli/render/kanshi.js +281 -209
  100. package/build/src/cli/render/receipt.d.ts +1 -0
  101. package/build/src/cli/render/receipt.js +20 -23
  102. package/build/src/cli/render/refusal.js +16 -15
  103. package/build/src/cli/render/region.js +22 -6
  104. package/build/src/cli/render/settings.js +4 -4
  105. package/build/src/cli/render/task.js +93 -21
  106. package/build/src/cli/render/terminal.js +16 -5
  107. package/build/src/cli/result.d.ts +11 -4
  108. package/build/src/cli/runtime.d.ts +6 -1
  109. package/build/src/cli/runtime.js +84 -63
  110. package/build/src/cli/selectors.js +2 -4
  111. package/build/src/cli/usage.js +6 -5
  112. package/build/src/contract-worktree.d.ts +1 -25
  113. package/build/src/contract-worktree.js +87 -174
  114. package/build/src/coordination/durable-file.js +7 -3
  115. package/build/src/coordination/sqlite-transaction-lock.js +29 -12
  116. package/build/src/core/facts/codec.js +7 -3
  117. package/build/src/core/facts/types.d.ts +1 -1
  118. package/build/src/core/subject.js +16 -9
  119. package/build/src/core/verbs/abandon.d.ts +1 -1
  120. package/build/src/core/verbs/abandon.js +1 -1
  121. package/build/src/core/verbs/amend.d.ts +1 -1
  122. package/build/src/core/verbs/amend.js +7 -7
  123. package/build/src/core/verbs/attestation.js +4 -1
  124. package/build/src/core/verbs/bind.d.ts +1 -1
  125. package/build/src/core/verbs/bind.js +1 -1
  126. package/build/src/core/verbs/placement.d.ts +1 -1
  127. package/build/src/core/verbs/placement.js +10 -8
  128. package/build/src/dispatch/index.js +8 -6
  129. package/build/src/duration.js +5 -1
  130. package/build/src/git/admission.js +5 -3
  131. package/build/src/git/hooks.d.ts +8 -0
  132. package/build/src/git/hooks.js +63 -14
  133. package/build/src/git/identity.js +1 -4
  134. package/build/src/git/integration.d.ts +2 -3
  135. package/build/src/git/integration.js +32 -39
  136. package/build/src/git/nuke.d.ts +1 -1
  137. package/build/src/git/nuke.js +110 -62
  138. package/build/src/git/observe.d.ts +13 -1
  139. package/build/src/git/observe.js +51 -44
  140. package/build/src/git/process.js +4 -2
  141. package/build/src/git/read-observation.js +10 -8
  142. package/build/src/git/reconcile.d.ts +14 -3
  143. package/build/src/git/reconcile.js +123 -102
  144. package/build/src/git/repository.d.ts +2 -0
  145. package/build/src/git/repository.js +13 -22
  146. package/build/src/git/target-placement.d.ts +9 -10
  147. package/build/src/git/target-placement.js +59 -62
  148. package/build/src/git/tender.d.ts +13 -2
  149. package/build/src/git/tender.js +28 -22
  150. package/build/src/git/terminal-seal.js +15 -12
  151. package/build/src/git/tree.js +5 -2
  152. package/build/src/git/workspace.d.ts +24 -2
  153. package/build/src/git/workspace.js +120 -16
  154. package/build/src/identity/coordinates.js +3 -1
  155. package/build/src/identity/selector.js +6 -3
  156. package/build/src/kanshi/fleet.d.ts +6 -0
  157. package/build/src/kanshi/fleet.js +27 -0
  158. package/build/src/kanshi/index.d.ts +1 -1
  159. package/build/src/kanshi/read.js +77 -45
  160. package/build/src/kanshi/report.d.ts +13 -24
  161. package/build/src/kanshi/select.js +25 -24
  162. package/build/src/library/address.d.ts +2 -1
  163. package/build/src/library/address.js +7 -8
  164. package/build/src/library/akuma-creation.js +20 -12
  165. package/build/src/library/audit.d.ts +0 -1
  166. package/build/src/library/audit.js +1 -2
  167. package/build/src/library/bind.d.ts +53 -5
  168. package/build/src/library/bind.js +98 -22
  169. package/build/src/library/catalog.d.ts +1 -0
  170. package/build/src/library/catalog.js +6 -3
  171. package/build/src/library/configuration.js +3 -1
  172. package/build/src/library/continuation.d.ts +37 -0
  173. package/build/src/library/continuation.js +86 -0
  174. package/build/src/library/contract.d.ts +39 -45
  175. package/build/src/library/contract.js +141 -118
  176. package/build/src/library/delivery.d.ts +18 -13
  177. package/build/src/library/fleet.d.ts +1 -1
  178. package/build/src/library/fleet.js +19 -11
  179. package/build/src/library/input.d.ts +2 -0
  180. package/build/src/library/input.js +14 -0
  181. package/build/src/library/keiyaku.d.ts +8 -7
  182. package/build/src/library/keiyaku.js +3 -3
  183. package/build/src/library/mutation.js +11 -4
  184. package/build/src/library/nuke.js +14 -5
  185. package/build/src/library/reconcile.d.ts +9 -4
  186. package/build/src/library/reconcile.js +30 -24
  187. package/build/src/library/refusal.d.ts +13 -5
  188. package/build/src/library/refusal.js +7 -0
  189. package/build/src/library/region.d.ts +8 -7
  190. package/build/src/library/region.js +31 -11
  191. package/build/src/library/repo.js +2 -2
  192. package/build/src/markdown/lex.js +9 -2
  193. package/build/src/markdown/parse.js +11 -7
  194. package/build/src/markdown/query.js +4 -2
  195. package/build/src/protocol/amend.js +3 -1
  196. package/build/src/protocol/attempt.d.ts +7 -3
  197. package/build/src/protocol/attempt.js +9 -5
  198. package/build/src/protocol/audit.d.ts +1 -1
  199. package/build/src/protocol/audit.js +4 -14
  200. package/build/src/protocol/bind.d.ts +19 -6
  201. package/build/src/protocol/bind.js +28 -13
  202. package/build/src/protocol/completion.d.ts +0 -1
  203. package/build/src/protocol/completion.js +1 -3
  204. package/build/src/protocol/deliver.d.ts +12 -4
  205. package/build/src/protocol/deliver.js +60 -51
  206. package/build/src/protocol/intent.d.ts +1 -0
  207. package/build/src/protocol/intent.js +9 -7
  208. package/build/src/protocol/operations.d.ts +10 -9
  209. package/build/src/protocol/operations.js +5 -3
  210. package/build/src/protocol/placement.d.ts +0 -1
  211. package/build/src/protocol/placement.js +6 -6
  212. package/build/src/protocol/read/observation.d.ts +11 -0
  213. package/build/src/protocol/read/observation.js +37 -0
  214. package/build/src/protocol/read/status.d.ts +23 -12
  215. package/build/src/protocol/read/status.js +90 -72
  216. package/build/src/protocol/reconcile.d.ts +1 -0
  217. package/build/src/protocol/reconcile.js +2 -3
  218. package/build/src/protocol/reintegrate.js +1 -1
  219. package/build/src/protocol/review.d.ts +0 -1
  220. package/build/src/protocol/review.js +15 -22
  221. package/build/src/protocol/run.d.ts +1 -0
  222. package/build/src/protocol/run.js +4 -5
  223. package/build/src/runtime/proc/launch.d.ts +33 -0
  224. package/build/src/runtime/proc/launch.js +98 -0
  225. package/build/src/runtime/proc/line-rpc.js +9 -3
  226. package/build/src/runtime/proc/run.d.ts +12 -6
  227. package/build/src/runtime/proc/run.js +78 -21
  228. package/build/src/runtime/proc/stdio.js +7 -10
  229. package/build/src/settings.js +6 -2
  230. package/build/src/settlement/fence.d.ts +0 -2
  231. package/build/src/settlement/fence.js +0 -24
  232. package/build/src/settlement/holder.js +13 -10
  233. package/build/src/settlement/settle.js +39 -6
  234. package/build/src/task/board.d.ts +21 -2
  235. package/build/src/task/board.js +35 -16
  236. package/build/src/task/compose.js +85 -22
  237. package/build/src/task/context.js +21 -8
  238. package/build/src/task/document.js +46 -10
  239. package/build/src/task/identity.js +19 -6
  240. package/build/src/task/index.js +118 -27
  241. package/build/src/task/input.js +29 -7
  242. package/build/src/task/mutation.js +61 -19
  243. package/build/src/task/operations.d.ts +8 -2
  244. package/build/src/task/operations.js +97 -32
  245. package/build/src/task/query.js +57 -28
  246. package/build/src/task/store.d.ts +5 -1
  247. package/build/src/task/store.js +88 -95
  248. package/build/src/verification/execution.js +14 -11
  249. package/build/src/workspace-place.js +19 -7
  250. package/build/src/world.js +16 -6
  251. 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())
@@ -173,18 +173,20 @@ export type TurnLedger = Readonly<{
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
175
  /** Split one nonnegative ordinary allowance into newest tail first, then pre-tail voice. */
176
- export declare function ordinarySnapshotBudget(ordinaryBudget: number): Readonly<{
176
+ export declare function ordinarySnapshotBudget(ordinaryBudget?: number): Readonly<{
177
177
  tail: number;
178
178
  voice: number;
179
179
  }>;
180
- /** Count selected ordinary rows; active tools and pending tells stay outside this total. */
181
- export declare function ordinarySelectedCount(snapshot: ActivitySnapshot): number;
182
180
  /** 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;
181
+ export declare function selectSnapshot(ledger: TurnLedger, input: Readonly<{
182
+ aperture: "monitoring" | "receipt";
183
+ budget?: Readonly<{
184
+ tail: number;
185
+ voice?: number;
186
+ }>;
187
+ }>): Readonly<{
188
+ snapshot: ActivitySnapshot;
189
+ ordinaryCount: number;
190
+ }>;
189
191
  export declare function selectHistory(ledger: TurnLedger, cursor: HistoryCursor): HistoryPage;
190
192
  export {};
@@ -15,12 +15,24 @@ function toolKey(turnSequence, id) {
15
15
  }
16
16
  function narrationRow(fact, event) {
17
17
  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 } : {}) };
18
+ return {
19
+ kind: "said",
20
+ sequence: fact.sequence,
21
+ turnSequence: fact.turnSequence,
22
+ at: fact.at,
23
+ text: event.text,
24
+ ...(event.truncated === true ? { truncated: true } : {}),
25
+ };
20
26
  }
21
27
  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 } : {}) };
28
+ return {
29
+ kind: event.type,
30
+ sequence: fact.sequence,
31
+ turnSequence: fact.turnSequence,
32
+ at: fact.at,
33
+ text: event.text,
34
+ ...(event.truncated === true ? { truncated: true } : {}),
35
+ };
24
36
  }
25
37
  return null;
26
38
  }
@@ -43,7 +55,16 @@ function projectToolEvent(rows, running, fact, event, phase) {
43
55
  const index = running.get(key);
44
56
  const started = index === undefined ? undefined : rows[index];
45
57
  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 } : {}) });
58
+ rows.push({
59
+ kind: "tool",
60
+ sequence: fact.sequence,
61
+ turnSequence: fact.turnSequence,
62
+ at: fact.at,
63
+ name: event.name,
64
+ call: event.call,
65
+ state: event.result,
66
+ ...(event.truncated === true ? { truncated: true } : {}),
67
+ });
47
68
  return;
48
69
  }
49
70
  const durationMs = duration(started.at, fact.at);
@@ -118,13 +139,25 @@ export function projectTurns(facts, retained = {
118
139
  };
119
140
  for (const fact of facts) {
120
141
  if (fact.kind === "turn-start") {
121
- const turn = { kind: "turn", sequence: fact.sequence, turnSequence: fact.sequence, bodySequence: fact.bodySequence, at: fact.startedAt };
142
+ const turn = {
143
+ kind: "turn",
144
+ sequence: fact.sequence,
145
+ turnSequence: fact.sequence,
146
+ bodySequence: fact.bodySequence,
147
+ at: fact.startedAt,
148
+ };
122
149
  const projected = { phase: "open", turn, rows: [], running: new Map() };
123
150
  state.turns.push(projected);
124
151
  state.turnsBySequence.set(turn.turnSequence, projected);
125
152
  }
126
153
  else if (fact.kind === "turn-end") {
127
- const outcome = { kind: "outcome", sequence: fact.sequence, turnSequence: fact.turnSequence, at: fact.completedAt, outcome: fact.outcome };
154
+ const outcome = {
155
+ kind: "outcome",
156
+ sequence: fact.sequence,
157
+ turnSequence: fact.turnSequence,
158
+ at: fact.completedAt,
159
+ outcome: fact.outcome,
160
+ };
128
161
  const turn = state.turnsBySequence.get(fact.turnSequence);
129
162
  if (turn === undefined)
130
163
  state.orphanOutcomes.push(outcome);
@@ -135,7 +168,13 @@ export function projectTurns(facts, retained = {
135
168
  }
136
169
  }
137
170
  else if (fact.kind === "call") {
138
- const row = { kind: "call", sequence: fact.sequence, turnSequence: fact.turnSequence, at: fact.at, text: fact.body };
171
+ const row = {
172
+ kind: "call",
173
+ sequence: fact.sequence,
174
+ turnSequence: fact.turnSequence,
175
+ at: fact.at,
176
+ text: fact.body,
177
+ };
139
178
  const turn = state.turnsBySequence.get(fact.turnSequence);
140
179
  if (turn === undefined)
141
180
  state.orphanRows.push(row);
@@ -143,7 +182,15 @@ export function projectTurns(facts, retained = {
143
182
  turn.rows.push(row);
144
183
  }
145
184
  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 });
185
+ state.tells.push({
186
+ kind: "tell",
187
+ sequence: fact.sequence,
188
+ at: fact.recordedAt,
189
+ tellId: fact.id,
190
+ text: fact.body,
191
+ state: fact.state,
192
+ deliveries: fact.deliveries,
193
+ });
147
194
  }
148
195
  else {
149
196
  projectActivityEvent(state, fact);
@@ -159,12 +206,15 @@ export function projectTurns(facts, retained = {
159
206
  const frontier = turns.at(-1);
160
207
  const openTurn = frontier?.kind === "open" ? frontier : undefined;
161
208
  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 }));
209
+ return {
210
+ rows,
211
+ turns,
212
+ retained,
213
+ ...(openTurn === undefined ? {} : { openTurn }),
214
+ ...(latestOutcome === undefined ? {} : { latestOutcome }),
215
+ };
166
216
  }
167
- function openEntries(ledger, window, selected) {
217
+ function assembleEntries(ledger, window, selected) {
168
218
  const windowRows = new Set(window);
169
219
  const result = [];
170
220
  let hidden = 0;
@@ -183,34 +233,20 @@ function openEntries(ledger, window, selected) {
183
233
  continue;
184
234
  flushGap();
185
235
  if (row.kind !== "turn" && row.kind !== "outcome" && !(row.kind === "tool" && row.state === "unsettled")) {
186
- result.push({ kind: "row", row });
236
+ result.push({ kind: "row", row: row });
187
237
  }
188
238
  }
189
239
  flushGap();
190
240
  return result;
191
241
  }
242
+ const DEFAULT_ORDINARY = DEFAULT_TAIL + DEFAULT_VOICE;
192
243
  /** Split one nonnegative ordinary allowance into newest tail first, then pre-tail voice. */
193
- export function ordinarySnapshotBudget(ordinaryBudget) {
244
+ export function ordinarySnapshotBudget(ordinaryBudget = DEFAULT_ORDINARY) {
194
245
  const tail = Math.min(DEFAULT_TAIL, Math.max(0, ordinaryBudget));
195
246
  return { tail, voice: Math.min(DEFAULT_VOICE, Math.max(0, ordinaryBudget) - tail) };
196
247
  }
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
248
  function isSuccessfulFileChange(row) {
210
- return row.kind === "tool"
211
- && typeof row.state === "object"
212
- && row.state.status === "ok"
213
- && row.call.kind === "fileChange";
249
+ return (row.kind === "tool" && typeof row.state === "object" && row.state.status === "ok" && row.call.kind === "fileChange");
214
250
  }
215
251
  function flattenReportedChanges(rows) {
216
252
  return rows.flatMap((row) => {
@@ -238,49 +274,70 @@ function frontierReportedChanges(ledger) {
238
274
  const closed = ledger.turns.findLast((turn) => turn.kind === "closed");
239
275
  return closed === undefined ? EMPTY_REPORTED : summarizeReportedChanges(closed.rows);
240
276
  }
277
+ function isPendingTell(row) {
278
+ return row.kind === "tell" && row.state === "pending";
279
+ }
280
+ function isToldTell(row) {
281
+ return row.kind === "tell" && row.state === "told";
282
+ }
283
+ function isActiveTool(row) {
284
+ return row.kind === "tool" && row.state === "active";
285
+ }
241
286
  /** 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 }) {
287
+ export function selectSnapshot(ledger, input) {
288
+ const budget = input.budget ?? { tail: DEFAULT_TAIL, voice: DEFAULT_VOICE };
243
289
  if (ledger.turns.length === 0 && !ledger.rows.some((row) => row.kind === "tell")) {
244
- return { kind: "unborn", entries: [], omitted: 0, ...EMPTY_REPORTED };
290
+ return { snapshot: { kind: "unborn", entries: [], omitted: 0, ...EMPTY_REPORTED }, ordinaryCount: 0 };
245
291
  }
246
- const pending = ledger.rows.filter((row) => row.kind === "tell" && row.state === "pending");
292
+ const pending = ledger.rows.filter(isPendingTell);
293
+ const latestTold = input.aperture === "monitoring" ? ledger.rows.findLast(isToldTell) : undefined;
294
+ const pins = latestTold === undefined ? pending : [...pending, latestTold];
247
295
  const reported = frontierReportedChanges(ledger);
248
296
  if (ledger.openTurn !== undefined) {
249
297
  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"));
298
+ const pinSet = new Set([...window.filter(isActiveTool), ...pins]);
299
+ const ordinary = window.filter((row) => !pinSet.has(row));
252
300
  const tailCount = Math.max(0, budget.tail);
253
301
  const tail = tailCount === 0 ? [] : ordinary.slice(-tailCount);
254
302
  const tailSet = new Set(tail);
255
303
  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"));
304
+ const voiceCandidates = ordinary.filter((row) => !tailSet.has(row) && (row.kind === "said" || row.kind === "thought"));
258
305
  const voice = voiceCount === 0 ? [] : voiceCandidates.slice(-voiceCount);
259
- const selected = new Set([...tail, ...voice, ...active, ...pending]);
306
+ const selected = new Set([...pinSet, ...tail, ...voice]);
260
307
  const omitted = window.filter((row) => !selected.has(row)).length;
261
- return { kind: "open", turn: ledger.openTurn.turn, entries: openEntries(ledger, window, selected), omitted, ...reported };
308
+ return {
309
+ snapshot: {
310
+ kind: "open",
311
+ turn: ledger.openTurn.turn,
312
+ entries: assembleEntries(ledger, window, selected),
313
+ omitted,
314
+ ...reported,
315
+ },
316
+ ordinaryCount: tail.length + voice.length,
317
+ };
262
318
  }
263
319
  return {
264
- kind: "idle",
265
- ...(ledger.latestOutcome === undefined ? {} : { outcome: ledger.latestOutcome }),
266
- entries: entries(pending),
267
- omitted: 0,
268
- ...reported,
320
+ snapshot: {
321
+ kind: "idle",
322
+ ...(ledger.latestOutcome === undefined ? {} : { outcome: ledger.latestOutcome }),
323
+ entries: assembleEntries(ledger, [], new Set(pins)),
324
+ omitted: 0,
325
+ ...reported,
326
+ },
327
+ ordinaryCount: 0,
269
328
  };
270
329
  }
271
- /** Apply the one snapshot policy used by every public observation consumer. */
272
- export function selectActivitySnapshot(facts) {
273
- return selectSnapshot(projectTurns(facts));
274
- }
275
330
  export function selectHistory(ledger, cursor) {
276
331
  const eligible = ledger.rows.filter((row) => cursor.before !== undefined
277
332
  ? row.sequence < cursor.before
278
- : cursor.since !== undefined ? row.sequence > cursor.since : true);
333
+ : cursor.since !== undefined
334
+ ? row.sequence > cursor.since
335
+ : true);
279
336
  const rows = cursor.since !== undefined ? eligible.slice(0, cursor.limit) : eligible.slice(-cursor.limit);
280
337
  const omitted = Math.max(0, eligible.length - rows.length);
281
338
  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);
339
+ ? ledger.rows[0] !== undefined && ledger.rows[0].sequence > cursor.since + 1
340
+ : ledger.retained.lowestRetained !== null && ledger.retained.lowestRetained > 1 && omitted === 0;
284
341
  return {
285
342
  rows,
286
343
  omitted,
@@ -1,9 +1,11 @@
1
+ export type SystemPromptMode = "append" | "replace";
1
2
  export type ProviderOptions = Readonly<{
2
3
  model?: string;
3
4
  effort?: string;
4
5
  readonly?: true;
5
6
  network?: "disabled" | "enabled";
6
7
  systemPrompt?: string;
8
+ systemPromptMode?: SystemPromptMode;
7
9
  }>;
8
10
  export type ReadonlyRestraint = Readonly<{
9
11
  enforcement: "native";
@@ -34,12 +34,22 @@ export function decodeProviderOptions(value) {
34
34
  throw new TypeError("provider option network must be disabled, enabled");
35
35
  }
36
36
  const systemPrompt = optionText(options, "systemPrompt", "allow");
37
+ const systemPromptMode = options.systemPromptMode;
38
+ if (systemPromptMode !== undefined) {
39
+ if (systemPromptMode !== "append" && systemPromptMode !== "replace") {
40
+ throw new TypeError("provider option systemPromptMode must be append, replace");
41
+ }
42
+ if (systemPrompt === undefined) {
43
+ throw new TypeError("provider option systemPromptMode requires systemPrompt");
44
+ }
45
+ }
37
46
  return Object.freeze({
38
47
  ...(model === undefined ? {} : { model }),
39
48
  ...(effort === undefined ? {} : { effort }),
40
49
  ...(options.readonly === undefined ? {} : { readonly: true }),
41
50
  ...(network === undefined ? {} : { network }),
42
51
  ...(systemPrompt === undefined ? {} : { systemPrompt }),
52
+ ...(systemPromptMode === undefined ? {} : { systemPromptMode }),
43
53
  });
44
54
  }
45
55
  export function decodeReadonlyRestraint(value) {
@@ -49,15 +59,20 @@ export function decodeReadonlyRestraint(value) {
49
59
  if (restraint.enforcement === "native") {
50
60
  return Object.freeze({ enforcement: "native" });
51
61
  }
52
- if (restraint.enforcement === "none"
53
- && 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) {
54
65
  return Object.freeze({ enforcement: "none", diagnostic: restraint.diagnostic });
55
66
  }
56
67
  throw new TypeError("readonly restraint must be native or none with a diagnostic");
57
68
  }
58
69
  function providerKind(value) {
59
- return value === "acp" || value === "claude-agent-sdk" || value === "codex-app-server" || value === "grok-build"
60
- || 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");
61
76
  }
62
77
  export function decodeProviderRecipe(input) {
63
78
  const value = record(input);
@@ -68,7 +83,8 @@ export function decodeProviderRecipe(input) {
68
83
  }
69
84
  if (!providerKind(value.kind))
70
85
  throw new TypeError("provider execution has unknown kind");
71
- 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)) {
72
88
  throw new TypeError("provider execution executable must be a nonblank string");
73
89
  }
74
90
  const config = value.config === undefined ? undefined : record(value.config);