@astrosheep/keiyaku 4.5.19 → 4.5.21

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 (197) hide show
  1. package/README.md +4 -4
  2. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +14 -7
  3. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +53 -20
  4. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/references/automation.md +237 -0
  5. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +6 -7
  6. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +2 -4
  7. package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +3 -3
  8. package/build/src/akuma/akuma-handle.d.ts +5 -1
  9. package/build/src/akuma/akuma-handle.js +4 -1
  10. package/build/src/akuma/akuma-instance.d.ts +7 -2
  11. package/build/src/akuma/akuma-instance.js +14 -7
  12. package/build/src/akuma/akuma-observe.d.ts +11 -0
  13. package/build/src/akuma/akuma-observe.js +23 -0
  14. package/build/src/akuma/akuma-product.d.ts +2 -0
  15. package/build/src/akuma/akuma-product.js +6 -0
  16. package/build/src/akuma/akuma.d.ts +20 -1
  17. package/build/src/akuma/akuma.js +2 -0
  18. package/build/src/akuma/allowed.d.ts +20 -0
  19. package/build/src/akuma/allowed.js +11 -0
  20. package/build/src/akuma/archetype.js +3 -0
  21. package/build/src/akuma/body.d.ts +25 -5
  22. package/build/src/akuma/body.js +70 -31
  23. package/build/src/akuma/call-request.d.ts +5 -0
  24. package/build/src/akuma/call-request.js +14 -4
  25. package/build/src/akuma/fleet-execution.d.ts +28 -0
  26. package/build/src/akuma/fleet-execution.js +36 -4
  27. package/build/src/akuma/fleet-observation.d.ts +54 -0
  28. package/build/src/akuma/fleet-request.d.ts +10 -0
  29. package/build/src/akuma/fleet-request.js +13 -3
  30. package/build/src/akuma/heart/facts.d.ts +2 -0
  31. package/build/src/akuma/heart/index.d.ts +1 -0
  32. package/build/src/akuma/heart/index.js +4 -1
  33. package/build/src/akuma/heart/rows.d.ts +2 -0
  34. package/build/src/akuma/heart/rows.js +7 -5
  35. package/build/src/akuma/heart/schema.d.ts +1 -1
  36. package/build/src/akuma/heart/schema.js +3 -1
  37. package/build/src/akuma/heart/tells.js +6 -5
  38. package/build/src/akuma/heart/timeline.js +1 -1
  39. package/build/src/akuma/provider-recipe.d.ts +1 -0
  40. package/build/src/akuma/provider-recipe.js +5 -0
  41. package/build/src/akuma/provider.d.ts +1 -1
  42. package/build/src/akuma/provider.js +1 -1
  43. package/build/src/akuma/providers/acp/core.js +3 -6
  44. package/build/src/akuma/providers/acp/index.js +3 -0
  45. package/build/src/akuma/providers/claude/index.js +10 -10
  46. package/build/src/akuma/providers/codex-app-server/index.js +12 -11
  47. package/build/src/akuma/providers/execution-environment.d.ts +6 -0
  48. package/build/src/akuma/providers/execution-environment.js +24 -0
  49. package/build/src/akuma/providers/grok-build/index.js +3 -0
  50. package/build/src/akuma/providers/opencode-sdk/index.js +32 -23
  51. package/build/src/akuma/providers/opencode-sdk/session.d.ts +8 -1
  52. package/build/src/akuma/providers/opencode-sdk/session.js +6 -5
  53. package/build/src/akuma/providers/pi/events.js +7 -2
  54. package/build/src/akuma/providers/pi/index.js +5 -2
  55. package/build/src/akuma/request-observation.d.ts +45 -0
  56. package/build/src/akuma/request-observation.js +162 -0
  57. package/build/src/akuma/request-rendezvous.d.ts +6 -3
  58. package/build/src/akuma/request-rendezvous.js +80 -14
  59. package/build/src/akuma/request-serve.js +27 -4
  60. package/build/src/akuma/request-wire.d.ts +5 -0
  61. package/build/src/akuma/request-wire.js +2 -0
  62. package/build/src/akuma/turn-drive.d.ts +1 -0
  63. package/build/src/akuma/turn-drive.js +2 -0
  64. package/build/src/akuma-body.js +5 -0
  65. package/build/src/body/amend.js +6 -4
  66. package/build/src/body/decode.d.ts +3 -1
  67. package/build/src/body/decode.js +53 -17
  68. package/build/src/body/region.d.ts +9 -1
  69. package/build/src/body/region.js +87 -12
  70. package/build/src/body/render.js +1 -1
  71. package/build/src/body/verification.d.ts +3 -1
  72. package/build/src/body/verification.js +4 -1
  73. package/build/src/cli/accepted.js +19 -2
  74. package/build/src/cli/commands/akuma-invoke.d.ts +3 -1
  75. package/build/src/cli/commands/akuma-invoke.js +134 -14
  76. package/build/src/cli/commands/akuma.js +24 -12
  77. package/build/src/cli/commands/contract-help.d.ts +15 -15
  78. package/build/src/cli/commands/contract-help.js +45 -19
  79. package/build/src/cli/commands/contract-invoke.d.ts +3 -0
  80. package/build/src/cli/commands/contract-invoke.js +25 -11
  81. package/build/src/cli/commands/contract.js +6 -4
  82. package/build/src/cli/commands/install.d.ts +1 -1
  83. package/build/src/cli/commands/install.js +1 -1
  84. package/build/src/cli/commands/status-set.js +15 -24
  85. package/build/src/cli/commands/task.js +18 -18
  86. package/build/src/cli/invoke.d.ts +3 -0
  87. package/build/src/cli/invoke.js +58 -23
  88. package/build/src/cli/main.js +5 -0
  89. package/build/src/cli/parse.d.ts +3 -0
  90. package/build/src/cli/parse.js +26 -4
  91. package/build/src/cli/render/akuma-activity.d.ts +100 -2
  92. package/build/src/cli/render/akuma-activity.js +485 -40
  93. package/build/src/cli/render/akuma.d.ts +10 -0
  94. package/build/src/cli/render/akuma.js +31 -21
  95. package/build/src/cli/render/audit.js +1 -1
  96. package/build/src/cli/render/board.js +1 -1
  97. package/build/src/cli/render/catalog.js +16 -13
  98. package/build/src/cli/render/contract-history.js +39 -10
  99. package/build/src/cli/render/contract-observation.js +10 -6
  100. package/build/src/cli/render/contract.js +109 -106
  101. package/build/src/cli/render/execution-progress.d.ts +26 -0
  102. package/build/src/cli/render/execution-progress.js +178 -0
  103. package/build/src/cli/render/kanshi-akuma.js +27 -39
  104. package/build/src/cli/render/kanshi.js +33 -26
  105. package/build/src/cli/render/nuke.js +1 -1
  106. package/build/src/cli/render/receipt.d.ts +1 -1
  107. package/build/src/cli/render/receipt.js +6 -6
  108. package/build/src/cli/render/refusal.js +10 -7
  109. package/build/src/cli/render/region.js +1 -1
  110. package/build/src/cli/render/settings.js +12 -2
  111. package/build/src/cli/render/status-line.d.ts +30 -0
  112. package/build/src/cli/render/status-line.js +58 -0
  113. package/build/src/cli/render/status-set.js +1 -1
  114. package/build/src/cli/render/task.d.ts +2 -0
  115. package/build/src/cli/render/task.js +11 -7
  116. package/build/src/cli/render/terminal.d.ts +0 -1
  117. package/build/src/cli/render/terminal.js +47 -15
  118. package/build/src/cli/result.d.ts +1 -0
  119. package/build/src/cli/runtime.d.ts +2 -0
  120. package/build/src/cli/runtime.js +36 -4
  121. package/build/src/cli/usage.js +2 -2
  122. package/build/src/cli/version.d.ts +1 -0
  123. package/build/src/cli/version.js +25 -0
  124. package/build/src/git/hooks.d.ts +13 -1
  125. package/build/src/git/hooks.js +2 -1
  126. package/build/src/git/workspace.d.ts +1 -1
  127. package/build/src/git/workspace.js +1 -1
  128. package/build/src/identity/mint.d.ts +13 -0
  129. package/build/src/identity/mint.js +26 -0
  130. package/build/src/identity/normalize.d.ts +4 -0
  131. package/build/src/identity/normalize.js +30 -0
  132. package/build/src/index.d.ts +2 -0
  133. package/build/src/index.js +1 -0
  134. package/build/src/kanshi/read.js +17 -4
  135. package/build/src/kanshi/report.d.ts +1 -0
  136. package/build/src/library/akuma-creation.d.ts +2 -0
  137. package/build/src/library/akuma-creation.js +34 -18
  138. package/build/src/library/audit.d.ts +2 -0
  139. package/build/src/library/audit.js +1 -0
  140. package/build/src/library/bind.js +18 -16
  141. package/build/src/library/composition.d.ts +1 -0
  142. package/build/src/library/contract-bind.d.ts +3 -1
  143. package/build/src/library/contract-bind.js +5 -3
  144. package/build/src/library/contract-execution.d.ts +2 -0
  145. package/build/src/library/contract-execution.js +15 -1
  146. package/build/src/library/contract-forwarding.d.ts +4 -0
  147. package/build/src/library/contract-forwarding.js +3 -0
  148. package/build/src/library/contract-handle.d.ts +7 -0
  149. package/build/src/library/contract-handle.js +25 -0
  150. package/build/src/library/contract-operations.d.ts +7 -2
  151. package/build/src/library/contract-operations.js +21 -1
  152. package/build/src/library/contract-types.d.ts +1 -0
  153. package/build/src/library/execution-result.d.ts +2 -2
  154. package/build/src/library/execution.d.ts +8 -0
  155. package/build/src/library/execution.js +102 -0
  156. package/build/src/library/fleet.d.ts +4 -1
  157. package/build/src/library/fleet.js +32 -8
  158. package/build/src/library/keiyaku.d.ts +2 -0
  159. package/build/src/library/mutation.js +15 -7
  160. package/build/src/plugin/akuma-signals.d.ts +6 -0
  161. package/build/src/plugin/akuma-signals.js +9 -1
  162. package/build/src/plugin/public.d.ts +6 -2
  163. package/build/src/plugin/runtime.d.ts +1 -3
  164. package/build/src/plugin/runtime.js +95 -103
  165. package/build/src/protocol/amend.js +4 -3
  166. package/build/src/protocol/attempt.d.ts +2 -0
  167. package/build/src/protocol/bind.js +41 -14
  168. package/build/src/protocol/completion.d.ts +3 -0
  169. package/build/src/protocol/completion.js +35 -11
  170. package/build/src/protocol/deliver.js +165 -92
  171. package/build/src/protocol/execution-observation.d.ts +181 -0
  172. package/build/src/protocol/execution-observation.js +45 -0
  173. package/build/src/protocol/intent.d.ts +18 -22
  174. package/build/src/protocol/intent.js +63 -41
  175. package/build/src/protocol/operations.d.ts +2 -0
  176. package/build/src/protocol/placement.js +5 -1
  177. package/build/src/protocol/progress.d.ts +5 -1
  178. package/build/src/protocol/progress.js +10 -1
  179. package/build/src/protocol/read/status.d.ts +1 -0
  180. package/build/src/protocol/read/status.js +10 -2
  181. package/build/src/protocol/reintegrate.d.ts +1 -1
  182. package/build/src/protocol/reintegrate.js +70 -43
  183. package/build/src/protocol/result-codec.js +27 -11
  184. package/build/src/protocol/review.js +53 -30
  185. package/build/src/protocol/run.d.ts +83 -36
  186. package/build/src/protocol/run.js +90 -75
  187. package/build/src/runtime/proc/run.d.ts +16 -4
  188. package/build/src/runtime/proc/run.js +64 -22
  189. package/build/src/runtime/proc/windows-launch.exe +0 -0
  190. package/build/src/settings.js +5 -14
  191. package/build/src/task/document.d.ts +5 -1
  192. package/build/src/task/document.js +6 -1
  193. package/build/src/verification/execution.d.ts +10 -2
  194. package/build/src/verification/execution.js +143 -42
  195. package/build/src/verification/observation.d.ts +41 -0
  196. package/build/src/verification/observation.js +29 -0
  197. package/package.json +3 -1
@@ -2,7 +2,7 @@ import { worktreeChangeId } from "../git/integration.js";
2
2
  import { captureTender, dirtyTenderDelta, dirtyTenderRefusal, } from "../git/tender.js";
3
3
  import { observeContractsForAdmissionAt } from "../git/observe.js";
4
4
  import { unmergedWorkspacePaths, worktreePath } from "../git/workspace.js";
5
- import { matchingPrivateRootObservation, privateStateSeatAttempt, sameSpeculativeWorktreeInput, } from "./run.js";
5
+ import { contractStateWitness, privateStateSeatAttempt, sameWorktreeWitness, STALE_PRIVATE_STATE_PREPARATION, } from "./run.js";
6
6
  import { dependencyKeySet } from "../core/subject.js";
7
7
  import { contractState } from "../core/facts/observation.js";
8
8
  import { gate } from "../core/facts/types.js";
@@ -58,14 +58,14 @@ export async function prepareReview(repository, stage) {
58
58
  },
59
59
  };
60
60
  }
61
- function preparedReviewCapture(input, state, prepared) {
61
+ function preparedReviewCapture(input, state, changeId) {
62
62
  return {
63
63
  kind: "prepared",
64
64
  data: {
65
65
  gate: REVIEWED,
66
66
  subject: dependencyKeySet([
67
67
  { kind: "document", value: state.terms.document.key },
68
- { kind: "change", value: prepared.data.changeId },
68
+ { kind: "change", value: changeId },
69
69
  ]),
70
70
  verdict: input.verdict,
71
71
  ...(input.summary === undefined ? {} : { summary: input.summary }),
@@ -88,32 +88,56 @@ async function recaptureReviewWorktree(input, state) {
88
88
  });
89
89
  return prepared.kind === "prepared" ? reviewWorktreeInput(prepared) : undefined;
90
90
  }
91
- async function speculateReview(input) {
91
+ /** Capture the reviewable worktree from one non-authoritative private-state read. */
92
+ async function prepareExternalReview(input) {
92
93
  const observation = await observeContractsForAdmissionAt(input.scope, input.channel, [input.contractId]);
93
94
  const state = contractState(observation.decision, input.contractId);
94
95
  if (state === null)
95
- return { observation };
96
+ return { kind: "unavailable" };
96
97
  const prepared = await captureReviewableWorktree(input.scope, {
97
98
  contractId: state.id,
98
99
  coordinates: state.coordinates,
99
100
  });
100
- if (prepared.kind === "refused") {
101
- return { observation, preparation: { kind: "refused", refusal: prepared.refusal } };
101
+ return {
102
+ kind: "artifacts",
103
+ witness: contractStateWitness(state),
104
+ artifacts: prepared.kind === "refused"
105
+ ? { kind: "refused", refusal: prepared.refusal }
106
+ : {
107
+ kind: "captured",
108
+ ...(prepared.data.workspace === undefined ? {} : { workspace: prepared.data.workspace }),
109
+ worktree: reviewWorktreeInput(prepared),
110
+ },
111
+ };
112
+ }
113
+ /** Assemble the in-custody attestation input from the fresh Contract and validated artifacts. */
114
+ async function assembleReviewAttempt(input, observation, external) {
115
+ const state = contractState(observation.decision, input.contractId);
116
+ if (external.kind === "unavailable")
117
+ return state === null ? { kind: "assembled" } : { kind: "stale" };
118
+ if (state === null)
119
+ return { kind: "stale" };
120
+ if (external.witness.head !== state.head)
121
+ return { kind: "stale" };
122
+ if (external.artifacts.kind === "refused") {
123
+ return { kind: "assembled", preparation: { kind: "refused", refusal: external.artifacts.refusal } };
124
+ }
125
+ if (!sameWorktreeWitness(external.artifacts.worktree, await recaptureReviewWorktree(input, state))) {
126
+ return { kind: "stale" };
102
127
  }
103
128
  return {
104
- observation,
105
- preparation: preparedReviewCapture(input, state, prepared),
106
- ...(prepared.data.workspace === undefined ? {} : { workspace: prepared.data.workspace }),
107
- worktree: reviewWorktreeInput(prepared),
129
+ kind: "assembled",
130
+ preparation: preparedReviewCapture(input, state, external.artifacts.worktree.changeId),
131
+ ...(external.artifacts.workspace === undefined ? {} : { workspace: external.artifacts.workspace }),
108
132
  };
109
133
  }
110
- async function decideAndAdmitReview(input, attempt, seat, observation, speculated) {
134
+ async function decideAndAdmitReview(input, attempt, seat, observation, assembled) {
111
135
  const decision = decideAttestation({
112
136
  input: {
113
137
  contractId: input.contractId,
114
138
  ...(input.actor === undefined ? {} : { actor: input.actor }),
115
139
  at: timestamp(),
116
- ...(speculated.preparation === undefined ? {} : { preparation: speculated.preparation }),
140
+ ...(assembled.preparation === undefined ? {} : { preparation: assembled.preparation }),
117
141
  },
118
142
  attempt,
119
143
  observation: observation.decision,
@@ -128,33 +152,30 @@ async function decideAndAdmitReview(input, attempt, seat, observation, speculate
128
152
  attempt,
129
153
  offer: decision.offer,
130
154
  primaryContract: input.contractId,
131
- ...(input.progress === undefined ? {} : input.progress === undefined ? {} : { progress: input.progress }),
155
+ ...(input.progress === undefined ? {} : { progress: input.progress }),
132
156
  });
133
157
  if (admission.kind !== "accepted")
134
158
  return admission;
135
159
  return {
136
160
  ...admission,
137
161
  value: {
138
- ...(speculated.workspace === undefined ? {} : { workspace: speculated.workspace }),
162
+ ...(assembled.workspace === undefined ? {} : { workspace: assembled.workspace }),
139
163
  },
140
164
  };
141
165
  }
142
- async function reviewAttemptInPrivateStateSeat(input, attempt, seat, speculated) {
143
- const observation = await matchingPrivateRootObservation(input.scope, input.channel, input.contractId, speculated.observation, async (fresh) => {
144
- if (speculated.worktree === undefined)
145
- return true;
146
- const state = contractState(fresh.decision, input.contractId);
147
- if (state === null)
148
- return false;
149
- return sameSpeculativeWorktreeInput(speculated.worktree, await recaptureReviewWorktree(input, state));
150
- });
151
- return "kind" in observation
152
- ? observation
153
- : await decideAndAdmitReview(input, attempt, seat, observation, speculated);
154
- }
166
+ /**
167
+ * One review attempt: the repeatable worktree capture happens outside custody, then a single
168
+ * in-custody observation, witness validation, subject assembly, decision, and publication.
169
+ */
155
170
  async function reviewAttempt(input, attempt) {
156
- const speculated = await speculateReview(input);
157
- return await privateStateSeatAttempt(input.scope, async (seat) => await reviewAttemptInPrivateStateSeat(input, attempt, seat, speculated), attemptDecisionWithSeatClose);
171
+ const external = await prepareExternalReview(input);
172
+ return await privateStateSeatAttempt(input.scope, async (seat) => {
173
+ const observation = await observeContractsForAdmissionAt(input.scope, input.channel, [input.contractId]);
174
+ const assembled = await assembleReviewAttempt(input, observation, external);
175
+ if (assembled.kind === "stale")
176
+ return STALE_PRIVATE_STATE_PREPARATION;
177
+ return await decideAndAdmitReview(input, attempt, seat, observation, assembled);
178
+ }, attemptDecisionWithSeatClose);
158
179
  }
159
180
  export async function admitReviewOperation(input) {
160
181
  const attempts = mintAttempts({ entryCount: 1 });
@@ -167,6 +188,8 @@ export async function admitReviewOperation(input) {
167
188
  }
168
189
  if (result.kind === "publication-failed")
169
190
  return { kind: "retry", reason: result };
191
+ if (result.kind === "stale" || result.kind === "redecide")
192
+ continue;
170
193
  if (result.kind === "collision" && index + 1 === attempts.length)
171
194
  return { kind: "retry", reason: result };
172
195
  }
@@ -3,15 +3,14 @@ import { type GitDecisionObservation } from "../git/observe.js";
3
3
  import { type PrivateStatePublicationSeat, type PrivateStateSeatOutcome } from "../git/private-state-seat.js";
4
4
  import type { GitDecodeChannel, GitTreeSelection } from "../git/read-observation.js";
5
5
  import type { GitRepository } from "../git/process.js";
6
- import { type GitOid, type GitRefAssertion } from "../git/repository.js";
6
+ import { type GitRefAssertion } from "../git/repository.js";
7
7
  import type { AttemptContext, DecideInput, OfferDecision } from "../core/decide.js";
8
8
  import type { Offer, TreeUpdate } from "../core/facts/offer.js";
9
- import { type ChangeId, type ContractId, type SnapshotId } from "../core/facts/types.js";
10
- import { type AcceptedAdmission, type AttemptTerminal } from "./attempt.js";
9
+ import type { ChangeId, ContractHead, ContractId, SnapshotId } from "../core/facts/types.js";
11
10
  import type { GitObjectId } from "../git/identity.js";
11
+ import { type AcceptedAdmission, type AttemptTerminal } from "./attempt.js";
12
12
  export declare const STALE_PRIVATE_STATE_PREPARATION: {
13
- readonly kind: "publication-failed";
14
- readonly diagnostic: "stale private-state preparation";
13
+ readonly kind: "stale";
15
14
  };
16
15
  export type ProtocolTerminal = Readonly<{
17
16
  kind: "exhausted";
@@ -26,11 +25,40 @@ export type CompanionDecorator = (input: Readonly<{
26
25
  contractId: ContractId;
27
26
  offer: Offer;
28
27
  }>) => readonly TreeUpdate[] | Promise<readonly TreeUpdate[]>;
29
- export type RunProtocolInput<Input extends Readonly<{
30
- contractId: ContractId;
31
- }>, Refusal, Seed extends Readonly<{
28
+ /**
29
+ * Seat-external preparation: immutable artifacts plus the currentness witnesses they carry.
30
+ * It never observes private state as authority and never forms an offer.
31
+ */
32
+ export type ExternalProtocolPreparation<Prepared, Refusal> = Readonly<{
33
+ kind: "prepared";
34
+ prepared: Prepared;
35
+ assertions?: readonly GitRefAssertion[];
36
+ }> | Readonly<{
37
+ kind: "refused";
38
+ refusal: Refusal;
39
+ }>;
40
+ /**
41
+ * In-custody assembly of one decision input from the fresh observation and an external artifact.
42
+ * `stale` means only that the outside preparation is spent: discard it and restart the cycle.
43
+ */
44
+ export type InCustodyProtocolPreparation<Input, Refusal> = Readonly<{
45
+ kind: "prepared";
46
+ input: Input;
47
+ assertions?: readonly GitRefAssertion[];
48
+ }> | Readonly<{
49
+ kind: "stale";
50
+ }> | Readonly<{
51
+ kind: "refused";
52
+ refusal: Refusal;
53
+ }>;
54
+ /** The one split-preparation mechanism: repeatable work outside custody, assembly inside it. */
55
+ export type ProtocolPreparation<Input, Refusal, Seed, Prepared> = Readonly<{
56
+ external: (seed: Seed) => ExternalProtocolPreparation<Prepared, Refusal> | Promise<ExternalProtocolPreparation<Prepared, Refusal>>;
57
+ assemble: (observation: GitDecisionObservation, seed: Seed, prepared: Prepared) => InCustodyProtocolPreparation<Input, Refusal> | Promise<InCustodyProtocolPreparation<Input, Refusal>>;
58
+ }>;
59
+ type ProtocolAdmission<Input extends Readonly<{
32
60
  contractId: ContractId;
33
- }> = Input> = Readonly<{
61
+ }>, Refusal> = Readonly<{
34
62
  channel: GitDecodeChannel;
35
63
  repository: GitRepository;
36
64
  progress?: ExecutionProgress;
@@ -45,28 +73,31 @@ export type RunProtocolInput<Input extends Readonly<{
45
73
  decorateOffer?: CompanionDecorator;
46
74
  validateAdmission?: (observation: GitDecisionObservation) => Refusal | undefined | Promise<Refusal | undefined>;
47
75
  observationSelection?: GitTreeSelection;
48
- }> & (Readonly<{
49
- input: Input;
50
- prepareInput?: never;
51
- }> | Readonly<{
76
+ }>;
77
+ /** An intent whose invocation is already its decision input. */
78
+ export type DirectRunProtocolInput<Seed extends Readonly<{
79
+ contractId: ContractId;
80
+ }>, Refusal> = ProtocolAdmission<Seed, Refusal> & Readonly<{
81
+ input: Seed;
82
+ preparation?: undefined;
83
+ }>;
84
+ /** An intent whose repeatable preparation runs outside custody and is assembled inside it. */
85
+ export type SplitRunProtocolInput<Input extends Readonly<{
86
+ contractId: ContractId;
87
+ }>, Refusal, Seed extends Readonly<{
88
+ contractId: ContractId;
89
+ }>, Prepared> = ProtocolAdmission<Input, Refusal> & Readonly<{
52
90
  input: Seed;
53
- prepareInput: (observation: GitDecisionObservation, input: Seed) => Readonly<{
54
- kind: "prepared";
55
- input: Input;
56
- assertions?: readonly GitRefAssertion[];
57
- }> | Readonly<{
58
- kind: "refused";
59
- refusal: Refusal;
60
- }> | Promise<Readonly<{
61
- kind: "prepared";
62
- input: Input;
63
- assertions?: readonly GitRefAssertion[];
64
- }> | Readonly<{
65
- kind: "refused";
66
- refusal: Refusal;
67
- }>>;
68
- }>);
91
+ preparation: ProtocolPreparation<Input, Refusal, Seed, Prepared>;
92
+ }>;
93
+ export type RunProtocolInput<Input extends Readonly<{
94
+ contractId: ContractId;
95
+ }>, Refusal, Seed extends Readonly<{
96
+ contractId: ContractId;
97
+ }> = Input, Prepared = Seed> = DirectRunProtocolInput<Seed, Refusal> | SplitRunProtocolInput<Input, Refusal, Seed, Prepared>;
69
98
  export type PreparedProtocolAttempt<Refusal> = Readonly<{
99
+ kind: "stale";
100
+ }> | Readonly<{
70
101
  kind: "refused";
71
102
  refusal: Refusal;
72
103
  }> | Readonly<{
@@ -76,29 +107,45 @@ export type PreparedProtocolAttempt<Refusal> = Readonly<{
76
107
  offer: Offer;
77
108
  assertions: readonly GitRefAssertion[];
78
109
  }>;
79
- export declare function privateRootCommit(observation: GitDecisionObservation): GitOid | null;
80
- export declare function samePrivateRootObservation(prepared: GitDecisionObservation, fresh: GitDecisionObservation): boolean;
81
- export type SpeculativeWorktreeInput = Readonly<{
110
+ /**
111
+ * The private-state witness a seat-external preparation was computed against. Equal journal heads
112
+ * mean the same decoded contract state, so the same document, coordinates, and facts.
113
+ */
114
+ export type ContractStateWitness = Readonly<{
115
+ head: ContractHead | null;
116
+ }>;
117
+ export declare function contractStateWitness(state: ContractStateWitness): ContractStateWitness;
118
+ /**
119
+ * A physical worktree capture carried across custody as a witness, never as authority.
120
+ *
121
+ * Unlike a ref or a journal head, uncommitted worktree bytes are covered by no atomic
122
+ * compare-and-swap, so the only sound proof that a seat-external capture still describes the
123
+ * bytes about to be published is a fresh capture compared against this witness under custody.
124
+ * A cheaper structural signature (head, merge head, dirty flag, changed paths) cannot judge it:
125
+ * a writer that rewrites an already-changed path - a conflict materialization, for instance -
126
+ * leaves every path-level signature identical while the bytes that would be published differ.
127
+ */
128
+ export type WorktreeWitness = Readonly<{
82
129
  tree: GitObjectId;
83
130
  head: SnapshotId;
84
131
  mergeHead?: SnapshotId;
85
132
  dirty: boolean;
86
133
  changeId: ChangeId;
87
134
  }>;
88
- export declare function sameSpeculativeWorktreeInput(prepared: SpeculativeWorktreeInput | undefined, fresh: SpeculativeWorktreeInput | undefined): boolean;
135
+ export declare function sameWorktreeWitness(prepared: WorktreeWitness | undefined, fresh: WorktreeWitness | undefined): boolean;
89
136
  export declare function privateStateAssertionsMatch(repository: GitRepository, assertions: readonly GitRefAssertion[]): Promise<boolean>;
90
137
  export declare function publicationFailedFromSeatError(error: unknown): AttemptTerminal;
91
- export declare function matchingPrivateRootObservation(repository: GitRepository, channel: GitDecodeChannel, contractId: ContractId, prepared: GitDecisionObservation, revalidate?: (fresh: GitDecisionObservation) => boolean | Promise<boolean>): Promise<GitDecisionObservation | typeof STALE_PRIVATE_STATE_PREPARATION>;
92
138
  export declare function privateStateSeatAttempt<T>(repository: GitRepository, action: (seat: PrivateStatePublicationSeat) => Promise<T>, wrap: (outcome: PrivateStateSeatOutcome<T>) => T): Promise<T | AttemptTerminal>;
93
139
  /** Form one decided offer without admitting or reinterpreting it. */
94
140
  export declare function prepareProtocolAttempt<Input extends Readonly<{
95
141
  contractId: ContractId;
96
142
  }>, Refusal, Seed extends Readonly<{
97
143
  contractId: ContractId;
98
- }> = Input>(input: RunProtocolInput<Input, Refusal, Seed>, baseAttempt: AttemptContext): Promise<PreparedProtocolAttempt<Refusal>>;
144
+ }> = Input, Prepared = Seed>(input: RunProtocolInput<Input, Refusal, Seed, Prepared>, baseAttempt: AttemptContext): Promise<PreparedProtocolAttempt<Refusal>>;
99
145
  /** Run bounded, verb-neutral attempts and return one real admission on acceptance. */
100
146
  export declare function runProtocol<Input extends Readonly<{
101
147
  contractId: ContractId;
102
148
  }>, Refusal, Seed extends Readonly<{
103
149
  contractId: ContractId;
104
- }> = Input>(input: RunProtocolInput<Input, Refusal, Seed>): Promise<ProtocolResult<Refusal>>;
150
+ }> = Input, Prepared = Seed>(input: RunProtocolInput<Input, Refusal, Seed, Prepared>): Promise<ProtocolResult<Refusal>>;
151
+ export {};
@@ -3,16 +3,12 @@ import { appendPrivateStateSeatClose, isPrivateStateSeatContention, mergePrivate
3
3
  import { GIT_REF, readRefs } from "../git/repository.js";
4
4
  import { admitDecidedOffer } from "./attempt.js";
5
5
  export const STALE_PRIVATE_STATE_PREPARATION = {
6
- kind: "publication-failed",
7
- diagnostic: "stale private-state preparation",
6
+ kind: "stale",
8
7
  };
9
- export function privateRootCommit(observation) {
10
- return observation.admission.snapshot.commit;
8
+ export function contractStateWitness(state) {
9
+ return { head: state.head };
11
10
  }
12
- export function samePrivateRootObservation(prepared, fresh) {
13
- return privateRootCommit(prepared) === privateRootCommit(fresh);
14
- }
15
- export function sameSpeculativeWorktreeInput(prepared, fresh) {
11
+ export function sameWorktreeWitness(prepared, fresh) {
16
12
  if (prepared === undefined || fresh === undefined)
17
13
  return prepared === fresh;
18
14
  return (prepared.tree === fresh.tree &&
@@ -32,14 +28,6 @@ export function publicationFailedFromSeatError(error) {
32
28
  return { kind: "publication-failed", diagnostic: error.message };
33
29
  throw error;
34
30
  }
35
- export async function matchingPrivateRootObservation(repository, channel, contractId, prepared, revalidate) {
36
- const fresh = await observeContractsForAdmissionAt(repository, channel, [contractId]);
37
- if (!samePrivateRootObservation(prepared, fresh))
38
- return STALE_PRIVATE_STATE_PREPARATION;
39
- if (revalidate !== undefined && !(await revalidate(fresh)))
40
- return STALE_PRIVATE_STATE_PREPARATION;
41
- return fresh;
42
- }
43
31
  export async function privateStateSeatAttempt(repository, action, wrap) {
44
32
  try {
45
33
  return wrap(await withPrivateStatePublicationSeat(repository, action));
@@ -53,30 +41,23 @@ async function observeProtocolDecision(input) {
53
41
  ? await observeContractsForAdmissionAt(input.repository, input.channel, input.contracts, input.observationSelection)
54
42
  : await input.observe(input.repository, input.channel, input.contracts);
55
43
  }
56
- async function prepareProtocolSeed(input, observation) {
57
- if (!("prepareInput" in input) || input.prepareInput === undefined) {
58
- return { kind: "prepared", input: input.input, assertions: [] };
59
- }
60
- const prepared = await input.prepareInput(observation, input.input);
61
- return prepared.kind === "refused" ? prepared : { ...prepared, assertions: prepared.assertions ?? [] };
62
- }
63
- async function decideDecoratedProtocolOffer(input, baseAttempt, decisionObservation, preparedInput) {
64
- const attempt = input.extendAttempt === undefined
65
- ? baseAttempt
66
- : input.extendAttempt(baseAttempt, decisionObservation.decision.size);
67
- const decision = input.decide({
68
- input: preparedInput.input,
44
+ async function decideDecoratedProtocolOffer(admission, primaryContract, decisionObservation, prepared) {
45
+ const attempt = admission.extendAttempt === undefined
46
+ ? prepared.attempt
47
+ : admission.extendAttempt(prepared.attempt, decisionObservation.decision.size);
48
+ const decision = admission.decide({
49
+ input: prepared.input,
69
50
  attempt,
70
51
  observation: decisionObservation.decision,
71
52
  });
72
53
  if (decision.kind === "refused")
73
54
  return decision;
74
- const companions = input.decorateOffer === undefined
55
+ const companions = admission.decorateOffer === undefined
75
56
  ? []
76
- : await input.decorateOffer({
77
- repository: input.repository,
57
+ : await admission.decorateOffer({
58
+ repository: admission.repository,
78
59
  observation: decisionObservation,
79
- contractId: input.input.contractId,
60
+ contractId: primaryContract,
80
61
  offer: decision.offer,
81
62
  });
82
63
  const offer = companions.length === 0
@@ -84,22 +65,31 @@ async function decideDecoratedProtocolOffer(input, baseAttempt, decisionObservat
84
65
  : { ...decision.offer, companions: [...(decision.offer.companions ?? []), ...companions] };
85
66
  const admissionObservation = companions.length === 0
86
67
  ? decisionObservation
87
- : await extendAdmissionPathsAt(input.channel, decisionObservation, companions.map(({ path }) => path));
68
+ : await extendAdmissionPathsAt(admission.channel, decisionObservation, companions.map(({ path }) => path));
88
69
  return {
89
70
  kind: "offered",
90
71
  observation: admissionObservation,
91
72
  attempt,
92
73
  offer,
93
- assertions: preparedInput.assertions,
74
+ assertions: prepared.assertions,
94
75
  };
95
76
  }
96
- /** Form one decided offer without admitting or reinterpreting it. */
97
- export async function prepareProtocolAttempt(input, baseAttempt) {
98
- const decisionObservation = await observeProtocolDecision(input);
99
- const preparedInput = await prepareProtocolSeed(input, decisionObservation);
100
- if (preparedInput.kind === "refused")
101
- return preparedInput;
102
- return await decideDecoratedProtocolOffer(input, baseAttempt, decisionObservation, preparedInput);
77
+ async function admitDecidedProtocolOffer(admission, primaryContract, seat, decisionObservation, prepared) {
78
+ const decided = await decideDecoratedProtocolOffer(admission, primaryContract, decisionObservation, prepared);
79
+ if (decided.kind === "refused")
80
+ return decided;
81
+ return await admitDecidedOffer({
82
+ channel: admission.channel,
83
+ repository: admission.repository,
84
+ seat,
85
+ decisionObservation: decided.observation,
86
+ attempt: decided.attempt,
87
+ offer: decided.offer,
88
+ primaryContract,
89
+ ...(admission.progress === undefined ? {} : { progress: admission.progress }),
90
+ assertions: decided.assertions,
91
+ ...(admission.validateAdmission === undefined ? {} : { validateAdmission: admission.validateAdmission }),
92
+ });
103
93
  }
104
94
  function protocolAttemptWithSeatClose(outcome) {
105
95
  return mergePrivateStateSeatClose(outcome, (value, closeLag) => {
@@ -108,47 +98,72 @@ function protocolAttemptWithSeatClose(outcome) {
108
98
  return { ...value, seatClose: appendPrivateStateSeatClose(value.seatClose, closeLag) };
109
99
  });
110
100
  }
111
- async function speculateProtocolAttempt(input) {
112
- const observation = await observeProtocolDecision(input);
113
- const prepared = await prepareProtocolSeed(input, observation);
114
- return prepared.kind === "refused" ? prepared : { observation, ...prepared };
101
+ async function runDirectProtocolAttempt(input, attempt) {
102
+ return await privateStateSeatAttempt(input.repository, async (seat) => {
103
+ const observation = await observeProtocolDecision(input);
104
+ return await admitDecidedProtocolOffer(input, input.input.contractId, seat, observation, {
105
+ attempt,
106
+ input: input.input,
107
+ assertions: [],
108
+ });
109
+ }, protocolAttemptWithSeatClose);
110
+ }
111
+ /**
112
+ * One full attempt: repeatable preparation outside custody, then a single in-custody cycle of
113
+ * observation, witness validation, assembly, decision, companions, and atomic publication.
114
+ */
115
+ async function runSplitProtocolAttempt(input, attempt) {
116
+ const external = await input.preparation.external(input.input);
117
+ if (external.kind === "refused")
118
+ return external;
119
+ return await privateStateSeatAttempt(input.repository, async (seat) => {
120
+ const observation = await observeProtocolDecision(input);
121
+ const assembled = await input.preparation.assemble(observation, input.input, external.prepared);
122
+ if (assembled.kind !== "prepared")
123
+ return assembled;
124
+ const assertions = [...(external.assertions ?? []), ...(assembled.assertions ?? [])];
125
+ if (!(await privateStateAssertionsMatch(input.repository, assertions)))
126
+ return STALE_PRIVATE_STATE_PREPARATION;
127
+ return await admitDecidedProtocolOffer(input, input.input.contractId, seat, observation, {
128
+ attempt,
129
+ input: assembled.input,
130
+ assertions,
131
+ });
132
+ }, protocolAttemptWithSeatClose);
115
133
  }
116
- async function admitSpeculativeProtocolAttempt(input, seat, speculated, attempt) {
117
- const fresh = await observeProtocolDecision(input);
118
- if (!samePrivateRootObservation(speculated.observation, fresh) ||
119
- !(await privateStateAssertionsMatch(input.repository, speculated.assertions))) {
120
- return STALE_PRIVATE_STATE_PREPARATION;
121
- }
122
- const decided = await decideDecoratedProtocolOffer(input, attempt, fresh, {
123
- kind: "prepared",
124
- input: speculated.input,
125
- assertions: speculated.assertions,
126
- });
127
- if (decided.kind === "refused")
128
- return decided;
129
- return await admitDecidedOffer({
130
- channel: input.channel,
131
- repository: input.repository,
132
- seat,
133
- decisionObservation: decided.observation,
134
- attempt: decided.attempt,
135
- offer: decided.offer,
136
- primaryContract: input.input.contractId,
137
- ...(input.progress === undefined ? {} : input.progress === undefined ? {} : { progress: input.progress }),
138
- assertions: decided.assertions,
139
- ...(input.validateAdmission === undefined ? {} : { validateAdmission: input.validateAdmission }),
134
+ /** Form one decided offer without admitting or reinterpreting it. */
135
+ export async function prepareProtocolAttempt(input, baseAttempt) {
136
+ if (input.preparation === undefined)
137
+ return await prepareDirectProtocolAttempt(input, baseAttempt);
138
+ const external = await input.preparation.external(input.input);
139
+ if (external.kind === "refused")
140
+ return external;
141
+ const decisionObservation = await observeProtocolDecision(input);
142
+ const assembled = await input.preparation.assemble(decisionObservation, input.input, external.prepared);
143
+ if (assembled.kind !== "prepared")
144
+ return assembled;
145
+ return await decideDecoratedProtocolOffer(input, input.input.contractId, decisionObservation, {
146
+ attempt: baseAttempt,
147
+ input: assembled.input,
148
+ assertions: [...(external.assertions ?? []), ...(assembled.assertions ?? [])],
140
149
  });
141
150
  }
142
- async function runSpeculativeProtocolAttempt(input, speculated, attempt) {
143
- if (speculated.kind === "refused")
144
- return speculated;
145
- return await privateStateSeatAttempt(input.repository, async (seat) => await admitSpeculativeProtocolAttempt(input, seat, speculated, attempt), protocolAttemptWithSeatClose);
151
+ async function prepareDirectProtocolAttempt(input, baseAttempt) {
152
+ const decisionObservation = await observeProtocolDecision(input);
153
+ return await decideDecoratedProtocolOffer(input, input.input.contractId, decisionObservation, {
154
+ attempt: baseAttempt,
155
+ input: input.input,
156
+ assertions: [],
157
+ });
146
158
  }
147
159
  /** Run bounded, verb-neutral attempts and return one real admission on acceptance. */
148
160
  export async function runProtocol(input) {
149
161
  const attempts = input.attempts;
150
162
  for (let index = 0; index < attempts.length; index += 1) {
151
- const result = await runSpeculativeProtocolAttempt(input, await speculateProtocolAttempt(input), attempts[index]);
163
+ const attempt = attempts[index];
164
+ const result = input.preparation === undefined
165
+ ? await runDirectProtocolAttempt(input, attempt)
166
+ : await runSplitProtocolAttempt(input, attempt);
152
167
  if (result.kind === "refused" || result.kind === "accepted" || result.kind === "publication-failed")
153
168
  return result;
154
169
  if (result.kind === "collision" && index + 1 === attempts.length)
@@ -8,6 +8,11 @@ type ProcessLaunch = Readonly<{
8
8
  readonly cwd?: string;
9
9
  readonly env?: NodeJS.ProcessEnv;
10
10
  readonly signal?: AbortSignal;
11
+ /** @internal Mechanical output observation only; it does not affect process ownership or outcome. */
12
+ readonly onOutput?: (output: Readonly<{
13
+ stream: "stdout" | "stderr";
14
+ text: string;
15
+ }>) => void;
11
16
  }>;
12
17
  export type CancellableProcess = Readonly<{
13
18
  child: ChildProcess;
@@ -27,23 +32,30 @@ type ProcessTerminal = Readonly<{
27
32
  readonly stderr: string;
28
33
  readonly truncated: boolean;
29
34
  }>;
35
+ type ProcessCapture = Readonly<{
36
+ readonly stdout: string;
37
+ readonly stderr: string;
38
+ readonly truncated: boolean;
39
+ }>;
30
40
  type ProcessTimeout = Readonly<{
31
41
  readonly kind: "timeout";
32
- }>;
42
+ }> & ProcessCapture;
33
43
  type ProcessSpawnError = Readonly<{
34
44
  readonly kind: "spawn-error";
35
45
  readonly diagnostic: string;
36
- }>;
46
+ }> & ProcessCapture;
37
47
  type ProcessUnknownExit = Readonly<{
38
48
  readonly kind: "unknown-exit";
39
- }>;
49
+ }> & ProcessCapture;
40
50
  type ProcessCancelled = Readonly<{
41
51
  readonly kind: "cancelled";
42
- }>;
52
+ }> & ProcessCapture;
43
53
  export type ProcessOutcome = ProcessTerminal | ProcessTimeout | ProcessSpawnError | ProcessUnknownExit | ProcessCancelled;
44
54
  type ProcessStreamError = Readonly<{
45
55
  readonly kind: "stream-error";
46
56
  readonly diagnostic: string;
57
+ readonly stderr: string;
58
+ readonly truncated: boolean;
47
59
  }>;
48
60
  export type ProcessConsumption = Readonly<{
49
61
  readonly outcome: ProcessOutcome | ProcessStreamError;