approval-md 0.2.0 → 0.3.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 (235) hide show
  1. package/README.md +63 -24
  2. package/SPEC.md +57 -11
  3. package/dist/src/channels/contract.d.ts +34 -1
  4. package/dist/src/channels/contract.js +200 -7
  5. package/dist/src/channels/contract.js.map +1 -1
  6. package/dist/src/channels/telegram.d.ts +123 -11
  7. package/dist/src/channels/telegram.js +218 -23
  8. package/dist/src/channels/telegram.js.map +1 -1
  9. package/dist/src/channels/web.d.ts +9 -0
  10. package/dist/src/channels/web.js +17 -0
  11. package/dist/src/channels/web.js.map +1 -1
  12. package/dist/src/cli/amend.js +214 -30
  13. package/dist/src/cli/amend.js.map +1 -1
  14. package/dist/src/cli/attest.d.ts +9 -0
  15. package/dist/src/cli/attest.js +134 -7
  16. package/dist/src/cli/attest.js.map +1 -1
  17. package/dist/src/cli/channel-telegram.d.ts +99 -26
  18. package/dist/src/cli/channel-telegram.js +311 -13
  19. package/dist/src/cli/channel-telegram.js.map +1 -1
  20. package/dist/src/cli/channel.d.ts +9 -0
  21. package/dist/src/cli/channel.js +9 -0
  22. package/dist/src/cli/channel.js.map +1 -1
  23. package/dist/src/cli/codex-bridge.d.ts +819 -0
  24. package/dist/src/cli/codex-bridge.js +1607 -0
  25. package/dist/src/cli/codex-bridge.js.map +1 -0
  26. package/dist/src/cli/codex.d.ts +1 -1
  27. package/dist/src/cli/codex.js +304 -7
  28. package/dist/src/cli/codex.js.map +1 -1
  29. package/dist/src/cli/daemon.js +4 -1
  30. package/dist/src/cli/daemon.js.map +1 -1
  31. package/dist/src/cli/doctor.js +467 -12
  32. package/dist/src/cli/doctor.js.map +1 -1
  33. package/dist/src/cli/execute.js +25 -2
  34. package/dist/src/cli/execute.js.map +1 -1
  35. package/dist/src/cli/help.d.ts +6 -2
  36. package/dist/src/cli/help.js +165 -60
  37. package/dist/src/cli/help.js.map +1 -1
  38. package/dist/src/cli/hook-codex.d.ts +49 -1
  39. package/dist/src/cli/hook-codex.js +60 -1
  40. package/dist/src/cli/hook-codex.js.map +1 -1
  41. package/dist/src/cli/hook.d.ts +459 -3
  42. package/dist/src/cli/hook.js +1062 -114
  43. package/dist/src/cli/hook.js.map +1 -1
  44. package/dist/src/cli/import.js +1 -1
  45. package/dist/src/cli/import.js.map +1 -1
  46. package/dist/src/cli/main.js +5 -3
  47. package/dist/src/cli/main.js.map +1 -1
  48. package/dist/src/cli/policy-apply.d.ts +195 -0
  49. package/dist/src/cli/policy-apply.js +573 -0
  50. package/dist/src/cli/policy-apply.js.map +1 -0
  51. package/dist/src/cli/policy.js +14 -1
  52. package/dist/src/cli/policy.js.map +1 -1
  53. package/dist/src/cli/preflight.d.ts +151 -13
  54. package/dist/src/cli/preflight.js +398 -41
  55. package/dist/src/cli/preflight.js.map +1 -1
  56. package/dist/src/cli/sandbox.js +17 -1
  57. package/dist/src/cli/sandbox.js.map +1 -1
  58. package/dist/src/cli/scaffold.d.ts +1 -1
  59. package/dist/src/cli/scaffold.js +1 -1
  60. package/dist/src/cli/setup-channel.d.ts +9 -0
  61. package/dist/src/cli/setup-channel.js +28 -1
  62. package/dist/src/cli/setup-channel.js.map +1 -1
  63. package/dist/src/cli/setup-common.d.ts +3 -1
  64. package/dist/src/cli/setup-common.js +3 -2
  65. package/dist/src/cli/setup-common.js.map +1 -1
  66. package/dist/src/cli/setup.d.ts +2 -0
  67. package/dist/src/cli/setup.js +94 -2
  68. package/dist/src/cli/setup.js.map +1 -1
  69. package/dist/src/cli/up.js +115 -51
  70. package/dist/src/cli/up.js.map +1 -1
  71. package/dist/src/cli/values.js +3 -4
  72. package/dist/src/cli/values.js.map +1 -1
  73. package/dist/src/cli/verb-registry.js +174 -9
  74. package/dist/src/cli/verb-registry.js.map +1 -1
  75. package/dist/src/cli/wordmark.d.ts +2 -2
  76. package/dist/src/cli/wordmark.js +2 -2
  77. package/dist/src/codex/broker.d.ts +229 -0
  78. package/dist/src/codex/broker.js +548 -0
  79. package/dist/src/codex/broker.js.map +1 -0
  80. package/dist/src/codex/runner.d.ts +178 -0
  81. package/dist/src/codex/runner.js +231 -0
  82. package/dist/src/codex/runner.js.map +1 -0
  83. package/dist/src/codex/serve.d.ts +56 -0
  84. package/dist/src/codex/serve.js +98 -0
  85. package/dist/src/codex/serve.js.map +1 -0
  86. package/dist/src/codex/workspace-commit.d.ts +219 -0
  87. package/dist/src/codex/workspace-commit.js +549 -0
  88. package/dist/src/codex/workspace-commit.js.map +1 -0
  89. package/dist/src/core/advance-cycle.d.ts +51 -0
  90. package/dist/src/core/advance-cycle.js +66 -2
  91. package/dist/src/core/advance-cycle.js.map +1 -1
  92. package/dist/src/core/agents-md.d.ts +20 -18
  93. package/dist/src/core/agents-md.js +33 -31
  94. package/dist/src/core/agents-md.js.map +1 -1
  95. package/dist/src/core/attest.d.ts +215 -0
  96. package/dist/src/core/attest.js +317 -7
  97. package/dist/src/core/attest.js.map +1 -1
  98. package/dist/src/core/audit.d.ts +18 -0
  99. package/dist/src/core/audit.js +13 -0
  100. package/dist/src/core/audit.js.map +1 -1
  101. package/dist/src/core/channel-owner.d.ts +213 -0
  102. package/dist/src/core/channel-owner.js +358 -0
  103. package/dist/src/core/channel-owner.js.map +1 -0
  104. package/dist/src/core/command-class.d.ts +154 -0
  105. package/dist/src/core/command-class.js +673 -20
  106. package/dist/src/core/command-class.js.map +1 -1
  107. package/dist/src/core/commit-guard.d.ts +272 -0
  108. package/dist/src/core/commit-guard.js +424 -0
  109. package/dist/src/core/commit-guard.js.map +1 -0
  110. package/dist/src/core/daemon-actor.d.ts +45 -0
  111. package/dist/src/core/daemon-actor.js +54 -0
  112. package/dist/src/core/daemon-actor.js.map +1 -0
  113. package/dist/src/core/dark-session.d.ts +109 -8
  114. package/dist/src/core/dark-session.js +266 -82
  115. package/dist/src/core/dark-session.js.map +1 -1
  116. package/dist/src/core/decision-refusal.d.ts +23 -2
  117. package/dist/src/core/decision-refusal.js +24 -2
  118. package/dist/src/core/decision-refusal.js.map +1 -1
  119. package/dist/src/core/env-file.d.ts +5 -0
  120. package/dist/src/core/env-file.js +60 -1
  121. package/dist/src/core/env-file.js.map +1 -1
  122. package/dist/src/core/execute.d.ts +15 -2
  123. package/dist/src/core/execute.js +15 -2
  124. package/dist/src/core/execute.js.map +1 -1
  125. package/dist/src/core/gate.d.ts +86 -1
  126. package/dist/src/core/gate.js +81 -1
  127. package/dist/src/core/gate.js.map +1 -1
  128. package/dist/src/core/gesture-refusal.d.ts +166 -0
  129. package/dist/src/core/gesture-refusal.js +188 -0
  130. package/dist/src/core/gesture-refusal.js.map +1 -0
  131. package/dist/src/core/harness-version.d.ts +1 -1
  132. package/dist/src/core/harness-version.js +3 -1
  133. package/dist/src/core/harness-version.js.map +1 -1
  134. package/dist/src/core/instance.d.ts +59 -2
  135. package/dist/src/core/instance.js +113 -0
  136. package/dist/src/core/instance.js.map +1 -1
  137. package/dist/src/core/log.d.ts +39 -1
  138. package/dist/src/core/log.js.map +1 -1
  139. package/dist/src/core/policy-explain.d.ts +10 -0
  140. package/dist/src/core/policy-explain.js +32 -0
  141. package/dist/src/core/policy-explain.js.map +1 -1
  142. package/dist/src/core/policy-load.d.ts +41 -1
  143. package/dist/src/core/policy-load.js +21 -3
  144. package/dist/src/core/policy-load.js.map +1 -1
  145. package/dist/src/core/policy-match.d.ts +43 -0
  146. package/dist/src/core/policy-match.js +52 -0
  147. package/dist/src/core/policy-match.js.map +1 -1
  148. package/dist/src/core/policy-proposal.d.ts +52 -0
  149. package/dist/src/core/policy-proposal.js +102 -2
  150. package/dist/src/core/policy-proposal.js.map +1 -1
  151. package/dist/src/core/protected-path-guard.d.ts +117 -4
  152. package/dist/src/core/protected-path-guard.js +362 -48
  153. package/dist/src/core/protected-path-guard.js.map +1 -1
  154. package/dist/src/core/question-preempted.d.ts +141 -0
  155. package/dist/src/core/question-preempted.js +152 -0
  156. package/dist/src/core/question-preempted.js.map +1 -0
  157. package/dist/src/core/read-scope.d.ts +172 -0
  158. package/dist/src/core/read-scope.js +252 -0
  159. package/dist/src/core/read-scope.js.map +1 -0
  160. package/dist/src/core/sandbox.d.ts +81 -0
  161. package/dist/src/core/sandbox.js +190 -1
  162. package/dist/src/core/sandbox.js.map +1 -1
  163. package/dist/src/core/sender-identity.d.ts +476 -0
  164. package/dist/src/core/sender-identity.js +572 -0
  165. package/dist/src/core/sender-identity.js.map +1 -0
  166. package/dist/src/core/shlex.d.ts +102 -0
  167. package/dist/src/core/shlex.js +159 -0
  168. package/dist/src/core/shlex.js.map +1 -0
  169. package/dist/src/core/values.d.ts +18 -8
  170. package/dist/src/core/values.js +36 -1
  171. package/dist/src/core/values.js.map +1 -1
  172. package/dist/src/daemon/advance.d.ts +10 -0
  173. package/dist/src/daemon/advance.js +25 -4
  174. package/dist/src/daemon/advance.js.map +1 -1
  175. package/dist/src/daemon/daemon.js +9 -0
  176. package/dist/src/daemon/daemon.js.map +1 -1
  177. package/dist/src/daemon/git-evidence.d.ts +2 -2
  178. package/dist/src/daemon/git-evidence.js +1 -1
  179. package/dist/src/mcp/server.js +8 -0
  180. package/dist/src/mcp/server.js.map +1 -1
  181. package/docs/cli-reference.md +932 -32
  182. package/docs/codex-enforced-session.md +75 -2
  183. package/docs/codex-workspace-broker.md +118 -0
  184. package/package.json +3 -1
  185. package/schema/event.schema.json +538 -9
  186. package/schema/fixtures/event/invalid/approval-granted-sender-hashed-false.json +20 -0
  187. package/schema/fixtures/event/invalid/approval-granted-sender-hashed-raw-id.json +20 -0
  188. package/schema/fixtures/event/invalid/audit-gesture-refused-human-actor.json +16 -0
  189. package/schema/fixtures/event/invalid/audit-gesture-refused-no-actor-no-sender.json +15 -0
  190. package/schema/fixtures/event/invalid/audit-gesture-refused-unknown-gesture.json +16 -0
  191. package/schema/fixtures/event/invalid/audit-question-preempted-agent-actor.json +16 -0
  192. package/schema/fixtures/event/invalid/audit-question-preempted-no-question-id.json +16 -0
  193. package/schema/fixtures/event/invalid/audit-question-preempted-unknown-source.json +15 -0
  194. package/schema/fixtures/event/invalid/gate-path-signed-off-absolute-path.json +14 -0
  195. package/schema/fixtures/event/invalid/gate-path-signed-off-agent-actor.json +14 -0
  196. package/schema/fixtures/event/invalid/gate-path-signed-off-missing-path.json +13 -0
  197. package/schema/fixtures/event/valid/approval-granted-sender-hashed.json +20 -0
  198. package/schema/fixtures/event/valid/audit-gesture-refused-review-note.json +21 -0
  199. package/schema/fixtures/event/valid/audit-gesture-refused-sender-key-unavailable.json +19 -0
  200. package/schema/fixtures/event/valid/audit-gesture-refused.json +19 -0
  201. package/schema/fixtures/event/valid/audit-question-preempted-no-verdict.json +16 -0
  202. package/schema/fixtures/event/valid/audit-question-preempted.json +20 -0
  203. package/schema/fixtures/event/valid/gate-path-signed-off.json +14 -0
  204. package/schema/fixtures/event/valid/harness-kind-claude-code.json +23 -0
  205. package/schema/fixtures/event/valid/harness-kind-codex.json +23 -0
  206. package/schema/fixtures/event/valid/harness-kind-cursor.json +23 -0
  207. package/schema/fixtures/event/valid/harness-kind-grok.json +23 -0
  208. package/schema/fixtures/event/valid/harness-kind-muse.json +23 -0
  209. package/schema/fixtures/policy/invalid/senders-half-keyed.json +20 -0
  210. package/schema/fixtures/policy/valid/canonical.json +1 -1
  211. package/schema/fixtures/policy/valid/senders-keyed.json +24 -0
  212. package/schema/fixtures/policy-md/valid/canonical.md +1 -1
  213. package/schema/fixtures/policy-md/valid/with-values.md +5 -7
  214. package/schema/fixtures/values/invalid/class-shaped.json +1 -1
  215. package/schema/fixtures/values/invalid/duplicate-entry.json +1 -1
  216. package/schema/fixtures/values/invalid/non-string-item.json +1 -1
  217. package/schema/fixtures/values/invalid/over-cap.json +1 -1
  218. package/schema/fixtures/values/invalid/unknown-key.json +1 -1
  219. package/schema/fixtures/values/invalid/version-float.json +1 -0
  220. package/schema/fixtures/values/invalid/version-integer.json +1 -0
  221. package/schema/fixtures/values/invalid/version-wrong-string.json +1 -0
  222. package/schema/fixtures/values/valid/empty-lists.json +2 -3
  223. package/schema/fixtures/values/valid/full.json +5 -7
  224. package/schema/fixtures/values/valid/minimal.json +1 -1
  225. package/schema/fixtures/values-md/invalid/schema-invalid.md +5 -3
  226. package/schema/fixtures/values-md/invalid/two-blocks.md +3 -3
  227. package/schema/fixtures/values-md/invalid/unterminated.md +2 -2
  228. package/schema/fixtures/values-md/invalid/version-1.md +69 -0
  229. package/schema/fixtures/values-md/invalid/version-unquoted.md +64 -0
  230. package/schema/fixtures/values-md/invalid/yaml-error.md +2 -2
  231. package/schema/fixtures/values-md/valid/absent.md +1 -1
  232. package/schema/fixtures/values-md/valid/with-values.md +5 -7
  233. package/schema/policy.schema.json +54 -2
  234. package/schema/values.schema.json +7 -11
  235. package/schema/fixtures/values/invalid/version-string.json +0 -1
@@ -0,0 +1,229 @@
1
+ /**
2
+ * The policy-bound Codex workspace change broker (APRV-325.2).
3
+ *
4
+ * APRV-325.1 shipped preparation, APRV-325.2.1 shipped the read-only planner,
5
+ * and this module is the thing both were for: the ONLY way a change reaches a
6
+ * canonical workspace in a constrained Codex session. It is deliberately not a
7
+ * mode of `src/mcp/server.ts`. That server publishes the whole agent verb
8
+ * catalog, including `run`, and a broker that lived inside it would be one
9
+ * unchecked flag away from the surface it exists to replace.
10
+ *
11
+ * ## What the caller may say, and what it may not
12
+ *
13
+ * A caller supplies exactly two things: a bounded list of typed operations, and
14
+ * the SHA-256 it believes the policy currently has. Everything else — the
15
+ * acting identity, the workspace root, the policy file, the log, the schema
16
+ * directory, the classes, the reversibility, the sandbox posture — comes from
17
+ * the installation manifest, which lives under a root-owned install root that
18
+ * no agent principal can write (`codex/manifest.ts`, `codex/trust.ts`).
19
+ * {@link parseBrokerInput} refuses an unknown key rather than ignoring it, so a
20
+ * caller that tries to name its own actor is told no instead of being quietly
21
+ * overridden, and {@link BROKER_TOOLS} is a POSITIVE allowlist of one: a name
22
+ * not on it is refused whether or not any surface advertised it.
23
+ *
24
+ * ## One action per class, never collapsed
25
+ *
26
+ * The planner returns one action leg per distinct path class, and this module
27
+ * registers, requests, starts and closes each of them separately. Collapsing
28
+ * four classes into one "workspace write" would lose exactly what the policy
29
+ * is for: the class is what the operator's roster, budget and autonomy are
30
+ * keyed to, and an action that reports a cheaper class than it performs is the
31
+ * self-reporting SPEC.md §11.1 invariant 4 forbids.
32
+ *
33
+ * ## The order, and why every step of it is load-bearing
34
+ *
35
+ * Read policy once and hash those exact bytes → check attestation against
36
+ * VERIFIED records → refuse if the caller's expected digest differs → plan →
37
+ * register the legs → authorize EVERY leg (policy, or a real grant token) →
38
+ * start EVERY leg → take custody → revalidate the plan UNDER custody → commit
39
+ * durably → read the workspace back → close every leg with what the reading
40
+ * said.
41
+ *
42
+ * Two of those orderings are the hard-won ones from the 2026-09-09 handover.
43
+ * **Every leg starts before any byte moves**, so a leg that refuses at start
44
+ * leaves a workspace that is untouched by construction rather than by cleanup;
45
+ * the already-started legs are closed `execution.failed` and the filesystem was
46
+ * never entered. And **revalidation happens under custody**, after the last
47
+ * start, because a revalidation that precedes the lock proves only what was
48
+ * true before another writer could act.
49
+ *
50
+ * ## Outcomes are read, not remembered
51
+ *
52
+ * `codex/workspace-commit.ts` classifies the workspace by reading it back
53
+ * against the journal. All-after closes every leg `execution.completed`;
54
+ * all-before closes every leg `execution.failed`; anything else — a partial
55
+ * apply, a failed rollback, an endpoint that could not be read — closes every
56
+ * leg `execution.indeterminate` with reason `workspace-commit-unknown`, the
57
+ * reason this task added to SPEC.md §8's closed set. Nothing here converts an
58
+ * indeterminate outcome into either of the others; that stays human-owned
59
+ * (`approval execution reconcile`).
60
+ */
61
+ import type { ClockOptions } from "../core/clock.js";
62
+ import type { AppendOptions } from "../core/log.js";
63
+ import type { CodexInstanceManifest } from "./manifest.js";
64
+ import { type CustodyReport, type WorkspaceState } from "./workspace-commit.js";
65
+ export declare const BROKER_VERSION: "approval.codex.broker.v1";
66
+ /**
67
+ * The positive server-side tool allowlist: one name, and nothing is reachable
68
+ * by any other.
69
+ *
70
+ * A deny list would have to name every verb the runtime grows next; this names
71
+ * the one a constrained Codex session may call, so anything added tomorrow is
72
+ * unreachable here until somebody decides otherwise. Fail closed, SPEC.md §11.
73
+ */
74
+ export declare const BROKER_TOOLS: ReadonlySet<string>;
75
+ /** The indeterminate reason a mixed or unreadable commit records (SPEC.md §8). */
76
+ export declare const WORKSPACE_COMMIT_UNKNOWN: "workspace-commit-unknown";
77
+ /**
78
+ * Everything the broker is, derived from the manifest and from nothing a caller
79
+ * said. Constructed by {@link brokerInstallation}; there is no other maker.
80
+ */
81
+ export interface BrokerInstallation {
82
+ instanceId: string;
83
+ /** `agent:codex-<instance_id>`, fixed by the installation. */
84
+ actor: string;
85
+ /** The canonical workspace root, absolute and normalized. */
86
+ root: string;
87
+ policyPath: string;
88
+ logPath: string;
89
+ }
90
+ /** Derive the fixed context from a validated instance manifest. */
91
+ export declare function brokerInstallation(manifest: CodexInstanceManifest): BrokerInstallation;
92
+ /** The whole of what a caller may say. */
93
+ export interface BrokerInput {
94
+ operations: unknown;
95
+ expected_policy_sha256: string;
96
+ }
97
+ export type BrokerInputResult = {
98
+ ok: true;
99
+ input: BrokerInput;
100
+ } | {
101
+ ok: false;
102
+ message: string;
103
+ };
104
+ /**
105
+ * Accept `{operations, expected_policy_sha256}` and refuse everything else.
106
+ *
107
+ * An unknown key is a refusal rather than a silent drop for the reason the MCP
108
+ * server refuses `--as`: a caller that named an actor, a root, a class or a
109
+ * token meant something by it, and the something they meant is not available
110
+ * here. Being told so is the only way they learn that.
111
+ */
112
+ export declare function parseBrokerInput(value: unknown): BrokerInputResult;
113
+ /**
114
+ * Every way the broker can say no. Frozen public API in the sense SPEC.md
115
+ * §11.1 invariant 6 means: each fires for exactly one condition, they are
116
+ * distinct from one another, and `tests/codex-broker.test.ts` pins the union.
117
+ */
118
+ export declare const BROKER_REFUSAL_CODES: readonly [
119
+ /** The tool name is not on {@link BROKER_TOOLS}. */
120
+ "tool-not-allowed",
121
+ /** The caller's object is not `{operations, expected_policy_sha256}`. */
122
+ "input-invalid",
123
+ /** The workspace root is not an absolute normalized path. */
124
+ "installation-invalid",
125
+ /** The log could not be read, is torn, or does not verify. */
126
+ "log-unavailable",
127
+ /** The policy file could not be read or parsed. */
128
+ "policy-unavailable",
129
+ /** The live policy bytes are not the attested ones. */
130
+ "policy-not-attested",
131
+ /** The caller's expected digest is not the live attested digest. */
132
+ "attestation-drift",
133
+ /** An endpoint names the broker's own reserved transaction paths. */
134
+ "reserved-path",
135
+ /** A replace whose after-image equals its preimage: nothing to approve. */
136
+ "no-op-operation",
137
+ /** The planner refused. `detail` carries its own code verbatim. */
138
+ "plan-refused",
139
+ /** The log already declares this task or key under different bytes. */
140
+ "replay",
141
+ /** Registration refused for any other gate reason. */
142
+ "register-refused",
143
+ /** Intake refused for any gate reason. */
144
+ "request-refused",
145
+ /** A leg needs a human's grant and no token for it was presented. */
146
+ "approval-required",
147
+ /** A leg's `execution.started` refused. Nothing was written to the workspace. */
148
+ "start-refused",
149
+ /** Another transaction holds the workspace lock. */
150
+ "custody-contended",
151
+ /** The lock or staging directory could not be created. */
152
+ "custody-unavailable",
153
+ /** The installation requires OS-exclusive custody and the host cannot prove it. */
154
+ "custody-insufficient",
155
+ /** The plan no longer validates against the workspace under custody. */
156
+ "workspace-drift",
157
+ /** Staging failed; the workspace is untouched by construction. */
158
+ "stage-failed",
159
+ /** The commit was attempted and did not take. Every leg is `execution.failed`. */
160
+ "commit-not-applied",
161
+ /** The commit was attempted and nobody knows. Every leg is indeterminate. */
162
+ "commit-unknown"];
163
+ export type BrokerRefusalCode = (typeof BROKER_REFUSAL_CODES)[number];
164
+ export interface BrokerRefusal {
165
+ ok: false;
166
+ code: BrokerRefusalCode;
167
+ message: string;
168
+ /** The underlying layer's own code, when this refusal wraps one. */
169
+ detail?: string;
170
+ /** The action keys a human must decide, when `code` is `approval-required`. */
171
+ pending?: readonly string[];
172
+ /** Present once a commit was attempted: what reading the workspace proved. */
173
+ state?: WorkspaceState;
174
+ }
175
+ export interface BrokerOptions extends ClockOptions {
176
+ /**
177
+ * Grant tokens, keyed by CLASS, for the legs whose policy resolves manual.
178
+ *
179
+ * Keyed by class rather than by action key because the class is the thing a
180
+ * human decided about and the key is derived; a caller cannot use this map to
181
+ * reach a leg it did not register, because every key is recomputed here.
182
+ */
183
+ tokens?: Readonly<Record<string, string>>;
184
+ /**
185
+ * Refuse unless the host proves OS-exclusive write custody (APRV-325.3 sets
186
+ * it). Absent, the broker still REPORTS which custody it got: the claim never
187
+ * silently softens, only the refusal is optional.
188
+ */
189
+ requireExclusiveCustody?: boolean;
190
+ schemaDir?: string;
191
+ append?: AppendOptions;
192
+ /** Forwarded verbatim to the commit's test seam. See `CommitOptions.onStep`. */
193
+ onStep?: (step: number) => void;
194
+ /** Test seam: called once after the last start and before custody is taken. */
195
+ afterStart?: () => void;
196
+ }
197
+ /** One class's leg through the gate. */
198
+ export interface BrokerLeg {
199
+ class: string;
200
+ actionKey: string;
201
+ /** How it was authorized: `policy` (no token exists) or `token` (a real grant). */
202
+ mode: "policy" | "token";
203
+ }
204
+ export interface BrokerSuccess {
205
+ ok: true;
206
+ version: typeof BROKER_VERSION;
207
+ task: string;
208
+ payload_hash: string;
209
+ policy_sha256: string;
210
+ legs: readonly BrokerLeg[];
211
+ custody: CustodyReport;
212
+ /** Always `"after"`: a success is a workspace that was read back as applied. */
213
+ state: "after";
214
+ }
215
+ export type BrokerResult = BrokerSuccess | BrokerRefusal;
216
+ /** The task id one proposal's bytes always produce. Deterministic, so a replay collides. */
217
+ export declare function brokerTaskId(instanceId: string, payloadHash: string): string;
218
+ /** The action key one class's leg always produces under that task. */
219
+ export declare function brokerActionKey(task: string, cls: string): string;
220
+ /**
221
+ * Apply one bounded typed proposal to the canonical workspace, or say exactly
222
+ * why not.
223
+ *
224
+ * `tool` is checked against {@link BROKER_TOOLS} first, before the input is
225
+ * even parsed: a surface that published nothing still refuses a name it does
226
+ * not serve, which is the defence in depth `src/mcp/server.ts` keeps for
227
+ * `mcp-guest-restricted`.
228
+ */
229
+ export declare function applyWorkspaceChange(tool: string, installation: BrokerInstallation, rawInput: unknown, options?: BrokerOptions): BrokerResult;