@sapiom/harness 0.5.1 → 0.7.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 (98) hide show
  1. package/CHANGELOG.md +152 -0
  2. package/dist/cli/doctor.d.ts.map +1 -1
  3. package/dist/cli/doctor.js +4 -2
  4. package/dist/cli/doctor.js.map +1 -1
  5. package/dist/core/account-plan.d.ts +51 -0
  6. package/dist/core/account-plan.d.ts.map +1 -0
  7. package/dist/core/account-plan.js +174 -0
  8. package/dist/core/account-plan.js.map +1 -0
  9. package/dist/core/adapters/claude-code.d.ts +36 -0
  10. package/dist/core/adapters/claude-code.d.ts.map +1 -1
  11. package/dist/core/adapters/claude-code.js +48 -1
  12. package/dist/core/adapters/claude-code.js.map +1 -1
  13. package/dist/core/adapters/codex.d.ts +6 -0
  14. package/dist/core/adapters/codex.d.ts.map +1 -1
  15. package/dist/core/adapters/codex.js +19 -13
  16. package/dist/core/adapters/codex.js.map +1 -1
  17. package/dist/core/bracketed-paste.d.ts +16 -0
  18. package/dist/core/bracketed-paste.d.ts.map +1 -1
  19. package/dist/core/bracketed-paste.js +17 -3
  20. package/dist/core/bracketed-paste.js.map +1 -1
  21. package/dist/core/canvas-manifest-check.d.ts.map +1 -1
  22. package/dist/core/canvas-manifest-check.js +36 -5
  23. package/dist/core/canvas-manifest-check.js.map +1 -1
  24. package/dist/core/canvas-svg.d.ts.map +1 -1
  25. package/dist/core/canvas-svg.js +9 -1
  26. package/dist/core/canvas-svg.js.map +1 -1
  27. package/dist/core/canvas-watcher.d.ts +25 -0
  28. package/dist/core/canvas-watcher.d.ts.map +1 -1
  29. package/dist/core/canvas-watcher.js +31 -5
  30. package/dist/core/canvas-watcher.js.map +1 -1
  31. package/dist/core/errors.d.ts +12 -0
  32. package/dist/core/errors.d.ts.map +1 -1
  33. package/dist/core/errors.js +14 -0
  34. package/dist/core/errors.js.map +1 -1
  35. package/dist/core/example-seed.js +1 -1
  36. package/dist/core/example-seed.js.map +1 -1
  37. package/dist/core/inject/claude-settings.d.ts.map +1 -1
  38. package/dist/core/inject/claude-settings.js +69 -7
  39. package/dist/core/inject/claude-settings.js.map +1 -1
  40. package/dist/core/inject/mcp-config.d.ts +21 -0
  41. package/dist/core/inject/mcp-config.d.ts.map +1 -1
  42. package/dist/core/inject/mcp-config.js +23 -13
  43. package/dist/core/inject/mcp-config.js.map +1 -1
  44. package/dist/core/inject/skills-plugin.d.ts.map +1 -1
  45. package/dist/core/inject/skills-plugin.js +41 -25
  46. package/dist/core/inject/skills-plugin.js.map +1 -1
  47. package/dist/core/session-manager.d.ts +29 -0
  48. package/dist/core/session-manager.d.ts.map +1 -1
  49. package/dist/core/session-manager.js +95 -1
  50. package/dist/core/session-manager.js.map +1 -1
  51. package/dist/core/spawn-target.d.ts +16 -0
  52. package/dist/core/spawn-target.d.ts.map +1 -1
  53. package/dist/core/spawn-target.js +87 -19
  54. package/dist/core/spawn-target.js.map +1 -1
  55. package/dist/core/strip-ansi.js +49 -11
  56. package/dist/core/strip-ansi.js.map +1 -1
  57. package/dist/core/task-manager.js +1 -1
  58. package/dist/core/task-manager.js.map +1 -1
  59. package/dist/index.d.ts +1 -1
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +1 -1
  62. package/dist/index.js.map +1 -1
  63. package/dist/server/account.d.ts +23 -0
  64. package/dist/server/account.d.ts.map +1 -0
  65. package/dist/server/account.js +32 -0
  66. package/dist/server/account.js.map +1 -0
  67. package/dist/server/actions.d.ts.map +1 -1
  68. package/dist/server/actions.js +1 -0
  69. package/dist/server/actions.js.map +1 -1
  70. package/dist/server/canvas-render.d.ts +9 -1
  71. package/dist/server/canvas-render.d.ts.map +1 -1
  72. package/dist/server/canvas-render.js +1 -0
  73. package/dist/server/canvas-render.js.map +1 -1
  74. package/dist/server/cwd-normalize.d.ts +18 -0
  75. package/dist/server/cwd-normalize.d.ts.map +1 -0
  76. package/dist/server/cwd-normalize.js +23 -0
  77. package/dist/server/cwd-normalize.js.map +1 -0
  78. package/dist/server/error-handler.d.ts +13 -0
  79. package/dist/server/error-handler.d.ts.map +1 -0
  80. package/dist/server/error-handler.js +7 -0
  81. package/dist/server/error-handler.js.map +1 -0
  82. package/dist/server/index.d.ts +8 -0
  83. package/dist/server/index.d.ts.map +1 -1
  84. package/dist/server/index.js +20 -6
  85. package/dist/server/index.js.map +1 -1
  86. package/dist/server/rest.d.ts.map +1 -1
  87. package/dist/server/rest.js +32 -10
  88. package/dist/server/rest.js.map +1 -1
  89. package/dist/shared/types.d.ts +81 -2
  90. package/dist/shared/types.d.ts.map +1 -1
  91. package/dist/shared/types.js +10 -0
  92. package/dist/shared/types.js.map +1 -1
  93. package/dist/web/assets/index-B6DkXea8.js +473 -0
  94. package/dist/web/assets/index-C7wzgAWT.css +32 -0
  95. package/dist/web/index.html +2 -2
  96. package/package.json +4 -4
  97. package/dist/web/assets/index-BuH6LNdL.js +0 -463
  98. package/dist/web/assets/index-Dmj2SA0s.css +0 -32
package/CHANGELOG.md CHANGED
@@ -1,5 +1,157 @@
1
1
  # @sapiom/harness
2
2
 
3
+ ## 0.7.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 3cbe957: Command palette: a proper fuzzy finder, redesigned as Command Search
8
+
9
+ The Cmd+K palette is rebuilt around the reported search failures ("cannot find
10
+ this simple agent"): short queries no longer "match" characters scattered
11
+ across absolute paths, duplicate past sessions no longer flood the list, and
12
+ what you search is what the rail shows.
13
+
14
+ - **Boundary-gated matcher** (`web/src/lib/fuzzy.ts`): every matched character
15
+ must be contiguous with the previous one or sit at a word/segment boundary,
16
+ with a substring fallback for mid-word fragments. Scattered-noise matches are
17
+ rejected outright, not merely down-ranked. Multi-term queries AND across
18
+ whitespace.
19
+ - **Ranking model** (`web/src/lib/palette.ts`, new): rows carry display names
20
+ (`displayAgentName`, `sessionDisplayName` — user renames are searchable);
21
+ same-title same-folder past sessions collapse to the newest; sections order
22
+ by their best hit with per-section caps (lifted when only one kind matches);
23
+ recency is a bounded score bonus that never lifts a path-only match over a
24
+ name match.
25
+ - **Command Search surface** (per the design-eng widget): a search bar with a
26
+ clear button, filter tabs — All / Sessions / Agents / Templates / Docs /
27
+ Files / Actions — cycled with Tab/Shift+Tab, and a shortcut-bar footer. App
28
+ verbs (browse templates, toggle theme, panel toggles, new session here) are
29
+ injected actions; the Templates tab lists the catalog and opens the gallery
30
+ focused on a template; the Docs tab searches a short list of docs pages with
31
+ the docs site as the footer destination.
32
+ - The active session is badged "current", demoted from the unqueried top spot,
33
+ and never the default selection; matched characters render on an
34
+ accent-tinted cap that stays legible on selected rows in both themes.
35
+
36
+ - f21f6a6: Windows: sessions create and deliver their prompt, the canvas refreshes, and nothing pops a console window
37
+
38
+ The desktop app was unusable on Windows — every `POST /api/sessions` answered
39
+ `500 {"error":"internal error"}`, and when a session did start, its first
40
+ prompt never reached the agent. Root-caused on a real machine and fixed
41
+ end to end.
42
+
43
+ - **Sessions.** Claude Code's own native auto-updater had renamed the running
44
+ `claude.exe` to `claude.exe.old.<ts>` inside the app-managed npm prefix and
45
+ never written the replacement, so every spawn failed while `doctor` (which
46
+ shells `where`) still reported the agent present. Boot now verifies the
47
+ agent actually spawns, repairs the managed install when it doesn't, and sets
48
+ `DISABLE_AUTOUPDATER` for installs the app owns. The refusal itself names
49
+ the situation instead of "target could not be determined".
50
+ - **The first prompt.** It is held until the session reports ready, which only
51
+ happens when the generated `SessionStart` hook POSTs back — and Claude Code
52
+ runs hooks through Git Bash on Windows, which cannot resolve a `.cmd`, so
53
+ the desktop's `node.cmd`-only shims meant the hook never ran. The host now
54
+ ships npm's extensionless sh shim too, a 20s hook-timeout fallback rescues a
55
+ session whose hook chain is broken (gated on Claude's blocking-prompt
56
+ screens so it can never answer a trust dialog), `emit.cjs` gets budgets a
57
+ cold loopback survives (SessionStart only — the other hooks block the
58
+ agent), and multi-line prompts are paste-wrapped under ConPTY, which hides
59
+ the bracketed-paste announcement.
60
+ - **Console windows.** The `sapiom-dev` MCP server was launched via `npx`,
61
+ whose `cmd.exe` sat on screen as a persistent blank window; closing it
62
+ killed the server and every later tool call hung. The app now installs
63
+ `@sapiom/mcp` into its own prefix and launches it through the app binary
64
+ (GUI subsystem — no console can exist), and every `child_process` call
65
+ across the harness, agent-core, the MCP and the desktop passes
66
+ `windowsHide`.
67
+ - **Canvas.** `fs.watch` reports native separators, so the watcher's
68
+ POSIX-literal comparison never matched on Windows and `canvas.reload` was
69
+ never published — every canvas hot-reload was silently dead there (the
70
+ "Preparing your agent" placeholder outliving a finished install was the
71
+ visible symptom).
72
+ - **Diagnosis.** 500s now carry the real message (and errno) instead of
73
+ "internal error", the desktop tees its main-process log to
74
+ `<userData>/logs/main.log`, and spawn failures map to actionable 4xx.
75
+ - **Also:** Git is provisioned from git-for-windows' checksum-pinned MinGit
76
+ when a Windows machine has none (template clone and deploy shell out to a
77
+ real `git`); client-supplied `cwd` is normalized server-side and the SPA's
78
+ path helpers understand both separators; gateway requests time out instead
79
+ of hanging an MCP tool call for minutes; and the updater falls back to
80
+ HTTP/1.1, names a GitHub 429 for what it is, and bounds every path that can
81
+ reach GitHub.
82
+
83
+ ### Patch Changes
84
+
85
+ - 4edcbf5: Update the bottom toast to the design-system widget spec: the tone now lives in a leading icon — a green circled check for success, a neutral ⓘ for information, a red ⚠ for errors — instead of a red edge stripe on every toast (which error-coded even "Path copied."). The card itself is the shared floating surface every popover uses. Toasts default to the error tone; positive results (copy confirmations, deploy success, describe finished, agents found, up-to-date/downloaded update checks) opt into success, and neutral status (deploy progress, editor hand-offs, empty scans) opts into info. Also: spring entrance and a short exit animation, the auto-dismiss timer pauses while hovered or focused, and the dismiss control is the shared 22px icon button.
86
+ - Updated dependencies [f21f6a6]
87
+ - @sapiom/agent-core@0.10.7
88
+ - @sapiom/mcp@0.12.5
89
+
90
+ ## 0.6.0
91
+
92
+ ### Minor Changes
93
+
94
+ - 928a639: Studio: a new agent's workspace folder now appears in the rail the instant you
95
+ start creating it — from the composer or a template — as an optimistic
96
+ "Creating agent…" row, pinned to the top and focusable. It stays put through
97
+ session-landing and the clone, and across the brief window after the session
98
+ binds but before its `sapiom.json` is registered, so switching sessions
99
+ mid-creation can never strand the in-progress agent.
100
+
101
+ Also fixes two rail/canvas issues:
102
+
103
+ - **Canvas no longer clips the terminal step.** A revise loop (a step pointing
104
+ back to an earlier one) left a gap in the layer numbering; the SVG height was
105
+ derived from the layer _count_ while nodes were positioned by their raw layer
106
+ _index_, so the deepest node (a terminal like `deliver`) fell below the
107
+ viewBox and was drawn off-screen with a dangling edge. Rows are now compacted
108
+ to consecutive positions, which also removes the empty band the gap produced.
109
+ - **Cloned agents show a short name in the rail.** The row now reads
110
+ `newsletter-autopilot` rather than the full package name
111
+ `@sapiom/example-newsletter-autopilot` (npm scope and a leading `example-`
112
+ stripped for display only — the full name is on hover, and the raw name still
113
+ keys testids and lookups).
114
+
115
+ - 5c0c646: Rail footer: live plan & balance card, and an "Update now" card for a downloaded desktop update
116
+
117
+ The rail's footer gains two shaded cards above the account row.
118
+
119
+ - **Plan & balance card** (both hosts): the harness server relays core reads at
120
+ `GET /api/account/plan` — the API key never reaches the page — showing the
121
+ org's plan name and one honest money line: daily spend against the org's
122
+ spend-limit rule (the same "$used / $cap" pair the dashboard renders),
123
+ falling back to the prepaid available balance, else nothing. An Upgrade pill
124
+ and a ⋮ menu deep-link to billing/usage on the dashboard (checkout is
125
+ dashboard-session-only). Signed-out or unreachable hides the card — it never
126
+ invents a number.
127
+ - **"Update now" card** (desktop only): when an update has finished
128
+ downloading, the main process pushes state over a new receive-only
129
+ `onUpdateState` bridge member (re-pushed on page load, buffered in the
130
+ preload so a reload can't drop it) and the card appears with the target
131
+ version. Clicking it goes through the existing `checkForUpdates()` — the
132
+ pending branch re-raises the update window — so there is still no
133
+ page-reachable install channel. It outlives "Later"; "Skip this version"
134
+ suppresses and retracts it (and now also disarms auto-install-on-quit for a
135
+ skipped build that was already staged).
136
+
137
+ ### Patch Changes
138
+
139
+ - 651c407: Canvas extraction failures now name the project directory they bundled. The Canvas is the only `check()` caller whose directory the user never typed — it comes from the bound workflow row — so a report of "check, run_local and deploy succeed but the Canvas fails on the same project" was unfalsifiable from the panel alone; esbuild's own paths are printed relative to the invoking package, which reads like the bundler resolving `node_modules` from the wrong root.
140
+ - 7bef8b2: Stop the empty-board auto-collapse from closing a canvas pane the user just opened. The collapse fires once per (session, binding), which was meant to make a redundant "still empty" probe harmless — but the pane's expand control is most often used right after starting a session on an agent, i.e. _before_ `activeSessionId` exists, so the probe that follows arrives under a different key and slams the pane shut a beat after the click (measured: 3–5 of 12 runs of the action-bar e2e, on `main`). A manual expand now claims the session, and a claim made while none is active adopts the one that reports next; switching sessions still re-arms the collapse.
141
+ - 95241fb: Give the rail toggle a resting surface while the app window is blurred: macOS hides the traffic lights it sits beside, so the bare glyph read as a gap on the left of the header.
142
+ - 21bb3f0: Let "Open in editor" target the editor you actually use.
143
+
144
+ The session menu hardcoded `vscode://file…`, so on a machine with Cursor (or
145
+ Windsurf, Zed, VS Code Insiders) and no VS Code the item silently did nothing —
146
+ the OS resolves the scheme and never reports back, so an unhandled scheme is
147
+ indistinguishable from a working one. Settings now carries an editor picker
148
+ (`HarnessSettings.editor`, `PATCH /api/settings`), the menu item names the
149
+ chosen editor ("Open in Cursor"), and a toast says where the folder was sent.
150
+ Windows paths are normalized to the `/C:/…` shape the handlers expect.
151
+
152
+ - Updated dependencies [651c407]
153
+ - @sapiom/agent-core@0.10.6
154
+
3
155
  ## 0.5.1
4
156
 
5
157
  ### Patch Changes
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAyDnE;;8CAE8C;AAC9C,eAAO,MAAM,sBAAsB,uCAAuC,CAAC;AAC3E,eAAO,MAAM,qBAAqB,2BAA2B,CAAC;AAE9D;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,EAAE,EAAE,OAAO,CAAC;IACZ;8DAC0D;IAC1D,kBAAkB,EAAE,WAAW,EAAE,CAAC;CACnC;AAED,wBAAsB,SAAS,IAAI,OAAO,CAAC,YAAY,CAAC,CA4BvD;AAED;;;;yBAIyB;AACzB,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,WAAW,CAEpE;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAM5D"}
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AA2DnE;;8CAE8C;AAC9C,eAAO,MAAM,sBAAsB,uCAAuC,CAAC;AAC3E,eAAO,MAAM,qBAAqB,2BAA2B,CAAC;AAE9D;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,EAAE,EAAE,OAAO,CAAC;IACZ;8DAC0D;IAC1D,kBAAkB,EAAE,WAAW,EAAE,CAAC;CACnC;AAED,wBAAsB,SAAS,IAAI,OAAO,CAAC,YAAY,CAAC,CA4BvD;AAED;;;;yBAIyB;AACzB,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,WAAW,CAEpE;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAM5D"}
@@ -6,7 +6,9 @@ async function which(bin) {
6
6
  try {
7
7
  const { stdout } = await execFileAsync(process.platform === "win32" ? "where" : "which", [
8
8
  bin,
9
- ]);
9
+ // windowsHide: a console child of the console-less desktop host would
10
+ // otherwise open a visible window (no-op on POSIX and in a real terminal).
11
+ ], { windowsHide: true });
10
12
  return stdout.trim().split("\n")[0] ?? null;
11
13
  }
12
14
  catch {
@@ -15,7 +17,7 @@ async function which(bin) {
15
17
  }
16
18
  async function version(bin, args = ["--version"]) {
17
19
  try {
18
- const { stdout } = await execFileAsync(bin, args);
20
+ const { stdout } = await execFileAsync(bin, args, { windowsHide: true });
19
21
  return stdout.trim().split("\n")[0] ?? null;
20
22
  }
21
23
  catch {
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * account-plan — the rail's plan card, read from the Sapiom CORE surface so it
3
+ * can never disagree with the dashboard's own billing views.
4
+ *
5
+ * Three reads, folded into one `AccountPlanView`:
6
+ *
7
+ * - `GET /v1/dashboard/plan` → plan name + status
8
+ * - `GET /v1/spending-rules/spend-limit-summary` → today's settled spend vs
9
+ * the org's spend-limit rule — the exact "$used / $cap" pair the dashboard's
10
+ * balance card renders (JSON:API envelope; all-null attributes when no rule)
11
+ * - `GET /v1/accounts/spend` → `availableBalanceUsd`,
12
+ * the prepaid fallback when no limit rule exists
13
+ *
14
+ * Same auth contract as template-catalog.ts (and verified there): core takes
15
+ * `Authorization: Bearer`, NOT the agents surface's `x-sapiom-api-key`, and
16
+ * every path is prefixed `/v1`. The key stays server-side; a 401/403 triggers
17
+ * exactly one credential refresh + retry.
18
+ *
19
+ * These endpoints are excluded from core's public OpenAPI surface, so their
20
+ * shapes are contracts by observation, not by document. Every field is
21
+ * narrowed defensively and any drift degrades the readout to `none` (or the
22
+ * whole view to `fallback`) rather than throwing — a broken billing read must
23
+ * never take the rail down.
24
+ */
25
+ import type { AccountPlanView } from "../shared/types.js";
26
+ import { type ApiKeyProvider } from "./api-key-provider.js";
27
+ export interface AccountPlanReader {
28
+ /** The assembled card view. Never throws: signed-out and unreachable both
29
+ * come back as `source: "fallback"` with an honest empty readout. */
30
+ view(): Promise<AccountPlanView>;
31
+ }
32
+ /** `{ data: { plan: { name, status } | null } }` → the card's plan line. */
33
+ export declare function extractPlan(body: unknown): AccountPlanView["plan"];
34
+ /** JSON:API `{ data: { attributes: { limitUsd, windowSpendUsd } } }` → the
35
+ * "$used / $cap" pair, or null when no qualifying rule exists (all-null
36
+ * attributes is that endpoint's documented "no rule" answer, not an error). */
37
+ export declare function extractLimit(body: unknown): {
38
+ usedUsd: number;
39
+ limitUsd: number;
40
+ } | null;
41
+ /** `{ data: { availableBalanceUsd } }` → the prepaid balance, or null. */
42
+ export declare function extractBalance(body: unknown): number | null;
43
+ export declare function createAccountPlanReader(opts: {
44
+ /** Accepts a provider (preferred — enables refresh-on-401) or a bare key. */
45
+ apiKey: string | null | ApiKeyProvider;
46
+ /** Override the core base URL (resolved from env by default). Test seam. */
47
+ baseUrl?: string;
48
+ /** Injectable fetch. Test seam. */
49
+ fetchImpl?: typeof fetch;
50
+ }): AccountPlanReader;
51
+ //# sourceMappingURL=account-plan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account-plan.d.ts","sourceRoot":"","sources":["../../src/core/account-plan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EACL,KAAK,cAAc,EAEpB,MAAM,uBAAuB,CAAC;AAa/B,MAAM,WAAW,iBAAiB;IAChC;0EACsE;IACtE,IAAI,IAAI,OAAO,CAAC,eAAe,CAAC,CAAC;CAClC;AAsBD,4EAA4E;AAC5E,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,GAAG,eAAe,CAAC,MAAM,CAAC,CAIlE;AAED;;gFAEgF;AAChF,wBAAgB,YAAY,CAC1B,IAAI,EAAE,OAAO,GACZ;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAO9C;AAED,0EAA0E;AAC1E,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAE3D;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE;IAC5C,6EAA6E;IAC7E,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,cAAc,CAAC;IACvC,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mCAAmC;IACnC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B,GAAG,iBAAiB,CA0GpB"}
@@ -0,0 +1,174 @@
1
+ /**
2
+ * account-plan — the rail's plan card, read from the Sapiom CORE surface so it
3
+ * can never disagree with the dashboard's own billing views.
4
+ *
5
+ * Three reads, folded into one `AccountPlanView`:
6
+ *
7
+ * - `GET /v1/dashboard/plan` → plan name + status
8
+ * - `GET /v1/spending-rules/spend-limit-summary` → today's settled spend vs
9
+ * the org's spend-limit rule — the exact "$used / $cap" pair the dashboard's
10
+ * balance card renders (JSON:API envelope; all-null attributes when no rule)
11
+ * - `GET /v1/accounts/spend` → `availableBalanceUsd`,
12
+ * the prepaid fallback when no limit rule exists
13
+ *
14
+ * Same auth contract as template-catalog.ts (and verified there): core takes
15
+ * `Authorization: Bearer`, NOT the agents surface's `x-sapiom-api-key`, and
16
+ * every path is prefixed `/v1`. The key stays server-side; a 401/403 triggers
17
+ * exactly one credential refresh + retry.
18
+ *
19
+ * These endpoints are excluded from core's public OpenAPI surface, so their
20
+ * shapes are contracts by observation, not by document. Every field is
21
+ * narrowed defensively and any drift degrades the readout to `none` (or the
22
+ * whole view to `fallback`) rather than throwing — a broken billing read must
23
+ * never take the rail down.
24
+ */
25
+ import { staticApiKeyProvider, } from "./api-key-provider.js";
26
+ import { resolveCoreBaseUrl } from "./definition-slug-resolver.js";
27
+ /** Mirrors template-catalog.ts: statuses worth one refresh + retry. */
28
+ function isAuthRejection(status) {
29
+ return status === 401 || status === 403;
30
+ }
31
+ /** How long a successful view is reused. Spend moves on the order of runs, not
32
+ * keystrokes; the dashboard's own hooks poll at 30–60s, and the SPA re-asks on
33
+ * its own cadence — a short TTL just absorbs bursts (mount + auth flip). */
34
+ const CACHE_TTL_MS = 60000;
35
+ function asObject(value) {
36
+ return typeof value === "object" && value !== null && !Array.isArray(value)
37
+ ? value
38
+ : null;
39
+ }
40
+ /** A finite number, from a JSON number or a decimal string (the ledger returns
41
+ * decimal(36,18) strings; the spend read model returns numbers). */
42
+ function usd(value) {
43
+ if (typeof value === "number")
44
+ return Number.isFinite(value) ? value : null;
45
+ if (typeof value === "string" && value.trim() !== "") {
46
+ const parsed = Number(value);
47
+ return Number.isFinite(parsed) ? parsed : null;
48
+ }
49
+ return null;
50
+ }
51
+ /** `{ data: { plan: { name, status } | null } }` → the card's plan line. */
52
+ export function extractPlan(body) {
53
+ const plan = asObject(asObject(asObject(body)?.data)?.plan);
54
+ if (!plan || typeof plan.name !== "string" || plan.name === "")
55
+ return null;
56
+ return { name: plan.name, status: plan.status === "active" ? "active" : "inactive" };
57
+ }
58
+ /** JSON:API `{ data: { attributes: { limitUsd, windowSpendUsd } } }` → the
59
+ * "$used / $cap" pair, or null when no qualifying rule exists (all-null
60
+ * attributes is that endpoint's documented "no rule" answer, not an error). */
61
+ export function extractLimit(body) {
62
+ const attrs = asObject(asObject(asObject(body)?.data)?.attributes);
63
+ if (!attrs)
64
+ return null;
65
+ const limitUsd = usd(attrs.limitUsd);
66
+ const usedUsd = usd(attrs.windowSpendUsd);
67
+ if (limitUsd === null || usedUsd === null)
68
+ return null;
69
+ return { usedUsd, limitUsd };
70
+ }
71
+ /** `{ data: { availableBalanceUsd } }` → the prepaid balance, or null. */
72
+ export function extractBalance(body) {
73
+ return usd(asObject(asObject(body)?.data)?.availableBalanceUsd);
74
+ }
75
+ export function createAccountPlanReader(opts) {
76
+ const provider = opts.apiKey !== null && typeof opts.apiKey === "object"
77
+ ? opts.apiKey
78
+ : staticApiKeyProvider(opts.apiKey);
79
+ const baseUrl = opts.baseUrl ?? resolveCoreBaseUrl();
80
+ const fetchImpl = opts.fetchImpl ?? fetch;
81
+ let cache = null;
82
+ // One line per distinct failure reason, not per poll — the SPA re-asks every
83
+ // minute and a persistent misconfiguration shouldn't flood the log.
84
+ const logged = new Set();
85
+ const warnOnce = (reason) => {
86
+ if (logged.has(reason))
87
+ return;
88
+ logged.add(reason);
89
+ console.error(`[harness] account plan unavailable from ${baseUrl} (${reason}); ` +
90
+ "the rail hides the plan card — check the harness is signed in " +
91
+ "and SAPIOM_CORE_URL points at a reachable Sapiom API");
92
+ };
93
+ /**
94
+ * GET `path` from the core surface with the held key, refreshing + retrying
95
+ * once on an auth rejection. Returns the parsed body, or null on any failure.
96
+ */
97
+ const getJson = async (path) => {
98
+ let apiKey = provider.getKey();
99
+ if (!apiKey)
100
+ return null;
101
+ const attempt = async (key) => {
102
+ try {
103
+ return await fetchImpl(`${baseUrl}${path}`, {
104
+ // Core (`api.*`) takes a Bearer token — see template-catalog.ts.
105
+ headers: { Authorization: `Bearer ${key}` },
106
+ });
107
+ }
108
+ catch (err) {
109
+ warnOnce(err instanceof Error ? err.message : String(err));
110
+ return null;
111
+ }
112
+ };
113
+ let response = await attempt(apiKey);
114
+ if (!response)
115
+ return null;
116
+ if (isAuthRejection(response.status)) {
117
+ const refreshed = await provider.refresh();
118
+ if (refreshed && refreshed !== apiKey) {
119
+ apiKey = refreshed;
120
+ response = await attempt(refreshed);
121
+ if (!response)
122
+ return null;
123
+ }
124
+ }
125
+ if (!response.ok) {
126
+ warnOnce(`HTTP ${response.status} on ${path}`);
127
+ return null;
128
+ }
129
+ try {
130
+ return await response.json();
131
+ }
132
+ catch {
133
+ warnOnce("response body was not JSON");
134
+ return null;
135
+ }
136
+ };
137
+ return {
138
+ async view() {
139
+ const fresh = cache && Date.now() - cache.at < CACHE_TTL_MS;
140
+ if (fresh && cache)
141
+ return cache.value;
142
+ // Signed out is an expected state (a harness launched without auth), not
143
+ // a fault — the card simply doesn't render.
144
+ if (!provider.getKey()) {
145
+ return { plan: null, readout: { kind: "none" }, source: "fallback", reason: "signed-out" };
146
+ }
147
+ // Three independent reads; each may fail alone. A plan with no numbers
148
+ // and numbers with no plan are both better than nothing, so partial
149
+ // success builds a partial view instead of discarding what answered.
150
+ const [planBody, limitBody, spendBody] = await Promise.all([
151
+ getJson("/v1/dashboard/plan"),
152
+ getJson("/v1/spending-rules/spend-limit-summary"),
153
+ getJson("/v1/accounts/spend"),
154
+ ]);
155
+ if (planBody === null && limitBody === null && spendBody === null) {
156
+ // Nothing answered — unreachable (or every shape drifted). Not cached:
157
+ // the next poll should get to try again.
158
+ return { plan: null, readout: { kind: "none" }, source: "fallback", reason: "unreachable" };
159
+ }
160
+ const plan = extractPlan(planBody);
161
+ const limit = extractLimit(limitBody);
162
+ const balance = extractBalance(spendBody);
163
+ const readout = limit
164
+ ? { kind: "limit", ...limit }
165
+ : balance !== null
166
+ ? { kind: "balance", availableUsd: balance }
167
+ : { kind: "none" };
168
+ const value = { plan, readout, source: "live" };
169
+ cache = { at: Date.now(), value };
170
+ return value;
171
+ },
172
+ };
173
+ }
174
+ //# sourceMappingURL=account-plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account-plan.js","sourceRoot":"","sources":["../../src/core/account-plan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,EAEL,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAEnE,uEAAuE;AACvE,SAAS,eAAe,CAAC,MAAc;IACrC,OAAO,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG,CAAC;AAC1C,CAAC;AAED;;6EAE6E;AAC7E,MAAM,YAAY,GAAG,KAAM,CAAC;AAW5B,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QACzE,CAAC,CAAE,KAAc;QACjB,CAAC,CAAC,IAAI,CAAC;AACX,CAAC;AAED;qEACqE;AACrE,SAAS,GAAG,CAAC,KAAc;IACzB,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IAC5E,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACrD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC7B,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;IACjD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,WAAW,CAAC,IAAa;IACvC,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;IAC5D,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAC5E,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC;AACvF,CAAC;AAED;;gFAEgF;AAChF,MAAM,UAAU,YAAY,CAC1B,IAAa;IAEb,MAAM,KAAK,GAAG,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,UAAU,CAAC,CAAC;IACnE,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACrC,MAAM,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IAC1C,IAAI,QAAQ,KAAK,IAAI,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACvD,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;AAC/B,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,cAAc,CAAC,IAAa;IAC1C,OAAO,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,mBAAmB,CAAC,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,IAOvC;IACC,MAAM,QAAQ,GACZ,IAAI,CAAC,MAAM,KAAK,IAAI,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ;QACrD,CAAC,CAAC,IAAI,CAAC,MAAM;QACb,CAAC,CAAC,oBAAoB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACxC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,kBAAkB,EAAE,CAAC;IACrD,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC;IAE1C,IAAI,KAAK,GAAkD,IAAI,CAAC;IAChE,6EAA6E;IAC7E,oEAAoE;IACpE,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;IACjC,MAAM,QAAQ,GAAG,CAAC,MAAc,EAAQ,EAAE;QACxC,IAAI,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC;YAAE,OAAO;QAC/B,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACnB,OAAO,CAAC,KAAK,CACX,2CAA2C,OAAO,KAAK,MAAM,KAAK;YAChE,gEAAgE;YAChE,sDAAsD,CACzD,CAAC;IACJ,CAAC,CAAC;IAEF;;;OAGG;IACH,MAAM,OAAO,GAAG,KAAK,EAAE,IAAY,EAA2B,EAAE;QAC9D,IAAI,MAAM,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC;QAC/B,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QAEzB,MAAM,OAAO,GAAG,KAAK,EAAE,GAAW,EAA4B,EAAE;YAC9D,IAAI,CAAC;gBACH,OAAO,MAAM,SAAS,CAAC,GAAG,OAAO,GAAG,IAAI,EAAE,EAAE;oBAC1C,iEAAiE;oBACjE,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,GAAG,EAAE,EAAE;iBAC5C,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,QAAQ,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;gBAC3D,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC,CAAC;QAEF,IAAI,QAAQ,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;QACrC,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC;QAE3B,IAAI,eAAe,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YACrC,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,CAAC;YAC3C,IAAI,SAAS,IAAI,SAAS,KAAK,MAAM,EAAE,CAAC;gBACtC,MAAM,GAAG,SAAS,CAAC;gBACnB,QAAQ,GAAG,MAAM,OAAO,CAAC,SAAS,CAAC,CAAC;gBACpC,IAAI,CAAC,QAAQ;oBAAE,OAAO,IAAI,CAAC;YAC7B,CAAC;QACH,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,QAAQ,CAAC,QAAQ,QAAQ,CAAC,MAAM,OAAO,IAAI,EAAE,CAAC,CAAC;YAC/C,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,CAAC;YACH,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC;YACP,QAAQ,CAAC,4BAA4B,CAAC,CAAC;YACvC,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC,CAAC;IAEF,OAAO;QACL,KAAK,CAAC,IAAI;YACR,MAAM,KAAK,GAAG,KAAK,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,EAAE,GAAG,YAAY,CAAC;YAC5D,IAAI,KAAK,IAAI,KAAK;gBAAE,OAAO,KAAK,CAAC,KAAK,CAAC;YAEvC,yEAAyE;YACzE,4CAA4C;YAC5C,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;gBACvB,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;YAC7F,CAAC;YAED,uEAAuE;YACvE,oEAAoE;YACpE,qEAAqE;YACrE,MAAM,CAAC,QAAQ,EAAE,SAAS,EAAE,SAAS,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;gBACzD,OAAO,CAAC,oBAAoB,CAAC;gBAC7B,OAAO,CAAC,wCAAwC,CAAC;gBACjD,OAAO,CAAC,oBAAoB,CAAC;aAC9B,CAAC,CAAC;YAEH,IAAI,QAAQ,KAAK,IAAI,IAAI,SAAS,KAAK,IAAI,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;gBAClE,uEAAuE;gBACvE,yCAAyC;gBACzC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;YAC9F,CAAC;YAED,MAAM,IAAI,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;YACnC,MAAM,KAAK,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;YACtC,MAAM,OAAO,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC;YAC1C,MAAM,OAAO,GAA+B,KAAK;gBAC/C,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE;gBAC7B,CAAC,CAAC,OAAO,KAAK,IAAI;oBAChB,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE;oBAC5C,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;YAEvB,MAAM,KAAK,GAAoB,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;YACjE,KAAK,GAAG,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,CAAC;YAClC,OAAO,KAAK,CAAC;QACf,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -96,6 +96,16 @@ export interface ClaudeCodeAdapterOptions {
96
96
  * exact turn count; larger files are read only at head+tail. */
97
97
  fullScanMaxBytes?: number;
98
98
  }
99
+ /**
100
+ * Claude Code's known blocking startup screens, matched against stripped-ANSI
101
+ * scrollback. Exported so the packaged smoke / e2e layers can pin the same
102
+ * patterns this adapter gates the ready-fallback on (see detectBlockingPrompt).
103
+ * Wording verified against Claude Code 2.1.x; a future rewording fails SAFE:
104
+ * an unmatched dialog only means the fallback flips `ready` and the injected
105
+ * prompt lands in a dialog the user is looking at anyway — the pre-fallback
106
+ * behaviour was the prompt silently vanishing after ten minutes.
107
+ */
108
+ export declare const CLAUDE_BLOCKING_PROMPT_PATTERNS: readonly RegExp[];
99
109
  export declare class ClaudeCodeAdapter implements HarnessAdapter {
100
110
  readonly id: "claude-code";
101
111
  readonly eventSource: "hooks";
@@ -103,10 +113,36 @@ export declare class ClaudeCodeAdapter implements HarnessAdapter {
103
113
  * launch/resume path below, so a rehydration brief composed into that file
104
114
  * needs no separate delivery. */
105
115
  readonly systemPromptDelivery: "launch-flag";
116
+ /**
117
+ * See `HarnessAdapter.readyFallback`. The SessionStart hook stays the
118
+ * primary signal (isReadyEnough gives claude-code NO immediate scrollback
119
+ * shortcut); this only lets SessionManager's generously-timed fallback
120
+ * rescue a session whose hook chain is broken — on Windows the hook runs
121
+ * `node` through Claude's hook shell, and when that resolution fails the
122
+ * POST never fires, `ready` never flips, and the held first prompt was
123
+ * silently dropped.
124
+ */
125
+ readonly readyFallback: "hook-timeout";
126
+ /**
127
+ * See `HarnessAdapter.assumesBracketedPaste`. Claude Code's Ink input layer
128
+ * enables mode 2004 in every interactive session — the macOS/Linux paste
129
+ * detection observing `ESC[?2004h` on real ptys is the in-repo evidence.
130
+ * Declared so Windows (where ConPTY hides that announcement) still paste-
131
+ * wraps multi-line prompts instead of submitting at the first newline.
132
+ */
133
+ readonly assumesBracketedPaste = true;
106
134
  private readonly binary;
107
135
  private readonly homeDir;
108
136
  private readonly fullScanMaxBytes;
109
137
  constructor(options?: ClaudeCodeAdapterOptions);
138
+ /**
139
+ * See `HarnessAdapter.detectBlockingPrompt` and `readyFallback` above: this
140
+ * exists ONLY to gate the hook-timeout fallback (never type a stray Enter
141
+ * into an unanswered trust dialog) — it deliberately does NOT give
142
+ * claude-code Codex's immediate scrollback shortcut, because the hook is
143
+ * reliable wherever the hook chain itself works.
144
+ */
145
+ detectBlockingPrompt(scrollback: string): boolean;
110
146
  doctor(): Promise<DoctorCheck[]>;
111
147
  launch(opts: LaunchOpts): SpawnSpec;
112
148
  resume(agentSessionId: string, opts: LaunchOpts): SpawnSpec;
@@ -1 +1 @@
1
- {"version":3,"file":"claude-code.d.ts","sourceRoot":"","sources":["../../../src/core/adapters/claude-code.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,OAAO,KAAK,EACV,WAAW,EACX,cAAc,EACd,UAAU,EACV,iBAAiB,EACjB,SAAS,EACV,MAAM,uBAAuB,CAAC;AAE/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,uBAAuB,UAAU,CAAC;AAE/C;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACrC,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAGjC;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CASxF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAMpF;AA4BD,MAAM,WAAW,wBAAwB;IACvC,wFAAwF;IACxF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kEAAkE;IAClE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;qEACiE;IACjE,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAwMD,qBAAa,iBAAkB,YAAW,cAAc;IACtD,QAAQ,CAAC,EAAE,EAAG,aAAa,CAAU;IACrC,QAAQ,CAAC,WAAW,EAAG,OAAO,CAAU;IACxC;;sCAEkC;IAClC,QAAQ,CAAC,oBAAoB,EAAG,aAAa,CAAU;IACvD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;gBAE9B,OAAO,GAAE,wBAA6B;IAM5C,MAAM,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IA6BtC,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS;IAgBnC,MAAM,CAAC,cAAc,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,SAAS;IAa3D;;;;;;;;;;;;;OAaG;IACH,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS;IAmBvC;;;;;;;;;;;;;;;;OAgBG;IACG,SAAS,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAShE,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;CA+ClE;AAcD,wBAAgB,uBAAuB,CAAC,OAAO,CAAC,EAAE,wBAAwB,GAAG,cAAc,CAE1F"}
1
+ {"version":3,"file":"claude-code.d.ts","sourceRoot":"","sources":["../../../src/core/adapters/claude-code.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,OAAO,KAAK,EACV,WAAW,EACX,cAAc,EACd,UAAU,EACV,iBAAiB,EACjB,SAAS,EACV,MAAM,uBAAuB,CAAC;AAG/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,uBAAuB,UAAU,CAAC;AAE/C;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACrC,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAGjC;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CASxF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAMpF;AA4BD,MAAM,WAAW,wBAAwB;IACvC,wFAAwF;IACxF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kEAAkE;IAClE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;qEACiE;IACjE,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAwMD;;;;;;;;GAQG;AACH,eAAO,MAAM,+BAA+B,EAAE,SAAS,MAAM,EAO5D,CAAC;AAEF,qBAAa,iBAAkB,YAAW,cAAc;IACtD,QAAQ,CAAC,EAAE,EAAG,aAAa,CAAU;IACrC,QAAQ,CAAC,WAAW,EAAG,OAAO,CAAU;IACxC;;sCAEkC;IAClC,QAAQ,CAAC,oBAAoB,EAAG,aAAa,CAAU;IACvD;;;;;;;;OAQG;IACH,QAAQ,CAAC,aAAa,EAAG,cAAc,CAAU;IACjD;;;;;;OAMG;IACH,QAAQ,CAAC,qBAAqB,QAAQ;IACtC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;gBAE9B,OAAO,GAAE,wBAA6B;IAMlD;;;;;;OAMG;IACH,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAK3C,MAAM,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IA6BtC,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS;IAgBnC,MAAM,CAAC,cAAc,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,SAAS;IAa3D;;;;;;;;;;;;;OAaG;IACH,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS;IAmBvC;;;;;;;;;;;;;;;;OAgBG;IACG,SAAS,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAShE,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;CA+ClE;AAcD,wBAAgB,uBAAuB,CAAC,OAAO,CAAC,EAAE,wBAAwB,GAAG,cAAc,CAE1F"}
@@ -10,6 +10,7 @@ import { readFile, readdir, realpath, stat, open } from "node:fs/promises";
10
10
  import { homedir } from "node:os";
11
11
  import { basename, join } from "node:path";
12
12
  import { promisify } from "node:util";
13
+ import { stripAnsi } from "../strip-ansi.js";
13
14
  /**
14
15
  * Minimum `claude` version the harness supports.
15
16
  *
@@ -295,6 +296,23 @@ function buildConfigArgs(opts) {
295
296
  args.push("--plugin-dir", opts.pluginDir);
296
297
  return args;
297
298
  }
299
+ /**
300
+ * Claude Code's known blocking startup screens, matched against stripped-ANSI
301
+ * scrollback. Exported so the packaged smoke / e2e layers can pin the same
302
+ * patterns this adapter gates the ready-fallback on (see detectBlockingPrompt).
303
+ * Wording verified against Claude Code 2.1.x; a future rewording fails SAFE:
304
+ * an unmatched dialog only means the fallback flips `ready` and the injected
305
+ * prompt lands in a dialog the user is looking at anyway — the pre-fallback
306
+ * behaviour was the prompt silently vanishing after ten minutes.
307
+ */
308
+ export const CLAUDE_BLOCKING_PROMPT_PATTERNS = [
309
+ // First-run / new-directory trust dialog.
310
+ /do\s+you\s+trust\s+the\s+files\s+in\s+this\s+(folder|directory)/i,
311
+ // First-run theme picker.
312
+ /choose\s+the\s+text\s+style/i,
313
+ // Signed-out login flow.
314
+ /select\s+login\s+method|sign\s+in\s+to\s+(use\s+)?claude/i,
315
+ ];
298
316
  export class ClaudeCodeAdapter {
299
317
  constructor(options = {}) {
300
318
  this.id = "claude-code";
@@ -303,14 +321,43 @@ export class ClaudeCodeAdapter {
303
321
  * launch/resume path below, so a rehydration brief composed into that file
304
322
  * needs no separate delivery. */
305
323
  this.systemPromptDelivery = "launch-flag";
324
+ /**
325
+ * See `HarnessAdapter.readyFallback`. The SessionStart hook stays the
326
+ * primary signal (isReadyEnough gives claude-code NO immediate scrollback
327
+ * shortcut); this only lets SessionManager's generously-timed fallback
328
+ * rescue a session whose hook chain is broken — on Windows the hook runs
329
+ * `node` through Claude's hook shell, and when that resolution fails the
330
+ * POST never fires, `ready` never flips, and the held first prompt was
331
+ * silently dropped.
332
+ */
333
+ this.readyFallback = "hook-timeout";
334
+ /**
335
+ * See `HarnessAdapter.assumesBracketedPaste`. Claude Code's Ink input layer
336
+ * enables mode 2004 in every interactive session — the macOS/Linux paste
337
+ * detection observing `ESC[?2004h` on real ptys is the in-repo evidence.
338
+ * Declared so Windows (where ConPTY hides that announcement) still paste-
339
+ * wraps multi-line prompts instead of submitting at the first newline.
340
+ */
341
+ this.assumesBracketedPaste = true;
306
342
  this.binary = options.binary ?? "claude";
307
343
  this.homeDir = options.homeDir ?? homedir();
308
344
  this.fullScanMaxBytes = options.fullScanMaxBytes ?? DEFAULT_FULL_SCAN_MAX_BYTES;
309
345
  }
346
+ /**
347
+ * See `HarnessAdapter.detectBlockingPrompt` and `readyFallback` above: this
348
+ * exists ONLY to gate the hook-timeout fallback (never type a stray Enter
349
+ * into an unanswered trust dialog) — it deliberately does NOT give
350
+ * claude-code Codex's immediate scrollback shortcut, because the hook is
351
+ * reliable wherever the hook chain itself works.
352
+ */
353
+ detectBlockingPrompt(scrollback) {
354
+ const cleaned = stripAnsi(scrollback);
355
+ return CLAUDE_BLOCKING_PROMPT_PATTERNS.some((pattern) => pattern.test(cleaned));
356
+ }
310
357
  async doctor() {
311
358
  let versionLine;
312
359
  try {
313
- const { stdout } = await execFileAsync(this.binary, ["--version"], { timeout: 5000 });
360
+ const { stdout } = await execFileAsync(this.binary, ["--version"], { timeout: 5000, windowsHide: true });
314
361
  versionLine = stdout.trim();
315
362
  }
316
363
  catch {