@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
@@ -1,7 +1,5 @@
1
1
  import * as path from "node:path";
2
- import { nativePolicyOwnership } from "../config/provider-policy-ownership.js";
3
- import { FlowError } from "../flow-error.js";
4
- import { buildCodexAppServerWorkspaceWriteSandboxPolicy, configOwnsApprovalPolicy, configOwnsSandboxPolicy, } from "./providers/codex-app-server.js";
2
+ import { FlowError } from "../../flow-error.js";
5
3
  const ACCESS_VALUES = new Set(["read", "write", "auto"]);
6
4
  const NETWORK_VALUES = new Set(["disabled", "enabled"]);
7
5
  const WEB_SEARCH_VALUES = new Set(["disabled", "cached", "live"]);
@@ -110,6 +108,42 @@ function assertEffortAllowed(effort, options) {
110
108
  return;
111
109
  throw new FlowError("INVALID_EFFORT_OVERRIDE", `Effort '${effort}' is not permitted; legal values: ${options.join(", ")}.`);
112
110
  }
111
+ /** Effective effort requires an explicit effective model. Model without effort remains legal. */
112
+ function assertEffortRequiresModel(model, effort, kind, akumaName) {
113
+ if (effort === undefined || model !== undefined)
114
+ return;
115
+ if (kind === "profile") {
116
+ throw new FlowError("INVALID_SETTINGS", `Akuma '${akumaName ?? ""}' sets effort '${effort}' without a model; set model whenever effort is configured.`);
117
+ }
118
+ throw new FlowError("INVALID_EFFORT_OVERRIDE", `Effort '${effort}' requires an explicit model.`);
119
+ }
120
+ function providerEffortAndModel(provider) {
121
+ switch (provider.kind) {
122
+ case "codex-sdk": {
123
+ const model = typeof provider.threadOptions.model === "string" ? provider.threadOptions.model : undefined;
124
+ const effort = typeof provider.threadOptions.modelReasoningEffort === "string"
125
+ ? provider.threadOptions.modelReasoningEffort
126
+ : undefined;
127
+ return { model, effort };
128
+ }
129
+ case "codex-app-server":
130
+ case "claude-agent-sdk":
131
+ case "opencode-sdk":
132
+ return { model: provider.model, effort: provider.effort };
133
+ case "pi":
134
+ return { model: provider.model, effort: provider.thinkingLevel };
135
+ }
136
+ }
137
+ function assertSnapshotEffortRequiresModel(snapshot) {
138
+ const fromAkuma = { model: snapshot.akuma.model, effort: snapshot.akuma.effort };
139
+ if (fromAkuma.effort !== undefined && fromAkuma.model === undefined) {
140
+ throw snapshotError("akuma.effort", "requires akuma.model");
141
+ }
142
+ const fromProvider = providerEffortAndModel(snapshot.provider);
143
+ if (fromProvider.effort !== undefined && fromProvider.model === undefined) {
144
+ throw snapshotError(`provider.${snapshot.provider.kind === "codex-sdk" ? "threadOptions.modelReasoningEffort" : snapshot.provider.kind === "pi" ? "thinkingLevel" : "effort"}`, "requires an explicit model");
145
+ }
146
+ }
113
147
  function parsePolicy(value) {
114
148
  const policy = objectAt(value, "akuma.policy");
115
149
  exactKeys(policy, ["access", "network", "webSearch"], "akuma.policy");
@@ -153,6 +187,21 @@ function parseProviderCommon(provider) {
153
187
  function optionalJsonObject(provider, key) {
154
188
  return key in provider ? cloneJsonObject(provider[key], `provider.${key}`) : undefined;
155
189
  }
190
+ function optionalStringRecord(provider, key) {
191
+ if (!(key in provider))
192
+ return undefined;
193
+ const value = provider[key];
194
+ if (!isPlainObject(value))
195
+ throw snapshotError(`provider.${key}`, "must be a plain JSON object");
196
+ const result = {};
197
+ for (const [entryKey, entryValue] of Object.entries(value)) {
198
+ if (typeof entryValue !== "string") {
199
+ throw snapshotError(`provider.${key}.${entryKey}`, "must be a string");
200
+ }
201
+ result[entryKey] = entryValue;
202
+ }
203
+ return Object.freeze(result);
204
+ }
156
205
  function parseSandboxPolicy(value) {
157
206
  const sandbox = objectAt(value, "provider.sandboxPolicy");
158
207
  exactKeys(sandbox, ["type", "writableRoots", "networkAccess", "excludeTmpdirEnvVar", "excludeSlashTmp"], "provider.sandboxPolicy");
@@ -198,7 +247,7 @@ function parseProvider(value) {
198
247
  return Object.freeze({ ...common, kind, ...(executable !== undefined ? { executable } : {}), ...(profile !== undefined ? { profile } : {}), ...(config !== undefined ? { config } : {}), threadOptions: cloneJsonObject(provider.threadOptions, "provider.threadOptions") });
199
248
  }
200
249
  case "codex-app-server": {
201
- exactKeys(provider, ["name", "source", "kind", "executable", "profile", "config", "model", "effort", "sandboxPolicy", "approvalPolicy"], "provider");
250
+ exactKeys(provider, ["name", "source", "kind", "executable", "profile", "config", "model", "effort", "sandboxPolicy", "approvalPolicy", "env"], "provider");
202
251
  if ("approvalPolicy" in provider && provider.approvalPolicy !== "never")
203
252
  throw snapshotError("provider.approvalPolicy", "must equal 'never'");
204
253
  const executable = optionalString(provider, "executable", "provider");
@@ -207,10 +256,11 @@ function parseProvider(value) {
207
256
  const model = optionalString(provider, "model", "provider");
208
257
  const effort = optionalString(provider, "effort", "provider");
209
258
  const sandboxPolicy = "sandboxPolicy" in provider ? parseSandboxPolicy(provider.sandboxPolicy) : undefined;
210
- return Object.freeze({ ...common, kind, ...(executable !== undefined ? { executable } : {}), ...(profile !== undefined ? { profile } : {}), ...(config !== undefined ? { config } : {}), ...(model !== undefined ? { model } : {}), ...(effort !== undefined ? { effort } : {}), ...(sandboxPolicy !== undefined ? { sandboxPolicy } : {}), ...(provider.approvalPolicy === "never" ? { approvalPolicy: "never" } : {}) });
259
+ const env = optionalStringRecord(provider, "env");
260
+ return Object.freeze({ ...common, kind, ...(executable !== undefined ? { executable } : {}), ...(profile !== undefined ? { profile } : {}), ...(config !== undefined ? { config } : {}), ...(model !== undefined ? { model } : {}), ...(effort !== undefined ? { effort } : {}), ...(sandboxPolicy !== undefined ? { sandboxPolicy } : {}), ...(provider.approvalPolicy === "never" ? { approvalPolicy: "never" } : {}), ...(env !== undefined ? { env } : {}) });
211
261
  }
212
262
  case "claude-agent-sdk": {
213
- exactKeys(provider, ["name", "source", "kind", "executable", "model", "effort", "settingSources", "permissionMode", "allowDangerouslySkipPermissions", "disallowedTools"], "provider");
263
+ exactKeys(provider, ["name", "source", "kind", "executable", "model", "effort", "settingSources", "permissionMode", "allowDangerouslySkipPermissions", "disallowedTools", "env"], "provider");
214
264
  const modes = new Set(["default", "plan", "acceptEdits", "bypassPermissions"]);
215
265
  const permissionMode = requiredEnum(provider, "permissionMode", "provider", modes);
216
266
  if (permissionMode === "bypassPermissions") {
@@ -240,20 +290,23 @@ function parseProvider(value) {
240
290
  const executable = optionalString(provider, "executable", "provider");
241
291
  const model = optionalString(provider, "model", "provider");
242
292
  const effort = optionalString(provider, "effort", "provider");
243
- return Object.freeze({ ...common, kind, ...(executable !== undefined ? { executable } : {}), ...(model !== undefined ? { model } : {}), ...(effort !== undefined ? { effort } : {}), ...(settingSources !== undefined ? { settingSources } : {}), permissionMode, ...(permissionMode === "bypassPermissions" ? { allowDangerouslySkipPermissions: true } : {}), ...(disallowedTools !== undefined ? { disallowedTools } : {}) });
293
+ const env = optionalStringRecord(provider, "env");
294
+ return Object.freeze({ ...common, kind, ...(executable !== undefined ? { executable } : {}), ...(model !== undefined ? { model } : {}), ...(effort !== undefined ? { effort } : {}), ...(settingSources !== undefined ? { settingSources } : {}), permissionMode, ...(permissionMode === "bypassPermissions" ? { allowDangerouslySkipPermissions: true } : {}), ...(disallowedTools !== undefined ? { disallowedTools } : {}), ...(env !== undefined ? { env } : {}) });
244
295
  }
245
296
  case "opencode-sdk": {
246
- exactKeys(provider, ["name", "source", "kind", "executable", "model", "effort"], "provider");
297
+ exactKeys(provider, ["name", "source", "kind", "executable", "model", "effort", "env"], "provider");
247
298
  const executable = optionalString(provider, "executable", "provider");
248
299
  const model = optionalString(provider, "model", "provider");
249
300
  const effort = optionalString(provider, "effort", "provider");
250
- return Object.freeze({ ...common, kind, ...(executable !== undefined ? { executable } : {}), ...(model !== undefined ? { model } : {}), ...(effort !== undefined ? { effort } : {}) });
301
+ const env = optionalStringRecord(provider, "env");
302
+ return Object.freeze({ ...common, kind, ...(executable !== undefined ? { executable } : {}), ...(model !== undefined ? { model } : {}), ...(effort !== undefined ? { effort } : {}), ...(env !== undefined ? { env } : {}) });
251
303
  }
252
304
  case "pi": {
253
- exactKeys(provider, ["name", "source", "kind", "model", "thinkingLevel"], "provider");
305
+ exactKeys(provider, ["name", "source", "kind", "model", "thinkingLevel", "env"], "provider");
254
306
  const model = optionalString(provider, "model", "provider");
255
307
  const thinkingLevel = optionalString(provider, "thinkingLevel", "provider");
256
- return Object.freeze({ ...common, kind, ...(model !== undefined ? { model } : {}), ...(thinkingLevel !== undefined ? { thinkingLevel } : {}) });
308
+ const env = optionalStringRecord(provider, "env");
309
+ return Object.freeze({ ...common, kind, ...(model !== undefined ? { model } : {}), ...(thinkingLevel !== undefined ? { thinkingLevel } : {}), ...(env !== undefined ? { env } : {}) });
257
310
  }
258
311
  default:
259
312
  throw snapshotError("provider.kind", `has invalid value '${kind}'`);
@@ -268,72 +321,13 @@ export function parseResolvedAkumaLaunchSnapshot(value) {
268
321
  throw snapshotError("akuma", "is required");
269
322
  if (!("provider" in snapshot))
270
323
  throw snapshotError("provider", "is required");
271
- return Object.freeze({ version: 1, akuma: parseAkuma(snapshot.akuma), provider: parseProvider(snapshot.provider) });
272
- }
273
- export function resolveAkumaLaunchSnapshot(selected, input) {
274
- const model = input.model ?? selected.profile.model;
275
- const effort = input.effort ?? selected.profile.effort;
276
- assertEffortAllowed(effort, selected.profile.effortOptions);
277
- const policy = {
278
- ...(selected.profile.access !== undefined ? { access: selected.profile.access } : {}),
279
- ...(selected.profile.network !== undefined ? { network: selected.profile.network } : {}),
280
- ...(selected.profile.webSearch !== undefined ? { webSearch: selected.profile.webSearch } : {}),
281
- };
282
- const akuma = {
283
- name: selected.name,
284
- source: selected.definition.source,
285
- body: selected.definition.body,
286
- ...(selected.profile.description !== undefined ? { description: selected.profile.description } : {}),
287
- ...(model !== undefined ? { model } : {}),
288
- ...(effort !== undefined ? { effort } : {}),
289
- ...(selected.profile.effortOptions !== undefined ? { effortOptions: selected.profile.effortOptions } : {}),
290
- policy,
291
- };
292
- const instance = selected.profile.providerInstance;
293
- const common = { name: selected.profile.provider, source: selected.profile.providerSource, kind: instance.kind };
294
- let provider;
295
- switch (instance.kind) {
296
- case "codex-sdk": {
297
- const threadOptions = { ...(instance.threadOptions ?? {}) };
298
- const ownership = nativePolicyOwnership(selected.profile.provider, instance);
299
- if (ownership.access.length === 0) {
300
- threadOptions.sandboxMode = selected.profile.access === "read" ? "read-only" : selected.profile.access === "auto" ? "danger-full-access" : "workspace-write";
301
- }
302
- if (ownership.network.length === 0)
303
- threadOptions.networkAccessEnabled = selected.profile.network === "enabled";
304
- if (ownership.webSearch.length === 0)
305
- threadOptions.webSearchMode = selected.profile.webSearch ?? "disabled";
306
- if (model !== undefined)
307
- threadOptions.model = model;
308
- if (effort !== undefined)
309
- threadOptions.modelReasoningEffort = effort;
310
- threadOptions.workingDirectory = input.executionCwd;
311
- threadOptions.skipGitRepoCheck = true;
312
- provider = { ...common, ...(instance.executable !== undefined ? { executable: instance.executable } : {}), ...(instance.profile !== undefined ? { profile: instance.profile } : {}), ...(instance.config !== undefined ? { config: instance.config } : {}), threadOptions };
313
- break;
314
- }
315
- case "codex-app-server": {
316
- const roots = [path.resolve(input.executionCwd), ...(instance.writableRoots ?? []).map((root) => path.resolve(input.projectRoot, root))];
317
- const writableRoots = [...new Set(roots)];
318
- provider = { ...common, ...(instance.executable !== undefined ? { executable: instance.executable } : {}), ...(instance.profile !== undefined ? { profile: instance.profile } : {}), ...(instance.config !== undefined ? { config: instance.config } : {}), ...(model !== undefined ? { model } : {}), ...(effort !== undefined ? { effort } : {}), ...(!configOwnsSandboxPolicy(instance.config) ? { sandboxPolicy: buildCodexAppServerWorkspaceWriteSandboxPolicy({ writableRoots, networkAccess: selected.profile.network === "enabled" }) } : {}), ...(!configOwnsApprovalPolicy(instance.config) ? { approvalPolicy: "never" } : {}) };
319
- break;
320
- }
321
- case "claude-agent-sdk": {
322
- const permissionMode = selected.profile.access === "read" ? "plan" : selected.profile.access === "write" ? "acceptEdits" : selected.profile.access === "auto" ? "bypassPermissions" : "default";
323
- provider = { ...common, ...(instance.executable !== undefined ? { executable: instance.executable } : {}), ...(model !== undefined ? { model } : {}), ...(effort !== undefined ? { effort } : {}), ...(instance.settingSources !== undefined ? { settingSources: instance.settingSources } : {}), permissionMode, ...(permissionMode === "bypassPermissions" ? { allowDangerouslySkipPermissions: true } : {}), ...(selected.profile.webSearch === "disabled" ? { disallowedTools: ["WebSearch"] } : {}) };
324
- break;
325
- }
326
- case "opencode-sdk":
327
- provider = { ...common, ...(instance.executable !== undefined ? { executable: instance.executable } : {}), ...(model !== undefined ? { model } : {}), ...(effort !== undefined ? { effort } : {}) };
328
- break;
329
- case "pi":
330
- provider = { ...common, ...(model !== undefined ? { model } : {}), ...(effort !== undefined ? { thinkingLevel: effort } : {}) };
331
- break;
332
- }
333
- return parseResolvedAkumaLaunchSnapshot({ version: 1, akuma, provider });
324
+ const parsed = Object.freeze({ version: 1, akuma: parseAkuma(snapshot.akuma), provider: parseProvider(snapshot.provider) });
325
+ assertSnapshotEffortRequiresModel(parsed);
326
+ return parsed;
334
327
  }
335
328
  export function applySnapshotEffort(snapshot, effort) {
336
329
  assertEffortAllowed(effort, snapshot.akuma.effortOptions);
330
+ assertEffortRequiresModel(snapshot.akuma.model, effort, "override");
337
331
  const akuma = { ...snapshot.akuma, effort };
338
332
  let provider;
339
333
  switch (snapshot.provider.kind) {
@@ -351,3 +345,4 @@ export function applySnapshotEffort(snapshot, effort) {
351
345
  }
352
346
  return parseResolvedAkumaLaunchSnapshot({ version: 1, akuma, provider });
353
347
  }
348
+ export { assertEffortAllowed, assertEffortRequiresModel };
@@ -0,0 +1,72 @@
1
+ import * as path from "node:path";
2
+ import { nativePolicyOwnership } from "../../config/provider-policy-ownership.js";
3
+ import { buildCodexAppServerWorkspaceWriteSandboxPolicy, configOwnsApprovalPolicy, configOwnsSandboxPolicy, } from "../providers/codex-app-server/adapter.js";
4
+ import { assertEffortAllowed, assertEffortRequiresModel, parseResolvedAkumaLaunchSnapshot, } from "./model.js";
5
+ export function resolveAkumaLaunchSnapshot(selected, input) {
6
+ const model = input.model ?? selected.profile.model;
7
+ const effort = input.effort ?? selected.profile.effort;
8
+ assertEffortAllowed(effort, selected.profile.effortOptions);
9
+ if (input.effort !== undefined) {
10
+ assertEffortRequiresModel(model, effort, "override");
11
+ }
12
+ else {
13
+ assertEffortRequiresModel(model, effort, "profile", selected.name);
14
+ }
15
+ const policy = {
16
+ ...(selected.profile.access !== undefined ? { access: selected.profile.access } : {}),
17
+ ...(selected.profile.network !== undefined ? { network: selected.profile.network } : {}),
18
+ ...(selected.profile.webSearch !== undefined ? { webSearch: selected.profile.webSearch } : {}),
19
+ };
20
+ const akuma = {
21
+ name: selected.name,
22
+ source: selected.definition.source,
23
+ body: selected.definition.body,
24
+ ...(selected.profile.description !== undefined ? { description: selected.profile.description } : {}),
25
+ ...(model !== undefined ? { model } : {}),
26
+ ...(effort !== undefined ? { effort } : {}),
27
+ ...(selected.profile.effortOptions !== undefined ? { effortOptions: selected.profile.effortOptions } : {}),
28
+ policy,
29
+ };
30
+ const instance = selected.profile.providerInstance;
31
+ const common = { name: selected.profile.provider, source: selected.profile.providerSource, kind: instance.kind };
32
+ let provider;
33
+ switch (instance.kind) {
34
+ case "codex-sdk": {
35
+ const threadOptions = { ...(instance.threadOptions ?? {}) };
36
+ const ownership = nativePolicyOwnership(selected.profile.provider, instance);
37
+ if (ownership.access.length === 0) {
38
+ threadOptions.sandboxMode = selected.profile.access === "read" ? "read-only" : selected.profile.access === "auto" ? "danger-full-access" : "workspace-write";
39
+ }
40
+ if (ownership.network.length === 0)
41
+ threadOptions.networkAccessEnabled = selected.profile.network === "enabled";
42
+ if (ownership.webSearch.length === 0)
43
+ threadOptions.webSearchMode = selected.profile.webSearch ?? "disabled";
44
+ if (model !== undefined)
45
+ threadOptions.model = model;
46
+ if (effort !== undefined)
47
+ threadOptions.modelReasoningEffort = effort;
48
+ threadOptions.workingDirectory = input.executionCwd;
49
+ threadOptions.skipGitRepoCheck = true;
50
+ provider = { ...common, ...(instance.executable !== undefined ? { executable: instance.executable } : {}), ...(instance.profile !== undefined ? { profile: instance.profile } : {}), ...(instance.config !== undefined ? { config: instance.config } : {}), threadOptions };
51
+ break;
52
+ }
53
+ case "codex-app-server": {
54
+ const roots = [path.resolve(input.executionCwd), ...(instance.writableRoots ?? []).map((root) => path.resolve(input.projectRoot, root))];
55
+ const writableRoots = [...new Set(roots)];
56
+ provider = { ...common, ...(instance.executable !== undefined ? { executable: instance.executable } : {}), ...(instance.profile !== undefined ? { profile: instance.profile } : {}), ...(instance.config !== undefined ? { config: instance.config } : {}), ...(model !== undefined ? { model } : {}), ...(effort !== undefined ? { effort } : {}), ...(!configOwnsSandboxPolicy(instance.config) ? { sandboxPolicy: buildCodexAppServerWorkspaceWriteSandboxPolicy({ writableRoots, networkAccess: selected.profile.network === "enabled" }) } : {}), ...(!configOwnsApprovalPolicy(instance.config) ? { approvalPolicy: "never" } : {}), ...(instance.env !== undefined ? { env: instance.env } : {}) };
57
+ break;
58
+ }
59
+ case "claude-agent-sdk": {
60
+ const permissionMode = selected.profile.access === "read" ? "plan" : selected.profile.access === "write" ? "acceptEdits" : selected.profile.access === "auto" ? "bypassPermissions" : "default";
61
+ provider = { ...common, ...(instance.executable !== undefined ? { executable: instance.executable } : {}), ...(model !== undefined ? { model } : {}), ...(effort !== undefined ? { effort } : {}), ...(instance.settingSources !== undefined ? { settingSources: instance.settingSources } : {}), permissionMode, ...(permissionMode === "bypassPermissions" ? { allowDangerouslySkipPermissions: true } : {}), ...(selected.profile.webSearch === "disabled" ? { disallowedTools: ["WebSearch"] } : {}), ...(instance.env !== undefined ? { env: instance.env } : {}) };
62
+ break;
63
+ }
64
+ case "opencode-sdk":
65
+ provider = { ...common, ...(instance.executable !== undefined ? { executable: instance.executable } : {}), ...(model !== undefined ? { model } : {}), ...(effort !== undefined ? { effort } : {}), ...(instance.env !== undefined ? { env: instance.env } : {}) };
66
+ break;
67
+ case "pi":
68
+ provider = { ...common, ...(model !== undefined ? { model } : {}), ...(effort !== undefined ? { thinkingLevel: effort } : {}), ...(instance.env !== undefined ? { env: instance.env } : {}) };
69
+ break;
70
+ }
71
+ return parseResolvedAkumaLaunchSnapshot({ version: 1, akuma, provider });
72
+ }
@@ -51,7 +51,7 @@ export async function createOpencodeServer(options = {}) {
51
51
  const startupTimeoutMs = options.startupTimeoutMs ?? DEFAULT_STARTUP_TIMEOUT_MS;
52
52
  const child = spawn(executable, [OPENCODE_SERVE_SUBCOMMAND, OPENCODE_HOSTNAME_FLAG, hostname, OPENCODE_PORT_FLAG, String(port)], {
53
53
  cwd: directory,
54
- env: { ...process.env, ...(options.env ?? {}) },
54
+ env: options.env !== undefined ? { ...options.env } : { ...process.env },
55
55
  stdio: ["ignore", "pipe", "pipe"],
56
56
  });
57
57
  let exited = false;
@@ -60,10 +60,30 @@ export async function createOpencodeServer(options = {}) {
60
60
  child.on("error", (error) => { stderr += `${stderr ? " " : ""}${error.message}`; });
61
61
  child.on("close", () => { exited = true; });
62
62
  const client = createOpencodeClient({ baseUrl: url, directory });
63
- const stop = async () => {
63
+ let gracefulStop;
64
+ let forceStop;
65
+ const stop = async (mode = "graceful") => {
64
66
  if (exited)
65
67
  return;
66
- await new Promise((resolve) => {
68
+ if (mode === "force") {
69
+ forceStop ??= new Promise((resolve, reject) => {
70
+ child.once("close", resolve);
71
+ try {
72
+ if (!child.kill("SIGKILL") && !exited)
73
+ reject(new Error("OpenCode server force kill failed"));
74
+ }
75
+ catch (error) {
76
+ reject(error);
77
+ }
78
+ });
79
+ await forceStop;
80
+ return;
81
+ }
82
+ if (forceStop) {
83
+ await forceStop;
84
+ return;
85
+ }
86
+ gracefulStop ??= new Promise((resolve) => {
67
87
  let settled = false;
68
88
  let forceKill;
69
89
  const finish = () => {
@@ -82,6 +102,7 @@ export async function createOpencodeServer(options = {}) {
82
102
  }, SERVER_SHUTDOWN_FORCE_KILL_MS);
83
103
  forceKill.unref();
84
104
  });
105
+ await gracefulStop;
85
106
  };
86
107
  try {
87
108
  await waitForServerReady(client, directory, startupTimeoutMs, options.signal);