From 4d3446f853b2274d087f337af7ba22606c44d9eb Mon Sep 17 00:00:00 2001 From: Lu Nelson Date: Thu, 2 Apr 2026 17:20:09 +0200 Subject: [PATCH 01/31] spike: raw Anthropic SDK for reliable tool execution (D30) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Agent SDK MCP tool registration broken (A2 invalidated). Spike proves @anthropic-ai/sdk client.messages.stream() with tool_choice forcing reliably calls ask_question. 3/3 tests pass: 1. Forced tool_choice → structured question args 2. Raw stream events match content_block_start/delta/stop format 3. Observer JSON extraction works via direct API call New: A26 (validated), D30. Invalidated: A2, A24, A25. Unblocks slices 7+ (phase transitions and beyond). Amp-Thread-ID: https://ampcode.com/threads/T-019d4eaf-1eb6-75cf-ab50-c2acf1c59c14 Co-authored-by: Amp --- memory/PLAN.md | 11 ++ memory/SPEC.md | 11 +- package-lock.json | 49 +++++++ package.json | 1 + spike/raw-sdk-tool-use.ts | 263 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 331 insertions(+), 4 deletions(-) create mode 100644 spike/raw-sdk-tool-use.ts diff --git a/memory/PLAN.md b/memory/PLAN.md index 2f53928e..a0a78c7b 100644 --- a/memory/PLAN.md +++ b/memory/PLAN.md @@ -150,6 +150,17 @@ - Ref: → docs/design/BREADBOARD.md §UI Affordances → P2 Entity sidebar - **Verification approach**: inner — unit tests for entity query on active path, stale badge computation. Middle — validate A21: `onData` → `setQueryData` updates sidebar without stale closure (if stale, fall back to parallel `EventSource`). Outer — manual visual inspection (entities render correctly, tabs work, stale badges appear). Debug mode overlay (observer extraction detail per-turn) should land here or in slice 5. → SPEC.md §Oracle Strategy (outer loop), §Acknowledged Blind Spots (cumulative graph integrity) +### Spikes + +2. **Raw Anthropic SDK for tool execution** — Can we replace `@anthropic-ai/claude-agent-sdk` `query()` with `@anthropic-ai/sdk` `client.messages.stream()` for reliable tool calls? Agent SDK MCP tool registration is broken (A2 invalidated). `done` + - Assumptions: → SPEC.md §Assumptions A2 (**invalidated**), A24 (**invalidated**), A25 (**invalidated**), A26 (**validated**) + - Decisions: → SPEC.md §Decisions D30 + - Time box: 2 hours + - Success: ✅ All 3 tests pass — (1) forced `tool_choice` produces structured `ask_question` call, (2) raw streaming events match `content_block_start/delta/stop` format, (3) observer JSON extraction works via direct API call + - Evidence: `spike/raw-sdk-tool-use.ts` + - Branch: `ln/spike-raw-anthropic-sdk` + - **Implication**: Slices 7+ unblocked. New migration slice needed (SDK swap) before phase transitions can proceed. + ## Phase 4: Full Interview @@ -248,7 +258,8 @@ Phase 2: 2 ──→ 3 (turn schema) ──→ 3c (Drizzle+core) ──→ 3d ( Phase 3: 3c ──→ 3b (rich chat UI) ──→ 4 (scope server) ──→ 4a (parts+context) ──→ 4b (client UI) ──→ 4c (UI foundation) ──→ 5 (observer) spike (observer fidelity) ──→ 5 3d + 5 ──→ 6 (entity sidebar) -Phase 4: 6 ──→ 7 (transitions) ──→ 8 (design) ──→ 9 (requirements) ──→ 10 (criteria) + spike 2 ──→ 6b (SDK migration) +Phase 4: 6b ──→ 7 (transitions) ──→ 8 (design) ──→ 9 (requirements) ──→ 10 (criteria) Phase 5: 6 ──→ 11 (branching) 6 ──→ 12 (entity lifecycle API) 10 ──→ 13 (export) diff --git a/memory/SPEC.md b/memory/SPEC.md index 2a851419..c8163359 100644 --- a/memory/SPEC.md +++ b/memory/SPEC.md @@ -341,9 +341,9 @@ This projection difference is a deliberate design choice, not an implementation | ------------------- | ----- | --------------------------- | | sse-adapter.test.ts | 8 | I1, I7, I21 | | db.test.ts | 32 | I5, I6, I9, I10, I11, I18, I20 | -| app.test.ts | 22 | I2, I3, I6, I7, I13, I14 | -| core.test.ts | 16 | I12, I13, I18, I22 | -| interview.test.ts | 16 | I16 | +| app.test.ts | 21 | I2, I3, I6, I7, I13, I14 | +| core.test.ts | 15 | I12, I13, I18, I22 | +| interview.test.ts | 15 | I16, A27 | | parts.test.ts | 23 | I17, I18 | | context.test.ts | 8 | I19 | | sdk.test.ts | 7 | I22 | From 3988a12992aa750c662fcbb849945349b477ea20 Mon Sep 17 00:00:00 2001 From: Lu Nelson Date: Thu, 2 Apr 2026 17:54:40 +0200 Subject: [PATCH 04/31] docs: spec D31 agent loop + plan slice 6c (pi-mono reference) Amp-Thread-ID: https://ampcode.com/threads/T-019d4ecf-de8a-7258-b074-08887c6a66bf Co-authored-by: Amp --- memory/PLAN.md | 24 +++++++++++++++++------- memory/SPEC.md | 6 +++++- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/memory/PLAN.md b/memory/PLAN.md index de8648ab..6743a97e 100644 --- a/memory/PLAN.md +++ b/memory/PLAN.md @@ -178,9 +178,19 @@ ### Slices -7. **Phase transition + resolution** — Agent judges when scope phase is complete (`is_resolution`). Core yields `phase-resolved` DomainEvent. Client shows summary modal. User confirms to advance. Phase indicator updates. `not-started` +6c. **Agent loop: stream → tool_use → execute → re-submit** — Implement a thin custom agent loop in `src/server/agent-loop.ts` modeled after `badlogic/pi-mono` `packages/agent/src/agent-loop.ts`. The loop: streams a response via `client.messages.stream()`, checks `stop_reason`, if `tool_use` executes registered tool handlers and re-submits with `tool_result` messages, repeats until `end_turn` or `maxTurns`. Yields `DomainEvent`s during streaming. Tool registry: `{ name, inputSchema, execute }[]`. `runInterviewer` refactored to call `agentLoop()` with phase-specific config. No changes to observer (single-shot, no loop). `not-started` + - Requirements: → SPEC.md §Requirements #2, #3, #7 + - Assumptions: → SPEC.md §Assumptions A28 + - Decisions: → SPEC.md §Decisions D31 + - Invariants to establish: I24 (agent loop re-submission — tool_use stop → tool_result → model sees result) + - Invariants to respect: → SPEC.md §Invariants I16, I22 + - Acceptance: `agentLoop()` yields correct DomainEvents for single-tool forced call (backward compat with current interviewer); `agentLoop()` re-submits tool results when `stop_reason === 'tool_use'` and stops on `end_turn`; `runInterviewer` passes all existing tests unchanged after refactor to use `agentLoop()`; `npm run verify` passes + - **Verification approach**: inner — unit tests for loop lifecycle (single-turn, multi-turn, maxTurns guard, tool execution, error handling). Middle — existing interview + core + app tests pass unchanged (backward compat). Outer — manual interview end-to-end. + - **Reference implementation**: `~/Clones/badlogic/pi-mono/packages/agent/src/agent-loop.ts` — specifically `runLoop()` (L155-232), `streamAssistantResponse()` (L238-320), `executeToolCalls()` (L322-388). Brunch adapts the inner loop and tool execution; omits steering/follow-up messages, parallel execution, provider abstraction, `convertToLlm`, `beforeToolCall`/`afterToolCall` hooks, AbortSignal. + +7. **Phase transition + resolution** — Agent judges when scope phase is complete (`is_resolution`). Core yields `phase-resolved` DomainEvent. Client shows summary modal. User confirms to advance. Phase indicator updates. Requires agent loop (6c) — the agent must call `ask_question` AND optionally signal resolution in the same turn, which requires multi-step tool use or `tool_choice: auto`. `not-started` - Requirements: → SPEC.md §Requirements #7, #8 - - Assumptions: → SPEC.md §Assumptions A15 + - Assumptions: → SPEC.md §Assumptions A15, A28 - Acceptance: agent marks resolution, summary shows, user confirms, phase indicator reflects completion 8. **Design drill-down phase** — Second agent skill. Walks the design tree with structured questions. Decisions extracted by observer. Continues until agent judges resolution. `not-started` @@ -194,9 +204,9 @@ - Acceptance: agent presents requirements, suggests gaps, user confirms, reviewed_at updated 10. **Criteria phase** — Fourth agent skill. For each confirmed requirement, agent proposes testable criteria. User selects/edits/confirms. Criteria get `reviewed_at` stamped. `not-started` - - Requirements: → SPEC.md §Requirements #12 - - Assumptions: — - - Acceptance: agent proposes criteria per requirement, user confirms, spec readiness predicate evaluable + - Requirements: → SPEC.md §Requirements #12 + - Assumptions: — + - Acceptance: agent proposes criteria per requirement, user confirms, spec readiness predicate evaluable ## Phase 5: Revisit + Export @@ -258,8 +268,8 @@ Phase 2: 2 ──→ 3 (turn schema) ──→ 3c (Drizzle+core) ──→ 3d ( Phase 3: 3c ──→ 3b (rich chat UI) ──→ 4 (scope server) ──→ 4a (parts+context) ──→ 4b (client UI) ──→ 4c (UI foundation) ──→ 5 (observer) spike (observer fidelity) ──→ 5 3d + 5 ──→ 6 (entity sidebar) - spike 2 ──→ 6b (SDK migration) -Phase 4: 6b ──→ 7 (transitions) ──→ 8 (design) ──→ 9 (requirements) ──→ 10 (criteria) + spike 2 ──→ 6b (SDK migration) ──→ 6c (agent loop) +Phase 4: 6c ──→ 7 (transitions) ──→ 8 (design) ──→ 9 (requirements) ──→ 10 (criteria) Phase 5: 6 ──→ 11 (branching) 6 ──→ 12 (entity lifecycle API) 10 ──→ 13 (export) diff --git a/memory/SPEC.md b/memory/SPEC.md index c8163359..71742f3b 100644 --- a/memory/SPEC.md +++ b/memory/SPEC.md @@ -22,7 +22,7 @@ The architecture (layered: db → core → adapters): - **Database**: SQLite via Drizzle ORM + `better-sqlite3` — TypeScript schema is single source of truth for types, DDL, and migrations. Auto-applies at startup. - **Core**: Interface-agnostic service layer — turn tree operations, interview orchestration, entity lifecycle, observer, phase management, export. `conductTurn()` returns `AsyncIterable` for streaming. No transport knowledge. -- **Agent engine**: Raw Anthropic SDK (`@anthropic-ai/sdk`) — `client.messages.stream()` for tool-using agents, `client.messages.create()` for silent extraction. Tools as JSON schemas in `params.tools` with `tool_choice` forcing. Custom agent loop for multi-step tool use. Each interview phase is an agent skill. Called by core, not by adapters. (D30 — replaces Claude Agent SDK) +- **Agent engine**: Raw Anthropic SDK (`@anthropic-ai/sdk`) — `client.messages.stream()` for tool-using agents, `client.messages.create()` for silent extraction. Tools as JSON schemas in `params.tools` with `tool_choice` forcing. Custom agent loop (`agentLoop()`) for multi-step tool use: stream response → check `stop_reason` → if `tool_use`, execute handler, append `tool_result`, re-submit → repeat until `end_turn` or max turns. Modeled after `badlogic/pi-mono` `packages/agent/src/agent-loop.ts` (D31). Each interview phase is an agent skill. Called by core, not by adapters. (D30 — replaces Claude Agent SDK) - **Observer agent**: Separate extraction call after each turn — captures decisions, assumptions, and their dependency edges. Invoked by core after turn completion. - **Web adapter**: Express.js translates `DomainEvent` stream to AI SDK UI Message Stream SSE. React + Vite + `@ai-sdk/react` `useChat` client. - **CLI adapter**: (future) Terminal I/O consuming the same `DomainEvent` stream @@ -91,9 +91,12 @@ The architecture (layered: db → core → adapters): | A25 | `SDKResultMessage` provides accurate `duration_ms`, `total_cost_usd`, and `usage` for per-agent observability — types confirmed in TS SDK (`SDKResultSuccess`, `SDKResultError`) | **invalidated** | D29, D30 | Observer agent | Invalidated: Agent SDK `ResultMessage` is moot (D30). Raw API provides `usage` (input/output tokens) on final message and `stop_reason`. Wall-clock duration measured locally. Cost computation requires manual model pricing lookup — deferred. | | A26 | Raw `@anthropic-ai/sdk` `client.messages.stream()` with `tool_choice: { type: 'tool', name: 'ask_question' }` reliably forces tool execution — no MCP registration, no subprocess | **validated** | D30 | All agent slices | Validated (spike): 3/3 tests pass. Model calls `ask_question` with correct structured args. Streaming events match `content_block_start/delta/stop` format. Observer JSON extraction works. See `spike/raw-sdk-tool-use.ts`. | | A27 | Hand-written `ask_question` tool JSON schema stays in sync with `structuredQuestionSchema` Zod type — no drift between the two representations | high | D30 | SDK migration | Test: Zod.parse succeeds on mock args matching the hand-written JSON schema shape. Both representations tested against same fixture data. | +| A28 | A thin custom agent loop (~80 LOC) is sufficient for brunch's tool execution needs — no need for a full agent framework. The loop handles: stream → tool_use stop → execute handler → tool_result → re-submit. Single-tool forced calls (current) and multi-tool auto calls (future) both fit this model. | high | D31 | Agent loop, Phase transitions | Validate with phase resolution slice (slice 7): agent must be able to call `ask_question` AND judge `is_resolution` in a single turn, requiring multi-step tool use or text + tool_use mixed response. | ## Decisions +31. **Custom agent loop modeled after `badlogic/pi-mono`** — Implement a thin agent loop in `src/server/agent-loop.ts` that manages the stream → tool_use → execute → tool_result → re-submit cycle. Reference implementation: `badlogic/pi-mono` `packages/agent/src/agent-loop.ts` (~630 LOC with full generality; brunch needs ~80-120 LOC subset). Key design choices adapted from pi-mono: (1) **`runLoop` inner loop** checks `stop_reason === 'tool_use'` and re-submits with tool results until `end_turn` — brunch needs this for phase 4 when agent must call `ask_question` then optionally judge `is_resolution`. (2) **Tool registry** as an array of `{ name, schema, execute }` objects passed to the loop — matches brunch's hand-written JSON schema pattern. (3) **DomainEvent emission during streaming** — pi-mono uses `emit(event)` callbacks; brunch yields `DomainEvent`s from the generator. (4) **`maxTurns` guard** — prevents runaway loops. What brunch does NOT adopt: steering/follow-up messages, parallel tool execution, `convertToLlm` transforms, `beforeToolCall`/`afterToolCall` hooks, AbortSignal, provider abstraction. The loop is Anthropic-specific, single-provider, sequential. `runInterviewer` becomes a thin caller of `agentLoop()` with phase-specific config (system prompt, tools, maxTurns). Depends on: A28, D30. Supersedes: current single-shot `client.messages.stream()` iteration in `runInterviewer`. + 30. **Replace Claude Agent SDK with raw Anthropic SDK** — Replace `@anthropic-ai/claude-agent-sdk` (`query()`, `createSdkMcpServer()`, `tool()`) with `@anthropic-ai/sdk` (`client.messages.stream()`, `client.messages.create()`). Tools defined as JSON schemas in `params.tools` with `tool_choice` forcing. Translator updated to consume raw streaming events directly (remove `stream_event` envelope). Observer uses direct API call with system-prompt-guided JSON extraction. Metrics from raw API `usage` + local wall-clock timing (no `ResultMessage`). Enables future tool extensibility (bash, read, MCP client) without subprocess limitations. Spike evidence: `spike/raw-sdk-tool-use.ts` — 3/3 tests pass. Depends on: A26. Supersedes: D27 (agent SDK composition), D28 (outputFormat), D29 (ResultMessage). Invalidates: A2, A24, A25. 27. **Agent module pattern — generator composition** — Each agent (interviewer, observer, future phase agents) is an async generator function yielding `DomainEvent`s. `conductTurn()` is a thin sequencer composing agents via `yield*`. No wrapper around `query()` — each agent calls the SDK directly with whatever options it needs (`outputFormat`, `effort`, `mcpServers`, etc.). A shared `translateStreamEvents()` utility in `sdk.ts` maps SDK `stream_event` messages to DomainEvents; streaming agents use it, silent agents don't. File layout: `interviewer.ts` (evolves from `interview.ts`), `observer.ts` (new), `sdk.ts` (new). Research: `docs/research/claude-agent-sdk-cookbook-patterns-vs-brunch-usage.md`. Depends on: D19. Supersedes: monolithic `conductTurn()` with inline `query()` call and stream parsing. @@ -210,6 +213,7 @@ The architecture (layered: db → core → adapters): | **phase resolution** | LLM judgment that shared understanding has been reached for a phase. Marked by `turn.is_resolution = true` on the last turn of a phase | | **ask_question tool** | The MCP tool the interviewer must use each turn. Accepts `{ question, why, impact, options[] }`, validated by `structuredQuestionSchema` (Zod). The tool handler persists structured data to the turn and options tables via closure over `db` + `turnId`. Defined in `interview.ts` | | **interview MCP server** | A per-turn MCP server created by `createInterviewMcpServer(db, turnId)`. Exposes the `ask_question` tool. The closure captures the current turn ID so the tool handler writes to the correct row. Passed to `query()` via `mcpServers` option. Defined in `interview.ts` | +| **agent loop** | The stream → tool_use → execute → tool_result → re-submit cycle. Implemented in `agent-loop.ts`. Modeled after `badlogic/pi-mono` `packages/agent/src/agent-loop.ts`. Yields `DomainEvent`s during streaming. Configurable with tools, system prompt, maxTurns. See D31 | | **interviewer** | The primary agent role: conducts the interview with structured questions, grounding, and impact signals. Must use the `ask_question` tool every turn. Does not extract entities | | **observer** | The secondary agent role: extracts decisions, assumptions, and dependency edges from each answered turn. Runs post-answer during user read time | | **core** | The interface-agnostic service layer between the database and transport adapters. Owns interview orchestration, entity lifecycle, observer invocation. Returns `AsyncIterable` for streaming | From dd086bf035cc9146a9b0555c3fb457583e668b26 Mon Sep 17 00:00:00 2001 From: Lu Nelson Date: Thu, 2 Apr 2026 17:56:35 +0200 Subject: [PATCH 05/31] test: oracle coverage for A27 schema sync and observer code-fence stripping Amp-Thread-ID: https://ampcode.com/threads/T-019d4ecf-de8a-7258-b074-08887c6a66bf Co-authored-by: Amp --- src/server/interview.test.ts | 27 +++++++++++++++++++++++++++ src/server/observer.test.ts | 30 ++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/src/server/interview.test.ts b/src/server/interview.test.ts index 3337a838..cd4bbd72 100644 --- a/src/server/interview.test.ts +++ b/src/server/interview.test.ts @@ -139,6 +139,33 @@ describe('getSystemPrompt', () => { }); }); +// --- Oracle: A27 — Zod/JSON schema sync --- + +describe('A27: structuredQuestionSchema and ASK_QUESTION_TOOL stay in sync', () => { + it('fixture data valid for the JSON schema also passes Zod parse', async () => { + const { ASK_QUESTION_TOOL } = await import('./interview.js'); + const fixture = { + question: 'What platform are you targeting?', + why: 'Platform choice affects architecture.', + impact: 'high', + options: [ + { content: 'Web only', is_recommended: true }, + { content: 'Desktop and mobile', is_recommended: false }, + ], + }; + + // Verify fixture matches JSON schema shape + const schema = ASK_QUESTION_TOOL.input_schema as Record; + expect(schema.type).toBe('object'); + expect((schema.required as string[]).sort()).toEqual(['impact', 'options', 'question', 'why']); + + // Verify same fixture passes Zod + const result = structuredQuestionSchema.parse(fixture); + expect(result.question).toBe(fixture.question); + expect(result.options).toHaveLength(2); + }); +}); + // --- Acceptance criterion: tool handler persistence --- describe('ask_question tool handler', () => { diff --git a/src/server/observer.test.ts b/src/server/observer.test.ts index 02465bc0..c0e7ff9d 100644 --- a/src/server/observer.test.ts +++ b/src/server/observer.test.ts @@ -165,6 +165,36 @@ describe('runObserver', () => { expect((metrics as any).durationMs).toBeGreaterThanOrEqual(0); }); + it('handles code-fence-wrapped JSON from model', async () => { + const jsonStr = JSON.stringify({ + decisions: [ + { content: 'Use REST', rationale: 'Simple', parentDecisionIds: [], parentAssumptionIds: [] }, + ], + assumptions: [], + }); + // Model wraps in ```json ... ``` + mockCreate.mockResolvedValue({ + id: 'msg-obs-1', + content: [{ type: 'text', text: '```json\n' + jsonStr + '\n```' }], + stop_reason: 'end_turn', + usage: { input_tokens: 200, output_tokens: 80 }, + }); + + const project = createProject(db, 'Test'); + const turn = createTurn(db, project.id, { phase: 'scope', question: 'Q', answer: 'A' }); + + const events: DomainEvent[] = []; + for await (const event of runObserver(db, turn, project.id)) { + events.push(event); + } + + const complete = events.find((e) => e.type === 'observer-complete') as any; + expect(complete.entityIds.decisions).toHaveLength(1); + + const entities = getEntitiesForProject(db, project.id); + expect(entities.decisions[0].content).toBe('Use REST'); + }); + it('handles empty extraction gracefully', async () => { mockObserverResponse({ decisions: [], From dbffecb13462ccdc20014a14174a5c895aae99f4 Mon Sep 17 00:00:00 2001 From: Lu Nelson Date: Thu, 2 Apr 2026 18:18:57 +0200 Subject: [PATCH 06/31] fix: enable thinking + tool_choice auto, add agent-tail for dev logs Amp-Thread-ID: https://ampcode.com/threads/T-019d4ecf-de8a-7258-b074-08887c6a66bf Co-authored-by: Amp --- .gitignore | 1 + package-lock.json | 1391 +++++++++++++++++++++++++++++----- package.json | 3 +- src/server/interview.test.ts | 2 +- src/server/interview.ts | 5 +- vite.config.ts | 3 +- 6 files changed, 1213 insertions(+), 192 deletions(-) diff --git a/.gitignore b/.gitignore index e5c27532..4ed13c98 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,4 @@ todo.txt # Claude Code worktrees .claude/worktrees/ +tmp/ diff --git a/package-lock.json b/package-lock.json index e1328e00..7669f380 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,6 +49,7 @@ "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@types/supertest": "^7.2.0", + "agent-tail": "^0.4.0", "concurrently": "^9.2.1", "drizzle-kit": "^0.31.10", "oxfmt": "^0.43.0", @@ -834,6 +835,16 @@ "@noble/ciphers": "^1.0.0" } }, + "node_modules/@emnapi/runtime": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", + "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@esbuild-kit/core-utils": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/@esbuild-kit/core-utils/-/core-utils-3.3.2.tgz", @@ -1762,234 +1773,962 @@ "mlly": "^1.8.0" } }, - "node_modules/@inquirer/ansi": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz", - "integrity": "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==", + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "engines": { "node": ">=18" } }, - "node_modules/@inquirer/confirm": { - "version": "5.1.21", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.21.tgz", - "integrity": "sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==", - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/type": "^3.0.10" - }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, "engines": { - "node": ">=18" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, - "peerDependencies": { - "@types/node": ">=18" + "funding": { + "url": "https://opencollective.com/libvips" }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" } }, - "node_modules/@inquirer/core": { - "version": "10.3.2", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz", - "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==", - "license": "MIT", - "dependencies": { - "@inquirer/ansi": "^1.0.2", - "@inquirer/figures": "^1.0.15", - "@inquirer/type": "^3.0.10", - "cli-width": "^4.1.0", - "mute-stream": "^2.0.0", - "signal-exit": "^4.1.0", - "wrap-ansi": "^6.2.0", - "yoctocolors-cjs": "^2.1.3" - }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, "engines": { - "node": ">=18" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, - "peerDependencies": { - "@types/node": ">=18" + "funding": { + "url": "https://opencollective.com/libvips" }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" } }, - "node_modules/@inquirer/core/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@inquirer/figures": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz", - "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==", - "license": "MIT", - "engines": { - "node": ">=18" + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@inquirer/type": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz", - "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "license": "MIT" + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "funding": { + "url": "https://opencollective.com/libvips" + } }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@mermaid-js/parser": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-1.1.0.tgz", - "integrity": "sha512-gxK9ZX2+Fex5zu8LhRQoMeMPEHbc73UKZ0FQ54YrQtUxE1VVhMwzeNtKRPAu5aXks4FasbMe4xB4bWrmq6Jlxw==", - "license": "MIT", - "dependencies": { - "langium": "^4.0.0" + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "peer": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@inquirer/ansi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz", + "integrity": "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/confirm": { + "version": "5.1.21", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.21.tgz", + "integrity": "sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core": { + "version": "10.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz", + "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==", + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz", + "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/type": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz", + "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mermaid-js/parser": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-1.1.0.tgz", + "integrity": "sha512-gxK9ZX2+Fex5zu8LhRQoMeMPEHbc73UKZ0FQ54YrQtUxE1VVhMwzeNtKRPAu5aXks4FasbMe4xB4bWrmq6Jlxw==", + "license": "MIT", + "dependencies": { + "langium": "^4.0.0" + } + }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.27.1", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.27.1.tgz", + "integrity": "sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA==", + "license": "MIT", + "dependencies": { + "@hono/node-server": "^1.19.9", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.2.1", + "express-rate-limit": "^8.2.1", + "hono": "^4.11.4", + "jose": "^6.1.3", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/@mswjs/interceptors": { + "version": "0.41.3", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.41.3.tgz", + "integrity": "sha512-cXu86tF4VQVfwz8W1SPbhoRyHJkti6mjH/XJIxp40jhO4j2k1m4KYrEykxqWPkFF3vrK4rgQppBh//AwyGSXPA==", + "license": "MIT", + "dependencies": { + "@open-draft/deferred-promise": "^2.2.0", + "@open-draft/logger": "^0.3.0", + "@open-draft/until": "^2.0.0", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.3", + "strict-event-emitter": "^0.5.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@next/env": { + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.2.tgz", + "integrity": "sha512-LqSGz5+xGk9EL/iBDr2yo/CgNQV6cFsNhRR2xhSXYh7B/hb4nePCxlmDvGEKG30NMHDFf0raqSyOZiQrO7BkHQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.2.tgz", + "integrity": "sha512-B92G3ulrwmkDSEJEp9+XzGLex5wC1knrmCSIylyVeiAtCIfvEJYiN3v5kXPlYt5R4RFlsfO/v++aKV63Acrugg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.2.tgz", + "integrity": "sha512-7ZwSgNKJNQiwW0CKhNm9B1WS2L1Olc4B2XY0hPYCAL3epFnugMhuw5TMWzMilQ3QCZcCHoYm9NGWTHbr5REFxw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.2.tgz", + "integrity": "sha512-c3m8kBHMziMgo2fICOP/cd/5YlrxDU5YYjAJeQLyFsCqVF8xjOTH/QYG4a2u48CvvZZSj1eHQfBCbyh7kBr30Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.2.tgz", + "integrity": "sha512-VKLuscm0P/mIfzt+SDdn2+8TNNJ7f0qfEkA+az7OqQbjzKdBxAHs0UvuiVoCtbwX+dqMEL9U54b5wQ/aN3dHeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.2.tgz", + "integrity": "sha512-kU3OPHJq6sBUjOk7wc5zJ7/lipn8yGldMoAv4z67j6ov6Xo/JvzA7L7LCsyzzsXmgLEhk3Qkpwqaq/1+XpNR3g==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10" } }, - "node_modules/@modelcontextprotocol/sdk": { - "version": "1.27.1", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.27.1.tgz", - "integrity": "sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA==", + "node_modules/@next/swc-linux-x64-musl": { + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.2.tgz", + "integrity": "sha512-CKXRILyErMtUftp+coGcZ38ZwE/Aqq45VMCcRLr2I4OXKrgxIBDXHnBgeX/UMil0S09i2JXaDL3Q+TN8D/cKmg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], "license": "MIT", - "dependencies": { - "@hono/node-server": "^1.19.9", - "ajv": "^8.17.1", - "ajv-formats": "^3.0.1", - "content-type": "^1.0.5", - "cors": "^2.8.5", - "cross-spawn": "^7.0.5", - "eventsource": "^3.0.2", - "eventsource-parser": "^3.0.0", - "express": "^5.2.1", - "express-rate-limit": "^8.2.1", - "hono": "^4.11.4", - "jose": "^6.1.3", - "json-schema-typed": "^8.0.2", - "pkce-challenge": "^5.0.0", - "raw-body": "^3.0.0", - "zod": "^3.25 || ^4.0", - "zod-to-json-schema": "^3.25.1" - }, + "optional": true, + "os": [ + "linux" + ], + "peer": true, "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@cfworker/json-schema": "^4.1.1", - "zod": "^3.25 || ^4.0" - }, - "peerDependenciesMeta": { - "@cfworker/json-schema": { - "optional": true - }, - "zod": { - "optional": false - } + "node": ">= 10" } }, - "node_modules/@modelcontextprotocol/sdk/node_modules/ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.2.tgz", + "integrity": "sha512-sS/jSk5VUoShUqINJFvNjVT7JfR5ORYj/+/ZpOYbbIohv/lQfduWnGAycq2wlknbOql2xOR0DoV0s6Xfcy49+g==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">= 10" } }, - "node_modules/@modelcontextprotocol/sdk/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT" - }, - "node_modules/@mswjs/interceptors": { - "version": "0.41.3", - "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.41.3.tgz", - "integrity": "sha512-cXu86tF4VQVfwz8W1SPbhoRyHJkti6mjH/XJIxp40jhO4j2k1m4KYrEykxqWPkFF3vrK4rgQppBh//AwyGSXPA==", + "node_modules/@next/swc-win32-x64-msvc": { + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.2.tgz", + "integrity": "sha512-aHaKceJgdySReT7qeck5oShucxWRiiEuwCGK8HHALe6yZga8uyFpLkPgaRw3kkF04U7ROogL/suYCNt/+CuXGA==", + "cpu": [ + "x64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "@open-draft/deferred-promise": "^2.2.0", - "@open-draft/logger": "^0.3.0", - "@open-draft/until": "^2.0.0", - "is-node-process": "^1.2.0", - "outvariant": "^1.4.3", - "strict-event-emitter": "^0.5.1" - }, + "optional": true, + "os": [ + "win32" + ], + "peer": true, "engines": { - "node": ">=18" + "node": ">= 10" } }, "node_modules/@noble/ciphers": { @@ -4967,6 +5706,17 @@ "react": "^18.0.0 || ^19.0.0" } }, + "node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.8.0" + } + }, "node_modules/@tailwindcss/node": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.2.tgz", @@ -6124,6 +6874,47 @@ "node": ">= 14" } }, + "node_modules/agent-tail": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/agent-tail/-/agent-tail-0.4.0.tgz", + "integrity": "sha512-El43u8Sde6UyUfqxPFIU44FS36LvfHELVQM+5XTdgCTAs8IzwHWg5erE+Qwn+HNz51BoqHH/z+T5LLYF8z2weg==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-tail-core": "^0.4.0", + "next-plugin-agent-tail": "^0.4.0", + "vite-plugin-agent-tail": "^0.4.0" + }, + "bin": { + "agent-tail": "dist/cli.mjs" + }, + "peerDependencies": { + "next": ">=13.0.0", + "react": ">=18.0.0", + "vite": ">=5.0.0" + }, + "peerDependenciesMeta": { + "next": { + "optional": true + }, + "react": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/agent-tail-core": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/agent-tail-core/-/agent-tail-core-0.4.0.tgz", + "integrity": "sha512-eSpGcuTiMbqpuq9N7ruztS3DsdxR/xEPYRw/D8MVfVLfMEYifaGuzqzxtzhRpc1piONTLahLidkp39GorbF1jg==", + "dev": true, + "license": "MIT", + "bin": { + "agent-tail": "dist/cli.mjs" + } + }, "node_modules/ai": { "version": "6.0.143", "resolved": "https://registry.npmjs.org/ai/-/ai-6.0.143.tgz", @@ -6701,6 +7492,14 @@ "node": ">= 12" } }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -11849,6 +12648,124 @@ "node": ">= 0.6" } }, + "node_modules/next": { + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/next/-/next-16.2.2.tgz", + "integrity": "sha512-i6AJdyVa4oQjyvX/6GeER8dpY/xlIV+4NMv/svykcLtURJSy/WzDnnUk/TM4d0uewFHK7xSQz4TbIwPgjky+3A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@next/env": "16.2.2", + "@swc/helpers": "0.5.15", + "baseline-browser-mapping": "^2.9.19", + "caniuse-lite": "^1.0.30001579", + "postcss": "8.4.31", + "styled-jsx": "5.1.6" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": ">=20.9.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "16.2.2", + "@next/swc-darwin-x64": "16.2.2", + "@next/swc-linux-arm64-gnu": "16.2.2", + "@next/swc-linux-arm64-musl": "16.2.2", + "@next/swc-linux-x64-gnu": "16.2.2", + "@next/swc-linux-x64-musl": "16.2.2", + "@next/swc-win32-arm64-msvc": "16.2.2", + "@next/swc-win32-x64-msvc": "16.2.2", + "sharp": "^0.34.5" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.51.1", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/next-plugin-agent-tail": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/next-plugin-agent-tail/-/next-plugin-agent-tail-0.4.0.tgz", + "integrity": "sha512-c6tkPCF23WTS0Y9CpbOauqEiNbzJG7g292InIUlnxBW0C5z7iYnmX39UMVikMMPbh/YKPzdsCOugeWpryJ3U5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-tail-core": "^0.4.0" + }, + "peerDependencies": { + "next": ">=13.0.0" + } + }, + "node_modules/next/node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/node-abi": { "version": "3.89.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.89.0.tgz", @@ -13471,6 +14388,68 @@ "url": "https://github.com/sponsors/colinhacks" } }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "peer": true, + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/sharp/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -13891,6 +14870,31 @@ "inline-style-parser": "0.2.7" } }, + "node_modules/styled-jsx": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, "node_modules/stylis": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", @@ -14719,6 +15723,19 @@ } } }, + "node_modules/vite-plugin-agent-tail": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/vite-plugin-agent-tail/-/vite-plugin-agent-tail-0.4.0.tgz", + "integrity": "sha512-5MpQUh9D+Jp0egUIn+g9zWorBFGpXLRWfbYNptF8lgneVsAmZUglyvic8ff7XVlSIywIiOT0x+GByqotwPKKkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-tail-core": "^0.4.0" + }, + "peerDependencies": { + "vite": ">=5.0.0" + } + }, "node_modules/vitest": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.0.tgz", diff --git a/package.json b/package.json index 9b99e1b8..15150e0b 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "scripts": { "build": "vite build", "check": "npm run fmt:check && npm run lint", - "dev": "concurrently \"vite\" \"npx tsx --env-file=.env --watch src/server/index.ts\"", + "dev": "agent-tail run 'vite: vite' 'api: npx tsx --env-file=.env --watch src/server/index.ts'", "fix": "npm run lint:fix && npm run fmt", "fmt": "oxfmt src/", "fmt:check": "oxfmt --check src/", @@ -68,6 +68,7 @@ "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@types/supertest": "^7.2.0", + "agent-tail": "^0.4.0", "concurrently": "^9.2.1", "drizzle-kit": "^0.31.10", "oxfmt": "^0.43.0", diff --git a/src/server/interview.test.ts b/src/server/interview.test.ts index cd4bbd72..c538b715 100644 --- a/src/server/interview.test.ts +++ b/src/server/interview.test.ts @@ -224,7 +224,7 @@ describe('conductTurn with interview config', () => { expect(mockStream).toHaveBeenCalled(); const callArgs = mockStream.mock.calls[0][0]; - expect(callArgs.tool_choice).toEqual({ type: 'tool', name: 'ask_question' }); + expect(callArgs.tool_choice).toEqual({ type: 'auto' }); expect(callArgs.tools).toBeDefined(); expect(callArgs.tools[0].name).toBe('ask_question'); }); diff --git a/src/server/interview.ts b/src/server/interview.ts index 4c58868a..e9012202 100644 --- a/src/server/interview.ts +++ b/src/server/interview.ts @@ -148,11 +148,12 @@ export async function* runInterviewer( const stream = client.messages.stream({ model: process.env.ANTHROPIC_MODEL || 'claude-sonnet-4-20250514', - max_tokens: 4096, + max_tokens: 16000, + thinking: { type: 'enabled', budget_tokens: 10000 }, system: getSystemPrompt(phase), messages: [{ role: 'user', content: fullPrompt }], tools: [ASK_QUESTION_TOOL], - tool_choice: { type: 'tool', name: 'ask_question' }, + tool_choice: { type: 'auto' }, }); for await (const rawEvent of stream) { diff --git a/vite.config.ts b/vite.config.ts index 9525e3d6..a7d6eaec 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -2,12 +2,13 @@ import { dirname, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; import tailwindcss from '@tailwindcss/vite'; import react from '@vitejs/plugin-react'; +import { agentTail } from 'agent-tail/vite'; import { defineConfig } from 'vite'; const __dirname = dirname(fileURLToPath(import.meta.url)); export default defineConfig({ - plugins: [react(), tailwindcss()], + plugins: [react(), tailwindcss(), agentTail()], resolve: { alias: { '@': resolve(__dirname, './src/client'), From 0727c394b8a07edd6293e444f96a006174ac4acc Mon Sep 17 00:00:00 2001 From: Lu Nelson Date: Thu, 2 Apr 2026 18:22:43 +0200 Subject: [PATCH 07/31] =?UTF-8?q?docs:=20handoff=20=E2=80=94=20SDK=20migra?= =?UTF-8?q?tion=20complete,=20live=20rendering=20regression=20open?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Amp-Thread-ID: https://ampcode.com/threads/T-019d4ecf-de8a-7258-b074-08887c6a66bf Co-authored-by: Amp --- HANDOFF.md | 124 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 HANDOFF.md diff --git a/HANDOFF.md b/HANDOFF.md new file mode 100644 index 00000000..650dcdfe --- /dev/null +++ b/HANDOFF.md @@ -0,0 +1,124 @@ +# Handoff + +> Generated by `ln-handoff` at 2026-04-02. Read this file to resume work. + +## Goal + +Ship the brunch spec elicitation MVP. SDK migration to raw Anthropic SDK is complete (D30). Agent loop (D31) is spec'd and planned. Live streaming regression remains — structured turn card doesn't render during the SSE stream (only on page refresh). + +## Session State + +- **Last completed skill**: `ln-build` — slice 6b (SDK migration) landed, 137 tests pass +- **Current skill**: `ln-handoff` +- **Flow position**: `grill → spec → plan → scope → build✓ → review✓ → spike✓ → build✓(6b) → review✓ → spec✓(D31) → plan✓(6c) → handoff` +- **Handoff trigger**: session wrapping up after SDK migration + outer-loop testing surfaced a live-rendering regression + +## In-flight work + +### Live streaming regression (NOT FIXED — critical for next session) + +**Symptom**: During live SSE streaming, the structured turn card (question + options + impact + why) does not render. The thinking section streams but then the UI hangs. On page refresh (hydration from persisted `assistant_parts`), everything renders correctly — thinking collapsible, question, options with "Recommended" marker, impact badge. + +**Root cause**: With `tool_choice: 'auto'` + `thinking: { type: 'enabled' }`, the model emits thinking tokens then a `tool_use` content block. The SSE adapter correctly translates these to `reasoning-start/delta/end` + `tool-call-streaming-start/delta/tool-call` events. But the client-side `useChat` + AI Elements rendering does NOT render tool-call-only assistant messages as visible structured turn cards during the live stream. The thinking section renders (reasoning parts work), but when the tool call arrives, the UI doesn't convert the streamed tool args into the visible turn card with options — that only happens on hydration from the persisted turn data. + +**Key constraint discovered**: Anthropic API rejects `thinking: { type: 'enabled' }` combined with `tool_choice: { type: 'tool', name: '...' }`. These are mutually exclusive. So we switched to `tool_choice: 'auto'` which allows thinking but doesn't guarantee the tool call. The system prompt's instruction ("you MUST use the ask_question tool") has been reliable in practice. + +**Evidence**: +- `curl` to `/api/projects/:id/chat` shows correct SSE: `reasoning-start` → `reasoning-delta`(n) → `reasoning-end` → `tool-call-streaming-start` → `tool-call-delta`(n) → `tool-call` → `finish-step` → `finish` → `[DONE]` +- DB confirms structured data persisted correctly (turn.question, turn.why, turn.impact, options with is_recommended) +- Page refresh renders everything correctly from `assistant_parts` JSON +- No console errors (client or server). agent-tail logs clean. + +**What needs fixing**: The client-side rendering path for live-streamed tool calls needs to either (a) render tool-call parts as structured turn cards during the stream, or (b) trigger a re-fetch/invalidation after the stream ends to hydrate from persisted data. Option (b) is the simpler path and matches the existing status-based invalidation pattern used for the entity sidebar. + +### Review findings (from ln-review of slice 6b) + +| # | Finding | Category | Impact | Status | +| --- | ------- | -------- | ------ | ------ | +| 1 | Client instantiated per call, not per request | depth | medium | **Deferred** — acceptable until agent loop lands (6c becomes the seam) | +| 2 | `interview.test.ts` doesn't mock `create()` for observer | coupling | low | **Deferred** — observer has dedicated tests; observer failure is non-fatal by design | +| 3 | `persistStructuredQuestion` coupled to stream-side JSON accumulation | depth | low | **No action** — current approach correct for raw SDK (no in-stream handler) | +| 4 | Observer JSON extraction fragile to code-fence variations | oracle-coverage | medium | **Fixed** — test added for `` ```json `` wrapped output | +| 5 | No test validates A27 (Zod/JSON schema sync) | oracle-coverage | medium | **Fixed** — test validates fixture against both JSON schema shape and Zod parse | +| 6 | `RawStreamEvent` interface unused as type guard | depth | low | **No action** — documents shape, translator handles unknown input gracefully | +| 7 | No naming misalignments with SPEC.md lexicon | naming | none | Clean | + +### Oracle advisory audit (from referenced thread T-019d4eaf) + +| # | Oracle Recommendation | Status | +|---|---|---| +| 1 | Commit to raw SDK, remove Agent SDK | ✅ Done | +| 2 | Forced `tool_choice` to `ask_question` | ⚠️ Changed to `auto` — forced is incompatible with thinking | +| 3 | Hand-written tool schemas | ✅ Done | +| 4 | Simplified observer via `client.messages.create()` | ✅ Done | +| 5 | Thin local seam wrappers | ⚠️ Partial — `createAnthropicClient()` exists but no `streamMessage()`/`createMessage()` wrappers. Defer to agent loop (6c). | +| 6 | Metrics from raw API usage + wall-clock | ✅ Done | +| 7 | Retain `@modelcontextprotocol/sdk` | ✅ N/A | +| 8 | Defer generic agent loop | ✅ Spec'd as D31, planned as slice 6c | +| 9 | Translator normalization (remove envelope) | ✅ Done | +| 10 | Scope + build migration | ✅ Done | +| 11 | Update test mocks | ✅ Done | +| 12 | Outer-loop manual test | ✅ Done — surfaced the live rendering regression | + +### Diagnostic evidence + +- **`tool_choice` + thinking incompatibility**: `curl` with `tool_choice: { type: 'tool', name: 'ask_question' }` + `thinking: { type: 'enabled' }` returns 400: `"Thinking may not be enabled when tool_choice forces tool use."` This is an Anthropic API constraint, not a brunch bug. +- **SSE format verified correct**: `curl -N` to `/api/projects/:id/chat` shows AI SDK-conformant events in correct order. The SSE adapter is not the problem. +- **DB persistence verified**: `sqlite3 brunch.db` queries confirm structured question data (question, why, impact, options with is_recommended) persists correctly on every test turn. +- **Hydration path verified**: Page refresh renders thinking collapsible + full turn card with options. The `assistant_parts` JSON is correct and the hydration code works. +- **Live stream rendering broken**: During the stream, thinking renders but tool-call events don't produce visible UI. The stream completes (network 200) but the turn card only appears after refresh. + +## Decisions and assumptions + +| Item | Type | Status | Source | +| --- | --- | --- | --- | +| D30 raw Anthropic SDK | decision | **persisted** | SPEC.md §Decisions | +| D31 custom agent loop (pi-mono) | decision | **persisted** | SPEC.md §Decisions | +| A26 raw SDK tool_choice forcing | assumption | **validated** (but see note) | SPEC.md §Assumptions | +| A27 JSON/Zod schema sync | assumption | high, test added | SPEC.md §Assumptions | +| A28 thin agent loop sufficient | assumption | high, untested | SPEC.md §Assumptions | +| thinking + forced tool_choice incompatible | discovery | **volatile** — not in SPEC.md | This conversation | +| tool_choice: auto reliable with system prompt | observation | **volatile** — tested once, not formalized | This conversation | + +**Note on A26**: A26 says forced `tool_choice` works. It does — but it's incompatible with thinking. We switched to `tool_choice: 'auto'` to get thinking back. A26 is still technically validated but the use case changed. Consider updating A26 or adding a new assumption. + +## Repo state + +- **Branch**: `ln/fe-559-migrate-sdk` +- **Stack**: `main → ... → fe-538-entity-sidebar → fe-559-migrate-sdk` +- **Recent commits**: + - `9faf5d6` fix: enable thinking + tool_choice auto, add agent-tail for dev logs + - `a838409` test: oracle coverage for A27 schema sync and observer code-fence stripping + - `f2121da` docs: spec D31 agent loop + plan slice 6c (pi-mono reference) + - `1011d80` docs: traceability for SDK migration slice (6b, FE-559) + - `a9bab3e` feat: replace claude agent sdk with raw anthropic sdk (D30) +- **Dirty files**: `HANDOFF.md` (this file) +- **Test status**: `npm run verify` — 137/137 pass, build clean +- **Dependencies**: `@anthropic-ai/sdk` (kept), `@anthropic-ai/claude-agent-sdk` (removed), `agent-tail` (added as devDep) +- **Linear issue**: FE-559 + +## Artifact status + +| Artifact | Exists | Current vs conversation | +| --- | --- | --- | +| memory/SPEC.md | yes | **Current** — D30, D31, A26-A28 persisted. Thinking/tool_choice constraint NOT recorded. | +| memory/PLAN.md | yes | **Current** — slice 6b done, 6c planned with pi-mono reference. | +| memory/REFACTOR.md | no | N/A | + +## Next steps + +1. **Fix live streaming regression** — The turn card must render during the SSE stream, not only on refresh. Investigate the client-side rendering path: does `useChat` expose tool-call parts? Do AI Elements render `tool-invocation` parts? Simplest fix may be triggering a refetch after stream completion (matches entity sidebar pattern). This is a **blocker** for the outer-loop experience. +2. **Update SPEC.md with thinking/tool_choice constraint** — Record the incompatibility as a decision or assumption. Update A26 to note the limitation. +3. **Scope slice 6c** (`ln-scope`) — Agent loop. The live rendering fix and agent loop may interact — the loop will change how tool calls are processed, which affects what events the client sees. +4. **Build slice 6c** (`ln-build`) — Implement agent loop modeled after pi-mono. +5. **Scope slice 7** (`ln-scope`) — Phase transitions, which depend on the agent loop. + +## Open questions + +1. **Live rendering fix approach** — Should the client render tool-call parts live (complex, requires mapping streaming tool args to structured UI), or refetch on stream end (simple, matches existing patterns)? The refetch approach introduces a flash (blank → rendered) but is far simpler. +2. **`tool_choice: 'auto'` reliability** — The system prompt says "you MUST use the ask_question tool." This worked in 4/4 manual tests. But with `auto`, the model *could* respond with text instead. How should the agent loop handle this? Retry? Accept text? The pi-mono pattern handles this naturally (loop continues on `tool_use` stop, exits on `end_turn`). +3. **Thinking budget tuning** — Currently `budget_tokens: 10000` with `max_tokens: 16000`. No data on whether this is optimal. May need tuning per phase. + +## Resume prompt + +> Read `HANDOFF.md` in the project root. The SDK migration (D30) is complete and the agent loop (D31) is spec'd. There is a **live streaming regression**: structured turn cards don't render during the SSE stream (only on page refresh). Fix this first — it's blocking the outer-loop experience. Then scope and build slice 6c (agent loop, pi-mono reference). Start by investigating the client-side rendering path for tool-call events during live streaming. From 92c8de08542895f1a42bebe77ef1cbe809ccb1ef Mon Sep 17 00:00:00 2001 From: Lu Nelson Date: Thu, 2 Apr 2026 18:41:19 +0200 Subject: [PATCH 08/31] dev tooling --- .mcp.json | 11 ++ AGENTS.md | 1 + docs/praxis/dev-server-logs.md | 61 ++++++++++ package-lock.json | 200 ++++++++++++++++++++++++++++++++- package.json | 5 +- 5 files changed, 274 insertions(+), 4 deletions(-) create mode 100644 .mcp.json create mode 100644 docs/praxis/dev-server-logs.md diff --git a/.mcp.json b/.mcp.json new file mode 100644 index 00000000..bd98b4ff --- /dev/null +++ b/.mcp.json @@ -0,0 +1,11 @@ +{ + "mcpServers": { + "shadcn": { + "command": "npx", + "args": [ + "shadcn@latest", + "mcp" + ] + } + } +} diff --git a/AGENTS.md b/AGENTS.md index 3e68225d..53325519 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -68,3 +68,4 @@ Read these before the relevant activity: - **`docs/praxis/graphite-workflow.md`** — before creating branches, submitting PRs, or reintegrating parallel work - **`docs/praxis/worktree-agents.md`** — before spawning parallel agent builds with `isolation: "worktree"` - **`docs/praxis/manual-testing.md`** — before outer-loop UI testing or fixture capture +- **`docs/praxis/dev-server-logs.md`** — before reading runtime logs from the dev server or browser diff --git a/docs/praxis/dev-server-logs.md b/docs/praxis/dev-server-logs.md new file mode 100644 index 00000000..4b885ded --- /dev/null +++ b/docs/praxis/dev-server-logs.md @@ -0,0 +1,61 @@ +# Dev Server & Debugging Tools + +Runtime tooling for observing the running application — logs, database inspection, and process management. + +## How it works + +The `npm run dev` script wraps two services with `agent-tail run`: + +- **`vite`** — frontend dev server → `tmp/logs/latest/vite.log` +- **`api`** — backend server → `tmp/logs/latest/api.log` + +The Vite plugin (`agentTail()` in `vite.config.ts`) captures browser `console.*` calls → `tmp/logs/latest/browser.log`. + +All output is also interleaved in `tmp/logs/latest/combined.log`. + +## Important + +The dev server **must** be started via `npm run dev` for server-side logs to be captured. If Vite is started directly (e.g. bare `vite`), only `browser.log` will exist — `vite.log`, `api.log`, and `combined.log` will be missing. + +## Orphan process cleanup + +The `dev` script kills any orphaned processes on ports 5173 (Vite) and 3000 (API) before starting. This prevents `EADDRINUSE` errors when a previous dev session was killed without clean shutdown (e.g. agent timeout, force-quit, crashed terminal). + +If you hit port conflicts outside `npm run dev`, kill orphans manually: + +```bash +lsof -ti:5173 | xargs kill -9 2>/dev/null +lsof -ti:3000 | xargs kill -9 2>/dev/null +``` + +## Reading logs + +Use `Read` or `Grep` against `tmp/logs/latest/`: + +```bash +# Check for server errors +grep -i error tmp/logs/latest/vite.log +grep -i error tmp/logs/latest/api.log + +# Browser console output +cat tmp/logs/latest/browser.log + +# Everything interleaved +cat tmp/logs/latest/combined.log +``` + +## Session history + +Each `npm run dev` invocation creates a timestamped session directory under `tmp/logs/`. The `latest` symlink always points to the most recent session. Older sessions remain available for comparison. + +## Drizzle Studio (database inspector) + +Browse and edit the SQLite database visually: + +```bash +npm run studio +``` + +Opens `https://local.drizzle.studio` in the browser. Reads from `brunch.db` (or `$BRUNCH_DB`) — the same file the API server writes to. Config lives in `drizzle.config.ts`. + +Note: tests use in-memory databases, so test data won't appear in Studio. Only data from actual dev server sessions (`npm run dev`) is visible. diff --git a/package-lock.json b/package-lock.json index 7669f380..ed8b8d9c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,7 +34,6 @@ "radix-ui": "^1.4.3", "react": "^19.2.4", "react-dom": "^19.2.4", - "shadcn": "^4.1.2", "shiki": "^4.0.2", "streamdown": "^2.5.0", "tailwind-merge": "^3.5.0", @@ -55,6 +54,7 @@ "oxfmt": "^0.43.0", "oxlint": "^1.58.0", "oxlint-tsgolint": "^0.19.0", + "shadcn": "^4.1.2", "supertest": "^7.2.2", "tsx": "^4.21.0", "typescript": "^5.9.3", @@ -232,6 +232,7 @@ "version": "7.27.3", "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.27.3" @@ -260,6 +261,7 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.6.tgz", "integrity": "sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", @@ -290,6 +292,7 @@ "version": "7.28.5", "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/traverse": "^7.28.5", @@ -333,6 +336,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.27.1" @@ -354,6 +358,7 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz", "integrity": "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-member-expression-to-functions": "^7.28.5", @@ -371,6 +376,7 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/traverse": "^7.27.1", @@ -439,6 +445,7 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" @@ -454,6 +461,7 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" @@ -469,6 +477,7 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.28.6.tgz", "integrity": "sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-transforms": "^7.28.6", @@ -515,6 +524,7 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.6.tgz", "integrity": "sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", @@ -534,6 +544,7 @@ "version": "7.28.5", "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.28.5.tgz", "integrity": "sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", @@ -664,6 +675,7 @@ "version": "1.59.1", "resolved": "https://registry.npmjs.org/@dotenvx/dotenvx/-/dotenvx-1.59.1.tgz", "integrity": "sha512-Qg+meC+XFxliuVSDlEPkKnaUjdaJKK6FNx/Wwl2UxhQR8pyPIuLhMavsF7ePdB9qFZUWV1jEK3ckbJir/WmF4w==", + "dev": true, "license": "BSD-3-Clause", "dependencies": { "commander": "^11.1.0", @@ -687,6 +699,7 @@ "version": "11.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=16" @@ -696,6 +709,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", @@ -719,6 +733,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -731,6 +746,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, "license": "Apache-2.0", "engines": { "node": ">=10.17.0" @@ -740,6 +756,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -752,6 +769,7 @@ "version": "3.1.5", "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz", "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==", + "dev": true, "license": "BlueOak-1.0.0", "engines": { "node": ">=18" @@ -761,6 +779,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.0.0" @@ -773,6 +792,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, "license": "MIT", "dependencies": { "mimic-fn": "^2.1.0" @@ -788,12 +808,14 @@ "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, "license": "ISC" }, "node_modules/@dotenvx/dotenvx/node_modules/strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -803,6 +825,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, "license": "ISC", "dependencies": { "isexe": "^3.1.1" @@ -825,6 +848,7 @@ "version": "0.2.6", "resolved": "https://registry.npmjs.org/@ecies/ciphers/-/ciphers-0.2.6.tgz", "integrity": "sha512-patgsRPKGkhhoBjETV4XxD0En4ui5fbX0hzayqI3M8tvNMGUoUvmyYAIWwlxBc1KX5cturfqByYdj5bYGRpN9g==", + "dev": true, "license": "MIT", "engines": { "bun": ">=1", @@ -2341,6 +2365,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz", "integrity": "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -2350,6 +2375,7 @@ "version": "5.1.21", "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.21.tgz", "integrity": "sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==", + "dev": true, "license": "MIT", "dependencies": { "@inquirer/core": "^10.3.2", @@ -2371,6 +2397,7 @@ "version": "10.3.2", "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz", "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==", + "dev": true, "license": "MIT", "dependencies": { "@inquirer/ansi": "^1.0.2", @@ -2398,6 +2425,7 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -2412,6 +2440,7 @@ "version": "1.0.15", "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz", "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -2421,6 +2450,7 @@ "version": "3.0.10", "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz", "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -2554,6 +2584,7 @@ "version": "0.41.3", "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.41.3.tgz", "integrity": "sha512-cXu86tF4VQVfwz8W1SPbhoRyHJkti6mjH/XJIxp40jhO4j2k1m4KYrEykxqWPkFF3vrK4rgQppBh//AwyGSXPA==", + "dev": true, "license": "MIT", "dependencies": { "@open-draft/deferred-promise": "^2.2.0", @@ -2735,6 +2766,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", + "dev": true, "license": "MIT", "engines": { "node": "^14.21.3 || >=16" @@ -2747,6 +2779,7 @@ "version": "1.9.7", "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", + "dev": true, "license": "MIT", "dependencies": { "@noble/hashes": "1.8.0" @@ -2762,6 +2795,7 @@ "version": "1.8.0", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true, "license": "MIT", "engines": { "node": "^14.21.3 || >=16" @@ -2774,6 +2808,7 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", @@ -2787,6 +2822,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -2796,6 +2832,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", @@ -2809,12 +2846,14 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", + "dev": true, "license": "MIT" }, "node_modules/@open-draft/logger": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz", "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", + "dev": true, "license": "MIT", "dependencies": { "is-node-process": "^1.2.0", @@ -2825,6 +2864,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", + "dev": true, "license": "MIT" }, "node_modules/@opentelemetry/api": { @@ -5457,6 +5497,7 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", + "dev": true, "license": "MIT" }, "node_modules/@shikijs/core": { @@ -5563,6 +5604,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -6102,6 +6144,7 @@ "version": "0.27.0", "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.27.0.tgz", "integrity": "sha512-Wf29UqxWDpc+i61k3oIOzcUfQt79PIT9y/MWfAGlrkjg6lBC1hwDECLXPVJAhWjiGbfBCxZd65F/LIZF3+jeJQ==", + "dev": true, "license": "MIT", "dependencies": { "fast-glob": "^3.3.3", @@ -6627,6 +6670,7 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.6.tgz", "integrity": "sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==", + "dev": true, "license": "MIT" }, "node_modules/@types/superagent": { @@ -6670,6 +6714,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/validate-npm-package-name/-/validate-npm-package-name-4.0.2.tgz", "integrity": "sha512-lrpDziQipxCEeK5kWxvljWYhUvOiB2A9izZd9B2AFarYAkqZshb4lPbRs7zKEic6eGtH8V/2qJW+dPp9OtF6bw==", + "dev": true, "license": "MIT" }, "node_modules/@ungap/structured-clone": { @@ -6869,6 +6914,7 @@ "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, "license": "MIT", "engines": { "node": ">= 14" @@ -6976,6 +7022,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -6985,6 +7032,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -7000,6 +7048,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, "license": "Python-2.0" }, "node_modules/aria-hidden": { @@ -7035,6 +7084,7 @@ "version": "0.16.1", "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.16.1.tgz", "integrity": "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==", + "dev": true, "license": "MIT", "dependencies": { "tslib": "^2.0.1" @@ -7064,6 +7114,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, "license": "MIT", "engines": { "node": "18 || 20 || >=22" @@ -7163,6 +7214,7 @@ "version": "5.0.5", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" @@ -7175,6 +7227,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -7251,6 +7304,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, "license": "MIT", "dependencies": { "run-applescript": "^7.0.0" @@ -7304,6 +7358,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -7460,6 +7515,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, "license": "MIT", "dependencies": { "restore-cursor": "^5.0.0" @@ -7475,6 +7531,7 @@ "version": "2.9.2", "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -7487,6 +7544,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, "license": "ISC", "engines": { "node": ">= 12" @@ -7504,6 +7562,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, "license": "ISC", "dependencies": { "string-width": "^4.2.0", @@ -7543,12 +7602,14 @@ "version": "13.0.3", "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.3.tgz", "integrity": "sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==", + "dev": true, "license": "MIT" }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -7561,6 +7622,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, "license": "MIT" }, "node_modules/combined-stream": { @@ -7590,6 +7652,7 @@ "version": "14.0.3", "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "dev": true, "license": "MIT", "engines": { "node": ">=20" @@ -7741,6 +7804,7 @@ "version": "9.0.1", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.1.tgz", "integrity": "sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==", + "dev": true, "license": "MIT", "dependencies": { "env-paths": "^2.2.1", @@ -7781,6 +7845,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, "license": "MIT", "bin": { "cssesc": "bin/cssesc" @@ -8320,6 +8385,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "dev": true, "license": "MIT", "engines": { "node": ">= 12" @@ -8380,6 +8446,7 @@ "version": "1.7.2", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==", + "dev": true, "license": "MIT", "peerDependencies": { "babel-plugin-macros": "^3.1.0" @@ -8403,6 +8470,7 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -8412,6 +8480,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz", "integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==", + "dev": true, "license": "MIT", "dependencies": { "bundle-name": "^4.1.0", @@ -8428,6 +8497,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -8440,6 +8510,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -8528,6 +8599,7 @@ "version": "8.0.4", "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" @@ -8546,6 +8618,7 @@ "version": "17.4.0", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.0.tgz", "integrity": "sha512-kCKF62fwtzwYm0IGBNjRUjtJgMfGapII+FslMHIjMR5KTnwEmBmWLDRSnc3XSNP8bNy34tekgQyDT0hr7pERRQ==", + "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -9197,6 +9270,7 @@ "version": "0.4.18", "resolved": "https://registry.npmjs.org/eciesjs/-/eciesjs-0.4.18.tgz", "integrity": "sha512-wG99Zcfcys9fZux7Cft8BAX/YrOJLJSZ3jyYPfhZHqN2E+Ffx+QXBDsv3gubEgPtV6dTzJMSQUwk1H98/t/0wQ==", + "dev": true, "license": "MIT", "dependencies": { "@ecies/ciphers": "^0.2.5", @@ -9226,6 +9300,7 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, "license": "MIT" }, "node_modules/encodeurl": { @@ -9275,6 +9350,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -9284,6 +9360,7 @@ "version": "1.3.4", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, "license": "MIT", "dependencies": { "is-arrayish": "^0.2.1" @@ -9414,6 +9491,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, "license": "BSD-2-Clause", "bin": { "esparse": "bin/esparse.js", @@ -9467,6 +9545,7 @@ "version": "9.6.1", "resolved": "https://registry.npmjs.org/execa/-/execa-9.6.1.tgz", "integrity": "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==", + "dev": true, "license": "MIT", "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", @@ -9585,6 +9664,7 @@ "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -9624,6 +9704,7 @@ "version": "1.20.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, "license": "ISC", "dependencies": { "reusify": "^1.0.4" @@ -9650,6 +9731,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "dev": true, "funding": [ { "type": "github", @@ -9673,6 +9755,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "dev": true, "license": "MIT", "dependencies": { "is-unicode-supported": "^2.0.0" @@ -9694,6 +9777,7 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -9767,6 +9851,7 @@ "version": "4.0.10", "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dev": true, "license": "MIT", "dependencies": { "fetch-blob": "^3.1.2" @@ -9848,6 +9933,7 @@ "version": "11.3.4", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz", "integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==", + "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", @@ -9885,6 +9971,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/fuzzysort/-/fuzzysort-3.1.0.tgz", "integrity": "sha512-sR9BNCjBg6LNgwvxlBd0sBABvQitkLzoVY9MYYROQVX/FvfJ4Mai9LsGhDgd8qYdds0bY77VzYd5iuB+v5rwQQ==", + "dev": true, "license": "MIT" }, "node_modules/gensync": { @@ -9900,6 +9987,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" @@ -9954,6 +10042,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/get-own-enumerable-keys/-/get-own-enumerable-keys-1.0.0.tgz", "integrity": "sha512-PKsK2FSrQCyxcGHsGrLDcK0lx+0Ke+6e8KFFozA9/fIQLhQzPaRvJFdcz7+Axg3jUH/Mq+NI4xa5u/UT2tQskA==", + "dev": true, "license": "MIT", "engines": { "node": ">=14.16" @@ -9979,6 +10068,7 @@ "version": "9.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "dev": true, "license": "MIT", "dependencies": { "@sec-ant/readable-stream": "^0.4.1", @@ -10014,6 +10104,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -10044,6 +10135,7 @@ "version": "16.13.2", "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.13.2.tgz", "integrity": "sha512-5bJ+nf/UCpAjHM8i06fl7eLyVC9iuNAjm9qzkiu2ZGhM0VscSvS6WDPfAwkdkBuoXGM9FJSbKl6wylMwP9Ktig==", + "dev": true, "license": "MIT", "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" @@ -10359,6 +10451,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-4.0.3.tgz", "integrity": "sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==", + "dev": true, "license": "MIT" }, "node_modules/hono": { @@ -10414,6 +10507,7 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, "license": "MIT", "dependencies": { "agent-base": "^7.1.2", @@ -10427,6 +10521,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz", "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==", + "dev": true, "license": "Apache-2.0", "engines": { "node": ">=18.18.0" @@ -10472,6 +10567,7 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, "license": "MIT", "engines": { "node": ">= 4" @@ -10481,6 +10577,7 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, "license": "MIT", "dependencies": { "parent-module": "^1.0.0", @@ -10566,6 +10663,7 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, "license": "MIT" }, "node_modules/is-decimal": { @@ -10582,6 +10680,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, "license": "MIT", "bin": { "is-docker": "cli.js" @@ -10597,6 +10696,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -10606,6 +10706,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -10615,6 +10716,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -10637,6 +10739,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-in-ssh/-/is-in-ssh-1.0.0.tgz", "integrity": "sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==", + "dev": true, "license": "MIT", "engines": { "node": ">=20" @@ -10649,6 +10752,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, "license": "MIT", "dependencies": { "is-docker": "^3.0.0" @@ -10667,6 +10771,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -10679,12 +10784,14 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", + "dev": true, "license": "MIT" }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -10694,6 +10801,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-3.0.0.tgz", "integrity": "sha512-IlsXEHOjtKhpN8r/tRFj2nDyTmHvcfNeu/nrRIcXE17ROeatXchkojffa1SpdqW4cr/Fj6QkEf/Gn4zf6KKvEQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -10724,6 +10832,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-3.1.0.tgz", "integrity": "sha512-rbku49cWloU5bSMI+zaRaXdQHXnthP6DZ/vLnfdSKyL4zUzuWnomtOEiZZOd+ioQ+avFo/qau3KPTc7Fjy1uPA==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -10736,6 +10845,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -10748,6 +10858,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -10760,6 +10871,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "dev": true, "license": "MIT", "dependencies": { "is-inside-container": "^1.0.0" @@ -10814,6 +10926,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -10838,6 +10951,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, "license": "MIT" }, "node_modules/json-schema": { @@ -10881,6 +10995,7 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, "license": "MIT", "dependencies": { "universalify": "^2.0.0" @@ -10923,6 +11038,7 @@ "version": "4.1.5", "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -11216,6 +11332,7 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, "license": "MIT" }, "node_modules/lodash-es": { @@ -11228,6 +11345,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "dev": true, "license": "MIT", "dependencies": { "chalk": "^5.3.0", @@ -11244,6 +11362,7 @@ "version": "5.6.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" @@ -11256,6 +11375,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -11655,12 +11775,14 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, "license": "MIT" }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -12362,6 +12484,7 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, "license": "MIT", "dependencies": { "braces": "^3.0.3", @@ -12375,6 +12498,7 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -12425,6 +12549,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -12434,6 +12559,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -12458,6 +12584,7 @@ "version": "10.2.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "brace-expansion": "^5.0.5" @@ -12547,6 +12674,7 @@ "version": "2.12.14", "resolved": "https://registry.npmjs.org/msw/-/msw-2.12.14.tgz", "integrity": "sha512-4KXa4nVBIBjbDbd7vfQNuQ25eFxug0aropCQFoI0JdOBuJWamkT1yLVIWReFI8SiTRc+H1hKzaNk+cLk2N9rtQ==", + "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -12591,6 +12719,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -12604,12 +12733,14 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "dev": true, "license": "MIT" }, "node_modules/mute-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "dev": true, "license": "ISC", "engines": { "node": "^18.17.0 || >=20.5.0" @@ -12795,6 +12926,7 @@ "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", "deprecated": "Use your platform's native DOMException instead", + "dev": true, "funding": [ { "type": "github", @@ -12814,6 +12946,7 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dev": true, "license": "MIT", "dependencies": { "data-uri-to-buffer": "^4.0.0", @@ -12838,6 +12971,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", + "dev": true, "license": "MIT", "dependencies": { "path-key": "^4.0.0", @@ -12854,6 +12988,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -12887,6 +13022,7 @@ "version": "1.1.33", "resolved": "https://registry.npmjs.org/object-treeify/-/object-treeify-1.1.33.tgz", "integrity": "sha512-EFVjAYfzWqWsBMRHPMAXLCDIJnpMhdWAqR7xG6M6a2cs6PMFpl/+Z20w9zDW4vkxOFfddegBKq9Rehd0bxWE7A==", + "dev": true, "license": "MIT", "engines": { "node": ">= 10" @@ -12928,6 +13064,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, "license": "MIT", "dependencies": { "mimic-function": "^5.0.0" @@ -12960,6 +13097,7 @@ "version": "11.0.0", "resolved": "https://registry.npmjs.org/open/-/open-11.0.0.tgz", "integrity": "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==", + "dev": true, "license": "MIT", "dependencies": { "default-browser": "^5.4.0", @@ -12980,6 +13118,7 @@ "version": "8.2.0", "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "dev": true, "license": "MIT", "dependencies": { "chalk": "^5.3.0", @@ -13003,6 +13142,7 @@ "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -13015,6 +13155,7 @@ "version": "5.6.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" @@ -13027,12 +13168,14 @@ "version": "10.6.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, "license": "MIT" }, "node_modules/ora/node_modules/string-width": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^10.3.0", @@ -13050,6 +13193,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^6.2.2" @@ -13065,6 +13209,7 @@ "version": "1.4.3", "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz", "integrity": "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==", + "dev": true, "license": "MIT" }, "node_modules/oxfmt": { @@ -13180,6 +13325,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, "license": "MIT", "dependencies": { "callsites": "^3.0.0" @@ -13217,6 +13363,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.0.0", @@ -13235,6 +13382,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -13268,6 +13416,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, "license": "MIT" }, "node_modules/path-data-parser": { @@ -13387,6 +13536,7 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", + "dev": true, "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -13418,6 +13568,7 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.1.0.tgz", "integrity": "sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==", + "dev": true, "license": "MIT", "engines": { "node": ">=20" @@ -13457,6 +13608,7 @@ "version": "9.3.0", "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.0.tgz", "integrity": "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==", + "dev": true, "license": "MIT", "dependencies": { "parse-ms": "^4.0.0" @@ -13472,6 +13624,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, "license": "MIT", "dependencies": { "kleur": "^3.0.3", @@ -13485,6 +13638,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -13542,6 +13696,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, "funding": [ { "type": "github", @@ -13800,6 +13955,7 @@ "version": "0.23.11", "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.11.tgz", "integrity": "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==", + "dev": true, "license": "MIT", "dependencies": { "ast-types": "^0.16.1", @@ -14027,6 +14183,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -14045,6 +14202,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -14064,6 +14222,7 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, "license": "MIT", "dependencies": { "onetime": "^7.0.0", @@ -14080,12 +14239,14 @@ "version": "0.10.1", "resolved": "https://registry.npmjs.org/rettime/-/rettime-0.10.1.tgz", "integrity": "sha512-uyDrIlUEH37cinabq0AX4QbgV4HbFZ/gqoiunWQ1UqBtRvTTytwhNYjE++pO/MjPTZL5KQCf2bEoJ/BJNVQ5Kw==", + "dev": true, "license": "MIT" }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, "license": "MIT", "engines": { "iojs": ">=1.0.0", @@ -14174,6 +14335,7 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -14186,6 +14348,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, "funding": [ { "type": "github", @@ -14338,6 +14501,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/shadcn/-/shadcn-4.1.2.tgz", "integrity": "sha512-qNQcCavkbYsgBj+X09tF2bTcwRd8abR880bsFkDU2kMqceMCLAm5c+cLg7kWDhfh1H9g08knpQ5ZEf6y/co16g==", + "dev": true, "license": "MIT", "dependencies": { "@babel/core": "^7.28.0", @@ -14383,6 +14547,7 @@ "version": "3.25.76", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -14586,6 +14751,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, "license": "ISC", "engines": { "node": ">=14" @@ -14643,12 +14809,14 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, "license": "MIT" }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -14711,6 +14879,7 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -14763,6 +14932,7 @@ "version": "0.5.1", "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz", "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==", + "dev": true, "license": "MIT" }, "node_modules/string_decoder": { @@ -14778,6 +14948,7 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -14806,6 +14977,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-5.0.0.tgz", "integrity": "sha512-zaJYxz2FtcMb4f+g60KsRNFOpVMUyuJgA51Zi5Z1DOTC3S59+OQiVOzE9GZt0x72uBGWKsQIuBKeF9iusmKFsg==", + "dev": true, "license": "BSD-2-Clause", "dependencies": { "get-own-enumerable-keys": "^1.0.0", @@ -14823,6 +14995,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -14835,6 +15008,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -14844,6 +15018,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -14967,6 +15142,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz", "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==", + "dev": true, "license": "MIT", "engines": { "node": ">=20" @@ -15048,6 +15224,7 @@ "version": "1.3.3", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "dev": true, "license": "MIT" }, "node_modules/tinybench": { @@ -15106,6 +15283,7 @@ "version": "7.0.27", "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.27.tgz", "integrity": "sha512-I4FZcVFcqCRuT0ph6dCDpPuO4Xgzvh+spkcTr1gK7peIvxWauoloVO0vuy1FQnijT63ss6AsHB6+OIM4aXHbPg==", + "dev": true, "license": "MIT", "dependencies": { "tldts-core": "^7.0.27" @@ -15118,12 +15296,14 @@ "version": "7.0.27", "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.27.tgz", "integrity": "sha512-YQ7uPjgWUibIK6DW5lrKujGwUKhLevU4hcGbP5O6TcIUb+oTjJYJVWPS4nZsIHrEEEG6myk/oqAJUEQmpZrHsg==", + "dev": true, "license": "MIT" }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -15145,6 +15325,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz", "integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==", + "dev": true, "license": "BSD-3-Clause", "dependencies": { "tldts": "^7.0.5" @@ -15202,6 +15383,7 @@ "version": "26.0.0", "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-26.0.0.tgz", "integrity": "sha512-ztMO++owQnz8c/gIENcM9XfCEzgoGphTv+nKpYNM1bgsdOVC/jRZuEBf6N+mLLDNg68Kl+GgUZfOySaRiG1/Ug==", + "dev": true, "license": "MIT", "dependencies": { "@ts-morph/common": "~0.27.0", @@ -15212,6 +15394,7 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, "license": "MIT", "dependencies": { "json5": "^2.2.2", @@ -15273,6 +15456,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.5.0.tgz", "integrity": "sha512-PlBfpQwiUvGViBNX84Yxwjsdhd1TUlXr6zjX7eoirtCPIr08NAmxwa+fcYBTeRQxHo9YC9wwF3m9i700sHma8g==", + "dev": true, "license": "(MIT OR CC0-1.0)", "dependencies": { "tagged-tag": "^1.0.0" @@ -15302,7 +15486,7 @@ "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -15329,6 +15513,7 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -15456,6 +15641,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, "license": "MIT", "engines": { "node": ">= 10.0.0" @@ -15474,6 +15660,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/until-async/-/until-async-3.0.2.tgz", "integrity": "sha512-IiSk4HlzAMqTUseHHe3VhIGyuFmN90zMTpD3Z3y8jeQbzLIq500MVM7Jq2vUAnTKAFPJrqwkzr6PoTcPhGcOiw==", + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/kettanaito" @@ -15593,6 +15780,7 @@ "version": "7.0.2", "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-7.0.2.tgz", "integrity": "sha512-hVDIBwsRruT73PbK7uP5ebUt+ezEtCmzZz3F59BSr2F6OVFnJ/6h8liuvdLrQ88Xmnk6/+xGGuq+pG9WwTuy3A==", + "dev": true, "license": "ISC", "engines": { "node": "^20.17.0 || >=22.9.0" @@ -15881,6 +16069,7 @@ "version": "3.3.3", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -15922,6 +16111,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -15945,6 +16135,7 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.3.1.tgz", "integrity": "sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==", + "dev": true, "license": "MIT", "dependencies": { "is-wsl": "^3.1.0", @@ -15961,6 +16152,7 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, "license": "ISC", "engines": { "node": ">=10" @@ -15976,6 +16168,7 @@ "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, "license": "MIT", "dependencies": { "cliui": "^8.0.1", @@ -15994,6 +16187,7 @@ "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -16003,6 +16197,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -16015,6 +16210,7 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" diff --git a/package.json b/package.json index 15150e0b..00d21548 100644 --- a/package.json +++ b/package.json @@ -15,13 +15,14 @@ "scripts": { "build": "vite build", "check": "npm run fmt:check && npm run lint", - "dev": "agent-tail run 'vite: vite' 'api: npx tsx --env-file=.env --watch src/server/index.ts'", + "dev": "agent-tail run 'vite: lsof -ti:5173 | xargs kill -9 2>/dev/null; vite' 'api: lsof -ti:3000 | xargs kill -9 2>/dev/null; npx tsx --env-file=.env --watch src/server/index.ts'", "fix": "npm run lint:fix && npm run fmt", "fmt": "oxfmt src/", "fmt:check": "oxfmt --check src/", "lint": "oxlint --type-aware --type-check src/", "lint:fix": "oxlint --type-aware --type-check --fix src/", "server": "npx tsx --env-file=.env src/server/index.ts", + "studio": "drizzle-kit studio", "test": "vitest run", "verify": "npm run check && npm run test && npm run build" }, @@ -53,7 +54,6 @@ "radix-ui": "^1.4.3", "react": "^19.2.4", "react-dom": "^19.2.4", - "shadcn": "^4.1.2", "shiki": "^4.0.2", "streamdown": "^2.5.0", "tailwind-merge": "^3.5.0", @@ -74,6 +74,7 @@ "oxfmt": "^0.43.0", "oxlint": "^1.58.0", "oxlint-tsgolint": "^0.19.0", + "shadcn": "^4.1.2", "supertest": "^7.2.2", "tsx": "^4.21.0", "typescript": "^5.9.3", From 5222f8477dbcdf15153233bd1b3bcb6668438464 Mon Sep 17 00:00:00 2001 From: Lu Nelson Date: Fri, 3 Apr 2026 15:26:53 +0200 Subject: [PATCH 09/31] codex refactor of types and boundaries --- memory/PLAN.md | 31 +- memory/SPEC.md | 66 ++- package-lock.json | 34 ++ package.json | 1 + src/client/components/EntitySidebar.tsx | 8 +- src/client/router.tsx | 24 +- src/client/routes/ComponentDebug.tsx | 67 ++- src/client/routes/InterviewWorkspace.tsx | 150 +++--- src/server/app.test.ts | 567 ++++++----------------- src/server/app.ts | 139 ++++-- src/server/context.test.ts | 4 +- src/server/core.test.ts | 407 ++++------------ src/server/core.ts | 101 ++-- src/server/db.ts | 4 + src/server/interview.test.ts | 338 ++------------ src/server/interview.ts | 189 +++----- src/server/observer.test.ts | 229 ++------- src/server/observer.ts | 60 +-- src/server/parts.test.ts | 249 +++------- src/server/parts.ts | 142 +----- src/server/sdk.test.ts | 89 ---- src/server/sdk.ts | 111 ----- src/server/sse-adapter.test.ts | 77 --- src/server/sse-adapter.ts | 152 ------ src/shared/api-types.ts | 7 + src/shared/chat.ts | 255 ++++++++++ 26 files changed, 1077 insertions(+), 2424 deletions(-) delete mode 100644 src/server/sdk.test.ts delete mode 100644 src/server/sdk.ts delete mode 100644 src/server/sse-adapter.test.ts delete mode 100644 src/server/sse-adapter.ts create mode 100644 src/shared/api-types.ts create mode 100644 src/shared/chat.ts diff --git a/memory/PLAN.md b/memory/PLAN.md index 6743a97e..4309aeaf 100644 --- a/memory/PLAN.md +++ b/memory/PLAN.md @@ -139,13 +139,13 @@ - Branch: `ln/fe-537-observer-agent` - **Verification approach**: inner — unit tests for entity writes with dependency edges, observer-complete DomainEvent emission post-commit, SSE adapter data-part encoding, sdk translateStreamEvents parity, observer-error non-fatality, agent-metrics shape. Middle — differential oracle from spike fixtures (deferred to manual testing). Outer — debug mode and fixture capture (deferred to slice 6). → SPEC.md §Oracle Strategy -6. **Entity sidebar (read-only)** `FE-538` — React sidebar in interview workspace showing decisions and assumptions in categorized tabs. TanStack Query manages entity state via `useQuery`; cache invalidated on chat stream completion (status transition `streaming` → `ready`). Entities API at `GET /api/projects/:id/entities`. Note: `onData` → `setQueryData` bridge from D22 not used — AI SDK `useChat` doesn't expose an `onData` callback for custom data parts; status-based invalidation used instead. Dependency edge display and stale badges deferred (require slices 11/12 infrastructure). `done` +6. **Entity sidebar (read-only)** `FE-538` — React sidebar in interview workspace showing decisions and assumptions in categorized tabs. TanStack Query manages entity state via `useQuery`; observer-result data parts on the chat stream drive cache invalidation from `useChat` `onData`. Entities API at `GET /api/projects/:id/entities`. Dependency edge display and stale badges deferred (require slices 11/12 infrastructure). `done` - Requirements: → SPEC.md §Requirements #6 - - Assumptions: → SPEC.md §Assumptions A21 (partially validated — status-based invalidation works; onData bridge not needed) - - Decisions: → SPEC.md §Decisions D22 (TanStack Query — yes; in-band onData sync — replaced with status-based invalidation) + - Assumptions: → SPEC.md §Assumptions A21 (partially validated — in-band `onData` invalidation path is implemented; browser outer-loop still pending) + - Decisions: → SPEC.md §Decisions D22 - Invariants established: → SPEC.md §Invariants I23 - Invariants respected: → SPEC.md §Invariants I9, I10, I14, I20, I21 - - Acceptance: 149 tests (2 new API tests); entities API returns decisions + assumptions; sidebar renders in categorized tabs; TanStack Query cache invalidated on stream completion; entities appear as interview progresses + - Acceptance: entities API returns decisions + assumptions; sidebar renders in categorized tabs; TanStack Query invalidates entity state from typed `data-observer-result` parts; entities appear as interview progresses - Branch: `ln/fe-538-entity-sidebar` - Ref: → docs/design/BREADBOARD.md §UI Affordances → P2 Entity sidebar - **Verification approach**: inner — unit tests for entity query on active path, stale badge computation. Middle — validate A21: `onData` → `setQueryData` updates sidebar without stale closure (if stale, fall back to parallel `EventSource`). Outer — manual visual inspection (entities render correctly, tabs work, stale badges appear). Debug mode overlay (observer extraction detail per-turn) should land here or in slice 5. → SPEC.md §Oracle Strategy (outer loop), §Acknowledged Blind Spots (cumulative graph integrity) @@ -161,15 +161,15 @@ - Branch: `ln/spike-raw-anthropic-sdk` - **Implication**: Slices 7+ unblocked. New migration slice needed (SDK swap) before phase transitions can proceed. - 6b. **SDK migration: replace Agent SDK with raw Anthropic SDK** `FE-559` — Replace `@anthropic-ai/claude-agent-sdk` (`query()`, `createSdkMcpServer()`, `tool()`) with `@anthropic-ai/sdk` (`client.messages.stream()`, `client.messages.create()`). Hand-written tool JSON schema with `tool_choice` forcing. Observer uses direct API + system-prompt JSON + Zod parse. Translator accepts raw events (no `stream_event` envelope). Metrics from raw API `usage` + local wall-clock timing. `done` + 6b. **AI SDK-native chat pivot** `FE-559` — Replace the remaining hand-written Anthropic stream translation path with `@ai-sdk/anthropic` plus AI SDK-native UI message streaming. Shared `BrunchUIMessage` contracts now span server request validation, persisted `parts[]`, SSE response streaming, and client hydration. Observer results stay in-band as typed data parts. `done` - Requirements: → SPEC.md §Requirements #2, #3, #5 - - Assumptions: → SPEC.md §Assumptions A26 (**validated**), A27 (new) - - Decisions: → SPEC.md §Decisions D30 - - Invariants established: → SPEC.md §Invariants I22 (updated — raw events), I16 (preserved) - - Invariants respected: → SPEC.md §Invariants I1, I5, I6, I12, I13, I20, I21 - - Acceptance: 135 tests pass (all updated mocks); `@anthropic-ai/claude-agent-sdk` removed from `package.json` and all imports; `npm run verify` passes + - Assumptions: → SPEC.md §Assumptions A21, A22, A23 + - Decisions: → SPEC.md §Decisions D8, D19, D22, D30, D31 + - Invariants established: → SPEC.md §Invariants I21, I22, I23 + - Invariants respected: → SPEC.md §Invariants I1, I5, I6, I14, I16, I17, I18, I19, I20 + - Acceptance: `@ai-sdk/anthropic` powers the interviewer and observer path; `sdk.ts` and `sse-adapter.ts` are retired; shared UI message/data-part contracts replace handwritten protocol unions; `npm run verify` passes - Branch: `ln/fe-559-migrate-sdk` - - **Verification approach**: inner — translator raw events (I22 updated), tool handler persistence (I16), observer entity extraction (I20, I21). Middle — `npm run verify`. Outer — manual interview end-to-end (first real `ask_question` via raw SDK). + - **Verification approach**: inner — typed contract and persistence tests (`core.test.ts`, `interview.test.ts`, `observer.test.ts`, `parts.test.ts`); route stream tests (`app.test.ts`). Middle — `npm run verify`. Outer — manual interview end-to-end with typed observer/sidebar sync. ## Phase 4: Full Interview @@ -178,15 +178,14 @@ ### Slices -6c. **Agent loop: stream → tool_use → execute → re-submit** — Implement a thin custom agent loop in `src/server/agent-loop.ts` modeled after `badlogic/pi-mono` `packages/agent/src/agent-loop.ts`. The loop: streams a response via `client.messages.stream()`, checks `stop_reason`, if `tool_use` executes registered tool handlers and re-submits with `tool_result` messages, repeats until `end_turn` or `maxTurns`. Yields `DomainEvent`s during streaming. Tool registry: `{ name, inputSchema, execute }[]`. `runInterviewer` refactored to call `agentLoop()` with phase-specific config. No changes to observer (single-shot, no loop). `not-started` +6c. **Interviewer loop hardening** — Extend the current AI SDK-native interviewer beyond the single visible `ask_question` flow when phase resolution or multi-step tool behavior requires it. Default path is `ToolLoopAgent`; if product needs tighter step control, replace or wrap it with an explicit loop over typed model/tool messages. `not-started` - Requirements: → SPEC.md §Requirements #2, #3, #7 - Assumptions: → SPEC.md §Assumptions A28 - Decisions: → SPEC.md §Decisions D31 - - Invariants to establish: I24 (agent loop re-submission — tool_use stop → tool_result → model sees result) + - Invariants to establish: I24 (multi-step interviewer control path is explicit and tested) - Invariants to respect: → SPEC.md §Invariants I16, I22 - - Acceptance: `agentLoop()` yields correct DomainEvents for single-tool forced call (backward compat with current interviewer); `agentLoop()` re-submits tool results when `stop_reason === 'tool_use'` and stops on `end_turn`; `runInterviewer` passes all existing tests unchanged after refactor to use `agentLoop()`; `npm run verify` passes - - **Verification approach**: inner — unit tests for loop lifecycle (single-turn, multi-turn, maxTurns guard, tool execution, error handling). Middle — existing interview + core + app tests pass unchanged (backward compat). Outer — manual interview end-to-end. - - **Reference implementation**: `~/Clones/badlogic/pi-mono/packages/agent/src/agent-loop.ts` — specifically `runLoop()` (L155-232), `streamAssistantResponse()` (L238-320), `executeToolCalls()` (L322-388). Brunch adapts the inner loop and tool execution; omits steering/follow-up messages, parallel execution, provider abstraction, `convertToLlm`, `beforeToolCall`/`afterToolCall` hooks, AbortSignal. + - Acceptance: interviewer can execute tool steps, continue, and resolve a phase without regressing the shared `BrunchUIMessage` contract; `npm run verify` passes + - **Verification approach**: inner — unit tests for multi-step interviewer lifecycle and phase-resolution paths. Middle — existing interview/app tests stay green. Outer — manual interview end-to-end across a phase boundary. 7. **Phase transition + resolution** — Agent judges when scope phase is complete (`is_resolution`). Core yields `phase-resolved` DomainEvent. Client shows summary modal. User confirms to advance. Phase indicator updates. Requires agent loop (6c) — the agent must call `ask_question` AND optionally signal resolution in the same turn, which requires multi-step tool use or `tool_choice: auto`. `not-started` - Requirements: → SPEC.md §Requirements #7, #8 diff --git a/memory/SPEC.md b/memory/SPEC.md index 71742f3b..2e4a5ff8 100644 --- a/memory/SPEC.md +++ b/memory/SPEC.md @@ -21,10 +21,10 @@ The core data model: The architecture (layered: db → core → adapters): - **Database**: SQLite via Drizzle ORM + `better-sqlite3` — TypeScript schema is single source of truth for types, DDL, and migrations. Auto-applies at startup. -- **Core**: Interface-agnostic service layer — turn tree operations, interview orchestration, entity lifecycle, observer, phase management, export. `conductTurn()` returns `AsyncIterable` for streaming. No transport knowledge. -- **Agent engine**: Raw Anthropic SDK (`@anthropic-ai/sdk`) — `client.messages.stream()` for tool-using agents, `client.messages.create()` for silent extraction. Tools as JSON schemas in `params.tools` with `tool_choice` forcing. Custom agent loop (`agentLoop()`) for multi-step tool use: stream response → check `stop_reason` → if `tool_use`, execute handler, append `tool_result`, re-submit → repeat until `end_turn` or max turns. Modeled after `badlogic/pi-mono` `packages/agent/src/agent-loop.ts` (D31). Each interview phase is an agent skill. Called by core, not by adapters. (D30 — replaces Claude Agent SDK) +- **Core**: Interface-agnostic service layer — turn tree operations, project-state loading, typed prompt/context building, entity lifecycle, observer invocation, phase management, export. No transport knowledge. +- **Agent engine**: AI SDK + Anthropic provider (`ai`, `@ai-sdk/anthropic`) — `ToolLoopAgent` powers the interviewer and `generateObject` powers the observer. Shared `BrunchUIMessage` / data-part contracts span request validation, persistence, server streaming, and client hydration. Future multi-step hardening builds on the AI SDK loop surface rather than a handwritten raw-event translator. (D30, D31) - **Observer agent**: Separate extraction call after each turn — captures decisions, assumptions, and their dependency edges. Invoked by core after turn completion. -- **Web adapter**: Express.js translates `DomainEvent` stream to AI SDK UI Message Stream SSE. React + Vite + `@ai-sdk/react` `useChat` client. +- **Web adapter**: Express.js returns AI SDK UI Message Stream SSE directly via `createUIMessageStream`. React + Vite + `@ai-sdk/react` `useChat` client consume the same typed message contract. - **CLI adapter**: (future) Terminal I/O consuming the same `DomainEvent` stream - **MCP adapter**: (future) MCP server exposing core operations as tools - **Output**: Flattened markdown spec exported on demand from the active path's entities @@ -76,16 +76,16 @@ The architecture (layered: db → core → adapters): | A10 | The `useChat` hook can consume custom SSE without AI SDK server runtime | **validated** | D9 | Walking skeleton | Validated: useChat consumes custom SSE via DefaultChatTransport | | A11 | Stateless `query()` with prompt-stuffed history is sufficient for multi-turn interviewing — SDK session persistence is unnecessary and undesirable | **validated** | D8, D12 | SQLite foundation | Validated: formatting history into prompt works. SDK sessions rejected as competing source of truth — opaque, machine-local, incompatible with portable data goals (atomic YAML / git-versionable). Turn tree is sole session model. | | A12 | `useChat` hook accepts initial messages to hydrate conversation state from server-stored history | **validated** | D9 | SQLite foundation | Validated: `useChat` doesn't have `initialMessages` prop but `setMessages` works for hydration | -| A13 | Phase-specific interview behavior is achievable via system prompt switching + in-process MCP tools on `query()` — the SDK's formal `AgentDefinition` skill system is unnecessary | **validated** | D2 | Interview phases | Validated: slice 4 uses `getSystemPrompt(phase)` + `createInterviewMcpServer()` per turn; 88 tests pass. SDK `AgentDefinition` subagent system not used — simpler approach with less indirection. | +| A13 | Phase-specific interview behavior is achievable via system prompt switching + typed AI SDK tools — a heavier agent-definition framework is unnecessary | **validated** | D2 | Interview phases | Validated: interviewer uses `getSystemPrompt(phase)` plus the typed `ask_question` tool in `interview.ts`; shared schema tests and persistence tests pass. | | A14 | A second-thread observer agent can reliably extract decisions, assumptions, and dependency edges from a single turn's Q&A | **validated** | D1 | Observer agent | Validated (spike): decisions 100% capture, assumptions semantically correct (~80% true semantic overlap). Edges not tested — deferred to slice 5. Use tool-based structured output and faster model (Haiku) in production. | | A15 | The LLM can reliably judge when a phase interview has reached sufficient understanding (is_resolution) | medium | D3 | Phase resolution | Probe across varied project types; measure false-positive resolution rate | | A16 | AI SDK `useChat` hook's `ToolUIPart` state machine (`input-streaming` → `input-available` → `output-available` / `output-error` / `approval-requested` → `approval-responded` / `output-denied`) models all permutations of pending, error, and success for both interim (thinking, tool calls) and final (response) data | high | D14 | Rich chat UI | Partially validated: SSE adapter emits tool-call events, client renders `dynamic-tool` parts with state labels (input-streaming, input-available, output-available, output-error). Browser outer-loop pending. | | A17 | AI Elements copy-paste components can be restyled without forking — they are ownable source files, not npm-locked dependencies | **validated** | D14 | Rich chat UI | Validated: installed via `npx ai-elements@latest add`; components live at `src/client/components/ai-elements/*.tsx` as editable source; `@ai-sdk/react` is devDep only (not bundled); `ai` package provides types. No hidden runtime dependency. | | A18 | Drizzle ORM migration runner reliably auto-applies schema changes from a migrations folder at startup with better-sqlite3 | **validated** | D18 | Drizzle refactor | Validated: migrate() auto-applies at startup in createDb(); all 39 existing tests pass against Drizzle-managed schema | -| A19 | `AsyncIterable` from core can be consumed by both SSE streaming (web) and line-by-line terminal output (CLI) without buffering issues | **validated** | D19 | Core extraction | Validated: conductTurn() yields DomainEvents consumed by Express SSE adapter; 12 new core tests + 9 app integration tests pass | +| A19 | A typed shared UI-message contract can span server validation, persistence, SSE streaming, and client hydration without a separate transport event layer | **validated** | D19 | Core extraction | Validated: `BrunchUIMessage` is used in `app.ts`, `parts.ts`, `InterviewWorkspace.tsx`, and route tests; `npm run verify` passes. | | A20 | Observer results can be delivered as typed data parts on the existing chat SSE stream without holding the connection open unacceptably long — observer is synchronous, runs within the same `conductTurn()` request, completes during user read time | high | D22 | Observer agent, Entity sidebar | Measure observer latency in slice 5; if >5s, fall back to out-of-band SSE (Option 2 in research doc) | | A21 | `useChat` `onData` callback reliably bridges to `queryClient.setQueryData` without stale-closure issues — known `onFinish` stale-closure bug (ai-sdk#550) may or may not affect `onData` | medium | D22 | Entity sidebar | Test in slice 6: verify `setQueryData` from `onData` updates sidebar reactively; if stale, use parallel `EventSource` instead | -| A22 | AI SDK `UIMessage.parts[]` with custom Data Parts (typed via `dataPartsSchema`) persisted as JSON on the turn table is sufficient for faithful UI resume — no separate `turn_message` table needed for current scope | **validated** | D23, D24 | Parts persistence | Validated: parts assembler converts DomainEvents to typed parts, round-trips through JSON persistence (I18). Client hydration from parts deferred to 4b (outer-loop). | +| A22 | AI SDK `UIMessage.parts[]` with custom Data Parts persisted as JSON on the turn table is sufficient for faithful UI resume — no separate `turn_message` table needed for current scope | **validated** | D23, D24 | Parts persistence | Validated: persisted assistant/user parts round-trip through JSON, hydrate back into `BrunchUIMessage`, and pass route/client tests. | | A23 | Custom Data Parts for structured user input (option selection, confirmation) can replace scalar `turn.answer` as the primary user-response model without breaking `formatHistory()` or observer context | **validated** | D24 | Parts persistence | Validated: Data Part schemas defined with Zod (I17), context builders read scalars not parts (I19), structured user input round-trip tested. Full UI wiring deferred to 4b. | | A24 | SDK `outputFormat` with JSON schema produces equally reliable entity extraction as MCP tool-based extraction — structurally simpler (one API call, no tool round-trip), schema validation built into SDK response via `structured_output` field on `SDKResultMessage` | **invalidated** | D28, D30 | Observer agent | Invalidated: Agent SDK `outputFormat` is moot since we're replacing the Agent SDK (D30). Raw API supports structured JSON output directly — spike confirmed `client.messages.create()` returns parseable JSON. Observer can use system-prompt-guided JSON or native structured output API. | | A25 | `SDKResultMessage` provides accurate `duration_ms`, `total_cost_usd`, and `usage` for per-agent observability — types confirmed in TS SDK (`SDKResultSuccess`, `SDKResultError`) | **invalidated** | D29, D30 | Observer agent | Invalidated: Agent SDK `ResultMessage` is moot (D30). Raw API provides `usage` (input/output tokens) on final message and `stop_reason`. Wall-clock duration measured locally. Cost computation requires manual model pricing lookup — deferred. | @@ -133,16 +133,16 @@ The architecture (layered: db → core → adapters): ### Technical stack 7. **SQLite via better-sqlite3** — Zero-config embedded DB. Turn tree, decisions, assumptions, requirements, criteria all in SQLite tables. Schema defined in Drizzle (see D18). Depends on: A5, A6. Supersedes: Dolt (docker-based). -8. **Express.js server emits AI SDK-conformant SSE** — Thin adapter: iterates `DomainEvent` stream from `conductTurn()`, translates each event to AI SDK UI Message Stream protocol via `createDomainAdapter()`. No AI SDK runtime imported server-side. The SDK is called by core, not by Express. Depends on: A1, A2, D19. Supersedes: hand-rolled NDJSON streaming, direct SDK iteration in Express (pre-3c). +8. **Express.js server emits AI SDK UI message streams directly** — The chat route validates incoming `BrunchUIMessage[]`, persists the new turn, merges the interviewer stream into `createUIMessageStream`, emits typed observer-result data parts in-band, and pipes the result to the response. No handwritten stream-translation layer remains on the web path. Depends on: A1, A19, D19. Supersedes: hand-rolled NDJSON and DomainEvent-to-SSE translation. 9. **React + Vite + @ai-sdk/react + @tanstack/react-router client** — `useChat` for conversation streaming. TanStack Router for type-safe routing with route loaders for data fetching on navigation (replaces manual `useEffect` hydration). Three routes for MVP: project list (`/`), interview workspace (`/project/:id`), export preview (`/project/:id/export`). See `docs/design/BREADBOARD.md`. Depends on: A9, A10. Supersedes: Preact, both existing frontends, single-page no-routing layout. 10. **npx-launchable single-command distribution** — `bin` entry, launcher starts Express (serves built Vite assets + API on one port), opens browser. Single env var: `ANTHROPIC_API_KEY`. DB auto-created in project directory or `~/.brunch/`. Depends on: A8. Supersedes: multi-step Docker + env var setup. 11. **Drop list** — Dolt/mysql2, OpenCode sidecar, Preact, both existing frontend implementations, NDJSON protocol, JSON Schema definitions (→ Zod), @tanstack/react-table, @dnd-kit/, dompurify, marked, four streaming functions in claude.js, dispatch.js. Depends on: —. Supersedes: —. 16. **Integer autoincrement primary keys** — All entity tables use `INTEGER PRIMARY KEY AUTOINCREMENT` instead of `TEXT` UUIDs. SQLite ROWID alias is simpler, matches the original DBML design, avoids UUID generation. No external systems reference these IDs. Client coerces to strings for `useChat` hydration (`turn-${id}-answer`, `turn-${id}-question`). Depends on: D7. Supersedes: `randomUUID()` TEXT PKs from slice 2. 18. **Drizzle ORM replaces raw DDL** — TypeScript schema definition (`drizzle/schema.ts`) is single source of truth for types, DDL, and migrations. Auto-applies from `drizzle/migrations/` at startup. Drizzle Studio available for DB inspection during development. Depends on: A18, D7. Supersedes: raw DDL strings in db.ts, DBML design document, hand-written TypeScript interfaces. -19. **Layered architecture with DomainEvent streaming** — Core interview orchestration extracted from Express handlers into interface-agnostic service layer. Core operations: turn tree (createProject, conductTurn, getActivePath, branch, checkout), entity lifecycle (revisitDecision, falsifyAssumption, verifyAssumption, CRUD for requirements/criteria, reviewRequirement/reviewCriterion), observer (runObserver), phase (getPhaseStatus), export (exportSpec). `conductTurn()` returns `AsyncIterable` — domain events (`stream-start`, `thinking`, `text-delta`, `tool-call-start`, `tool-call-delta`, `tool-call-end`, `stream-end`, `turn-created`, `error`, `observer-complete`; future: `phase-resolved`) that each adapter translates to its transport format. `observer-complete` is emitted post-commit (after SQLite transaction) and carries created entity IDs for cache coherence (see D22). Web (Express+SSE), CLI, and MCP adapters are thin transport layers. Depends on: A19, D8, D12. Supersedes: interview logic embedded in Express POST handler. +19. **Layered architecture with an AI SDK-native chat boundary** — Core interview orchestration is split into typed helpers (`prepareTurn`, `finalizeTurn`, context builders, persistence helpers) while Express owns the chat stream composition. The boundary between server and client is `BrunchUIMessage`, not a separate in-house event protocol. Observer-result data stays in-band on the same stream for cache coherence (see D22). CLI and MCP can still derive later from the stabilized domain operations, but the web path optimizes for the typed UI-message contract first. Depends on: A19, D8, D12. Supersedes: interview logic embedded in Express POST handler and the DomainEvent-to-SSE translation layer. 21. **oxlint + oxfmt + tsgolint replaces eslint + tsc** — oxlint for linting (including 59 type-aware rules via tsgolint, the Go-based TypeScript backend), oxfmt for formatting (single quotes, 110 width, sorted imports). `npm run fix` (lint:fix + fmt) is the fast inner loop; `npm run verify` (check + test + build) is the commit gate. `--type-check` flag replaces `tsc --noEmit`. Depends on: —. Supersedes: eslint (removed), separate `tsc --noEmit` step. 20. **CLI executable with subcommands** — `npx brunch` launches web UI (default). `npx brunch [command]` for CLI operations on the same DB. Future: sidecar MCP server. Depends on: D10, D19. Supersedes: web-only distribution model in D10. -22. **TanStack Query + SSE-driven invalidation for observer entity sync** — Observer-created entities (decisions, assumptions, edges) sync to the React UI via two mechanisms: (1) **In-band data parts** (default): `conductTurn()` yields `observer-complete` DomainEvents after the SQLite transaction commits; the Express SSE adapter emits these as typed data parts on the existing chat stream; `useChat`'s `onData` callback bridges to `queryClient.setQueryData` for instant sidebar updates. (2) **Out-of-band SSE** (fallback): if the observer moves to async post-processing, a dedicated `/api/events/:projectId` `EventSource` in a React context drives `queryClient.invalidateQueries`. TanStack Query owns all persisted entity state; a small Zustand store handles transient UI state only (observer-running indicator, phase progress). TanStack DB evaluated and rejected — overkill for server-authoritative single-user app without offline, multi-tab, or complex cross-collection query needs. Research: `docs/research/async-server-state-to-ui-sync-for-chat-observer-agents.md`. Depends on: A20, A21, D4, D9, D19. Supersedes: —. +22. **TanStack Query + in-band observer-result sync** — Observer-created entities sync to the React UI through typed `data-observer-result` parts on the existing chat stream. `useChat`'s `onData` callback invalidates the entity query for the active project; project-state refresh remains route-driven on stream completion. If the observer later becomes async, a dedicated `EventSource` remains the fallback. TanStack Query owns persisted entity state; the chat stream owns transient message state. TanStack DB remains unnecessary for the current server-authoritative model. Research: `docs/research/async-server-state-to-ui-sync-for-chat-observer-agents.md`. Depends on: A20, A21, D4, D9, D19. Supersedes: status-based sidebar refresh workarounds. ## Invariants @@ -154,18 +154,18 @@ The architecture (layered: db → core → adapters): | # | Invariant | Established by | Protected by | Proves | | --- | ---------------------------- | ------------------- | -------------------------------- | ------- | -| I1 | SSE protocol conformance | Slice 1 (skeleton) | sse-adapter.test.ts | D8 | +| I1 | SSE protocol conformance | Slice 1 (skeleton) | app.test.ts | D8 | | I2 | Stream lifecycle correctness | Slice 1 (skeleton) | app.test.ts | D8 | -| I3 | Thinking/text separation | Slice 1 (skeleton) | sse-adapter.test.ts, app.test.ts | D8 | +| I3 | Thinking/text separation | Slice 1 (skeleton) | app.test.ts | D8 | | I4 | Vite proxy routing | Slice 1 (skeleton) | vite.config.ts (manual) | D10 | | I5 | DB lifecycle correctness | Slice 2 (SQLite) | db.test.ts | D7 | | I6 | Turn persistence | Slice 3 (turn tree) | db.test.ts, app.test.ts | D1, D7 | -| I7 | Tool call SSE conformance | Slice 3b (rich UI) | sse-adapter.test.ts | D8, D14 | +| I7 | Tool call SSE conformance | Slice 3b (rich UI) | app.test.ts, manual (outer loop) | D8, D14 | | I8 | Tool part state rendering | Slice 3b (rich UI) | manual (outer loop) | D14 | | I9 | Turn tree parent chain | Slice 3 (turn tree) | db.test.ts | D1 | | I10 | Active path resolution | Slice 3 (turn tree) | db.test.ts | D1 | | I11 | Drizzle migration auto-apply | Slice 3c (Drizzle) | db.test.ts | D18 | -| I12 | DomainEvent streaming | Slice 3c (Drizzle) | core.test.ts | D19 | +| I12 | Typed server chat boundary | Slice 3c (Drizzle) | core.test.ts, app.test.ts | D19 | | I13 | Core/adapter separation | Slice 3c (Drizzle) | core.test.ts, app.test.ts | D19 | | I14 | Project-scoped API routes | Slice 3d (routing) | app.test.ts | D9 | | I15 | Route loader hydration | Slice 3d (routing) | manual (outer loop) | D9 | @@ -174,9 +174,9 @@ The architecture (layered: db → core → adapters): | I18 | Parts round-trip fidelity | Slice 4a (parts persistence) | parts.test.ts (8 tests), core.test.ts | D23 | | I19 | Context builder equivalence | Slice 4a (parts persistence) | context.test.ts (7 tests) | D25 | | I20 | Entity persistence with turn linkage | Slice 5 (observer) | db.test.ts (7 tests), observer.test.ts | D4, D5 | -| I21 | Observer-complete post-commit | Slice 5 (observer) | observer.test.ts (6 tests), sse-adapter.test.ts (3 tests) | D22 | -| I22 | Agent generator composition | Slice 5 (observer) | core.test.ts, sdk.test.ts (7 tests) | D27 | -| I23 | Entity sidebar reactive update | Slice 6 (sidebar) | app.test.ts (2 tests), manual (outer loop) | D22 | +| I21 | Observer-result in-band sync | Slice 5 (observer) | observer.test.ts, app.test.ts | D22 | +| I22 | AI SDK-native interviewer path | Slice 6b (AI SDK pivot) | app.test.ts, interview.test.ts | D30, D31 | +| I23 | Entity sidebar reactive update | Slice 6 (sidebar) | app.test.ts, manual (outer loop) | D22 | ## Lexicon @@ -209,15 +209,13 @@ The architecture (layered: db → core → adapters): | **active path** | The branch from HEAD to root in the turn tree. Determines which turns, decisions, and assumptions are currently active | | **branch** (verb) | Fork the turn tree from a given turn, creating a new path and moving HEAD. Analogous to git branch + checkout | | **checkout** (verb) | Move HEAD to an existing turn on a different branch without creating new turns. Analogous to git checkout | -| **phase** | A stage of the interview: `scope`, `design`, `requirements`, `criteria`. Immutable provenance on each turn. Each phase is implemented via `getSystemPrompt(phase)` + a per-turn MCP tool server (`createInterviewMcpServer`). See D2, A13 | +| **phase** | A stage of the interview: `scope`, `design`, `requirements`, `criteria`. Immutable provenance on each turn. Each phase is implemented via `getSystemPrompt(phase)` plus typed AI SDK tools. See D2, A13 | | **phase resolution** | LLM judgment that shared understanding has been reached for a phase. Marked by `turn.is_resolution = true` on the last turn of a phase | -| **ask_question tool** | The MCP tool the interviewer must use each turn. Accepts `{ question, why, impact, options[] }`, validated by `structuredQuestionSchema` (Zod). The tool handler persists structured data to the turn and options tables via closure over `db` + `turnId`. Defined in `interview.ts` | -| **interview MCP server** | A per-turn MCP server created by `createInterviewMcpServer(db, turnId)`. Exposes the `ask_question` tool. The closure captures the current turn ID so the tool handler writes to the correct row. Passed to `query()` via `mcpServers` option. Defined in `interview.ts` | -| **agent loop** | The stream → tool_use → execute → tool_result → re-submit cycle. Implemented in `agent-loop.ts`. Modeled after `badlogic/pi-mono` `packages/agent/src/agent-loop.ts`. Yields `DomainEvent`s during streaming. Configurable with tools, system prompt, maxTurns. See D31 | +| **ask_question tool** | The typed AI SDK tool the interviewer must use each turn. Accepts `{ question, why, impact, options[] }`, validated by `structuredQuestionSchema` (Zod). The tool handler persists structured data to the turn and options tables via closure over `db` + `turnId`. Defined in `interview.ts` | +| **agent loop** | The stepwise interviewer control path. Today this is provided by AI SDK `ToolLoopAgent`; future phase-resolution work may wrap or replace it for tighter multi-step control. See D31 | | **interviewer** | The primary agent role: conducts the interview with structured questions, grounding, and impact signals. Must use the `ask_question` tool every turn. Does not extract entities | | **observer** | The secondary agent role: extracts decisions, assumptions, and dependency edges from each answered turn. Runs post-answer during user read time | -| **core** | The interface-agnostic service layer between the database and transport adapters. Owns interview orchestration, entity lifecycle, observer invocation. Returns `AsyncIterable` for streaming | -| **domain event** | A typed event yielded by `conductTurn()` — `stream-start`, `thinking`, `text-delta`, `tool-call-start`, `tool-call-delta`, `tool-call-end`, `stream-end`, `turn-created`, `error`, `observer-complete`. Future: `phase-resolved`. Each adapter translates to its transport format (SSE, terminal, MCP). `observer-complete` is emitted post-commit and drives cache coherence (D22) | +| **core** | The interface-agnostic service layer between the database and transport adapters. Owns turn preparation/finalization, context building, project state, and entity lifecycle. The web chat stream is assembled in Express over shared `BrunchUIMessage` contracts | | **decision graph** | The DAG of decisions and their dependencies (on prior decisions and assumptions). Revisiting a decision forks the turn tree | | **path exclusion** | Invalidation by moving HEAD so entities on the abandoned branch leave the active path. Lazy — computed by the active-path query, no eager writes. Triggered by `revisitDecision` / `branch` | | **flag propagation** | Invalidation by walking dependency graph edges and marking entities stale (nulling `reviewed_at`). Eager — triggered by `falsifyAssumption` or `updateRequirement` | @@ -228,9 +226,9 @@ The architecture (layered: db → core → adapters): | **parts[]** | Ordered array of typed content blocks in a `UIMessage`. Built-in types: `text`, `reasoning`, `tool-{name}` (4 states), `file`. Custom types via Data Parts: `data-option-selection`, `data-confirmation`, `data-phase-summary`, etc. Source of truth for rendering. See D23, D24 | | **Data Part** | Custom typed `UIMessage` part (`data-{name}`) defined via Zod schema. Enables structured user input (option selection, confirmation) and domain-specific assistant output (phase summary, observer result). Persisted in `parts[]` JSON. See D24 | | **context builder** | A typed function that projects turn-tree + entity data into inference context for a specific consumer (interviewer, observer, phase judge). Reads from domain model, not from persisted parts. See D25 | -| **in-band sync** | Observer entity updates delivered as typed data parts on the existing `conductTurn()` SSE stream. Default mechanism — zero additional infrastructure (D22) | +| **in-band sync** | Observer entity updates delivered as typed data parts on the existing chat SSE stream. Default mechanism — zero additional infrastructure (D22) | | **out-of-band sync** | Observer entity updates delivered via a dedicated `EventSource` SSE channel (`/api/events/:projectId`). Fallback mechanism if observer becomes async (D22) | -| **cache invalidation** | Signaling TanStack Query that cached data is stale. Two forms: `queryClient.setQueryData` (push new data directly into cache) and `queryClient.invalidateQueries` (trigger background refetch). Driven by `observer-complete` events (D22) | +| **cache invalidation** | Signaling TanStack Query that cached data is stale. In the current web path, `useChat` `onData` invalidates the entity query from `data-observer-result`, while route invalidation refreshes project state on stream completion | ## Verification Design @@ -341,17 +339,15 @@ This projection difference is a deliberate design choice, not an implementation -| File | Tests | Protects | -| ------------------- | ----- | --------------------------- | -| sse-adapter.test.ts | 8 | I1, I7, I21 | -| db.test.ts | 32 | I5, I6, I9, I10, I11, I18, I20 | -| app.test.ts | 21 | I2, I3, I6, I7, I13, I14 | -| core.test.ts | 15 | I12, I13, I18, I22 | -| interview.test.ts | 15 | I16, A27 | -| parts.test.ts | 23 | I17, I18 | -| context.test.ts | 8 | I19 | -| sdk.test.ts | 7 | I22 | -| observer.test.ts | 6 | I20, I21 | +| File | Tests | Protects | +| ---------------- | ----- | ------------------------ | +| db.test.ts | 32 | I5, I6, I9, I10, I11, I20 | +| app.test.ts | 6 | I1, I2, I3, I7, I14, I21, I23 | +| core.test.ts | 6 | I12, I13, I18 | +| interview.test.ts| 6 | I16 | +| parts.test.ts | 7 | I17, I18 | +| context.test.ts | 8 | I19 | +| observer.test.ts | 2 | I20, I21 | ## Acceptance Criteria (exit conditions) diff --git a/package-lock.json b/package-lock.json index ed8b8d9c..02e398c6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "name": "@hashintel/brunch", "license": "(MIT OR Apache-2.0)", "dependencies": { + "@ai-sdk/anthropic": "^3.0.66", "@ai-sdk/react": "^3.0.145", "@anthropic-ai/sdk": "^0.82.0", "@fontsource-variable/geist": "^5.2.8", @@ -62,6 +63,39 @@ "vitest": "^4.1.0" } }, + "node_modules/@ai-sdk/anthropic": { + "version": "3.0.66", + "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-3.0.66.tgz", + "integrity": "sha512-yJpQ2x6ACwbXo5D6HsVWd2FFnnWcetfGx4oxkG66P8FawusvrY2vL2qMiiNTruWrxEYDy+YHc3ctv8C769MMJA==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "3.0.8", + "@ai-sdk/provider-utils": "4.0.22" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, + "node_modules/@ai-sdk/anthropic/node_modules/@ai-sdk/provider-utils": { + "version": "4.0.22", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-4.0.22.tgz", + "integrity": "sha512-B2OTFcRw/Pdka9ZTjpXv6T6qZ6RruRuLokyb8HwW+aoW9ndJ3YasA3/mVswyJw7VMBF8ofXgqvcrCt9KYvFifg==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "3.0.8", + "@standard-schema/spec": "^1.1.0", + "eventsource-parser": "^3.0.6" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4.1.8" + } + }, "node_modules/@ai-sdk/gateway": { "version": "3.0.85", "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-3.0.85.tgz", diff --git a/package.json b/package.json index 00d21548..94f37a07 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "verify": "npm run check && npm run test && npm run build" }, "dependencies": { + "@ai-sdk/anthropic": "^3.0.66", "@ai-sdk/react": "^3.0.145", "@anthropic-ai/sdk": "^0.82.0", "@fontsource-variable/geist": "^5.2.8", diff --git a/src/client/components/EntitySidebar.tsx b/src/client/components/EntitySidebar.tsx index 8f542587..19519c3b 100644 --- a/src/client/components/EntitySidebar.tsx +++ b/src/client/components/EntitySidebar.tsx @@ -4,13 +4,7 @@ import { useState } from 'react'; import { Badge } from '@/components/ui/badge'; import { cn } from '@/lib/utils'; -type Decision = { id: number; project_id: number; content: string; rationale: string | null }; -type Assumption = { id: number; project_id: number; content: string }; - -type EntitiesData = { - decisions: Decision[]; - assumptions: Assumption[]; -}; +import type { EntitiesData } from '../../shared/api-types.js'; const tabs = ['Decisions', 'Assumptions'] as const; type Tab = (typeof tabs)[number]; diff --git a/src/client/router.tsx b/src/client/router.tsx index ebd2fa28..85353903 100644 --- a/src/client/router.tsx +++ b/src/client/router.tsx @@ -1,5 +1,6 @@ import { createRootRoute, createRoute, createRouter, Outlet } from '@tanstack/react-router'; +import type { ProjectListItem, ProjectState } from '../shared/api-types.js'; import { ComponentDebug } from './routes/ComponentDebug.js'; import { ExportPreview } from './routes/ExportPreview.js'; import { InterviewWorkspace } from './routes/InterviewWorkspace.js'; @@ -21,7 +22,7 @@ const indexRoute = createRoute({ loader: async () => { const res = await fetch('/api/projects'); if (!res.ok) throw new Error('Failed to load projects'); - return res.json() as Promise>; + return res.json() as Promise; }, component: ProjectList, }); @@ -33,26 +34,7 @@ const projectRoute = createRoute({ loader: async ({ params }) => { const res = await fetch(`/api/projects/${params.id}`); if (!res.ok) throw new Error('Failed to load project'); - return res.json() as Promise<{ - project: { id: number; name: string; active_turn_id: number | null }; - turns: Array<{ - id: number; - answer: string | null; - question: string | null; - why: string | null; - impact: string | null; - phase: string; - user_parts: string | null; - assistant_parts: string | null; - options: Array<{ - id: number; - position: number; - content: string; - is_recommended: boolean; - is_selected: boolean; - }>; - }>; - }>; + return res.json() as Promise; }, component: InterviewWorkspace, }); diff --git a/src/client/routes/ComponentDebug.tsx b/src/client/routes/ComponentDebug.tsx index 2f6c413f..ad621c06 100644 --- a/src/client/routes/ComponentDebug.tsx +++ b/src/client/routes/ComponentDebug.tsx @@ -1,5 +1,4 @@ import { Link } from '@tanstack/react-router'; -import type { ToolUIPart, UIMessage } from 'ai'; import { useState, useCallback } from 'react'; import { @@ -34,7 +33,9 @@ import { Card, CardContent } from '@/components/ui/card'; import { Separator } from '@/components/ui/separator'; import { cn } from '@/lib/utils'; -const FIXTURE_MESSAGES: UIMessage[] = [ +import type { BrunchUIMessage } from '../../shared/chat.js'; + +const FIXTURE_MESSAGES: BrunchUIMessage[] = [ { id: 'debug-1', role: 'user', @@ -64,51 +65,46 @@ const FIXTURE_MESSAGES: UIMessage[] = [ role: 'assistant', parts: [ { - type: 'tool-invocation', - toolInvocationId: 'debug-tool-1', - toolName: 'ask_question', + type: 'tool-ask_question', + toolCallId: 'debug-tool-1', state: 'output-available', - step: 0, - args: { + input: { question: 'What concurrency model should the event system use?', why: 'This determines how multiple observers can process events without blocking the main interview flow.', impact: 'high', options: [ { content: 'Single-threaded with async/await', is_recommended: true }, - { content: 'Worker threads for heavy extraction' }, - { content: 'Queue-based with retry semantics' }, + { content: 'Worker threads for heavy extraction', is_recommended: false }, + { content: 'Queue-based with retry semantics', is_recommended: false }, ], }, output: { - success: true, + ok: true, turnId: 42, + optionCount: 3, }, - } as unknown as ToolUIPart, + }, { type: 'text', - text: "Here's how the structured question would appear. The `ask_question` tool validates the output via Zod schema before persisting.", + text: "Here's how the structured question tool appears on the AI SDK-native stream before the workspace renders the matching turn card.", }, ], }, ]; -const FIXTURE_CODE = `export async function* conductTurn( - db: Database, - projectId: number, - userAnswer: string, -): AsyncIterable { - const turn = await db.createTurn(projectId, userAnswer); - - yield { type: 'turn-created', turnId: turn.id }; +const FIXTURE_CODE = `const stream = createUIMessageStream({ + async execute({ writer }) { + writer.merge( + interviewer.toUIMessageStream({ + sendReasoning: true, + sendFinish: false, + }), + ); - const response = await callAgent(turn); - - for (const entity of response.extractedEntities) { - await db.persistEntity(entity); - yield { type: 'entity-extracted', entity }; - } - - yield { type: 'turn-complete', turnId: turn.id }; + const entityIds = await runObserver(db, persistedTurn, projectId); + writer.write({ type: 'data-observer-result', data: { entityIds } }); + writer.write({ type: 'finish', finishReason: 'stop' }); + }, }`; const impactStyles: Record = { @@ -165,18 +161,13 @@ export function ComponentDebug() { ); } - if (part.type === 'tool-invocation') { - const toolPart = part as unknown as ToolUIPart & { toolName?: string }; + if (part.type === 'tool-ask_question') { return ( - + - - + + ); @@ -209,7 +200,7 @@ export function ComponentDebug() { ] as const ).map((state) => ( - + ))} diff --git a/src/client/routes/InterviewWorkspace.tsx b/src/client/routes/InterviewWorkspace.tsx index 6a926af3..ba1732c1 100644 --- a/src/client/routes/InterviewWorkspace.tsx +++ b/src/client/routes/InterviewWorkspace.tsx @@ -1,9 +1,8 @@ import { useChat } from '@ai-sdk/react'; import { useQueryClient } from '@tanstack/react-query'; import { useLoaderData, useParams, Link, useRouter } from '@tanstack/react-router'; -import type { UIMessage } from 'ai'; import { DefaultChatTransport } from 'ai'; -import { useState, useEffect, useMemo, useCallback, useRef } from 'react'; +import { useState, useEffect, useMemo, useCallback } from 'react'; import { Conversation, @@ -20,49 +19,64 @@ import { type PromptInputMessage, } from '@/components/ai-elements/prompt-input'; import { Reasoning, ReasoningContent, ReasoningTrigger } from '@/components/ai-elements/reasoning'; -import { Tool, ToolHeader, ToolContent, type ToolPart } from '@/components/ai-elements/tool'; +import { Tool, ToolHeader, ToolContent, ToolInput, ToolOutput } from '@/components/ai-elements/tool'; import { EntitySidebar } from '@/components/EntitySidebar'; import { cn } from '@/lib/utils'; -type LoaderTurn = { - id: number; - answer: string | null; - question: string | null; - why: string | null; - impact: string | null; - phase: string; - user_parts: string | null; - assistant_parts: string | null; - options: Array<{ - id: number; - position: number; - content: string; - is_recommended: boolean; - is_selected: boolean; - }>; -}; +import type { ProjectStateTurn } from '../../shared/api-types.js'; +import { + assistantPartsSchema, + brunchDataPartSchemas, + isAskQuestionUIPart, + type BrunchAssistantPart, + type BrunchUIMessage, + type BrunchUserPart, + userPartsSchema, +} from '../../shared/chat.js'; + +function parseAssistantParts(json: string | null): BrunchAssistantPart[] { + if (!json) return []; + try { + return assistantPartsSchema.parse(JSON.parse(json)) as BrunchAssistantPart[]; + } catch { + return []; + } +} + +function parseUserParts(json: string | null): BrunchUserPart[] { + if (!json) return []; + try { + return userPartsSchema.parse(JSON.parse(json)); + } catch { + return []; + } +} -function hydrateMessages(turns: LoaderTurn[]): UIMessage[] { - const msgs: UIMessage[] = []; +function hydrateMessages(turns: ProjectStateTurn[]): BrunchUIMessage[] { + const msgs: BrunchUIMessage[] = []; for (const turn of turns) { - if (turn.answer) { + const hydratedUserParts = parseUserParts(turn.user_parts); + const userParts = + hydratedUserParts.length > 0 + ? hydratedUserParts.some((part) => part.type === 'text') || !turn.answer + ? hydratedUserParts + : ([{ type: 'text', text: turn.answer }, ...hydratedUserParts] as BrunchUserPart[]) + : turn.answer + ? ([{ type: 'text', text: turn.answer }] as BrunchUserPart[]) + : []; + + if (userParts.length > 0) { msgs.push({ id: `turn-${turn.id}-answer`, role: 'user', - parts: [{ type: 'text' as const, text: turn.answer }], + parts: userParts, }); } - if (turn.assistant_parts) { - try { - const parts = JSON.parse(turn.assistant_parts); - if (Array.isArray(parts) && parts.length > 0) { - msgs.push({ id: `turn-${turn.id}-assistant`, role: 'assistant', parts }); - continue; - } - } catch { - // fall through to scalar synthesis - } + const assistantParts = parseAssistantParts(turn.assistant_parts); + if (assistantParts.length > 0) { + msgs.push({ id: `turn-${turn.id}-assistant`, role: 'assistant', parts: assistantParts }); + continue; } if (turn.question) { @@ -87,11 +101,12 @@ function TurnCard({ onSelect, disabled, }: { - turn: LoaderTurn; + turn: ProjectStateTurn; onSelect: (turnId: number, position: number) => void; disabled: boolean; }) { - const hasSelection = turn.options.some((o) => o.is_selected); + const options = turn.options ?? []; + const hasSelection = options.some((o) => o.is_selected); return (
@@ -111,7 +126,7 @@ function TurnCard({ )}
- {turn.options.map((opt) => { + {options.map((opt) => { const isSelected = opt.is_selected; return ( , + PromptInputTextarea: () =>