@zhihaohong52/sonata 0.4.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 (183) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +437 -0
  3. package/dist/adapters/claude.js +83 -0
  4. package/dist/adapters/claude.js.map +1 -0
  5. package/dist/adapters/codex.js +382 -0
  6. package/dist/adapters/codex.js.map +1 -0
  7. package/dist/adapters/index.js +21 -0
  8. package/dist/adapters/index.js.map +1 -0
  9. package/dist/adapters/opencode.js +104 -0
  10. package/dist/adapters/opencode.js.map +1 -0
  11. package/dist/adapters/pi.js +152 -0
  12. package/dist/adapters/pi.js.map +1 -0
  13. package/dist/adapters/reasonix.js +358 -0
  14. package/dist/adapters/reasonix.js.map +1 -0
  15. package/dist/adapters/types.js +2 -0
  16. package/dist/adapters/types.js.map +1 -0
  17. package/dist/agent-markers.js +3 -0
  18. package/dist/agent-markers.js.map +1 -0
  19. package/dist/aipricing.js +109 -0
  20. package/dist/aipricing.js.map +1 -0
  21. package/dist/catalog.js +274 -0
  22. package/dist/catalog.js.map +1 -0
  23. package/dist/cli.js +766 -0
  24. package/dist/cli.js.map +1 -0
  25. package/dist/commands/approve.js +23 -0
  26. package/dist/commands/approve.js.map +1 -0
  27. package/dist/commands/auth.js +41 -0
  28. package/dist/commands/auth.js.map +1 -0
  29. package/dist/commands/catalog.js +172 -0
  30. package/dist/commands/catalog.js.map +1 -0
  31. package/dist/commands/code.js +113 -0
  32. package/dist/commands/code.js.map +1 -0
  33. package/dist/commands/dispatch.js +148 -0
  34. package/dist/commands/dispatch.js.map +1 -0
  35. package/dist/commands/doctor.js +545 -0
  36. package/dist/commands/doctor.js.map +1 -0
  37. package/dist/commands/gc.js +25 -0
  38. package/dist/commands/gc.js.map +1 -0
  39. package/dist/commands/init-log.js +83 -0
  40. package/dist/commands/init-log.js.map +1 -0
  41. package/dist/commands/init.js +157 -0
  42. package/dist/commands/init.js.map +1 -0
  43. package/dist/commands/log.js +24 -0
  44. package/dist/commands/log.js.map +1 -0
  45. package/dist/commands/route.js +660 -0
  46. package/dist/commands/route.js.map +1 -0
  47. package/dist/commands/run.js +199 -0
  48. package/dist/commands/run.js.map +1 -0
  49. package/dist/commands/runs.js +37 -0
  50. package/dist/commands/runs.js.map +1 -0
  51. package/dist/commands/serve.js +818 -0
  52. package/dist/commands/serve.js.map +1 -0
  53. package/dist/commands/status.js +14 -0
  54. package/dist/commands/status.js.map +1 -0
  55. package/dist/commands/sync.js +258 -0
  56. package/dist/commands/sync.js.map +1 -0
  57. package/dist/commands/tail.js +229 -0
  58. package/dist/commands/tail.js.map +1 -0
  59. package/dist/commands/usage.js +85 -0
  60. package/dist/commands/usage.js.map +1 -0
  61. package/dist/commands/verify.js +69 -0
  62. package/dist/commands/verify.js.map +1 -0
  63. package/dist/commands/wait.js +38 -0
  64. package/dist/commands/wait.js.map +1 -0
  65. package/dist/config.js +588 -0
  66. package/dist/config.js.map +1 -0
  67. package/dist/detect.js +445 -0
  68. package/dist/detect.js.map +1 -0
  69. package/dist/filelock.js +83 -0
  70. package/dist/filelock.js.map +1 -0
  71. package/dist/init/apply.js +96 -0
  72. package/dist/init/apply.js.map +1 -0
  73. package/dist/init/candidates.js +115 -0
  74. package/dist/init/candidates.js.map +1 -0
  75. package/dist/init/discover.js +216 -0
  76. package/dist/init/discover.js.map +1 -0
  77. package/dist/init/helpers.js +429 -0
  78. package/dist/init/helpers.js.map +1 -0
  79. package/dist/init/interactive-state.js +128 -0
  80. package/dist/init/interactive-state.js.map +1 -0
  81. package/dist/init/plan.js +225 -0
  82. package/dist/init/plan.js.map +1 -0
  83. package/dist/init/scripted-state.js +130 -0
  84. package/dist/init/scripted-state.js.map +1 -0
  85. package/dist/init/toml.js +83 -0
  86. package/dist/init/toml.js.map +1 -0
  87. package/dist/init/validate.js +149 -0
  88. package/dist/init/validate.js.map +1 -0
  89. package/dist/ledger.js +123 -0
  90. package/dist/ledger.js.map +1 -0
  91. package/dist/mcp/protocol.js +64 -0
  92. package/dist/mcp/protocol.js.map +1 -0
  93. package/dist/mcp/server.js +96 -0
  94. package/dist/mcp/server.js.map +1 -0
  95. package/dist/mcp/tools.js +210 -0
  96. package/dist/mcp/tools.js.map +1 -0
  97. package/dist/mode.js +64 -0
  98. package/dist/mode.js.map +1 -0
  99. package/dist/native/claude-shim.js +228 -0
  100. package/dist/native/claude-shim.js.map +1 -0
  101. package/dist/native/codex-auth.js +177 -0
  102. package/dist/native/codex-auth.js.map +1 -0
  103. package/dist/native/copilot-auth.js +105 -0
  104. package/dist/native/copilot-auth.js.map +1 -0
  105. package/dist/native/credentials.js +83 -0
  106. package/dist/native/credentials.js.map +1 -0
  107. package/dist/native/hosts.js +60 -0
  108. package/dist/native/hosts.js.map +1 -0
  109. package/dist/native/litellm.js +103 -0
  110. package/dist/native/litellm.js.map +1 -0
  111. package/dist/native/models.js +96 -0
  112. package/dist/native/models.js.map +1 -0
  113. package/dist/native/oauth-login.js +112 -0
  114. package/dist/native/oauth-login.js.map +1 -0
  115. package/dist/native/router.js +469 -0
  116. package/dist/native/router.js.map +1 -0
  117. package/dist/native/tls.js +73 -0
  118. package/dist/native/tls.js.map +1 -0
  119. package/dist/native/usage.js +119 -0
  120. package/dist/native/usage.js.map +1 -0
  121. package/dist/normalize.js +98 -0
  122. package/dist/normalize.js.map +1 -0
  123. package/dist/pricing.js +86 -0
  124. package/dist/pricing.js.map +1 -0
  125. package/dist/roles.js +49 -0
  126. package/dist/roles.js.map +1 -0
  127. package/dist/sessions.js +62 -0
  128. package/dist/sessions.js.map +1 -0
  129. package/dist/settings.js +131 -0
  130. package/dist/settings.js.map +1 -0
  131. package/dist/store.js +70 -0
  132. package/dist/store.js.map +1 -0
  133. package/dist/tmux.js +89 -0
  134. package/dist/tmux.js.map +1 -0
  135. package/dist/tui-ink/app-state.js +340 -0
  136. package/dist/tui-ink/app-state.js.map +1 -0
  137. package/dist/tui-ink/app.js +156 -0
  138. package/dist/tui-ink/app.js.map +1 -0
  139. package/dist/tui-ink/components/byok-step.js +102 -0
  140. package/dist/tui-ink/components/byok-step.js.map +1 -0
  141. package/dist/tui-ink/components/login-screen.js +80 -0
  142. package/dist/tui-ink/components/login-screen.js.map +1 -0
  143. package/dist/tui-ink/components/models-step.js +92 -0
  144. package/dist/tui-ink/components/models-step.js.map +1 -0
  145. package/dist/tui-ink/components/multi-select-state.js +56 -0
  146. package/dist/tui-ink/components/multi-select-state.js.map +1 -0
  147. package/dist/tui-ink/components/multi-select.js +69 -0
  148. package/dist/tui-ink/components/multi-select.js.map +1 -0
  149. package/dist/tui-ink/components/providers-step.js +285 -0
  150. package/dist/tui-ink/components/providers-step.js.map +1 -0
  151. package/dist/tui-ink/components/ranked-select-state.js +41 -0
  152. package/dist/tui-ink/components/ranked-select-state.js.map +1 -0
  153. package/dist/tui-ink/components/ranked-select.js +61 -0
  154. package/dist/tui-ink/components/ranked-select.js.map +1 -0
  155. package/dist/tui-ink/components/search-select.js +47 -0
  156. package/dist/tui-ink/components/search-select.js.map +1 -0
  157. package/dist/tui-ink/components/text-input-state.js +42 -0
  158. package/dist/tui-ink/components/text-input-state.js.map +1 -0
  159. package/dist/tui-ink/components/text-input.js +48 -0
  160. package/dist/tui-ink/components/text-input.js.map +1 -0
  161. package/dist/tui-ink/run.js +38 -0
  162. package/dist/tui-ink/run.js.map +1 -0
  163. package/dist/tui-ink/test-compile.js +4 -0
  164. package/dist/tui-ink/test-compile.js.map +1 -0
  165. package/dist/tui-ink/types.js +2 -0
  166. package/dist/tui-ink/types.js.map +1 -0
  167. package/dist/tui.js +330 -0
  168. package/dist/tui.js.map +1 -0
  169. package/dist/types.js +2 -0
  170. package/dist/types.js.map +1 -0
  171. package/dist/watchdog.js +85 -0
  172. package/dist/watchdog.js.map +1 -0
  173. package/hooks/capture-mode.mjs +56 -0
  174. package/hooks/ensure-serve.mjs +140 -0
  175. package/hooks/hooks.json +12 -0
  176. package/hooks/route-session.mjs +47 -0
  177. package/hooks/route-subagent.mjs +54 -0
  178. package/package.json +64 -0
  179. package/roles/code.md +13 -0
  180. package/roles/explore.md +9 -0
  181. package/roles/plan.md +10 -0
  182. package/roles/review.md +11 -0
  183. package/skills/loop/SKILL.md +43 -0
@@ -0,0 +1,382 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+ import { existsSync, readFileSync } from 'node:fs';
4
+ import { join } from 'node:path';
5
+ import { connect } from 'node:net';
6
+ import { spawn } from 'node:child_process';
7
+ import { isReadOnlyRole } from '../config.js';
8
+ const run = promisify(execFile);
9
+ /**
10
+ * Codex model discovery.
11
+ *
12
+ * Codex has no `models` subcommand, which is why its models were hand-written
13
+ * into sonata.toml. It does have `codex app-server`, a JSON-RPC service whose
14
+ * `model/list` method returns the catalogue the picker needs. The protocol is
15
+ * not guesswork: `codex app-server generate-json-schema` emits it, and the
16
+ * response captured from a real call is in
17
+ * `tests/fixtures/codex/model-list.json`.
18
+ *
19
+ * Unlike opencode and pi, codex has no provider dimension — ids are bare
20
+ * (`gpt-5.6-sol`), which `parseConfig` enforces. `provider` is set to `codex`
21
+ * for grouping in the picker only; `ref` stays bare so the config key comes
22
+ * out as `codex-gpt-5.6-sol`.
23
+ */
24
+ export function parseCodexModels(stdout) {
25
+ let parsed;
26
+ try {
27
+ parsed = JSON.parse(stdout);
28
+ }
29
+ catch {
30
+ return [];
31
+ }
32
+ // Accept either the whole JSON-RPC response or the result object alone.
33
+ const data = parsed?.result?.data ?? parsed?.data;
34
+ if (!Array.isArray(data))
35
+ return [];
36
+ const out = [];
37
+ for (const m of data) {
38
+ // `hidden` marks models the picker is meant not to offer (internal or
39
+ // review-only entries). A missing id is not a model.
40
+ if (m === null || typeof m !== 'object')
41
+ continue;
42
+ if (typeof m.id !== 'string' || m.id.length === 0)
43
+ continue;
44
+ if (m.hidden === true)
45
+ continue;
46
+ out.push({
47
+ harness: 'codex',
48
+ provider: 'codex',
49
+ id: m.id,
50
+ ref: m.id,
51
+ name: typeof m.displayName === 'string' ? m.displayName : undefined,
52
+ });
53
+ }
54
+ return out;
55
+ }
56
+ /**
57
+ * Asks a short-lived `codex app-server` for its catalogue.
58
+ *
59
+ * The server speaks newline-delimited JSON-RPC and requires `initialize`
60
+ * before any other method. Everything is bounded by a timeout and the child is
61
+ * always killed: `sonata doctor` must never hang on a harness.
62
+ */
63
+ export function codexModelList(timeoutMs = 15_000) {
64
+ return new Promise((resolve) => {
65
+ let child;
66
+ try {
67
+ child = spawn('codex', ['app-server'], { stdio: ['pipe', 'pipe', 'ignore'] });
68
+ }
69
+ catch {
70
+ resolve(null);
71
+ return;
72
+ }
73
+ let buf = '';
74
+ let settled = false;
75
+ const finish = (value) => {
76
+ if (settled)
77
+ return;
78
+ settled = true;
79
+ clearTimeout(timer);
80
+ child.kill();
81
+ resolve(value);
82
+ };
83
+ const timer = setTimeout(() => finish(null), timeoutMs);
84
+ child.on('error', () => finish(null));
85
+ child.on('exit', () => finish(null));
86
+ child.stdout?.on('data', (d) => {
87
+ buf += d.toString();
88
+ let i;
89
+ while ((i = buf.indexOf('\n')) >= 0) {
90
+ const line = buf.slice(0, i);
91
+ buf = buf.slice(i + 1);
92
+ if (line.trim().length === 0)
93
+ continue;
94
+ let msg;
95
+ try {
96
+ msg = JSON.parse(line);
97
+ }
98
+ catch {
99
+ continue;
100
+ }
101
+ if (msg?.id === 1) {
102
+ child.stdin?.write(`${JSON.stringify({ jsonrpc: '2.0', id: 2, method: 'model/list', params: {} })}\n`);
103
+ }
104
+ else if (msg?.id === 2) {
105
+ finish(line);
106
+ }
107
+ }
108
+ });
109
+ child.stdin?.write(`${JSON.stringify({
110
+ jsonrpc: '2.0',
111
+ id: 1,
112
+ method: 'initialize',
113
+ params: { clientInfo: { name: 'sonata', version: '0.0.1', title: 'sonata' } },
114
+ })}\n`);
115
+ });
116
+ }
117
+ /**
118
+ * Codex CLI adapter.
119
+ *
120
+ * Codex maps onto sonata's permission modes more directly than OpenCode does,
121
+ * because `codex exec` takes an explicit sandbox policy. It also writes its
122
+ * final message to a file on request, which gives sonata a harness-guaranteed
123
+ * report instead of relying on the model to write one.
124
+ *
125
+ * `codex exec` is strictly non-interactive and cannot raise an approval prompt,
126
+ * so `default` mode uses the interactive TUI with `approval_policy=on-request`
127
+ * — otherwise a sonata agent in `default` mode would be able to write without
128
+ * ever asking, which is more permissive than the session that spawned it.
129
+ */
130
+ /**
131
+ * Every pattern here was written from real captured codex output, which lives
132
+ * in tests/fixtures/panes/. The previous set was written from imagination and
133
+ * matched none of what codex actually prints: a run waiting for approval was
134
+ * reported as STALLED rather than PAUSED.
135
+ *
136
+ * Deliberately absent are loose patterns like /approve.*\?/ — codex relays the
137
+ * model's own prose to the pane, and a model that writes "shall I approve the
138
+ * PR?" would otherwise park the run in a PAUSED state nobody can clear.
139
+ */
140
+ const PROMPT_PATTERNS = [
141
+ // Codex phrases every approval this way: "Would you like to run the
142
+ // following command?", and the same shape for other escalations.
143
+ /^\s*would you like to .*\?\s*$/i,
144
+ // The footer under any codex selection list.
145
+ /press enter to confirm/i,
146
+ // Shown on first entry to a directory codex has not been trusted in. Not an
147
+ // approval of the model's work — the run has not started yet.
148
+ /do you trust the contents of this directory\?/i,
149
+ ];
150
+ /** Longest prompt block worth relaying; codex lists are far shorter than this. */
151
+ const MAX_PROMPT_LINES = 14;
152
+ /** `codex exec -s <mode>`; `default` is handled separately via the TUI. */
153
+ function sandboxFor(mode) {
154
+ switch (mode) {
155
+ case 'plan':
156
+ return 'read-only';
157
+ case 'acceptEdits':
158
+ return 'workspace-write';
159
+ case 'bypassPermissions':
160
+ return 'danger-full-access';
161
+ default:
162
+ return 'read-only';
163
+ }
164
+ }
165
+ function shellQuote(s) {
166
+ return `'${s.replace(/'/g, `'\\''`)}'`;
167
+ }
168
+ function buildScript(input) {
169
+ // A read-only role must never be able to write, whatever the permission mode
170
+ // says: force the read-only sandbox and the non-interactive `codex exec`,
171
+ // which never raises an approval prompt.
172
+ const readOnly = isReadOnlyRole(input.role);
173
+ const interactive = input.mode === 'default' && !readOnly;
174
+ const lastMessage = `${input.runDir}/last-message.txt`;
175
+ const message = 'Follow the instructions in the attached file.';
176
+ // The instructions path is given in the prompt rather than piped, so stdin
177
+ // stays free and the prompt survives the harness's own formatting.
178
+ const prompt = `${message}\n\nInstructions file: ${input.instructionsPath}`;
179
+ // `--skip-git-repo-check` is accepted by `codex exec` only; passing it to the
180
+ // interactive TUI is a hard argument error. Keep it out of the shared flags.
181
+ const common = [`-m ${shellQuote(input.modelId)}`];
182
+ let invocation;
183
+ if (interactive) {
184
+ // The user's config may set approval_policy=never globally; override it so
185
+ // approvals actually surface and sonata's watcher can escalate them.
186
+ invocation = [
187
+ 'codex',
188
+ ...common,
189
+ '-c approval_policy="on-request"',
190
+ '-s workspace-write',
191
+ shellQuote(prompt),
192
+ ].join(' ');
193
+ }
194
+ else {
195
+ invocation = [
196
+ 'codex exec',
197
+ ...common,
198
+ '--skip-git-repo-check',
199
+ '-c approval_policy="never"',
200
+ `-s ${readOnly ? 'read-only' : sandboxFor(input.mode)}`,
201
+ `-o ${shellQuote(lastMessage)}`,
202
+ shellQuote(prompt),
203
+ ].join(' ');
204
+ }
205
+ const script = interactive ? interactiveScript(input, invocation) : [
206
+ '#!/bin/bash',
207
+ 'set -o pipefail',
208
+ `cd ${shellQuote(input.cwd)} || exit 97`,
209
+ `${invocation} 2>&1 | tee -a ${shellQuote(`${input.runDir}/harness.log`)}`,
210
+ `echo $? > ${shellQuote(`${input.runDir}/exit`)}`,
211
+ '',
212
+ ].join('\n');
213
+ // Read-only roles and plan mode both invoke Codex with a read-only sandbox,
214
+ // so the model cannot write report.md. Default-mode code runs use the
215
+ // interactive workspace-write TUI instead of sandboxFor() and can write it.
216
+ const canWriteReport = interactive || (!readOnly && sandboxFor(input.mode) !== 'read-only');
217
+ return { script, interactive, canWriteReport };
218
+ }
219
+ /** `default` mode needs Codex's interactive TUI to surface approvals. */
220
+ function interactiveScript(input, invocation) {
221
+ const reportPath = `${input.runDir}/report.md`;
222
+ const exitPath = `${input.runDir}/exit`;
223
+ return [
224
+ '#!/bin/bash',
225
+ 'set -o pipefail',
226
+ `cd ${shellQuote(input.cwd)} || exit 97`,
227
+ `SESSION="$(tmux display-message -p '#S')"`,
228
+ '(',
229
+ // The report is the harness's own "task over" signal. Until it lands, the
230
+ // model may still be working or waiting on an approval.
231
+ ` while [ ! -f ${shellQuote(reportPath)} ]; do sleep 2; done`,
232
+ // See reasonix's interactiveScript for why the composer is cleared before
233
+ // each quit attempt and why the exit sentinel acknowledges the quit.
234
+ ' for _ in $(seq 1 20); do',
235
+ ` [ -f ${shellQuote(exitPath)} ] && break`,
236
+ ' tmux send-keys -t "$SESSION" C-u',
237
+ ' tmux send-keys -t "$SESSION" C-d',
238
+ ' sleep 3',
239
+ ' done',
240
+ ') >/dev/null 2>&1 &',
241
+ // Codex refuses a TUI whose stdout is redirected: "Error: stdout is not a terminal".
242
+ invocation,
243
+ `echo $? > ${shellQuote(exitPath)}`,
244
+ '',
245
+ ].join('\n');
246
+ }
247
+ /** Base URL codex is configured to talk to, if it overrides the default. */
248
+ export function configuredBaseUrl(configText) {
249
+ const m = configText.match(/^\s*openai_base_url\s*=\s*"([^"]+)"/m);
250
+ return m ? m[1] : null;
251
+ }
252
+ /**
253
+ * Whether codex has been granted trust for a directory. On first entry to an
254
+ * unseen directory the TUI blocks on "Do you trust the contents of this
255
+ * directory?" before running anything, which strands a `default`-mode run at a
256
+ * prompt that has nothing to do with the task.
257
+ */
258
+ export function projectTrusted(configText, cwd) {
259
+ const section = new RegExp(`^\\s*\\[projects\\.(?:"${cwd.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}")\\]\\s*$`, 'm');
260
+ const m = configText.match(section);
261
+ if (!m)
262
+ return false;
263
+ const rest = configText.slice(m.index + m[0].length);
264
+ // Only the keys belonging to this section, i.e. up to the next section head.
265
+ const body = rest.split(/^\s*\[/m)[0];
266
+ return /^\s*trust_level\s*=\s*"trusted"/m.test(body);
267
+ }
268
+ /** Resolves once we know whether something is accepting connections. */
269
+ function portOpen(host, port, timeoutMs = 1500) {
270
+ return new Promise((resolve) => {
271
+ const socket = connect({ host, port });
272
+ const done = (ok) => { socket.destroy(); resolve(ok); };
273
+ socket.setTimeout(timeoutMs);
274
+ socket.once('connect', () => done(true));
275
+ socket.once('timeout', () => done(false));
276
+ socket.once('error', () => done(false));
277
+ });
278
+ }
279
+ async function codexHealth(env) {
280
+ const problems = [];
281
+ try {
282
+ // Hard timeout: `codex login status` can block indefinitely when the
283
+ // configured endpoint is unreachable, and doctor must never hang.
284
+ // `codex login status` reports on stderr, not stdout — checking only
285
+ // stdout produces a false "not logged in".
286
+ const { stdout, stderr } = await run('codex', ['login', 'status'], { timeout: 5_000 });
287
+ if (!/logged in/i.test(`${stdout}${stderr}`)) {
288
+ problems.push({
289
+ severity: 'error',
290
+ message: 'codex is not logged in',
291
+ fix: 'codex login',
292
+ });
293
+ }
294
+ }
295
+ catch {
296
+ problems.push({
297
+ severity: 'error',
298
+ message: 'codex login status failed or timed out — the token may need refreshing',
299
+ fix: 'codex logout && codex login',
300
+ });
301
+ }
302
+ // A custom base URL means codex depends on something local being up. A run
303
+ // against a dead proxy burns minutes retrying before failing, so check first.
304
+ const configPath = join(env.home, '.codex', 'config.toml');
305
+ const configText = existsSync(configPath) ? readFileSync(configPath, 'utf8') : '';
306
+ const baseUrl = configText ? configuredBaseUrl(configText) : null;
307
+ if (!projectTrusted(configText, env.cwd)) {
308
+ problems.push({
309
+ severity: 'warn',
310
+ message: `codex has not been granted trust for ${env.cwd}, so a default-mode run will block on its trust prompt`,
311
+ fix: `run \`codex\` once in ${env.cwd} and answer "Yes, continue"`,
312
+ });
313
+ }
314
+ if (baseUrl) {
315
+ try {
316
+ const url = new URL(baseUrl);
317
+ const port = Number(url.port || (url.protocol === 'https:' ? 443 : 80));
318
+ if (!(await portOpen(url.hostname, port))) {
319
+ problems.push({
320
+ severity: 'error',
321
+ message: `codex is configured to use ${baseUrl}, but nothing is listening there`,
322
+ fix: 'Start the proxy (e.g. `headroom proxy`), or remove openai_base_url from ~/.codex/config.toml',
323
+ });
324
+ }
325
+ }
326
+ catch {
327
+ problems.push({
328
+ severity: 'warn',
329
+ message: `codex has an unparseable openai_base_url: ${baseUrl}`,
330
+ });
331
+ }
332
+ }
333
+ return problems;
334
+ }
335
+ export const codexAdapter = {
336
+ name: 'codex',
337
+ versionCommand: ['codex', '--version'],
338
+ supportedVersions: '>=0.140.0 <1.0.0',
339
+ pathPrepend: [],
340
+ plan: buildScript,
341
+ canPromptForApproval: true,
342
+ promptPatterns: PROMPT_PATTERNS,
343
+ /**
344
+ * Returns the whole prompt block, not just the line that matched. A codex
345
+ * approval spans several lines — the question, the command, and the numbered
346
+ * options — and the matched line alone ("Would you like to run the following
347
+ * command?") omits the one thing the caller needs to decide: the command.
348
+ */
349
+ describePrompt(lines) {
350
+ for (let i = lines.length - 1; i >= 0; i--) {
351
+ if (!PROMPT_PATTERNS.some((re) => re.test(lines[i])))
352
+ continue;
353
+ // The match may land on the footer below the question, so walk back up
354
+ // over the option list to the question that introduced it.
355
+ let start = i;
356
+ while (start > 0 && start > i - MAX_PROMPT_LINES && !/^\s*would you like to |do you trust the contents/i.test(lines[start])) {
357
+ start--;
358
+ }
359
+ const block = lines
360
+ .slice(start, start + MAX_PROMPT_LINES)
361
+ .map((l) => l.trimEnd())
362
+ .filter((l) => l.length > 0);
363
+ return block.length > 0 ? block.join('\n') : lines[i].trim();
364
+ }
365
+ return null;
366
+ },
367
+ /**
368
+ * `y` is the accelerator codex prints beside "Yes, proceed"; it acts at once,
369
+ * so no Enter follows — a trailing Enter would fall through to the composer.
370
+ * Verified against a live TUI to clear the directory-trust prompt too, whose
371
+ * options are numbered and print no `y`. There is no `n` accelerator: the
372
+ * deny path is esc.
373
+ */
374
+ approveKeys: { yes: ['y'], no: ['Escape'] },
375
+ /**
376
+ * Codex writes its final message here via `-o`. Used when the model did not
377
+ * write a report of its own, which is far better than scraping the pane.
378
+ */
379
+ fallbackReportFile: 'last-message.txt',
380
+ health: codexHealth,
381
+ };
382
+ //# sourceMappingURL=codex.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex.js","sourceRoot":"","sources":["../../src/adapters/codex.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import { openCodeAdapter } from './opencode.js';
2
+ import { codexAdapter } from './codex.js';
3
+ import { piAdapter } from './pi.js';
4
+ import { reasonixAdapter } from './reasonix.js';
5
+ import { claudeAdapter } from './claude.js';
6
+ const ADAPTERS = {
7
+ opencode: openCodeAdapter,
8
+ codex: codexAdapter,
9
+ pi: piAdapter,
10
+ reasonix: reasonixAdapter,
11
+ claude: claudeAdapter,
12
+ };
13
+ export function getAdapter(name) {
14
+ const a = ADAPTERS[name];
15
+ if (!a) {
16
+ throw new Error(`sonata: unknown harness "${name}". Known: ${Object.keys(ADAPTERS).join(', ')}`);
17
+ }
18
+ return a;
19
+ }
20
+ export { openCodeAdapter, codexAdapter, piAdapter, reasonixAdapter, claudeAdapter };
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,QAAQ,GAAmC;IAC/C,QAAQ,EAAE,eAAe;IACzB,KAAK,EAAE,YAAY;IACnB,EAAE,EAAE,SAAS;IACb,QAAQ,EAAE,eAAe;IACzB,MAAM,EAAE,aAAa;CACtB,CAAC;AAEF,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,MAAM,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IACzB,IAAI,CAAC,CAAC,EAAE,CAAC;QACP,MAAM,IAAI,KAAK,CACb,4BAA4B,IAAI,aAAa,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAChF,CAAC;IACJ,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,SAAS,EAAE,eAAe,EAAE,aAAa,EAAE,CAAC"}
@@ -0,0 +1,104 @@
1
+ import { isReadOnlyRole } from '../config.js';
2
+ /**
3
+ * Empty, and that is the finding rather than an omission.
4
+ *
5
+ * `opencode run` has no approval UI. Probed against opencode 1.18 with
6
+ * `--interactive` and `permission = { bash = "ask" }`, a tool call that needs
7
+ * approval is not offered to the user at all — it is refused outright:
8
+ *
9
+ * ! permission requested: bash (rm file.txt); auto-rejecting
10
+ * ✗ rm file.txt failed
11
+ *
12
+ * See tests/fixtures/panes/opencode-auto-reject.txt. So a `run` invocation is
13
+ * never PAUSED: it either proceeds unasked or fails. The earlier patterns here
14
+ * were invented rather than observed and matched none of this.
15
+ */
16
+ const PROMPT_PATTERNS = [];
17
+ /**
18
+ * `opencode run --agent` accepts PRIMARY agents only — `build` and `plan`.
19
+ *
20
+ * `explore` exists but is a subagent, so asking for it does not fail: opencode
21
+ * substitutes the default agent, which is the write-capable `build`, and says
22
+ * so only in a pane warning nothing parses. The explore role therefore stopped
23
+ * being read-only without anything reporting it. Enabling `explore` in
24
+ * opencode.json does not help — it is still not primary.
25
+ *
26
+ * So every read-only role maps to `plan`, which is primary and genuinely
27
+ * read-only. A role sonata calls read-only must never resolve to `build`.
28
+ */
29
+ function agentFor(input) {
30
+ if (input.mode === 'plan')
31
+ return 'plan';
32
+ if (isReadOnlyRole(input.role))
33
+ return 'plan';
34
+ return 'build';
35
+ }
36
+ function buildScript(input) {
37
+ const readOnly = isReadOnlyRole(input.role);
38
+ // `default` means "ask before doing anything irreversible". OpenCode cannot
39
+ // ask — it either proceeds unprompted or auto-rejects — so honouring the mode
40
+ // is impossible. Running anyway would make a sonata subagent more permissive
41
+ // than the session that dispatched it, silently. Refuse instead, and say why.
42
+ //
43
+ // Read-only roles are exempt: there is nothing for them to ask about.
44
+ if (input.mode === 'default' && !readOnly) {
45
+ throw new Error('sonata: opencode cannot ask for approval, so it cannot honour `default` ' +
46
+ 'permission mode. Re-dispatch in acceptEdits or bypassPermissions to run ' +
47
+ 'it ungated, or use a codex model, whose TUI does prompt.');
48
+ }
49
+ const auto = !readOnly && (input.mode === 'acceptEdits' || input.mode === 'bypassPermissions');
50
+ const agent = agentFor(input);
51
+ // `--interactive` selects opencode's split-footer renderer, which streams the
52
+ // run into the pane. It does NOT make the run answerable: opencode auto-
53
+ // rejects permission requests in `run` mode either way, so the plan reports
54
+ // interactive: false and sonata never waits for an approval that cannot come.
55
+ //
56
+ // `modelId` is a full `provider/model` ref, passed through untouched. It used
57
+ // to be prefixed with a hardcoded `opencode/`, which sent every run to the
58
+ // free tier whatever the user selected — and that tier serves almost none of
59
+ // the models people configure, so the run died before the model saw the task.
60
+ const flags = ['run', `--agent ${agent}`, `-m ${input.modelId}`, '--interactive'];
61
+ if (auto)
62
+ flags.push('--auto');
63
+ // `-f` is declared as an array option, so it greedily consumes any following
64
+ // positional. The message MUST come before `-f` or opencode treats the prompt
65
+ // text as a second filename and exits with "File not found".
66
+ const script = [
67
+ '#!/bin/bash',
68
+ 'set -o pipefail',
69
+ 'export PATH="$HOME/.opencode/bin:$PATH"',
70
+ `cd '${input.cwd}' || exit 97`,
71
+ `opencode ${flags.join(' ')} 'Follow the attached instructions.' -f '${input.instructionsPath}' 2>&1 | tee -a '${input.runDir}/harness.log'`,
72
+ `echo $? > '${input.runDir}/exit'`,
73
+ '',
74
+ ].join('\n');
75
+ // A run under opencode's `plan` agent cannot produce report.md, so such a
76
+ // run is not degraded merely for lacking one; its output is the report.
77
+ //
78
+ // The enforcement is policy, not tool removal: the plan agent is instructed
79
+ // that any modification is a violation superseding all other instructions,
80
+ // and it declines. Probed directly — a run asked only to write one file
81
+ // wrote nothing and reported "blocked by policy, not by error" — and
82
+ // observed on two unrelated review runs that could not write their reports.
83
+ // Weaker than pi, whose allowlist removes the write tool outright, but the
84
+ // outcome for sonata is the same: no report arrives.
85
+ //
86
+ // Keyed off the agent actually chosen rather than the role, because the two
87
+ // can differ: `plan` mode sends a write-capable role to the plan agent too.
88
+ return { script, interactive: false, canWriteReport: agent !== 'plan' };
89
+ }
90
+ export const openCodeAdapter = {
91
+ name: 'opencode',
92
+ versionCommand: ['opencode', '--version'],
93
+ supportedVersions: '>=1.18.0 <2.0.0',
94
+ pathPrepend: ['$HOME/.opencode/bin'],
95
+ plan: buildScript,
96
+ canPromptForApproval: false,
97
+ promptPatterns: PROMPT_PATTERNS,
98
+ describePrompt() {
99
+ return null;
100
+ },
101
+ /** Nothing to send: `opencode run` never waits for an answer. */
102
+ approveKeys: { yes: [], no: [] },
103
+ };
104
+ //# sourceMappingURL=opencode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opencode.js","sourceRoot":"","sources":["../../src/adapters/opencode.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C;;;;;;;;;;;;;GAaG;AACH,MAAM,eAAe,GAAa,EAAE,CAAC;AAErC;;;;;;;;;;;GAWG;AACH,SAAS,QAAQ,CAAC,KAAgB;IAChC,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;QAAE,OAAO,MAAM,CAAC;IACzC,IAAI,cAAc,CAAC,KAAK,CAAC,IAAI,CAAC;QAAE,OAAO,MAAM,CAAC;IAC9C,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,WAAW,CAAC,KAAgB;IACnC,MAAM,QAAQ,GAAG,cAAc,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAE5C,4EAA4E;IAC5E,8EAA8E;IAC9E,6EAA6E;IAC7E,8EAA8E;IAC9E,EAAE;IACF,sEAAsE;IACtE,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CACb,0EAA0E;YAC1E,0EAA0E;YAC1E,0DAA0D,CAC3D,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,QAAQ,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,aAAa,IAAI,KAAK,CAAC,IAAI,KAAK,mBAAmB,CAAC,CAAC;IAC/F,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAE9B,8EAA8E;IAC9E,yEAAyE;IACzE,4EAA4E;IAC5E,8EAA8E;IAC9E,EAAE;IACF,8EAA8E;IAC9E,2EAA2E;IAC3E,6EAA6E;IAC7E,8EAA8E;IAC9E,MAAM,KAAK,GAAG,CAAC,KAAK,EAAE,WAAW,KAAK,EAAE,EAAE,MAAM,KAAK,CAAC,OAAO,EAAE,EAAE,eAAe,CAAC,CAAC;IAClF,IAAI,IAAI;QAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAE/B,6EAA6E;IAC7E,8EAA8E;IAC9E,6DAA6D;IAC7D,MAAM,MAAM,GAAG;QACb,aAAa;QACb,iBAAiB;QACjB,yCAAyC;QACzC,OAAO,KAAK,CAAC,GAAG,cAAc;QAC9B,YAAY,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,4CAA4C,KAAK,CAAC,gBAAgB,oBAAoB,KAAK,CAAC,MAAM,eAAe;QAC5I,cAAc,KAAK,CAAC,MAAM,QAAQ;QAClC,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEb,0EAA0E;IAC1E,wEAAwE;IACxE,EAAE;IACF,4EAA4E;IAC5E,2EAA2E;IAC3E,wEAAwE;IACxE,qEAAqE;IACrE,4EAA4E;IAC5E,2EAA2E;IAC3E,qDAAqD;IACrD,EAAE;IACF,4EAA4E;IAC5E,4EAA4E;IAC5E,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,KAAK,MAAM,EAAE,CAAC;AAC1E,CAAC;AAED,MAAM,CAAC,MAAM,eAAe,GAAmB;IAC7C,IAAI,EAAE,UAAU;IAChB,cAAc,EAAE,CAAC,UAAU,EAAE,WAAW,CAAC;IACzC,iBAAiB,EAAE,iBAAiB;IACpC,WAAW,EAAE,CAAC,qBAAqB,CAAC;IACpC,IAAI,EAAE,WAAW;IACjB,oBAAoB,EAAE,KAAK;IAC3B,cAAc,EAAE,eAAe;IAC/B,cAAc;QACZ,OAAO,IAAI,CAAC;IACd,CAAC;IACD,iEAAiE;IACjE,WAAW,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;CACjC,CAAC"}