@astrosheep/keiyaku 4.0.1 → 4.0.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 (245) hide show
  1. package/README.md +137 -5
  2. package/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +1 -1
  3. package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +1 -1
  4. package/build/integrations/marketplace/plugins/keiyaku/package.json +1 -1
  5. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +3 -3
  6. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +91 -35
  7. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +98 -58
  8. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +152 -53
  9. package/build/src/akuma/abort.d.ts +2 -0
  10. package/build/src/akuma/abort.js +50 -0
  11. package/build/src/akuma/akuma.d.ts +23 -14
  12. package/build/src/akuma/akuma.js +120 -104
  13. package/build/src/akuma/archetype.d.ts +7 -6
  14. package/build/src/akuma/archetype.js +68 -41
  15. package/build/src/akuma/body.d.ts +3 -5
  16. package/build/src/akuma/body.js +408 -213
  17. package/build/src/akuma/heart/facts.d.ts +25 -34
  18. package/build/src/akuma/heart/facts.js +18 -7
  19. package/build/src/akuma/heart/index.d.ts +39 -42
  20. package/build/src/akuma/heart/index.js +134 -93
  21. package/build/src/akuma/heart/rows.d.ts +9 -31
  22. package/build/src/akuma/heart/rows.js +26 -44
  23. package/build/src/akuma/heart/schema.d.ts +1 -1
  24. package/build/src/akuma/heart/schema.js +7 -15
  25. package/build/src/akuma/heart/soul.d.ts +8 -0
  26. package/build/src/akuma/heart/soul.js +136 -0
  27. package/build/src/akuma/heart/storage.d.ts +28 -14
  28. package/build/src/akuma/heart/storage.js +86 -26
  29. package/build/src/akuma/identity.d.ts +2 -2
  30. package/build/src/akuma/identity.js +9 -9
  31. package/build/src/akuma/index.d.ts +6 -1
  32. package/build/src/akuma/index.js +6 -0
  33. package/build/src/akuma/projection.d.ts +117 -38
  34. package/build/src/akuma/projection.js +208 -97
  35. package/build/src/akuma/provider-recipe.d.ts +24 -0
  36. package/build/src/akuma/provider-recipe.js +100 -0
  37. package/build/src/akuma/provider.d.ts +12 -4
  38. package/build/src/akuma/provider.js +117 -70
  39. package/build/src/akuma/providers/acp/core.d.ts +19 -0
  40. package/build/src/akuma/providers/acp/core.js +159 -0
  41. package/build/src/akuma/providers/acp/events.d.ts +20 -0
  42. package/build/src/akuma/providers/acp/events.js +73 -0
  43. package/build/src/akuma/providers/acp/index.d.ts +12 -0
  44. package/build/src/akuma/providers/acp/index.js +94 -0
  45. package/build/src/akuma/providers/claude/events.js +66 -13
  46. package/build/src/akuma/providers/claude/index.d.ts +2 -1
  47. package/build/src/akuma/providers/claude/index.js +35 -21
  48. package/build/src/akuma/providers/codex-app-server/events.js +4 -1
  49. package/build/src/akuma/providers/codex-app-server/index.d.ts +3 -3
  50. package/build/src/akuma/providers/codex-app-server/index.js +24 -31
  51. package/build/src/akuma/providers/grok-build/index.d.ts +4 -0
  52. package/build/src/akuma/providers/grok-build/index.js +70 -0
  53. package/build/src/akuma/providers/index.d.ts +5 -3
  54. package/build/src/akuma/providers/index.js +24 -59
  55. package/build/src/akuma/providers/opencode-sdk/events.d.ts +40 -34
  56. package/build/src/akuma/providers/opencode-sdk/events.js +220 -101
  57. package/build/src/akuma/providers/opencode-sdk/index.d.ts +3 -2
  58. package/build/src/akuma/providers/opencode-sdk/index.js +233 -128
  59. package/build/src/akuma/providers/opencode-sdk/session.d.ts +11 -26
  60. package/build/src/akuma/providers/opencode-sdk/session.js +10 -9
  61. package/build/src/akuma/providers/pi/events.js +49 -11
  62. package/build/src/akuma/providers/pi/index.d.ts +1 -1
  63. package/build/src/akuma/providers/pi/index.js +50 -21
  64. package/build/src/akuma/publication.d.ts +3 -3
  65. package/build/src/akuma/publication.js +28 -40
  66. package/build/src/akuma/requests.d.ts +9 -7
  67. package/build/src/akuma/requests.js +92 -65
  68. package/build/src/alias/index.d.ts +2 -2
  69. package/build/src/alias/index.js +10 -10
  70. package/build/src/body/decode.js +7 -1
  71. package/build/src/body/region.d.ts +2 -0
  72. package/build/src/body/region.js +77 -7
  73. package/build/src/cli/accepted.d.ts +17 -13
  74. package/build/src/cli/accepted.js +72 -34
  75. package/build/src/cli/actor.js +3 -3
  76. package/build/src/cli/commands/akuma-invoke.d.ts +4 -3
  77. package/build/src/cli/commands/akuma-invoke.js +14 -9
  78. package/build/src/cli/commands/akuma.d.ts +13 -3
  79. package/build/src/cli/commands/akuma.js +67 -34
  80. package/build/src/cli/commands/contract.d.ts +18 -5
  81. package/build/src/cli/commands/contract.js +9 -3
  82. package/build/src/cli/commands/task-invoke.d.ts +17 -4
  83. package/build/src/cli/commands/task-invoke.js +43 -20
  84. package/build/src/cli/commands/task-query.d.ts +5 -0
  85. package/build/src/cli/commands/task-query.js +247 -0
  86. package/build/src/cli/commands/task.d.ts +3 -1
  87. package/build/src/cli/commands/task.js +51 -13
  88. package/build/src/cli/coordinates.d.ts +18 -0
  89. package/build/src/cli/coordinates.js +120 -0
  90. package/build/src/cli/draft.d.ts +8 -0
  91. package/build/src/cli/draft.js +96 -0
  92. package/build/src/cli/invoke.d.ts +4 -3
  93. package/build/src/cli/invoke.js +160 -179
  94. package/build/src/cli/main.js +34 -6
  95. package/build/src/cli/parse.d.ts +9 -2
  96. package/build/src/cli/parse.js +29 -7
  97. package/build/src/cli/render/akuma-tool-command.d.ts +6 -0
  98. package/build/src/cli/render/akuma-tool-command.js +144 -0
  99. package/build/src/cli/render/akuma-tool.d.ts +2 -2
  100. package/build/src/cli/render/akuma-tool.js +30 -4
  101. package/build/src/cli/render/akuma.d.ts +1 -0
  102. package/build/src/cli/render/akuma.js +211 -116
  103. package/build/src/cli/render/audit.d.ts +3 -0
  104. package/build/src/cli/render/audit.js +104 -0
  105. package/build/src/cli/render/contract.d.ts +3 -3
  106. package/build/src/cli/render/contract.js +192 -79
  107. package/build/src/cli/render/kanshi.js +263 -168
  108. package/build/src/cli/render/receipt.d.ts +19 -0
  109. package/build/src/cli/render/receipt.js +106 -0
  110. package/build/src/cli/render/refusal.d.ts +16 -2
  111. package/build/src/cli/render/refusal.js +88 -20
  112. package/build/src/cli/render/region.d.ts +2 -0
  113. package/build/src/cli/render/region.js +17 -0
  114. package/build/src/cli/render/task.d.ts +2 -1
  115. package/build/src/cli/render/task.js +223 -60
  116. package/build/src/cli/render/terminal.d.ts +7 -0
  117. package/build/src/cli/render/terminal.js +46 -0
  118. package/build/src/cli/render/text.js +6 -3
  119. package/build/src/cli/result.d.ts +115 -20
  120. package/build/src/cli/usage.d.ts +1 -0
  121. package/build/src/cli/usage.js +3 -0
  122. package/build/src/contract-worktree.d.ts +6 -5
  123. package/build/src/contract-worktree.js +95 -57
  124. package/build/src/coordination/durable-file.d.ts +4 -3
  125. package/build/src/coordination/durable-file.js +29 -25
  126. package/build/src/coordination/sqlite-transaction-lock.d.ts +4 -0
  127. package/build/src/coordination/sqlite-transaction-lock.js +22 -5
  128. package/build/src/core/facts/fold.js +1 -6
  129. package/build/src/core/facts/gate.d.ts +1 -0
  130. package/build/src/core/facts/gate.js +1 -0
  131. package/build/src/core/verbs/amend.d.ts +1 -1
  132. package/build/src/core/verbs/amend.js +0 -11
  133. package/build/src/core/verbs/deliver.d.ts +1 -1
  134. package/build/src/core/verbs/deliver.js +1 -7
  135. package/build/src/core/verbs/placement.d.ts +1 -1
  136. package/build/src/core/verbs/placement.js +4 -1
  137. package/build/src/dispatch/index.d.ts +2 -2
  138. package/build/src/dispatch/index.js +17 -17
  139. package/build/src/git/admission.d.ts +1 -1
  140. package/build/src/git/admission.js +11 -11
  141. package/build/src/git/hooks.js +13 -13
  142. package/build/src/git/integration.d.ts +14 -15
  143. package/build/src/git/integration.js +80 -44
  144. package/build/src/git/observe.d.ts +5 -3
  145. package/build/src/git/observe.js +18 -12
  146. package/build/src/git/read-observation.js +9 -9
  147. package/build/src/git/reconcile.d.ts +2 -0
  148. package/build/src/git/reconcile.js +138 -95
  149. package/build/src/git/repository.d.ts +27 -19
  150. package/build/src/git/repository.js +155 -83
  151. package/build/src/git/scratch.d.ts +7 -3
  152. package/build/src/git/scratch.js +44 -37
  153. package/build/src/git/target-placement.d.ts +40 -4
  154. package/build/src/git/target-placement.js +221 -101
  155. package/build/src/git/tender.d.ts +6 -5
  156. package/build/src/git/tender.js +24 -43
  157. package/build/src/git/terminal-seal.d.ts +1 -1
  158. package/build/src/git/terminal-seal.js +7 -5
  159. package/build/src/git/workspace.d.ts +36 -4
  160. package/build/src/git/workspace.js +53 -18
  161. package/build/src/identity/selector.js +1 -1
  162. package/build/src/index.d.ts +3 -3
  163. package/build/src/index.js +1 -1
  164. package/build/src/kanshi/index.d.ts +2 -2
  165. package/build/src/kanshi/index.js +1 -1
  166. package/build/src/kanshi/read.d.ts +2 -1
  167. package/build/src/kanshi/read.js +118 -27
  168. package/build/src/kanshi/report.d.ts +50 -0
  169. package/build/src/kanshi/select.d.ts +5 -0
  170. package/build/src/kanshi/select.js +32 -0
  171. package/build/src/library/address.d.ts +12 -3
  172. package/build/src/library/address.js +77 -46
  173. package/build/src/library/akuma-creation.d.ts +6 -1
  174. package/build/src/library/akuma-creation.js +86 -13
  175. package/build/src/library/audit.d.ts +18 -0
  176. package/build/src/library/audit.js +44 -0
  177. package/build/src/library/bind.js +4 -4
  178. package/build/src/library/catalog.d.ts +1 -1
  179. package/build/src/library/catalog.js +5 -8
  180. package/build/src/library/contract.d.ts +5 -6
  181. package/build/src/library/contract.js +11 -35
  182. package/build/src/library/delivery.d.ts +2 -1
  183. package/build/src/library/fleet.d.ts +2 -2
  184. package/build/src/library/fleet.js +35 -71
  185. package/build/src/library/keiyaku.d.ts +3 -2
  186. package/build/src/library/keiyaku.js +1 -0
  187. package/build/src/library/mutation.d.ts +7 -6
  188. package/build/src/library/mutation.js +23 -25
  189. package/build/src/library/reconcile.d.ts +29 -0
  190. package/build/src/library/reconcile.js +179 -0
  191. package/build/src/library/region.d.ts +12 -0
  192. package/build/src/library/region.js +15 -1
  193. package/build/src/library/repo.d.ts +4 -16
  194. package/build/src/library/repo.js +13 -38
  195. package/build/src/protocol/attempt.js +6 -6
  196. package/build/src/protocol/bind.js +4 -4
  197. package/build/src/protocol/intent.d.ts +15 -2
  198. package/build/src/protocol/intent.js +21 -6
  199. package/build/src/protocol/operations.d.ts +73 -22
  200. package/build/src/protocol/operations.js +292 -88
  201. package/build/src/protocol/outcome.d.ts +9 -3
  202. package/build/src/protocol/outcome.js +3 -1
  203. package/build/src/protocol/placement.d.ts +15 -4
  204. package/build/src/protocol/placement.js +58 -25
  205. package/build/src/protocol/read/status.d.ts +8 -1
  206. package/build/src/protocol/read/status.js +61 -14
  207. package/build/src/runtime/proc/line-rpc.d.ts +3 -8
  208. package/build/src/runtime/proc/line-rpc.js +16 -43
  209. package/build/src/runtime/proc/run.d.ts +15 -29
  210. package/build/src/runtime/proc/run.js +113 -105
  211. package/build/src/runtime/proc/stdio.d.ts +18 -0
  212. package/build/src/runtime/proc/stdio.js +58 -0
  213. package/build/src/settings.d.ts +2 -2
  214. package/build/src/settings.js +8 -8
  215. package/build/src/settlement/fence.d.ts +0 -4
  216. package/build/src/settlement/fence.js +0 -9
  217. package/build/src/settlement/holder.d.ts +11 -1
  218. package/build/src/settlement/holder.js +25 -0
  219. package/build/src/settlement/settle.d.ts +1 -1
  220. package/build/src/settlement/settle.js +52 -89
  221. package/build/src/task/board.d.ts +3 -3
  222. package/build/src/task/board.js +6 -9
  223. package/build/src/task/compose.d.ts +1 -1
  224. package/build/src/task/compose.js +18 -10
  225. package/build/src/task/context.d.ts +3 -3
  226. package/build/src/task/context.js +14 -14
  227. package/build/src/task/document.d.ts +2 -1
  228. package/build/src/task/document.js +15 -5
  229. package/build/src/task/index.d.ts +19 -7
  230. package/build/src/task/index.js +42 -23
  231. package/build/src/task/operations.d.ts +15 -9
  232. package/build/src/task/operations.js +78 -48
  233. package/build/src/task/query.d.ts +67 -0
  234. package/build/src/task/query.js +279 -0
  235. package/build/src/task/store.d.ts +2 -2
  236. package/build/src/task/store.js +40 -25
  237. package/build/src/verification/execution.d.ts +4 -2
  238. package/build/src/verification/execution.js +9 -5
  239. package/build/src/workspace-place.d.ts +41 -0
  240. package/build/src/workspace-place.js +386 -0
  241. package/build/src/world.d.ts +13 -2
  242. package/build/src/world.js +81 -48
  243. package/package.json +3 -2
  244. package/build/src/protocol/read/audit.d.ts +0 -31
  245. package/build/src/protocol/read/audit.js +0 -55
@@ -1,6 +1,6 @@
1
1
  import { decodeAgentEvent } from "./provider.js";
2
- const TAIL_LIMIT = 3;
3
- const VOICE_LIMIT = 5;
2
+ const DEFAULT_TAIL = 3;
3
+ const DEFAULT_VOICE = 3;
4
4
  function duration(startedAt, completedAt) {
5
5
  const start = Date.parse(startedAt);
6
6
  const end = Date.parse(completedAt);
@@ -22,124 +22,235 @@ function narrationRow(fact, event) {
22
22
  }
23
23
  return null;
24
24
  }
25
- /** Project the complete retained fact window in persisted timeline order. */
26
- export function projectTimeline(facts) {
27
- const state = { rows: [], running: new Map() };
28
- for (const fact of facts) {
29
- if (fact.kind === "turn-start")
30
- state.rows.push({ settled: false, row: { kind: "turn", sequence: fact.sequence, turnSequence: fact.sequence, bodySequence: fact.bodySequence, at: fact.startedAt } });
31
- else if (fact.kind === "turn-end")
32
- state.rows.push({ settled: true, row: { kind: "outcome", sequence: fact.sequence, turnSequence: fact.turnSequence, at: fact.completedAt, outcome: fact.outcome } });
33
- else if (fact.kind === "call")
34
- state.rows.push({ settled: true, row: { kind: "call", sequence: fact.sequence, turnSequence: fact.turnSequence, at: fact.at, text: fact.body } });
35
- else if (fact.kind === "tell")
36
- state.rows.push({ settled: fact.state !== "pending", row: { kind: "tell", sequence: fact.sequence, at: fact.recordedAt, tellId: fact.id, text: fact.body, state: fact.state, deliveries: fact.deliveries } });
37
- else
38
- projectActivityEvent(state, fact);
25
+ function projectToolEvent(rows, running, fact, event, phase) {
26
+ const key = toolKey(fact.turnSequence, event.id);
27
+ if (event.phase === "started") {
28
+ running.set(key, rows.length);
29
+ rows.push({
30
+ kind: "tool",
31
+ sequence: fact.sequence,
32
+ turnSequence: fact.turnSequence,
33
+ at: fact.at,
34
+ name: event.name,
35
+ call: event.call,
36
+ state: phase === "open" ? "active" : "unsettled",
37
+ ...(event.truncated === true ? { truncated: true } : {}),
38
+ });
39
+ return;
40
+ }
41
+ const index = running.get(key);
42
+ const started = index === undefined ? undefined : rows[index];
43
+ if (started?.kind !== "tool") {
44
+ 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 } : {}) });
45
+ return;
39
46
  }
40
- return state.rows.map((entry) => entry.row);
47
+ const durationMs = duration(started.at, fact.at);
48
+ rows[index] = {
49
+ kind: "tool",
50
+ sequence: started.sequence,
51
+ turnSequence: fact.turnSequence,
52
+ at: started.at,
53
+ completedAt: fact.at,
54
+ ...(durationMs === undefined ? {} : { durationMs }),
55
+ name: event.name,
56
+ call: event.call,
57
+ state: event.result,
58
+ ...(started.truncated === true || event.truncated === true ? { truncated: true } : {}),
59
+ };
60
+ running.delete(key);
41
61
  }
42
62
  function projectActivityEvent(state, fact) {
43
63
  const event = decodeAgentEvent(fact.event);
44
64
  const narration = narrationRow(fact, event);
45
65
  if (narration !== null) {
46
- state.rows.push({ settled: true, row: narration });
66
+ const turn = state.turnsBySequence.get(fact.turnSequence);
67
+ if (turn === undefined)
68
+ state.orphanRows.push(narration);
69
+ else
70
+ turn.rows.push(narration);
47
71
  return;
48
72
  }
49
73
  if (event.type !== "tool")
50
74
  return;
51
- const key = toolKey(fact.turnSequence, event.id);
52
- if (event.phase === "started") {
53
- state.running.set(key, state.rows.length);
54
- state.rows.push({ settled: false, row: { kind: "tool", sequence: fact.sequence, turnSequence: fact.turnSequence, at: fact.at, name: event.name, call: event.call, state: "running", ...(event.truncated === true ? { truncated: true } : {}) } });
55
- return;
75
+ const turn = state.turnsBySequence.get(fact.turnSequence);
76
+ if (turn === undefined)
77
+ projectToolEvent(state.orphanRows, state.orphanRunning, fact, event, "closed");
78
+ else
79
+ projectToolEvent(turn.rows, turn.running, fact, event, turn.phase);
80
+ }
81
+ function demoteActive(row) {
82
+ if (row.kind !== "tool" || row.state !== "active")
83
+ return row;
84
+ return { ...row, state: "unsettled" };
85
+ }
86
+ function foldOutcomeVoice(rows, outcome) {
87
+ if (outcome?.outcome.kind !== "answered")
88
+ return rows;
89
+ const finalVoice = rows.findLast((row) => row.kind === "said");
90
+ return finalVoice?.text === outcome.outcome.answer && finalVoice.truncated !== true
91
+ ? rows.filter((row) => row !== finalVoice)
92
+ : rows;
93
+ }
94
+ function finishTurn(turn) {
95
+ const rows = foldOutcomeVoice(turn.rows, turn.outcome);
96
+ if (turn.phase === "closed" && turn.outcome !== undefined) {
97
+ return { kind: "closed", turn: turn.turn, rows: rows.map(demoteActive), outcome: turn.outcome };
56
98
  }
57
- const index = state.running.get(key);
58
- const started = index === undefined ? undefined : state.rows[index]?.row;
59
- if (started?.kind !== "tool") {
60
- state.rows.push({ settled: true, row: {
61
- kind: "tool", sequence: fact.sequence, turnSequence: fact.turnSequence, at: fact.at,
62
- name: event.name, call: event.call, state: event.result,
63
- ...(event.truncated === true ? { truncated: true } : {}),
64
- } });
65
- return;
99
+ if (rows.some((row) => row.kind === "tool" && row.state === "unsettled")) {
100
+ throw new Error(`open Turn ${turn.turn.turnSequence} contains an unsettled tool`);
66
101
  }
67
- const startedAt = started.at;
68
- const durationMs = duration(startedAt, fact.at);
69
- const completed = {
70
- kind: "tool", sequence: started.sequence, turnSequence: fact.turnSequence, at: startedAt,
71
- completedAt: fact.at, ...(durationMs === undefined ? {} : { durationMs }),
72
- name: event.name, call: event.call, state: event.result,
73
- ...(started?.kind === "tool" && started.truncated === true || event.truncated === true ? { truncated: true } : {}),
102
+ return { kind: "open", turn: turn.turn, rows: rows };
103
+ }
104
+ /** Fold the retained fact timeline into Turn-owned rows without inventing provider facts. */
105
+ export function projectTurns(facts, retained = {
106
+ lowestRetained: facts[0]?.sequence ?? null,
107
+ highest: facts.at(-1)?.sequence ?? null,
108
+ }) {
109
+ const state = {
110
+ turns: [],
111
+ turnsBySequence: new Map(),
112
+ tells: [],
113
+ orphanRows: [],
114
+ orphanOutcomes: [],
115
+ orphanRunning: new Map(),
74
116
  };
75
- state.rows[index] = { settled: true, row: completed };
76
- state.running.delete(key);
77
- }
78
- function pinned(row, latestOutcome, openTurn) {
79
- return row === latestOutcome
80
- || (row.kind === "turn" && row.turnSequence === openTurn)
81
- || (row.kind === "call" && row.turnSequence === openTurn)
82
- || (row.kind === "tool" && row.state === "running")
83
- || (row.kind === "tell" && row.state === "pending");
84
- }
85
- /** Apply the bounded snapshot policy after semantic projection. */
86
- export function selectActivitySnapshot(facts, input = {}) {
87
- const projected = projectTimeline(facts);
88
- const ended = new Set(projected.filter((row) => row.kind === "outcome").map((row) => row.turnSequence));
89
- const openTurn = projected.findLast((row) => row.kind === "turn" && !ended.has(row.turnSequence));
90
- const latestOutcome = projected.findLast((row) => row.kind === "outcome");
91
- const ordinary = projected.filter((row) => !pinned(row, latestOutcome, openTurn?.turnSequence));
92
- const selected = new Set(ordinary.slice(-TAIL_LIMIT));
93
- if ((input.profile ?? "status") === "status") {
94
- for (const row of ordinary.filter((value) => value.kind === "said" || value.kind === "thought").slice(-VOICE_LIMIT)) {
95
- selected.add(row);
117
+ for (const fact of facts) {
118
+ if (fact.kind === "turn-start") {
119
+ const turn = { kind: "turn", sequence: fact.sequence, turnSequence: fact.sequence, bodySequence: fact.bodySequence, at: fact.startedAt };
120
+ const projected = { phase: "open", turn, rows: [], running: new Map() };
121
+ state.turns.push(projected);
122
+ state.turnsBySequence.set(turn.turnSequence, projected);
123
+ }
124
+ else if (fact.kind === "turn-end") {
125
+ const outcome = { kind: "outcome", sequence: fact.sequence, turnSequence: fact.turnSequence, at: fact.completedAt, outcome: fact.outcome };
126
+ const turn = state.turnsBySequence.get(fact.turnSequence);
127
+ if (turn === undefined)
128
+ state.orphanOutcomes.push(outcome);
129
+ else {
130
+ turn.phase = "closed";
131
+ turn.rows = turn.rows.map(demoteActive);
132
+ turn.outcome = outcome;
133
+ }
134
+ }
135
+ else if (fact.kind === "call") {
136
+ const row = { kind: "call", sequence: fact.sequence, turnSequence: fact.turnSequence, at: fact.at, text: fact.body };
137
+ const turn = state.turnsBySequence.get(fact.turnSequence);
138
+ if (turn === undefined)
139
+ state.orphanRows.push(row);
140
+ else
141
+ turn.rows.push(row);
142
+ }
143
+ else if (fact.kind === "tell") {
144
+ state.tells.push({ kind: "tell", sequence: fact.sequence, at: fact.recordedAt, tellId: fact.id, text: fact.body, state: fact.state, deliveries: fact.deliveries });
145
+ }
146
+ else {
147
+ projectActivityEvent(state, fact);
96
148
  }
97
149
  }
98
- for (const row of projected)
99
- if (pinned(row, latestOutcome, openTurn?.turnSequence))
100
- selected.add(row);
101
- const entries = [];
102
- let hidden = [];
103
- const flush = () => {
104
- if (hidden.length === 1)
105
- entries.push({ kind: "row", row: hidden[0] });
106
- else if (hidden.length > 1)
107
- entries.push({ kind: "gap", count: hidden.length });
108
- hidden = [];
150
+ const turns = state.turns.map(finishTurn);
151
+ const rows = [
152
+ ...turns.flatMap((turn) => [turn.turn, ...turn.rows, ...(turn.kind === "closed" ? [turn.outcome] : [])]),
153
+ ...state.tells,
154
+ ...state.orphanRows,
155
+ ...state.orphanOutcomes,
156
+ ].sort((left, right) => left.sequence - right.sequence);
157
+ const frontier = turns.at(-1);
158
+ const openTurn = frontier?.kind === "open" ? frontier : undefined;
159
+ const latestOutcome = rows.findLast((row) => row.kind === "outcome");
160
+ return { rows, turns, retained, ...(openTurn === undefined ? {} : { openTurn }), ...(latestOutcome === undefined ? {} : { latestOutcome }) };
161
+ }
162
+ function entries(rows) {
163
+ return rows.map((row) => ({ kind: "row", row }));
164
+ }
165
+ function openEntries(ledger, window, selected) {
166
+ const windowRows = new Set(window);
167
+ const result = [];
168
+ let hidden = 0;
169
+ const flushGap = () => {
170
+ if (hidden === 0)
171
+ return;
172
+ result.push({ kind: "gap", count: hidden });
173
+ hidden = 0;
109
174
  };
110
- for (const row of projected) {
175
+ for (const row of ledger.rows) {
176
+ if (windowRows.has(row) && !selected.has(row)) {
177
+ hidden += 1;
178
+ continue;
179
+ }
111
180
  if (!selected.has(row))
112
- hidden.push(row);
113
- else {
114
- flush();
115
- entries.push({ kind: "row", row });
181
+ continue;
182
+ flushGap();
183
+ if (row.kind !== "turn" && row.kind !== "outcome" && !(row.kind === "tool" && row.state === "unsettled")) {
184
+ result.push({ kind: "row", row });
116
185
  }
117
186
  }
118
- flush();
187
+ flushGap();
188
+ return result;
189
+ }
190
+ /** Split one nonnegative ordinary allowance into newest tail first, then pre-tail voice. */
191
+ export function ordinarySnapshotBudget(ordinaryBudget) {
192
+ const tail = Math.min(DEFAULT_TAIL, Math.max(0, ordinaryBudget));
193
+ return { tail, voice: Math.min(DEFAULT_VOICE, Math.max(0, ordinaryBudget) - tail) };
194
+ }
195
+ /** Count selected ordinary rows; active tools and pending tells stay outside this total. */
196
+ export function ordinarySelectedCount(snapshot) {
197
+ return snapshot.entries.reduce((count, entry) => {
198
+ if (entry.kind !== "row")
199
+ return count;
200
+ if (entry.row.kind === "tool" && entry.row.state === "active")
201
+ return count;
202
+ if (entry.row.kind === "tell" && entry.row.state === "pending")
203
+ return count;
204
+ return count + 1;
205
+ }, 0);
206
+ }
207
+ /** Select one current Turn, one latest outcome, or no focus; pending tells stay actionable. */
208
+ export function selectSnapshot(ledger, budget = { tail: DEFAULT_TAIL, voice: DEFAULT_VOICE }) {
209
+ if (ledger.turns.length === 0 && !ledger.rows.some((row) => row.kind === "tell"))
210
+ return { kind: "unborn", entries: [], omitted: 0 };
211
+ const pending = ledger.rows.filter((row) => row.kind === "tell" && row.state === "pending");
212
+ if (ledger.openTurn !== undefined) {
213
+ const window = ledger.openTurn.rows;
214
+ const active = window.filter((row) => row.kind === "tool" && row.state === "active");
215
+ const ordinary = window.filter((row) => !(row.kind === "tool" && row.state === "active"));
216
+ const tailCount = Math.max(0, budget.tail);
217
+ const tail = tailCount === 0 ? [] : ordinary.slice(-tailCount);
218
+ const tailSet = new Set(tail);
219
+ const voiceCount = Math.max(0, budget.voice ?? DEFAULT_VOICE);
220
+ const voiceCandidates = ordinary
221
+ .filter((row) => !tailSet.has(row) && (row.kind === "said" || row.kind === "thought"));
222
+ const voice = voiceCount === 0 ? [] : voiceCandidates.slice(-voiceCount);
223
+ const selected = new Set([...tail, ...voice, ...active, ...pending]);
224
+ const omitted = window.filter((row) => !selected.has(row)).length;
225
+ return { kind: "open", turn: ledger.openTurn.turn, entries: openEntries(ledger, window, selected), omitted };
226
+ }
119
227
  return {
120
- entries,
121
- lowestRetained: input.lowestRetained ?? (facts[0]?.sequence ?? null),
122
- highest: input.highest ?? (facts.at(-1)?.sequence ?? null),
228
+ kind: "idle",
229
+ ...(ledger.latestOutcome === undefined ? {} : { outcome: ledger.latestOutcome }),
230
+ entries: entries(pending),
231
+ omitted: 0,
123
232
  };
124
233
  }
125
- /** Apply cursor and limit to the one semantic timeline projection. */
126
- export function projectActivityHistory(slice, input) {
127
- const projected = projectTimeline(slice.rows);
128
- const eligible = projected.filter((row) => input.before !== undefined
129
- ? row.sequence < input.before
130
- : input.since !== undefined ? row.sequence > input.since : true);
131
- const rows = input.since !== undefined ? eligible.slice(0, input.limit) : eligible.slice(-input.limit);
132
- const forwardHistoryLost = input.since !== undefined
133
- && slice.rows[0] !== undefined
134
- && slice.rows[0].sequence > input.since + 1;
234
+ /** Apply the one snapshot policy used by every public observation consumer. */
235
+ export function selectActivitySnapshot(facts) {
236
+ return selectSnapshot(projectTurns(facts));
237
+ }
238
+ export function selectHistory(ledger, cursor) {
239
+ const eligible = ledger.rows.filter((row) => cursor.before !== undefined
240
+ ? row.sequence < cursor.before
241
+ : cursor.since !== undefined ? row.sequence > cursor.since : true);
242
+ const rows = cursor.since !== undefined ? eligible.slice(0, cursor.limit) : eligible.slice(-cursor.limit);
243
+ const omitted = Math.max(0, eligible.length - rows.length);
244
+ const historyLost = cursor.since !== undefined
245
+ ? (ledger.rows[0] !== undefined && ledger.rows[0].sequence > cursor.since + 1)
246
+ : (ledger.retained.lowestRetained !== null && ledger.retained.lowestRetained > 1 && omitted === 0);
135
247
  return {
136
248
  rows,
137
- omitted: Math.max(0, eligible.length - rows.length),
138
- hasEarlier: input.since === undefined && eligible.length > rows.length,
139
- hasLater: input.since !== undefined && eligible.length > rows.length,
140
- historyLost: forwardHistoryLost || (input.since === undefined && eligible.length === rows.length
141
- && slice.lowestRetained !== null && slice.lowestRetained > 1),
142
- lowestRetained: slice.lowestRetained,
143
- highest: slice.highest,
249
+ omitted,
250
+ hasEarlier: cursor.since === undefined && eligible.length > rows.length,
251
+ hasLater: cursor.since !== undefined && eligible.length > rows.length,
252
+ historyLost,
253
+ lowestRetained: ledger.retained.lowestRetained,
254
+ highest: ledger.retained.highest,
144
255
  };
145
256
  }
@@ -0,0 +1,24 @@
1
+ export type ProviderOptions = Readonly<{
2
+ model?: string;
3
+ effort?: string;
4
+ readonly?: true;
5
+ network?: "disabled" | "enabled";
6
+ systemPrompt?: string;
7
+ }>;
8
+ export type ReadonlyRestraint = Readonly<{
9
+ enforcement: "native";
10
+ diagnostic?: never;
11
+ } | {
12
+ enforcement: "none";
13
+ diagnostic: string;
14
+ }>;
15
+ export type ProviderExecution = Readonly<{
16
+ name: string;
17
+ kind: "acp" | "claude-agent-sdk" | "codex-app-server" | "grok-build" | "opencode-sdk" | "pi";
18
+ executable?: string;
19
+ config?: Readonly<Record<string, unknown>>;
20
+ env?: Readonly<Record<string, string>>;
21
+ }>;
22
+ export declare function decodeProviderOptions(value: unknown): ProviderOptions;
23
+ export declare function decodeReadonlyRestraint(value: unknown): ReadonlyRestraint;
24
+ export declare function decodeProviderRecipe(input: unknown): ProviderExecution;
@@ -0,0 +1,100 @@
1
+ function record(value) {
2
+ return value !== null && typeof value === "object" && !Array.isArray(value)
3
+ ? value
4
+ : null;
5
+ }
6
+ function snapshot(value) {
7
+ if (Array.isArray(value))
8
+ return Object.freeze(value.map(snapshot));
9
+ const object = record(value);
10
+ if (object === null)
11
+ return value;
12
+ return Object.freeze(Object.fromEntries(Object.entries(object).map(([key, item]) => [key, snapshot(item)])));
13
+ }
14
+ function optionText(options, field, blank) {
15
+ const selected = options[field];
16
+ if (selected === undefined)
17
+ return undefined;
18
+ if (typeof selected !== "string" || (blank === "refuse" && selected.trim().length === 0)) {
19
+ throw new TypeError(`provider option ${field} must be ${blank === "allow" ? "a string" : "a nonblank string"}`);
20
+ }
21
+ return selected;
22
+ }
23
+ export function decodeProviderOptions(value) {
24
+ const options = record(value);
25
+ if (options === null)
26
+ throw new TypeError("provider options must be an object");
27
+ const allowed = ["effort", "model", "network", "readonly", "systemPrompt"];
28
+ const unknown = Object.keys(options).find((key) => !allowed.includes(key));
29
+ if (unknown !== undefined)
30
+ throw new TypeError(`provider options have unknown field ${unknown}`);
31
+ const model = optionText(options, "model", "refuse");
32
+ const effort = optionText(options, "effort", "refuse");
33
+ if (options.readonly !== undefined && options.readonly !== true) {
34
+ throw new TypeError("provider option readonly must be true");
35
+ }
36
+ const network = options.network;
37
+ if (network !== undefined && network !== "disabled" && network !== "enabled") {
38
+ throw new TypeError("provider option network must be disabled, enabled");
39
+ }
40
+ const systemPrompt = optionText(options, "systemPrompt", "allow");
41
+ return Object.freeze({
42
+ ...(model === undefined ? {} : { model }),
43
+ ...(effort === undefined ? {} : { effort }),
44
+ ...(options.readonly === undefined ? {} : { readonly: true }),
45
+ ...(network === undefined ? {} : { network }),
46
+ ...(systemPrompt === undefined ? {} : { systemPrompt }),
47
+ });
48
+ }
49
+ export function decodeReadonlyRestraint(value) {
50
+ const restraint = record(value);
51
+ if (restraint === null)
52
+ throw new TypeError("readonly restraint must be an object");
53
+ const keys = Object.keys(restraint).sort();
54
+ if (restraint.enforcement === "native" && keys.length === 1 && keys[0] === "enforcement") {
55
+ return Object.freeze({ enforcement: "native" });
56
+ }
57
+ if (restraint.enforcement === "none"
58
+ && keys.length === 2 && keys[0] === "diagnostic" && keys[1] === "enforcement"
59
+ && typeof restraint.diagnostic === "string" && restraint.diagnostic.trim().length > 0) {
60
+ return Object.freeze({ enforcement: "none", diagnostic: restraint.diagnostic });
61
+ }
62
+ throw new TypeError("readonly restraint must be native or none with a diagnostic");
63
+ }
64
+ function providerKind(value) {
65
+ return value === "acp" || value === "claude-agent-sdk" || value === "codex-app-server" || value === "grok-build"
66
+ || value === "opencode-sdk" || value === "pi";
67
+ }
68
+ export function decodeProviderRecipe(input) {
69
+ const value = record(input);
70
+ if (value === null)
71
+ throw new TypeError("provider execution must be an object");
72
+ const allowed = ["config", "env", "executable", "kind", "name"];
73
+ const unknown = Object.keys(value).find((key) => !allowed.includes(key));
74
+ if (unknown !== undefined)
75
+ throw new TypeError(`provider execution has unknown field ${unknown}`);
76
+ if (typeof value.name !== "string" || value.name.trim().length === 0) {
77
+ throw new TypeError("provider execution name must be a nonblank string");
78
+ }
79
+ if (!providerKind(value.kind))
80
+ throw new TypeError("provider execution has unknown kind");
81
+ if (value.executable !== undefined && (typeof value.executable !== "string" || value.executable.trim().length === 0)) {
82
+ throw new TypeError("provider execution executable must be a nonblank string");
83
+ }
84
+ const config = value.config === undefined ? undefined : record(value.config);
85
+ if (config === null)
86
+ throw new TypeError("provider execution config must be an object");
87
+ const env = value.env === undefined ? undefined : record(value.env);
88
+ if (env === null)
89
+ throw new TypeError("provider execution env must be an object");
90
+ if (env !== undefined && Object.values(env).some((item) => typeof item !== "string")) {
91
+ throw new TypeError("provider execution env must contain only string values");
92
+ }
93
+ return Object.freeze({
94
+ name: value.name,
95
+ kind: value.kind,
96
+ ...(value.executable === undefined ? {} : { executable: value.executable }),
97
+ ...(config === undefined ? {} : { config: snapshot(config) }),
98
+ ...(env === undefined ? {} : { env: Object.freeze({ ...env }) }),
99
+ });
100
+ }
@@ -1,19 +1,25 @@
1
- import type { Confinement, ProviderOptions } from "./heart/index.js";
1
+ import type { Confinement } from "./heart/index.js";
2
2
  import { decodeResumeCoordinate, encodeResumeCoordinate, type ResumeCoordinate } from "./coordinate.js";
3
+ import type { ProviderOptions, ReadonlyRestraint } from "./provider-recipe.js";
3
4
  export type { ResumeCoordinate } from "./coordinate.js";
4
- export type { ProviderOptions } from "./heart/index.js";
5
5
  export declare const AKUMA_REQUESTS_ENV = "AKUMA_REQUESTS";
6
6
  export declare const AGENT_EVENT_TEXT_LIMIT = 16384;
7
7
  export declare const AGENT_THOUGHT_TEXT_LIMIT = 4000;
8
+ export type SearchScope = "content" | "files" | "web";
8
9
  export type ToolCall = Readonly<{
9
10
  kind: "run";
10
11
  command: string;
11
12
  }> | Readonly<{
12
13
  kind: "read";
13
14
  path: string;
15
+ offset?: number;
16
+ limit?: number;
14
17
  }> | Readonly<{
15
18
  kind: "search";
16
19
  query: string;
20
+ scope?: SearchScope;
21
+ path?: string;
22
+ glob?: string;
17
23
  }> | Readonly<{
18
24
  kind: "fileChange";
19
25
  changes: readonly Readonly<{
@@ -67,7 +73,6 @@ export type AgentEvent = Readonly<{
67
73
  truncated?: true;
68
74
  }>;
69
75
  export { decodeResumeCoordinate, encodeResumeCoordinate };
70
- export declare function decodeProviderOptions(value: unknown): ProviderOptions;
71
76
  export declare function decodeAgentEvent(value: unknown): AgentEvent;
72
77
  export declare function encodeAgentEvent(event: AgentEvent): unknown;
73
78
  export declare function boundedEventText(value: string): string;
@@ -89,7 +94,7 @@ export declare class AgentEventChannel implements AsyncIterable<AgentEvent> {
89
94
  export type TurnResult = Readonly<{
90
95
  kind: "answered";
91
96
  answer: string;
92
- historyId: string;
97
+ historyId?: string;
93
98
  }> | Readonly<{
94
99
  kind: "failed";
95
100
  diagnostic: string;
@@ -117,6 +122,7 @@ export type Session = Readonly<{
117
122
  events: AsyncIterable<AgentEvent>;
118
123
  receipts?: AsyncIterable<TellReceipt>;
119
124
  completion: Promise<TurnResult>;
125
+ /** Fulfills after every adapter-owned OS child or native session is disposed. */
120
126
  abort(): Promise<void>;
121
127
  tell?(tell: Readonly<{
122
128
  id: string;
@@ -131,6 +137,7 @@ export type DriveInput = Readonly<{
131
137
  }>[];
132
138
  cwd: string;
133
139
  options: ProviderOptions;
140
+ signal: AbortSignal;
134
141
  requests?: Readonly<{
135
142
  dir: string;
136
143
  }>;
@@ -138,6 +145,7 @@ export type DriveInput = Readonly<{
138
145
  export type ProviderOptionAdmission = Readonly<{
139
146
  kind: "admitted";
140
147
  options: ProviderOptions;
148
+ readonly?: ReadonlyRestraint;
141
149
  }> | Readonly<{
142
150
  kind: "refused";
143
151
  diagnostic: string;