@astrosheep/keiyaku 2.8.4 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (229) hide show
  1. package/README.md +35 -15
  2. package/build/.tsbuildinfo +1 -1
  3. package/build/agents/{call-terms_v2.js → call-terms.js} +12 -8
  4. package/build/agents/harness/control-types.js +1 -0
  5. package/build/agents/harness/execution-handle.js +23 -3
  6. package/build/agents/harness/index.js +6 -607
  7. package/build/agents/harness/outcome.js +141 -0
  8. package/build/agents/harness/projection.js +310 -0
  9. package/build/agents/harness/router.js +201 -0
  10. package/build/agents/harness/runtime.js +2 -1
  11. package/build/agents/{launch-snapshot_v2.js → launch-snapshot/model.js} +69 -74
  12. package/build/agents/launch-snapshot/resolve.js +72 -0
  13. package/build/agents/opencode-sdk.js +24 -3
  14. package/build/agents/providers/{claude-agent-sdk.js → claude-agent-sdk/adapter.js} +46 -368
  15. package/build/agents/providers/claude-agent-sdk/events.js +218 -0
  16. package/build/agents/providers/claude-agent-sdk/session.js +158 -0
  17. package/build/agents/providers/codex-app-server/adapter.js +347 -0
  18. package/build/agents/providers/codex-app-server/events.js +352 -0
  19. package/build/agents/providers/codex-app-server/session.js +266 -0
  20. package/build/agents/providers/codex-sdk.js +23 -5
  21. package/build/agents/providers/opencode-sdk/adapter.js +344 -0
  22. package/build/agents/providers/opencode-sdk/events.js +354 -0
  23. package/build/agents/providers/opencode-sdk/session.js +180 -0
  24. package/build/agents/providers/pi/adapter.js +269 -0
  25. package/build/agents/providers/pi/checkpoint.js +41 -0
  26. package/build/agents/providers/pi/events.js +282 -0
  27. package/build/agents/{selector_v2.js → selector.js} +8 -6
  28. package/build/cli/commands/akuma/akuma/handler.js +4 -0
  29. package/build/cli/commands/akuma/akuma/meta.js +16 -0
  30. package/build/cli/commands/akuma/catalog.js +8 -0
  31. package/build/cli/commands/akuma/list/handler.js +15 -0
  32. package/build/cli/commands/akuma/list/meta-ls.js +9 -0
  33. package/build/cli/commands/akuma/view/handler.js +34 -0
  34. package/build/cli/commands/akuma/view/meta.js +8 -0
  35. package/build/cli/commands/akuma.js +1 -1
  36. package/build/cli/commands/contract/amend/handler.js +10 -0
  37. package/build/cli/commands/contract/amend/meta.js +12 -0
  38. package/build/cli/commands/contract/arc/handler.js +9 -0
  39. package/build/cli/commands/contract/arc/meta.js +26 -0
  40. package/build/cli/commands/contract/bind/handler.js +23 -0
  41. package/build/cli/commands/contract/bind/meta.js +30 -0
  42. package/build/cli/commands/contract/catalog.js +16 -0
  43. package/build/cli/commands/contract/forfeit/handler.js +13 -0
  44. package/build/cli/commands/contract/forfeit/meta.js +12 -0
  45. package/build/cli/commands/contract/log/handler.js +12 -0
  46. package/build/cli/commands/contract/log/meta.js +12 -0
  47. package/build/cli/commands/contract/petition/handler.js +26 -0
  48. package/build/cli/commands/contract/petition/meta.js +23 -0
  49. package/build/cli/commands/contract/renew/handler.js +10 -0
  50. package/build/cli/commands/contract/renew/meta.js +17 -0
  51. package/build/cli/commands/projection/call/handler.js +39 -0
  52. package/build/cli/commands/projection/call/meta.js +9 -0
  53. package/build/cli/commands/projection/catalog.js +14 -0
  54. package/build/cli/commands/projection/kill/handler.js +35 -0
  55. package/build/cli/commands/projection/kill/meta.js +11 -0
  56. package/build/cli/commands/projection/revive/handler.js +53 -0
  57. package/build/cli/commands/projection/revive/meta.js +9 -0
  58. package/build/cli/commands/projection/status/handler.js +23 -0
  59. package/build/cli/commands/projection/status/meta.js +9 -0
  60. package/build/cli/commands/projection/tell/handler.js +34 -0
  61. package/build/cli/commands/projection/tell/meta.js +9 -0
  62. package/build/cli/commands/projection/wait/handler.js +21 -0
  63. package/build/cli/commands/projection/wait/meta.js +11 -0
  64. package/build/cli/commands/registry.js +100 -0
  65. package/build/cli/commands/shared.js +71 -0
  66. package/build/cli/commands/skills/catalog.js +6 -0
  67. package/build/cli/commands/skills/install/handler.js +10 -0
  68. package/build/cli/commands/skills/install/meta.js +9 -0
  69. package/build/cli/commands/skills/skills/handler.js +4 -0
  70. package/build/cli/commands/skills/skills/meta.js +10 -0
  71. package/build/cli/commands/system/catalog.js +8 -0
  72. package/build/cli/commands/system/completion/handler.js +10 -0
  73. package/build/cli/commands/system/completion/meta.js +9 -0
  74. package/build/cli/commands/system/dump-env/handler.js +3 -0
  75. package/build/cli/commands/system/dump-env/meta.js +10 -0
  76. package/build/cli/commands/system/guide/handler.js +4 -0
  77. package/build/cli/commands/system/guide/meta.js +9 -0
  78. package/build/cli/commands/task/add/handler.js +21 -0
  79. package/build/cli/commands/task/add/meta.js +8 -0
  80. package/build/cli/commands/task/catalog.js +22 -0
  81. package/build/cli/commands/task/doctor/handler.js +4 -0
  82. package/build/cli/commands/task/doctor/meta.js +8 -0
  83. package/build/cli/commands/task/done/handler.js +17 -0
  84. package/build/cli/commands/task/done/meta.js +8 -0
  85. package/build/cli/commands/task/drop/handler.js +6 -0
  86. package/build/cli/commands/task/drop/meta.js +8 -0
  87. package/build/cli/commands/task/ls/handler.js +13 -0
  88. package/build/cli/commands/task/ls/meta.js +8 -0
  89. package/build/cli/commands/task/shared.js +131 -0
  90. package/build/cli/commands/task/start/handler.js +6 -0
  91. package/build/cli/commands/task/start/meta.js +8 -0
  92. package/build/cli/commands/task/stop/handler.js +17 -0
  93. package/build/cli/commands/task/stop/meta.js +8 -0
  94. package/build/cli/commands/task/task/handler.js +5 -0
  95. package/build/cli/commands/task/task/meta.js +11 -0
  96. package/build/cli/commands/task/update/handler.js +18 -0
  97. package/build/cli/commands/task/update/meta.js +8 -0
  98. package/build/cli/commands/task/view/handler.js +14 -0
  99. package/build/cli/commands/task/view/meta.js +8 -0
  100. package/build/cli/completion.js +26 -27
  101. package/build/cli/flags.js +79 -2
  102. package/build/cli/help.js +27 -49
  103. package/build/cli/index.js +121 -374
  104. package/build/cli/output-stream-error-policy.js +17 -0
  105. package/build/cli/parse.js +159 -72
  106. package/build/cli/projection-address.js +5 -0
  107. package/build/cli/render/arc.js +1 -1
  108. package/build/cli/render/errors.js +4 -3
  109. package/build/cli/render/format.js +14 -0
  110. package/build/cli/render/kanshi.js +106 -0
  111. package/build/cli/render/petition.js +24 -1
  112. package/build/cli/render/projection-activity.js +5 -1
  113. package/build/cli/render/shared.js +2 -2
  114. package/build/cli/render/status.js +79 -13
  115. package/build/cli/render/task.js +113 -0
  116. package/build/cli/render/wait.js +23 -3
  117. package/build/cli/types.js +1 -1
  118. package/build/config/{akuma-loader_v2.js → akuma-loader.js} +2 -2
  119. package/build/config/env.js +9 -4
  120. package/build/config/settings/disease.js +26 -0
  121. package/build/config/settings/knobs.js +8 -0
  122. package/build/config/settings/loader.js +244 -0
  123. package/build/config/settings/schema.js +276 -0
  124. package/build/core/addressing.js +1 -1
  125. package/build/core/amend.js +3 -3
  126. package/build/core/arc.js +5 -4
  127. package/build/core/bind-workspace.js +123 -0
  128. package/build/core/bind.js +75 -155
  129. package/build/core/call/call.js +224 -0
  130. package/build/core/call/context.js +238 -0
  131. package/build/core/call/execution.js +217 -0
  132. package/build/core/call/prompt.js +42 -0
  133. package/build/core/call/reference.js +24 -0
  134. package/build/core/call-persist.js +1 -1
  135. package/build/core/claim-delivery.js +247 -0
  136. package/build/core/claim.js +39 -277
  137. package/build/core/context.js +2 -1
  138. package/build/core/{render.js → contract-view.js} +12 -140
  139. package/build/core/draft.js +107 -9
  140. package/build/core/entry.js +9 -1
  141. package/build/core/execution-coordinate.js +4 -20
  142. package/build/core/execution-pact.js +16 -31
  143. package/build/core/forfeit.js +35 -22
  144. package/build/core/hints.js +2 -2
  145. package/build/core/log.js +2 -2
  146. package/build/core/petition-claim-gates.js +8 -6
  147. package/build/core/petition-preview.js +97 -0
  148. package/build/core/petition-run.js +3 -2
  149. package/build/core/petition.js +39 -26
  150. package/build/core/projection/generation/database.js +306 -0
  151. package/build/core/projection/generation/identity.js +84 -0
  152. package/build/core/projection/generation/model.js +201 -0
  153. package/build/core/projection/generation/store.js +73 -0
  154. package/build/core/projection/generation/transitions.js +258 -0
  155. package/build/core/projection/heart.js +0 -307
  156. package/build/core/projection/tell/database.js +109 -0
  157. package/build/core/projection/tell/model.js +97 -0
  158. package/build/core/{projection-tells.js → projection/tell/store.js} +3 -158
  159. package/build/core/projection-activity.js +23 -2
  160. package/build/core/projection-core.js +2 -1
  161. package/build/core/projection-execution-observer.js +259 -0
  162. package/build/core/projection-generation-continuation.js +1 -1
  163. package/build/core/projection-generation-execution.js +90 -20
  164. package/build/core/projection-generation-launcher.js +1 -1
  165. package/build/core/projection-generation-runner.js +198 -4
  166. package/build/core/projection-identity.js +1 -1
  167. package/build/core/projection-kill.js +50 -17
  168. package/build/core/projection-life-observer.js +1 -1
  169. package/build/core/projection-life-protocol.js +10 -0
  170. package/build/core/projection-mint.js +1 -1
  171. package/build/core/projection-status.js +45 -32
  172. package/build/core/projection-wait.js +12 -2
  173. package/build/core/projection-wake.js +5 -2
  174. package/build/core/{queue_v2.js → queue.js} +2 -1
  175. package/build/core/registry.js +1 -1
  176. package/build/core/renew.js +7 -7
  177. package/build/core/response-artifact-catalog-sqlite.js +112 -0
  178. package/build/core/response-artifact-catalog.js +7 -0
  179. package/build/core/seal.js +44 -16
  180. package/build/core/status/board.js +286 -0
  181. package/build/core/status/drift.js +99 -0
  182. package/build/core/status/lifecycle.js +84 -0
  183. package/build/core/status/reconciliation.js +131 -0
  184. package/build/core/stored-agent-event.js +28 -4
  185. package/build/core/target-ref.js +10 -0
  186. package/build/core/task/board.js +175 -0
  187. package/build/core/task/commands.js +211 -0
  188. package/build/core/task/document.js +189 -0
  189. package/build/core/task/model.js +1 -0
  190. package/build/core/task/settlement-git.js +207 -0
  191. package/build/core/task/settlement-policy.js +82 -0
  192. package/build/core/task/source-board.js +84 -0
  193. package/build/core/task/source-model.js +1 -0
  194. package/build/core/task/validation.js +44 -0
  195. package/build/core/task-contract.js +224 -0
  196. package/build/core/task-doctor.js +14 -0
  197. package/build/core/task-git-actor.js +38 -0
  198. package/build/core/task-git-runtime.js +34 -0
  199. package/build/core/task-git-store.js +375 -0
  200. package/build/core/task-repository.js +1 -0
  201. package/build/core/task-store-repository.js +86 -0
  202. package/build/core/task-store.js +1 -0
  203. package/build/core/task-worktree.js +51 -0
  204. package/build/core/task.js +4 -0
  205. package/build/core/transcripts.js +138 -56
  206. package/build/core/validation.js +17 -0
  207. package/build/core/verdict.js +2 -2
  208. package/build/core/worktree-path.js +24 -6
  209. package/build/flow-error.js +3 -2
  210. package/build/generated/version.js +1 -1
  211. package/build/git/commits.js +3 -0
  212. package/build/git/diff/parsers.js +0 -94
  213. package/build/index.js +2 -0
  214. package/build/keiyaku-constants.js +2 -0
  215. package/build/keiyaku.js +4 -3
  216. package/package.json +3 -2
  217. package/skills/keiyaku/SKILL.md +1 -1
  218. package/skills/keiyaku-akuma/SKILL.md +11 -6
  219. package/build/agents/harness/pump.js +0 -158
  220. package/build/agents/providers/codex-app-server.js +0 -864
  221. package/build/agents/providers/opencode-sdk.js +0 -363
  222. package/build/agents/providers/pi.js +0 -557
  223. package/build/config/settings.js +0 -533
  224. package/build/core/call.js +0 -686
  225. package/build/core/projection/generation.js +0 -412
  226. package/build/core/projection/identity.js +0 -81
  227. package/build/core/projection/mint.js +0 -174
  228. package/build/core/projection-generation-store.js +0 -707
  229. package/build/core/status.js +0 -539
@@ -9,6 +9,15 @@ function duration(first, last) {
9
9
  return Math.max(0, last - first);
10
10
  }
11
11
  function warningIdentity(warning) {
12
+ if (warning.reason === "retry") {
13
+ return JSON.stringify({
14
+ reason: warning.reason,
15
+ attempt: warning.attempt,
16
+ maxRetries: warning.maxRetries,
17
+ retryDelayMs: warning.retryDelayMs,
18
+ errorStatus: warning.errorStatus,
19
+ });
20
+ }
12
21
  return JSON.stringify({
13
22
  reason: warning.reason,
14
23
  originalModel: warning.originalModel,
@@ -37,6 +46,7 @@ export function foldAgentActivity(events) {
37
46
  const latestThought = new Map();
38
47
  const firstThoughtAt = new Map();
39
48
  const thoughtEventCount = new Map();
49
+ const latestMessage = new Map();
40
50
  const warnings = new Map();
41
51
  const usageByTurn = new Map();
42
52
  const turns = [];
@@ -46,6 +56,9 @@ export function foldAgentActivity(events) {
46
56
  for (const indexed of sorted) {
47
57
  const event = indexed.event;
48
58
  switch (event.type) {
59
+ case "message":
60
+ latestMessage.set(JSON.stringify([event.turn, event.id]), indexed);
61
+ break;
49
62
  case "activity": {
50
63
  latestActivity.set(event.id, indexed);
51
64
  if (event.phase !== "updated" && !firstActivityCall.has(event.id))
@@ -96,8 +109,16 @@ export function foldAgentActivity(events) {
96
109
  const ledgerActivities = [];
97
110
  for (const indexed of sorted) {
98
111
  const event = indexed.event;
99
- if (event.type === "message") {
100
- settled.push({ kind: "said", order: indexed.order, at: event.at, turn: event.turn, text: event.text, settled: true });
112
+ if (event.type === "message" && latestMessage.get(JSON.stringify([event.turn, event.id])) === indexed) {
113
+ settled.push({
114
+ kind: "said",
115
+ order: indexed.order,
116
+ at: event.at,
117
+ turn: event.turn,
118
+ id: event.id,
119
+ text: event.text,
120
+ settled: true,
121
+ });
101
122
  continue;
102
123
  }
103
124
  if (event.type === "activity" && latestActivity.get(event.id) === indexed) {
@@ -4,7 +4,8 @@ import { isErrnoException } from "../errno.js";
4
4
  import { projectionCoordinateRoot } from "./projection-coordinate.js";
5
5
  import { ProjectionStateError, } from "./atomic-publish.js";
6
6
  export { atomicPublishFile, ProjectionStateError, randomHex8 } from "./atomic-publish.js";
7
- export { claimAllInbox, claimFencedTells, claimTellToInflight, demoteSubmittedTellsForReplay, isTellCausallyConsumed, listTellIds, markTellConsumed, markTellSubmitted, readTellOriginal, readTellSubmitted, snapshotPendingTells, writeInboxTell, } from "./projection-tells.js";
7
+ export { isTellCausallyConsumed, } from "./projection/tell/model.js";
8
+ export { claimAllInbox, claimFencedTells, claimTellToInflight, demoteSubmittedTellsForReplay, listTellIds, markTellConsumed, markTellSubmitted, readTellOriginal, readTellSubmitted, snapshotPendingTells, writeInboxTell, } from "./projection/tell/store.js";
8
9
  /** Relative segments for `.keiyaku/projection`. */
9
10
  export const PROJECTION_ROOT_SEGMENTS = [".keiyaku", "projection"];
10
11
  export const PROJECTION_ID_PATTERN = /^[a-zA-Z0-9]+(?:[.-][a-zA-Z0-9]+)*\/[0-9a-f]{8}$/;
@@ -0,0 +1,259 @@
1
+ import { claimAllInbox, claimFencedTells, listTellIds, markTellSubmitted, markTellConsumed, readTellSubmitted, readTellOriginal, isTellCausallyConsumed, appendGenerationEvent, } from "./projection-core.js";
2
+ import { openProjectionTellObserver, } from "./projection/tell/database.js";
3
+ function frameTellBodies(projectionDirectory, tellIds) {
4
+ const originals = tellIds.map((tellId) => readTellOriginal(projectionDirectory, "inflight", tellId));
5
+ const frames = originals
6
+ .map((original, index) => `--- tell ${tellIds[index]} ---\n${original.text}`)
7
+ .join("\n\n");
8
+ const effort = originals.reduce((value, original) => original.effort ?? value, undefined);
9
+ return { frames, ...(effort ? { effort } : {}) };
10
+ }
11
+ function operatorBodyForContinuation(prompt, tellFrames, sessionContinuationPromptPrefix) {
12
+ return sessionContinuationPromptPrefix === undefined
13
+ ? `${prompt}\n\n${tellFrames}`
14
+ : `${sessionContinuationPromptPrefix}${tellFrames}`;
15
+ }
16
+ /** Tier-2 boundary snapshot. No tells leaves prompt byte-identical. */
17
+ export function snapshotReplayPrompt(projectionDirectory, prompt, tellFence, executionId, options = {}) {
18
+ const tellIds = claimFencedTells(projectionDirectory, tellFence, executionId);
19
+ if (tellIds.length === 0)
20
+ return { prompt, tellIds };
21
+ const { frames, effort } = frameTellBodies(projectionDirectory, tellIds);
22
+ const nextPrompt = operatorBodyForContinuation(prompt, frames, options.sessionContinuationPromptPrefix);
23
+ return { prompt: nextPrompt, tellIds, ...(effort ? { effort } : {}) };
24
+ }
25
+ /** Tier-2 has no steer acknowledgement: actual driver start is its ordinal-zero submission point. */
26
+ export function markReplayStarted(projectionDirectory, executionId, tellIds) {
27
+ for (const tellId of tellIds) {
28
+ if (listTellIds(projectionDirectory, "inflight").includes(tellId)) {
29
+ markTellSubmitted(projectionDirectory, tellId, { executionId, fence: { kind: "ordinal", ordinal: 0 } });
30
+ }
31
+ }
32
+ }
33
+ /** A successful replay terminal is checkpoint ordinal one; failures intentionally do not consume. */
34
+ export function markReplayCompleted(projectionDirectory, executionId) {
35
+ for (const tellId of listTellIds(projectionDirectory, "submitted")) {
36
+ const submitted = readTellSubmitted(projectionDirectory, tellId);
37
+ if (submitted.executionId === executionId && submitted.fence && isTellCausallyConsumed(submitted.fence, { kind: "ordinal", ordinal: 1 })) {
38
+ markTellConsumed(projectionDirectory, tellId);
39
+ }
40
+ }
41
+ }
42
+ /**
43
+ * Tier-2's only submission acknowledgement. A created driver is deliberately
44
+ * insufficient: observe the first provider event, or a successful completion
45
+ * from a provider that emitted no events.
46
+ */
47
+ export function createReplayRunStartGate(projectionDirectory, executionId, tellIds) {
48
+ let started = false;
49
+ const start = () => {
50
+ if (started)
51
+ return;
52
+ markReplayStarted(projectionDirectory, executionId, tellIds);
53
+ started = true;
54
+ };
55
+ return { observeProviderEvent: start, observeSuccessfulCompletion: start };
56
+ }
57
+ const LIVE_TELL_DELIVERY_THROTTLE_MS = 200;
58
+ function submitEligible(projectionDirectory, executionId, control, requireInbox, submitting, observer, pendingCompletionEvidence) {
59
+ if (requireInbox && !observer?.hasInbox())
60
+ return;
61
+ claimAllInbox(projectionDirectory);
62
+ for (const tellId of listTellIds(projectionDirectory, "inflight")) {
63
+ if (submitting.has(tellId))
64
+ continue;
65
+ // Construct before pin: a sync read/build failure must not leave submitting stuck.
66
+ const original = readTellOriginal(projectionDirectory, "inflight", tellId);
67
+ const tell = {
68
+ id: tellId,
69
+ text: `--- tell ${tellId} ---\n${original.text}`,
70
+ ...(original.effort ? { effort: original.effort } : {}),
71
+ };
72
+ submitting.add(tellId);
73
+ void Promise.resolve()
74
+ .then(() => control.enqueue(tell))
75
+ .then((ack) => {
76
+ if (!ack)
77
+ throw new Error("provider enqueue resolved without TellSubmitAck");
78
+ markTellSubmitted(projectionDirectory, tellId, { executionId, fence: ack.fence });
79
+ const pending = pendingCompletionEvidence.get(tellId);
80
+ if (!pending)
81
+ return;
82
+ for (const evidence of pending) {
83
+ if (isTellCausallyConsumed(ack.fence, evidence)) {
84
+ markTellConsumed(projectionDirectory, tellId);
85
+ break;
86
+ }
87
+ }
88
+ pendingCompletionEvidence.delete(tellId);
89
+ })
90
+ .catch(() => {
91
+ // Leave in inflight for the next checkpoint or ingress opportunity.
92
+ // Reject means this crossing never accepted the tell; drop stale evidence.
93
+ pendingCompletionEvidence.delete(tellId);
94
+ })
95
+ .finally(() => submitting.delete(tellId));
96
+ }
97
+ }
98
+ export function createLiveTellDeliveryController(projectionDirectory, executionId, dependencies = {}) {
99
+ const now = dependencies.now ?? Date.now;
100
+ const setTimer = dependencies.setTimer ?? ((callback, delayMs) => setTimeout(callback, delayMs));
101
+ const clearTimer = dependencies.clearTimer ?? ((timer) => clearTimeout(timer));
102
+ const openObserver = dependencies.openObserver ?? openProjectionTellObserver;
103
+ const submitting = new Set();
104
+ // In-memory only: bridges completion-before-ack when a tell is still submitting.
105
+ const pendingCompletionEvidence = new Map();
106
+ let observer;
107
+ let timer;
108
+ let nextOpportunityAt = 0;
109
+ let checking = false;
110
+ let dirty = false;
111
+ let closed = false;
112
+ let latestControl;
113
+ let opportunityEpoch = 0;
114
+ const scheduleTrailing = () => {
115
+ if (closed || timer !== undefined || !dirty)
116
+ return;
117
+ const delayMs = Math.max(0, nextOpportunityAt - now());
118
+ timer = setTimer(() => {
119
+ timer = undefined;
120
+ if (closed || !dirty)
121
+ return;
122
+ if (now() < nextOpportunityAt) {
123
+ scheduleTrailing();
124
+ return;
125
+ }
126
+ const control = latestControl;
127
+ if (control)
128
+ runOpportunity(control);
129
+ }, delayMs);
130
+ };
131
+ const discardObserver = () => {
132
+ try {
133
+ observer?.close();
134
+ }
135
+ catch {
136
+ // Observer close failures cannot change delivery policy.
137
+ }
138
+ observer = undefined;
139
+ };
140
+ const runOpportunity = (control) => {
141
+ if (closed)
142
+ return;
143
+ const epoch = opportunityEpoch;
144
+ dirty = false;
145
+ checking = true;
146
+ nextOpportunityAt = now() + LIVE_TELL_DELIVERY_THROTTLE_MS;
147
+ try {
148
+ observer ??= openObserver(projectionDirectory);
149
+ if (observer.hasDataVersionChanged()) {
150
+ submitEligible(projectionDirectory, executionId, control, true, submitting, observer, pendingCompletionEvidence);
151
+ }
152
+ }
153
+ catch {
154
+ // Observation and event delivery failures never terminate provider execution.
155
+ discardObserver();
156
+ }
157
+ finally {
158
+ checking = false;
159
+ if (epoch === opportunityEpoch && dirty)
160
+ scheduleTrailing();
161
+ }
162
+ };
163
+ const cancelOpportunity = () => {
164
+ opportunityEpoch += 1;
165
+ dirty = false;
166
+ if (timer !== undefined) {
167
+ clearTimer(timer);
168
+ timer = undefined;
169
+ }
170
+ };
171
+ return {
172
+ requestDelivery(control) {
173
+ if (closed)
174
+ return;
175
+ latestControl = control;
176
+ dirty = true;
177
+ if (checking || now() < nextOpportunityAt) {
178
+ scheduleTrailing();
179
+ return;
180
+ }
181
+ runOpportunity(control);
182
+ },
183
+ checkpoint(control, checkpoint) {
184
+ if (closed)
185
+ return;
186
+ latestControl = control;
187
+ cancelOpportunity();
188
+ submitEligible(projectionDirectory, executionId, control, false, submitting, observer, pendingCompletionEvidence);
189
+ if (!checkpoint.consumptionEvidence)
190
+ return;
191
+ for (const tellId of listTellIds(projectionDirectory, "submitted")) {
192
+ const submitted = readTellSubmitted(projectionDirectory, tellId);
193
+ if (submitted.executionId === executionId && submitted.fence && isTellCausallyConsumed(submitted.fence, checkpoint.fence)) {
194
+ markTellConsumed(projectionDirectory, tellId);
195
+ }
196
+ }
197
+ // Capture still-in-flight enqueues so a late ack can still see this evidence.
198
+ for (const tellId of submitting) {
199
+ const pending = pendingCompletionEvidence.get(tellId);
200
+ if (pending)
201
+ pending.push(checkpoint.fence);
202
+ else
203
+ pendingCompletionEvidence.set(tellId, [checkpoint.fence]);
204
+ }
205
+ },
206
+ close() {
207
+ if (closed)
208
+ return;
209
+ closed = true;
210
+ cancelOpportunity();
211
+ discardObserver();
212
+ // Do not drop pendingCompletionEvidence: in-flight acks may still submit/consume.
213
+ },
214
+ };
215
+ }
216
+ /**
217
+ * Build an onSyncCheckpoint callback that claims all inbox tells
218
+ * and delivers them via the given SteerControl.
219
+ * Intended for use inside adapter start() calls as the onSyncCheckpoint callback.
220
+ */
221
+ export function buildSyncCheckpointCallback(projectionDirectory, executionId) {
222
+ const controller = createLiveTellDeliveryController(projectionDirectory, executionId);
223
+ return (control, checkpoint) => controller.checkpoint(control, checkpoint);
224
+ }
225
+ /**
226
+ * Durable execution observer: appends prepared events, delivers live tells at
227
+ * checkpoints, and exposes the public ExecutionObserver surface for harness call/revive.
228
+ */
229
+ export function createProjectionExecutionObserver(input, dependencies = {}) {
230
+ const appendEvent = dependencies.appendGenerationEvent ?? appendGenerationEvent;
231
+ const tellController = createLiveTellDeliveryController(input.projectionDirectory, input.executionId);
232
+ let closed = false;
233
+ const observer = {
234
+ onEvent(event) {
235
+ input.replayStartGate?.observeProviderEvent();
236
+ appendEvent(input.projectionDirectory, input.executionId, event);
237
+ },
238
+ onCheckpoint(control, checkpoint) {
239
+ tellController.checkpoint(control, checkpoint);
240
+ },
241
+ onDeliveryOpportunity(control) {
242
+ tellController.requestDelivery(control);
243
+ },
244
+ };
245
+ return {
246
+ observer,
247
+ close() {
248
+ if (closed)
249
+ return;
250
+ closed = true;
251
+ try {
252
+ tellController.close();
253
+ }
254
+ catch {
255
+ // Controller cleanup cannot change provider completion.
256
+ }
257
+ },
258
+ };
259
+ }
@@ -1,5 +1,5 @@
1
1
  import { ProjectionStateError } from "./atomic-publish.js";
2
- import { applySnapshotEffort, parseResolvedAkumaLaunchSnapshot, } from "../agents/launch-snapshot_v2.js";
2
+ import { applySnapshotEffort, parseResolvedAkumaLaunchSnapshot, } from "../agents/launch-snapshot/model.js";
3
3
  import { listTellIds, readTellOriginal, readTellSubmitted, } from "./projection-core.js";
4
4
  export function snapshotPendingTells(projectionDirectory) {
5
5
  const pending = ["inbox", "inflight", "submitted"]
@@ -1,8 +1,9 @@
1
1
  import { initializeConfig } from "../config/env.js";
2
- import { buildCallTermsV2 } from "../agents/call-terms_v2.js";
3
- import { parseResolvedAkumaLaunchSnapshot, } from "../agents/launch-snapshot_v2.js";
4
- import { revive, call } from "../agents/harness/index.js";
5
- import { readProjectionEvents } from "./projection-core.js";
2
+ import { buildCallTerms } from "../agents/call-terms.js";
3
+ import { parseResolvedAkumaLaunchSnapshot, } from "../agents/launch-snapshot/model.js";
4
+ import { revive, call, } from "../agents/harness/index.js";
5
+ import { demoteSubmittedTellsForReplay, readProjectionEvents, } from "./projection-core.js";
6
+ import { createProjectionExecutionObserver, createReplayRunStartGate, markReplayCompleted, snapshotReplayPrompt, } from "./projection-execution-observer.js";
6
7
  import { readProjectionIdentity } from "./projection-identity.js";
7
8
  import { selectValidStoredAgentEvents } from "./stored-agent-event.js";
8
9
  import { persistResponseHistory } from "./transcripts.js";
@@ -45,6 +46,7 @@ function parseLaunchInputs(launch) {
45
46
  const title = optionalString(raw.title, "title");
46
47
  const context = optionalString(raw.context, "context");
47
48
  const resumePath = optionalString(raw.resumePath, "resumePath");
49
+ const sourceArtifactId = optionalString(raw.sourceArtifactId, "sourceArtifactId");
48
50
  return {
49
51
  prompt: requireString(raw.prompt, "prompt"),
50
52
  cwd: requireString(raw.cwd, "cwd"),
@@ -58,6 +60,7 @@ function parseLaunchInputs(launch) {
58
60
  persistProjectionResponse: raw.persistProjectionResponse,
59
61
  ...(raw.session ? { session: raw.session } : {}),
60
62
  ...(resumePath ? { resumePath } : {}),
63
+ ...(sourceArtifactId ? { sourceArtifactId } : {}),
61
64
  };
62
65
  }
63
66
  function persistenceError(error) {
@@ -81,8 +84,9 @@ function maximumValidEnvelopeTurn(projectionDirectory) {
81
84
  return maximum;
82
85
  }
83
86
  async function completionFacts(projectionDirectory, executionId, inputs, outcome) {
87
+ const providerUnavailable = outcome.error?.code === "SUBAGENT_PROVIDER_UNAVAILABLE";
84
88
  let persistence = {};
85
- if (inputs.persistProjectionResponse) {
89
+ if (inputs.persistProjectionResponse && !providerUnavailable) {
86
90
  try {
87
91
  if (!inputs.evidenceCwd) {
88
92
  throw new Error("projection response persistence is missing evidenceCwd");
@@ -120,29 +124,95 @@ async function completionFacts(projectionDirectory, executionId, inputs, outcome
120
124
  launchSnapshot: inputs.launchSnapshot,
121
125
  effectivePolicy: inputs.launchSnapshot.akuma.policy,
122
126
  ...(outcome.error ? { error: outcome.error } : {}),
127
+ ...(providerUnavailable && inputs.sourceArtifactId
128
+ ? { sourceArtifactId: inputs.sourceArtifactId }
129
+ : {}),
123
130
  ...persistence,
124
131
  });
125
132
  }
126
- export async function executeProjectionGeneration(projectionDirectory, launch) {
133
+ /**
134
+ * Construct the public Projection once and expose a provider-neutral execution
135
+ * handle. Provider adapters and harness public types are unchanged.
136
+ */
137
+ export function executeProjectionGeneration(projectionDirectory, launch) {
127
138
  const inputs = parseLaunchInputs(launch);
128
139
  initializeConfig(process.env, inputs.cwd);
129
- const terms = buildCallTermsV2(inputs.launchSnapshot, inputs.prompt, inputs.cwd, {
140
+ const executionId = launch.executionId;
141
+ const tellFence = launch.facts.tellFence ?? [];
142
+ demoteSubmittedTellsForReplay(projectionDirectory, executionId);
143
+ const baseTerms = buildCallTerms(inputs.launchSnapshot, inputs.prompt, inputs.cwd, {
130
144
  idleTimeoutMs: inputs.idleTimeoutMs,
131
- projectionDir: projectionDirectory,
132
- projectionExecutionId: launch.executionId,
133
- projectionTellFence: launch.facts.tellFence ?? [],
134
145
  initialTurn: maximumValidEnvelopeTurn(projectionDirectory),
135
146
  });
136
- const projection = inputs.session && inputs.resumePath
137
- ? revive({ ...terms, session: inputs.session, resumePath: inputs.resumePath })
138
- : call(terms);
139
- const outcome = await projection.outcome;
147
+ const launchSnapshot = baseTerms.provider === "codex-sdk"
148
+ ? snapshotReplayPrompt(projectionDirectory, baseTerms.prompt, tellFence, executionId, {
149
+ sessionContinuationPromptPrefix: inputs.session && inputs.resumePath
150
+ ? baseTerms.sessionContinuationPromptPrefix
151
+ : undefined,
152
+ })
153
+ : undefined;
154
+ const terms = launchSnapshot
155
+ ? baseTerms.provider === "codex-sdk" && launchSnapshot.effort
156
+ ? {
157
+ ...baseTerms,
158
+ prompt: launchSnapshot.prompt,
159
+ threadOptions: {
160
+ ...baseTerms.threadOptions,
161
+ modelReasoningEffort: launchSnapshot.effort,
162
+ },
163
+ }
164
+ : { ...baseTerms, prompt: launchSnapshot.prompt }
165
+ : baseTerms;
166
+ // A constructed driver is not an SDK run acknowledgement. Tier-2 has no
167
+ // live steer surface, so the first provider event is the earliest harness
168
+ // observable start; a successful no-event completion is the only fallback.
169
+ const replayStartGate = launchSnapshot
170
+ ? createReplayRunStartGate(projectionDirectory, executionId, launchSnapshot.tellIds)
171
+ : undefined;
172
+ const observerController = createProjectionExecutionObserver({
173
+ projectionDirectory,
174
+ executionId,
175
+ ...(replayStartGate ? { replayStartGate } : {}),
176
+ });
177
+ let projection;
178
+ try {
179
+ projection = inputs.session && inputs.resumePath
180
+ ? revive({ ...terms, session: inputs.session, resumePath: inputs.resumePath }, { observer: observerController.observer })
181
+ : call(terms, { observer: observerController.observer });
182
+ }
183
+ catch (error) {
184
+ observerController.close();
185
+ throw error;
186
+ }
187
+ const outcome = projection.outcome.then(async (result) => {
188
+ try {
189
+ if (result.status === "completed") {
190
+ // A provider may complete successfully without emitting any observable
191
+ // events. Its successful completion is still a real run start.
192
+ replayStartGate?.observeSuccessfulCompletion();
193
+ if (launchSnapshot) {
194
+ markReplayCompleted(projectionDirectory, executionId);
195
+ }
196
+ }
197
+ return {
198
+ verdict: result.status === "completed"
199
+ ? "completed"
200
+ : result.status === "dismissed"
201
+ ? "dismissed"
202
+ : "failed",
203
+ facts: await completionFacts(projectionDirectory, executionId, inputs, result),
204
+ };
205
+ }
206
+ finally {
207
+ observerController.close();
208
+ }
209
+ }, (error) => {
210
+ observerController.close();
211
+ throw error;
212
+ });
140
213
  return {
141
- verdict: outcome.status === "completed"
142
- ? "completed"
143
- : outcome.status === "dismissed"
144
- ? "dismissed"
145
- : "failed",
146
- facts: await completionFacts(projectionDirectory, launch.executionId, inputs, outcome),
214
+ events: projection.events,
215
+ outcome,
216
+ abort: (mode) => projection.abort(mode),
147
217
  };
148
218
  }
@@ -1,5 +1,5 @@
1
1
  import { setTimeout as delay } from "node:timers/promises";
2
- import { openProjectionGenerationStore } from "./projection-generation-store.js";
2
+ import { openProjectionGenerationStore } from "./projection/generation/store.js";
3
3
  import { spawnProjectionGenerationRunner, } from "./projection-generation-process.js";
4
4
  export const PROJECTION_ADOPTION_TIMEOUT_MS = 4_000;
5
5
  export const PROJECTION_ADOPTION_POLL_MS = 20;