@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,85 @@
1
+ /**
2
+ * `sonata usage` — what the native path actually consumed.
3
+ *
4
+ * Two honesty constraints shape this file. Unpriced volume is reported beside
5
+ * the total and never folded into it: a total that treats "unknown" as zero is
6
+ * worse than no total. And this measures the *native* path only — a `sonata
7
+ * dispatch` run executes in the foreign CLI's own process and never transits
8
+ * the router, so its tokens are unobservable and the output says so rather than
9
+ * presenting a partial figure as complete.
10
+ */
11
+ import { loadAiPricing } from '../aipricing.js';
12
+ import { readRows } from '../ledger.js';
13
+ import { loadSessions } from '../sessions.js';
14
+ const UNITS = { m: 60_000, h: 3_600_000, d: 86_400_000 };
15
+ export function parseDuration(text) {
16
+ const match = /^(\d+)([mhd])$/.exec(text.trim());
17
+ if (match === null)
18
+ throw new Error(`sonata usage: invalid duration "${text}" — use 30m, 12h or 7d`);
19
+ const duration = Number(match[1]) * UNITS[match[2]];
20
+ // A long enough digit run parses as a Number but overflows the multiplication
21
+ // to Infinity, which would silently select the entire ledger as the cutoff.
22
+ if (!Number.isFinite(duration))
23
+ throw new Error(`sonata usage: invalid duration "${text}" — use 30m, 12h or 7d`);
24
+ return duration;
25
+ }
26
+ function labelOf(row, by, sessions) {
27
+ switch (by) {
28
+ // An anthropic row has no sonata key; its alias is the model, which is
29
+ // exactly the baseline the comparison needs on the same axis.
30
+ // `??` alone let an empty-string alias through, and a router request that
31
+ // never resolved a model writes exactly that — producing a nameless row in
32
+ // a cost report, which is the one thing a cost report may not have.
33
+ case 'model': return row.key || row.alias || '(unresolved)';
34
+ case 'role': return row.role ?? '—';
35
+ case 'tier': return row.tier ?? '—';
36
+ case 'gateway': return row.gateway ?? row.upstream;
37
+ case 'session': return row.session ?? 'unknown';
38
+ case 'project': return row.session === undefined ? 'unknown' : (sessions[row.session]?.cwd ?? 'unknown');
39
+ }
40
+ }
41
+ export function aggregate(rows, by, sessions) {
42
+ const buckets = new Map();
43
+ const unpriced = { requests: 0, input: 0, output: 0 };
44
+ let pricedTotalUsd = 0;
45
+ for (const row of rows) {
46
+ const label = labelOf(row, by, sessions);
47
+ const bucket = buckets.get(label) ?? { label, requests: 0, input: 0, output: 0, costUsd: 0, unpricedRequests: 0 };
48
+ bucket.requests += 1;
49
+ bucket.input += row.tokens.input;
50
+ bucket.output += row.tokens.output;
51
+ // `totalUsd` of 0 is a real price (a free tier). Only `source: 'none'`
52
+ // means unknown, and an unknown must never sum as zero.
53
+ if (row.price.source === 'none' || row.price.totalUsd === undefined) {
54
+ bucket.unpricedRequests += 1;
55
+ unpriced.requests += 1;
56
+ unpriced.input += row.tokens.input;
57
+ unpriced.output += row.tokens.output;
58
+ }
59
+ else {
60
+ bucket.costUsd += row.price.totalUsd;
61
+ pricedTotalUsd += row.price.totalUsd;
62
+ }
63
+ buckets.set(label, bucket);
64
+ }
65
+ return {
66
+ buckets: [...buckets.values()].sort((a, b) => b.costUsd - a.costUsd || b.requests - a.requests),
67
+ pricedTotalUsd,
68
+ unpriced,
69
+ };
70
+ }
71
+ export async function cmdUsage(opts) {
72
+ const now = Date.now();
73
+ let rows = readRows(opts.home, now - parseDuration(opts.since), now);
74
+ if (opts.session !== undefined)
75
+ rows = rows.filter((row) => row.session === opts.session);
76
+ const report = aggregate(rows, opts.by, loadSessions(opts.home));
77
+ const cache = loadAiPricing(opts.home);
78
+ if (cache !== undefined) {
79
+ const fetched = Date.parse(cache.fetchedAt);
80
+ if (Number.isFinite(fetched))
81
+ report.priceCacheAgeMs = now - fetched;
82
+ }
83
+ return report;
84
+ }
85
+ //# sourceMappingURL=usage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usage.js","sourceRoot":"","sources":["../../src/commands/usage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { runDir } from '../store.js';
4
+ /**
5
+ * Confirms a run actually happened, and on which model.
6
+ *
7
+ * The second layer under the wrapper's tool restriction: a fabricated report
8
+ * carries no id that survives this.
9
+ */
10
+ export function cmdVerify(opts) {
11
+ // Shares the store's definition rather than rebuilding the path: two copies
12
+ // could disagree about where runs live, and this is the check that decides
13
+ // whether a run happened at all.
14
+ const dir = runDir(opts.cwd, opts.id);
15
+ const metaPath = join(dir, 'meta.json');
16
+ if (!existsSync(metaPath)) {
17
+ return { ok: false, detail: `no run "${opts.id}" — looked in ${dir}` };
18
+ }
19
+ let meta;
20
+ try {
21
+ meta = JSON.parse(readFileSync(metaPath, 'utf8'));
22
+ }
23
+ catch {
24
+ return { ok: false, detail: `run "${opts.id}" has unreadable meta.json` };
25
+ }
26
+ // A meta that is not an object reads every field as undefined, which looked
27
+ // exactly like a successful verification of a run that never happened —
28
+ // defeating the only check that a report corresponds to real work.
29
+ if (typeof meta !== 'object' || meta === null || Array.isArray(meta)) {
30
+ return { ok: false, detail: `run "${opts.id}" has unusable meta.json` };
31
+ }
32
+ if (typeof meta.model !== 'string') {
33
+ return { ok: false, detail: `run "${opts.id}" names no model in meta.json` };
34
+ }
35
+ if (opts.model !== undefined && meta.model !== opts.model) {
36
+ return {
37
+ ok: false,
38
+ detail: `run "${opts.id}" ran ${meta.model}, not ${opts.model}`,
39
+ };
40
+ }
41
+ // `meta.exitCode` is written by `sonata tail`, so a run whose tail never
42
+ // completed looks live forever — exactly the case verify exists for. The
43
+ // exit sentinel is written by the launched shell itself and is the truth
44
+ // about whether the process finished. Observed on 2026-08-12: a run that
45
+ // exited 0 and wrote a full report reported "still running", because the
46
+ // wrapper's `tail` was denied by the permission classifier mid-run.
47
+ const exit = meta.exitCode !== undefined
48
+ ? `exit ${meta.exitCode}`
49
+ : sentinelExit(dir) ?? 'still running';
50
+ const unreconciled = meta.exitCode === undefined && sentinelExit(dir) !== null
51
+ ? ' · finished but never read back by `sonata tail`'
52
+ : '';
53
+ return {
54
+ ok: true,
55
+ detail: `${opts.id}: ${meta.role} on ${meta.model} via ${meta.harness} · ${exit}` +
56
+ (meta.degraded ? ' · degraded' : '') + unreconciled,
57
+ };
58
+ }
59
+ /** The exit sentinel the launched shell writes, independent of `tail`. */
60
+ function sentinelExit(dir) {
61
+ try {
62
+ const raw = readFileSync(join(dir, 'exit'), 'utf8').trim();
63
+ return /^-?\d+$/.test(raw) ? `exit ${raw}` : null;
64
+ }
65
+ catch {
66
+ return null;
67
+ }
68
+ }
69
+ //# sourceMappingURL=verify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.js","sourceRoot":"","sources":["../../src/commands/verify.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import { loadConfig } from '../config.js';
2
+ import { cmdTail } from './tail.js';
3
+ /**
4
+ * Blocks until a run reaches a state worth returning to the caller.
5
+ *
6
+ * This exists so a dispatch costs one tool call instead of one per model turn.
7
+ * Every intermediate PROGRESS result used to be returned to the wrapper agent,
8
+ * which re-sent its whole context each time — so the cost grew with the length
9
+ * of the run rather than the size of its result. Nothing read those lines.
10
+ *
11
+ * The loop reuses cmdTail rather than growing a second state machine, so pane
12
+ * diffing, events.jsonl and the cursor keep advancing exactly as before and
13
+ * `sonata log` is unaffected.
14
+ */
15
+ export async function cmdWait(opts) {
16
+ const now = opts.now ?? (() => Date.now());
17
+ const tail = opts.tail ?? cmdTail;
18
+ const pollMs = opts.pollMs ?? 500;
19
+ const windowSeconds = opts.windowSeconds
20
+ ?? loadConfig(opts.cwd).run.dispatchWindowSeconds;
21
+ const deadline = now() + windowSeconds * 1000;
22
+ for (;;) {
23
+ const result = await tail({
24
+ cwd: opts.cwd,
25
+ id: opts.id,
26
+ // One tail call must not outlast the window it is spending.
27
+ waitSeconds: Math.max(0, Math.ceil((deadline - now()) / 1000)),
28
+ onLines: opts.onLines,
29
+ pollMs,
30
+ });
31
+ if (result.state !== 'PROGRESS')
32
+ return { ...result, id: opts.id };
33
+ if (now() >= deadline)
34
+ return { ...result, state: 'RUNNING', id: opts.id };
35
+ await new Promise((r) => setTimeout(r, pollMs));
36
+ }
37
+ }
38
+ //# sourceMappingURL=wait.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wait.js","sourceRoot":"","sources":["../../src/commands/wait.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA2BpC;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,IAAiB;IAC7C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,OAAO,CAAC;IAClC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,GAAG,CAAC;IAClC,MAAM,aAAa,GAAG,IAAI,CAAC,aAAa;WACnC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,qBAAqB,CAAC;IACpD,MAAM,QAAQ,GAAG,GAAG,EAAE,GAAG,aAAa,GAAG,IAAI,CAAC;IAE9C,SAAS,CAAC;QACR,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC;YACxB,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,4DAA4D;YAC5D,WAAW,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,GAAG,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;YAC9D,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,MAAM;SACP,CAAC,CAAC;QACH,IAAI,MAAM,CAAC,KAAK,KAAK,UAAU;YAAE,OAAO,EAAE,GAAG,MAAM,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC;QACnE,IAAI,GAAG,EAAE,IAAI,QAAQ;YAAE,OAAO,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC;QAC3E,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;IAClD,CAAC;AACH,CAAC"}