diff --git a/.changeset/calm-services-start.md b/.changeset/calm-services-start.md new file mode 100644 index 0000000000..ccdd17b6b0 --- /dev/null +++ b/.changeset/calm-services-start.md @@ -0,0 +1,5 @@ +--- +"@opencode-ai/client": patch +--- + +Reuse a same-version background service when a repeated health probe succeeds instead of replacing an endpoint another client may already be using. diff --git a/CONTEXT.md b/CONTEXT.md deleted file mode 100644 index 79611f30f5..0000000000 --- a/CONTEXT.md +++ /dev/null @@ -1,245 +0,0 @@ -# OpenCode Session Runtime - -OpenCode sessions preserve durable conversational history while assembling the runtime context an agent needs to act correctly in its current environment. - -## Language - -**Model Context**: -The complete model-visible input assembled for one **Step**, including system instructions, **Session History**, tool definitions, and step-local additions. **Instructions** are one component of Model Context, not a synonym for it. -_Avoid_: System Context - -**Instructions**: -The opaque algebra of independently refreshable typed instruction sources that render the durable instruction baseline and chronological updates shown to the model. -_Avoid_: Model Context, System Context - -**Session History**: -The projected chronological conversation selected for a **Step** after applying the active compaction boundary and interleaving derived **Instruction Updates** from the current **Instruction Epoch**. -_Avoid_: Session Context - -**Instruction Source**: -One independently read typed value within **Instructions**, represented by a stable namespaced key, canonical JSON codec, pure first/changed renderers, and an optional removal renderer. -_Avoid_: Prompt fragment - -**InstructionEntry**: -One API-managed, durable, per-Session instruction value. Its slash-free client key maps to the `api/` **Instruction Source** key. Entries deliberately render to the model as mechanism-neutral `` blocks: the model sees session context, not how it was attached. - -**InstructionDiscovery**: -The Location-scoped service that observes ambient global and upward-project `AGENTS.md` files as one ordered aggregate **Instruction Source**. - -**Instruction State**: -The Session-owned projection cache of one instruction log fold: epoch start, values at that start, current values, and the last folded sequence. It is rebuilt from durable events and never authors model-visible facts. - -**Instruction Update**: -A durable `session.instructions.updated` value delta admitted at a **Safe Step Boundary**. Its model-visible System text is rendered from stored values at request assembly and is never persisted verbatim. -_Avoid_: Correction, stored prose, raw text diff - -**Initial Instructions**: -The deterministic instruction text rendered from values at the current **Instruction Epoch** start and sent as provider-cache prefix state until completed compaction moves the epoch or Session movement or committed revert resets it. -_Avoid_: Live system prompt - -**Instruction Epoch**: -The span between completed compactions. Its start is the last `session.compaction.ended` sequence, or the initial complete instruction delta when no prior epoch exists. - -**Instruction Values**: -The key-to-hash map produced by folding instruction deltas in durable sequence order. Hash bodies live once in the content-addressed instruction blob store. - -**Unavailable Instruction Source**: -An expected temporary inability to read an **Instruction Source** value; the runtime retains its prior effective value and emits no update, while an unavailable source blocks the initial complete delta. - -**Safe Step Boundary**: -The point during Step preparation, after prior tool settlement and before durable input promotion, where instruction changes may be admitted chronologically. - -**Admitted Prompt**: -A durable user input accepted into the Session inbox but not yet included in **Session History**. - -**Prompt Promotion**: -The durable transition that removes an **Admitted Prompt** from pending input and appends its user message to **Session History**. - -**Step**: -One logical LLM call spanning pre-flight instruction synchronization, input promotion, request build, and compaction check; the provider stream; and tool settlement. -_Avoid_: provider turn, turn (unqualified) - -**Physical Attempt**: -One actual provider request on the wire in service of a **Step**; most Steps have one Physical Attempt, while overflow-triggered compaction recovery may give one Step two. - -**Assistant Turn**: -A reserved name for the not-yet-modeled unit containing all **Steps** from prompt promotion until the assistant yields the floor; do not reify it until something durable needs it. - -**Settlement**: -The terminal transition for a unit of work: Step and tool settlement are durable, while drain and execution settlement are coordinator-observed. - -**Execution**: -One session-scoped coordinator busy period from first wake until idle. An Execution is process-local coordination rather than a durable domain entity. - -**Session Drain**: -One process-local execution span that promotes eligible input and runs required **Steps** until no immediate continuation remains. A Session Drain has no durable identity or transcript boundary. - -**Model Tool Output**: -The bounded projection of a Core-executed tool result persisted in Session history and replayed to the model. A tool may shape this projection semantically, but the Tool Registry enforces the final size limit. - -**Managed Tool Output File**: -A temporary file created under OpenCode's shared tool-output directory to retain complete output that was too large for Session history. - -**Model Request Options**: -Provider-semantic model settings selected from the Catalog and active Session variant before the LLM protocol adapter encodes them for a provider request. -_Avoid_: Request body, wire options - -**Generation Controls**: -Provider-neutral sampling and output controls, partitioned from provider semantics and compatibility wire fields when model metadata enters the Catalog. - -**Native Continuation Metadata**: -Opaque protocol-shaped data attached to assistant content and required to continue that content natively with a compatible model, such as a reasoning signature or provider-hosted item identifier. - -**PTY Environment**: -The host-supplied environment overlay applied by the server when creating a PTY, observed for the request Location and resolved PTY working directory. - -**OpenCode Client**: -The generated Promise and Effect APIs derived from the public `HttpApi`; **Embedded OpenCode** shares the Effect API through an in-memory `HttpClient` against the same router and handlers. -_Avoid_: Remote client - -**SDK Contract IR**: -The runtime-neutral compiled representation of the authoritative `HttpApi`, preserving encoded and decoded type projections plus transport metadata so independent SDK emitters can choose their public value model and runtime interpreter. - -**Embedded OpenCode**: -A scoped in-process host that structurally extends the **OpenCode Client**, supplies an in-memory HTTP transport, and exposes additional same-process capabilities directly. -_Avoid_: Local implementation - -**Page**: -A bounded ordered result containing `items` and opaque `previous` and `next` cursor links for navigating the same query in either direction. -_Avoid_: Response envelope - -## Relationships - -- **Instructions** is an opaque carrier composed from zero or more **Instruction Sources**. -- **Model Context** is broader than **Instructions**. For each **Step**, the runner assembles the selected agent or provider system text, **Initial Instructions**, **Session History**, available tools, and step-local additions into one model request. -- **Session History** persists conversational messages. The runner derives model-facing **Instruction Update** messages from value deltas and interleaves them by durable sequence; **Initial Instructions** remain separate provider-request state. -- The runner explicitly loads and combines instruction built-ins, **InstructionDiscovery**, selected-agent skill guidance, reference guidance, MCP guidance, and **InstructionEntry** values. There is no instruction registry. -- `Instructions.combine(...)` preserves caller order and rejects duplicate stable namespaced source keys. The runner loads its producers concurrently, then combines them in its fixed declared order. -- Each **Instruction Source** read returns one coherent typed value, explicit removal, or temporary unavailability. `Instructions.make(...)` hides the value type so differently typed sources compose uniformly; its canonical codec defines storage and hash equivalence, while pure renderers produce first, changed, and optional removal text. -- `Instructions.read(...)` reads every composed source concurrently and exactly once at the boundary. `Instructions.diff(...)` compares encoded-value hashes with current **Instruction Values** and returns one delta plus new blob bodies. -- `Instructions.renderInitial(...)` renders values at the **Instruction Epoch** start. `Instructions.renderUpdate(...)` renders one hydrated delta against the values immediately before it. -- A changed **Instruction Source** contributes its hash to one **Instruction Update**; explicit removal contributes the `"removed"` sentinel. -- An **Instruction Update** persists only its value delta. Rendered text is derived during request assembly and excluded from compaction summaries. -- The instruction blob insert, durable delta, and **Instruction State** advance commit atomically. -- Changes from multiple **Instruction Sources** admitted at one safe boundary combine into one **Instruction Update**. -- Instruction changes are sampled and admitted lazily at a **Safe Step Boundary**, never pushed asynchronously when their source changes. -- At a **Safe Step Boundary**, prior tool results are already settled; instruction preparation completes before newly admitted user input promotes. -- An **Admitted Prompt** is replayable pending input, not yet model-visible **Session History**. -- **Prompt Promotion** atomically consumes the pending inbox entry and appends its model-visible user message. -- Steering prompts promote at the next **Safe Step Boundary** while the current **Session Drain** still requires continuation. Promoting any newly admitted user input resets the selected agent's step allowance; multiple prompts promoted at one boundary reset it once. -- A queued prompt does not promote while the current **Session Drain** requires continuation. The runner promotes one queued prompt when the Session would otherwise become idle, then reevaluates continuation before promoting another. -- A **Session Drain** is process-local coordination rather than a durable domain entity. Durable recovery must reason from prompts, projected history, physical attempts, and tool state rather than inventing an enclosing execution identity. -- An **Execution** contains one or more **Session Drains**; a **Session Drain** contains one reserved assistant-turn span at a time; that span contains **Steps**; and each **Step** contains one or more **Physical Attempts** plus any tool calls it requires. -- A **Step** record covers only the model-visible span from first assistant output through tool settlement; pre-flight leaves no record, and one Step settles at most one record. -- The first **Step** admits one complete delta and renders **Initial Instructions** without narrating that delta in history; an unavailable initial source blocks the Step instead of persisting incomplete values. -- Instruction preparation precedes durable input promotion on every Step so an unavailable first baseline leaves pending input untouched and later updates enter history before newly promoted input. -- Completed compaction moves the **Instruction Epoch** to the exact `session.compaction.ended` sequence and copies current hashes to the epoch's initial values. Earlier updates leave active model history while durable deltas remain. -- A newly composed **Instruction Source** absent from current **Instruction Values** emits its first rendering once at the next **Safe Step Boundary**. -- **Unavailable Instruction Source** uses stale-while-revalidate semantics and is distinct from a successfully loaded absence, which may emit removal text. -- **InstructionDiscovery** observes ambient instructions as one ordered aggregate **Instruction Source**. -- Ambient discovery reads global and upward-project `AGENTS.md` files and honors `OPENCODE_DISABLE_PROJECT_CONFIG` for project files. -- After a successful internal file or directory read, nearby `AGENTS.md` files toward the Location root are injected once per Session as durable synthetic instruction messages. -- **InstructionEntry** stores API-managed per-Session JSON values. Each entry contributes one `api/` **Instruction Source**, so adding, replacing, or removing an entry is reconciled at the next **Safe Step Boundary**. -- Location-scoped instruction producers naturally re-resolve when a moved Session next runs in its destination Location. -- Moving a Session clears its **Instruction State**, so the destination must admit a complete delta before another prompt can promote. Committed revert does the same; replay derives both resets from their durable events. -- Selected-agent available-skill guidance is an **Instruction Source** composed explicitly by the runner. It lists only names and descriptions permitted for that agent; skill bodies and locations are exposed only through the permission-checked `skill` tool. -- The selected agent and model are sampled when a **Step** starts. Changes admitted after that boundary apply to the next Step and do not restart the current Step. -- An agent switch that changes selected-agent guidance produces an **Instruction Update** while preserving the current baseline. -- Local tool authorization and pending permission requests retain the effective agent of the **Step** that issued the call; a later agent switch cannot change that call's policy. -- Instruction source changes never wake idle Sessions; the next naturally scheduled **Safe Step Boundary** loads and compares current values lazily. -- Once admitted, an **Instruction Update** remains durable even if the following **Physical Attempt** fails and is replayed unchanged on retry. -- **Instruction Updates** remain durable value history but are not `session_message` rows. Clients display changed keys rather than model-facing prose. -- The date **Instruction Source** initially preserves host-local calendar-date behavior; a configured user timezone may replace that default later. -- **Initial Instructions** are recomputed deterministically from durable values for every request; rendered bytes are not stored. -- A model/provider switch preserves current **Instruction Values**, the **Instruction Epoch**, and chronological conversation history; the new selection applies to the next **Step**. -- **Native Continuation Metadata** remains in durable history. Step projection includes it only for a successful exact originating provider/model match; failed Steps and incompatible models omit opaque metadata, while non-empty visible reasoning lowers to ordinary assistant text after a model switch. This conservative relation may widen only when recorded provider tests establish compatibility. -- **Model Request Options** remain provider-semantic through Catalog resolution. The Session runner maps them into the LLM package's provider-option namespace; the selected protocol adapter alone owns provider wire encoding. -- **Generation Controls**, protocol-semantic **Model Request Options**, and compatibility request body fields are separate Catalog domains. A shared ingestion adapter partitions legacy and models.dev AI-SDK-shaped options before routing. -- The **PTY Environment** is a server concern rather than a Core PTY concern. PTY creation merges caller values, then the host overlay, then Core-forced terminal invariants such as `TERM` and `OPENCODE_TERMINAL`. -- Networked and **Embedded OpenCode** use the same **OpenCode Client** and preserve the full HTTP encoding, routing, middleware, and decoding boundary; only the `HttpClient` transport differs. -- The Effect-native network constructor obtains `HttpClient.HttpClient` from its environment so callers own transport selection, recording, tracing, retries, and tests. Convenience runtimes may provide a fetch transport separately. -- Creating **Embedded OpenCode** is scoped. Closing its owning Scope releases the in-process server resources, database resources, registrations, and fibers. -- **Embedded OpenCode** exposes shared client capabilities and embedded-only capabilities on one object; consumers do not navigate through a nested `.client` property. -- The beta **OpenCode Client** currently uses plural consumer-facing capability groups such as `sessions`; whether the stable Session namespace should instead be singular `session` must be settled before stabilization. Internal server identifiers do not implicitly define public client names. -- Server's concrete `HttpApi` is authoritative for shared **OpenCode Client** capabilities. Codegen compiles its Session group directly; the Effect runtime uses an equivalent Protocol-only projection so generated artifacts remain independent of Core and Server. -- SDK generation reflects the public `HttpApi` once into an **SDK Contract IR**. Promise and Effect emitters share endpoint structure and transport metadata without being required to expose identical public values: an emitter may select encoded wire types, decoded domain types, compile-time brands, runtime validation, and its own execution abstraction independently. -- The first Effect emitter is the rich projection: it exposes decoded Effect-native values, preserves brands and schema transformations, performs runtime schema decoding, and delegates transport interpretation to `HttpApiClient`. Lighter wire-shaped Effect output remains possible through another emitter policy rather than constraining the shared IR. -- The rich Effect emitter regenerates private executable schemas when the **SDK Contract IR** proves that their transport semantics can be reproduced exactly. Contracts with authoritative custom transformations use the import-based Effect emitter against a Protocol-only client projection whose generated transport output is tested against Server's concrete API; the Promise emitter still derives zero-Effect structural wire types from the same IR. -- `@opencode-ai/protocol` owns Session endpoint construction and middleware placement. Server supplies concrete middleware keys to produce the authoritative build-time API; the client projection supplies transport-only keys without importing Core or Server at runtime. -- The first Promise emitter targets the same clean domain-oriented method organization rather than Hey API source compatibility. It returns unwrapped values directly, rejects declared and infrastructure failures, and begins with minimal client-level transport configuration; result wrappers, interceptors, and legacy generated signatures are outside the initial surface. -- The first Promise emitter parses response syntax and trusts its generated structural types; it does not perform runtime structural validation. Malformed payload syntax fails, while a syntactically valid shape mismatch is not detected at the SDK boundary. Standalone validator generation remains an optional future emitter policy. -- Declared Promise-client failures retain their tagged structural wire values and have generated type guards. Consumers do not depend on generated `Error` subclass identity, preserving discrimination across package copies and realms while remaining structurally aligned with Effect domain errors. -- Promise-client infrastructure failures use one generated `ClientError` class with a structured reason such as transport failure, unexpected status, unsupported content type, or malformed response. Promise methods reject with either a tagged declared domain failure or `ClientError`, matching the Effect client's conceptual domain/infrastructure error division. -- Promise methods accept a separate optional per-call transport-options argument containing `AbortSignal` and header overrides. Cancellation and transport metadata do not enter the domain input object; broader interceptor and response-mode APIs remain deferred. -- Promise streaming methods return a lazy `AsyncIterable` directly rather than a Promise-wrapped stream object. Iteration opens the connection, `AbortSignal` cancels it, and ending iteration closes the underlying request; the Effect emitter analogously returns `Stream` directly. -- Promise SSE connection establishment, declared HTTP failures, and infrastructure failures occur during `AsyncIterable` iteration, beginning with its first `next()` call, rather than during synchronous method construction. -- Neither generated streaming runtime automatically reconnects after disconnection. Promise `AsyncIterable` and Effect `Stream` fail explicitly; live consumers refresh and resubscribe, while durable sequence-based resume remains explicit composition above the generated client. -- Promise client construction is synchronous and network-free. It requires `baseUrl`, defaults to `globalThis.fetch`, accepts client-level headers, and merges them with per-call header overrides. -- Effect client construction accepts an explicit `baseUrl` and obtains `HttpClient.HttpClient` from the Effect environment. It does not install fetch or duplicate per-call transport policy; callers transform/provide the client for headers, tracing, retries, recording, and tests, while fiber interruption owns cancellation. -- Promise and Effect emitters each own their generated public type modules. The **SDK Contract IR**, not a physically shared generated type package, is the common source; this permits zero-Effect wire types and rich decoded Effect types to evolve independently. -- Promise and Effect network clients ship from `@opencode-ai/client` behind isolated root and `/effect` exports. The root has no runtime path to Effect; `/effect` imports only Effect, Schema, and Protocol. -- The Effect-native scoped host belongs to `@opencode-ai/sdk-next`, which will assume the existing `@opencode-ai/sdk` name after legacy consumers migrate. Client remains network-only and SDK depends one-way on Client. -- SDK executes Server's assembled `HttpRouter` in memory. It opens no listener and performs no network I/O, while preserving Server routing, middleware, codecs, handlers, and errors. -- The Effect Client and SDK re-export their decoded datatype facade from Schema so callers do not depend on internal package locations or Core's versioned names. -- A capability intended for both networked and **Embedded OpenCode** belongs in the authoritative public `HttpApi`; embedded-only same-process capabilities extend **Embedded OpenCode** separately. -- `sessions.log({ sessionID, after, follow })` is the public durable Session event stream. It verifies the Session, replays durable events after the optional aggregate sequence, optionally continues with newly committed durable events, excludes live-only fragments, and is transported as SSE in both networked and embedded modes. -- `events.subscribe()` is a distinct public instance-wide live stream for Session and non-Session activity. It has no replay guarantee and includes connection, heartbeat, and instance-disposal lifecycle events; consumers recover from disconnection by refreshing authoritative state. -- A Session ID is not an optional filter on `events.subscribe()`: instance-wide live events and durable Session events have different schemas, replay guarantees, cursors, lifecycle events, and failure behavior. -- The initial common OpenCode Client does not expose server-global event aggregation. `events.subscribe()` is bounded to the connected OpenCode instance or workspace; any future cross-instance administrative stream requires a separately designed API. -- `events.subscribe()` does not automatically reconnect after transport loss. The live-only stream fails with `ClientError`; consumers refresh authoritative state before explicitly opening a new subscription because events missed during disconnection cannot be replayed. -- `sessions.log({ sessionID, after, follow })` returns the generated HTTP client's cold durable event stream and does not build reconnection policy into the endpoint or client constructor. Transport loss fails the stream with `ClientError`. Callers may compose an explicit resuming stream above it by retaining the last observed durable sequence and opening a new subscription with `after`; any reusable resume helper remains a separate API design question. -- The stable `sessions.list(...)` design returns a **Page** in both networked and **Embedded OpenCode**; embedded execution does not define a separate unbounded array-returning list operation. The beta client currently preserves the existing HTTP `{ data, cursor }` envelope until emitter-level Page projection is implemented. -- Session list cursors are opaque branded values carrying continuation query and ordering state. Consumers pass them back unchanged and do not inspect storage anchors or encoded filter fields. -- A Session list continuation accepts only its opaque cursor. Scope, filters, ordering, and page size are fixed by the initial query and carried by that cursor. -- `sessions.messages(...)` returns a **Page** and uses the same cursor discipline as `sessions.list(...)`: the initial request supplies `sessionID`, ordering, and page size; continuation supplies `sessionID` plus only an opaque branded message cursor carrying ordering, page size, direction, and message anchor. Using a cursor with another Session is invalid. -- `sessions.message({ sessionID, messageID })` is a required resource lookup. An unknown Session fails with `SessionNotFoundError`; a known Session with an absent or differently owned message fails with `MessageNotFoundError` without disclosing cross-Session ownership. Absence is not represented as `undefined` across the public HTTP boundary. -- `sessions.interrupt({ sessionID })` first verifies that the durable Session exists, failing with `SessionNotFoundError` otherwise. For a known Session, interruption is idempotent: idle, already-settled, or locally unowned execution is a no-op. -- `sessions.active()` snapshots the current process's foreground Session drain registry as a record of Session IDs to `{ type: "running" }`. Missing IDs are inactive; background subagents and tasks do not make their parent Session active, and process restart clears the registry. -- `sessions.context({ sessionID })` preserves the existing message-only operation. It returns projected **Session History**; it does not include or represent the complete **Model Context**, whose agent system text, **Initial Instructions**, tools, and step-local additions remain separate. -- **Open question**: Should a future, separately named operation expose complete **Model Context**, including the instruction baseline, applied instruction metadata, tools, and step-local additions? -- `sessions.prompt(...)` exposes `resume?: boolean`. Omitting it preserves durable admission followed by an advisory execution wake; `resume: false` requests durable admit-only behavior. -- The public operation remains `sessions.prompt(...)`; `SessionPending.admit` is the internal primitive, while the public `Admission` result and `resume` option express its durable admission semantics. -- `sessions.create(...)` accepts an optional `location`. Omission resolves through the connected OpenCode instance's default or current location; an explicit value selects a known location. Networked and embedded transports use the same handler semantics. -- `sessions.switchAgent({ sessionID, agent })` is part of the common client alongside `sessions.switchModel(...)`. It affects subsequent Session activity and fails with `SessionNotFoundError` for an unknown Session. -- The **Embedded OpenCode** Layer delegates to the same scoped creation path; it does not define a second implementation. -- A **PTY Environment** adapter observes plugins in the request Location while passing the resolved PTY working directory to the hook; standalone servers use an empty adapter. -- An **Instruction Update** lowers to the provider's native chronological instruction role when supported and to a wrapped chronological fallback otherwise. -- When the aggregate discovered instruction set changes, its **Instruction Update** includes the complete current ordered set and supersedes the prior aggregate value; when no discovered instructions remain, the message states that previously loaded instructions no longer apply. -- Ambient project instruction discovery honors `OPENCODE_DISABLE_PROJECT_CONFIG`; global instructions remain eligible. -- Oversized textual **Model Tool Output** retains a bounded preview in Session history while its complete text moves to managed tool-output storage. Arbitrary structured-result size is a separate concern. -- One tool settlement receives one aggregate textual limit, using the configured maximum lines or UTF-8 bytes, whichever is reached first. The limit is provider-independent; token pressure belongs to context assembly and compaction. -- Generic truncation preserves the beginning and end of textual output. Tools may apply a more meaningful strategy before the Tool Registry enforces the final limit. -- A truncated **Model Tool Output** identifies its complete text in the bounded model-visible preview. The Tool Registry also supplies managed paths as internal metadata to tool hooks; Session events do not expose a typed `outputPaths` field. -- A **Managed Tool Output File** is temporary and may expire after its retention period. The bounded **Model Tool Output**, not the file, is the durable replayable record. -- Failure to retain a **Managed Tool Output File** fails settlement operationally. The Session never publishes a successful result whose complete output was lost during generic bounding. -- Once a tool operation succeeds, bounding its **Model Tool Output** and publishing its one durable settlement form an interruption-safe completion region. Raw oversized success is never published before a later correction. -- When a structured-only result would exceed the **Model Tool Output** limit, its validated structured value remains unchanged for Session consumers while model replay uses a bounded textual JSON preview and optional managed output path. -- Existing tool-managed output paths survive generic bounding. A fallback file retains exactly the complete projected text received by the Tool Registry and never claims to reconstruct output already discarded by tool-specific shaping. -- **Managed Tool Output Files** use globally unique names in one shared flat directory. They receive no special filesystem authority; each tool applies its ordinary external-path policy. -- Provider-executed tool results remain provider-native transcript facts outside generic Tool Registry bounding. Their context control requires provider-aware pruning or compaction because some providers require exact structured round-trip payloads. - -## Client contract architecture - -Semantic values that mean the same thing internally and publicly live in the lightweight Schema leaf. Core consumes Schema for domain behavior; Protocol composes Schema values into paths, payloads, envelopes, errors, cursors, and streams; Server imports both, hosts Protocol's exact groups, and owns protocol/domain adaptation. The root Promise client remains zero-Effect, `/effect` depends on Effect plus Schema and Protocol, and `@opencode-ai/sdk-next` composes the scoped in-process host above Client, Core, and Server. - -Shared public records are plain objects declared with `Schema.Struct`. A same-name inferred interface gives object records readable TypeScript signatures without constructors, prototypes, or nominal identity; unions retain explicit type aliases. - -Before stabilizing the client API: - -- Keep additional public schemas in Schema and additional network groups in Protocol; neither package may transitively load databases, Drizzle, Session execution, providers, watchers, native modules, or WASM. -- Keep concrete Location middleware keys in Server while Protocol owns their placement. Client projections may supply transport-only keys, but must prove generated equivalence with Server's concrete API. -- Project the existing list response envelope to the stable client **Page** shape and enforce separate initial-query and cursor-continuation inputs without changing the hosted V2 wire contract. -- Settle the stable consumer namespace (`session` versus the current beta `sessions`) and use an explicit codegen annotation if the consumer name should differ from the server group identifier. -- Preserve V2 route paths, operation IDs, codecs, errors, middleware behavior, and OpenAPI output while making this change. -- Preserve browser-safe `@opencode-ai/client` and `@opencode-ai/client/effect` bundles through import-boundary tests. -- Define embedded-host placement before supporting multiple hosts over one database. Hosts that share durable Session storage must also share process-local Session execution coordination, or each host must receive isolated storage explicitly. -- Keep an embedded request scope alive until any streamed response body finishes. The initial non-streaming Session surface does not exercise this lifetime boundary; Session and instance event streams must do so before joining the embedded client. - -## Example dialogue - -> **Dev:** "The date changed while the session was active. Should the **Instruction Update** say what the old date was?" -> **Domain expert:** "No. Emit the newly effective date so the agent can act on the current instructions." - -## Flagged ambiguities - -- Legacy `experimental.chat.system.transform` can mutate assembled system text arbitrarily, but V2 plugins do not yet expose an equivalent hook. Decide separately whether to port it, model dynamic uses as explicit **Instruction Sources**, or narrow its semantics. diff --git a/bun.lock b/bun.lock index 6627771aca..edb60172f5 100644 --- a/bun.lock +++ b/bun.lock @@ -17,6 +17,8 @@ "@ast-grep/cli": "0.44.0", "@tsconfig/bun": "catalog:", "@types/mime-types": "3.0.1", + "@types/react": "19.2.17", + "@types/react-dom": "19.2.3", "@typescript/native-preview": "catalog:", "glob": "13.0.5", "husky": "9.1.7", @@ -115,6 +117,7 @@ "@parcel/watcher": "2.5.1", "effect": "catalog:", "fuzzysort": "catalog:", + "immer": "11.1.4", "jsonc-parser": "3.3.1", "open": "10.1.2", "opentui-spinner": "catalog:", @@ -1112,6 +1115,34 @@ "typescript": "catalog:", }, }, + "packages/www": { + "name": "@opencode-ai/www", + "version": "1.17.18", + "dependencies": { + "@cloudflare/vite-plugin": "1.44.0", + "@tailwindcss/vite": "4.3.2", + "@tanstack/react-router": "1.170.17", + "@tanstack/react-start": "1.168.27", + "@tanstack/router-plugin": "1.168.19", + "fumadocs-core": "16.11.1", + "fumadocs-mdx": "15.1.0", + "fumadocs-ui": "16.11.1", + "react": "19.2.7", + "react-dom": "19.2.7", + "tailwindcss": "4.3.2", + "vite": "8.1.4", + }, + "devDependencies": { + "@types/mdx": "2.0.14", + "@types/node": "catalog:", + "@types/react": "19.2.17", + "@types/react-dom": "19.2.3", + "@typescript/native-preview": "catalog:", + "@vitejs/plugin-react": "6.0.3", + "typescript": "catalog:", + "wrangler": "4.110.0", + }, + }, }, "trustedDependencies": [ "esbuild", @@ -1741,6 +1772,10 @@ "@fontsource/inter": ["@fontsource/inter@5.2.8", "", {}, "sha512-P6r5WnJoKiNVV+zvW2xM13gNdFhAEpQ9dQJHt3naLvfg+LkF2ldgSLiF4T41lf1SQCM9QmkqPTn4TH568IRagg=="], + "@fuma-translate/react": ["@fuma-translate/react@1.0.2", "", { "peerDependencies": { "@types/react": "*", "react": "^19.2.0", "react-dom": "^19.2.0" }, "optionalPeers": ["@types/react"] }, "sha512-uOiOtBx3nRXR8Nu1GzBf1tApgF1FErDBTHxRIAQeyQdyOoZbrNRN6H4kDCWObY4qyGeGbHydG0DHzgeUgFDMIw=="], + + "@fumadocs/tailwind": ["@fumadocs/tailwind@0.1.0", "", { "peerDependencies": { "tailwindcss": "^4.0.0" }, "optionalPeers": ["tailwindcss"] }, "sha512-nF/DCAwOR21HZ4AkjIOv3Iqwyqywzb6pdyeMcoa+aZzirXj5ntvNZbe3jJ0v3ehhtrRfYYeXBezvjn8ZmV+fuQ=="], + "@gar/promise-retry": ["@gar/promise-retry@1.0.3", "", {}, "sha512-GmzA9ckNokPypTg10pgpeHNQe7ph+iIKKmhKu3Ob9ANkswreCx7R3cKmY781K8QK3AqVL3xVh9A42JvIAbkkSA=="], "@gitlab/opencode-gitlab-auth": ["@gitlab/opencode-gitlab-auth@1.3.3", "", { "dependencies": { "@fastify/rate-limit": "^10.2.0", "@opencode-ai/plugin": "*", "fastify": "^5.2.0", "open": "^10.0.0" } }, "sha512-FT+KsCmAJjtqWr1YAq0MywGgL9kaLQ4apmsoowAXrPqHtoYf2i/nY10/A+L06kNj22EATeEDRpbB1NWXMto/SA=="], @@ -1765,6 +1800,8 @@ "@ibm/telemetry-js": ["@ibm/telemetry-js@1.11.0", "", { "bin": { "ibmtelemetry": "dist/collect.js" } }, "sha512-RO/9j+URJnSfseWg9ZkEX9p+a3Ousd33DBU7rOafoZB08RqdzxFVYJ2/iM50dkBuD0o7WX7GYt1sLbNgCoE+pA=="], + "@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="], + "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="], @@ -1777,6 +1814,10 @@ "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="], + "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA=="], + + "@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.2.4", "", { "os": "linux", "cpu": "none" }, "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA=="], + "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.0.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA=="], "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="], @@ -1789,6 +1830,10 @@ "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="], + "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA=="], + + "@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.2.4" }, "os": "linux", "cpu": "none" }, "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw=="], + "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.0.4" }, "os": "linux", "cpu": "s390x" }, "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q=="], "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="], @@ -1799,6 +1844,8 @@ "@img/sharp-wasm32": ["@img/sharp-wasm32@0.33.5", "", { "dependencies": { "@emnapi/runtime": "^1.2.0" }, "cpu": "none" }, "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg=="], + "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g=="], + "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.33.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ=="], "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="], @@ -2107,6 +2154,14 @@ "@one-ini/wasm": ["@one-ini/wasm@0.1.1", "", {}, "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw=="], + "@oozcitak/dom": ["@oozcitak/dom@2.0.2", "", { "dependencies": { "@oozcitak/infra": "^2.0.2", "@oozcitak/url": "^3.0.0", "@oozcitak/util": "^10.0.0" } }, "sha512-GjpKhkSYC3Mj4+lfwEyI1dqnsKTgwGy48ytZEhm4A/xnH/8z9M3ZVXKr/YGQi3uCLs1AEBS+x5T2JPiueEDW8w=="], + + "@oozcitak/infra": ["@oozcitak/infra@2.0.2", "", { "dependencies": { "@oozcitak/util": "^10.0.0" } }, "sha512-2g+E7hoE2dgCz/APPOEK5s3rMhJvNxSMBrP+U+j1OWsIbtSpWxxlUjq1lU8RIsFJNYv7NMlnVsCuHcUzJW+8vA=="], + + "@oozcitak/url": ["@oozcitak/url@3.0.0", "", { "dependencies": { "@oozcitak/infra": "^2.0.2", "@oozcitak/util": "^10.0.0" } }, "sha512-ZKfET8Ak1wsLAiLWNfFkZc/BraDccuTJKR6svTYc7sVjbR+Iu0vtXdiDMY4o6jaFl5TW2TlS7jbLl4VovtAJWQ=="], + + "@oozcitak/util": ["@oozcitak/util@10.0.0", "", {}, "sha512-hAX0pT/73190NLqBPPWSdBVGtbY6VOhWYK3qqHqtXQ1gK7kS2yz4+ivsN07hpJ6I3aeMtKP6J6npsEKOAzuTLA=="], + "@openapi-contrib/openapi-schema-to-json-schema": ["@openapi-contrib/openapi-schema-to-json-schema@3.2.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3" } }, "sha512-Gj6C0JwCr8arj0sYuslWXUBSP/KnUlEGnPW4qxlXvAl543oaNQgMgIgkQUA6vs5BCCvwTEiL8m/wdWzfl4UvSw=="], "@openauthjs/openauth": ["@openauthjs/openauth@0.0.0-20250322224806", "", { "dependencies": { "@standard-schema/spec": "1.0.0-beta.3", "aws4fetch": "1.0.20", "jose": "5.9.6" }, "peerDependencies": { "arctic": "^2.2.2", "hono": "^4.0.0" } }, "sha512-p5IWSRXvABcwocH2dNI0w8c1QJelIOFulwhKk+aLLFfUbs8u1pr7kQbYe8yCSM2+bcLHiwbogpUQc2ovrGwCuw=="], @@ -2185,6 +2240,8 @@ "@opencode-ai/web": ["@opencode-ai/web@workspace:packages/web"], + "@opencode-ai/www": ["@opencode-ai/www@workspace:packages/www"], + "@openrouter/ai-sdk-provider": ["@openrouter/ai-sdk-provider@2.9.0", "", { "peerDependencies": { "ai": "^6.0.0", "zod": "^3.25.0 || ^4.0.0" } }, "sha512-Seva+NCa0WUQnJIUE5GzHsUv1WTIeyqwz0ELl2VtS6NP+eF+77yCXGFVOMbvoCM7QMjlnhv7931e89R+8pJdcQ=="], "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], @@ -2235,6 +2292,8 @@ "@opentui/solid": ["@opentui/solid@0.4.3", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.4.3", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.12", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.12" } }, "sha512-RcV0+S8HMdXOASyr7HmJUBuTUIaFPzAxMDa44VftS5C2JUgrmAuWo0Njv1q3TWRB1owjHnyKhEfWGKq7A82wxw=="], + "@orama/orama": ["@orama/orama@3.1.18", "", {}, "sha512-a61ljmRVVyG5MC/698C8/FfFDw5a8LOIvyOLW5fztgUXqUpc1jOfQzOitSCbge657OgXXThmY3Tk8fpiDb4UcA=="], + "@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="], "@oslojs/binary": ["@oslojs/binary@1.0.0", "", {}, "sha512-9RCU6OwXU6p67H4NODbuxv2S3eenuQ4/WFLrsq+K/k682xrznH5EVWA7N4VFk9VYVcbFtKqur5YQQZc0ySGhsQ=="], @@ -2545,19 +2604,25 @@ "@radix-ui/colors": ["@radix-ui/colors@1.0.1", "", {}, "sha512-xySw8f0ZVsAEP+e7iLl3EvcBXX7gsIlC1Zso/sPBW9gIWerBTgz6axrjU+MZ39wD+WFi5h5zdWpsg3+hwt2Qsg=="], + "@radix-ui/number": ["@radix-ui/number@1.1.2", "", {}, "sha512-ceTwaxc4I5IOi97DgCotl3pqiyRGvffcc0oOsE2dQYaJOFIDsDt4VWG6xEbg1QePv9QWausCEIppud/tJ1wNig=="], + "@radix-ui/primitive": ["@radix-ui/primitive@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" } }, "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw=="], + "@radix-ui/react-accordion": ["@radix-ui/react-accordion@1.2.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.5", "@radix-ui/react-collapsible": "1.1.16", "@radix-ui/react-collection": "1.1.12", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-BpZJNmetujnGgUI6OX0jEhEmlA46WPqgub8Rv09Kyquwd0cc1ndMKpiPYCjmBU6KSSRPAMtgLpEoZSG/tdNIWQ=="], + "@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.0.3", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-primitive": "1.0.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA=="], "@radix-ui/react-collapsible": ["@radix-ui/react-collapsible@1.0.3", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/primitive": "1.0.1", "@radix-ui/react-compose-refs": "1.0.1", "@radix-ui/react-context": "1.0.1", "@radix-ui/react-id": "1.0.1", "@radix-ui/react-presence": "1.0.1", "@radix-ui/react-primitive": "1.0.3", "@radix-ui/react-use-controllable-state": "1.0.1", "@radix-ui/react-use-layout-effect": "1.0.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-UBmVDkmR6IvDsloHVN+3rtx4Mi5TFvylYXpluuv0f37dtaz3H99bp8No0LGXRigVpl3UAT4l9j6bIchh42S/Gg=="], - "@radix-ui/react-collection": ["@radix-ui/react-collection@1.0.3", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-compose-refs": "1.0.1", "@radix-ui/react-context": "1.0.1", "@radix-ui/react-primitive": "1.0.3", "@radix-ui/react-slot": "1.0.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA=="], + "@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.12", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-slot": "1.3.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-nb67INpE0IahJKN7EYPp9m9YGwYeKlnzxT3MwXVkgCskaSJia97kG4T0ywpjNUSSnoJk/uvk12V8vbrEHEj+/Q=="], "@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" }, "optionalPeers": ["@types/react"] }, "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw=="], "@radix-ui/react-context": ["@radix-ui/react-context@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" }, "optionalPeers": ["@types/react"] }, "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg=="], - "@radix-ui/react-direction": ["@radix-ui/react-direction@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" }, "optionalPeers": ["@types/react"] }, "sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA=="], + "@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.19", "", { "dependencies": { "@radix-ui/primitive": "1.1.5", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-dismissable-layer": "1.1.15", "@radix-ui/react-focus-guards": "1.1.4", "@radix-ui/react-focus-scope": "1.1.12", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-portal": "1.1.13", "@radix-ui/react-presence": "1.1.7", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-slot": "1.3.0", "@radix-ui/react-use-controllable-state": "1.2.3", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-+HhbN2+YtkRgVirjZ2afMeutQRuGOrdkWR5+EFC58SJojGmtyNQwYzgi6tHBpOxvFHefMtPeHdgtjz0BOGxFQg=="], + + "@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-C3vFhbyi4SW3PmbAi6Awpu4OzJtd0MxGurvSsYtr7p7nM8RNB3VAF3CUmnp2j50knpkrRcB7+ycVXzgLgF6yNA=="], "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.0.4", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/primitive": "1.0.1", "@radix-ui/react-compose-refs": "1.0.1", "@radix-ui/react-primitive": "1.0.3", "@radix-ui/react-use-callback-ref": "1.0.1", "@radix-ui/react-use-escape-keydown": "1.0.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7UpBa/RKMoHJYjie1gkF1DlK8l1fdU/VKDpoS3rCCo8YBJR294GwcEHyxHw72yvphJ7ld0AXEcSLAzY2F/WyCg=="], @@ -2567,34 +2632,46 @@ "@radix-ui/react-id": ["@radix-ui/react-id@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-use-layout-effect": "1.0.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" }, "optionalPeers": ["@types/react"] }, "sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ=="], + "@radix-ui/react-navigation-menu": ["@radix-ui/react-navigation-menu@1.2.18", "", { "dependencies": { "@radix-ui/primitive": "1.1.5", "@radix-ui/react-collection": "1.1.12", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.15", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-presence": "1.1.7", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-layout-effect": "1.1.2", "@radix-ui/react-use-previous": "1.1.2", "@radix-ui/react-visually-hidden": "1.2.7" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-K9HiuxZ6xCwSaHcIuUpxyhy4w5gpwzWjh9dHTSbMN3Ix4qAyVObS9RlU3zMycb0PO3v9Tpk0BXMwWvXOUbVXew=="], + "@radix-ui/react-popover": ["@radix-ui/react-popover@1.0.6", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/primitive": "1.0.1", "@radix-ui/react-compose-refs": "1.0.1", "@radix-ui/react-context": "1.0.1", "@radix-ui/react-dismissable-layer": "1.0.4", "@radix-ui/react-focus-guards": "1.0.1", "@radix-ui/react-focus-scope": "1.0.3", "@radix-ui/react-id": "1.0.1", "@radix-ui/react-popper": "1.1.2", "@radix-ui/react-portal": "1.0.3", "@radix-ui/react-presence": "1.0.1", "@radix-ui/react-primitive": "1.0.3", "@radix-ui/react-slot": "1.0.2", "@radix-ui/react-use-controllable-state": "1.0.1", "aria-hidden": "^1.1.1", "react-remove-scroll": "2.5.5" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-cZ4defGpkZ0qTRtlIBzJLSzL6ht7ofhhW4i1+pkemjV1IKXm0wgCRnee154qlV6r9Ttunmh2TNZhMfV2bavUyA=="], "@radix-ui/react-popper": ["@radix-ui/react-popper@1.1.2", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.0.3", "@radix-ui/react-compose-refs": "1.0.1", "@radix-ui/react-context": "1.0.1", "@radix-ui/react-primitive": "1.0.3", "@radix-ui/react-use-callback-ref": "1.0.1", "@radix-ui/react-use-layout-effect": "1.0.1", "@radix-ui/react-use-rect": "1.0.1", "@radix-ui/react-use-size": "1.0.1", "@radix-ui/rect": "1.0.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-1CnGGfFi/bbqtJZZ0P/NQY20xdG3E0LALJaLUEoKwPLwl6PPPfbeiCqMVQnhoFRAxjJj4RpBRJzDmUgsex2tSg=="], "@radix-ui/react-portal": ["@radix-ui/react-portal@1.0.3", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-primitive": "1.0.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-xLYZeHrWoPmA5mEKEfZZevoVRK/Q43GfzRXkWV6qawIWWK8t6ifIiLQdd7rmQ4Vk1bmI21XhqF9BN3jWf+phpA=="], - "@radix-ui/react-presence": ["@radix-ui/react-presence@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-compose-refs": "1.0.1", "@radix-ui/react-use-layout-effect": "1.0.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg=="], + "@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.7", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-zBZ4QM5XG3JRanDmqXYf3MD6th4AFXFmgU6KNMFzUaV6F3uw9I5/zjMUvFriSEn5ewo1nxuibvyxJdmLlDcslA=="], "@radix-ui/react-primitive": ["@radix-ui/react-primitive@1.0.3", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-slot": "1.0.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g=="], "@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.0.4", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/primitive": "1.0.1", "@radix-ui/react-collection": "1.0.3", "@radix-ui/react-compose-refs": "1.0.1", "@radix-ui/react-context": "1.0.1", "@radix-ui/react-direction": "1.0.1", "@radix-ui/react-id": "1.0.1", "@radix-ui/react-primitive": "1.0.3", "@radix-ui/react-use-callback-ref": "1.0.1", "@radix-ui/react-use-controllable-state": "1.0.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ=="], + "@radix-ui/react-scroll-area": ["@radix-ui/react-scroll-area@1.2.14", "", { "dependencies": { "@radix-ui/number": "1.1.2", "@radix-ui/primitive": "1.1.5", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-presence": "1.1.7", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bBODCWZK7JTbQLHs0uIP4f73wIWatakK4OS33UzkR1x897wu0PuO658a3f+6P2GEGyDzGYMuHRatMVoAk9WZTw=="], + "@radix-ui/react-slot": ["@radix-ui/react-slot@1.0.2", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-compose-refs": "1.0.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" }, "optionalPeers": ["@types/react"] }, "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg=="], + "@radix-ui/react-tabs": ["@radix-ui/react-tabs@1.1.17", "", { "dependencies": { "@radix-ui/primitive": "1.1.5", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-presence": "1.1.7", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-roving-focus": "1.1.15", "@radix-ui/react-use-controllable-state": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-nRyXnrAVCwjeXcHbvEbLS6ndbTeKHG1RqCP4A8Gw5L4cemDzPXdD8rAmr6wet0v57R69wGvuIIsFjHSVkZiMzQ=="], + "@radix-ui/react-toggle": ["@radix-ui/react-toggle@1.0.3", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/primitive": "1.0.1", "@radix-ui/react-primitive": "1.0.3", "@radix-ui/react-use-controllable-state": "1.0.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Pkqg3+Bc98ftZGsl60CLANXQBBQ4W3mTFS9EJvNxKMZ7magklKV69/id1mlAlOFDDfHvlCms0fx8fA4CMKDJHg=="], "@radix-ui/react-toggle-group": ["@radix-ui/react-toggle-group@1.0.4", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/primitive": "1.0.1", "@radix-ui/react-context": "1.0.1", "@radix-ui/react-direction": "1.0.1", "@radix-ui/react-primitive": "1.0.3", "@radix-ui/react-roving-focus": "1.0.4", "@radix-ui/react-toggle": "1.0.3", "@radix-ui/react-use-controllable-state": "1.0.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Uaj/M/cMyiyT9Bx6fOZO0SAG4Cls0GptBWiBmBxofmDbNVnYYoyRWj/2M/6VCi/7qcXFWnHhRUfdfZFvvkuu8A=="], "@radix-ui/react-tooltip": ["@radix-ui/react-tooltip@1.0.6", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/primitive": "1.0.1", "@radix-ui/react-compose-refs": "1.0.1", "@radix-ui/react-context": "1.0.1", "@radix-ui/react-dismissable-layer": "1.0.4", "@radix-ui/react-id": "1.0.1", "@radix-ui/react-popper": "1.1.2", "@radix-ui/react-portal": "1.0.3", "@radix-ui/react-presence": "1.0.1", "@radix-ui/react-primitive": "1.0.3", "@radix-ui/react-slot": "1.0.2", "@radix-ui/react-use-controllable-state": "1.0.1", "@radix-ui/react-visually-hidden": "1.0.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-DmNFOiwEc2UDigsYj6clJENma58OelxD24O4IODoZ+3sQc3Zb+L8w1EP+y9laTuKCLAysPw4fD6/v0j4KNV8rg=="], - "@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" }, "optionalPeers": ["@types/react"] }, "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ=="], + "@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw=="], "@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-use-callback-ref": "1.0.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" }, "optionalPeers": ["@types/react"] }, "sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA=="], + "@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.3", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-6c8ZqvPTWILEKnyVkP53EGRCcpnJiKTC21sS/6R1GF5xKyHJJWQEPfkqlcgUkdRQivd6tb23abUwe4ngWmY0JA=="], + "@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.0.3", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-use-callback-ref": "1.0.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" }, "optionalPeers": ["@types/react"] }, "sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg=="], + "@radix-ui/react-use-is-hydrated": ["@radix-ui/react-use-is-hydrated@0.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-qwOiz4Tjo8CNnrOLAYUMXeZwDzXgXpvK4TKQPmWLECM9XoWvA6+0Z2/7Ag3A4ivjS4ovbLJPbskkxioFyBhr8A=="], + "@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" }, "optionalPeers": ["@types/react"] }, "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ=="], + "@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-IGBQPtRFdhN6MQ8dbegVmBq1LVZluya3F1jWY+puIcQC3MHctRwTDSBWCkL/3ZcnMJLTMJ++Z+ktmvg0F89iCw=="], + "@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/rect": "1.0.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" }, "optionalPeers": ["@types/react"] }, "sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw=="], "@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-use-layout-effect": "1.0.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" }, "optionalPeers": ["@types/react"] }, "sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g=="], @@ -3019,9 +3096,29 @@ "@tanstack/directive-functions-plugin": ["@tanstack/directive-functions-plugin@1.134.5", "", { "dependencies": { "@babel/code-frame": "7.27.1", "@babel/core": "^7.27.7", "@babel/traverse": "^7.27.7", "@babel/types": "^7.27.7", "@tanstack/router-utils": "1.133.19", "babel-dead-code-elimination": "^1.0.10", "pathe": "^2.0.3", "tiny-invariant": "^1.3.3" }, "peerDependencies": { "vite": ">=6.0.0 || >=7.0.0" } }, "sha512-J3oawV8uBRBbPoLgMdyHt+LxzTNuWRKNJJuCLWsm/yq6v0IQSvIVCgfD2+liIiSnDPxGZ8ExduPXy8IzS70eXw=="], + "@tanstack/history": ["@tanstack/history@1.162.0", "", {}, "sha512-79pf/RkhteYZTRgcR4F9kbk84P2N8rugQJswxfIqovlbRiT3yI7eBE+5QorIrZaOKktsgzRlXh1l/du/xpl4iA=="], + "@tanstack/query-core": ["@tanstack/query-core@5.91.2", "", {}, "sha512-Uz2pTgPC1mhqrrSGg18RKCWT/pkduAYtxbcyIyKBhw7dTWjXZIzqmpzO2lBkyWr4hlImQgpu1m1pei3UnkFRWw=="], - "@tanstack/router-utils": ["@tanstack/router-utils@1.133.19", "", { "dependencies": { "@babel/core": "^7.27.4", "@babel/generator": "^7.27.5", "@babel/parser": "^7.27.5", "@babel/preset-typescript": "^7.27.1", "ansis": "^4.1.0", "diff": "^8.0.2", "pathe": "^2.0.3", "tinyglobby": "^0.2.15" } }, "sha512-WEp5D2gPxvlLDRXwD/fV7RXjYtqaqJNXKB/L6OyZEbT+9BG/Ib2d7oG9GSUZNNMGPGYAlhBUOi3xutySsk6rxA=="], + "@tanstack/react-router": ["@tanstack/react-router@1.170.17", "", { "dependencies": { "@tanstack/history": "1.162.0", "@tanstack/react-store": "^0.9.3", "@tanstack/router-core": "1.171.14", "isbot": "^5.1.22" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-ppLkjCfSMaeug9rmFRYzOd4TIqWV+yTE7tzIny7alJsSnM7w4lzEZm6eqCehG0SPetpZ0R3K+UnanSmBgOAVcQ=="], + + "@tanstack/react-start": ["@tanstack/react-start@1.168.27", "", { "dependencies": { "@tanstack/react-router": "1.170.17", "@tanstack/react-start-client": "1.168.15", "@tanstack/react-start-rsc": "0.1.26", "@tanstack/react-start-server": "1.167.21", "@tanstack/router-utils": "1.162.2", "@tanstack/start-client-core": "1.170.13", "@tanstack/start-plugin-core": "1.171.19", "@tanstack/start-server-core": "1.169.16", "pathe": "^2.0.3" }, "peerDependencies": { "@rsbuild/core": "^2.0.0", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0", "vite": ">=7.0.0" }, "optionalPeers": ["@rsbuild/core", "vite"] }, "sha512-rdGFDqfCW71gyofyAxaYxhelNKmeVjpmbpm0uFYbNHORCa///4aBxi7B7ecShibKv9O4GfJ66MPX5F0ozbm+ig=="], + + "@tanstack/react-start-client": ["@tanstack/react-start-client@1.168.15", "", { "dependencies": { "@tanstack/react-router": "1.170.17", "@tanstack/router-core": "1.171.14", "@tanstack/start-client-core": "1.170.13" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-pW50PHvadgi50iNCw6deUOvqc9rzs30SstyFZY2tcS9z1XlqTlELSvGowjxdu2m0ymtqm1emj1jau1iP7+3+PQ=="], + + "@tanstack/react-start-rsc": ["@tanstack/react-start-rsc@0.1.26", "", { "dependencies": { "@tanstack/react-router": "1.170.17", "@tanstack/router-core": "1.171.14", "@tanstack/router-utils": "1.162.2", "@tanstack/start-client-core": "1.170.13", "@tanstack/start-fn-stubs": "1.162.0", "@tanstack/start-plugin-core": "1.171.19", "@tanstack/start-server-core": "1.169.16", "@tanstack/start-storage-context": "1.167.16", "pathe": "^2.0.3" }, "peerDependencies": { "@rspack/core": ">=2.0.0-0", "@vitejs/plugin-rsc": ">=0.5.20", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0", "react-server-dom-rspack": ">=0.0.2" }, "optionalPeers": ["@rspack/core", "@vitejs/plugin-rsc", "react-server-dom-rspack"] }, "sha512-+FMm3qtT1gWsl0i5sG/Q70mh1k7tzZUsPBaqbg4v34zVJZ+XGn1mJb34x2w7z1M0/e7co6GkZfV8BRpczrs8UA=="], + + "@tanstack/react-start-server": ["@tanstack/react-start-server@1.167.21", "", { "dependencies": { "@tanstack/react-router": "1.170.17", "@tanstack/router-core": "1.171.14", "@tanstack/start-server-core": "1.169.16" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-puJ7eFxaLuDzeM/tiLDJaXCA4uK+PZnEOoIC73zipsFqx865MGzrRS/GSZxeVxjavC5iHU+ZwC+rgI0qYSol1A=="], + + "@tanstack/react-store": ["@tanstack/react-store@0.9.3", "", { "dependencies": { "@tanstack/store": "0.9.3", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-y2iHd/N9OkoQbFJLUX1T9vbc2O9tjH0pQRgTcx1/Nz4IlwLvkgpuglXUx+mXt0g5ZDFrEeDnONPqkbfxXJKwRg=="], + + "@tanstack/router-core": ["@tanstack/router-core@1.171.14", "", { "dependencies": { "@tanstack/history": "1.162.0", "cookie-es": "^3.0.0", "seroval": "^1.5.4", "seroval-plugins": "^1.5.4" } }, "sha512-Mo3hwx0qB0cJsVYGDjG0+Ouf7VV74h/vsoDMGztdlyzDanp4gBA2s7IVvm6hFrmQM6GpD9F0Z7SqD7OldfLE7g=="], + + "@tanstack/router-generator": ["@tanstack/router-generator@1.167.18", "", { "dependencies": { "@babel/types": "^7.28.5", "@tanstack/router-core": "1.171.14", "@tanstack/router-utils": "1.162.2", "@tanstack/virtual-file-routes": "1.162.0", "jiti": "^2.7.0", "magic-string": "^0.30.21", "prettier": "^3.5.0", "zod": "^4.4.3" } }, "sha512-kFvM4caRds9Q3EXg64bZubJ6rbDxyV0YDSBSGvOGzmKspQPdz5Xrh0uj5T1Ov8avUUg+c761u04VQAaEzSBXRw=="], + + "@tanstack/router-plugin": ["@tanstack/router-plugin@1.168.19", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "@tanstack/router-core": "1.171.14", "@tanstack/router-generator": "1.167.18", "@tanstack/router-utils": "1.162.2", "chokidar": "^5.0.0", "unplugin": "^3.0.0", "zod": "^4.4.3" }, "peerDependencies": { "@rsbuild/core": ">=1.0.2 || ^2.0.0", "@tanstack/react-router": "^1.170.17", "vite": ">=5.0.0 || >=6.0.0 || >=7.0.0 || >=8.0.0", "vite-plugin-solid": "^2.11.10 || ^3.0.0-0", "webpack": ">=5.92.0" }, "optionalPeers": ["@rsbuild/core", "@tanstack/react-router", "vite", "vite-plugin-solid", "webpack"] }, "sha512-aFglwLc+bbPTgZlkXn3PvOwpjJAfgUyPGSuql4MP3XrqTTh6WkBiy2RYb6oaG5h0s7EKwivEuq85K3Y4V0Mt1g=="], + + "@tanstack/router-utils": ["@tanstack/router-utils@1.162.2", "", { "dependencies": { "@babel/generator": "^7.28.5", "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "ansis": "^4.1.0", "babel-dead-code-elimination": "^1.0.12", "diff": "^8.0.2", "pathe": "^2.0.3", "tinyglobby": "^0.2.15" } }, "sha512-hTWqJtqIFFdvuCl8WXNyrodp2L9zo2G37xKRrcVmVRWpAB2h+U1LuRAfS4tsFTiWOIoE/B+WDVFB8JpoEdw6jQ=="], "@tanstack/server-functions-plugin": ["@tanstack/server-functions-plugin@1.134.5", "", { "dependencies": { "@babel/code-frame": "7.27.1", "@babel/core": "^7.27.7", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1", "@babel/template": "^7.27.2", "@babel/traverse": "^7.27.7", "@babel/types": "^7.27.7", "@tanstack/directive-functions-plugin": "1.134.5", "babel-dead-code-elimination": "^1.0.9", "tiny-invariant": "^1.3.3" } }, "sha512-2sWxq70T+dOEUlE3sHlXjEPhaFZfdPYlWTSkHchWXrFGw2YOAa+hzD6L9wHMjGDQezYd03ue8tQlHG+9Jzbzgw=="], @@ -3029,8 +3126,22 @@ "@tanstack/solid-virtual": ["@tanstack/solid-virtual@3.13.28", "", { "dependencies": { "@tanstack/virtual-core": "3.17.0" }, "peerDependencies": { "solid-js": "^1.3.0" } }, "sha512-kRuOEL5orH/rzGgxNgfgOttsgV6cgrUeupVtrHMITb5p0rZ3hnxhbu/lhKcR9+7x+EJdfUtJIb2CVC85mlw15g=="], + "@tanstack/start-client-core": ["@tanstack/start-client-core@1.170.13", "", { "dependencies": { "@tanstack/router-core": "1.171.14", "@tanstack/start-fn-stubs": "1.162.0", "@tanstack/start-storage-context": "1.167.16", "seroval": "^1.5.4" } }, "sha512-o37M3msIK5ec87kPrIYJWXb1XPnjIe5/jrkGLXiXpFuVL99z7mhoBCzftKtVPtzqI8EElnRE/VGFYT9BHNnWcw=="], + + "@tanstack/start-fn-stubs": ["@tanstack/start-fn-stubs@1.162.0", "", {}, "sha512-QWfUZ3Yo923tdQn38LyKMU8rcTw69zc+T4dAvgTWV4O56SqFRsGfS0lSWIMhJRwXIx/bvdi7nTUBDdZtTHtpTQ=="], + + "@tanstack/start-plugin-core": ["@tanstack/start-plugin-core@1.171.19", "", { "dependencies": { "@babel/code-frame": "7.27.1", "@babel/core": "^7.28.5", "@babel/types": "^7.28.5", "@tanstack/router-core": "1.171.14", "@tanstack/router-generator": "1.167.18", "@tanstack/router-plugin": "1.168.19", "@tanstack/router-utils": "1.162.2", "@tanstack/start-server-core": "1.169.16", "exsolve": "^1.0.7", "lightningcss": "^1.32.0", "pathe": "^2.0.3", "picomatch": "^4.0.3", "seroval": "^1.5.4", "source-map": "^0.7.6", "srvx": "^0.11.9", "tinyglobby": "^0.2.15", "ufo": "^1.5.4", "vitefu": "^1.1.1", "xmlbuilder2": "^4.0.3", "zod": "^4.4.3" }, "peerDependencies": { "@rsbuild/core": "^2.0.0", "vite": ">=7.0.0" }, "optionalPeers": ["@rsbuild/core", "vite"] }, "sha512-+fpW3Z/2vPT8HDV1c5p2WC6/g2k/AV/ujdJVDcn/VFd+gXRtzSX1D/LfozlaDbhDoEsqOnAk/mGwjg60JkUA2Q=="], + + "@tanstack/start-server-core": ["@tanstack/start-server-core@1.169.16", "", { "dependencies": { "@tanstack/history": "1.162.0", "@tanstack/router-core": "1.171.14", "@tanstack/start-client-core": "1.170.13", "@tanstack/start-storage-context": "1.167.16", "fetchdts": "^0.1.6", "h3-v2": "npm:h3@2.0.1-rc.20", "seroval": "^1.5.4" } }, "sha512-lvAjQpH3nHJtd4xy0iHIaWbsTbyN9EBxuYCxbtXH0EpeBQPg+TCPhu9GQC9WbbA1rE//s82CpE55oYDQMqkU5A=="], + + "@tanstack/start-storage-context": ["@tanstack/start-storage-context@1.167.16", "", { "dependencies": { "@tanstack/router-core": "1.171.14" } }, "sha512-zTegxlij4BC1DbCrC6rsVlMOQVMzOuG5IllacZEkrUdhiFwMIMYpk0VWGH+d0ucx5RBkmv8e8GNX3AOVBWclfg=="], + + "@tanstack/store": ["@tanstack/store@0.9.3", "", {}, "sha512-8reSzl/qGWGGVKhBoxXPMWzATSbZLZFWhwBAFO9NAyp0TxzfBP0mIrGb8CP8KrQTmvzXlR/vFPPUrHTLBGyFyw=="], + "@tanstack/virtual-core": ["@tanstack/virtual-core@3.17.0", "", {}, "sha512-gOxY/hFkPh/XQYhnThBHzkbkX3Ed+z/iushyz+R+JAr213aXxUDgQoTgTdrDpBSRsjFM73P/KfUyWmaF9WHMkQ=="], + "@tanstack/virtual-file-routes": ["@tanstack/virtual-file-routes@1.162.0", "", {}, "sha512-uhOeFyxLcU41HzvrxsGpiWdcMbScY1EDgbZ5K7DVRMYInbLYWAC0EA/kx9wXAoSM8q82bUG2hRl8+EAjE6XAbA=="], + "@testing-library/dom": ["@testing-library/dom@10.4.1", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "picocolors": "1.1.1", "pretty-format": "^27.0.2" } }, "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg=="], "@testing-library/jest-dom": ["@testing-library/jest-dom@6.9.1", "", { "dependencies": { "@adobe/css-tools": "^4.4.0", "aria-query": "^5.0.0", "css.escape": "^1.5.1", "dom-accessibility-api": "^0.6.3", "picocolors": "^1.1.1", "redent": "^3.0.0" } }, "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA=="], @@ -3141,7 +3252,7 @@ "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], - "@types/mdx": ["@types/mdx@2.0.13", "", {}, "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw=="], + "@types/mdx": ["@types/mdx@2.0.14", "", {}, "sha512-T48PeuJtvLosNTPVhfnIp3i/n3a4g4Bad7YCq5k64D4u7NwDrAotikQ+5+sjtUvBmxCMlbo3dVL+C2dP0rWHzg=="], "@types/micromatch": ["@types/micromatch@4.0.10", "", { "dependencies": { "@types/braces": "*" } }, "sha512-5jOhFDElqr4DKTrTEbnW8DZ4Hz5LRUEmyrGpCMrD/NphYv3nUnaF08xmSLx1rGGnyEs/kFnhiw6dCgcDqMr5PQ=="], @@ -3179,7 +3290,7 @@ "@types/range-parser": ["@types/range-parser@1.2.7", "", {}, "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ=="], - "@types/react": ["@types/react@18.0.25", "", { "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", "csstype": "^3.0.2" } }, "sha512-xD6c0KDT4m7n9uD4ZHi02lzskaiqcBxf4zi+tXZY98a04wvc0hi/TcCPC2FOESZi51Nd7tlUeOJY8RofL799/g=="], + "@types/react": ["@types/react@19.2.17", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw=="], "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], @@ -3263,7 +3374,7 @@ "@vercel/oidc": ["@vercel/oidc@3.2.0", "", {}, "sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug=="], - "@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="], + "@vitejs/plugin-react": ["@vitejs/plugin-react@6.0.3", "", { "dependencies": { "@rolldown/pluginutils": "^1.0.1" }, "peerDependencies": { "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", "babel-plugin-react-compiler": "^1.0.0", "vite": "^8.0.0" }, "optionalPeers": ["@rolldown/plugin-babel", "babel-plugin-react-compiler"] }, "sha512-vmFvco5/QuC2f9Oj+wTk0+9XeDFkHxSamwZKYc7MxYwKICfvUvlMhqKI0VuICPltGqh1neqBKDvO4kes1ya8vg=="], "@vitest/coverage-v8": ["@vitest/coverage-v8@4.1.8", "", { "dependencies": { "@bcoe/v8-coverage": "^1.0.2", "@vitest/utils": "4.1.8", "ast-v8-to-istanbul": "^1.0.0", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", "istanbul-reports": "^3.2.0", "magicast": "^0.5.2", "obug": "^2.1.1", "std-env": "^4.0.0-rc.1", "tinyrainbow": "^3.1.0" }, "peerDependencies": { "@vitest/browser": "4.1.8", "vitest": "4.1.8" }, "optionalPeers": ["@vitest/browser"] }, "sha512-lt3kovsyHwYe00wq4D1ti0Z974fWj4NLp6siqiyEufUpyFwK9Yhi7rBhac9JL5aA0zoMrJqc4vYPZRUnI7l7nw=="], @@ -3619,6 +3730,8 @@ "citty": ["citty@0.1.6", "", { "dependencies": { "consola": "^3.2.3" } }, "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ=="], + "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="], + "classnames": ["classnames@2.3.2", "", {}, "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw=="], "clean-css": ["clean-css@5.3.3", "", { "dependencies": { "source-map": "~0.6.0" } }, "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg=="], @@ -3651,6 +3764,8 @@ "cmd-shim": ["cmd-shim@8.0.0", "", {}, "sha512-Jk/BK6NCapZ58BKUxlSI+ouKRbjH1NLZCgJkYoab+vEHUY3f6OzpNBN9u7HFSv9J6TRDGs4PLOHezoKGaFRSCA=="], + "cnfast": ["cnfast@0.0.8", "", { "bin": { "cnfast": "bin/cli.js" } }, "sha512-EjXKMfGfdwtV4AcNSQ6AwQaVzpC1B7IxeiwA3FlhTXz+YFlMKVi4c1JX9tgD2QOlahQXjB8KUXrBaYG+3v871Q=="], + "code-excerpt": ["code-excerpt@4.0.0", "", { "dependencies": { "convert-to-spaces": "^2.0.1" } }, "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA=="], "collapse-white-space": ["collapse-white-space@2.1.0", "", {}, "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw=="], @@ -3679,6 +3794,8 @@ "compress-commons": ["compress-commons@6.0.2", "", { "dependencies": { "crc-32": "^1.2.0", "crc32-stream": "^6.0.0", "is-stream": "^2.0.1", "normalize-path": "^3.0.0", "readable-stream": "^4.0.0" } }, "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg=="], + "compute-scroll-into-view": ["compute-scroll-into-view@3.1.1", "", {}, "sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw=="], + "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], "condense-newlines": ["condense-newlines@0.2.1", "", { "dependencies": { "extend-shallow": "^2.0.1", "is-whitespace": "^0.3.0", "kind-of": "^3.0.2" } }, "sha512-P7X+QL9Hb9B/c8HI5BFFKmjgBu2XpQuF98WZ9XkO+dBGgk5XgwiQz7o1SmpglNWId3581UcS0SFAWfoIhMHPfg=="], @@ -4019,6 +4136,8 @@ "estree-util-to-js": ["estree-util-to-js@2.0.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "astring": "^1.8.0", "source-map": "^0.7.0" } }, "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg=="], + "estree-util-value-to-estree": ["estree-util-value-to-estree@3.5.0", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-aMV56R27Gv3QmfmF1MY12GWkGzzeAezAX+UplqHVASfjc9wNzI/X6hC0S9oxq61WT4aQesLGslWP9tKk6ghRZQ=="], + "estree-util-visit": ["estree-util-visit@2.0.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/unist": "^3.0.0" } }, "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww=="], "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], @@ -4111,6 +4230,8 @@ "fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="], + "fetchdts": ["fetchdts@0.1.7", "", {}, "sha512-YoZjBdafyLIop9lSxXVI33oLD5kN31q4Td+CasofLLYeLXRFeOsuOw0Uo+XNRi9PZlbfdlN2GmRtm4tCEQ9/KA=="], + "filelist": ["filelist@1.0.6", "", { "dependencies": { "minimatch": "^5.0.1" } }, "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA=="], "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], @@ -4171,6 +4292,12 @@ "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + "fumadocs-core": ["fumadocs-core@16.11.1", "", { "dependencies": { "@orama/orama": "^3.1.18", "estree-util-value-to-estree": "^3.5.0", "github-slugger": "^2.0.0", "hast-util-to-estree": "^3.1.3", "hast-util-to-jsx-runtime": "^2.3.6", "js-yaml": "^5.2.1", "mdast-util-mdx": "^3.0.0", "mdast-util-to-markdown": "^2.1.2", "remark": "^15.0.1", "remark-gfm": "^4.0.1", "remark-rehype": "^11.1.2", "scroll-into-view-if-needed": "^3.1.0", "shiki": "^4.3.1", "tinyglobby": "^0.2.17", "unified": "^11.0.5", "unist-util-visit": "^5.1.0", "vfile": "^6.0.3" }, "peerDependencies": { "@mdx-js/mdx": "*", "@mixedbread/sdk": "0.x.x", "@orama/core": "1.x.x", "@oramacloud/client": "2.x.x", "@tanstack/react-router": "1.x.x", "@types/estree-jsx": "*", "@types/hast": "*", "@types/mdast": "*", "@types/react": "*", "algoliasearch": "5.x.x", "flexsearch": "*", "lucide-react": "*", "next": "16.x.x", "react": "^19.2.0", "react-dom": "^19.2.0", "react-router": "7.x.x || 8.x.x", "waku": "*", "zod": "4.x.x" }, "optionalPeers": ["@mdx-js/mdx", "@mixedbread/sdk", "@orama/core", "@oramacloud/client", "@tanstack/react-router", "@types/estree-jsx", "@types/hast", "@types/mdast", "@types/react", "algoliasearch", "flexsearch", "lucide-react", "next", "react", "react-dom", "react-router", "waku", "zod"] }, "sha512-tKuh1AKoVTb+f7IoAOM2cfz5djd3YhePeqA95q6mf422gEvDTeJms23OJ+icYRWZ6ryNQ5W/ZsgKEe87M5HVYg=="], + + "fumadocs-mdx": ["fumadocs-mdx@15.1.0", "", { "dependencies": { "@mdx-js/mdx": "^3.1.1", "@standard-schema/spec": "^1.1.0", "chokidar": "^5.0.0", "esbuild": "^0.28.1", "estree-util-value-to-estree": "^3.5.0", "github-slugger": "^2.0.0", "js-yaml": "^5.2.1", "mdast-util-mdx": "^3.0.0", "picocolors": "^1.1.1", "picomatch": "^4.0.5", "tinyexec": "^1.2.4", "tinyglobby": "^0.2.17", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.1.0", "vfile": "^6.0.3", "zod": "^4.4.3" }, "peerDependencies": { "@fumadocs/satteri": "0.x.x", "@types/mdast": "*", "@types/mdx": "*", "@types/react": "*", "fumadocs-core": "^16.7.0", "mdast-util-directive": "*", "next": "^15.3.0 || ^16.0.0", "react": "^19.2.0", "rolldown": "*", "satteri": "^0.9.4", "vite": "7.x.x || 8.x.x" }, "optionalPeers": ["@fumadocs/satteri", "@types/mdast", "@types/mdx", "@types/react", "mdast-util-directive", "next", "react", "rolldown", "satteri", "vite"], "bin": { "fumadocs-mdx": "./bin.js" } }, "sha512-2nDusSlYFuNVcyB51jgY3tA3r01ALTwoURrMDNoc7cbJKZ2sac/PW+CDq6SHTArkgRMmFiKYQGfspJdjgTtPTg=="], + + "fumadocs-ui": ["fumadocs-ui@16.11.1", "", { "dependencies": { "@fuma-translate/react": "^1.0.2", "@fumadocs/tailwind": "0.1.0", "@radix-ui/react-accordion": "^1.2.15", "@radix-ui/react-collapsible": "^1.1.15", "@radix-ui/react-dialog": "^1.1.18", "@radix-ui/react-direction": "^1.1.2", "@radix-ui/react-navigation-menu": "^1.2.17", "@radix-ui/react-popover": "^1.1.18", "@radix-ui/react-presence": "^1.1.6", "@radix-ui/react-scroll-area": "^1.2.13", "@radix-ui/react-slot": "^1.3.0", "@radix-ui/react-tabs": "^1.1.16", "class-variance-authority": "^0.7.1", "cnfast": "^0.0.8", "lucide-react": "^1.23.0", "motion": "^12.42.2", "next-themes": "^0.4.6", "react-remove-scroll": "^2.7.2", "rehype-raw": "^7.0.0", "scroll-into-view-if-needed": "^3.1.0", "shiki": "^4.3.1", "unist-util-visit": "^5.1.0" }, "peerDependencies": { "@takumi-rs/image-response": "*", "@types/mdx": "*", "@types/react": "*", "fumadocs-core": "16.11.1", "next": "16.x.x", "react": "^19.2.0", "react-dom": "^19.2.0" }, "optionalPeers": ["@takumi-rs/image-response", "@types/mdx", "@types/react", "next"] }, "sha512-Dq819PFV4RGhAI9Wd4erSCiRlEDLVOZae+kgE5LeOKFH8mbKX49U8N17ldFOhdkC9EZpxMZdEKul77RDgFHQww=="], + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], "function.prototype.name": ["function.prototype.name@1.1.8", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "functions-have-names": "^1.2.3", "hasown": "^2.0.2", "is-callable": "^1.2.7" } }, "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q=="], @@ -4253,6 +4380,8 @@ "h3": ["h3@2.0.1-rc.4", "", { "dependencies": { "rou3": "^0.7.8", "srvx": "^0.9.1" }, "peerDependencies": { "crossws": "^0.4.1" }, "optionalPeers": ["crossws"] }, "sha512-vZq8pEUp6THsXKXrUXX44eOqfChic2wVQ1GlSzQCBr7DeFBkfIZAo2WyNND4GSv54TAa0E4LYIK73WSPdgKUgw=="], + "h3-v2": ["h3@2.0.1-rc.20", "", { "dependencies": { "rou3": "^0.8.1", "srvx": "^0.11.13" }, "peerDependencies": { "crossws": "^0.4.1" }, "optionalPeers": ["crossws"], "bin": { "h3": "bin/h3.mjs" } }, "sha512-28ljodXuUp0fZovdiSRq4G9OgrxCztrJe5VdYzXAB7ueRvI7pIUqLU14Xi3XqdYJ/khXjfpUOOD2EQa6CmBgsg=="], + "happy-dom": ["happy-dom@20.9.0", "", { "dependencies": { "@types/node": ">=20.0.0", "@types/whatwg-mimetype": "^3.0.2", "@types/ws": "^8.18.1", "entities": "^7.0.1", "whatwg-mimetype": "^3.0.0", "ws": "^8.18.3" } }, "sha512-GZZ9mKe8r646NUAf/zemnGbjYh4Bt8/MqASJY+pSm5ZDtc3YQox+4gsLI7yi1hba6o+eCsGxpHn5+iEVn31/FQ=="], "has-bigints": ["has-bigints@1.1.0", "", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="], @@ -4521,6 +4650,8 @@ "isbinaryfile": ["isbinaryfile@5.0.7", "", {}, "sha512-gnWD14Jh3FzS3CPhF0AxNOJ8CxqeblPTADzI38r0wt8ZyQl5edpy75myt08EG2oKvpyiqSqsx+Wkz9vtkbTqYQ=="], + "isbot": ["isbot@5.2.0", "", {}, "sha512-gbZiGCb4B5xaoxg9mS7koAyRdvJnArk10VLSHOgz6rtBG93/pi1xOFaVvXMKZ7JXgyZ8zAbNRK5uIBdIUTFSqw=="], + "isexe": ["isexe@4.0.0", "", {}, "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw=="], "isomorphic-ws": ["isomorphic-ws@5.0.0", "", { "peerDependencies": { "ws": "*" } }, "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw=="], @@ -4637,6 +4768,8 @@ "lightningcss": ["lightningcss@1.30.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.30.1", "lightningcss-darwin-x64": "1.30.1", "lightningcss-freebsd-x64": "1.30.1", "lightningcss-linux-arm-gnueabihf": "1.30.1", "lightningcss-linux-arm64-gnu": "1.30.1", "lightningcss-linux-arm64-musl": "1.30.1", "lightningcss-linux-x64-gnu": "1.30.1", "lightningcss-linux-x64-musl": "1.30.1", "lightningcss-win32-arm64-msvc": "1.30.1", "lightningcss-win32-x64-msvc": "1.30.1" } }, "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg=="], + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="], + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ=="], "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA=="], @@ -4705,6 +4838,8 @@ "lru_map": ["lru_map@0.4.1", "", {}, "sha512-I+lBvqMMFfqaV8CJCISjI3wbjmwVu/VyOoU7+qtu9d7ioW5klMgsTTiUOUp+DJvfTTzKXoPbyC6YfgkNcyPSOg=="], + "lucide-react": ["lucide-react@1.24.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-YT6mBD8lGKkg4nM39enlm94/sfJIiW0YKUT60fBy4YK8tai31ylg1VhGNWxkpSKHo9UagfnZqwIff3HTDQwXeA=="], + "luxon": ["luxon@3.6.1", "", {}, "sha512-tJLxrKJhO2ukZ5z0gyjY1zPh3Rh88Ej9P7jNrZiHMUXHae1yvI2imgOZtL1TO8TW6biMMKfTtAOoEJANgtWBMQ=="], "lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="], @@ -4953,6 +5088,8 @@ "next-mdx-remote-client": ["next-mdx-remote-client@1.1.8", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@mdx-js/mdx": "^3.1.1", "@mdx-js/react": "^3.1.1", "@types/mdx": "^2.0.13", "remark-mdx-remove-esm": "^1.3.2", "serialize-error": "^13.0.1", "vfile": "^6.0.3", "vfile-matter": "^5.0.1" }, "peerDependencies": { "react": ">= 18.3.0 < 19.0.0", "react-dom": ">= 18.3.0 < 19.0.0" } }, "sha512-IElOrn02JjGQZxx+re7wMx/1AUG+Arte9aDImAtxjAfMw6xuSCaH5mTCunKelkWzFyFdRb565jO8jRICvvh96g=="], + "next-themes": ["next-themes@0.4.6", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="], + "nf3": ["nf3@0.1.12", "", {}, "sha512-qbMXT7RTGh74MYWPeqTIED8nDW70NXOULVHpdWcdZ7IVHVnAsMV9fNugSNnvooipDc1FMOzpis7T9nXJEbJhvQ=="], "nimma": ["nimma@0.2.3", "", { "dependencies": { "@jsep-plugin/regex": "^1.0.1", "@jsep-plugin/ternary": "^1.0.2", "astring": "^1.8.1", "jsep": "^1.2.0" }, "optionalDependencies": { "jsonpath-plus": "^6.0.1 || ^10.1.0", "lodash.topath": "^4.5.2" } }, "sha512-1ZOI8J+1PKKGceo/5CT5GfQOG6H8I2BencSK06YarZ2wXwH37BSSUWldqJmMJYA5JfqDqffxDXynt6f11AyKcA=="], @@ -5329,7 +5466,7 @@ "react-docgen-typescript": ["react-docgen-typescript@2.4.0", "", { "peerDependencies": { "typescript": ">= 4.3.x" } }, "sha512-ZtAp5XTO5HRzQctjPU0ybY0RRCQO19X/8fxn3w7y2VVTUbGHDKULPTL4ky3vB05euSgG5NpALhEhDPvQ56wvXg=="], - "react-dom": ["react-dom@18.2.0", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" }, "peerDependencies": { "react": "^18.2.0" } }, "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g=="], + "react-dom": ["react-dom@19.2.7", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.7" } }, "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ=="], "react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="], @@ -5337,7 +5474,7 @@ "react-refresh": ["react-refresh@0.17.0", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="], - "react-remove-scroll": ["react-remove-scroll@2.5.5", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.3", "react-style-singleton": "^2.2.1", "tslib": "^2.1.0", "use-callback-ref": "^1.3.0", "use-sidecar": "^1.1.2" }, "peerDependencies": { "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw=="], + "react-remove-scroll": ["react-remove-scroll@2.7.2", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q=="], "react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="], @@ -5419,7 +5556,7 @@ "remark-math": ["remark-math@6.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-math": "^3.0.0", "micromark-extension-math": "^3.0.0", "unified": "^11.0.0" } }, "sha512-MMqgnP74Igy+S3WwnhQ7kqGlEerTETXMvJhrUzDikVZ2/uogJCb+WHUg97hK9/jcfc0dkD73s3LN8zU49cTEtA=="], - "remark-mdx": ["remark-mdx@3.1.0", "", { "dependencies": { "mdast-util-mdx": "^3.0.0", "micromark-extension-mdxjs": "^3.0.0" } }, "sha512-Ngl/H3YXyBV9RcRNdlYsZujAmhsxwzxpDzpDEhFBVAGthS4GDgnctpDjgFl/ULx5UEDzqtW1cyBSNKqYYrqLBA=="], + "remark-mdx": ["remark-mdx@3.1.1", "", { "dependencies": { "mdast-util-mdx": "^3.0.0", "micromark-extension-mdxjs": "^3.0.0" } }, "sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg=="], "remark-mdx-remove-esm": ["remark-mdx-remove-esm@1.3.2", "", { "dependencies": { "@types/mdast": "^4.0.4", "unist-util-remove": "^4.0.0" }, "peerDependencies": { "unified": "^11" } }, "sha512-BvL8VSdVXy9S7NlHP56nUJAHFc45h5E9HnHiLUGHe5tw3Yvm/3cVZvAzlkEEh2i+fkq2uKrf2xn5VmItBhMypA=="], @@ -5517,7 +5654,9 @@ "sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="], - "scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], + "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], + + "scroll-into-view-if-needed": ["scroll-into-view-if-needed@3.1.0", "", { "dependencies": { "compute-scroll-into-view": "^3.0.2" } }, "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ=="], "section-matter": ["section-matter@1.0.0", "", { "dependencies": { "extend-shallow": "^2.0.1", "kind-of": "^6.0.0" } }, "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA=="], @@ -6123,6 +6262,8 @@ "xmlbuilder": ["xmlbuilder@11.0.1", "", {}, "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="], + "xmlbuilder2": ["xmlbuilder2@4.0.3", "", { "dependencies": { "@oozcitak/dom": "^2.0.2", "@oozcitak/infra": "^2.0.2", "@oozcitak/util": "^10.0.0", "js-yaml": "^4.1.1" } }, "sha512-bx8Q1STctnNaaDymWnkfQLKofs0mGNN7rLLapJlGuV3VlvegD7Ls4ggMjE3aUSWItCCzU0PEv45lI87iSigiCA=="], + "xmlhttprequest-ssl": ["xmlhttprequest-ssl@2.1.2", "", {}, "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ=="], "xss": ["xss@1.0.15", "", { "dependencies": { "commander": "^2.20.3", "cssfilter": "0.0.10" }, "bin": { "xss": "bin/xss" } }, "sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg=="], @@ -6475,6 +6616,8 @@ "@fastify/proxy-addr/ipaddr.js": ["ipaddr.js@2.4.0", "", {}, "sha512-9VGk3HGanVE6JoZXHiCpnGy5X0jYDnN4EA4lntFPj+1vIWlFhIylq2CrrCOJH9EAhc5CYhq18F2Av2tgoAPsYQ=="], + "@fuma-translate/react/react": ["react@19.2.7", "", {}, "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ=="], + "@hey-api/json-schema-ref-parser/js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], "@hey-api/openapi-ts/open": ["open@11.0.0", "", { "dependencies": { "default-browser": "^5.4.0", "define-lazy-prop": "^3.0.0", "is-in-ssh": "^1.0.0", "is-inside-container": "^1.0.0", "powershell-utils": "^0.1.0", "wsl-utils": "^0.3.0" } }, "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw=="], @@ -6485,26 +6628,34 @@ "@inquirer/core/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="], + "@jsx-email/cli/@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="], + "@jsx-email/cli/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "@jsx-email/cli/esbuild": ["esbuild@0.19.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.19.12", "@esbuild/android-arm": "0.19.12", "@esbuild/android-arm64": "0.19.12", "@esbuild/android-x64": "0.19.12", "@esbuild/darwin-arm64": "0.19.12", "@esbuild/darwin-x64": "0.19.12", "@esbuild/freebsd-arm64": "0.19.12", "@esbuild/freebsd-x64": "0.19.12", "@esbuild/linux-arm": "0.19.12", "@esbuild/linux-arm64": "0.19.12", "@esbuild/linux-ia32": "0.19.12", "@esbuild/linux-loong64": "0.19.12", "@esbuild/linux-mips64el": "0.19.12", "@esbuild/linux-ppc64": "0.19.12", "@esbuild/linux-riscv64": "0.19.12", "@esbuild/linux-s390x": "0.19.12", "@esbuild/linux-x64": "0.19.12", "@esbuild/netbsd-x64": "0.19.12", "@esbuild/openbsd-x64": "0.19.12", "@esbuild/sunos-x64": "0.19.12", "@esbuild/win32-arm64": "0.19.12", "@esbuild/win32-ia32": "0.19.12", "@esbuild/win32-x64": "0.19.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg=="], + "@jsx-email/cli/react-dom": ["react-dom@18.2.0", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" }, "peerDependencies": { "react": "^18.2.0" } }, "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g=="], + "@jsx-email/cli/tailwindcss": ["tailwindcss@3.3.3", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", "chokidar": "^3.5.3", "didyoumean": "^1.2.2", "dlv": "^1.1.3", "fast-glob": "^3.2.12", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", "jiti": "^1.18.2", "lilconfig": "^2.1.0", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", "picocolors": "^1.0.0", "postcss": "^8.4.23", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", "postcss-load-config": "^4.0.1", "postcss-nested": "^6.0.1", "postcss-selector-parser": "^6.0.11", "resolve": "^1.22.2", "sucrase": "^3.32.0" }, "bin": { "tailwind": "lib/cli.js", "tailwindcss": "lib/cli.js" } }, "sha512-A0KgSkef7eE4Mf+nKJ83i75TMyq8HqY3qmFIJSWy8bNt0v1lG7jUcpGpoTFxAwYcWOphcTBLPPJg+bDfhDf52w=="], "@jsx-email/cli/vite": ["vite@4.5.14", "", { "dependencies": { "esbuild": "^0.18.10", "postcss": "^8.4.27", "rollup": "^3.27.1" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@types/node": ">= 14", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-+v57oAaoYNnO3hIu5Z/tJRZjq5aHM2zDve9YZ8HngVHbhk66RStobhb1sqPMIPEleV6cNKYK4eGrAbE9Ulbl2g=="], "@jsx-email/doiuse-email/htmlparser2": ["htmlparser2@9.1.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.1.0", "entities": "^4.5.0" } }, "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ=="], + "@jsx-email/tailwind/react-dom": ["react-dom@18.2.0", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" }, "peerDependencies": { "react": "^18.2.0" } }, "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g=="], + "@kobalte/core/solid-presence": ["solid-presence@0.1.8", "", { "dependencies": { "@corvu/utils": "~0.4.0" }, "peerDependencies": { "solid-js": "^1.8" } }, "sha512-pWGtXUFWYYUZNbg5YpG5vkQJyOtzn2KXhxYaMx/4I+lylTLYkITOLevaCwMRN+liCVk0pqB6EayLWojNqBFECA=="], "@malept/flatpak-bundler/fs-extra": ["fs-extra@9.1.0", "", { "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ=="], + "@mdx-js/mdx/@types/mdx": ["@types/mdx@2.0.13", "", {}, "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw=="], + "@mdx-js/mdx/acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], - "@mdx-js/mdx/remark-mdx": ["remark-mdx@3.1.1", "", { "dependencies": { "mdast-util-mdx": "^3.0.0", "micromark-extension-mdxjs": "^3.0.0" } }, "sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg=="], - "@mdx-js/mdx/source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], + "@mdx-js/react/@types/mdx": ["@types/mdx@2.0.13", "", {}, "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw=="], + "@mintlify/cli/chalk": ["chalk@5.2.0", "", {}, "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA=="], "@mintlify/cli/fs-extra": ["fs-extra@11.2.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw=="], @@ -6541,6 +6692,8 @@ "@mintlify/common/remark-gfm": ["remark-gfm@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA=="], + "@mintlify/common/remark-mdx": ["remark-mdx@3.1.0", "", { "dependencies": { "mdast-util-mdx": "^3.0.0", "micromark-extension-mdxjs": "^3.0.0" } }, "sha512-Ngl/H3YXyBV9RcRNdlYsZujAmhsxwzxpDzpDEhFBVAGthS4GDgnctpDjgFl/ULx5UEDzqtW1cyBSNKqYYrqLBA=="], + "@mintlify/common/remark-rehype": ["remark-rehype@11.1.1", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "mdast-util-to-hast": "^13.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ=="], "@mintlify/common/sucrase": ["sucrase@3.34.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "glob": "7.1.6", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw=="], @@ -6559,6 +6712,8 @@ "@mintlify/mdx/react": ["react@19.2.3", "", {}, "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA=="], + "@mintlify/mdx/react-dom": ["react-dom@18.2.0", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" }, "peerDependencies": { "react": "^18.2.0" } }, "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g=="], + "@mintlify/mdx/shiki": ["shiki@3.23.0", "", { "dependencies": { "@shikijs/core": "3.23.0", "@shikijs/engine-javascript": "3.23.0", "@shikijs/engine-oniguruma": "3.23.0", "@shikijs/langs": "3.23.0", "@shikijs/themes": "3.23.0", "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA=="], "@mintlify/prebuild/chalk": ["chalk@5.3.0", "", {}, "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w=="], @@ -6689,6 +6844,8 @@ "@opencode-ai/cli/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + "@opencode-ai/console-mail/@types/react": ["@types/react@18.0.25", "", { "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", "csstype": "^3.0.2" } }, "sha512-xD6c0KDT4m7n9uD4ZHi02lzskaiqcBxf4zi+tXZY98a04wvc0hi/TcCPC2FOESZi51Nd7tlUeOJY8RofL799/g=="], + "@opencode-ai/core/@ai-sdk/openai": ["@ai-sdk/openai@3.0.53", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Wld+Rbc05KaUn08uBt06eEuwcgalcIFtIl32Yp+GxuZXUQwOb6YeAuq+C6da4ch6BurFoqEaLemJVwjBb7x+PQ=="], "@opencode-ai/core/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.41", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-kNAGINk71AlOXx10Dq/PXw4t/9XjdK8uxfpVElRwtSFMdeSiLVt58p9TPx4/FJD+hxZuVhvxYj9r42osxWq79g=="], @@ -6709,10 +6866,24 @@ "@opencode-ai/session-ui/@solid-primitives/resize-observer": ["@solid-primitives/resize-observer@2.1.3", "", { "dependencies": { "@solid-primitives/event-listener": "^2.4.3", "@solid-primitives/rootless": "^1.5.2", "@solid-primitives/static-store": "^0.1.2", "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-zBLje5E06TgOg93S7rGPldmhDnouNGhvfZVKOp+oG2XU8snA+GoCSSCz1M+jpNAg5Ek2EakU5UVQqL152WmdXQ=="], + "@opencode-ai/storybook/@types/react": ["@types/react@18.0.25", "", { "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", "csstype": "^3.0.2" } }, "sha512-xD6c0KDT4m7n9uD4ZHi02lzskaiqcBxf4zi+tXZY98a04wvc0hi/TcCPC2FOESZi51Nd7tlUeOJY8RofL799/g=="], + "@opencode-ai/ui/@solid-primitives/resize-observer": ["@solid-primitives/resize-observer@2.1.3", "", { "dependencies": { "@solid-primitives/event-listener": "^2.4.3", "@solid-primitives/rootless": "^1.5.2", "@solid-primitives/static-store": "^0.1.2", "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-zBLje5E06TgOg93S7rGPldmhDnouNGhvfZVKOp+oG2XU8snA+GoCSSCz1M+jpNAg5Ek2EakU5UVQqL152WmdXQ=="], "@opencode-ai/web/@shikijs/transformers": ["@shikijs/transformers@3.20.0", "", { "dependencies": { "@shikijs/core": "3.20.0", "@shikijs/types": "3.20.0" } }, "sha512-PrHHMRr3Q5W1qB/42kJW6laqFyWdhrPF2hNR9qjOm1xcSiAO3hAHo7HaVyHE6pMyevmy3i51O8kuGGXC78uK3g=="], + "@opencode-ai/www/@cloudflare/vite-plugin": ["@cloudflare/vite-plugin@1.44.0", "", { "dependencies": { "@cloudflare/unenv-preset": "2.16.1", "miniflare": "4.20260708.1", "unenv": "2.0.0-rc.24", "wrangler": "4.110.0", "ws": "8.21.0" }, "peerDependencies": { "vite": "^6.1.0 || ^7.0.0 || ^8.0.0" }, "bin": { "cf-vite": "bin/cf-vite" } }, "sha512-8wGGunqRcs34o4GRq0Rurp7GZg30xtLJeRGUU81a49r9zQRjlp3xIlsWr3nFlSCso4eE3cjZfiKC/2y116M4TQ=="], + + "@opencode-ai/www/@tailwindcss/vite": ["@tailwindcss/vite@4.3.2", "", { "dependencies": { "@tailwindcss/node": "4.3.2", "@tailwindcss/oxide": "4.3.2", "tailwindcss": "4.3.2" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7 || ^8" } }, "sha512-eHpMeX4JXfVNJDEcsouTeCBubJBTcTLigeaw/NTUW6PB5ATKKXdyonnXgTBX2VuRbjz1hjfz6C5XAhr52ImQXA=="], + + "@opencode-ai/www/react": ["react@19.2.7", "", {}, "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ=="], + + "@opencode-ai/www/tailwindcss": ["tailwindcss@4.3.2", "", {}, "sha512-WtctNNSH8A9jlMIqxzuYumOHU5uGZyRv0Q5svQl+oEPy5w84YpBxdb7MdqyiSPQge5jTJ6zFQLq0PFygdccSBA=="], + + "@opencode-ai/www/vite": ["vite@8.1.4", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.5", "postcss": "^8.5.16", "rolldown": "~1.1.4", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.3.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-bTT9PsdWO+MQMNG9ZXIP/qM9wGh37DFxTV/sPq9cFpHr3w4jkgef032PkAL9jAqhk3Nz8NQw3O8n6/xFkqO4QQ=="], + + "@opencode-ai/www/wrangler": ["wrangler@4.110.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.5.0", "@cloudflare/unenv-preset": "2.16.1", "blake3-wasm": "2.1.5", "esbuild": "0.28.1", "miniflare": "4.20260708.1", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20260708.1" }, "optionalDependencies": { "fsevents": "2.3.3" }, "peerDependencies": { "@cloudflare/workers-types": "^5.20260708.1" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "cf-wrangler": "bin/cf-wrangler.js", "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-xZeXKYi7hxQRF5anL+v77RkufJNpF9f3Eqeyqq2QBsETpLZgh0Agj0jJ6JPtkbgn6ukZdh8OK5egsGPWIditgg=="], + "@opentui/core/diff": ["diff@9.0.0", "", {}, "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw=="], "@opentui/solid/@babel/core": ["@babel/core@7.28.0", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.0", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.27.3", "@babel/helpers": "^7.27.6", "@babel/parser": "^7.28.0", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.0", "@babel/types": "^7.28.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ=="], @@ -6743,6 +6914,146 @@ "@puppeteer/browsers/yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], + "@radix-ui/react-accordion/@radix-ui/primitive": ["@radix-ui/primitive@1.1.5", "", {}, "sha512-d86WIWFYNtGA0H/d8exstrTRTp7eWJYlYJbtNofxr/3ljupZYn6EFDG/Qgu/0Kc8v7yMUxySagqJsL1+PdYjWg=="], + + "@radix-ui/react-accordion/@radix-ui/react-collapsible": ["@radix-ui/react-collapsible@1.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.5", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-presence": "1.1.7", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-opfXRe6nnzyGmCDPx+l1Aqo/RbqWtQal2FnsBqF9hhePp6j0LsRoBaRxcMOlTv+uYTJVtWYZKg9t9wTe+BA/ZA=="], + + "@radix-ui/react-accordion/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA=="], + + "@radix-ui/react-accordion/@radix-ui/react-context": ["@radix-ui/react-context@1.2.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-fOE+JtN9rygNZkCnHRBEP0TAvLldlhyOxMsbwFvTP4nAs+nBmfnna+o/Zski2wkmY1YMrFC0aSzsHoLY47iLrg=="], + + "@radix-ui/react-accordion/@radix-ui/react-id": ["@radix-ui/react-id@1.1.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA=="], + + "@radix-ui/react-accordion/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.7", "", { "dependencies": { "@radix-ui/react-slot": "1.3.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bC3NiwsprbxKjuon9l7X6BUTw7FPVzEYaL92MPEY5SCd/9hUTPXVFtVwRix7778wtRsVao+zE062gL79FZleeQ=="], + + "@radix-ui/react-accordion/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.3", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.3", "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA=="], + + "@radix-ui/react-arrow/react-dom": ["react-dom@18.2.0", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" }, "peerDependencies": { "react": "^18.2.0" } }, "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g=="], + + "@radix-ui/react-collapsible/@radix-ui/react-presence": ["@radix-ui/react-presence@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-compose-refs": "1.0.1", "@radix-ui/react-use-layout-effect": "1.0.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg=="], + + "@radix-ui/react-collapsible/react-dom": ["react-dom@18.2.0", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" }, "peerDependencies": { "react": "^18.2.0" } }, "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g=="], + + "@radix-ui/react-collection/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA=="], + + "@radix-ui/react-collection/@radix-ui/react-context": ["@radix-ui/react-context@1.2.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-fOE+JtN9rygNZkCnHRBEP0TAvLldlhyOxMsbwFvTP4nAs+nBmfnna+o/Zski2wkmY1YMrFC0aSzsHoLY47iLrg=="], + + "@radix-ui/react-collection/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.7", "", { "dependencies": { "@radix-ui/react-slot": "1.3.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bC3NiwsprbxKjuon9l7X6BUTw7FPVzEYaL92MPEY5SCd/9hUTPXVFtVwRix7778wtRsVao+zE062gL79FZleeQ=="], + + "@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.3.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA=="], + + "@radix-ui/react-dialog/@radix-ui/primitive": ["@radix-ui/primitive@1.1.5", "", {}, "sha512-d86WIWFYNtGA0H/d8exstrTRTp7eWJYlYJbtNofxr/3ljupZYn6EFDG/Qgu/0Kc8v7yMUxySagqJsL1+PdYjWg=="], + + "@radix-ui/react-dialog/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA=="], + + "@radix-ui/react-dialog/@radix-ui/react-context": ["@radix-ui/react-context@1.2.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-fOE+JtN9rygNZkCnHRBEP0TAvLldlhyOxMsbwFvTP4nAs+nBmfnna+o/Zski2wkmY1YMrFC0aSzsHoLY47iLrg=="], + + "@radix-ui/react-dialog/@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.5", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-effect-event": "0.0.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-b0XaRlzn2QKuo10XyNgi2DAJDf5XC9d1nD3FJcuvCjbR7+4Ad28zmZsLsqx+hvDEzMnRuZaZxZm9gYObV6RmRA=="], + + "@radix-ui/react-dialog/@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.4", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-cot/aB/mOm0IYVYTTmQcEEK1M48lZWi8FlYe5nDPQQ8NYZUlXEFgncJ9p2Kzer3RKSrY7cTTpEMLZKNo9QoP5Q=="], + + "@radix-ui/react-dialog/@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.12", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-callback-ref": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-jjk/lqTeNL0azUx5ZYzVrl4NgaDIrdzTNE4mABV9yBFI7FQqN7pIgzV1bTleUezP2QiTGA1BFTqY8MegDgWX9A=="], + + "@radix-ui/react-dialog/@radix-ui/react-id": ["@radix-ui/react-id@1.1.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA=="], + + "@radix-ui/react-dialog/@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.13", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-z3oXfmaHLJTF1wktbjgD6cn9jiEbq3WSondB10LIuIt2m2Ym4iJlrW04/euMwENDdWDdE7z+OuY7Qyp1YpRSwA=="], + + "@radix-ui/react-dialog/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.7", "", { "dependencies": { "@radix-ui/react-slot": "1.3.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bC3NiwsprbxKjuon9l7X6BUTw7FPVzEYaL92MPEY5SCd/9hUTPXVFtVwRix7778wtRsVao+zE062gL79FZleeQ=="], + + "@radix-ui/react-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.3.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA=="], + + "@radix-ui/react-dialog/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.3", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.3", "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA=="], + + "@radix-ui/react-dismissable-layer/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" }, "optionalPeers": ["@types/react"] }, "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ=="], + + "@radix-ui/react-dismissable-layer/react-dom": ["react-dom@18.2.0", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" }, "peerDependencies": { "react": "^18.2.0" } }, "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g=="], + + "@radix-ui/react-focus-scope/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" }, "optionalPeers": ["@types/react"] }, "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ=="], + + "@radix-ui/react-focus-scope/react-dom": ["react-dom@18.2.0", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" }, "peerDependencies": { "react": "^18.2.0" } }, "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g=="], + + "@radix-ui/react-navigation-menu/@radix-ui/primitive": ["@radix-ui/primitive@1.1.5", "", {}, "sha512-d86WIWFYNtGA0H/d8exstrTRTp7eWJYlYJbtNofxr/3ljupZYn6EFDG/Qgu/0Kc8v7yMUxySagqJsL1+PdYjWg=="], + + "@radix-ui/react-navigation-menu/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA=="], + + "@radix-ui/react-navigation-menu/@radix-ui/react-context": ["@radix-ui/react-context@1.2.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-fOE+JtN9rygNZkCnHRBEP0TAvLldlhyOxMsbwFvTP4nAs+nBmfnna+o/Zski2wkmY1YMrFC0aSzsHoLY47iLrg=="], + + "@radix-ui/react-navigation-menu/@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.5", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-effect-event": "0.0.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-b0XaRlzn2QKuo10XyNgi2DAJDf5XC9d1nD3FJcuvCjbR7+4Ad28zmZsLsqx+hvDEzMnRuZaZxZm9gYObV6RmRA=="], + + "@radix-ui/react-navigation-menu/@radix-ui/react-id": ["@radix-ui/react-id@1.1.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA=="], + + "@radix-ui/react-navigation-menu/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.7", "", { "dependencies": { "@radix-ui/react-slot": "1.3.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bC3NiwsprbxKjuon9l7X6BUTw7FPVzEYaL92MPEY5SCd/9hUTPXVFtVwRix7778wtRsVao+zE062gL79FZleeQ=="], + + "@radix-ui/react-navigation-menu/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.3", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.3", "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA=="], + + "@radix-ui/react-navigation-menu/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA=="], + + "@radix-ui/react-navigation-menu/@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.2.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.7" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-1wNZBggTDK3GRuuQ6nP4k2yi7a6l7I5qbMPbZcRsrGsGVead/f/d5FhEzUvqFs0bcrDLx7n1zKQ3JvLR6whaaw=="], + + "@radix-ui/react-popover/@radix-ui/react-presence": ["@radix-ui/react-presence@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-compose-refs": "1.0.1", "@radix-ui/react-use-layout-effect": "1.0.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg=="], + + "@radix-ui/react-popover/react-dom": ["react-dom@18.2.0", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" }, "peerDependencies": { "react": "^18.2.0" } }, "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g=="], + + "@radix-ui/react-popover/react-remove-scroll": ["react-remove-scroll@2.5.5", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.3", "react-style-singleton": "^2.2.1", "tslib": "^2.1.0", "use-callback-ref": "^1.3.0", "use-sidecar": "^1.1.2" }, "peerDependencies": { "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw=="], + + "@radix-ui/react-popper/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" }, "optionalPeers": ["@types/react"] }, "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ=="], + + "@radix-ui/react-popper/react-dom": ["react-dom@18.2.0", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" }, "peerDependencies": { "react": "^18.2.0" } }, "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g=="], + + "@radix-ui/react-portal/react-dom": ["react-dom@18.2.0", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" }, "peerDependencies": { "react": "^18.2.0" } }, "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g=="], + + "@radix-ui/react-presence/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA=="], + + "@radix-ui/react-primitive/react-dom": ["react-dom@18.2.0", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" }, "peerDependencies": { "react": "^18.2.0" } }, "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g=="], + + "@radix-ui/react-roving-focus/@radix-ui/react-collection": ["@radix-ui/react-collection@1.0.3", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-compose-refs": "1.0.1", "@radix-ui/react-context": "1.0.1", "@radix-ui/react-primitive": "1.0.3", "@radix-ui/react-slot": "1.0.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA=="], + + "@radix-ui/react-roving-focus/@radix-ui/react-direction": ["@radix-ui/react-direction@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" }, "optionalPeers": ["@types/react"] }, "sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA=="], + + "@radix-ui/react-roving-focus/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" }, "optionalPeers": ["@types/react"] }, "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ=="], + + "@radix-ui/react-roving-focus/react-dom": ["react-dom@18.2.0", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" }, "peerDependencies": { "react": "^18.2.0" } }, "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g=="], + + "@radix-ui/react-scroll-area/@radix-ui/primitive": ["@radix-ui/primitive@1.1.5", "", {}, "sha512-d86WIWFYNtGA0H/d8exstrTRTp7eWJYlYJbtNofxr/3ljupZYn6EFDG/Qgu/0Kc8v7yMUxySagqJsL1+PdYjWg=="], + + "@radix-ui/react-scroll-area/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA=="], + + "@radix-ui/react-scroll-area/@radix-ui/react-context": ["@radix-ui/react-context@1.2.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-fOE+JtN9rygNZkCnHRBEP0TAvLldlhyOxMsbwFvTP4nAs+nBmfnna+o/Zski2wkmY1YMrFC0aSzsHoLY47iLrg=="], + + "@radix-ui/react-scroll-area/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.7", "", { "dependencies": { "@radix-ui/react-slot": "1.3.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bC3NiwsprbxKjuon9l7X6BUTw7FPVzEYaL92MPEY5SCd/9hUTPXVFtVwRix7778wtRsVao+zE062gL79FZleeQ=="], + + "@radix-ui/react-scroll-area/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA=="], + + "@radix-ui/react-tabs/@radix-ui/primitive": ["@radix-ui/primitive@1.1.5", "", {}, "sha512-d86WIWFYNtGA0H/d8exstrTRTp7eWJYlYJbtNofxr/3ljupZYn6EFDG/Qgu/0Kc8v7yMUxySagqJsL1+PdYjWg=="], + + "@radix-ui/react-tabs/@radix-ui/react-context": ["@radix-ui/react-context@1.2.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-fOE+JtN9rygNZkCnHRBEP0TAvLldlhyOxMsbwFvTP4nAs+nBmfnna+o/Zski2wkmY1YMrFC0aSzsHoLY47iLrg=="], + + "@radix-ui/react-tabs/@radix-ui/react-id": ["@radix-ui/react-id@1.1.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA=="], + + "@radix-ui/react-tabs/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.7", "", { "dependencies": { "@radix-ui/react-slot": "1.3.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bC3NiwsprbxKjuon9l7X6BUTw7FPVzEYaL92MPEY5SCd/9hUTPXVFtVwRix7778wtRsVao+zE062gL79FZleeQ=="], + + "@radix-ui/react-tabs/@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.5", "@radix-ui/react-collection": "1.1.12", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-direction": "1.1.2", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-is-hydrated": "0.1.1", "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-40svmmugfM3mUN7VUDGVE1tQGOhyi8enlGD0CNJEcMM36C1f71PKM21DFgNHUfem0XnA+d8H8oN3Z9ZpJjSslg=="], + + "@radix-ui/react-tabs/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.3", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.3", "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA=="], + + "@radix-ui/react-toggle/react-dom": ["react-dom@18.2.0", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" }, "peerDependencies": { "react": "^18.2.0" } }, "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g=="], + + "@radix-ui/react-toggle-group/@radix-ui/react-direction": ["@radix-ui/react-direction@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" }, "optionalPeers": ["@types/react"] }, "sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA=="], + + "@radix-ui/react-toggle-group/react-dom": ["react-dom@18.2.0", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" }, "peerDependencies": { "react": "^18.2.0" } }, "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g=="], + + "@radix-ui/react-tooltip/@radix-ui/react-presence": ["@radix-ui/react-presence@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-compose-refs": "1.0.1", "@radix-ui/react-use-layout-effect": "1.0.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg=="], + + "@radix-ui/react-tooltip/react-dom": ["react-dom@18.2.0", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" }, "peerDependencies": { "react": "^18.2.0" } }, "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g=="], + + "@radix-ui/react-use-controllable-state/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" }, "optionalPeers": ["@types/react"] }, "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ=="], + + "@radix-ui/react-use-effect-event/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA=="], + + "@radix-ui/react-use-escape-keydown/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" }, "optionalPeers": ["@types/react"] }, "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ=="], + + "@radix-ui/react-visually-hidden/react-dom": ["react-dom@18.2.0", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" }, "peerDependencies": { "react": "^18.2.0" } }, "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g=="], + "@rolldown/binding-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.6", "", { "dependencies": { "@tybys/wasm-util": "^0.10.3" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg=="], "@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], @@ -6829,6 +7140,8 @@ "@stoplight/yaml/@stoplight/types": ["@stoplight/types@14.1.1", "", { "dependencies": { "@types/json-schema": "^7.0.4", "utility-types": "^3.10.0" } }, "sha512-/kjtr+0t0tjKr+heVfviO9FrU/uGLc+QNX3fHJc19xsCNYqU7lVhaXxDmEID9BZTjG+/r9pK9xP/xU02XGg65g=="], + "@storybook/addon-docs/react-dom": ["react-dom@18.2.0", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" }, "peerDependencies": { "react": "^18.2.0" } }, "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g=="], + "@storybook/csf-plugin/unplugin": ["unplugin@2.3.11", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" }, "bundled": true }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="], @@ -6845,10 +7158,48 @@ "@tanstack/directive-functions-plugin/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], + "@tanstack/directive-functions-plugin/@tanstack/router-utils": ["@tanstack/router-utils@1.133.19", "", { "dependencies": { "@babel/core": "^7.27.4", "@babel/generator": "^7.27.5", "@babel/parser": "^7.27.5", "@babel/preset-typescript": "^7.27.1", "ansis": "^4.1.0", "diff": "^8.0.2", "pathe": "^2.0.3", "tinyglobby": "^0.2.15" } }, "sha512-WEp5D2gPxvlLDRXwD/fV7RXjYtqaqJNXKB/L6OyZEbT+9BG/Ib2d7oG9GSUZNNMGPGYAlhBUOi3xutySsk6rxA=="], + + "@tanstack/router-core/cookie-es": ["cookie-es@3.1.1", "", {}, "sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg=="], + + "@tanstack/router-core/seroval": ["seroval@1.5.5", "", {}, "sha512-bSjOuPcwPKLSJNhr9+bZxA20nQxVle5J5MNsYRVE6cIg7KpRLXGupymePavu0jrxlPiPsr4xGZSB8yUY2sH2sw=="], + + "@tanstack/router-core/seroval-plugins": ["seroval-plugins@1.5.5", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-+BDhqYM6CEn3x09v44dpa9p6974FuUB2dxk+Ctn04k0cO1Zt6QODTXfmEZK0eBaTe/fJBvP4NMGuNJ+R8T+QMg=="], + + "@tanstack/router-generator/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], + + "@tanstack/router-plugin/@babel/core": ["@babel/core@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-compilation-targets": "^7.29.7", "@babel/helper-module-transforms": "^7.29.7", "@babel/helpers": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA=="], + + "@tanstack/router-plugin/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], + + "@tanstack/router-plugin/unplugin": ["unplugin@3.3.0", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "picomatch": "^4.0.4", "webpack-virtual-modules": "^0.6.2" }, "peerDependencies": { "@farmfe/core": "*", "@rspack/core": "*", "bun-types-no-globals": "*", "esbuild": "*", "rolldown": "*", "rollup": "*", "unloader": "*", "vite": "*", "webpack": "*" }, "optionalPeers": ["@farmfe/core", "@rspack/core", "bun-types-no-globals", "esbuild", "rolldown", "rollup", "unloader", "vite", "webpack"] }, "sha512-qa66K+crbfyE6JK10GjvbJeRrOsuC/JpbnHctfyp/i4oBTxWOzJfRZyDiOk1PtErMFRu8JhsU/wPvOdBNWe5Rg=="], + + "@tanstack/router-plugin/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], + "@tanstack/router-utils/diff": ["diff@8.0.3", "", {}, "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ=="], "@tanstack/server-functions-plugin/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], + "@tanstack/start-client-core/seroval": ["seroval@1.5.5", "", {}, "sha512-bSjOuPcwPKLSJNhr9+bZxA20nQxVle5J5MNsYRVE6cIg7KpRLXGupymePavu0jrxlPiPsr4xGZSB8yUY2sH2sw=="], + + "@tanstack/start-plugin-core/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], + + "@tanstack/start-plugin-core/@babel/core": ["@babel/core@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-compilation-targets": "^7.29.7", "@babel/helper-module-transforms": "^7.29.7", "@babel/helpers": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA=="], + + "@tanstack/start-plugin-core/lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], + + "@tanstack/start-plugin-core/picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="], + + "@tanstack/start-plugin-core/seroval": ["seroval@1.5.5", "", {}, "sha512-bSjOuPcwPKLSJNhr9+bZxA20nQxVle5J5MNsYRVE6cIg7KpRLXGupymePavu0jrxlPiPsr4xGZSB8yUY2sH2sw=="], + + "@tanstack/start-plugin-core/source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], + + "@tanstack/start-plugin-core/srvx": ["srvx@0.11.21", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-GWTHjKMeekX8CwJf4VU9Oo6mJpSGaflGMddbCvR+Cmmh9sslRMiGbAoqqZacE0r1ncARh6buCEETr2W52F8b1w=="], + + "@tanstack/start-plugin-core/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], + + "@tanstack/start-server-core/seroval": ["seroval@1.5.5", "", {}, "sha512-bSjOuPcwPKLSJNhr9+bZxA20nQxVle5J5MNsYRVE6cIg7KpRLXGupymePavu0jrxlPiPsr4xGZSB8yUY2sH2sw=="], + "@testing-library/dom/aria-query": ["aria-query@5.3.0", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A=="], "@testing-library/dom/dom-accessibility-api": ["dom-accessibility-api@0.5.16", "", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="], @@ -6861,7 +7212,7 @@ "@vercel/functions/@vercel/oidc": ["@vercel/oidc@3.8.0", "", { "dependencies": { "@vercel/cli-config": "0.2.0", "@vercel/cli-exec": "1.0.0", "jose": "^5.9.6" } }, "sha512-r00laGW6Pv778RoR6M2NxX91ycSj+PBwVo+fOb9Bif+F0IyUKt25zrvBzfEzQpeAzbqOgPZyQibEWDdDFApd+A=="], - "@vitejs/plugin-react/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="], + "@vitejs/plugin-react/vite": ["vite@8.1.4", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.5", "postcss": "^8.5.16", "rolldown": "~1.1.4", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.3.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-bTT9PsdWO+MQMNG9ZXIP/qM9wGh37DFxTV/sPq9cFpHr3w4jkgef032PkAL9jAqhk3Nz8NQw3O8n6/xFkqO4QQ=="], "@vitest/coverage-v8/@vitest/utils": ["@vitest/utils@4.1.8", "", { "dependencies": { "@vitest/pretty-format": "4.1.8", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg=="], @@ -7043,8 +7394,40 @@ "form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + "framer-motion/react-dom": ["react-dom@18.2.0", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" }, "peerDependencies": { "react": "^18.2.0" } }, "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g=="], + "fs-extra/jsonfile": ["jsonfile@6.2.1", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q=="], + "fumadocs-core/js-yaml": ["js-yaml@5.2.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.mjs" } }, "sha512-zfLtNfQqxVqq3uaTqSkh4x4hZw3KHobGUA0fJUj4wawW8bsQLTVqpHdXSIzidh7o+4lEW36tANuAGdaFx6Zgnw=="], + + "fumadocs-core/shiki": ["shiki@4.3.1", "", { "dependencies": { "@shikijs/core": "4.3.1", "@shikijs/engine-javascript": "4.3.1", "@shikijs/engine-oniguruma": "4.3.1", "@shikijs/langs": "4.3.1", "@shikijs/themes": "4.3.1", "@shikijs/types": "4.3.1", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oR+qDVi2OjX1tmDpyv+3KviX01KzO6Af+0NNnKnsp9491UEGz2YpxTuJboS/6VhYpTdqzmuJBuiTlrAWWJAssw=="], + + "fumadocs-mdx/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], + + "fumadocs-mdx/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], + + "fumadocs-mdx/esbuild": ["esbuild@0.28.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.1", "@esbuild/android-arm": "0.28.1", "@esbuild/android-arm64": "0.28.1", "@esbuild/android-x64": "0.28.1", "@esbuild/darwin-arm64": "0.28.1", "@esbuild/darwin-x64": "0.28.1", "@esbuild/freebsd-arm64": "0.28.1", "@esbuild/freebsd-x64": "0.28.1", "@esbuild/linux-arm": "0.28.1", "@esbuild/linux-arm64": "0.28.1", "@esbuild/linux-ia32": "0.28.1", "@esbuild/linux-loong64": "0.28.1", "@esbuild/linux-mips64el": "0.28.1", "@esbuild/linux-ppc64": "0.28.1", "@esbuild/linux-riscv64": "0.28.1", "@esbuild/linux-s390x": "0.28.1", "@esbuild/linux-x64": "0.28.1", "@esbuild/netbsd-arm64": "0.28.1", "@esbuild/netbsd-x64": "0.28.1", "@esbuild/openbsd-arm64": "0.28.1", "@esbuild/openbsd-x64": "0.28.1", "@esbuild/openharmony-arm64": "0.28.1", "@esbuild/sunos-x64": "0.28.1", "@esbuild/win32-arm64": "0.28.1", "@esbuild/win32-ia32": "0.28.1", "@esbuild/win32-x64": "0.28.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw=="], + + "fumadocs-mdx/js-yaml": ["js-yaml@5.2.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.mjs" } }, "sha512-zfLtNfQqxVqq3uaTqSkh4x4hZw3KHobGUA0fJUj4wawW8bsQLTVqpHdXSIzidh7o+4lEW36tANuAGdaFx6Zgnw=="], + + "fumadocs-mdx/picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="], + + "fumadocs-mdx/tinyexec": ["tinyexec@1.2.4", "", {}, "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg=="], + + "fumadocs-mdx/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], + + "fumadocs-ui/@radix-ui/react-collapsible": ["@radix-ui/react-collapsible@1.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.5", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-presence": "1.1.7", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-controllable-state": "1.2.3", "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-opfXRe6nnzyGmCDPx+l1Aqo/RbqWtQal2FnsBqF9hhePp6j0LsRoBaRxcMOlTv+uYTJVtWYZKg9t9wTe+BA/ZA=="], + + "fumadocs-ui/@radix-ui/react-popover": ["@radix-ui/react-popover@1.1.19", "", { "dependencies": { "@radix-ui/primitive": "1.1.5", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-dismissable-layer": "1.1.15", "@radix-ui/react-focus-guards": "1.1.4", "@radix-ui/react-focus-scope": "1.1.12", "@radix-ui/react-id": "1.1.2", "@radix-ui/react-popper": "1.3.3", "@radix-ui/react-portal": "1.1.13", "@radix-ui/react-presence": "1.1.7", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-slot": "1.3.0", "@radix-ui/react-use-controllable-state": "1.2.3", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-jkrTdQVxnIB8fpn0NyyxW9CTB5aCXZZelVz5z+Xmii6g5WxMqS3fInNslZ63puP39+Puu4jYohUK31y3dT87gQ=="], + + "fumadocs-ui/@radix-ui/react-slot": ["@radix-ui/react-slot@1.3.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA=="], + + "fumadocs-ui/motion": ["motion@12.42.2", "", { "dependencies": { "framer-motion": "^12.42.2", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-Atvv11yUKIid41cVrRBDVX5m8tF8kNpExRSlbpt6APClhDjtwQssgFHhQzejxw7/7YYbjHSPKBVbHo05BuJT5Q=="], + + "fumadocs-ui/react": ["react@19.2.7", "", {}, "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ=="], + + "fumadocs-ui/shiki": ["shiki@4.3.1", "", { "dependencies": { "@shikijs/core": "4.3.1", "@shikijs/engine-javascript": "4.3.1", "@shikijs/engine-oniguruma": "4.3.1", "@shikijs/langs": "4.3.1", "@shikijs/themes": "4.3.1", "@shikijs/types": "4.3.1", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oR+qDVi2OjX1tmDpyv+3KviX01KzO6Af+0NNnKnsp9491UEGz2YpxTuJboS/6VhYpTdqzmuJBuiTlrAWWJAssw=="], + "gaxios/node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="], "get-uri/data-uri-to-buffer": ["data-uri-to-buffer@6.0.2", "", {}, "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw=="], @@ -7057,6 +7440,10 @@ "got/@sindresorhus/is": ["@sindresorhus/is@4.6.0", "", {}, "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw=="], + "h3-v2/rou3": ["rou3@0.8.1", "", {}, "sha512-ePa+XGk00/3HuCqrEnK3LxJW7I0SdNg6EFzKUJG73hMAdDcOUC/i/aSz7LSDwLrGr33kal/rqOGydzwl6U7zBA=="], + + "h3-v2/srvx": ["srvx@0.11.21", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-GWTHjKMeekX8CwJf4VU9Oo6mJpSGaflGMddbCvR+Cmmh9sslRMiGbAoqqZacE0r1ncARh6buCEETr2W52F8b1w=="], + "html-minifier-terser/commander": ["commander@10.0.1", "", {}, "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug=="], "html-minifier-terser/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], @@ -7125,8 +7512,12 @@ "motion/framer-motion": ["framer-motion@12.40.0", "", { "dependencies": { "motion-dom": "^12.40.0", "motion-utils": "^12.39.0", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-uaBd3qC1v3KQqBEjwTUd183K6PbS+j0yR9w9VmEOLWA/tnUcSn8Xa3uck7t4dgpDoUss8xQTcj8W2L07lrnLFg=="], + "next-mdx-remote-client/@types/mdx": ["@types/mdx@2.0.13", "", {}, "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw=="], + "next-mdx-remote-client/react": ["react@19.2.3", "", {}, "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA=="], + "next-mdx-remote-client/react-dom": ["react-dom@18.2.0", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" }, "peerDependencies": { "react": "^18.2.0" } }, "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g=="], + "next-mdx-remote-client/serialize-error": ["serialize-error@13.0.1", "", { "dependencies": { "non-error": "^0.1.0", "type-fest": "^5.4.1" } }, "sha512-bBZaRwLH9PN5HbLCjPId4dP5bNGEtumcErgOX952IsvOhVPrm3/AeK1y0UHA/QaPG701eg0yEnOKsCOC6X/kaA=="], "nitro/h3": ["h3@2.0.1-rc.5", "", { "dependencies": { "rou3": "^0.7.9", "srvx": "^0.9.1" }, "peerDependencies": { "crossws": "^0.4.1" }, "optionalPeers": ["crossws"] }, "sha512-qkohAzCab0nLzXNm78tBjZDvtKMTmtygS8BJLT3VPczAQofdqlFXDPkXdLMJN4r05+xqneG8snZJ0HgkERCZTg=="], @@ -7213,6 +7604,8 @@ "rc/ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], + "react-dom/react": ["react@19.2.7", "", {}, "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ=="], + "react-reconciler/react": ["react@19.2.3", "", {}, "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA=="], "react-reconciler/scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="], @@ -7333,6 +7726,8 @@ "wrap-ansi-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "xmlbuilder2/js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], + "xss/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], "yaml-language-server/request-light": ["request-light@0.5.8", "", {}, "sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg=="], @@ -7509,6 +7904,8 @@ "@inquirer/core/wrap-ansi/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "@jsx-email/cli/@vitejs/plugin-react/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="], + "@jsx-email/cli/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.19.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA=="], "@jsx-email/cli/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.19.12", "", { "os": "android", "cpu": "arm" }, "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w=="], @@ -7555,6 +7952,8 @@ "@jsx-email/cli/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.19.12", "", { "os": "win32", "cpu": "x64" }, "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA=="], + "@jsx-email/cli/react-dom/scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], + "@jsx-email/cli/tailwindcss/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], "@jsx-email/cli/tailwindcss/glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], @@ -7569,6 +7968,8 @@ "@jsx-email/doiuse-email/htmlparser2/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + "@jsx-email/tailwind/react-dom/scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], + "@malept/flatpak-bundler/fs-extra/jsonfile": ["jsonfile@6.2.1", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q=="], "@mintlify/cli/fs-extra/jsonfile": ["jsonfile@6.2.1", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q=="], @@ -7629,6 +8030,10 @@ "@mintlify/mdx/@shikijs/transformers/@shikijs/types": ["@shikijs/types@3.20.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-lhYAATn10nkZcBQ0BlzSbJA3wcmL5MXUUF8d2Zzon6saZDlToKaiRX60n2+ZaHJCmXEcZRWNzn+k9vplr8Jhsw=="], + "@mintlify/mdx/react-dom/react": ["react@18.2.0", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ=="], + + "@mintlify/mdx/react-dom/scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], + "@mintlify/mdx/shiki/@shikijs/core": ["@shikijs/core@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA=="], "@mintlify/mdx/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA=="], @@ -7829,6 +8234,30 @@ "@opencode-ai/web/@shikijs/transformers/@shikijs/types": ["@shikijs/types@3.20.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-lhYAATn10nkZcBQ0BlzSbJA3wcmL5MXUUF8d2Zzon6saZDlToKaiRX60n2+ZaHJCmXEcZRWNzn+k9vplr8Jhsw=="], + "@opencode-ai/www/@cloudflare/vite-plugin/@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.16.1", "", { "peerDependencies": { "unenv": "2.0.0-rc.24", "workerd": ">1.20260305.0 <2.0.0-0" }, "optionalPeers": ["workerd"] }, "sha512-ECxObrMfyTl5bhQf/lZCXwo5G6xX9IAUo+nDMKK4SZ8m4Jvvxp52vilxyySSWh2YTZz8+HQ07qGH/2rEom1vDw=="], + + "@opencode-ai/www/@cloudflare/vite-plugin/miniflare": ["miniflare@4.20260708.1", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "sharp": "0.34.5", "undici": "7.28.0", "workerd": "1.20260708.1", "ws": "8.21.0", "youch": "4.1.0-beta.10" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-c94O9zRDISdqO18EHt6l0iF/fWgWt8p18PJvRsA/L/NJZ9Cfke3s/F5Blg1XXF7WDutVRzWVWy8Vy4LaT5ifsA=="], + + "@opencode-ai/www/@tailwindcss/vite/@tailwindcss/node": ["@tailwindcss/node@4.3.2", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "5.21.6", "jiti": "^2.7.0", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.3.2" } }, "sha512-yWP/sqEcBLaD8JuA6zNwxoYKr75qxTioYwlRwekj5Jr/I5GXnoJfjetH/psLUIv74cYTH2lBUEzBkinthoYcBg=="], + + "@opencode-ai/www/@tailwindcss/vite/@tailwindcss/oxide": ["@tailwindcss/oxide@4.3.2", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.3.2", "@tailwindcss/oxide-darwin-arm64": "4.3.2", "@tailwindcss/oxide-darwin-x64": "4.3.2", "@tailwindcss/oxide-freebsd-x64": "4.3.2", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.2", "@tailwindcss/oxide-linux-arm64-gnu": "4.3.2", "@tailwindcss/oxide-linux-arm64-musl": "4.3.2", "@tailwindcss/oxide-linux-x64-gnu": "4.3.2", "@tailwindcss/oxide-linux-x64-musl": "4.3.2", "@tailwindcss/oxide-wasm32-wasi": "4.3.2", "@tailwindcss/oxide-win32-arm64-msvc": "4.3.2", "@tailwindcss/oxide-win32-x64-msvc": "4.3.2" } }, "sha512-z8ZgnzX8gdNoWLBLqBPoh/sjnxkwvf9ZuWjnO0l0yIzbLa5/9S+eC5QxGZKRobVHIC3/1BoMWjHblqWjcgFgag=="], + + "@opencode-ai/www/vite/lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], + + "@opencode-ai/www/vite/picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="], + + "@opencode-ai/www/vite/postcss": ["postcss@8.5.16", "", { "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg=="], + + "@opencode-ai/www/wrangler/@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.5.0", "", {}, "sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg=="], + + "@opencode-ai/www/wrangler/@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.16.1", "", { "peerDependencies": { "unenv": "2.0.0-rc.24", "workerd": ">1.20260305.0 <2.0.0-0" }, "optionalPeers": ["workerd"] }, "sha512-ECxObrMfyTl5bhQf/lZCXwo5G6xX9IAUo+nDMKK4SZ8m4Jvvxp52vilxyySSWh2YTZz8+HQ07qGH/2rEom1vDw=="], + + "@opencode-ai/www/wrangler/esbuild": ["esbuild@0.28.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.1", "@esbuild/android-arm": "0.28.1", "@esbuild/android-arm64": "0.28.1", "@esbuild/android-x64": "0.28.1", "@esbuild/darwin-arm64": "0.28.1", "@esbuild/darwin-x64": "0.28.1", "@esbuild/freebsd-arm64": "0.28.1", "@esbuild/freebsd-x64": "0.28.1", "@esbuild/linux-arm": "0.28.1", "@esbuild/linux-arm64": "0.28.1", "@esbuild/linux-ia32": "0.28.1", "@esbuild/linux-loong64": "0.28.1", "@esbuild/linux-mips64el": "0.28.1", "@esbuild/linux-ppc64": "0.28.1", "@esbuild/linux-riscv64": "0.28.1", "@esbuild/linux-s390x": "0.28.1", "@esbuild/linux-x64": "0.28.1", "@esbuild/netbsd-arm64": "0.28.1", "@esbuild/netbsd-x64": "0.28.1", "@esbuild/openbsd-arm64": "0.28.1", "@esbuild/openbsd-x64": "0.28.1", "@esbuild/openharmony-arm64": "0.28.1", "@esbuild/sunos-x64": "0.28.1", "@esbuild/win32-arm64": "0.28.1", "@esbuild/win32-ia32": "0.28.1", "@esbuild/win32-x64": "0.28.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw=="], + + "@opencode-ai/www/wrangler/miniflare": ["miniflare@4.20260708.1", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "sharp": "0.34.5", "undici": "7.28.0", "workerd": "1.20260708.1", "ws": "8.21.0", "youch": "4.1.0-beta.10" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-c94O9zRDISdqO18EHt6l0iF/fWgWt8p18PJvRsA/L/NJZ9Cfke3s/F5Blg1XXF7WDutVRzWVWy8Vy4LaT5ifsA=="], + + "@opencode-ai/www/wrangler/workerd": ["workerd@1.20260708.1", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20260708.1", "@cloudflare/workerd-darwin-arm64": "1.20260708.1", "@cloudflare/workerd-linux-64": "1.20260708.1", "@cloudflare/workerd-linux-arm64": "1.20260708.1", "@cloudflare/workerd-windows-64": "1.20260708.1" }, "bin": { "workerd": "bin/workerd" } }, "sha512-WAK+Kt/VVCSldH2qSr8lx46XCJ4Q+bdlHNaFqUtOHthBEIB8C1N8HVW+VOLrxDoTCk0NGNv0zajnBeQK4JOB9w=="], + "@opentui/solid/@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], "@oxc-parser/binding-wasm32-wasi/@emnapi/core/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="], @@ -7843,6 +8272,60 @@ "@puppeteer/browsers/yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + "@radix-ui/react-accordion/@radix-ui/react-collapsible/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA=="], + + "@radix-ui/react-accordion/@radix-ui/react-id/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA=="], + + "@radix-ui/react-accordion/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.3.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA=="], + + "@radix-ui/react-accordion/@radix-ui/react-use-controllable-state/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA=="], + + "@radix-ui/react-arrow/react-dom/scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], + + "@radix-ui/react-collapsible/react-dom/scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], + + "@radix-ui/react-dialog/@radix-ui/react-id/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA=="], + + "@radix-ui/react-dialog/@radix-ui/react-portal/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA=="], + + "@radix-ui/react-dialog/@radix-ui/react-use-controllable-state/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA=="], + + "@radix-ui/react-dismissable-layer/react-dom/scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], + + "@radix-ui/react-focus-scope/react-dom/scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], + + "@radix-ui/react-navigation-menu/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.3.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA=="], + + "@radix-ui/react-popover/react-dom/scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], + + "@radix-ui/react-popper/react-dom/scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], + + "@radix-ui/react-portal/react-dom/scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], + + "@radix-ui/react-primitive/react-dom/scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], + + "@radix-ui/react-roving-focus/react-dom/scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], + + "@radix-ui/react-scroll-area/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.3.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA=="], + + "@radix-ui/react-tabs/@radix-ui/react-id/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA=="], + + "@radix-ui/react-tabs/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.3.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA=="], + + "@radix-ui/react-tabs/@radix-ui/react-roving-focus/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA=="], + + "@radix-ui/react-tabs/@radix-ui/react-roving-focus/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA=="], + + "@radix-ui/react-tabs/@radix-ui/react-use-controllable-state/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA=="], + + "@radix-ui/react-toggle-group/react-dom/scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], + + "@radix-ui/react-toggle/react-dom/scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], + + "@radix-ui/react-tooltip/react-dom/scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], + + "@radix-ui/react-visually-hidden/react-dom/scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], + "@rolldown/binding-wasm32-wasi/@napi-rs/wasm-runtime/@tybys/wasm-util": ["@tybys/wasm-util@0.10.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg=="], "@sentry/bundler-plugin-core/glob/minimatch": ["minimatch@8.0.7", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-V+1uQNdzybxa14e/p00HZnQNNcTjnRJjDxg2V8wtkjFctq4M7hXFws4oekyTP0Jebeq7QYtpFyOeBAjc88zvYg=="], @@ -7909,12 +8392,48 @@ "@stoplight/spectral-core/minimatch/brace-expansion": ["brace-expansion@1.1.15", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg=="], + "@storybook/addon-docs/react-dom/scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], + "@storybook/csf-plugin/unplugin/acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], "@storybook/csf-plugin/unplugin/webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="], "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@tybys/wasm-util": ["@tybys/wasm-util@0.10.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg=="], + "@tanstack/directive-functions-plugin/@tanstack/router-utils/diff": ["diff@8.0.3", "", {}, "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ=="], + + "@tanstack/router-plugin/@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "@tanstack/router-plugin/chokidar/readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="], + + "@tanstack/router-plugin/unplugin/picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="], + + "@tanstack/router-plugin/unplugin/webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="], + + "@tanstack/start-plugin-core/@babel/core/@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], + + "@tanstack/start-plugin-core/@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "@tanstack/start-plugin-core/lightningcss/lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="], + + "@tanstack/start-plugin-core/lightningcss/lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="], + + "@tanstack/start-plugin-core/lightningcss/lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="], + + "@tanstack/start-plugin-core/lightningcss/lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="], + + "@tanstack/start-plugin-core/lightningcss/lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="], + + "@tanstack/start-plugin-core/lightningcss/lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="], + + "@tanstack/start-plugin-core/lightningcss/lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="], + + "@tanstack/start-plugin-core/lightningcss/lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="], + + "@tanstack/start-plugin-core/lightningcss/lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="], + + "@tanstack/start-plugin-core/lightningcss/lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="], + "@vercel/cli-exec/execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], "@vercel/cli-exec/execa/human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="], @@ -7931,6 +8450,12 @@ "@vercel/functions/@vercel/oidc/jose": ["jose@5.9.6", "", {}, "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ=="], + "@vitejs/plugin-react/vite/lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], + + "@vitejs/plugin-react/vite/picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="], + + "@vitejs/plugin-react/vite/postcss": ["postcss@8.5.16", "", { "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg=="], + "@vitest/coverage-v8/@vitest/utils/@vitest/pretty-format": ["@vitest/pretty-format@4.1.8", "", { "dependencies": { "tinyrainbow": "^3.1.0" } }, "sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA=="], "@vitest/expect/@vitest/utils/@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="], @@ -8075,6 +8600,130 @@ "form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + "framer-motion/react-dom/scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], + + "fumadocs-core/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "fumadocs-core/shiki/@shikijs/core": ["@shikijs/core@4.3.1", "", { "dependencies": { "@shikijs/primitive": "4.3.1", "@shikijs/types": "4.3.1", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-ANMDxuaPsNMdDC1m4vfvhlDmJweMwkE5XitTwrq2rWHx5jM+dlm4MmHt2PP6t0uejfR77SuhrhJ0zEijIF/uhA=="], + + "fumadocs-core/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@4.3.1", "", { "dependencies": { "@shikijs/types": "4.3.1", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.6" } }, "sha512-JBItcnPuYq7jVJdZo/vMj94r+szT7XEjHFX+mvFDGSEIbVAXAGyHAHzhbWzpGOwYidCZrErJLLgn2PVeiokHnQ=="], + + "fumadocs-core/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@4.3.1", "", { "dependencies": { "@shikijs/types": "4.3.1", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-OXyNMzg0pews+msMj4cHeqT4xiYKKvbnn6VbdAXxfoFl3SSx4fJTc8FadECuc5/H9p3BzhNAoAUXKwAu9rWYhg=="], + + "fumadocs-core/shiki/@shikijs/langs": ["@shikijs/langs@4.3.1", "", { "dependencies": { "@shikijs/types": "4.3.1" } }, "sha512-m0l9nsDqgBHvbZbk7A0/kXz/impK3uB/c6rAn6Gpg/uPtdZRQ+alsN/17MU5thb68XTj/4DxkZAotrM0GGSpDQ=="], + + "fumadocs-core/shiki/@shikijs/themes": ["@shikijs/themes@4.3.1", "", { "dependencies": { "@shikijs/types": "4.3.1" } }, "sha512-dgpoJ4WqNi2yTmizQHBJ5zcX6j2lE6icN/0yt4l1kkf16jrY/pwPLoTb1ETsWMz0OBLf9ZNvwmxft+cH+N9qSA=="], + + "fumadocs-core/shiki/@shikijs/types": ["@shikijs/types@4.3.1", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-CHFxE0jztBIZRHH6gxXE7DXUCFXjReEGxZ/j0rfSLGKZuwp2xBYycEP14875DSa9KLL/6700oxIq6oO6ef9K2g=="], + + "fumadocs-mdx/chokidar/readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="], + + "fumadocs-mdx/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ=="], + + "fumadocs-mdx/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.28.1", "", { "os": "android", "cpu": "arm" }, "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ=="], + + "fumadocs-mdx/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.28.1", "", { "os": "android", "cpu": "arm64" }, "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg=="], + + "fumadocs-mdx/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.28.1", "", { "os": "android", "cpu": "x64" }, "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng=="], + + "fumadocs-mdx/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.28.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q=="], + + "fumadocs-mdx/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.28.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ=="], + + "fumadocs-mdx/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.28.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw=="], + + "fumadocs-mdx/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.28.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ=="], + + "fumadocs-mdx/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.28.1", "", { "os": "linux", "cpu": "arm" }, "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ=="], + + "fumadocs-mdx/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.28.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g=="], + + "fumadocs-mdx/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.28.1", "", { "os": "linux", "cpu": "ia32" }, "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w=="], + + "fumadocs-mdx/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg=="], + + "fumadocs-mdx/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ=="], + + "fumadocs-mdx/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.28.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ=="], + + "fumadocs-mdx/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ=="], + + "fumadocs-mdx/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.28.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag=="], + + "fumadocs-mdx/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.28.1", "", { "os": "linux", "cpu": "x64" }, "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA=="], + + "fumadocs-mdx/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.28.1", "", { "os": "none", "cpu": "arm64" }, "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw=="], + + "fumadocs-mdx/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.28.1", "", { "os": "none", "cpu": "x64" }, "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg=="], + + "fumadocs-mdx/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.28.1", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q=="], + + "fumadocs-mdx/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.28.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw=="], + + "fumadocs-mdx/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.28.1", "", { "os": "none", "cpu": "arm64" }, "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg=="], + + "fumadocs-mdx/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.28.1", "", { "os": "sunos", "cpu": "x64" }, "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ=="], + + "fumadocs-mdx/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.28.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA=="], + + "fumadocs-mdx/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.28.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg=="], + + "fumadocs-mdx/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.1", "", { "os": "win32", "cpu": "x64" }, "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A=="], + + "fumadocs-mdx/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "fumadocs-ui/@radix-ui/react-collapsible/@radix-ui/primitive": ["@radix-ui/primitive@1.1.5", "", {}, "sha512-d86WIWFYNtGA0H/d8exstrTRTp7eWJYlYJbtNofxr/3ljupZYn6EFDG/Qgu/0Kc8v7yMUxySagqJsL1+PdYjWg=="], + + "fumadocs-ui/@radix-ui/react-collapsible/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA=="], + + "fumadocs-ui/@radix-ui/react-collapsible/@radix-ui/react-context": ["@radix-ui/react-context@1.2.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-fOE+JtN9rygNZkCnHRBEP0TAvLldlhyOxMsbwFvTP4nAs+nBmfnna+o/Zski2wkmY1YMrFC0aSzsHoLY47iLrg=="], + + "fumadocs-ui/@radix-ui/react-collapsible/@radix-ui/react-id": ["@radix-ui/react-id@1.1.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA=="], + + "fumadocs-ui/@radix-ui/react-collapsible/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.7", "", { "dependencies": { "@radix-ui/react-slot": "1.3.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bC3NiwsprbxKjuon9l7X6BUTw7FPVzEYaL92MPEY5SCd/9hUTPXVFtVwRix7778wtRsVao+zE062gL79FZleeQ=="], + + "fumadocs-ui/@radix-ui/react-collapsible/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.3", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.3", "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA=="], + + "fumadocs-ui/@radix-ui/react-collapsible/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA=="], + + "fumadocs-ui/@radix-ui/react-popover/@radix-ui/primitive": ["@radix-ui/primitive@1.1.5", "", {}, "sha512-d86WIWFYNtGA0H/d8exstrTRTp7eWJYlYJbtNofxr/3ljupZYn6EFDG/Qgu/0Kc8v7yMUxySagqJsL1+PdYjWg=="], + + "fumadocs-ui/@radix-ui/react-popover/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA=="], + + "fumadocs-ui/@radix-ui/react-popover/@radix-ui/react-context": ["@radix-ui/react-context@1.2.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-fOE+JtN9rygNZkCnHRBEP0TAvLldlhyOxMsbwFvTP4nAs+nBmfnna+o/Zski2wkmY1YMrFC0aSzsHoLY47iLrg=="], + + "fumadocs-ui/@radix-ui/react-popover/@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.5", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-effect-event": "0.0.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-b0XaRlzn2QKuo10XyNgi2DAJDf5XC9d1nD3FJcuvCjbR7+4Ad28zmZsLsqx+hvDEzMnRuZaZxZm9gYObV6RmRA=="], + + "fumadocs-ui/@radix-ui/react-popover/@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.4", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-cot/aB/mOm0IYVYTTmQcEEK1M48lZWi8FlYe5nDPQQ8NYZUlXEFgncJ9p2Kzer3RKSrY7cTTpEMLZKNo9QoP5Q=="], + + "fumadocs-ui/@radix-ui/react-popover/@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.12", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-callback-ref": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-jjk/lqTeNL0azUx5ZYzVrl4NgaDIrdzTNE4mABV9yBFI7FQqN7pIgzV1bTleUezP2QiTGA1BFTqY8MegDgWX9A=="], + + "fumadocs-ui/@radix-ui/react-popover/@radix-ui/react-id": ["@radix-ui/react-id@1.1.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA=="], + + "fumadocs-ui/@radix-ui/react-popover/@radix-ui/react-popper": ["@radix-ui/react-popper@1.3.3", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.11", "@radix-ui/react-compose-refs": "1.1.3", "@radix-ui/react-context": "1.2.0", "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-callback-ref": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.2", "@radix-ui/react-use-rect": "1.1.2", "@radix-ui/react-use-size": "1.1.2", "@radix-ui/rect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-mS7dGpyjv6b+gsDjLF7e0ia1W4Im1B1hSCy2yuXlHuvnZxHKagfDaobt/KAKt27EpZMit2pss8eJBVyVjEWM+g=="], + + "fumadocs-ui/@radix-ui/react-popover/@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.13", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.7", "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-z3oXfmaHLJTF1wktbjgD6cn9jiEbq3WSondB10LIuIt2m2Ym4iJlrW04/euMwENDdWDdE7z+OuY7Qyp1YpRSwA=="], + + "fumadocs-ui/@radix-ui/react-popover/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.7", "", { "dependencies": { "@radix-ui/react-slot": "1.3.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bC3NiwsprbxKjuon9l7X6BUTw7FPVzEYaL92MPEY5SCd/9hUTPXVFtVwRix7778wtRsVao+zE062gL79FZleeQ=="], + + "fumadocs-ui/@radix-ui/react-popover/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.3", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.3", "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA=="], + + "fumadocs-ui/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA=="], + + "fumadocs-ui/motion/framer-motion": ["framer-motion@12.42.2", "", { "dependencies": { "motion-dom": "^12.42.2", "motion-utils": "^12.39.0", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-5XY9luDiu0oHfHBjpDthFMh0ES+122w6p/papSJBweMkO8Sn+PW2QaEgRblQBpWFnuvZS5qvarpt/hO2pjGmnw=="], + + "fumadocs-ui/shiki/@shikijs/core": ["@shikijs/core@4.3.1", "", { "dependencies": { "@shikijs/primitive": "4.3.1", "@shikijs/types": "4.3.1", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-ANMDxuaPsNMdDC1m4vfvhlDmJweMwkE5XitTwrq2rWHx5jM+dlm4MmHt2PP6t0uejfR77SuhrhJ0zEijIF/uhA=="], + + "fumadocs-ui/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@4.3.1", "", { "dependencies": { "@shikijs/types": "4.3.1", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.6" } }, "sha512-JBItcnPuYq7jVJdZo/vMj94r+szT7XEjHFX+mvFDGSEIbVAXAGyHAHzhbWzpGOwYidCZrErJLLgn2PVeiokHnQ=="], + + "fumadocs-ui/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@4.3.1", "", { "dependencies": { "@shikijs/types": "4.3.1", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-OXyNMzg0pews+msMj4cHeqT4xiYKKvbnn6VbdAXxfoFl3SSx4fJTc8FadECuc5/H9p3BzhNAoAUXKwAu9rWYhg=="], + + "fumadocs-ui/shiki/@shikijs/langs": ["@shikijs/langs@4.3.1", "", { "dependencies": { "@shikijs/types": "4.3.1" } }, "sha512-m0l9nsDqgBHvbZbk7A0/kXz/impK3uB/c6rAn6Gpg/uPtdZRQ+alsN/17MU5thb68XTj/4DxkZAotrM0GGSpDQ=="], + + "fumadocs-ui/shiki/@shikijs/themes": ["@shikijs/themes@4.3.1", "", { "dependencies": { "@shikijs/types": "4.3.1" } }, "sha512-dgpoJ4WqNi2yTmizQHBJ5zcX6j2lE6icN/0yt4l1kkf16jrY/pwPLoTb1ETsWMz0OBLf9ZNvwmxft+cH+N9qSA=="], + + "fumadocs-ui/shiki/@shikijs/types": ["@shikijs/types@4.3.1", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-CHFxE0jztBIZRHH6gxXE7DXUCFXjReEGxZ/j0rfSLGKZuwp2xBYycEP14875DSa9KLL/6700oxIq6oO6ef9K2g=="], + "iconv-corefoundation/cli-truncate/slice-ansi": ["slice-ansi@3.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", "is-fullwidth-code-point": "^3.0.0" } }, "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ=="], "iconv-corefoundation/cli-truncate/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], @@ -8121,6 +8770,10 @@ "motion/framer-motion/motion-utils": ["motion-utils@12.39.0", "", {}, "sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ=="], + "next-mdx-remote-client/react-dom/react": ["react@18.2.0", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ=="], + + "next-mdx-remote-client/react-dom/scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], + "next-mdx-remote-client/serialize-error/type-fest": ["type-fest@5.7.0", "", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg=="], "opencode-gitlab-auth/open/wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="], @@ -8253,6 +8906,8 @@ "wrap-ansi-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + "xmlbuilder2/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + "@actions/artifact/@actions/core/@actions/exec/@actions/io": ["@actions/io@2.0.0", "", {}, "sha512-Jv33IN09XLO+0HS79aaODsvIRyduiF7NY/F6LYeK5oeUmrsz7aFdRphQjFoESF4jS7lMauDOttKALcpapVDIAg=="], "@actions/github/@octokit/plugin-paginate-rest/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@20.0.0", "", {}, "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA=="], @@ -8465,6 +9120,128 @@ "@opencode-ai/desktop/@actions/artifact/@actions/http-client/undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="], + "@opencode-ai/www/@cloudflare/vite-plugin/@cloudflare/unenv-preset/workerd": ["workerd@1.20260708.1", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20260708.1", "@cloudflare/workerd-darwin-arm64": "1.20260708.1", "@cloudflare/workerd-linux-64": "1.20260708.1", "@cloudflare/workerd-linux-arm64": "1.20260708.1", "@cloudflare/workerd-windows-64": "1.20260708.1" }, "bin": { "workerd": "bin/workerd" } }, "sha512-WAK+Kt/VVCSldH2qSr8lx46XCJ4Q+bdlHNaFqUtOHthBEIB8C1N8HVW+VOLrxDoTCk0NGNv0zajnBeQK4JOB9w=="], + + "@opencode-ai/www/@cloudflare/vite-plugin/miniflare/sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "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" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="], + + "@opencode-ai/www/@cloudflare/vite-plugin/miniflare/undici": ["undici@7.28.0", "", {}, "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA=="], + + "@opencode-ai/www/@cloudflare/vite-plugin/miniflare/workerd": ["workerd@1.20260708.1", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20260708.1", "@cloudflare/workerd-darwin-arm64": "1.20260708.1", "@cloudflare/workerd-linux-64": "1.20260708.1", "@cloudflare/workerd-linux-arm64": "1.20260708.1", "@cloudflare/workerd-windows-64": "1.20260708.1" }, "bin": { "workerd": "bin/workerd" } }, "sha512-WAK+Kt/VVCSldH2qSr8lx46XCJ4Q+bdlHNaFqUtOHthBEIB8C1N8HVW+VOLrxDoTCk0NGNv0zajnBeQK4JOB9w=="], + + "@opencode-ai/www/@tailwindcss/vite/@tailwindcss/node/enhanced-resolve": ["enhanced-resolve@5.21.6", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ=="], + + "@opencode-ai/www/@tailwindcss/vite/@tailwindcss/node/lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], + + "@opencode-ai/www/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.3.2", "", { "os": "android", "cpu": "arm64" }, "sha512-WHxqIuHpvZ5VtdX6GTl1Ik/Vp2YuN42Et+0CdeaVd/frQ9jAvGmvR8vLT+jk3e8/Q3x8kECB9+R17pgpp2BulA=="], + + "@opencode-ai/www/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.3.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-GZypeUY/IDJW3877KeM+O67vbXr3MBnbtEL4aYhNErv/JWZhye2vGSWWG9tB6iiqR2MqRNkY8IOUy4NdSZV26w=="], + + "@opencode-ai/www/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.3.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-UIIzmefR6KO1sDU7MzRqAxC8iBpft/VhkGjTjnhoS6k7Z3rQ9wEgA1ODSiyH/tcSYssulNm4Ci3hOeK1jH7ccQ=="], + + "@opencode-ai/www/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.3.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-GN+uAmcI6DNspnCDwtOAZrTz6oukJnp337qZvxqCGLd3BHBzJpO0ZbTLRvJNdztOeAmTzewewGIMPb0tk2R4WA=="], + + "@opencode-ai/www/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.3.2", "", { "os": "linux", "cpu": "arm" }, "sha512-4ABn7qSbdHRwTiDiuWNegCyb5+2FJ4vKIKc3DmKrvAFw7MU1Lm11dIkTPwUaFdTzc7IsOpDbqBrlh0x6y36U/w=="], + + "@opencode-ai/www/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.3.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-wDgEIGwoM8w8pufh9LVt1PahDgNdKXrLC2qfAnV3vAmococ9RWbxeAw4pxPttd/TsJfwjyLf90Dg1y9y8I6Emw=="], + + "@opencode-ai/www/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.3.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-J5Nuk0uZQIiMTJj3LEx4sAA9tMFUoXQZFv1J6An+QGYe53HKRJuFDi0rpq/tuouCZeAbOBY3kQ6g8qeD4TUjtA=="], + + "@opencode-ai/www/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.3.2", "", { "os": "linux", "cpu": "x64" }, "sha512-kqCZpSKOBEJO4mz7OqWoofBZeXTAwaVGPj0ErAj7CojmhKpWVWVOnrt9dE8odoIraZq4oj3ausM37kXi+Tow8w=="], + + "@opencode-ai/www/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.3.2", "", { "os": "linux", "cpu": "x64" }, "sha512-cixpqbh2toJDmkuCRI68nXA8ZxNmdK9Y+9v5h3MC3ZQKy/0BO8AWzlkWyRM7JAFSGBlfig4YVTPsK6MVgqz1uw=="], + + "@opencode-ai/www/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.3.2", "", { "dependencies": { "@emnapi/core": "^1.11.1", "@emnapi/runtime": "^1.11.1", "@emnapi/wasi-threads": "^1.2.2", "@napi-rs/wasm-runtime": "^1.1.4", "@tybys/wasm-util": "^0.10.2", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-4ec2Z/LOmRsAgU23CS4xeJfcJlmRg94A/XrbGRCF1gyU/zdDfRLYDVsS+ynSZCmGNxQ1jQriQOKMQeQxBA3Isw=="], + + "@opencode-ai/www/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.3.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-Zyr/M0+XcYZu3bZrUytc7TXvrk0ftWfl8gN2MwekNDzhqhKRUucMPSeOzM0o0wH5AWOU49BsKRrfKxI2atCPMQ=="], + + "@opencode-ai/www/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.3.2", "", { "os": "win32", "cpu": "x64" }, "sha512-QI9BO7KlNZsp2GuO0jwAAj5jCDABOKXRkCk2XuKTSaNEFSdfzqswYVTtCHBNKHLsqyjFyFkqlDiwkNbTYSssMQ=="], + + "@opencode-ai/www/vite/lightningcss/lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="], + + "@opencode-ai/www/vite/lightningcss/lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="], + + "@opencode-ai/www/vite/lightningcss/lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="], + + "@opencode-ai/www/vite/lightningcss/lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="], + + "@opencode-ai/www/vite/lightningcss/lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="], + + "@opencode-ai/www/vite/lightningcss/lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="], + + "@opencode-ai/www/vite/lightningcss/lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="], + + "@opencode-ai/www/vite/lightningcss/lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="], + + "@opencode-ai/www/vite/lightningcss/lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="], + + "@opencode-ai/www/vite/lightningcss/lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="], + + "@opencode-ai/www/wrangler/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ=="], + + "@opencode-ai/www/wrangler/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.28.1", "", { "os": "android", "cpu": "arm" }, "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ=="], + + "@opencode-ai/www/wrangler/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.28.1", "", { "os": "android", "cpu": "arm64" }, "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg=="], + + "@opencode-ai/www/wrangler/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.28.1", "", { "os": "android", "cpu": "x64" }, "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng=="], + + "@opencode-ai/www/wrangler/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.28.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q=="], + + "@opencode-ai/www/wrangler/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.28.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ=="], + + "@opencode-ai/www/wrangler/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.28.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw=="], + + "@opencode-ai/www/wrangler/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.28.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ=="], + + "@opencode-ai/www/wrangler/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.28.1", "", { "os": "linux", "cpu": "arm" }, "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ=="], + + "@opencode-ai/www/wrangler/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.28.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g=="], + + "@opencode-ai/www/wrangler/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.28.1", "", { "os": "linux", "cpu": "ia32" }, "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w=="], + + "@opencode-ai/www/wrangler/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg=="], + + "@opencode-ai/www/wrangler/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ=="], + + "@opencode-ai/www/wrangler/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.28.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ=="], + + "@opencode-ai/www/wrangler/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ=="], + + "@opencode-ai/www/wrangler/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.28.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag=="], + + "@opencode-ai/www/wrangler/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.28.1", "", { "os": "linux", "cpu": "x64" }, "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA=="], + + "@opencode-ai/www/wrangler/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.28.1", "", { "os": "none", "cpu": "arm64" }, "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw=="], + + "@opencode-ai/www/wrangler/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.28.1", "", { "os": "none", "cpu": "x64" }, "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg=="], + + "@opencode-ai/www/wrangler/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.28.1", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q=="], + + "@opencode-ai/www/wrangler/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.28.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw=="], + + "@opencode-ai/www/wrangler/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.28.1", "", { "os": "none", "cpu": "arm64" }, "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg=="], + + "@opencode-ai/www/wrangler/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.28.1", "", { "os": "sunos", "cpu": "x64" }, "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ=="], + + "@opencode-ai/www/wrangler/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.28.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA=="], + + "@opencode-ai/www/wrangler/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.28.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg=="], + + "@opencode-ai/www/wrangler/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.1", "", { "os": "win32", "cpu": "x64" }, "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A=="], + + "@opencode-ai/www/wrangler/miniflare/sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "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" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="], + + "@opencode-ai/www/wrangler/miniflare/undici": ["undici@7.28.0", "", {}, "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA=="], + + "@opencode-ai/www/wrangler/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20260708.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-HXFCvhS1wpg3uXO0CLUwmwC41i2loM5FSK69EUchOBpmYBAXxT1oHLm6EOA5lqhTk5Mu9kjRiQYxa1GwKPwfJg=="], + + "@opencode-ai/www/wrangler/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20260708.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-JVlJaKDoRTVKSroHIlf8g3UCPjKj4iDbMZE2CNYht5qQ+2rL0FAUiVlV82G3BqKnnw9kHYnnsMzC08b9zVtdzA=="], + + "@opencode-ai/www/wrangler/workerd/@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20260708.1", "", { "os": "linux", "cpu": "x64" }, "sha512-3daE60YdD7YX0Jtuzc9DE/r/qMkmx8ZvHTkF8Mzmp3F5tbzlV0DAzmu5PFUPF2WuvtKbAhZKbvC2cHmWpQYxnA=="], + + "@opencode-ai/www/wrangler/workerd/@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20260708.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-VLdNYOx5Hj+9C6isy0ACWZsbMtSxex2DIJWEe7cZxUdlphZ58ZT8zxNXK8yunFiowd34hn3VwGMopdvdj8lvmA=="], + + "@opencode-ai/www/wrangler/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20260708.1", "", { "os": "win32", "cpu": "x64" }, "sha512-bC/aSAwLy16Vjo24i9XU3aWH+eRgz7NeR5xPKavGbembO18ZywYTQbXh14eXtY6fAqN3RzRG8psijTdhX4xydA=="], + "@puppeteer/browsers/yargs/cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], "@puppeteer/browsers/yargs/cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], @@ -8473,6 +9250,8 @@ "@puppeteer/browsers/yargs/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "@radix-ui/react-tabs/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA=="], + "@sentry/bundler-plugin-core/glob/minimatch/brace-expansion": ["brace-expansion@2.1.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA=="], "@sentry/bundler-plugin-core/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], @@ -8501,6 +9280,26 @@ "@vercel/cli-exec/execa/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], + "@vitejs/plugin-react/vite/lightningcss/lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="], + + "@vitejs/plugin-react/vite/lightningcss/lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="], + + "@vitejs/plugin-react/vite/lightningcss/lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="], + + "@vitejs/plugin-react/vite/lightningcss/lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="], + + "@vitejs/plugin-react/vite/lightningcss/lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="], + + "@vitejs/plugin-react/vite/lightningcss/lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="], + + "@vitejs/plugin-react/vite/lightningcss/lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="], + + "@vitejs/plugin-react/vite/lightningcss/lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="], + + "@vitejs/plugin-react/vite/lightningcss/lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="], + + "@vitejs/plugin-react/vite/lightningcss/lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="], + "ai-gateway-provider/@ai-sdk/amazon-bedrock/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], "ai-gateway-provider/@ai-sdk/anthropic/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], @@ -8565,6 +9364,30 @@ "filelist/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + "fumadocs-core/shiki/@shikijs/core/@shikijs/primitive": ["@shikijs/primitive@4.3.1", "", { "dependencies": { "@shikijs/types": "4.3.1", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-CXQRQOYy1leqQ8ceTeJdmXv/bsUY++6QyLpXJ94LZAAYj5X2SKRdc5ipguv4NPyGVKItB2PPwUpRNe0Sjh5S1A=="], + + "fumadocs-ui/@radix-ui/react-popover/@radix-ui/react-id/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA=="], + + "fumadocs-ui/@radix-ui/react-popover/@radix-ui/react-popper/@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.11", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.7" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Kdil9BB1rIFC/khmf4hC35bn8701AJcizTU7G7cUbEbk5XqqbjDuHW60uUfKqO5WojjZcbAW51Q7P0hRmMLw8A=="], + + "fumadocs-ui/@radix-ui/react-popover/@radix-ui/react-popper/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA=="], + + "fumadocs-ui/@radix-ui/react-popover/@radix-ui/react-popper/@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.1.2", "", { "dependencies": { "@radix-ui/rect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-d8a+bBY/FxikNPlgJJoaBHZX+zKVbWHYJGTLnLvveQgFSTntkGdEKv3JDtHrMS0DNYpllz2nRsTLGLKYttbpmw=="], + + "fumadocs-ui/@radix-ui/react-popover/@radix-ui/react-popper/@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-giWQp+4mxjBPt4KZ0MmyuykFNWfbDxKt4x+fPkRYmgRFJSbCZFzUglvMb/Kjn38tm10YP4ufiQZDx3zna4LU6w=="], + + "fumadocs-ui/@radix-ui/react-popover/@radix-ui/react-popper/@radix-ui/rect": ["@radix-ui/rect@1.1.2", "", {}, "sha512-xnXE7wG13PI+cxieVssYXlQJuYVRhH9NBoxt3KNwzghDIA69GMm7d4wXRouHIYjE+KvS6U/MsMO73NdS2MH9ZA=="], + + "fumadocs-ui/@radix-ui/react-popover/@radix-ui/react-portal/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA=="], + + "fumadocs-ui/@radix-ui/react-popover/@radix-ui/react-use-controllable-state/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA=="], + + "fumadocs-ui/motion/framer-motion/motion-dom": ["motion-dom@12.42.2", "", { "dependencies": { "motion-utils": "^12.39.0" } }, "sha512-5gIMWLp/PycBtJRJWRgjxke5n8dlvkSn2DrYW+tr3XcqAZY1xZh6BJyooJXCM8wdfM7wfMjkBJNLge1CKPUIRA=="], + + "fumadocs-ui/motion/framer-motion/motion-utils": ["motion-utils@12.39.0", "", {}, "sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ=="], + + "fumadocs-ui/shiki/@shikijs/core/@shikijs/primitive": ["@shikijs/primitive@4.3.1", "", { "dependencies": { "@shikijs/types": "4.3.1", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-CXQRQOYy1leqQ8ceTeJdmXv/bsUY++6QyLpXJ94LZAAYj5X2SKRdc5ipguv4NPyGVKItB2PPwUpRNe0Sjh5S1A=="], + "iconv-corefoundation/cli-truncate/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "iconv-corefoundation/cli-truncate/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], @@ -8657,6 +9480,138 @@ "@mintlify/scraping/yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + "@opencode-ai/www/@cloudflare/vite-plugin/@cloudflare/unenv-preset/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20260708.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-HXFCvhS1wpg3uXO0CLUwmwC41i2loM5FSK69EUchOBpmYBAXxT1oHLm6EOA5lqhTk5Mu9kjRiQYxa1GwKPwfJg=="], + + "@opencode-ai/www/@cloudflare/vite-plugin/@cloudflare/unenv-preset/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20260708.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-JVlJaKDoRTVKSroHIlf8g3UCPjKj4iDbMZE2CNYht5qQ+2rL0FAUiVlV82G3BqKnnw9kHYnnsMzC08b9zVtdzA=="], + + "@opencode-ai/www/@cloudflare/vite-plugin/@cloudflare/unenv-preset/workerd/@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20260708.1", "", { "os": "linux", "cpu": "x64" }, "sha512-3daE60YdD7YX0Jtuzc9DE/r/qMkmx8ZvHTkF8Mzmp3F5tbzlV0DAzmu5PFUPF2WuvtKbAhZKbvC2cHmWpQYxnA=="], + + "@opencode-ai/www/@cloudflare/vite-plugin/@cloudflare/unenv-preset/workerd/@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20260708.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-VLdNYOx5Hj+9C6isy0ACWZsbMtSxex2DIJWEe7cZxUdlphZ58ZT8zxNXK8yunFiowd34hn3VwGMopdvdj8lvmA=="], + + "@opencode-ai/www/@cloudflare/vite-plugin/@cloudflare/unenv-preset/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20260708.1", "", { "os": "win32", "cpu": "x64" }, "sha512-bC/aSAwLy16Vjo24i9XU3aWH+eRgz7NeR5xPKavGbembO18ZywYTQbXh14eXtY6fAqN3RzRG8psijTdhX4xydA=="], + + "@opencode-ai/www/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="], + + "@opencode-ai/www/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="], + + "@opencode-ai/www/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="], + + "@opencode-ai/www/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="], + + "@opencode-ai/www/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="], + + "@opencode-ai/www/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="], + + "@opencode-ai/www/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ=="], + + "@opencode-ai/www/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="], + + "@opencode-ai/www/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="], + + "@opencode-ai/www/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="], + + "@opencode-ai/www/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="], + + "@opencode-ai/www/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="], + + "@opencode-ai/www/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.4" }, "os": "linux", "cpu": "s390x" }, "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg=="], + + "@opencode-ai/www/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="], + + "@opencode-ai/www/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="], + + "@opencode-ai/www/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="], + + "@opencode-ai/www/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.5", "", { "dependencies": { "@emnapi/runtime": "^1.7.0" }, "cpu": "none" }, "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw=="], + + "@opencode-ai/www/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg=="], + + "@opencode-ai/www/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="], + + "@opencode-ai/www/@cloudflare/vite-plugin/miniflare/sharp/semver": ["semver@7.8.5", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="], + + "@opencode-ai/www/@cloudflare/vite-plugin/miniflare/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20260708.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-HXFCvhS1wpg3uXO0CLUwmwC41i2loM5FSK69EUchOBpmYBAXxT1oHLm6EOA5lqhTk5Mu9kjRiQYxa1GwKPwfJg=="], + + "@opencode-ai/www/@cloudflare/vite-plugin/miniflare/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20260708.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-JVlJaKDoRTVKSroHIlf8g3UCPjKj4iDbMZE2CNYht5qQ+2rL0FAUiVlV82G3BqKnnw9kHYnnsMzC08b9zVtdzA=="], + + "@opencode-ai/www/@cloudflare/vite-plugin/miniflare/workerd/@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20260708.1", "", { "os": "linux", "cpu": "x64" }, "sha512-3daE60YdD7YX0Jtuzc9DE/r/qMkmx8ZvHTkF8Mzmp3F5tbzlV0DAzmu5PFUPF2WuvtKbAhZKbvC2cHmWpQYxnA=="], + + "@opencode-ai/www/@cloudflare/vite-plugin/miniflare/workerd/@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20260708.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-VLdNYOx5Hj+9C6isy0ACWZsbMtSxex2DIJWEe7cZxUdlphZ58ZT8zxNXK8yunFiowd34hn3VwGMopdvdj8lvmA=="], + + "@opencode-ai/www/@cloudflare/vite-plugin/miniflare/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20260708.1", "", { "os": "win32", "cpu": "x64" }, "sha512-bC/aSAwLy16Vjo24i9XU3aWH+eRgz7NeR5xPKavGbembO18ZywYTQbXh14eXtY6fAqN3RzRG8psijTdhX4xydA=="], + + "@opencode-ai/www/@tailwindcss/vite/@tailwindcss/node/lightningcss/lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="], + + "@opencode-ai/www/@tailwindcss/vite/@tailwindcss/node/lightningcss/lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="], + + "@opencode-ai/www/@tailwindcss/vite/@tailwindcss/node/lightningcss/lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="], + + "@opencode-ai/www/@tailwindcss/vite/@tailwindcss/node/lightningcss/lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="], + + "@opencode-ai/www/@tailwindcss/vite/@tailwindcss/node/lightningcss/lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="], + + "@opencode-ai/www/@tailwindcss/vite/@tailwindcss/node/lightningcss/lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="], + + "@opencode-ai/www/@tailwindcss/vite/@tailwindcss/node/lightningcss/lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="], + + "@opencode-ai/www/@tailwindcss/vite/@tailwindcss/node/lightningcss/lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="], + + "@opencode-ai/www/@tailwindcss/vite/@tailwindcss/node/lightningcss/lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="], + + "@opencode-ai/www/@tailwindcss/vite/@tailwindcss/node/lightningcss/lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="], + + "@opencode-ai/www/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.11.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ=="], + + "@opencode-ai/www/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.11.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw=="], + + "@opencode-ai/www/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA=="], + + "@opencode-ai/www/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.6", "", { "dependencies": { "@tybys/wasm-util": "^0.10.3" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" }, "bundled": true }, "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg=="], + + "@opencode-ai/www/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg=="], + + "@opencode-ai/www/@tailwindcss/vite/@tailwindcss/oxide/@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "@opencode-ai/www/wrangler/miniflare/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="], + + "@opencode-ai/www/wrangler/miniflare/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="], + + "@opencode-ai/www/wrangler/miniflare/sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="], + + "@opencode-ai/www/wrangler/miniflare/sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="], + + "@opencode-ai/www/wrangler/miniflare/sharp/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="], + + "@opencode-ai/www/wrangler/miniflare/sharp/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="], + + "@opencode-ai/www/wrangler/miniflare/sharp/@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ=="], + + "@opencode-ai/www/wrangler/miniflare/sharp/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="], + + "@opencode-ai/www/wrangler/miniflare/sharp/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="], + + "@opencode-ai/www/wrangler/miniflare/sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="], + + "@opencode-ai/www/wrangler/miniflare/sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="], + + "@opencode-ai/www/wrangler/miniflare/sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="], + + "@opencode-ai/www/wrangler/miniflare/sharp/@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.4" }, "os": "linux", "cpu": "s390x" }, "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg=="], + + "@opencode-ai/www/wrangler/miniflare/sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="], + + "@opencode-ai/www/wrangler/miniflare/sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="], + + "@opencode-ai/www/wrangler/miniflare/sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="], + + "@opencode-ai/www/wrangler/miniflare/sharp/@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.5", "", { "dependencies": { "@emnapi/runtime": "^1.7.0" }, "cpu": "none" }, "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw=="], + + "@opencode-ai/www/wrangler/miniflare/sharp/@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg=="], + + "@opencode-ai/www/wrangler/miniflare/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="], + + "@opencode-ai/www/wrangler/miniflare/sharp/semver": ["semver@7.8.5", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="], + "@puppeteer/browsers/yargs/cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "@puppeteer/browsers/yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], diff --git a/package.json b/package.json index b6329f9c96..cef269ea54 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "dev:web": "bun --cwd packages/app dev", "dev:console": "ulimit -n 10240 2>/dev/null; bun run --cwd packages/console/app dev", "dev:stats": "bun sst shell --stage=production -- bun run --cwd packages/stats/app dev", + "dev:www": "bun run --cwd packages/www dev", "dev:storybook": "bun --cwd packages/storybook storybook", "lint": "oxlint", "lint:effect-patterns": "ast-grep scan -c script/ast-grep/sgconfig.yml packages/core/src packages/server/src packages/protocol/src packages/cli/src", @@ -102,6 +103,8 @@ "devDependencies": { "@actions/artifact": "5.0.1", "@ast-grep/cli": "0.44.0", + "@types/react": "19.2.17", + "@types/react-dom": "19.2.3", "@tsconfig/bun": "catalog:", "@types/mime-types": "3.0.1", "@typescript/native-preview": "catalog:", diff --git a/packages/app/src/i18n/en.ts b/packages/app/src/i18n/en.ts index 37e372ee31..343923af76 100644 --- a/packages/app/src/i18n/en.ts +++ b/packages/app/src/i18n/en.ts @@ -65,8 +65,8 @@ export const dict = { "command.message.next.description": "Go to the next user message", "command.model.choose": "Choose model", "command.model.choose.description": "Select a different model", - "command.mcp.toggle": "Toggle MCPs", - "command.mcp.toggle.description": "Toggle MCPs", + "command.mcp.toggle": "Manage MCP servers", + "command.mcp.toggle.description": "Enable or disable MCP servers", "command.agent.cycle": "Cycle agent", "command.agent.cycle.description": "Switch to the next agent", "command.agent.cycle.reverse": "Cycle agent backwards", @@ -307,9 +307,9 @@ export const dict = { "prompt.toast.promptSendFailed.title": "Failed to send prompt", "prompt.toast.promptSendFailed.description": "Unable to retrieve session", - "dialog.mcp.title": "MCPs", + "dialog.mcp.title": "MCP servers", "dialog.mcp.description": "{{enabled}} of {{total}} enabled", - "dialog.mcp.empty": "No MCPs configured", + "dialog.mcp.empty": "No MCP servers configured", "dialog.lsp.empty": "LSPs auto-detected from file types", "dialog.plugins.empty": "Plugins configured in opencode.json", diff --git a/packages/cli/package.json b/packages/cli/package.json index fbd12647e6..e5172df3c4 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -45,6 +45,7 @@ "@parcel/watcher": "2.5.1", "effect": "catalog:", "fuzzysort": "catalog:", + "immer": "11.1.4", "jsonc-parser": "3.3.1", "open": "10.1.2", "opentui-spinner": "catalog:", diff --git a/packages/cli/src/commands/handlers/default.ts b/packages/cli/src/commands/handlers/default.ts index d7b5921628..474d2d184d 100644 --- a/packages/cli/src/commands/handlers/default.ts +++ b/packages/cli/src/commands/handlers/default.ts @@ -4,7 +4,7 @@ import { run } from "@opencode-ai/tui" import { loadBuiltinPlugins } from "@opencode-ai/tui/builtins" import { Commands } from "../commands" import { Runtime } from "../../framework/runtime" -import { TuiConfig } from "../../tui-config" +import { Config } from "../../config" import { Effect, Option } from "effect" import { Server } from "../../services/server" import { Updater } from "../../services/updater" @@ -35,13 +35,18 @@ export default Runtime.handler(Commands, (input) => ), ) preflight.loading() - const config = yield* TuiConfig.load() + const config = yield* Config.Service let disposeSlots: (() => void) | undefined - const runFork = Effect.runForkWith(yield* Effect.context()) + const context = yield* Effect.context() + const runFork = Effect.runForkWith(context) + const runPromise = Effect.runPromiseWith(context) yield* run({ server, args: { continue: input.continue, sessionID: Option.getOrUndefined(input.session) }, - config, + config: { + get: () => runPromise(config.get()), + update: (update) => runPromise(config.update(update)), + }, terminalHandoff: () => preflight.finish(), log: (level, message, tags) => { const effect = diff --git a/packages/cli/src/config/config.ts b/packages/cli/src/config/config.ts new file mode 100644 index 0000000000..d9f01b3fce --- /dev/null +++ b/packages/cli/src/config/config.ts @@ -0,0 +1,109 @@ +export * as Config from "./config" + +import { Global } from "@opencode-ai/core/global" +import { Context, Effect, FileSystem, Layer, Option, Schema, Semaphore } from "effect" +import { produce, type Draft } from "immer" +import { applyEdits, modify, parse, type ParseError } from "jsonc-parser" +import path from "path" +import { ConfigMigration } from "./migrate" +import { Info } from "./schema" + +export * from "./schema" + +export interface Interface { + readonly path: string + readonly get: () => Effect.Effect + readonly update: (update: (draft: Draft) => void) => Effect.Effect +} + +export class Service extends Context.Service()("@opencode/cli/config/Config") {} + +const decode = Schema.decodeUnknownOption(Info) +const decodeRecord = Schema.decodeUnknownOption(Schema.Record(Schema.String, Schema.Any)) +const empty: Info = {} + +export const layer = Layer.effect( + Service, + Effect.gen(function* () { + const fs = yield* FileSystem.FileSystem + const global = yield* Global.Service + const file = path.join(global.config, "cli.json") + const lock = yield* Semaphore.make(1) + + const readJson = Effect.fnUntraced(function* () { + const text = yield* fs.readFileString(file).pipe(Effect.catch(() => Effect.succeed(undefined))) + if (text === undefined) return undefined + const errors: ParseError[] = [] + const value: any = parse(text, errors, { allowTrailingComma: true }) + if (errors.length) return undefined + return Option.getOrUndefined(decodeRecord(value)) + }) + + const write = Effect.fnUntraced(function* (text: string) { + const temp = file + ".tmp" + yield* fs.makeDirectory(path.dirname(file), { recursive: true }) + yield* fs.writeFileString(temp, text, { mode: 0o600 }) + yield* fs.rename(temp, file) + }) + + const migrate = ConfigMigration.run({ file, config: global.config, state: global.state }).pipe( + Effect.provideService(FileSystem.FileSystem, fs), + ) + + const get = Effect.fn("cli.config.get")(function* () { + yield* migrate.pipe(Effect.catchCause((cause) => Effect.logWarning("failed to migrate cli config", { cause }))) + return Option.getOrElse(decode(yield* readJson()), () => empty) + }) + + const update = Effect.fn("cli.config.update")((update: (draft: Draft) => void) => + lock + .withPermits(1)( + Effect.gen(function* () { + yield* migrate + const current = Option.getOrElse(decode(yield* readJson()), () => empty) + const next = produce(current, update) + const edits = changes(current, next) + if (!edits.length) return current + const text = yield* fs.readFileString(file).pipe(Effect.catch(() => Effect.succeed("{}"))) + const updated = edits.reduce( + (text, edit) => + applyEdits( + text, + modify(text, edit.path, edit.value, { formattingOptions: { tabSize: 2, insertSpaces: true } }), + ), + text, + ) + const errors: ParseError[] = [] + const config = Option.getOrUndefined(decode(parse(updated, errors, { allowTrailingComma: true }))) + if (errors.length || config === undefined) return yield* Effect.fail(new Error("Invalid CLI config update")) + yield* write(updated.endsWith("\n") ? updated : updated + "\n") + return config + }), + ) + .pipe(Effect.mapError((cause) => new Error("Failed to update CLI config", { cause }))), + ) + + return Service.of({ path: file, get, update }) + }), +) + +type Edit = { readonly path: (string | number)[]; readonly value: any } + +function changes(before: any, after: any, path: (string | number)[] = []): Edit[] { + if (Object.is(before, after)) return [] + if ( + before !== null && + after !== null && + typeof before === "object" && + typeof after === "object" && + !Array.isArray(before) && + !Array.isArray(after) + ) { + return [...new Set([...Object.keys(before), ...Object.keys(after)])].flatMap((key) => { + if (!(key in after)) return [{ path: [...path, key], value: undefined }] + if (!(key in before)) return [{ path: [...path, key], value: after[key] }] + return changes(before[key], after[key], [...path, key]) + }) + } + return [{ path, value: after }] +} diff --git a/packages/cli/src/config/index.ts b/packages/cli/src/config/index.ts new file mode 100644 index 0000000000..60e39c3163 --- /dev/null +++ b/packages/cli/src/config/index.ts @@ -0,0 +1 @@ +export * as Config from "./config" diff --git a/packages/cli/src/config/migrate.ts b/packages/cli/src/config/migrate.ts new file mode 100644 index 0000000000..bf57fdb317 --- /dev/null +++ b/packages/cli/src/config/migrate.ts @@ -0,0 +1,142 @@ +export * as ConfigMigration from "./migrate" + +import { TuiConfigV1 } from "@opencode-ai/tui/config/v1" +import { Effect, FileSystem, Option, Schema } from "effect" +import { parse, type ParseError } from "jsonc-parser" +import path from "path" +import type { Info } from "./schema" + +const decodeV1 = Schema.decodeUnknownOption(TuiConfigV1.Info) +const decodeRecord = Schema.decodeUnknownOption(Schema.Record(Schema.String, Schema.Any)) + +export const run = Effect.fn("cli.config.migrate")(function* (input: { + readonly file: string + readonly config: string + readonly state: string +}) { + const fs = yield* FileSystem.FileSystem + if (yield* fs.exists(input.file).pipe(Effect.orElseSucceed(() => false))) return + + const legacyValue = yield* readJson(path.join(input.config, "tui.json")) + const legacy = Option.getOrUndefined(decodeV1(legacyValue)) + const kv = yield* readJson(path.join(input.state, "kv.json")) + const migrated = migrateV1(legacy, kv ?? {}) + if (!Object.keys(migrated).length) return + + const temp = input.file + ".tmp" + yield* fs.makeDirectory(path.dirname(input.file), { recursive: true }) + yield* fs.writeFileString(temp, JSON.stringify(migrated, null, 2) + "\n", { mode: 0o600 }) + yield* fs.rename(temp, input.file) + yield* Effect.logInfo("migrated cli config", { + from: [ + legacyValue === undefined ? undefined : path.join(input.config, "tui.json"), + kv === undefined ? undefined : path.join(input.state, "kv.json"), + ].filter(Boolean), + to: input.file, + }) +}) + +export function migrateV1(legacy: TuiConfigV1.Info | undefined, kv: Record): Info { + const plugins = [ + ...(legacy?.plugin?.map((plugin) => + typeof plugin === "string" ? plugin : { package: plugin[0], options: plugin[1] }, + ) ?? []), + ...Object.entries(legacy?.plugin_enabled ?? {}).map(([id, enabled]) => (enabled ? id : `-${id}`)), + ] + const themeName = legacy?.theme ?? kv.theme + const themeMode = kv.theme_mode_lock + const attentionSoundPack = kv.attention_sound_pack + const diffView = kv.diff_viewer_view ?? (legacy?.diff_style === "stacked" ? "unified" : undefined) + const thinking = + kv.thinking_mode ?? + (kv.thinking_visibility === undefined ? undefined : kv.thinking_visibility ? "show" : "hide") + + return { + ...(themeName !== undefined || themeMode !== undefined + ? { theme: { ...(themeName === undefined ? {} : { name: themeName }), ...(themeMode === undefined ? {} : { mode: themeMode }) } } + : {}), + ...(legacy?.keybinds === undefined ? {} : { keybinds: legacy.keybinds }), + ...(plugins.length ? { plugins } : {}), + ...(legacy?.leader_timeout === undefined ? {} : { leader: { timeout: legacy.leader_timeout } }), + ...(legacy?.scroll_speed === undefined && legacy?.scroll_acceleration?.enabled === undefined + ? {} + : { + scroll: { + ...(legacy.scroll_speed === undefined ? {} : { speed: legacy.scroll_speed }), + ...(legacy.scroll_acceleration?.enabled === undefined + ? {} + : { acceleration: legacy.scroll_acceleration.enabled }), + }, + }), + ...(legacy?.attention === undefined && attentionSoundPack === undefined + ? {} + : { + attention: { + ...legacy?.attention, + ...(attentionSoundPack === undefined ? {} : { sound_pack: attentionSoundPack }), + }, + }), + ...(legacy?.diff_style === undefined && + kv.diff_wrap_mode === undefined && + kv.diff_viewer_show_file_tree === undefined && + kv.diff_viewer_single_patch === undefined && + diffView === undefined + ? {} + : { + diffs: { + ...(kv.diff_wrap_mode === undefined ? {} : { wrap: kv.diff_wrap_mode }), + ...(kv.diff_viewer_show_file_tree === undefined ? {} : { tree: kv.diff_viewer_show_file_tree }), + ...(kv.diff_viewer_single_patch === undefined ? {} : { single: kv.diff_viewer_single_patch }), + ...(diffView === undefined ? {} : { view: diffView }), + }, + }), + ...(kv.terminal_title_enabled === undefined ? {} : { terminal: { title: kv.terminal_title_enabled } }), + ...(kv.file_context_enabled === undefined && kv.paste_summary_enabled === undefined + ? {} + : { + prompt: { + ...(kv.file_context_enabled === undefined ? {} : { editor: kv.file_context_enabled }), + ...(kv.paste_summary_enabled === undefined + ? {} + : { paste: kv.paste_summary_enabled ? ("compact" as const) : ("full" as const) }), + }, + }), + ...(kv.sidebar === undefined && + kv.scrollbar_visible === undefined && + thinking === undefined && + kv.exploration_grouping === undefined + ? {} + : { + session: { + ...(kv.sidebar === undefined ? {} : { sidebar: kv.sidebar }), + ...(kv.scrollbar_visible === undefined ? {} : { scrollbar: kv.scrollbar_visible }), + ...(thinking === undefined ? {} : { thinking }), + ...(kv.exploration_grouping === undefined + ? {} + : { grouping: kv.exploration_grouping ? ("auto" as const) : ("none" as const) }), + }, + }), + ...(kv.tips_hidden === undefined && kv.dismissed_getting_started === undefined + ? {} + : { + hints: { + ...(kv.tips_hidden === undefined ? {} : { tips: !kv.tips_hidden }), + ...(kv.dismissed_getting_started === undefined + ? {} + : { onboarding: !kv.dismissed_getting_started }), + }, + }), + ...(kv.animations_enabled === undefined ? {} : { animations: kv.animations_enabled }), + ...(legacy?.mouse === undefined ? {} : { mouse: legacy.mouse }), + } +} + +const readJson = Effect.fnUntraced(function* (target: string) { + const fs = yield* FileSystem.FileSystem + const text = yield* fs.readFileString(target).pipe(Effect.catch(() => Effect.succeed(undefined))) + if (text === undefined) return undefined + const errors: ParseError[] = [] + const value: any = parse(text, errors, { allowTrailingComma: true }) + if (errors.length) return undefined + return Option.getOrUndefined(decodeRecord(value)) +}) diff --git a/packages/cli/src/config/schema.ts b/packages/cli/src/config/schema.ts new file mode 100644 index 0000000000..fc6b9724c6 --- /dev/null +++ b/packages/cli/src/config/schema.ts @@ -0,0 +1,5 @@ +import { Config } from "@opencode-ai/tui/config" +import { Schema } from "effect" + +export const Info = Schema.Struct({ ...Config.Info.fields }) +export type Info = Schema.Schema.Type diff --git a/packages/cli/src/framework/runtime.ts b/packages/cli/src/framework/runtime.ts index 2108da415d..cffd80e772 100644 --- a/packages/cli/src/framework/runtime.ts +++ b/packages/cli/src/framework/runtime.ts @@ -3,6 +3,7 @@ import { Command } from "effect/unstable/cli" import { Spec } from "./spec" import { Global } from "@opencode-ai/core/global" import { Updater } from "../services/updater" +import { Config } from "../config" export type Input = Value extends Spec.Node @@ -13,18 +14,26 @@ export type Input = type RuntimeHandler = ( input: unknown, -) => Effect.Effect +) => Effect.Effect< + void, + unknown, + FileSystem.FileSystem | Global.Service | Updater.Service | Config.Service | Scope.Scope +> type Loader = () => Promise<{ default: ( input: Input, - ) => Effect.Effect + ) => Effect.Effect< + void, + any, + FileSystem.FileSystem | Global.Service | Updater.Service | Config.Service | Scope.Scope + > }> type ProvidedCommand = Command.Command< string, unknown, unknown, unknown, - FileSystem.FileSystem | Global.Service | Updater.Service | Scope.Scope + FileSystem.FileSystem | Global.Service | Updater.Service | Config.Service | Scope.Scope > export type Handlers = keyof Node["commands"] extends never diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 75f798b0eb..2128495526 100755 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -11,6 +11,7 @@ import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" import { LayerNode } from "@opencode-ai/core/effect/layer-node" import { Global } from "@opencode-ai/core/global" import { AppProcess } from "@opencode-ai/core/process" +import { Config } from "./config" const Handlers = Runtime.handlers(Commands, { $: () => import("./commands/handlers/default"), @@ -51,6 +52,7 @@ Effect.logInfo("cli starting", { }).pipe( Effect.flatMap(() => Runtime.run(Commands, Handlers, { version: InstallationVersion })), Effect.annotateLogs({ role: "cli" }), + Effect.provide(Config.layer), Effect.provide(Updater.layer), Effect.provide(AppNodeBuilder.build(LayerNode.group([Global.node, AppProcess.node]))), Effect.provide(Observability.layer), diff --git a/packages/cli/src/tui-config.ts b/packages/cli/src/tui-config.ts deleted file mode 100644 index 1b838917bb..0000000000 --- a/packages/cli/src/tui-config.ts +++ /dev/null @@ -1,24 +0,0 @@ -export * as TuiConfig from "./tui-config" - -import { Global } from "@opencode-ai/core/global" -import { TuiConfig } from "@opencode-ai/tui/config/v1" -import { Effect, FileSystem, Option, Schema } from "effect" -import { parse, type ParseError } from "jsonc-parser" -import path from "path" - -export const load = Effect.fn("TuiConfig.load")(function* () { - const fs = yield* FileSystem.FileSystem - const global = yield* Global.Service - const filepath = path.join(global.config, "tui.json") - const text = yield* fs.readFileString(filepath).pipe(Effect.catch(() => Effect.succeed(undefined))) - if (!text) return TuiConfig.resolve({}, { terminalSuspend: process.platform !== "win32" }) - - const errors: ParseError[] = [] - const input: unknown = parse(text, errors, { allowTrailingComma: true }) - if (errors.length) return TuiConfig.resolve({}, { terminalSuspend: process.platform !== "win32" }) - - return TuiConfig.resolve( - Option.getOrElse(Schema.decodeUnknownOption(TuiConfig.Info)(input), () => ({})), - { terminalSuspend: process.platform !== "win32" }, - ) -}) diff --git a/packages/cli/test/config.test.ts b/packages/cli/test/config.test.ts new file mode 100644 index 0000000000..a337682666 --- /dev/null +++ b/packages/cli/test/config.test.ts @@ -0,0 +1,144 @@ +import { NodeFileSystem } from "@effect/platform-node" +import { Global } from "@opencode-ai/core/global" +import { Effect } from "effect" +import { expect, test } from "bun:test" +import path from "path" +import { Config } from "../src/config" + +function run(directory: string, effect: Effect.Effect) { + return Effect.runPromise( + effect.pipe( + Effect.provide(Config.layer), + Effect.provide(Global.layerWith({ config: directory, state: directory })), + Effect.provide(NodeFileSystem.layer), + ), + ) +} + +test("migrates tui and kv config into cli.json", async () => { + const directory = await Bun.$`mktemp -d`.text().then((value) => value.trim()) + await Bun.write( + path.join(directory, "tui.json"), + JSON.stringify({ + theme: "legacy", + keybinds: { leader: "ctrl+o" }, + plugin: [["example", { mode: "safe" }]], + plugin_enabled: { disabled: false }, + leader_timeout: 500, + scroll_speed: 2, + scroll_acceleration: { enabled: true }, + diff_style: "stacked", + mouse: false, + }), + ) + await Bun.write( + path.join(directory, "kv.json"), + JSON.stringify({ + theme_mode_lock: "light", + attention_sound_pack: "custom.pack", + diff_wrap_mode: "none", + diff_viewer_show_file_tree: false, + diff_viewer_single_patch: true, + diff_viewer_view: "split", + terminal_title_enabled: false, + file_context_enabled: false, + paste_summary_enabled: false, + sidebar: "hide", + scrollbar_visible: true, + thinking_mode: "show", + exploration_grouping: false, + tips_hidden: true, + dismissed_getting_started: true, + animations_enabled: false, + skipped_version: "9.9.9", + which_key_layout: "overlay", + }), + ) + + try { + const config = await run( + directory, + Effect.gen(function* () { + const service = yield* Config.Service + return yield* service.get() + }), + ) + + expect(config).toMatchObject({ + theme: { name: "legacy", mode: "light" }, + keybinds: { leader: "ctrl+o" }, + plugins: [{ package: "example", options: { mode: "safe" } }, "-disabled"], + leader: { timeout: 500 }, + scroll: { speed: 2, acceleration: true }, + attention: { sound_pack: "custom.pack" }, + diffs: { wrap: "none", tree: false, single: true, view: "split" }, + terminal: { title: false }, + prompt: { editor: false, paste: "full" }, + session: { sidebar: "hide", scrollbar: true, thinking: "show", grouping: "none" }, + hints: { tips: false, onboarding: false }, + animations: false, + mouse: false, + }) + expect(config).not.toHaveProperty("skipped_version") + expect(config).not.toHaveProperty("which_key") + expect((await Bun.file(path.join(directory, "cli.json")).json()).keybinds).toEqual({ leader: "ctrl+o" }) + expect(await Bun.file(path.join(directory, "cli.json")).exists()).toBe(true) + expect(await Bun.file(path.join(directory, "tui.json")).exists()).toBe(true) + expect(await Bun.file(path.join(directory, "kv.json")).exists()).toBe(true) + } finally { + await Bun.$`rm -rf ${directory}` + } +}) + +test("migrates before the first update and does not remigrate afterward", async () => { + const directory = await Bun.$`mktemp -d`.text().then((value) => value.trim()) + await Bun.write(path.join(directory, "tui.json"), JSON.stringify({ theme: "legacy" })) + + try { + const config = await run( + directory, + Effect.gen(function* () { + const service = yield* Config.Service + yield* service.update((draft) => { + draft.animations = false + draft.mouse = false + }) + yield* Effect.promise(() => + Bun.write(path.join(directory, "tui.json"), JSON.stringify({ theme: "changed" })), + ) + return yield* service.get() + }), + ) + + expect(config).toEqual({ theme: { name: "legacy" }, animations: false, mouse: false }) + expect(await Bun.file(path.join(directory, "cli.json")).json()).toEqual({ + theme: { name: "legacy" }, + animations: false, + mouse: false, + }) + } finally { + await Bun.$`rm -rf ${directory}` + } +}) + +test("updates a config draft while preserving JSONC comments", async () => { + const directory = await Bun.$`mktemp -d`.text().then((value) => value.trim()) + await Bun.write(path.join(directory, "cli.json"), "{\n // Keep this comment\n \"animations\": true\n}\n") + + try { + const config = await run( + directory, + Effect.gen(function* () { + const service = yield* Config.Service + return yield* service.update((draft) => { + draft.prompt = { paste: "compact" } + }) + }), + ) + + expect(config).toEqual({ animations: true, prompt: { paste: "compact" } }) + expect(await Bun.file(path.join(directory, "cli.json")).text()).toContain("// Keep this comment") + } finally { + await Bun.$`rm -rf ${directory}` + } +}) diff --git a/packages/cli/test/tui-config.test.ts b/packages/cli/test/tui-config.test.ts deleted file mode 100644 index a9656c4fae..0000000000 --- a/packages/cli/test/tui-config.test.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { NodeFileSystem } from "@effect/platform-node" -import { Global } from "@opencode-ai/core/global" -import { Effect } from "effect" -import { expect, test } from "bun:test" -import path from "path" -import { TuiConfig } from "../src/tui-config" - -test("loads the global tui config", async () => { - const directory = await Bun.$`mktemp -d`.text().then((value) => value.trim()) - await Bun.write(path.join(directory, "tui.json"), JSON.stringify({ keybinds: { leader: "ctrl+o" } })) - - try { - const config = await Effect.runPromise( - TuiConfig.load().pipe( - Effect.provide(Global.layerWith({ config: directory })), - Effect.provide(NodeFileSystem.layer), - ), - ) - - expect(config.keybinds.get("leader")?.[0]?.key).toBe("ctrl+o") - expect(config.keybinds.get("session.new")?.[0]?.key).toBe("n") - } finally { - await Bun.$`rm -rf ${directory}` - } -}) diff --git a/packages/client/src/effect/service.ts b/packages/client/src/effect/service.ts index f047d462bf..3c42f0de56 100644 --- a/packages/client/src/effect/service.ts +++ b/packages/client/src/effect/service.ts @@ -59,11 +59,11 @@ const discoverLocal = Effect.fnUntraced(function* (options: Options) { export const start = Effect.fn("service.start")(function* (options: StartOptions = {}) { const compatible = yield* discover(options) if (compatible !== undefined) return compatible - const mismatched = yield* find(options) - yield* Effect.sync(() => - options.onStart?.(mismatched === undefined ? "missing" : "version-mismatch", mismatched?.info), - ) - if (mismatched !== undefined) yield* kill(mismatched.info, options).pipe(Effect.ignore) + const existing = yield* find(options) + if (existing?.version !== undefined && (options.version === undefined || existing.version === options.version)) + return existing.endpoint + yield* Effect.sync(() => options.onStart?.(existing === undefined ? "missing" : "version-mismatch", existing?.info)) + if (existing !== undefined) yield* kill(existing.info, options).pipe(Effect.ignore) const [command, ...args] = options.command ?? ["opencode", "serve", "--service"] if (command === undefined) return yield* Effect.fail(new Error("Missing service command")) @@ -138,6 +138,7 @@ const read = Effect.fnUntraced(function* (file?: string) { type LocalService = { readonly info: Info readonly endpoint: Endpoint + readonly version?: string } const probe = Effect.fnUntraced(function* (info: Info, version?: string, allowLegacy = false) { @@ -161,7 +162,7 @@ const probe = Effect.fnUntraced(function* (info: Info, version?: string, allowLe if (health.value.pid !== info.pid) return undefined if (info.version !== undefined && health.value.version !== info.version) return undefined if (version !== undefined && health.value.version !== version) return undefined - return { info, endpoint } satisfies LocalService + return { info, endpoint, version: health.value.version } satisfies LocalService } if ( !allowLegacy || diff --git a/packages/client/test/fixture/service.ts b/packages/client/test/fixture/service.ts new file mode 100644 index 0000000000..3f77edb419 --- /dev/null +++ b/packages/client/test/fixture/service.ts @@ -0,0 +1,39 @@ +import { rename, writeFile } from "node:fs/promises" + +const [registration, mode] = process.argv.slice(2) +if (registration === undefined || mode === undefined) throw new Error("Missing service fixture arguments") + +let requests = 0 +const server = Bun.serve({ + port: 0, + async fetch(request) { + if (new URL(request.url).pathname !== "/api/health") return new Response(null, { status: 404 }) + requests += 1 + if (mode === "modern" && requests === 1) { + await writeFile(registration + ".first-request", "") + while (!(await Bun.file(registration + ".release").exists())) await Bun.sleep(5) + return new Response(null, { status: 503 }) + } + if (mode === "legacy") return Response.json({ healthy: true }) + return Response.json({ healthy: true, version: "test", pid: process.pid }) + }, +}) + +await writeFile( + registration + ".tmp", + JSON.stringify({ + id: crypto.randomUUID(), + version: mode === "legacy" ? undefined : "test", + url: server.url.toString(), + pid: process.pid, + }), + { mode: 0o600 }, +) +await rename(registration + ".tmp", registration) + +const shutdown = () => { + server.stop(true) + process.exit() +} +process.on("SIGTERM", shutdown) +process.on("SIGINT", shutdown) diff --git a/packages/client/test/service.test.ts b/packages/client/test/service.test.ts new file mode 100644 index 0000000000..21428c9166 --- /dev/null +++ b/packages/client/test/service.test.ts @@ -0,0 +1,91 @@ +import { NodeFileSystem } from "@effect/platform-node" +import { afterEach, expect, test } from "bun:test" +import { Effect } from "effect" +import { mkdtemp, rm, writeFile } from "node:fs/promises" +import { tmpdir } from "node:os" +import { join } from "node:path" +import { Service } from "../src/effect/index" + +const fixture = join(import.meta.dir, "fixture/service.ts") +const processes: Bun.Subprocess[] = [] +const directories: string[] = [] + +afterEach(async () => { + processes.forEach((process) => process.kill("SIGTERM")) + await Promise.all(processes.splice(0).map((process) => process.exited)) + await Promise.all(directories.splice(0).map((directory) => rm(directory, { recursive: true, force: true }))) +}) + +test("a concurrent same-version start cannot invalidate a resolved endpoint", async () => { + const directory = await temp() + const registration = join(directory, "service.json") + spawn(registration, "modern") + await waitForFile(registration) + const original = await Bun.file(registration).json() + + const starts: Service.StartReason[] = [] + const first = run( + Service.start({ + file: registration, + version: "test", + command: [], + onStart: (reason) => starts.push(reason), + }), + ) + await waitForFile(registration + ".first-request") + + const resolved = await run(Service.start({ file: registration, version: "test" })) + expect(resolved.url).toBe(original.url) + + await writeFile(registration + ".release", "") + await first + + expect(starts).toEqual([]) + expect(await Bun.file(registration).json()).toEqual(original) + expect(await health(resolved.url)).toEqual({ healthy: true, version: "test", pid: original.pid }) +}) + +test("a legacy health response is still replaced", async () => { + const directory = await temp() + const registration = join(directory, "service.json") + const existing = spawn(registration, "legacy") + await waitForFile(registration) + + const starts: Service.StartReason[] = [] + const result = run(Service.start({ file: registration, command: [], onStart: (reason) => starts.push(reason) })) + + await expect(result).rejects.toThrow("Missing service command") + expect(starts).toEqual(["version-mismatch"]) + await existing.exited +}) + +function run(effect: Effect.Effect) { + return Effect.runPromise(effect.pipe(Effect.provide(NodeFileSystem.layer))) +} + +function spawn(registration: string, mode: string, ...args: string[]) { + const subprocess = Bun.spawn([process.execPath, fixture, registration, mode, ...args], { + stdout: "ignore", + stderr: "inherit", + }) + processes.push(subprocess) + return subprocess +} + +async function temp() { + const directory = await mkdtemp(join(tmpdir(), "opencode-client-service-")) + directories.push(directory) + return directory +} + +async function waitForFile(file: string) { + for (let attempt = 0; attempt < 600; attempt++) { + if (await Bun.file(file).exists()) return + await Bun.sleep(5) + } + throw new Error(`Timed out waiting for ${file}`) +} + +async function health(url: string) { + return fetch(new URL("/api/health", url), { signal: AbortSignal.timeout(1_000) }).then((response) => response.json()) +} diff --git a/packages/codemode/AGENTS.md b/packages/codemode/AGENTS.md index bc72c815ea..94c3fcb2d7 100644 --- a/packages/codemode/AGENTS.md +++ b/packages/codemode/AGENTS.md @@ -4,7 +4,7 @@ - Do not add a speculative generic permission or approval policy. A host omits tools it does not expose and enforces domain authorization inside each provided tool. - Keep Code Mode unaware of host session, channel, and conversation models. The hosting application supplies trusted execution scope around it. - Tool schemas are the model-facing Interface. Keep arguments minimal and natural to the operation; never add unrelated IDs as ambient capability tokens. -- When interpreter behavior or support changes, update `interpreter-support.md` and direct tests in the same PR. Update `codemode.md` when the package design, integration status, or rationale changes. +- When interpreter behavior or support changes, update `interpreter-support.md` and direct tests in the same PR. ## OpenAPI diff --git a/packages/codemode/README.md b/packages/codemode/README.md index 1b30549ec8..e8447e09b2 100644 --- a/packages/codemode/README.md +++ b/packages/codemode/README.md @@ -177,8 +177,8 @@ No limit has a default, on purpose: execution budgets are host policy. A host wi interruption should set `maxOutputBytes` and `timeoutMs`. Limits are safe integers; invalid configuration throws a `RangeError` at construction. Exceeding `maxOutputBytes` never fails the execution - oversized output is truncated with an in-band marker. The timeout interrupts in-flight tool fibers and pure busy loops alike; a value the program -already returned survives a cleanup timeout as a success with a `TimeoutExceeded` warning. Two internals are fixed -constants, not knobs: at most 8 concurrent tool calls, and 32 levels of data nesting at boundaries. +already returned survives a cleanup timeout as a success with a `TimeoutExceeded` warning. CodeMode does not limit +tool-call concurrency. Data nesting at boundaries is limited to 32 levels. ## Boundaries and Non-Goals diff --git a/packages/codemode/codemode.md b/packages/codemode/codemode.md deleted file mode 100644 index 22092c533e..0000000000 --- a/packages/codemode/codemode.md +++ /dev/null @@ -1,164 +0,0 @@ -# CodeMode Design and Status - -This is the living design and status document for `@opencode-ai/codemode` and its existing V2 OpenCode adapter. -It records current behavior, intentional boundaries, durable rationale, and material remaining work. - -Completed implementation history, branch names, test counts, and closed findings belong in git, not here. Remove -completed work instead of preserving checked-off chronology. - -Detailed package API documentation lives in [README.md](./README.md), and the checkable language/runtime matrix lives -in [interpreter-support.md](./interpreter-support.md). OpenAPI-specific follow-ups live in -[src/openapi/TODO.md](./src/openapi/TODO.md). - -## How CodeMode Works - -### Purpose - -CodeMode gives a model one `execute` tool backed by a confined JavaScript interpreter. Inside the program, the model -can call an explicit tree of schema-described tools, sequence dependent work, run independent calls concurrently, -and filter or aggregate results before returning them to the agent loop. - -The goals are: - -- Reduce model context consumed by large tool catalogs. -- Avoid an agent round-trip between every dependent tool call. -- Keep large intermediate results inside the program instead of sending them through model context. -- Give generated code only the authority explicitly supplied by the host. - -CodeMode is an orchestration language, not a general JavaScript runtime or an application authorization system. - -### Runtime - -The generic runtime lives in `packages/codemode` and is host-neutral: - -1. The host builds a tree of `Tool.make(...)` definitions and calls `CodeMode.make(...)` or `CodeMode.execute(...)`. -2. CodeMode generates model instructions, a budgeted inline catalog, and the global `search(...)` built-in. -3. TypeScript syntax is transpiled away, Acorn parses the resulting JavaScript, and an owned tree-walking interpreter - executes it without `eval`. -4. Tool inputs and outputs cross schema and plain-data boundaries before they become visible on either side. -5. Execution returns `CodeMode.Result`. Expected program and tool failures are diagnostic data; host interruption - remains Effect interruption. - -Effect Schemas validate and transform tool inputs and outputs. JSON Schemas render model-facing signatures but do not -validate values; adapter-provided values still cross the plain-data boundary. A tool without an output schema is -advertised as `Promise`. - -### Discovery and model workflow - -The model sees a token-budgeted catalog. Every namespace remains visible, and complete signatures are selected -round-robin across namespaces so one large namespace cannot starve the others. The global `search(...)` built-in is -always callable - synchronously, counted as an admitted tool call - and is advertised when the inline catalog is -partial. - -The intended workflow is: - -1. Pick an exact signature from the inline catalog, or return `search(...)` results and use a selected path in the - next execution. -2. Call the exact returned path without guessing or normalizing segments. -3. Narrow `Promise` results before reading fields. -4. Start independent calls together and await them with `Promise.all`. -5. Filter and aggregate inside the program, then return only the data needed by the model. - -Search returns directly usable JavaScript paths, descriptions, and complete TypeScript signatures. It supports exact -path lookup, namespace browsing, deterministic ranking, and pagination. - -### Tool execution - -Every sandbox promise starts eagerly on a run-once fiber owned by the whole CodeMode execution, including tool calls, -async functions, chained `.then`/`.catch`/`.finally` reactions, `new Promise(executor)` constructions, and the -`Promise.all`/`allSettled`/`race`/`any`/`resolve`/`reject` statics. Nested functions therefore cannot end the lifetime -of work they started. -Independent aggregate batches overlap, and rejection is observed at the eventual `await` or chained rejection handler. -`Promise.race` and `Promise.any` use native non-cancelling settlement semantics: the deciding member wins while losers -continue running, and an all-rejected `Promise.any` rejects with an `AggregateError`. `new Promise(...)` hands the -executor first-class resolve/reject callables that may escape and settle the promise later, exactly once. -Reaction ordering matches what V8 makes observable - handlers and await continuations are deferred and run in attach -order, and a combinator settles one reaction turn after its deciding member - without promising exact microtask-count -parity beyond that. At normal completion CodeMode interrupts everything still running - race losers, -fail-fast `Promise.all` stragglers, and fire-and-forget calls alike: the program has returned, so no future await can -exist, and work whose completion matters must be awaited by the program. Waiting for any class of leftover instead -would let it hold the execution open, or deadlock it when queued work needs tool-call permits the leftovers occupy. -Rejections that settled un-awaited before the return become `Success.warnings` diagnostics. A fatal program failure or -host interruption closes the execution promise scope and interrupts its active fibers instead. A timeout does the -same, except that a value the program already returned is preserved alongside a `TimeoutExceeded` warning rather than -discarded. At most eight tool calls execute concurrently. - -The public execution-policy knobs are `timeoutMs`, `maxToolCalls`, and `maxOutputBytes`. The package supplies no -defaults because budgets are host policy. The interpreter also enforces fixed internal boundaries for tool-call -concurrency and data nesting depth. `maxOutputBytes` bounds retained payload bytes, not the complete rendered message; -warning diagnostics have an equal separate budget so a large value cannot starve them, and fixed truncation notices and -host-added framing are intentionally outside the budgets. - -### Data, files, and failures - -Program results and tool arguments are JSON-like data. Dates become ISO strings at host boundaries; RegExp, Map, and -Set values become `{}` as they do under JSON serialization. Promise and runtime reference values cannot cross the -boundary. - -Unknown host failures and invalid outputs are sanitized. `ToolError` is the explicit channel for a safe message that a -tool wants the model to see. Diagnostic categories distinguish parsing, unsupported syntax, unknown tools, invalid -data, tool failures, limits, timeouts, execution failures, and warning truncation. - -Files and other attachment content stay outside the interpreter. A host may collect them while child tools execute and -attach them to the outer result, but the program receives only the structured tool output. - -### V2 OpenCode adapter - -CodeMode is integrated into V2 through `packages/core/src/tool/registry.ts` and -`packages/core/src/tool/execute.ts`: - -- Core has one canonical `Tool` representation. Location-scoped producers register direct or deferred tools through - `Tools.Service`. -- Each model step snapshots effective registrations, applies catalog visibility filtering, and exposes direct tools - normally. -- When visible deferred tools exist, Core reserves and materializes one `execute` tool. Grouped deferred tools become - CodeMode namespaces instead of flattened model-facing names. -- Nested calls execute the registered `Tool` values captured for the model request; later registrations affect later - requests. -- Authorization and side-effect ordering remain responsibilities of the leaf tool. Catalog visibility is not execution - authorization. -- Structured child output enters the interpreter. File parts are collected host-side and attached to the outer result. -- Nested call statuses are returned as final `execute` metadata for the TUI. -- `execute` is the one model-facing tool invocation. Nested calls reuse its invocation context and do not independently - run registry hooks or model-output bounding; this keeps complete intermediate structured values available for - in-program filtering. The outer `execute` settlement is the single model-output bounding boundary. -- Core supplies no CodeMode timeout or tool-call limit. User cancellation interrupts the outer invocation and its - supervised children; the outer settlement applies Core's normal output-retention policy. - -MCP tools use this canonical path: they register as grouped tools and are deferred while CodeMode is enabled. Existing -output schemas are preserved in generated signatures. Direct Core tools remain direct and are not ambient globals -inside CodeMode. - -## Intentionally Unsupported - -These are product boundaries rather than DSL backlog: - -- Ambient filesystem, process, environment, network, credential, or application access. External work must go through - supplied tools. -- Modules, imports, dynamic imports, `eval`, arbitrary host globals, npm packages, and prototype mutation. -- Generic permission prompts, authorization policy, durable pause/resume, replay, storage, or exactly-once external - side effects. Hosts and tools own those concerns. -- Heuristic parsing of text tool results as JSON. A result should not silently change type based on its contents. - -The OpenAPI adapter may gain more transports and encodings, but it must continue skipping operations it cannot -represent accurately rather than guessing semantics. - -## Decisions and Rationale - -| Decision | Rationale | -| ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| Keep an owned tree-walking interpreter. | The product need is bounded tool orchestration, not arbitrary JavaScript. Owning the language surface keeps authority and behavior explicit. | -| Treat schemas as the model-facing interface. | Signatures drive correct calls; Effect Schema also provides the runtime validation boundary, while JSON Schema supports adapter interoperability. | -| Keep authority host-owned. | CodeMode can only confine programs to supplied tools. The host chooses those tools, and each tool enforces its own authorization and side-effect policy. | -| Use progressive catalog disclosure plus search. | Large tool sets should not consume the prompt, but every namespace must remain discoverable and speculative search calls should remain valid. | -| Start promises eagerly and supervise them for the execution. | This preserves normal call-time parallelism and run-once settlement while allowing pending work to be interrupted when the program returns. | -| Keep files outside the sandbox value space. | Models should compose structured data without routing binary payloads through generated code or context. | -| Treat `execute` as the model-facing invocation boundary. | Nested calls are implementation details of one orchestration program. Reusing the outer context and bounding only the final result preserves complete intermediate data without inventing durable child-call identities. | -| Return expected failures as data. | Models need actionable diagnostics without exposing private host causes; host interruption and defects must still propagate correctly. | -| Leave execution-limit defaults to hosts. | Appropriate budgets depend on the surrounding product and its own cancellation, retention, and output-bounding policies. | -| Skip unsupported OpenAPI operations. | Incorrect parameter encoding, authentication, or transport behavior is worse than a precise `skipped` reason. | - -## Remaining Work - -The [interpreter support checklist](./interpreter-support.md) owns concrete DSL, standard-library, semantic-correctness, -diagnostic, and data-boundary work. OpenAPI adapter work remains in [src/openapi/TODO.md](./src/openapi/TODO.md). diff --git a/packages/codemode/interpreter-support.md b/packages/codemode/interpreter-support.md index 0bfef64ef3..9962302c3c 100644 --- a/packages/codemode/interpreter-support.md +++ b/packages/codemode/interpreter-support.md @@ -24,7 +24,7 @@ ultimate source of truth. - [x] Tool calls through the host-provided `tools` tree only. - [x] The global `search(...)` built-in: synchronous tool discovery that counts as an admitted tool call and is shadowable by program declarations like other globals. -- [x] Cooperative timeout, tool-call accounting, output bounding, and a maximum of eight concurrent tool calls. +- [x] Cooperative timeout, an optional total tool-call limit, output bounding, and unrestricted tool-call concurrency. - [ ] Full JavaScript or TypeScript compatibility. CodeMode is a bounded orchestration language. ## Values and literals @@ -171,7 +171,6 @@ ultimate source of truth. - [ ] `Array.prototype.toSpliced`. - [ ] Canonical index handling: a key such as `"01"` must not alias index `1`. - [ ] Complete sparse-array parity. Promise combinators do consume holes as `undefined` members, as in JS. -- [ ] Correct `findLast` return behavior when its predicate mutates the examined element. ## Strings @@ -291,7 +290,6 @@ ultimate source of truth. These are actionable implementation items. Check them off only when behavior and direct tests land. - [x] Return real promises from `Promise.all`, `Promise.allSettled`, and `Promise.race`. -- [ ] Bound pending tool-call admission/allocation in addition to execution concurrency. - [ ] Guarantee every advertised tool path is executable, including dotted and blocked path segments. - [ ] Define safe outbound handling for non-finite numbers and `undefined` so invalid values cannot silently become `null` in render-only or OpenAPI tool calls. diff --git a/packages/codemode/src/interpreter/methods.ts b/packages/codemode/src/interpreter/methods.ts index 365a077837..5c3574088b 100644 --- a/packages/codemode/src/interpreter/methods.ts +++ b/packages/codemode/src/interpreter/methods.ts @@ -727,16 +727,16 @@ const invokeArrayMethod = ( } return undefined case "reduce": { - let accumulator: unknown - let start: number - if (args.length >= 2) { - accumulator = args[1] - start = 0 - } else { - if (length === 0) - throw new InterpreterRuntimeError("Array.reduce of an empty array with no initial value.", node) - accumulator = target[0] - start = 1 + let start = 0 + let accumulator = args[1] + if (args.length < 2) { + while (start < length && !(start in target)) start += 1 + if (start === length) + throw new InterpreterRuntimeError("Array.reduce of an empty array with no initial value.", node).as( + "TypeError", + ) + accumulator = target[start] + start += 1 } for (let index = start; index < length; index += 1) { if (!(index in target)) continue @@ -745,16 +745,16 @@ const invokeArrayMethod = ( return accumulator } case "reduceRight": { - let accumulator: unknown - let start: number - if (args.length >= 2) { - accumulator = args[1] - start = length - 1 - } else { - if (length === 0) - throw new InterpreterRuntimeError("Array.reduceRight of an empty array with no initial value.", node) - accumulator = target[length - 1] - start = length - 2 + let start = length - 1 + let accumulator = args[1] + if (args.length < 2) { + while (start >= 0 && !(start in target)) start -= 1 + if (start < 0) + throw new InterpreterRuntimeError("Array.reduceRight of an empty array with no initial value.", node).as( + "TypeError", + ) + accumulator = target[start] + start -= 1 } for (let index = start; index >= 0; index -= 1) { if (!(index in target)) continue @@ -764,7 +764,8 @@ const invokeArrayMethod = ( } case "findLast": for (let index = length - 1; index >= 0; index -= 1) { - if (yield* apply([target[index], index, target])) return target[index] + const item = target[index] + if (yield* apply([item, index, target])) return item } return undefined case "findLastIndex": diff --git a/packages/codemode/src/interpreter/runtime.ts b/packages/codemode/src/interpreter/runtime.ts index 1770c84e94..3b9e18cc9d 100644 --- a/packages/codemode/src/interpreter/runtime.ts +++ b/packages/codemode/src/interpreter/runtime.ts @@ -1,4 +1,4 @@ -import { Cause, Effect, Semaphore } from "effect" +import { Cause, Effect } from "effect" import { isBlockedMember, ToolReference, ToolRuntimeError, type SafeObject } from "../tool-runtime.js" import { type AstNode, @@ -50,7 +50,7 @@ import { dateMethods } from "../stdlib/date.js" import { mathConstants } from "../stdlib/math.js" import { numberConstants, numberMethods, numberStatics } from "../stdlib/number.js" import { objectMethodsPreservingIdentity } from "../stdlib/object.js" -import { promiseStatics, TOOL_CALL_CONCURRENCY } from "../stdlib/promise.js" +import { promiseStatics } from "../stdlib/promise.js" import { escapeRegexHint, regexpMethods, regexpProperties, regexFailureReason } from "../stdlib/regexp.js" import { stringMethods, stringStatics } from "../stdlib/string.js" import { @@ -150,7 +150,6 @@ export class Interpreter { private readonly invokeSearch: (args: Array) => Effect.Effect private readonly toolKeys: (path: ReadonlyArray) => ReadonlyArray private readonly logs: Array - private readonly callPermits: Semaphore.Semaphore private readonly promises: PromiseRuntime private readonly runner: CallbackRunner = { invokeFunction: (fn, args) => this.invokeFunction(fn, args), @@ -163,7 +162,6 @@ export class Interpreter { toolKeys: (path: ReadonlyArray) => ReadonlyArray, promises: PromiseRuntime, logs: Array = [], - callPermits: Semaphore.Semaphore = Semaphore.makeUnsafe(TOOL_CALL_CONCURRENCY), ) { const globalScope = new Map() this.scopes = new ScopeStack([globalScope]) @@ -171,7 +169,6 @@ export class Interpreter { this.invokeSearch = invokeSearch this.toolKeys = toolKeys this.logs = logs - this.callPermits = callPermits this.promises = promises globalScope.set("tools", { mutable: false, value: new ToolReference([]) }) globalScope.set("search", { mutable: false, value: new SearchFunction() }) @@ -239,7 +236,7 @@ export class Interpreter { path: ReadonlyArray, args: Array, ): Effect.Effect { - return this.createPromise(this.callPermits.withPermit(Effect.suspend(() => this.invokeTool(path, args)))) + return this.createPromise(Effect.suspend(() => this.invokeTool(path, args))) } private createPromise(effect: Effect.Effect): Effect.Effect { @@ -1496,14 +1493,7 @@ export class Interpreter { } private invokeFunction(fn: CodeModeFunction, args: Array): Effect.Effect { - const invocation = new Interpreter( - this.invokeTool, - this.invokeSearch, - this.toolKeys, - this.promises, - this.logs, - this.callPermits, - ) + const invocation = new Interpreter(this.invokeTool, this.invokeSearch, this.toolKeys, this.promises, this.logs) invocation.scopes = new ScopeStack([...fn.capturedScopes, new Map()]) const run = Effect.gen(function* () { // Seed all parameters first so defaults cannot fall through to same-named outer bindings. diff --git a/packages/codemode/src/stdlib/promise.ts b/packages/codemode/src/stdlib/promise.ts index 2bad2caf0c..73d02d6c0c 100644 --- a/packages/codemode/src/stdlib/promise.ts +++ b/packages/codemode/src/stdlib/promise.ts @@ -1,5 +1,3 @@ import type { PromiseMethodName } from "../interpreter/model.js" export const promiseStatics = new Set(["all", "allSettled", "race", "any", "resolve", "reject"]) - -export const TOOL_CALL_CONCURRENCY = 8 diff --git a/packages/codemode/test/array-callbacks-test262.test.ts b/packages/codemode/test/array-callbacks-test262.test.ts index 3de3a94a4c..c34137abd0 100644 --- a/packages/codemode/test/array-callbacks-test262.test.ts +++ b/packages/codemode/test/array-callbacks-test262.test.ts @@ -26,9 +26,11 @@ * - test/built-ins/Array/prototype/forEach/15.4.4.18-7-1.js * - test/built-ins/Array/prototype/forEach/15.4.4.18-7-2.js * - test/built-ins/Array/prototype/reduce/15.4.4.21-9-5.js + * - test/built-ins/Array/prototype/reduce/15.4.4.21-9-c-ii-20.js * - test/built-ins/Array/prototype/reduce/15.4.4.21-9-1.js * - test/built-ins/Array/prototype/reduce/15.4.4.21-10-1.js * - test/built-ins/Array/prototype/reduceRight/15.4.4.22-9-5.js + * - test/built-ins/Array/prototype/reduceRight/15.4.4.22-9-c-ii-20.js * - test/built-ins/Array/prototype/reduceRight/15.4.4.22-9-1.js * - test/built-ins/Array/prototype/reduceRight/15.4.4.22-10-1.js * - test/built-ins/Array/prototype/flatMap/depth-always-one.js @@ -210,6 +212,11 @@ const cases = [ code: `let calls = 0; const result = [1].reduce(() => { calls += 1; return 2 }); return [result, calls]`, expected: [1, 0], }, + { + path: "test/built-ins/Array/prototype/reduce/15.4.4.21-9-c-ii-20.js", + code: `let accessed = false; const result = [11].reduce((previous) => { accessed = true; return previous === undefined }, undefined); return [result, accessed]`, + expected: [true, true], + }, { path: "test/built-ins/Array/prototype/reduce/15.4.4.21-10-1.js", code: `const input = [1, 2, 3, 4, 5]; input.reduce(() => 1); return input`, @@ -225,6 +232,11 @@ const cases = [ code: `let calls = 0; const result = [1].reduceRight(() => { calls += 1; return 2 }); return [result, calls]`, expected: [1, 0], }, + { + path: "test/built-ins/Array/prototype/reduceRight/15.4.4.22-9-c-ii-20.js", + code: `let accessed = false; const result = [11].reduceRight((previous) => { accessed = true; return previous === undefined }, undefined); return [result, accessed]`, + expected: [true, true], + }, { path: "test/built-ins/Array/prototype/reduceRight/15.4.4.22-10-1.js", code: `const input = [1, 2, 3, 4, 5]; input.reduceRight(() => 1); return input`, @@ -323,3 +335,46 @@ describe("Test262 Array callback adaptations", () => { }) } }) + +describe("Array callback regressions", () => { + test("reduce and reduceRight find the first present element", async () => { + expect( + await value(` + const left = [] + left[2] = 3 + const right = [] + right[0] = 4 + right[3] = 1 + right.pop() + return [left.reduce((a, b) => a + b), right.reduceRight((a, b) => a + b)] + `), + ).toEqual([3, 4]) + }) + + test("reduce and reduceRight reject arrays containing only holes", async () => { + expect( + await value(` + const values = [] + values[2] = 1 + values.pop() + let left + let right + try { values.reduce((a, b) => a + b) } catch (error) { left = error.name } + try { values.reduceRight((a, b) => a + b) } catch (error) { right = error.name } + return [left, right] + `), + ).toEqual(["TypeError", "TypeError"]) + }) + + test("findLast returns the value observed before predicate mutation", async () => { + expect( + await value(` + const values = [1] + return values.findLast((item, index, array) => { + array[index] = 2 + return true + }) + `), + ).toBe(1) + }) +}) diff --git a/packages/codemode/test/promise.test.ts b/packages/codemode/test/promise.test.ts index 5ce4d003d5..db607336de 100644 --- a/packages/codemode/test/promise.test.ts +++ b/packages/codemode/test/promise.test.ts @@ -560,7 +560,7 @@ describe("Promise.all over arbitrary arrays", () => { expect(trace.maxActive).toBeGreaterThan(1) }) - test("caps live tool-call concurrency at the fixed internal constant (8)", async () => { + test("does not cap live tool-call concurrency", async () => { const trace = makeTrace() const result = await value( ` @@ -572,8 +572,7 @@ describe("Promise.all over arbitrary arrays", () => { { trace }, ) expect(result).toBe(20) - expect(trace.maxActive).toBeGreaterThan(1) - expect(trace.maxActive).toBeLessThanOrEqual(8) + expect(trace.maxActive).toBe(20) }) test("resolves the empty array", async () => { diff --git a/packages/core/src/config.ts b/packages/core/src/config.ts index e12f7848d6..55a1641f1c 100644 --- a/packages/core/src/config.ts +++ b/packages/core/src/config.ts @@ -200,7 +200,6 @@ const layer = Layer.effect( .up({ targets: [".opencode", ".claude", ".agents", ...names.toReversed()], start: location.directory, - stop: location.project.directory, }) .pipe(Effect.orDie) diff --git a/packages/core/src/flag/flag.ts b/packages/core/src/flag/flag.ts index b33c20c1ab..1c975040dc 100644 --- a/packages/core/src/flag/flag.ts +++ b/packages/core/src/flag/flag.ts @@ -52,9 +52,6 @@ export const Flag = { get OPENCODE_EXPERIMENTAL_REFERENCES() { return enabledByExperimental("OPENCODE_EXPERIMENTAL_REFERENCES") }, - get CODEMODE_ENABLED() { - return process.env["CODEMODE_ENABLED"] === undefined || truthy("CODEMODE_ENABLED") - }, get OPENCODE_TUI_CONFIG() { return process.env["OPENCODE_TUI_CONFIG"] }, diff --git a/packages/core/src/mcp/guidance.ts b/packages/core/src/mcp/guidance.ts index 4be0d412f5..2e7f34b047 100644 --- a/packages/core/src/mcp/guidance.ts +++ b/packages/core/src/mcp/guidance.ts @@ -1,7 +1,6 @@ export * as McpGuidance from "./guidance" import { makeLocationNode } from "../effect/app-node" -import { Flag } from "../flag/flag" import { Context, Effect, Layer, Schema } from "effect" import { AgentV2 } from "../agent" import { PermissionV2 } from "../permission" @@ -18,11 +17,7 @@ type Summary = typeof Summary.Type const entries = (servers: ReadonlyArray) => servers.flatMap((server) => [ ` `, - ...(Flag.CODEMODE_ENABLED - ? [ - ` Use tools from this server through \`execute\` under \`tools[${JSON.stringify(McpTool.group(server.server))}]\`.`, - ] - : []), + ` Use tools from this server through \`execute\` under \`tools[${JSON.stringify(McpTool.group(server.server))}]\`.`, ...server.instructions.split("\n").map((line) => ` ${line}`), " ", ]) @@ -81,7 +76,7 @@ export const layer = Layer.effect( removed: () => "MCP server instructions are no longer available.", }, }) - if (Flag.CODEMODE_ENABLED && PermissionV2.evaluate("execute", "*", agent.permissions).effect === "deny") + if (PermissionV2.evaluate("execute", "*", agent.permissions).effect === "deny") return source(Instructions.removed) const [instructions, tools] = yield* Effect.all([mcp.instructions(), mcp.tools()], { concurrency: "unbounded", @@ -90,12 +85,9 @@ export const layer = Layer.effect( const visible = instructions .filter((item) => { const owned = tools.filter((tool) => tool.server === item.server) - return ( - (!Flag.CODEMODE_ENABLED && owned.length === 0) || - owned.some( - (tool) => - PermissionV2.evaluate(McpTool.name(tool.server, tool.name), "*", agent.permissions).effect !== "deny", - ) + return owned.some( + (tool) => + PermissionV2.evaluate(McpTool.name(tool.server, tool.name), "*", agent.permissions).effect !== "deny", ) }) .map((item) => ({ server: item.server, instructions: item.instructions })) diff --git a/packages/core/src/session/runner/llm.ts b/packages/core/src/session/runner/llm.ts index 932470d869..d18351b53e 100644 --- a/packages/core/src/session/runner/llm.ts +++ b/packages/core/src/session/runner/llm.ts @@ -543,7 +543,8 @@ const layer = Layer.effect( needsContinuation = result.needsContinuation step = result.step + 1 if (needsContinuation) { - promotion = (yield* SessionPending.compaction(db, input.sessionID)) ? undefined : "steer" + yield* runPendingCompaction(input.sessionID) + promotion = "steer" continue } yield* runPendingCompaction(input.sessionID) diff --git a/packages/core/src/tool/AGENTS.md b/packages/core/src/tool/AGENTS.md index 9bfd05f1a4..08c9fabefe 100644 --- a/packages/core/src/tool/AGENTS.md +++ b/packages/core/src/tool/AGENTS.md @@ -29,7 +29,8 @@ Leaves own resolution, permission, and side-effect ordering. Translate only expe ## Registration Built-ins and plugin tools register through `Tools.Service.register({ [name]: tool })`. Registrations may provide a -group, which flattens direct model names to `_`, and may be deferred from direct model exposure. +group, which flattens direct model names to `_`, and default into CodeMode (`codemode` defaults true; +`codemode: false` keeps the tool on the provider's native tool list). Registrations are scoped: diff --git a/packages/core/src/tool/edit.ts b/packages/core/src/tool/edit.ts index 927cd3098d..436a60e7ce 100644 --- a/packages/core/src/tool/edit.ts +++ b/packages/core/src/tool/edit.ts @@ -212,6 +212,7 @@ export const Plugin = { }), "edit", ), + { codemode: false }, ), ) .pipe(Effect.orDie) diff --git a/packages/core/src/tool/execute.ts b/packages/core/src/tool/execute.ts index 826b1ca4fa..89672dbfc6 100644 --- a/packages/core/src/tool/execute.ts +++ b/packages/core/src/tool/execute.ts @@ -58,16 +58,16 @@ export const create = (registrations: ReadonlyMap) => { }) if (registration.group === undefined) { const path = registration.name - if (Object.hasOwn(tools, path)) throw new TypeError(`Deferred tool namespace conflict: ${path}`) + if (Object.hasOwn(tools, path)) throw new TypeError(`CodeMode tool namespace conflict: ${path}`) tools[path] = value continue } const path = registration.name const namespace = registration.group const group = tools[namespace] - if (group && Tool.isDefinition(group)) throw new TypeError(`Deferred tool namespace conflict: ${namespace}`) + if (group && Tool.isDefinition(group)) throw new TypeError(`CodeMode tool namespace conflict: ${namespace}`) if (group) { - if (Object.hasOwn(group, path)) throw new TypeError(`Deferred tool namespace conflict: ${namespace}.${path}`) + if (Object.hasOwn(group, path)) throw new TypeError(`CodeMode tool namespace conflict: ${namespace}.${path}`) group[path] = value continue } diff --git a/packages/core/src/tool/glob.ts b/packages/core/src/tool/glob.ts index b047101c39..3cde812aaa 100644 --- a/packages/core/src/tool/glob.ts +++ b/packages/core/src/tool/glob.ts @@ -106,6 +106,7 @@ export const Plugin = { ), ), }), + { codemode: false }, ), ) .pipe(Effect.orDie) diff --git a/packages/core/src/tool/grep.ts b/packages/core/src/tool/grep.ts index 937963f4ca..cdf61866c6 100644 --- a/packages/core/src/tool/grep.ts +++ b/packages/core/src/tool/grep.ts @@ -137,6 +137,7 @@ export const Plugin = { ), ), }), + { codemode: false }, ), ) .pipe(Effect.orDie) diff --git a/packages/core/src/tool/mcp.ts b/packages/core/src/tool/mcp.ts index 7503e4e94e..8aad746ed6 100644 --- a/packages/core/src/tool/mcp.ts +++ b/packages/core/src/tool/mcp.ts @@ -5,7 +5,7 @@ import { McpEvent } from "@opencode-ai/schema/mcp-event" import { Effect, Exit, type JsonSchema, Layer, Scope, Semaphore, Stream } from "effect" import { makeLocationNode } from "../effect/app-node" import { EventV2 } from "../event" -import { Flag } from "../flag/flag" + import { MCP } from "../mcp" import { PermissionV2 } from "../permission" import { Tool } from "./tool" @@ -107,7 +107,7 @@ export const layer = Layer.effectDiscard( const next = yield* Scope.fork(scope) yield* Effect.forEach( groups, - ([group, record]) => tools.register(record, { group, deferred: Flag.CODEMODE_ENABLED }), + ([group, record]) => tools.register(record, { group }), { discard: true, }, diff --git a/packages/core/src/tool/patch.ts b/packages/core/src/tool/patch.ts index 843f4df8c3..4d7c3030b9 100644 --- a/packages/core/src/tool/patch.ts +++ b/packages/core/src/tool/patch.ts @@ -191,6 +191,7 @@ export const Plugin = { }), "edit", ), + { codemode: false }, ), ) .pipe(Effect.orDie) diff --git a/packages/core/src/tool/question.ts b/packages/core/src/tool/question.ts index ec2b1938a0..41c9d2dd50 100644 --- a/packages/core/src/tool/question.ts +++ b/packages/core/src/tool/question.ts @@ -106,6 +106,7 @@ export const Plugin = { }), ), }), + { codemode: false }, ), ) .pipe(Effect.orDie) diff --git a/packages/core/src/tool/read.ts b/packages/core/src/tool/read.ts index b71680142b..aa2e81b564 100644 --- a/packages/core/src/tool/read.ts +++ b/packages/core/src/tool/read.ts @@ -139,6 +139,7 @@ export const Plugin = { ) }, }), + { codemode: false }, ), ) .pipe(Effect.orDie) diff --git a/packages/core/src/tool/registry.ts b/packages/core/src/tool/registry.ts index 0429d7ba44..b1087c7822 100644 --- a/packages/core/src/tool/registry.ts +++ b/packages/core/src/tool/registry.ts @@ -3,7 +3,6 @@ export * as ToolRegistry from "./registry" import { ToolOutput, type ToolCall, type ToolDefinition, type ToolResultValue } from "@opencode-ai/llm" import { Context, Effect, Layer, Scope } from "effect" import type { AgentV2 } from "../agent" -import { Flag } from "../flag/flag" import { PermissionV2 } from "../permission" import { SessionMessage } from "../session/message" import { SessionSchema } from "../session/schema" @@ -56,7 +55,7 @@ const registryLayer = Layer.effect( readonly tool: AnyTool readonly name: string readonly group?: string - readonly deferred: boolean + readonly codemode: boolean } const local = new Map>() @@ -132,7 +131,8 @@ const registryLayer = Layer.effect( register: Effect.fn("ToolRegistry.register")(function* (tools, options) { const entries = registrationEntries(tools, options?.group) if (entries.length === 0) return - const reserved = options?.deferred ? undefined : entries.find((entry) => entry.key === "execute") + const codemode = options?.codemode ?? true + const reserved = codemode ? undefined : entries.find((entry) => entry.key === "execute") if (reserved) return yield* Effect.fail( new RegistrationError({ name: reserved.key, message: 'Tool name "execute" is reserved for CodeMode' }), @@ -149,7 +149,7 @@ const registryLayer = Layer.effect( tool: entry.tool, name: entry.name, group: entry.group, - deferred: options?.deferred ?? false, + codemode, }, }, ]) @@ -168,18 +168,17 @@ const registryLayer = Layer.effect( }), materialize: Effect.fn("ToolRegistry.materialize")(function* (permissions) { const direct = new Map() - const deferred = new Map() + const codemode = new Map() const rules = permissions ?? [] for (const [name, entries] of local) { const registration = entries.at(-1)?.registration if (!registration) continue - if (registration.deferred && !Flag.CODEMODE_ENABLED) continue if (whollyDisabled(permission(registration.tool, name), rules)) continue - if (registration.deferred) deferred.set(name, registration) + if (registration.codemode) codemode.set(name, registration) else direct.set(name, registration) } const execute = - deferred.size > 0 && !whollyDisabled("execute", rules) ? ExecuteTool.create(deferred) : undefined + codemode.size > 0 && !whollyDisabled("execute", rules) ? ExecuteTool.create(codemode) : undefined return { definitions: [ ...Array.from(direct, ([name, registration]) => definition(name, registration.tool)), diff --git a/packages/core/src/tool/shell.ts b/packages/core/src/tool/shell.ts index d2b8bb4744..34583387d7 100644 --- a/packages/core/src/tool/shell.ts +++ b/packages/core/src/tool/shell.ts @@ -132,6 +132,8 @@ export const Plugin = { return runtime.session.synthetic({ sessionID, text: `\n${text}\n`, + description: command, + metadata: { source: "shell", state }, }) }), Effect.forkIn(scope, { startImmediately: true }), @@ -276,6 +278,7 @@ export const Plugin = { ), ), }), + { codemode: false }, ), ) .pipe(Effect.orDie) diff --git a/packages/core/src/tool/skill.ts b/packages/core/src/tool/skill.ts index 1f80bd9684..2589fd08ad 100644 --- a/packages/core/src/tool/skill.ts +++ b/packages/core/src/tool/skill.ts @@ -97,6 +97,7 @@ export const Plugin = { }).pipe(Effect.mapError((error) => unableToLoad(input.id, error))) }), }), + { codemode: false }, ), ) .pipe(Effect.orDie) diff --git a/packages/core/src/tool/subagent.ts b/packages/core/src/tool/subagent.ts index 9b7015935a..6fcda996dc 100644 --- a/packages/core/src/tool/subagent.ts +++ b/packages/core/src/tool/subagent.ts @@ -208,6 +208,7 @@ export const Plugin = { return { sessionID: child.id, status: "completed" as const, output: result?.info.output ?? NO_TEXT } }), }), + { codemode: false }, ), ) .pipe(Effect.orDie) diff --git a/packages/core/src/tool/webfetch.ts b/packages/core/src/tool/webfetch.ts index d761645939..3b900e1784 100644 --- a/packages/core/src/tool/webfetch.ts +++ b/packages/core/src/tool/webfetch.ts @@ -174,6 +174,7 @@ export const Plugin = { } }).pipe(Effect.mapError((error) => new ToolFailure({ message: `Unable to fetch ${input.url}`, error }))), }), + { codemode: false }, ), ) .pipe(Effect.orDie) diff --git a/packages/core/src/tool/websearch.ts b/packages/core/src/tool/websearch.ts index 9c91d84f92..ab65c33a4c 100644 --- a/packages/core/src/tool/websearch.ts +++ b/packages/core/src/tool/websearch.ts @@ -252,6 +252,7 @@ export const Plugin = { ) }, }), + { codemode: false }, ), ) .pipe(Effect.orDie) diff --git a/packages/core/src/tool/write.ts b/packages/core/src/tool/write.ts index a88063f4e2..d7cd8c54f4 100644 --- a/packages/core/src/tool/write.ts +++ b/packages/core/src/tool/write.ts @@ -91,6 +91,7 @@ export const Plugin = { }), "edit", ), + { codemode: false }, ), ) .pipe(Effect.orDie) diff --git a/packages/core/test/config/config.test.ts b/packages/core/test/config/config.test.ts index e6c0a6b915..94c84c3b02 100644 --- a/packages/core/test/config/config.test.ts +++ b/packages/core/test/config/config.test.ts @@ -876,7 +876,7 @@ describe("Config", () => { ), ) - it.live("loads global, ancestor, and .opencode configuration up to the project boundary", () => + it.live("loads global and ancestor configuration across the project boundary", () => Effect.acquireRelease( Effect.promise(() => tmpdir()), (tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()), @@ -936,6 +936,7 @@ describe("Config", () => { ]) expect(documents.map((document) => document.info.$schema)).toEqual([ "global", + "outside", "root", "parent", "directory", @@ -951,6 +952,8 @@ describe("Config", () => { AbsolutePath.make(path.join(root, ".agents")), "global", AbsolutePath.make(global), + "outside", + AbsolutePath.make(path.join(tmp.path, "opencode.json")), "root", AbsolutePath.make(path.join(root, "opencode.json")), "parent", diff --git a/packages/core/test/plugin.test.ts b/packages/core/test/plugin.test.ts index 8c2db479e5..6d9e0aac3d 100644 --- a/packages/core/test/plugin.test.ts +++ b/packages/core/test/plugin.test.ts @@ -260,6 +260,7 @@ describe("PluginV2", () => { output: Schema.Struct({ ok: Schema.Boolean }), execute: () => Effect.succeed({ ok: true }), }), + { codemode: false }, ), ) .pipe(Effect.orDie), @@ -273,7 +274,7 @@ describe("PluginV2", () => { }), ) - it.effect("groups tool names and defers registrations from direct exposure", () => + it.effect("groups tool names and routes codemode registrations through execute", () => Effect.gen(function* () { const plugins = yield* PluginV2.Service const registry = yield* ToolRegistry.Service @@ -289,9 +290,9 @@ describe("PluginV2", () => { effect: (ctx) => ctx.tool .transform((draft) => { - draft.add("plain", tool("Plain")) - draft.add("look/up", tool("Lookup"), { group: "context 7" }) - draft.add("search", tool("Search"), { group: "context 7", deferred: true }) + draft.add("plain", tool("Plain"), { codemode: false }) + draft.add("look/up", tool("Lookup"), { group: "context 7", codemode: false }) + draft.add("search", tool("Search"), { group: "context 7" }) }) .pipe(Effect.orDie), }) @@ -330,6 +331,7 @@ describe("PluginV2", () => { output: Schema.Struct({ text: Schema.String }), execute: ({ text }) => Effect.sync(() => executed.push({ text })).pipe(Effect.as({ text })), }), + { codemode: false }, ), ) .pipe(Effect.orDie) diff --git a/packages/core/test/plugin/promise.test.ts b/packages/core/test/plugin/promise.test.ts index 433341d398..afe72f6b59 100644 --- a/packages/core/test/plugin/promise.test.ts +++ b/packages/core/test/plugin/promise.test.ts @@ -135,6 +135,7 @@ describe("fromPromise", () => { await ctx.tool.transform((tools) => { tools.add({ name: "hello", + options: { codemode: false }, description: "Hello", input: Schema.Struct({ name: Schema.String }), output: Schema.String, diff --git a/packages/core/test/session-runner-tool-registry.test.ts b/packages/core/test/session-runner-tool-registry.test.ts index 8c14b2aebf..b7351cd900 100644 --- a/packages/core/test/session-runner-tool-registry.test.ts +++ b/packages/core/test/session-runner-tool-registry.test.ts @@ -70,7 +70,7 @@ describe("ToolRegistry", () => { bash: make(), edit: make("edit"), write: make("edit"), - }) + }, { codemode: false }) const names = (permissions: PermissionV2.Ruleset) => toolDefinitions(service, permissions).pipe(Effect.map((definitions) => definitions.map((tool) => tool.name))) @@ -95,8 +95,8 @@ describe("ToolRegistry", () => { Effect.gen(function* () { const service = yield* ToolRegistry.Service const shared = make() - yield* service.register({ first: shared }) - yield* service.register({ second: Tool.withPermission(shared, "edit") }) + yield* service.register({ first: shared }, { codemode: false }) + yield* service.register({ second: Tool.withPermission(shared, "edit") }, { codemode: false }) Tool.withPermission(shared, "question") expect( @@ -110,7 +110,7 @@ describe("ToolRegistry", () => { it.effect("reuses model definitions across requests", () => Effect.gen(function* () { const service = yield* ToolRegistry.Service - yield* service.register({ echo: make() }) + yield* service.register({ echo: make() }, { codemode: false }) const first = yield* toolDefinitions(service) const second = yield* toolDefinitions(service) @@ -122,7 +122,7 @@ describe("ToolRegistry", () => { Effect.gen(function* () { const service = yield* ToolRegistry.Service const scope = yield* Scope.make() - yield* service.register({ echo: make() }).pipe(Scope.provide(scope)) + yield* service.register({ echo: make() }, { codemode: false }).pipe(Scope.provide(scope)) expect((yield* toolDefinitions(service)).map((tool) => tool.name)).toEqual(["echo"]) yield* Scope.close(scope, Exit.void) expect(yield* toolDefinitions(service)).toEqual([]) @@ -135,7 +135,7 @@ describe("ToolRegistry", () => { const scope = yield* Scope.make() const registered = yield* Deferred.make() const fiber = yield* service - .register({ echo: make() }) + .register({ echo: make() }, { codemode: false }) .pipe( Effect.andThen(Deferred.succeed(registered, undefined)), Effect.andThen(Effect.never), @@ -161,7 +161,7 @@ describe("ToolRegistry", () => { output: Schema.Struct({ ok: Schema.Boolean }), execute: () => Effect.fail(new Tool.Failure({ message: "Denied" })), }), - }) + }, { codemode: false }) expect( yield* executeTool(service, { sessionID, @@ -184,7 +184,7 @@ describe("ToolRegistry", () => { output: Schema.Struct({}), execute: () => Effect.die("unexpected executor defect"), }), - }) + }, { codemode: false }) expect( yield* service.materialize().pipe( Effect.flatMap((materialized) => @@ -203,7 +203,7 @@ describe("ToolRegistry", () => { it.effect("propagates retention failures through settlement", () => Effect.gen(function* () { const service = yield* ToolRegistry.Service - yield* service.register({ echo: make() }) + yield* service.register({ echo: make() }, { codemode: false }) const materialized = yield* service.materialize() const exit = yield* materialized.settle(call("echo", "call-retention-failure")).pipe(Effect.exit) @@ -234,7 +234,7 @@ describe("ToolRegistry", () => { output: Schema.Struct({ ok: Schema.Boolean }), execute: (_, context) => Effect.sync(() => contexts.push(context)).pipe(Effect.as({ ok: true })), }), - }) + }, { codemode: false }) yield* executeTool(service, { sessionID, ...identity, @@ -248,7 +248,7 @@ describe("ToolRegistry", () => { Effect.gen(function* () { bounds.length = 0 const service = yield* ToolRegistry.Service - yield* service.register({ bounded: make() }) + yield* service.register({ bounded: make() }, { codemode: false }) expect( yield* settleTool(service, { sessionID, @@ -282,7 +282,7 @@ describe("ToolRegistry", () => { execute: ({ value }) => Effect.sync(() => executed.push(value)).pipe(Effect.as({ value })), toModelOutput: ({ output }) => [{ type: "text", text: String(output.value) }], }), - }) + }, { codemode: false }) expect( yield* executeTool(service, { @@ -319,7 +319,7 @@ describe("ToolRegistry", () => { }), execute: () => Effect.succeed({ value: "invalid" }), }), - }) + }, { codemode: false }) expect( yield* executeTool(service, { sessionID, @@ -334,10 +334,10 @@ describe("ToolRegistry", () => { Effect.gen(function* () { const service = yield* ToolRegistry.Service const scope = yield* Scope.make() - yield* service.register({ echo: constant("advertised") }).pipe(Scope.provide(scope)) + yield* service.register({ echo: constant("advertised") }, { codemode: false }).pipe(Scope.provide(scope)) const request = yield* service.materialize() yield* Scope.close(scope, Exit.void) - yield* service.register({ echo: constant("replacement") }) + yield* service.register({ echo: constant("replacement") }, { codemode: false }) expect((yield* request.settle(call("echo"))).result).toEqual({ type: "text", value: "advertised" }) expect(yield* executeTool(service, call("echo"))).toEqual({ type: "text", value: "replacement" }) @@ -347,9 +347,9 @@ describe("ToolRegistry", () => { it.effect("reveals the previous registration after an overlay closes", () => Effect.gen(function* () { const service = yield* ToolRegistry.Service - yield* service.register({ echo: constant("base") }) + yield* service.register({ echo: constant("base") }, { codemode: false }) const overlay = yield* Scope.make() - yield* service.register({ echo: constant("overlay") }).pipe(Scope.provide(overlay)) + yield* service.register({ echo: constant("overlay") }, { codemode: false }).pipe(Scope.provide(overlay)) expect(yield* executeTool(service, call("echo"))).toEqual({ type: "text", value: "overlay" }) yield* Scope.close(overlay, Exit.void) @@ -357,37 +357,31 @@ describe("ToolRegistry", () => { }), ) - it.effect("executes deferred tools advertised in a model request", () => + it.effect("executes codemode tools advertised in a model request", () => Effect.gen(function* () { const service = yield* ToolRegistry.Service const executed: string[] = [] const scope = yield* Scope.make() yield* service - .register( - { - echo: Tool.make({ - description: "Echo text", - input: Schema.Struct({ text: Schema.String }), - output: Schema.Struct({ text: Schema.String }), - execute: ({ text }) => Effect.sync(() => executed.push(`old:${text}`)).pipe(Effect.as({ text })), - }), - }, - { deferred: true }, - ) - .pipe(Scope.provide(scope)) - const materialized = yield* service.materialize() - yield* Scope.close(scope, Exit.void) - yield* service.register( - { + .register({ echo: Tool.make({ description: "Echo text", input: Schema.Struct({ text: Schema.String }), output: Schema.Struct({ text: Schema.String }), - execute: ({ text }) => Effect.sync(() => executed.push(`new:${text}`)).pipe(Effect.as({ text })), + execute: ({ text }) => Effect.sync(() => executed.push(`old:${text}`)).pipe(Effect.as({ text })), }), - }, - { deferred: true }, - ) + }) + .pipe(Scope.provide(scope)) + const materialized = yield* service.materialize() + yield* Scope.close(scope, Exit.void) + yield* service.register({ + echo: Tool.make({ + description: "Echo text", + input: Schema.Struct({ text: Schema.String }), + output: Schema.Struct({ text: Schema.String }), + execute: ({ text }) => Effect.sync(() => executed.push(`new:${text}`)).pipe(Effect.as({ text })), + }), + }) const settlement = yield* materialized.settle({ ...call("execute"), diff --git a/packages/core/test/session-runner.test.ts b/packages/core/test/session-runner.test.ts index 6b7316c6ed..3bbd2560f0 100644 --- a/packages/core/test/session-runner.test.ts +++ b/packages/core/test/session-runner.test.ts @@ -261,7 +261,7 @@ const echo = Layer.effectDiscard( output: Schema.Any, execute: () => Effect.succeed({ big: 1n }), }), - }), + }, { codemode: false }), ), ) const echoNode = makeLocationNode({ name: "test/session-runner-tools", layer: echo, deps: [ToolRegistry.node] }) @@ -782,7 +782,7 @@ describe("SessionRunnerLLM", () => { return { answer: query.toUpperCase() } }), }), - }) + }, { codemode: false }) yield* admit(session, "Use application context") responses = [reply.tool("call-location", "location_context", { query: "hello" }), []] @@ -827,7 +827,7 @@ describe("SessionRunnerLLM", () => { output: Schema.Struct({ value: Schema.String }), execute: () => Effect.sync(() => executions.push("advertised")).pipe(Effect.as({ value: "advertised" })), }), - }) + }, { codemode: false }) .pipe(Scope.provide(scope)) yield* admit(session, "Use the reloaded tool") responses = [ @@ -852,7 +852,7 @@ describe("SessionRunnerLLM", () => { output: Schema.Struct({ value: Schema.String }), execute: () => Effect.sync(() => executions.push("replacement")).pipe(Effect.as({ value: "replacement" })), }), - }) + }, { codemode: false }) yield* Deferred.succeed(streamGate, undefined) yield* Fiber.join(run) @@ -1614,14 +1614,14 @@ describe("SessionRunnerLLM", () => { }), ) - it.effect("runs one durable compaction barrier before later steer and queued prompts", () => + it.effect("runs one durable compaction barrier after tool settlement and before later inputs", () => Effect.gen(function* () { const session = yield* setup currentModel = recoveryModel streamGate = yield* Deferred.make() streamStarted = yield* Deferred.make() responses = [ - reply.text("Active complete", "text-active"), + reply.tool("call-active", "echo", { text: "active" }), [LLMEvent.textDelta({ id: "summary", text: "durable summary" })], reply.text("Steer complete", "text-steer"), reply.text("Queue complete", "text-queue"), @@ -3189,7 +3189,7 @@ describe("SessionRunnerLLM", () => { Effect.mapError(() => new Tool.Failure({ message: "Permission blocked" })), ), }), - }) + }, { codemode: false }) yield* admit(session, "Call blocked") responses = [reply.tool("call-blocked", "blocked", {}), reply.stop()] @@ -3221,7 +3221,7 @@ describe("SessionRunnerLLM", () => { output: Schema.Struct({}), execute: () => Effect.die(new PermissionV2.DeclinedError()), }), - }) + }, { codemode: false }) yield* admit(session, "Call declined") response = reply.tool("call-declined", "declined", {}) @@ -3261,7 +3261,7 @@ describe("SessionRunnerLLM", () => { Effect.mapError(() => new Tool.Failure({ message: "Use another tool" })), ), }), - }) + }, { codemode: false }) yield* admit(session, "Call corrected") responses = [reply.tool("call-corrected", "corrected", {}), reply.stop()] @@ -3321,7 +3321,7 @@ describe("SessionRunnerLLM", () => { Effect.gen(function* () { const session = yield* setup const registry = yield* ToolRegistry.Service - yield* registry.register({ permissionfail: permissionFail }) + yield* registry.register({ permissionfail: permissionFail }, { codemode: false }) yield* admit(session, "Reject permission") responses = [ reply.tool("call-permission", "permissionfail", {}), @@ -3366,7 +3366,7 @@ describe("SessionRunnerLLM", () => { output: Schema.Struct({}), execute: () => Effect.die(new QuestionTool.CancelledError()), }), - }) + }, { codemode: false }) yield* admit(session, "Ask then stop") responses = [reply.tool("call-question", "question", {}), []] diff --git a/packages/core/test/tool-shell.test.ts b/packages/core/test/tool-shell.test.ts index 6a6a53e171..896cc96d03 100644 --- a/packages/core/test/tool-shell.test.ts +++ b/packages/core/test/tool-shell.test.ts @@ -2,7 +2,7 @@ import fs from "fs/promises" import { realpathSync } from "node:fs" import path from "path" import { describe, expect, test } from "bun:test" -import { DateTime, Duration, Effect, Fiber, Layer, Scope } from "effect" +import { DateTime, Duration, Effect, Fiber, Layer, Scope, Stream } from "effect" import { Money } from "@opencode-ai/schema/money" import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" import { LayerNode } from "@opencode-ai/core/effect/layer-node" @@ -443,6 +443,12 @@ describe("ShellTool", () => { reset() return withSession(tmp.path, (registry) => Effect.gen(function* () { + const events = yield* EventV2.Service + const admitted = yield* events.subscribe(SessionEvent.InputAdmitted).pipe( + Stream.filter((event) => event.data.sessionID === sessionID && event.data.input.type === "synthetic"), + Stream.runHead, + Effect.forkScoped({ startImmediately: true }), + ) const settled = yield* settleTool(registry, call({ command: idleCommand, timeout: 50, background: true })) const structured = settled.output?.structured as Record | undefined const shellID = typeof structured?.shellID === "string" ? structured.shellID : undefined @@ -454,6 +460,13 @@ describe("ShellTool", () => { const id = ShellSchema.ID.make(shellID) expect((yield* shell.list()).map((info) => info.id)).toContain(id) expect((yield* shell.wait(id)).status).toBe("timeout") + expect((yield* Fiber.join(admitted)).valueOrUndefined?.data.input.data).toMatchObject({ + description: idleCommand, + metadata: { + source: "shell", + state: "completed", + }, + }) }), ) }, diff --git a/packages/docs/build/plugins.mdx b/packages/docs/build/plugins.mdx index 08c8ce4dec..ad9cff8a3e 100644 --- a/packages/docs/build/plugins.mdx +++ b/packages/docs/build/plugins.mdx @@ -315,11 +315,12 @@ export default Plugin.define({ Unsupported characters in tool and group names are normalized to underscores. The resulting exposed key must begin with a letter and contain at most 64 letters, digits, underscores, or hyphens. Set `options` on the declaration to -configure registration with `{ group, deferred }`: +configure registration with `{ group, codemode }`: - `group` prefixes and groups the exposed tool name. -- `deferred: true` makes the tool available through the deferred `execute` - tool instead of exposing it directly. +- `codemode` defaults to `true` and makes the tool available through the + `execute` CodeMode tool. Set `codemode: false` to expose it directly to the + provider. The executor receives a second context argument containing `sessionID`, `agent`, `assistantMessageID`, and `toolCallID`. diff --git a/packages/docs/build/sdk.mdx b/packages/docs/build/sdk.mdx index b72aac9c09..afc29dca0b 100644 --- a/packages/docs/build/sdk.mdx +++ b/packages/docs/build/sdk.mdx @@ -1,12 +1,21 @@ --- title: "SDK" -description: "Embed an OpenCode host in an Effect application." +description: "Embed OpenCode directly in your application." --- -`@opencode-ai/sdk-next` is the Effect-native SDK for applications that need to -host OpenCode in-process. Unlike the [network client](/build/client), it assembles the -OpenCode server and routes API calls through its HTTP router in memory. It opens -no HTTP listener and adds no network hop between the client and server. +We're working on a general-purpose SDK for embedding OpenCode directly inside +your application. The regular SDK is coming soon. + +An Effect-native version is available now for applications built with Effect. +Its current documentation is below. For other applications, run OpenCode as a +server and use the [TypeScript client](/build/client) in the meantime. + +## Effect + +`@opencode-ai/sdk-next` hosts OpenCode in-process. Unlike the +[network client](/build/client), it assembles the OpenCode server and routes API +calls through its HTTP router in memory. It opens no HTTP listener and adds no +network hop between the client and server. The V2 SDK is beta and currently private to the OpenCode workspace. It is not diff --git a/packages/docs/migrate-v1.mdx b/packages/docs/migrate-v1.mdx index 328a1dcaa0..aa90e7d7f8 100644 --- a/packages/docs/migrate-v1.mdx +++ b/packages/docs/migrate-v1.mdx @@ -3,14 +3,19 @@ title: "Migrate from V1" description: "Move from OpenCode V1 to the OpenCode 2.0 beta." --- - - The only intentional breaking changes in V2 are the server API and the plugin API. All other functionality is intended - to remain compatible with V1. - +## Breaking changes -Existing config files, agent definitions, command definitions, skills, and other files in `.opencode/` should continue to -work without changes. If one of these stops working in V2, treat it as a beta compatibility bug rather than an expected -migration requirement. +V2 has three intentional breaking changes: + +- [Plugins](#plugins) use a new plugin API. +- The [server API and clients](#server-api-and-clients) have new contracts. +- [TUI configuration](#tui-configuration) moves from layered `tui.json(c)` files to one global `cli.json` file (auto migrated). + +All other functionality is intended to remain compatible with V1. + +Existing server config files, agent definitions, command definitions, skills, and other files in `.opencode/` should +continue to work without changes. If one of these stops working in V2, treat it as a beta compatibility bug rather than +an expected migration requirement. Run `/report` if existing V1 functionality does not work in V2. The report skill collects diagnostics and helps you file @@ -466,6 +471,58 @@ If a V1 setup relied on a `CLAUDE.md` fallback, move that guidance into the appl discovers `AGENTS.md`; because non-API V1 behavior is intended to remain compatible, also run `/report` with the affected project details. See [Instructions](/instructions). +## TUI configuration + +V1 loaded `tui.json(c)` from the global config directory and from project directories discovered while walking up from +the current directory. V2 instead stores CLI and TUI settings in one global file: + +```text +~/.config/opencode/cli.json +``` + +The CLI owns this file. The background service does not load it, and V2 does not discover or merge project-local +`tui.json(c)` or `cli.json` files. + +The native V2 format groups related settings. For example: + +```jsonc +// V1: ~/.config/opencode/tui.json +{ + "theme": "tokyonight", + "scroll_speed": 2, + "scroll_acceleration": { + "enabled": true + } +} + +// V2: ~/.config/opencode/cli.json +{ + "theme": { + "name": "tokyonight" + }, + "scroll": { + "speed": 2, + "acceleration": true + } +} +``` + +V2 migrates the global TUI configuration automatically. On the first CLI or TUI startup, when `cli.json` does not already +exist, it: + +- Reads `~/.config/opencode/tui.json`. +- Reads persisted TUI preferences from the legacy `kv.json` state file. +- Converts supported settings to the native grouped format and writes `~/.config/opencode/cli.json`. +- Leaves the V1 files unchanged so V1 can continue using them. + +Migration runs only while `cli.json` is absent. Once that file exists, V2 treats it as the source of truth and does not +continually synchronize later changes from `tui.json` or `kv.json`. If you created `cli.json` before starting V2, merge any +V1 settings you still need into it manually. + +Project-local V1 TUI configuration is not migrated because V2 has no project-local CLI configuration. Move settings you +still want into the global `cli.json`; when multiple projects used different values for the same setting, choose the +global behavior you want V2 to use. + ## Plugins Rename `plugin` to `plugins`. Replace a package-and-options tuple with an object: diff --git a/packages/opencode/script/build.ts b/packages/opencode/script/build.ts index 236838dbde..9f59cffaef 100755 --- a/packages/opencode/script/build.ts +++ b/packages/opencode/script/build.ts @@ -159,7 +159,6 @@ for (const item of targets) { const localPath = path.resolve(dir, "node_modules/@opentui/core/parser.worker.js") const rootPath = path.resolve(dir, "../../node_modules/@opentui/core/parser.worker.js") const parserWorker = fs.realpathSync(fs.existsSync(localPath) ? localPath : rootPath) - const workerPath = "./src/cli/tui/worker.ts" // Use platform-specific bunfs root path based on target OS const bunfsRoot = item.os === "win32" ? "B:/~BUN/root/" : "/$bunfs/root/" @@ -185,13 +184,12 @@ for (const item of targets) { windows: {}, }, files: embeddedFileMap ? { "opencode-web-ui.gen.ts": embeddedFileMap } : {}, - entrypoints: ["./src/index.ts", parserWorker, workerPath, ...(embeddedFileMap ? ["opencode-web-ui.gen.ts"] : [])], + entrypoints: ["./src/index.ts", parserWorker, ...(embeddedFileMap ? ["opencode-web-ui.gen.ts"] : [])], define: { FFF_LIBC: JSON.stringify(item.abi === "musl" ? "musl" : "gnu"), OPENCODE_VERSION: `'${Script.version}'`, OPENCODE_MODELS_DEV: generated.modelsData, OTUI_TREE_SITTER_WORKER_PATH: bunfsRoot + workerRelativePath, - OPENCODE_WORKER_PATH: workerPath, OPENCODE_CHANNEL: `'${Script.channel}'`, OPENCODE_LIBC: item.os === "linux" ? `'${item.abi ?? "glibc"}'` : "", ...(item.os === "linux" ? { "process.env.OPENTUI_LIBC": JSON.stringify(item.abi ?? "glibc") } : {}), diff --git a/packages/opencode/src/cli/cmd/attach.ts b/packages/opencode/src/cli/cmd/attach.ts deleted file mode 100644 index 0d06bd7c41..0000000000 --- a/packages/opencode/src/cli/cmd/attach.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { cmd } from "./cmd" -import { UI } from "@/cli/ui" -import { errorMessage } from "@opencode-ai/tui/util/error" -import { validateSession } from "../tui/validate-session" -import { ServerAuth } from "@/server/auth" -import { OpenCode } from "@opencode-ai/client/promise" -import { createOpencodeClient } from "@opencode-ai/sdk/v2" - -export const AttachCommand = cmd({ - command: "attach ", - describe: "attach to a running opencode server", - builder: (yargs) => - yargs - .positional("url", { - type: "string", - describe: "http://localhost:4096", - demandOption: true, - }) - .option("dir", { - type: "string", - description: "directory to run in", - }) - .option("continue", { - alias: ["c"], - describe: "continue the last session", - type: "boolean", - }) - .option("session", { - alias: ["s"], - type: "string", - describe: "session id to continue", - }) - .option("fork", { - type: "boolean", - describe: "fork the session when continuing (use with --continue or --session)", - }) - .option("password", { - alias: ["p"], - type: "string", - describe: "basic auth password (defaults to OPENCODE_SERVER_PASSWORD)", - }) - .option("username", { - alias: ["u"], - type: "string", - describe: "basic auth username (defaults to OPENCODE_SERVER_USERNAME or 'opencode')", - }), - handler: async (args) => { - const directory = (() => { - if (!args.dir) return undefined - try { - process.chdir(args.dir) - return process.cwd() - } catch { - // If the directory doesn't exist locally (remote attach), pass it through. - return args.dir - } - })() - - const { TuiConfig } = await import("@/config/tui") - if (args.fork && !args.continue && !args.session) { - UI.error("--fork requires --continue or --session") - process.exitCode = 1 - return - } - - const headers = ServerAuth.headers({ password: args.password, username: args.username }) - const config = await TuiConfig.get() - - try { - await validateSession({ - url: args.url, - sessionID: args.session, - directory, - headers, - }) - } catch (error) { - UI.error(errorMessage(error)) - process.exitCode = 1 - return - } - - const { Effect } = await import("effect") - const { run } = await import("../tui/layer") - const { createLegacyTuiPluginHost } = await import("@/plugin/tui/runtime") - await Effect.runPromise( - run({ - // @ts-expect-error V1 does not consume the V2-only server input. - client: createOpencodeClient({ baseUrl: args.url, headers, directory }), - api: OpenCode.make({ baseUrl: args.url, headers }), - config, - pluginHost: createLegacyTuiPluginHost(), - args: { - continue: args.continue, - sessionID: args.session, - fork: args.fork, - }, - }), - ) - }, -}) diff --git a/packages/opencode/src/cli/cmd/tui.ts b/packages/opencode/src/cli/cmd/tui.ts deleted file mode 100644 index 6f9dd25f51..0000000000 --- a/packages/opencode/src/cli/cmd/tui.ts +++ /dev/null @@ -1,198 +0,0 @@ -import { cmd } from "@/cli/cmd/cmd" -import { Rpc } from "@/util/rpc" -import { type rpc } from "../tui/worker" -import path from "path" -import { fileURLToPath } from "url" -import { UI } from "@/cli/ui" -import { errorMessage } from "@opencode-ai/tui/util/error" -import { withTimeout } from "@/util/timeout" -import { withNetworkOptions, resolveNetworkOptionsNoConfig, hasArg } from "@/cli/network" -import { Filesystem } from "@/util/filesystem" -import { OpenCode } from "@opencode-ai/client/promise" -import { createOpencodeClient } from "@opencode-ai/sdk/v2" -import { writeHeapSnapshot } from "v8" -import { ServerAuth } from "@/server/auth" -import { validateSession } from "../tui/validate-session" -import { win32InstallCtrlCGuard } from "@opencode-ai/tui/terminal-win32" - -declare global { - const OPENCODE_WORKER_PATH: string -} - -async function target() { - if (typeof OPENCODE_WORKER_PATH !== "undefined") return OPENCODE_WORKER_PATH - const dist = new URL("./cli/tui/worker.js", import.meta.url) - if (await Filesystem.exists(fileURLToPath(dist))) return dist - return new URL("../tui/worker.ts", import.meta.url) -} - -async function input(value?: string) { - const piped = process.stdin.isTTY ? undefined : await Bun.stdin.text() - if (!value) return piped - if (!piped) return value - return piped + "\n" + value -} - -export function resolveThreadDirectory(project?: string, envPWD = process.env.PWD, cwd = process.cwd()) { - const root = Filesystem.resolve(envPWD ?? cwd) - if (project) return Filesystem.resolve(path.isAbsolute(project) ? project : path.join(root, project)) - return Filesystem.resolve(cwd) -} - -export const TuiThreadCommand = cmd({ - command: "$0 [project]", - describe: "start opencode tui", - builder: (yargs) => - withNetworkOptions(yargs) - .positional("project", { - type: "string", - describe: "path to start opencode in", - }) - .option("model", { - type: "string", - alias: ["m"], - describe: "model to use in the format of provider/model", - }) - .option("continue", { - alias: ["c"], - describe: "continue the last session", - type: "boolean", - }) - .option("session", { - alias: ["s"], - type: "string", - describe: "session id to continue", - }) - .option("fork", { - type: "boolean", - describe: "fork the session when continuing (use with --continue or --session)", - }) - .option("prompt", { - type: "string", - describe: "prompt to use", - }) - .option("agent", { - type: "string", - describe: "agent to use", - }) - .option("auto", { - type: "boolean", - describe: "auto-approve permissions that are not explicitly denied (dangerous!)", - default: false, - }) - .option("yolo", { - type: "boolean", - hidden: true, - default: false, - }) - .option("dangerously-skip-permissions", { - type: "boolean", - hidden: true, - default: false, - }), - handler: async (args) => { - const unguard = win32InstallCtrlCGuard() - try { - const { TuiConfig } = await import("@/config/tui") - if (args.fork && !args.continue && !args.session) { - UI.error("--fork requires --continue or --session") - process.exitCode = 1 - return - } - - // Resolve relative --project paths from PWD, then use the real cwd after - // chdir so the thread and worker share the same directory key. - const next = resolveThreadDirectory(args.project) - const file = await target() - try { - process.chdir(next) - } catch { - UI.error("Failed to change directory to " + next) - return - } - const cwd = Filesystem.resolve(process.cwd()) - - const worker = new Worker(file, { - env: Object.fromEntries( - Object.entries(process.env).filter((entry): entry is [string, string] => entry[1] !== undefined), - ), - }) - const client = Rpc.client(worker) - const reload = () => { - client.call("reload", undefined).catch(() => {}) - } - process.on("SIGUSR2", reload) - - let stopped = false - const stop = async () => { - if (stopped) return - stopped = true - process.off("SIGUSR2", reload) - await withTimeout(client.call("shutdown", undefined), 5000).catch(() => {}) - worker.terminate() - } - - const prompt = await input(args.prompt) - const config = await TuiConfig.get() - - const network = resolveNetworkOptionsNoConfig(args) - const external = hasArg("--port") || hasArg("--hostname") || network.mdns === true - const headers = external ? ServerAuth.headers() : undefined - const url = (await client.call("server", network)).url - - try { - await validateSession({ - url, - sessionID: args.session, - directory: cwd, - headers, - }) - } catch (error) { - UI.error(errorMessage(error)) - process.exitCode = 1 - return - } - - setTimeout(() => { - client.call("checkUpgrade", { directory: cwd }).catch(() => {}) - }, 1000).unref?.() - - try { - const { Effect } = await import("effect") - const { run } = await import("../tui/layer") - const { createLegacyTuiPluginHost } = await import("@/plugin/tui/runtime") - await Effect.runPromise( - run({ - // @ts-expect-error V1 does not consume the V2-only server input. - client: createOpencodeClient({ baseUrl: url, headers, directory: cwd }), - api: OpenCode.make({ baseUrl: url, headers }), - async onSnapshot() { - const tui = writeHeapSnapshot("tui.heapsnapshot") - const server = await client.call("snapshot", undefined) - return [tui, server] - }, - config, - pluginHost: createLegacyTuiPluginHost(), - args: { - continue: args.continue, - sessionID: args.session, - agent: args.agent, - model: args.model, - prompt, - fork: args.fork, - auto: args.auto || args.yolo || args["dangerously-skip-permissions"], - }, - }), - ) - } finally { - await stop() - } - } finally { - try { - unguard?.() - } catch {} - } - process.exit(0) - }, -}) -// scratch diff --git a/packages/opencode/src/cli/tui/layer.ts b/packages/opencode/src/cli/tui/layer.ts deleted file mode 100644 index a592b86dae..0000000000 --- a/packages/opencode/src/cli/tui/layer.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { run as runTui, type TuiInput } from "@opencode-ai/tui" -import { Global } from "@opencode-ai/core/global" -import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" -import { Effect } from "effect" - -export function run(input: TuiInput) { - return runTui(input).pipe(Effect.provide(AppNodeBuilder.build(Global.node))) -} diff --git a/packages/opencode/src/cli/tui/worker.ts b/packages/opencode/src/cli/tui/worker.ts deleted file mode 100644 index c03b3b7076..0000000000 --- a/packages/opencode/src/cli/tui/worker.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { Server } from "@/server/server" -import { InstanceRuntime } from "@/project/instance-runtime" -import { Rpc } from "@/util/rpc" -import { upgrade } from "@/cli/upgrade" -import { Config } from "@/config/config" -import { writeHeapSnapshot } from "node:v8" -import { Heap } from "@/cli/heap" -import { AppRuntime } from "@/effect/app-runtime" -import { Effect } from "effect" -import { disposeAllInstancesAndEmitGlobalDisposed } from "@/server/global-lifecycle" - -Heap.start() - -const onUnhandledRejection = (_error: unknown) => {} - -const onUncaughtException = (_error: Error) => {} - -process.on("unhandledRejection", onUnhandledRejection) -process.on("uncaughtException", onUncaughtException) - -let server: Awaited> | undefined - -export const rpc = { - snapshot() { - const result = writeHeapSnapshot("server.heapsnapshot") - return result - }, - async server(input: { port: number; hostname: string; mdns?: boolean; cors?: string[] }) { - if (server) await server.stop(true) - server = await Server.listen(input) - return { url: server.url.toString() } - }, - async checkUpgrade(input: { directory: string }) { - await InstanceRuntime.load({ directory: input.directory }) - await upgrade().catch(() => {}) - }, - async reload() { - await AppRuntime.runPromise( - Effect.gen(function* () { - const cfg = yield* Config.Service - yield* cfg.invalidate() - yield* disposeAllInstancesAndEmitGlobalDisposed({ swallowErrors: true }) - }), - ) - }, - async shutdown() { - await InstanceRuntime.disposeAllInstances() - if (server) await server.stop(true) - process.off("unhandledRejection", onUnhandledRejection) - process.off("uncaughtException", onUncaughtException) - }, -} - -Rpc.listen(rpc) diff --git a/packages/opencode/src/index.ts b/packages/opencode/src/index.ts index daa99d43c0..1a62f05c7a 100644 --- a/packages/opencode/src/index.ts +++ b/packages/opencode/src/index.ts @@ -18,9 +18,7 @@ import { McpCommand } from "./cli/cmd/mcp" import { GithubCommand } from "./cli/cmd/github" import { ExportCommand } from "./cli/cmd/export" import { ImportCommand } from "./cli/cmd/import" -import { AttachCommand } from "./cli/cmd/attach" import { V2ServeCommand } from "./cli/cmd/v2-serve" -import { TuiThreadCommand } from "./cli/cmd/tui" import { AcpCommand } from "./cli/cmd/acp" import { EOL } from "os" import { WebCommand } from "./cli/cmd/web" @@ -82,8 +80,6 @@ const cli = yargs(args) .command(AcpCommand) .command(McpCommand) .command(V2ServeCommand) - .command(TuiThreadCommand) - .command(AttachCommand) .command(RunCommand) .command(GenerateCommand) .command(DebugCommand) diff --git a/packages/opencode/src/plugin/tui/runtime.ts b/packages/opencode/src/plugin/tui/runtime.ts index 6b07166795..e39484dd19 100644 --- a/packages/opencode/src/plugin/tui/runtime.ts +++ b/packages/opencode/src/plugin/tui/runtime.ts @@ -42,7 +42,7 @@ import { ConfigPluginV1 } from "@opencode-ai/core/v1/config/plugin" import { createCommandShim } from "@opencode-ai/tui/plugin/command-shim" import { RuntimeFlags } from "@/effect/runtime-flags" import { Effect } from "effect" -import { createPluginRuntime, type PluginRuntime, type TuiPluginHost } from "@opencode-ai/tui/plugin/runtime" +import { createPluginRuntime, type PluginRuntime } from "@opencode-ai/tui/plugin/runtime" ensureRuntimePluginSupport({ additional: keymapRuntimeModules }) @@ -1121,11 +1121,4 @@ async function load(input: { } } -export function createLegacyTuiPluginHost(): TuiPluginHost { - return { - start: init, - dispose, - } -} - export * as TuiPluginRuntime from "./runtime" diff --git a/packages/opencode/src/temporary.ts b/packages/opencode/src/temporary.ts index 8ece4b89ef..ee9afb60b4 100644 --- a/packages/opencode/src/temporary.ts +++ b/packages/opencode/src/temporary.ts @@ -1,5 +1,4 @@ import yargs from "yargs" -import { TuiThreadCommand } from "./cli/cmd/tui" import { V2ServeCommand } from "./cli/cmd/v2-serve" import { InstallationVersion } from "@opencode-ai/core/installation/version" import { hideBin } from "yargs/helpers" @@ -29,5 +28,4 @@ const cli = yargs(hideBin(process.argv)) if (opts.logLevel) process.env.OPENCODE_LOG_LEVEL = opts.logLevel }) .command(V2ServeCommand) - .command(TuiThreadCommand) .parse() diff --git a/packages/opencode/test/cli/tui/attach.test.ts b/packages/opencode/test/cli/tui/attach.test.ts deleted file mode 100644 index 3535063d0e..0000000000 --- a/packages/opencode/test/cli/tui/attach.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { describe, expect, test } from "bun:test" - -describe("tui attach", () => { - test("loads the TUI integration lazily", async () => { - const source = await Bun.file(new URL("../../../src/cli/cmd/attach.ts", import.meta.url)).text() - - expect(source).toContain('await import("../tui/layer")') - expect(source).toMatch(/await import\(["']@\/plugin\/tui\/runtime["']\)/) - expect(source).not.toContain('import("./app")') - }) -}) diff --git a/packages/opencode/test/cli/tui/thread.test.ts b/packages/opencode/test/cli/tui/thread.test.ts deleted file mode 100644 index 4c5d69d67f..0000000000 --- a/packages/opencode/test/cli/tui/thread.test.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { describe, expect, test } from "bun:test" -import { Effect } from "effect" -import fs from "fs/promises" -import path from "path" -import yargs from "yargs" -import { tmpdir } from "../../fixture/fixture" -import { TuiThreadCommand, resolveThreadDirectory } from "../../../src/cli/cmd/tui" -import { cliIt } from "../../lib/cli-process" - -describe("tui thread", () => { - test("loads the TUI integration lazily", async () => { - const source = await Bun.file(new URL("../../../src/cli/cmd/tui.ts", import.meta.url)).text() - - expect(source).toContain('await import("../tui/layer")') - expect(source).toMatch(/await import\(["']@\/plugin\/tui\/runtime["']\)/) - expect(source).not.toContain('import("./app")') - }) - - test("forwards the CLI environment to the TUI worker", async () => { - const source = await Bun.file(new URL("../../../src/cli/cmd/tui.ts", import.meta.url)).text() - - expect(source).toMatch(/new Worker\(file, \{\s*env: Object\.fromEntries\(\s*Object\.entries\(process\.env\)/) - }) - - async function check(project?: string) { - await using tmp = await tmpdir({ git: true }) - const link = path.join(path.dirname(tmp.path), path.basename(tmp.path) + "-link") - const type = process.platform === "win32" ? "junction" : "dir" - - try { - await fs.symlink(tmp.path, link, type) - expect(resolveThreadDirectory(project, link, tmp.path)).toBe(tmp.path) - } finally { - await fs.rm(link, { recursive: true, force: true }).catch(() => undefined) - } - } - - test("uses the real cwd when PWD points at a symlink", async () => { - await check() - }) - - test("uses the real cwd after resolving a relative project from PWD", async () => { - await check(".") - }) - - test("resolves a relative project from PWD when cwd differs", async () => { - await using pwd = await tmpdir({ git: true }) - await using cwd = await tmpdir({ git: true }) - - expect(resolveThreadDirectory(".", pwd.path, cwd.path)).toBe(pwd.path) - expect(resolveThreadDirectory(undefined, pwd.path, cwd.path)).toBe(cwd.path) - }) - - test("preserves boolean negation for existing options", async () => { - const args = await yargs([]) - .command({ ...TuiThreadCommand, handler: () => {} }) - .exitProcess(false) - .parse(["--mdns", "--no-mdns"]) - - expect(args.mdns).toBe(false) - }) - - cliIt.live("rejects removed top-level mini alias", ({ opencode }) => - Effect.gen(function* () { - const result = yield* opencode.spawn(["--mini"]) - - opencode.expectExit(result, 1) - expect(result.stderr).not.toContain("opencode mini requires a TTY stdout") - }), - ) - - cliIt.live("rejects removed run mini flag", ({ opencode }) => - Effect.gen(function* () { - const result = yield* opencode.spawn(["run", "--mini"]) - - opencode.expectExit(result, 1) - expect(result.stderr).not.toContain("opencode mini requires a TTY stdout") - }), - ) - - cliIt.live("rejects removed attach mini alias", ({ opencode }) => - Effect.gen(function* () { - const result = yield* opencode.spawn(["attach", "http://127.0.0.1:1", "--mini"]) - - opencode.expectExit(result, 1) - expect(result.stderr).not.toContain("opencode mini requires a TTY stdout") - }), - ) -}) diff --git a/packages/plugin/src/tui.ts b/packages/plugin/src/tui.ts index 101e12a4f1..d3f144e859 100644 --- a/packages/plugin/src/tui.ts +++ b/packages/plugin/src/tui.ts @@ -358,6 +358,7 @@ export type TuiTheme = { readonly ready: boolean } +/** @deprecated Persistent TUI KV storage is not supported in V2. */ export type TuiKV = { get: (key: string, fallback?: Value) => Value set: (key: string, value: unknown) => void @@ -407,13 +408,41 @@ type TuiAttentionConfigView = { sounds: Partial> } -type TuiConfigView = Pick & - NonNullable & { - leader_timeout: number - attention: TuiAttentionConfigView - plugin_enabled?: Record - keybinds: TuiBindingLookupView +type TuiConfigView = { + $schema?: string + theme?: string | { name?: string; mode?: "system" | "dark" | "light" } + plugin?: PluginConfig["plugin"] + plugins?: ReadonlyArray }> + plugin_enabled?: Record + leader?: { timeout: number } + leader_timeout?: number + scroll?: { speed?: number; acceleration?: boolean } + scroll_speed?: number + scroll_acceleration?: { enabled: boolean } + attention: TuiAttentionConfigView + diffs?: { + wrap?: "word" | "none" + tree?: boolean + single?: boolean + view?: "auto" | "split" | "unified" } + diff_style?: "auto" | "stacked" + terminal?: { title?: boolean } + prompt?: + | { editor?: boolean; paste?: "compact" | "full" } + | { max_height?: number; max_width?: number | "auto" } + session?: { + sidebar?: "auto" | "hide" + scrollbar?: boolean + thinking?: "show" | "hide" + markdown?: "source" | "rendered" + grouping?: "auto" | "none" + } + hints?: { tips?: boolean; onboarding?: boolean } + animations?: boolean + mouse: boolean + keybinds: TuiBindingLookupView +} export type TuiApp = { readonly version: string @@ -601,6 +630,7 @@ export type TuiPluginApi = { dialog: TuiDialogStack } readonly tuiConfig: Frozen + /** @deprecated Persistent TUI KV storage is not supported in V2. */ kv: TuiKV state: TuiState theme: TuiTheme diff --git a/packages/plugin/src/v2/effect/tool.ts b/packages/plugin/src/v2/effect/tool.ts index 7ba420d7a2..ce53b78bd3 100644 --- a/packages/plugin/src/v2/effect/tool.ts +++ b/packages/plugin/src/v2/effect/tool.ts @@ -272,7 +272,8 @@ export interface ToolExecuteAfterEvent { export interface RegisterOptions { readonly group?: string - readonly deferred?: boolean + /** Defaults to true. False exposes the tool directly to the provider. */ + readonly codemode?: boolean } export interface ToolDraft { diff --git a/packages/plugin/src/v2/promise/tool.ts b/packages/plugin/src/v2/promise/tool.ts index c804f1d1eb..77d936f580 100644 --- a/packages/plugin/src/v2/promise/tool.ts +++ b/packages/plugin/src/v2/promise/tool.ts @@ -69,7 +69,8 @@ export interface ToolExecuteAfterEvent { export interface RegisterOptions { readonly group?: string - readonly deferred?: boolean + /** Defaults to true. False exposes the tool directly to the provider. */ + readonly codemode?: boolean } export interface ToolDraft { diff --git a/packages/tui/package.json b/packages/tui/package.json index 9c16e0a731..ee70ca50b0 100644 --- a/packages/tui/package.json +++ b/packages/tui/package.json @@ -12,19 +12,17 @@ "exports": { ".": "./src/index.tsx", "./builtins": "./src/feature-plugins/builtins.ts", + "./config": "./src/config/index.tsx", + "./config/keybind": "./src/config/keybind.ts", "./config/v1": "./src/config/v1/index.tsx", "./config/v1/keybind": "./src/config/v1/keybind.ts", - "./config/v2": "./src/config/v2/index.ts", - "./config/v2/keybind": "./src/config/v2/keybind.ts", "./context/args": "./src/context/args.tsx", "./context/epilogue": "./src/context/epilogue.tsx", "./context/exit": "./src/context/exit.tsx", - "./context/kv": "./src/context/kv.tsx", "./context/log": "./src/context/log.tsx", "./context/project": "./src/context/project.tsx", "./context/runtime": "./src/context/runtime.tsx", "./context/sdk": "./src/context/sdk.tsx", - "./context/sync": "./src/context/sync.tsx", "./context/theme": "./src/context/theme.tsx", "./context/editor": "./src/context/editor.ts", "./context/clipboard": "./src/context/clipboard.tsx", diff --git a/packages/tui/src/app.tsx b/packages/tui/src/app.tsx index 61144859a9..574686ced1 100644 --- a/packages/tui/src/app.tsx +++ b/packages/tui/src/app.tsx @@ -44,7 +44,6 @@ import { useEvent } from "./context/event" import { SDKProvider, useSDK } from "./context/sdk" import { StartupLoading } from "./component/startup-loading" import { Reconnecting } from "./component/reconnecting" -import { SyncProvider, useSync } from "./context/sync" import { DataProvider, useData } from "./context/data" import { LocationProvider } from "./context/location" import { LocalProvider, useLocal } from "./context/local" @@ -53,6 +52,7 @@ import { DialogModel } from "./component/dialog-model" import { useConnected } from "./component/use-connected" import { DialogMcp } from "./component/dialog-mcp" import { DialogStatus } from "./component/dialog-status" +import { DialogConfig } from "./component/dialog-config" import { DialogDebug } from "./component/dialog-debug" import { DialogPair, type DialogPairCredentials } from "./component/dialog-pair" import { createOpencodeClient } from "@opencode-ai/sdk/v2/client" @@ -70,12 +70,11 @@ import { DialogAlert } from "./ui/dialog-alert" import { DialogConfirm } from "./ui/dialog-confirm" import { ToastProvider, useToast } from "./ui/toast" import { isDefaultTitle } from "./util/session" -import { KVProvider, useKV } from "./context/kv" import * as Model from "./util/model" import { ArgsProvider, useArgs, type Args } from "./context/args" import open from "open" import { PromptRefProvider, usePromptRef } from "./context/prompt" -import { TuiConfigProvider, useTuiConfig, type TuiConfig } from "./config/v1" +import { Config, ConfigProvider, useConfig } from "./config" import { createTuiApiAdapters } from "./plugin/adapters" import { createTuiApi } from "./plugin/api" import { createPluginRuntime, PluginRuntimeProvider, usePluginRuntime, type TuiPluginHost } from "./plugin/runtime" @@ -137,14 +136,12 @@ const appBindingCommands = [ "diff.open", "app.debug", "app.console", - "app.heap_snapshot", "terminal.suspend", "terminal.title.toggle", "app.toggle.animations", "app.toggle.file_context", "app.toggle.diffwrap", "app.toggle.paste_summary", - "app.toggle.session_directory_filter", ] as const export type TuiInput = { @@ -154,8 +151,7 @@ export type TuiInput = { reload?: () => Promise } args: Args - config: TuiConfig.Resolved - onSnapshot?: () => Promise + config: Config.Interface pluginHost: TuiPluginHost terminalHandoff?: () => Promise< | { @@ -183,26 +179,12 @@ function errorMessage(error: unknown) { return error instanceof Error ? error.message : String(error) } -function isVersionGreater(left: string, right: string) { - const parse = (value: string) => { - const [core, prerelease] = value.replace(/^v/, "").split("-", 2) - return { core: core.split(".").map((part) => Number.parseInt(part, 10) || 0), prerelease } - } - const a = parse(left) - const b = parse(right) - for (let index = 0; index < Math.max(a.core.length, b.core.length); index++) { - const difference = (a.core[index] ?? 0) - (b.core[index] ?? 0) - if (difference) return difference > 0 - } - if (a.prerelease === b.prerelease) return false - if (!a.prerelease) return true - if (!b.prerelease) return false - return a.prerelease.localeCompare(b.prerelease, undefined, { numeric: true }) > 0 -} - export const run = Effect.fn("Tui.run")(function* (input: TuiInput) { const log = input.log ?? (() => {}) const global = yield* Global.Service + const config = Config.resolve(yield* Effect.tryPromise(() => input.config.get()), { + terminalSuspend: process.platform !== "win32", + }) const options = { baseUrl: input.server.endpoint.url, headers: Service.headers(input.server.endpoint) } const api = OpenCode.make(options) const directory = yield* Effect.tryPromise(() => api.file.list({ location: { directory: process.cwd() } })).pipe( @@ -235,7 +217,7 @@ export const run = Effect.fn("Tui.run")(function* (input: TuiInput) { useKittyKeyboard: {}, autoFocus: false, openConsoleOnError: false, - useMouse: !Flag.OPENCODE_DISABLE_MOUSE && input.config.mouse, + useMouse: !Flag.OPENCODE_DISABLE_MOUSE && config.mouse, consoleOptions: { keyBindings: [{ name: "y", ctrl: true, action: "copy-selection" }], }, @@ -263,7 +245,7 @@ export const run = Effect.fn("Tui.run")(function* (input: TuiInput) { win32DisableProcessedInput() const keymap = createDefaultOpenTuiKeymap(renderer) yield* Effect.acquireRelease( - Effect.sync(() => registerOpencodeKeymap(keymap, renderer, input.config)), + Effect.sync(() => registerOpencodeKeymap(keymap, renderer, config)), (unregister) => Effect.sync(unregister), ) yield* Effect.addFinalizer(() => @@ -337,7 +319,11 @@ export const run = Effect.fn("Tui.run")(function* (input: TuiInput) { - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -430,20 +411,22 @@ export const run = Effect.fn("Tui.run")(function* (input: TuiInput) { }) }) -function App(props: { onSnapshot?: () => Promise; pluginHost: TuiPluginHost; pair?: DialogPairCredentials }) { +function App(props: { + pluginHost: TuiPluginHost + pair?: DialogPairCredentials +}) { const log = useLog({ component: "app" }) const startup = useTuiStartup() - const tuiConfig = useTuiConfig() + const configState = useConfig() + const config = configState.data const route = useRoute() const dimensions = useTerminalDimensions() const renderer = useRenderer() const dialog = useDialog() const local = useLocal() - const kv = useKV() const keymap = useOpencodeKeymap() const event = useEvent() const sdk = useSDK() - const sync = useSync() const toast = useToast() const themeState = useTheme() const { theme, mode, setMode, locked, lock, unlock } = themeState @@ -452,7 +435,7 @@ function App(props: { onSnapshot?: () => Promise; pluginHost: TuiPlugi const exit = useExit() const promptRef = usePromptRef() const pluginRuntime = usePluginRuntime() - const attention = createTuiAttention({ renderer, config: tuiConfig, kv }) + const attention = createTuiAttention({ renderer, config, update: configState.update }) const clipboard = useClipboard() // Toast once when an MCP server enters a failed or needs-auth state so the user knows to act, @@ -478,7 +461,7 @@ function App(props: { onSnapshot?: () => Promise; pluginHost: TuiPlugi toast.show({ variant: "error", title: `MCP server failed: ${server.name}`, - message: "Open MCPs to view details.", + message: "Open MCP servers to view details.", }) } }) @@ -486,15 +469,14 @@ function App(props: { onSnapshot?: () => Promise; pluginHost: TuiPlugi const api = createTuiApi( createTuiApiAdapters({ version: InstallationVersion, - tuiConfig, + tuiConfig: config, dialog, keymap, - kv, route, routes: pluginRuntime.routes, event, sdk, - sync, + project, data, theme: themeState, toast, @@ -507,7 +489,6 @@ function App(props: { onSnapshot?: () => Promise; pluginHost: TuiPlugi props.pluginHost .start({ api, - config: tuiConfig, runtime: pluginRuntime, dispose: () => attention.dispose(), }) @@ -543,10 +524,12 @@ function App(props: { onSnapshot?: () => Promise; pluginHost: TuiPlugi renderer.clearSelection() } - const [terminalTitleEnabled, setTerminalTitleEnabled] = createSignal(kv.get("terminal_title_enabled", true)) - const [pasteSummaryEnabled, setPasteSummaryEnabled] = createSignal( - kv.get("paste_summary_enabled", true), - ) + const terminalTitleEnabled = () => config.terminal?.title ?? true + const pasteSummaryEnabled = () => config.prompt?.paste !== "full" + + createEffect(() => { + renderer.useMouse = !Flag.OPENCODE_DISABLE_MOUSE && config.mouse + }) // Update terminal window title based on current route and session createEffect(() => { @@ -741,7 +724,7 @@ function App(props: { onSnapshot?: () => Promise; pluginHost: TuiPlugi }, { name: "mcp.list", - title: "MCP Servers", + title: "MCP servers", category: "Agent", slashName: "mcps", run: () => { @@ -805,6 +788,15 @@ function App(props: { onSnapshot?: () => Promise; pluginHost: TuiPlugi }, category: "Integration", }, + { + name: "opencode.settings", + title: "Open settings", + slashName: "settings", + run: () => { + dialog.replace(() => ) + }, + category: "System", + }, { name: "opencode.status", title: "View status", @@ -863,6 +855,7 @@ function App(props: { onSnapshot?: () => Promise; pluginHost: TuiPlugi { name: "theme.switch_mode", title: mode() === "dark" ? "Switch to light mode" : "Switch to dark mode", + hidden: true, run: () => { setMode(mode() === "dark" ? "light" : "dark") dialog.clear() @@ -872,6 +865,7 @@ function App(props: { onSnapshot?: () => Promise; pluginHost: TuiPlugi { name: "theme.mode.lock", title: locked() ? "Unlock theme mode" : "Lock theme mode", + hidden: true, run: () => { if (locked()) unlock() else lock() @@ -923,20 +917,6 @@ function App(props: { onSnapshot?: () => Promise; pluginHost: TuiPlugi dialog.clear() }, }, - { - name: "app.heap_snapshot", - title: "Write heap snapshot", - category: "System", - run: async () => { - const files = await props.onSnapshot?.() - toast.show({ - variant: "info", - message: `Heap snapshot written to ${files?.join(", ")}`, - duration: 5000, - }) - dialog.clear() - }, - }, { name: "terminal.suspend", title: "Suspend terminal", @@ -953,41 +933,57 @@ function App(props: { onSnapshot?: () => Promise; pluginHost: TuiPlugi name: "terminal.title.toggle", title: terminalTitleEnabled() ? "Disable terminal title" : "Enable terminal title", category: "System", + hidden: true, run: () => { - setTerminalTitleEnabled((prev) => { - const next = !prev - kv.set("terminal_title_enabled", next) - if (!next) renderer.setTerminalTitle("") - return next - }) + const next = !terminalTitleEnabled() + if (!next) renderer.setTerminalTitle("") + void configState + .update((draft) => { + draft.terminal = { ...draft.terminal, title: next } + }) + .catch(toast.error) dialog.clear() }, }, { name: "app.toggle.animations", - title: kv.get("animations_enabled", true) ? "Disable animations" : "Enable animations", + title: (config.animations ?? true) ? "Disable animations" : "Enable animations", category: "System", + hidden: true, run: () => { - kv.set("animations_enabled", !kv.get("animations_enabled", true)) + void configState + .update((draft) => { + draft.animations = !(config.animations ?? true) + }) + .catch(toast.error) dialog.clear() }, }, { name: "app.toggle.file_context", - title: kv.get("file_context_enabled", true) ? "Disable file context" : "Enable file context", + title: (config.prompt?.editor ?? true) ? "Disable file context" : "Enable file context", category: "System", + hidden: true, run: () => { - kv.set("file_context_enabled", !kv.get("file_context_enabled", true)) + void configState + .update((draft) => { + draft.prompt = { ...draft.prompt, editor: !(config.prompt?.editor ?? true) } + }) + .catch(toast.error) dialog.clear() }, }, { name: "app.toggle.diffwrap", - title: kv.get("diff_wrap_mode", "word") === "word" ? "Disable diff wrapping" : "Enable diff wrapping", + title: (config.diffs?.wrap ?? "word") === "word" ? "Disable diff wrapping" : "Enable diff wrapping", category: "System", + hidden: true, run: () => { - const current = kv.get("diff_wrap_mode", "word") - kv.set("diff_wrap_mode", current === "word" ? "none" : "word") + void configState + .update((draft) => { + draft.diffs = { ...draft.diffs, wrap: (config.diffs?.wrap ?? "word") === "word" ? "none" : "word" } + }) + .catch(toast.error) dialog.clear() }, }, @@ -995,23 +991,13 @@ function App(props: { onSnapshot?: () => Promise; pluginHost: TuiPlugi name: "app.toggle.paste_summary", title: pasteSummaryEnabled() ? "Disable paste summary" : "Enable paste summary", category: "System", + hidden: true, run: () => { - setPasteSummaryEnabled((prev) => { - const next = !prev - kv.set("paste_summary_enabled", next) - return next - }) - dialog.clear() - }, - }, - { - name: "app.toggle.session_directory_filter", - title: kv.get("session_directory_filter_enabled", true) - ? "Disable session directory filtering" - : "Enable session directory filtering", - category: "System", - run: async () => { - kv.set("session_directory_filter_enabled", !kv.get("session_directory_filter_enabled", true)) + void configState + .update((draft) => { + draft.prompt = { ...draft.prompt, paste: pasteSummaryEnabled() ? "full" : "compact" } + }) + .catch(toast.error) dialog.clear() }, }, @@ -1037,11 +1023,11 @@ function App(props: { onSnapshot?: () => Promise; pluginHost: TuiPlugi useBindings(() => ({ mode: OPENCODE_BASE_MODE, - bindings: tuiConfig.keybinds.gather("app", appBindingCommands), + bindings: config.keybinds.gather("app", appBindingCommands), })) useBindings(() => ({ - bindings: tuiConfig.keybinds.gather("app.global", appGlobalBindingCommands), + bindings: config.keybinds.gather("app.global", appGlobalBindingCommands), })) useBindings(() => ({ @@ -1051,7 +1037,7 @@ function App(props: { onSnapshot?: () => Promise; pluginHost: TuiPlugi if (!current?.focused) return true return current.current.text === "" }, - bindings: tuiConfig.keybinds.gather("app_exit", ["app.exit"]), + bindings: config.keybinds.gather("app_exit", ["app.exit"]), })) event.on("tui.command.execute", (evt, { workspace }) => { @@ -1109,21 +1095,13 @@ function App(props: { onSnapshot?: () => Promise; pluginHost: TuiPlugi event.on("installation.update-available", async (evt) => { const version = evt.data.version - const skipped = kv.get("skipped_version") - if (skipped && !isVersionGreater(version, skipped)) return - const choice = await DialogConfirm.show( dialog, `Update Available`, `A new release v${version} is available. Would you like to update now?`, - "skip", + "later", ) - if (choice === false) { - kv.set("skipped_version", version) - return - } - if (choice !== true) return toast.show({ diff --git a/packages/tui/src/attention.ts b/packages/tui/src/attention.ts index 3fea75e5ff..e210a745d6 100644 --- a/packages/tui/src/attention.ts +++ b/packages/tui/src/attention.ts @@ -10,7 +10,7 @@ import type { TuiAttentionSoundPack, TuiAttentionSoundPackInfo, } from "@opencode-ai/plugin/tui" -import { AttentionSoundName, type TuiConfig } from "./config/v1" +import { AttentionSoundName, type Config } from "./config" import { Schema } from "effect" import stripAnsi from "strip-ansi" import * as TuiAudio from "./audio" @@ -38,9 +38,8 @@ type TuiAttentionHost = TuiAttention & { dispose(): void } -const DEFAULT_TITLE = "opencode" +const DEFAULT_TITLE = "OpenCode" const DEFAULT_PACK_ID = "opencode.default" -const KV_SOUND_PACK = "attention_sound_pack" const TITLE_LIMIT = 80 const MESSAGE_LIMIT = 240 const BUILTIN_PACK: RegisteredSoundPack = { @@ -80,7 +79,7 @@ function clampVolume(volume: number) { return Math.min(1, Math.max(0, volume)) } -function soundVolume(input: TuiAttentionNotifyInput, config: Pick) { +function soundVolume(input: TuiAttentionNotifyInput, config: Pick) { if (!config.attention.sound) return if (input.sound === false) return if (input.sound === undefined) return clampVolume(config.attention.volume) @@ -113,7 +112,9 @@ function focusSkip(when: TuiAttentionWhen, focus: FocusState) { export function createTuiAttention(input: { renderer: AttentionRenderer - config: Pick + config: Pick + update?: Config.Interface["update"] + /** @deprecated Ignored. Sound-pack persistence uses CLI config. */ kv?: TuiKV audio?: Pick }): TuiAttentionHost { @@ -134,8 +135,7 @@ export function createTuiAttention(input: { input.renderer.on("blur", onBlur) function configuredPackID() { - const stored = input.kv?.get(KV_SOUND_PACK, undefined) - return activePackID ?? stored ?? input.config.attention.sound_pack + return activePackID ?? input.config.attention.sound_pack } function currentPack() { @@ -234,7 +234,12 @@ export function createTuiAttention(input: { const pack = packs.get(id) if (!pack) return false activePackID = pack.id - if (options?.persist) input.kv?.set(KV_SOUND_PACK, pack.id) + if (options?.persist) + void input + .update?.((draft) => { + draft.attention = { ...draft.attention, sound_pack: pack.id } + }) + .catch(() => {}) return true }, current() { diff --git a/packages/tui/src/component/command-palette.tsx b/packages/tui/src/component/command-palette.tsx index a113419653..ff7b863678 100644 --- a/packages/tui/src/component/command-palette.tsx +++ b/packages/tui/src/component/command-palette.tsx @@ -8,7 +8,7 @@ import { useKeymapSelector, useOpencodeKeymap, } from "../keymap" -import { useTuiConfig } from "../config/v1" +import { useConfig } from "../config" type PaletteCommandEntry = ReturnType[number] @@ -24,7 +24,7 @@ function isSuggestedPaletteCommand(entry: PaletteCommandEntry) { } export function CommandPaletteDialog() { - const config = useTuiConfig() + const config = useConfig().data const keymap = useOpencodeKeymap() const entries = useKeymapSelector((keymap: OpenTuiKeymap) => { const query = { diff --git a/packages/tui/src/component/dialog-config.tsx b/packages/tui/src/component/dialog-config.tsx new file mode 100644 index 0000000000..7e964d3437 --- /dev/null +++ b/packages/tui/src/component/dialog-config.tsx @@ -0,0 +1,298 @@ +import { createMemo, createSignal } from "solid-js" +import { useConfig } from "../config" +import { useTheme } from "../context/theme" +import { DialogSelect } from "../ui/dialog-select" +import { useToast } from "../ui/toast" + +type Setting = { + title: string + category: string + path: string[] + default: unknown + values?: readonly unknown[] + labels?: readonly string[] + step?: number + min?: number + max?: number + format?: (value: unknown) => string +} + +const settings: Setting[] = [ + { + title: "Theme", + category: "Appearance", + path: ["theme", "name"], + default: "opencode", + }, + { + title: "Color mode", + category: "Appearance", + path: ["theme", "mode"], + default: "system", + values: ["system", "dark", "light"], + }, + { + title: "Animations", + category: "Appearance", + path: ["animations"], + default: true, + values: [false, true], + labels: ["off", "on"], + }, + { + title: "Tips", + category: "Appearance", + path: ["hints", "tips"], + default: true, + values: [false, true], + labels: ["off", "on"], + }, + { + title: "Onboarding", + category: "Appearance", + path: ["hints", "onboarding"], + default: true, + values: [false, true], + labels: ["off", "on"], + }, + { + title: "Sidebar", + category: "Session", + path: ["session", "sidebar"], + default: "auto", + values: ["hide", "auto"], + }, + { + title: "Scrollbar", + category: "Session", + path: ["session", "scrollbar"], + default: false, + values: [false, true], + labels: ["off", "on"], + }, + { + title: "Thinking", + category: "Session", + path: ["session", "thinking"], + default: "hide", + values: ["hide", "show"], + }, + { + title: "Markdown", + category: "Session", + path: ["session", "markdown"], + default: "rendered", + values: ["source", "rendered"], + }, + { + title: "Grouping", + category: "Session", + path: ["session", "grouping"], + default: "auto", + values: ["none", "auto"], + }, + { + title: "Layout", + category: "Diffs", + path: ["diffs", "view"], + default: "auto", + values: ["auto", "split", "unified"], + }, + { + title: "Wrapping", + category: "Diffs", + path: ["diffs", "wrap"], + default: "word", + values: ["none", "word"], + }, + { + title: "File tree", + category: "Diffs", + path: ["diffs", "tree"], + default: true, + values: [false, true], + labels: ["off", "on"], + }, + { + title: "Single patch", + category: "Diffs", + path: ["diffs", "single"], + default: false, + values: [false, true], + labels: ["off", "on"], + }, + { + title: "Scroll speed", + category: "Input", + path: ["scroll", "speed"], + default: 3, + step: 0.25, + min: 0.25, + max: 10, + format: (value) => Number(value).toFixed(2), + }, + { + title: "Acceleration", + category: "Input", + path: ["scroll", "acceleration"], + default: false, + values: [false, true], + labels: ["off", "on"], + }, + { + title: "Mouse", + category: "Input", + path: ["mouse"], + default: true, + values: [false, true], + labels: ["off", "on"], + }, + { + title: "Editor context", + category: "Input", + path: ["prompt", "editor"], + default: true, + values: [false, true], + labels: ["off", "on"], + }, + { + title: "Large pastes", + category: "Input", + path: ["prompt", "paste"], + default: "compact", + values: ["compact", "full"], + }, + { + title: "Leader timeout", + category: "Input", + path: ["leader", "timeout"], + default: 2000, + step: 250, + min: 250, + max: 10000, + format: (value) => `${value} ms`, + }, + { + title: "Attention", + category: "Alerts", + path: ["attention", "enabled"], + default: false, + values: [false, true], + labels: ["off", "on"], + }, + { + title: "Notifications", + category: "Alerts", + path: ["attention", "notifications"], + default: true, + values: [false, true], + labels: ["off", "on"], + }, + { + title: "Sounds", + category: "Alerts", + path: ["attention", "sound"], + default: true, + values: [false, true], + labels: ["off", "on"], + }, + { + title: "Volume", + category: "Alerts", + path: ["attention", "volume"], + default: 0.4, + step: 0.1, + min: 0, + max: 1, + format: (value) => `${Math.round(Number(value) * 100)}%`, + }, + { + title: "Window title", + category: "Terminal", + path: ["terminal", "title"], + default: true, + values: [false, true], + labels: ["off", "on"], + }, +] + +export function DialogConfig() { + const config = useConfig() + const toast = useToast() + const themeState = useTheme() + const [selected, setSelected] = createSignal(0) + const [saving, setSaving] = createSignal(false) + + const value = (setting: Setting) => { + const current = setting.path.reduce((result, key) => { + if (!result || typeof result !== "object") return undefined + return (result as Record)[key] + }, config.data) + if (setting.path.join(".") === "theme.name") return current ?? themeState.selected + return current ?? setting.default + } + const values = (setting: Setting) => + setting.path.join(".") === "theme.name" + ? Object.keys(themeState.all()).sort((a, b) => a.localeCompare(b, undefined, { sensitivity: "base" })) + : setting.values + const display = (setting: Setting) => { + const current = value(setting) + if (setting.format) return setting.format(current) + const index = setting.values?.indexOf(current) + return index === undefined || index < 0 ? String(current) : (setting.labels?.[index] ?? String(current)) + } + const options = createMemo(() => + settings.map((setting, index) => ({ + title: setting.title, + category: setting.category, + footer: display(setting), + value: index, + })), + ) + + async function change(direction: number, index = selected()) { + if (saving()) return + const setting = settings[index] + const current = value(setting) + const choices = values(setting) + const next = choices + ? choices[(choices.indexOf(current) + direction + choices.length) % choices.length] + : Math.min(setting.max!, Math.max(setting.min!, Number(current) + direction * setting.step!)) + if (next === current) return + setSaving(true) + await config + .update((draft) => { + const parent = setting.path.slice(0, -1).reduce>((result, key) => { + if (!result[key] || typeof result[key] !== "object") result[key] = {} + return result[key] as Record + }, draft) + parent[setting.path.at(-1)!] = next + }) + .catch(toast.error) + .finally(() => setSaving(false)) + } + + return ( + setSelected(option.value)} + onSelect={(option) => void change(1, option.value)} + footerHints={[{ title: "←/→", label: "change" }]} + bindings={[ + { + key: "left", + desc: "Previous value", + group: "Settings", + cmd: () => void change(-1), + }, + { + key: "right", + desc: "Next value", + group: "Settings", + cmd: () => void change(1), + }, + ]} + /> + ) +} diff --git a/packages/tui/src/component/dialog-mcp.tsx b/packages/tui/src/component/dialog-mcp.tsx index 34afc6fa48..fc21b2b610 100644 --- a/packages/tui/src/component/dialog-mcp.tsx +++ b/packages/tui/src/component/dialog-mcp.tsx @@ -9,7 +9,7 @@ import type { McpServer } from "@opencode-ai/client" import { useClipboard } from "../context/clipboard" import { useToast } from "../ui/toast" import { useKeyboard, useTerminalDimensions } from "@opentui/solid" -import { useTuiConfig } from "../config/v1" +import { useConfig } from "../config" import { getScrollAcceleration } from "../util/scroll" import { useBindings } from "../keymap" @@ -92,7 +92,7 @@ export function DialogMcp() { when={detail()} fallback={ void }) { const toast = useToast() const { theme } = useTheme() const dimensions = useTerminalDimensions() - const tuiConfig = useTuiConfig() + const config = useConfig().data const [copied, setCopied] = createSignal(false) const error = () => statusMeta(props.server.status, theme).error ?? "Unknown MCP connection error" const height = createMemo(() => Math.max(3, Math.floor(dimensions().height / 2) - 5)) @@ -152,7 +152,7 @@ function DialogMcpError(props: { server: McpServer; onBack: () => void }) { - MCP / {props.server.name} + MCP server: {props.server.name} esc back @@ -164,7 +164,7 @@ function DialogMcpError(props: { server: McpServer; onBack: () => void }) { ref={(element: ScrollBoxRenderable) => (scroll = element)} height={height()} scrollbarOptions={{ visible: false }} - scrollAcceleration={getScrollAcceleration(tuiConfig)} + scrollAcceleration={getScrollAcceleration(config)} > {error()} diff --git a/packages/tui/src/component/dialog-model.tsx b/packages/tui/src/component/dialog-model.tsx index fcbc472965..f998a28417 100644 --- a/packages/tui/src/component/dialog-model.tsx +++ b/packages/tui/src/component/dialog-model.tsx @@ -132,6 +132,7 @@ export function DialogModel(props: { providerID?: string }) { { command: "model.dialog.provider", title: connected() ? "Connect integration" : "View all integrations", + selection: "none", onTrigger() { dialog.replace(() => ( ) } diff --git a/packages/tui/src/component/dialog-move-session.tsx b/packages/tui/src/component/dialog-move-session.tsx index cf6cb361de..3ea26a50d5 100644 --- a/packages/tui/src/component/dialog-move-session.tsx +++ b/packages/tui/src/component/dialog-move-session.tsx @@ -345,6 +345,7 @@ export function DialogMoveSession(props: DialogMoveSessionProps) { { command: "dialog.move_session.new", title: "new", + selection: "none", onTrigger: () => void create(), }, { @@ -360,6 +361,7 @@ export function DialogMoveSession(props: DialogMoveSessionProps) { { command: "dialog.move_session.refresh", title: "refresh", + selection: "none", onTrigger: () => void refetch(), }, ] diff --git a/packages/tui/src/component/dialog-project-copy-name.tsx b/packages/tui/src/component/dialog-project-copy-name.tsx index 59dcc4a481..3ae331da59 100644 --- a/packages/tui/src/component/dialog-project-copy-name.tsx +++ b/packages/tui/src/component/dialog-project-copy-name.tsx @@ -1,7 +1,7 @@ import { InputRenderable, TextAttributes } from "@opentui/core" import { Slug } from "@opencode-ai/core/util/slug" import { createSignal, onMount } from "solid-js" -import { useTuiConfig } from "../config/v1" +import { useConfig } from "../config" import { useTheme } from "../context/theme" import { useBindings, useCommandShortcut } from "../keymap" import { useDialog, type DialogContext } from "../ui/dialog" @@ -9,7 +9,7 @@ import { useDialog, type DialogContext } from "../ui/dialog" export function DialogProjectCopyName(props: { onConfirm: (name: string) => void }) { const dialog = useDialog() const { theme } = useTheme() - const tuiConfig = useTuiConfig() + const config = useConfig().data const generateShortcut = useCommandShortcut("dialog.project_copy.generate") const [inputTarget, setInputTarget] = createSignal() let input: InputRenderable @@ -35,7 +35,7 @@ export function DialogProjectCopyName(props: { onConfirm: (name: string) => void run: generate, }, ], - bindings: tuiConfig.keybinds.get("dialog.project_copy.generate"), + bindings: config.keybinds.get("dialog.project_copy.generate"), })) onMount(() => { diff --git a/packages/tui/src/component/dialog-skill.tsx b/packages/tui/src/component/dialog-skill.tsx index c8e493ef37..d49d054c91 100644 --- a/packages/tui/src/component/dialog-skill.tsx +++ b/packages/tui/src/component/dialog-skill.tsx @@ -46,7 +46,6 @@ export function DialogSkill(props: DialogSkillProps) { title: skill.name.padEnd(maxWidth), description: skill.description?.replace(/\s+/g, " ").trim(), value: skill.id, - category: "Skills", onSelect: () => { props.onSelect(skill.id) dialog.clear() @@ -57,7 +56,6 @@ export function DialogSkill(props: DialogSkillProps) { return ( - 0} fallback={No MCP Servers}> + 0} fallback={No MCP servers}> - {mcp().length} MCP Servers + + {mcp().length} MCP server{mcp().length === 1 ? "" : "s"} + {(item) => ( diff --git a/packages/tui/src/component/dialog-workspace-file-changes.tsx b/packages/tui/src/component/dialog-workspace-file-changes.tsx index 231f6c4648..415b70623a 100644 --- a/packages/tui/src/component/dialog-workspace-file-changes.tsx +++ b/packages/tui/src/component/dialog-workspace-file-changes.tsx @@ -5,7 +5,7 @@ import { createMemo, For } from "solid-js" import { createStore } from "solid-js/store" import { FilePath } from "../ui/file-path" import { useTheme } from "../context/theme" -import { useTuiConfig } from "../config/v1" +import { useConfig } from "../config" import { useDialog, type DialogContext } from "../ui/dialog" import { getScrollAcceleration } from "../util/scroll" @@ -32,9 +32,9 @@ export function DialogWorkspaceFileChanges(props: { }) { const dialog = useDialog() const { theme } = useTheme() - const tuiConfig = useTuiConfig() + const config = useConfig().data const dimensions = useTerminalDimensions() - const scrollAcceleration = createMemo(() => getScrollAcceleration(tuiConfig)) + const scrollAcceleration = createMemo(() => getScrollAcceleration(config)) const [store, setStore] = createStore({ active: "yes" as WorkspaceFileChangesChoice }) const height = createMemo(() => Math.min(props.files.length, 8)) const fileNameWidth = createMemo( diff --git a/packages/tui/src/component/error-component.tsx b/packages/tui/src/component/error-component.tsx index 1141da8382..8d778badca 100644 --- a/packages/tui/src/component/error-component.tsx +++ b/packages/tui/src/component/error-component.tsx @@ -108,7 +108,7 @@ export function ErrorComponent(props: { error: Error; reset: () => void; mode?: {/* Headline */} - opencode crashed + OpenCode crashed An unexpected error stopped the session. @@ -192,7 +192,7 @@ export function ErrorComponent(props: { error: Error; reset: () => void; mode?: ? "Report copied — paste it into a new GitHub issue." : "Copy the report and open a GitHub issue to help us fix this."} - opencode {InstallationVersion} + OpenCode {InstallationVersion} @@ -211,7 +211,7 @@ function buildIssueURL(message: string, stack: string) { url.searchParams.set("terminal", describeTerminal()) url.searchParams.set( "reproduce", - "Reported automatically from the opencode crash screen. If you can, describe what you were doing when it crashed.", + "Reported automatically from the OpenCode crash screen. If you can, describe what you were doing when it crashed.", ) // Budget the stack against the fully URL-encoded length (not the raw length) so @@ -220,7 +220,7 @@ function buildIssueURL(message: string, stack: string) { // so measuring url.toString() is both correct and safe on any input. const MAX_URL_LENGTH = 6000 const marker = "\n... (truncated)" - const head = `The opencode TUI crashed with an unexpected error.\n\n**Error:** ${message}\n\n**Stack trace:**\n` + const head = `The OpenCode TUI crashed with an unexpected error.\n\n**Error:** ${message}\n\n**Stack trace:**\n` const setBody = (body: string) => url.searchParams.set("description", head + "```\n" + body + "\n```") setBody(stack) diff --git a/packages/tui/src/component/prompt/autocomplete.tsx b/packages/tui/src/component/prompt/autocomplete.tsx index 0a58892aef..3f01827880 100644 --- a/packages/tui/src/component/prompt/autocomplete.tsx +++ b/packages/tui/src/component/prompt/autocomplete.tsx @@ -11,7 +11,7 @@ import { useSDK } from "../../context/sdk" import { useData } from "../../context/data" import { getScrollAcceleration } from "../../util/scroll" import { useTuiPaths } from "../../context/runtime" -import { useTuiConfig } from "../../config/v1" +import { useConfig } from "../../config" import { useLocation } from "../../context/location" import { useTheme, selectedForeground } from "../../context/theme" import { SplitBorder } from "../../ui/border" @@ -92,7 +92,7 @@ export function Autocomplete(props: { const { theme } = useTheme() const dimensions = useTerminalDimensions() const frecency = useFrecency() - const tuiConfig = useTuiConfig() + const config = useConfig().data const paths = useTuiPaths() const location = useLocation() const [store, setStore] = createStore({ @@ -628,7 +628,7 @@ export function Autocomplete(props: { }, }, ], - bindings: tuiConfig.keybinds.gather("prompt.autocomplete", [ + bindings: config.keybinds.gather("prompt.autocomplete", [ "prompt.autocomplete.prev", "prompt.autocomplete.next", "prompt.autocomplete.hide", @@ -714,7 +714,7 @@ export function Autocomplete(props: { }) let scroll: ScrollBoxRenderable - const scrollAcceleration = createMemo(() => getScrollAcceleration(tuiConfig)) + const scrollAcceleration = createMemo(() => getScrollAcceleration(config)) return ( data.session.status(props.sessionID ?? "")) @@ -177,11 +176,10 @@ export function Prompt(props: PromptProps) { const exit = useExit() const dimensions = useTerminalDimensions() const { theme, syntax } = useTheme() - const kv = useKV() - const animationsEnabled = createMemo(() => kv.get("animations_enabled", true)) + const animationsEnabled = createMemo(() => config.animations ?? true) const list = createMemo(() => props.placeholders?.normal ?? []) const shell = createMemo(() => props.placeholders?.shell ?? []) - const fileContextEnabled = createMemo(() => kv.get("file_context_enabled", true)) + const fileContextEnabled = createMemo(() => config.prompt?.editor ?? true) const [dismissedEditorSelectionKey, setDismissedEditorSelectionKey] = createSignal() const editorContext = createMemo(() => { const selection = fileContextEnabled() ? editor.selection() : undefined @@ -538,7 +536,7 @@ export function Prompt(props: PromptProps) { useBindings(() => ({ mode: OPENCODE_BASE_MODE, - bindings: tuiConfig.keybinds.gather("prompt.palette", [ + bindings: config.keybinds.gather("prompt.palette", [ "prompt.submit", "prompt.editor", "prompt.editor_context.clear", @@ -779,7 +777,7 @@ export function Prompt(props: PromptProps) { return { target: inputTarget, enabled: inputTarget() !== undefined && !props.disabled, - bindings: tuiConfig.keybinds.get("prompt.paste"), + bindings: config.keybinds.get("prompt.paste"), } }) @@ -787,7 +785,7 @@ export function Prompt(props: PromptProps) { return { target: inputTarget, enabled: inputTarget() !== undefined && !props.disabled && store.prompt.text !== "", - bindings: tuiConfig.keybinds.get("prompt.clear"), + bindings: config.keybinds.get("prompt.clear"), } }) @@ -870,7 +868,7 @@ export function Prompt(props: PromptProps) { }, }, ], - bindings: tuiConfig.keybinds.get("prompt.history.previous"), + bindings: config.keybinds.get("prompt.history.previous"), } }) @@ -910,7 +908,7 @@ export function Prompt(props: PromptProps) { }, }, ], - bindings: tuiConfig.keybinds.get("prompt.history.next"), + bindings: config.keybinds.get("prompt.history.next"), } }) @@ -1191,7 +1189,7 @@ export function Prompt(props: PromptProps) { const lineCount = (pastedContent.match(/\n/g)?.length ?? 0) + 1 if ( (lineCount >= 3 || pastedContent.length > 150) && - kv.get("paste_summary_enabled", true) + config.prompt?.paste !== "full" ) { pasteText(pastedContent, `[Pasted ~${lineCount} lines]`) return @@ -1321,7 +1319,7 @@ export function Prompt(props: PromptProps) { }), } }) - const maxHeight = createMemo(() => tuiConfig.prompt?.max_height ?? Math.max(6, Math.floor(dimensions().height / 3))) + const maxHeight = createMemo(() => Math.max(6, Math.floor(dimensions().height / 3))) const moveLabelWidth = createMemo(() => Math.max(12, Math.min(44, dimensions().width - 48))) return ( @@ -1495,7 +1493,7 @@ export function Prompt(props: PromptProps) { - [⋯]}> + [⋯]}> diff --git a/packages/tui/src/component/spinner.tsx b/packages/tui/src/component/spinner.tsx index f648a605a6..5ba9bc3133 100644 --- a/packages/tui/src/component/spinner.tsx +++ b/packages/tui/src/component/spinner.tsx @@ -1,6 +1,6 @@ import { Show } from "solid-js" import { useTheme } from "../context/theme" -import { useKV } from "../context/kv" +import { useConfig } from "../config" import type { JSX } from "@opentui/solid" import type { RGBA } from "@opentui/core" import { registerOpencodeSpinner } from "./register-spinner" @@ -11,10 +11,10 @@ export const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", export function Spinner(props: { children?: JSX.Element; color?: RGBA }) { const { theme } = useTheme() - const kv = useKV() + const config = useConfig().data const color = () => props.color ?? theme.textMuted return ( - ⋯ {props.children}}> + ⋯ {props.children}}> diff --git a/packages/tui/src/config/index.tsx b/packages/tui/src/config/index.tsx new file mode 100644 index 0000000000..ea75ef0699 --- /dev/null +++ b/packages/tui/src/config/index.tsx @@ -0,0 +1,214 @@ +export * as Config from "." + +import { createBindingLookup } from "@opentui/keymap/extras" +import { Schema } from "effect" +import { createContext, type JSX, useContext } from "solid-js" +import { createStore, reconcile } from "solid-js/store" +import { TuiKeybind } from "./keybind" + +export interface Interface { + readonly get: () => Promise + readonly update: (update: (draft: any) => void) => Promise +} + +export const AttentionSoundName = Schema.Literals([ + "default", + "question", + "permission", + "error", + "done", + "subagent_done", +]) +export type AttentionSoundName = Schema.Schema.Type +export type AttentionSoundPaths = Partial> + +export const Plugin = Schema.Union([ + Schema.String, + Schema.Struct({ + package: Schema.String.annotate({ description: "Plugin package name or path" }), + options: Schema.optional(Schema.Record(Schema.String, Schema.Any)).annotate({ + description: "Options passed to the plugin", + }), + }), +]) + +export const Info = Schema.Struct({ + theme: Schema.optional( + Schema.Struct({ + name: Schema.optional(Schema.String).annotate({ description: "Theme name" }), + mode: Schema.optional(Schema.Literals(["system", "dark", "light"])).annotate({ + description: "Color mode; 'system' follows the terminal", + }), + }), + ).annotate({ description: "Color theme settings" }), + keybinds: Schema.optional(TuiKeybind.KeybindOverrides).annotate({ description: "Custom key bindings" }), + plugins: Schema.optional(Schema.Array(Plugin)).annotate({ + description: "Ordered plugin enablement directives and external package declarations", + }), + leader: Schema.optional( + Schema.Struct({ + timeout: Schema.optional(Schema.Int.check(Schema.isGreaterThan(0))).annotate({ + description: "Time in milliseconds to wait for a key after the leader key", + }), + }), + ).annotate({ description: "Leader key behavior" }), + scroll: Schema.optional( + Schema.Struct({ + speed: Schema.optional(Schema.Number.check(Schema.isGreaterThanOrEqualTo(0.001))).annotate({ + description: "Distance scrolled per input tick", + }), + acceleration: Schema.optional(Schema.Boolean).annotate({ + description: "Accelerate scrolling from repeated input", + }), + }), + ).annotate({ description: "Scrolling behavior" }), + attention: Schema.optional( + Schema.Struct({ + enabled: Schema.optional(Schema.Boolean).annotate({ description: "Enable attention alerts" }), + notifications: Schema.optional(Schema.Boolean).annotate({ description: "Show system notifications" }), + sound: Schema.optional(Schema.Boolean).annotate({ description: "Play attention sounds" }), + volume: Schema.optional( + Schema.Number.check(Schema.isGreaterThanOrEqualTo(0), Schema.isLessThanOrEqualTo(1)), + ).annotate({ description: "Attention sound volume from 0 to 1" }), + sound_pack: Schema.optional(Schema.String).annotate({ description: "Active attention sound pack ID" }), + sounds: Schema.optional( + Schema.Record( + AttentionSoundName, + Schema.optionalKey(Schema.String), + ), + ).annotate({ description: "Sound file overrides by attention event" }), + }), + ).annotate({ description: "System notification and sound settings" }), + diffs: Schema.optional( + Schema.Struct({ + wrap: Schema.optional(Schema.Literals(["word", "none"])).annotate({ + description: "Line wrapping behavior in diff output", + }), + tree: Schema.optional(Schema.Boolean).annotate({ description: "Show the diff file tree" }), + single: Schema.optional(Schema.Boolean).annotate({ description: "Show only the selected file patch" }), + view: Schema.optional(Schema.Literals(["auto", "split", "unified"])).annotate({ + description: "Diff layout; 'auto' selects a layout from the available width", + }), + }), + ).annotate({ description: "Diff presentation settings" }), + terminal: Schema.optional( + Schema.Struct({ + title: Schema.optional(Schema.Boolean).annotate({ description: "Update the terminal window title" }), + }), + ).annotate({ description: "Terminal integration settings" }), + prompt: Schema.optional( + Schema.Struct({ + editor: Schema.optional(Schema.Boolean).annotate({ + description: "Include the active editor file or selection as prompt context", + }), + paste: Schema.optional(Schema.Literals(["compact", "full"])).annotate({ + description: "Display large pastes as compact placeholders or full text", + }), + }), + ).annotate({ description: "Prompt input behavior" }), + session: Schema.optional( + Schema.Struct({ + sidebar: Schema.optional(Schema.Literals(["auto", "hide"])).annotate({ + description: "Session sidebar visibility; 'auto' shows it when space permits", + }), + scrollbar: Schema.optional(Schema.Boolean).annotate({ description: "Show the session transcript scrollbar" }), + thinking: Schema.optional(Schema.Literals(["show", "hide"])).annotate({ + description: "Show or hide model reasoning by default", + }), + grouping: Schema.optional(Schema.Literals(["auto", "none"])).annotate({ + description: "Group related transcript items automatically or render each item separately", + }), + markdown: Schema.optional(Schema.Literals(["source", "rendered"])).annotate({ + description: "Show Markdown syntax markers or conceal them in rendered transcript content", + }), + }), + ).annotate({ description: "Session transcript presentation settings" }), + hints: Schema.optional( + Schema.Struct({ + tips: Schema.optional(Schema.Boolean).annotate({ description: "Show usage tips on the home screen" }), + onboarding: Schema.optional(Schema.Boolean).annotate({ description: "Show getting-started guidance" }), + }), + ).annotate({ description: "In-product guidance settings" }), + animations: Schema.optional(Schema.Boolean).annotate({ description: "Enable interface animations" }), + mouse: Schema.optional(Schema.Boolean).annotate({ description: "Enable terminal mouse capture" }), +}) +export type Info = Schema.Schema.Type + +export type Resolved = Omit & { + attention: { + enabled: boolean + notifications: boolean + sound: boolean + volume: number + sound_pack: string + sounds: AttentionSoundPaths + } + keybinds: TuiKeybind.BindingLookupView + leader: { timeout: number } + mouse: boolean +} + +export function resolve(input: Info, options: { terminalSuspend: boolean }): Resolved { + const keybinds: TuiKeybind.KeybindOverrides = { ...input.keybinds } + if (!options.terminalSuspend) { + keybinds.terminal_suspend = "none" + if (keybinds.input_undo === undefined) { + const inputUndo = TuiKeybind.defaultValue("input_undo") + keybinds.input_undo = ["ctrl+z", ...(typeof inputUndo === "string" ? inputUndo.split(",") : [])] + .filter((value, index, values) => values.indexOf(value) === index) + .join(",") + } + } + + return { + ...input, + attention: { + enabled: input.attention?.enabled ?? false, + notifications: input.attention?.notifications ?? true, + sound: input.attention?.sound ?? true, + volume: input.attention?.volume ?? 0.4, + sound_pack: input.attention?.sound_pack ?? "opencode.default", + sounds: input.attention?.sounds ?? {}, + }, + keybinds: createBindingLookup(TuiKeybind.toBindingConfig(TuiKeybind.parse(keybinds)), { + commandMap: TuiKeybind.CommandMap, + bindingDefaults: TuiKeybind.bindingDefaults(), + }), + leader: { timeout: input.leader?.timeout ?? 2000 }, + mouse: input.mouse ?? true, + } +} + +const ConfigContext = createContext<{ + data: Resolved + update: Interface["update"] +}>() + +export function ConfigProvider(props: { + config: Resolved + service?: Interface + options?: { terminalSuspend: boolean } + children: JSX.Element +}) { + const [config, setConfig] = createStore(props.config) + const host = props.service + const update = async (update: (draft: any) => void) => { + if (!host) throw new Error("Config updates are not available") + const info = await host.update(update) + setConfig(reconcile(resolve(info, props.options ?? { terminalSuspend: true }))) + return info + } + return ( + {props.children} + ) +} + +export function useConfig() { + const value = useContext(ConfigContext) + if (!value) throw new Error("ConfigProvider is missing") + return value +} + +export function useConfigOptional() { + return useContext(ConfigContext) +} diff --git a/packages/tui/src/config/keybind.ts b/packages/tui/src/config/keybind.ts new file mode 100644 index 0000000000..475f5120f8 --- /dev/null +++ b/packages/tui/src/config/keybind.ts @@ -0,0 +1,2 @@ +export * from "./v1/keybind" +export * as TuiKeybind from "./v1/keybind" diff --git a/packages/tui/src/config/v1/index.tsx b/packages/tui/src/config/v1/index.tsx index df9239763a..dfa388c3bf 100644 --- a/packages/tui/src/config/v1/index.tsx +++ b/packages/tui/src/config/v1/index.tsx @@ -1,4 +1,5 @@ export * as TuiConfig from "." +export * as TuiConfigV1 from "." import { createBindingLookup } from "@opentui/keymap/extras" import { Schema } from "effect" diff --git a/packages/tui/src/config/v1/keybind.ts b/packages/tui/src/config/v1/keybind.ts index 4b108f5303..18c68a2ba4 100644 --- a/packages/tui/src/config/v1/keybind.ts +++ b/packages/tui/src/config/v1/keybind.ts @@ -53,7 +53,6 @@ export const Definitions = { app_toggle_file_context: keybind("none", "Toggle file context"), app_toggle_diffwrap: keybind("none", "Toggle diff wrapping"), app_toggle_paste_summary: keybind("none", "Toggle paste summary"), - app_toggle_session_directory_filter: keybind("none", "Toggle session directory filtering"), command_list: keybind("ctrl+p", "List available commands"), help_show: keybind("none", "Open help dialog"), docs_open: keybind("none", "Open documentation"), @@ -143,7 +142,6 @@ export const Definitions = { messages_copy: keybind("y", "Copy message"), messages_undo: keybind("u", "Undo message"), messages_redo: keybind("r", "Redo message"), - messages_toggle_conceal: keybind("h", "Toggle code block concealment in messages"), display_thinking: keybind("none", "Toggle thinking blocks visibility"), prompt_submit: keybind("none", "Submit prompt"), @@ -257,7 +255,6 @@ export const CommandMap = { app_toggle_file_context: "app.toggle.file_context", app_toggle_diffwrap: "app.toggle.diffwrap", app_toggle_paste_summary: "app.toggle.paste_summary", - app_toggle_session_directory_filter: "app.toggle.session_directory_filter", command_list: "command.palette.show", help_show: "help.show", docs_open: "docs.open", @@ -343,7 +340,6 @@ export const CommandMap = { messages_copy: "messages.copy", messages_undo: "session.undo", messages_redo: "session.redo", - messages_toggle_conceal: "session.toggle.conceal", display_thinking: "session.toggle.thinking", prompt_submit: "prompt.submit", prompt_editor_context_clear: "prompt.editor_context.clear", diff --git a/packages/tui/src/config/v2/index.ts b/packages/tui/src/config/v2/index.ts deleted file mode 100644 index 23ebc01e56..0000000000 --- a/packages/tui/src/config/v2/index.ts +++ /dev/null @@ -1,97 +0,0 @@ -export * as TuiConfigV2 from "." - -import { Schema } from "effect" -import { TuiKeybind } from "./keybind" - -export const Plugin = Schema.Union([ - Schema.String, - Schema.Struct({ - package: Schema.String, - options: Schema.optional(Schema.Record(Schema.String, Schema.Any)), - }), -]) - -export const Info = Schema.Struct({ - theme: Schema.optional( - Schema.Struct({ - name: Schema.optional(Schema.String), - mode: Schema.optional(Schema.Literals(["system", "dark", "light"])), - }), - ), - keybinds: Schema.optional(TuiKeybind.KeybindOverrides), - plugins: Schema.optional(Schema.Array(Plugin)), - leader: Schema.optional( - Schema.Struct({ - timeout: Schema.optional(Schema.Int.check(Schema.isGreaterThan(0))), - }), - ), - scroll: Schema.optional( - Schema.Struct({ - speed: Schema.optional(Schema.Number.check(Schema.isGreaterThanOrEqualTo(0.001))), - acceleration: Schema.optional(Schema.Boolean), - }), - ), - attention: Schema.optional( - Schema.Struct({ - enabled: Schema.optional(Schema.Boolean), - notifications: Schema.optional(Schema.Boolean), - sound: Schema.optional(Schema.Boolean), - volume: Schema.optional(Schema.Number.check(Schema.isGreaterThanOrEqualTo(0), Schema.isLessThanOrEqualTo(1))), - sound_pack: Schema.optional(Schema.String), - sounds: Schema.optional( - Schema.Record( - Schema.Literals(["default", "question", "permission", "error", "done", "subagent_done"]), - Schema.optionalKey(Schema.String), - ), - ), - }), - ), - diffs: Schema.optional( - Schema.Struct({ - wrap: Schema.optional(Schema.Literals(["word", "none"])), - tree: Schema.optional(Schema.Boolean), - single: Schema.optional(Schema.Boolean), - view: Schema.optional(Schema.Literals(["auto", "split", "unified"])), - }), - ), - terminal: Schema.optional( - Schema.Struct({ - title: Schema.optional(Schema.Boolean), - }), - ), - composer: Schema.optional( - Schema.Struct({ - file_context: Schema.optional(Schema.Boolean), - paste_summary: Schema.optional(Schema.Boolean), - }), - ), - session: Schema.optional( - Schema.Struct({ - sidebar: Schema.optional(Schema.Literals(["auto", "hide"])), - scrollbar: Schema.optional(Schema.Boolean), - thinking: Schema.optional(Schema.Literals(["show", "hide"])), - group_exploration: Schema.optional(Schema.Boolean), - directory_filter: Schema.optional(Schema.Boolean), - }), - ), - which_key: Schema.optional( - Schema.Struct({ - layout: Schema.optional(Schema.Literals(["dock", "overlay"])), - pending_preview: Schema.optional(Schema.Boolean), - }), - ), - hints: Schema.optional( - Schema.Struct({ - tips: Schema.optional(Schema.Boolean), - getting_started: Schema.optional(Schema.Boolean), - }), - ), - updates: Schema.optional( - Schema.Struct({ - skipped: Schema.optional(Schema.String), - }), - ), - animations: Schema.optional(Schema.Boolean), - mouse: Schema.optional(Schema.Boolean), -}) -export type Info = Schema.Schema.Type diff --git a/packages/tui/src/config/v2/keybind.ts b/packages/tui/src/config/v2/keybind.ts deleted file mode 100644 index 5dcd6d4f67..0000000000 --- a/packages/tui/src/config/v2/keybind.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * as TuiKeybind from "./keybind" - -import { Schema } from "effect" - -export const KeybindOverrides = Schema.Struct({}) -export type KeybindOverrides = Schema.Schema.Type diff --git a/packages/tui/src/context/kv.tsx b/packages/tui/src/context/kv.tsx deleted file mode 100644 index 7b90c95f59..0000000000 --- a/packages/tui/src/context/kv.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { createSignal, type Setter } from "solid-js" -import { createStore, unwrap } from "solid-js/store" -import { createSimpleContext } from "./helper" -import { Flock } from "@opencode-ai/core/util/flock" -import { Global } from "@opencode-ai/core/global" -import { readJson, writeJsonAtomic } from "../util/persistence" -import { useTuiPaths } from "./runtime" -import path from "path" - -export const { use: useKV, provider: KVProvider } = createSimpleContext({ - name: "KV", - init: () => { - const paths = useTuiPaths() - void Global.Path.state - const file = path.join(paths.state, "kv.json") - const lock = `tui-kv:${file}` - const [ready, setReady] = createSignal(false) - const [store, setStore] = createStore>() - // Queue same-process writes so rapid updates persist in order. - let write = Promise.resolve() - - Flock.withLock(lock, () => readJson>(file)) - .then((x) => { - setStore(x) - }) - .catch((error) => { - console.error("Failed to read KV state", { error }) - }) - .finally(() => { - setReady(true) - }) - - const result = { - get ready() { - return ready() - }, - get store() { - return store - }, - signal(name: string, defaultValue: T) { - if (store[name] === undefined) setStore(name, defaultValue) - return [ - function () { - return result.get(name) - }, - function setter(next: Setter) { - result.set(name, next) - }, - ] as const - }, - get(key: string, defaultValue?: any) { - return store[key] ?? defaultValue - }, - set(key: string, value: any) { - setStore(key, value) - const snapshot = structuredClone(unwrap(store)) - write = write - .then(() => Flock.withLock(lock, () => writeJsonAtomic(file, snapshot))) - .catch((error) => { - console.error("Failed to write KV state", { error }) - }) - }, - } - return result - }, -}) diff --git a/packages/tui/src/context/sync.tsx b/packages/tui/src/context/sync.tsx deleted file mode 100644 index f0133f85c0..0000000000 --- a/packages/tui/src/context/sync.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import type { - Agent, - Command, - Config, - FormatterStatus, - LspStatus, - McpResource, - McpStatus, - Message, - Part, - PermissionRequest, - Provider, - QuestionRequest, - Session, - FileDiffInfo, - VcsInfo, -} from "@opencode-ai/sdk/v2" -import { createStore } from "solid-js/store" -import { createSimpleContext } from "./helper" -import { useProject } from "./project" - -export const { - context: SyncContext, - use: useSync, - provider: SyncProvider, -} = createSimpleContext({ - name: "Sync", - init: () => { - const project = useProject() - const [store, setStore] = createStore<{ - status: "loading" | "partial" | "complete" - provider: Provider[] - agent: Agent[] - command: Command[] - permission: Record - question: Record - config: Config - session: Session[] - session_diff: Record - message: Record - part: Record - lsp: LspStatus[] - mcp: Record - mcp_resource: Record - formatter: FormatterStatus[] - vcs: VcsInfo | undefined - }>({ - status: "complete", - provider: [], - agent: [], - command: [], - permission: {}, - question: {}, - config: {}, - session: [], - session_diff: {}, - message: {}, - part: {}, - lsp: [], - mcp: {}, - mcp_resource: {}, - formatter: [], - vcs: undefined, - }) - - return { - data: store, - set: setStore, - get status() { - return store.status - }, - get ready() { - return true - }, - get path() { - return project.instance.path() - }, - session: { - get(_sessionID: string) { - return undefined as Session | undefined - }, - query() { - return {} as { scope?: "project"; path?: string } - }, - async refresh() {}, - status(_sessionID: string) { - return "idle" as const - }, - async sync(_sessionID: string) {}, - }, - async bootstrap(_input: { fatal?: boolean } = {}) {}, - } - }, -}) diff --git a/packages/tui/src/context/theme.tsx b/packages/tui/src/context/theme.tsx index 79456b8498..8c4ab4de9f 100644 --- a/packages/tui/src/context/theme.tsx +++ b/packages/tui/src/context/theme.tsx @@ -22,8 +22,7 @@ import { import { createEffect, createMemo, onCleanup, onMount } from "solid-js" import { createStore, produce } from "solid-js/store" import { createSimpleContext } from "./helper" -import { useKV } from "./kv" -import { useTuiConfig } from "../config/v1" +import { useConfig } from "../config" import { Global } from "@opencode-ai/core/global" import { Glob } from "@opencode-ai/core/util/glob" import { readFile } from "node:fs/promises" @@ -103,8 +102,8 @@ export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({ name: "Theme", init: (props: { mode: "dark" | "light"; source?: ThemeSource }) => { const renderer = useRenderer() - const config = useTuiConfig() - const kv = useKV() + const configState = useConfig() + const config = configState.data const themes = props.source ?? themeSource const pick = (value: unknown) => { if (value === "dark" || value === "light") return value @@ -113,22 +112,30 @@ export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({ setStore( produce((draft) => { - const lock = pick(kv.get("theme_mode_lock")) + const lock = pick(config.theme?.mode) const mode = lock ?? pick(renderer.themeMode) ?? props.mode - if (!lock && pick(kv.get("theme_mode")) !== undefined) kv.set("theme_mode", undefined) draft.mode = mode draft.lock = lock - const active = config.theme ?? kv.get("theme", "opencode") + const active = config.theme?.name ?? "opencode" draft.active = typeof active === "string" ? active : "opencode" draft.ready = false }), ) createEffect(() => { - const theme = config.theme + const theme = config.theme?.name if (theme) setStore("active", theme) }) + createEffect(() => { + const mode = config.theme?.mode + if (mode === "dark" || mode === "light") { + pin(mode, false) + return + } + if (mode === "system" && store.lock !== undefined) free(false) + }) + function syncCustomThemes() { return themes .discover() @@ -200,23 +207,31 @@ export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({ } function apply(mode: "dark" | "light") { - if (store.lock !== undefined) kv.set("theme_mode", mode) if (store.mode === mode) return setStore("mode", mode) refreshSystemTheme(mode) } - function pin(mode: "dark" | "light" = store.mode) { + function pin(mode: "dark" | "light" = store.mode, persist = true) { setStore("lock", mode) - kv.set("theme_mode_lock", mode) apply(mode) + if (!persist) return + void configState + .update((draft) => { + draft.theme = { ...draft.theme, mode } + }) + .catch(() => {}) } - function free() { + function free(persist = true) { setStore("lock", undefined) - kv.set("theme_mode_lock", undefined) - kv.set("theme_mode", undefined) refreshSystemTheme(renderer.themeMode ?? store.mode) + if (!persist) return + void configState + .update((draft) => { + draft.theme = { ...draft.theme, mode: "system" } + }) + .catch(() => {}) } const handle = (mode: "dark" | "light") => { @@ -256,13 +271,6 @@ export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({ const values = createMemo(() => { const active = store.themes[store.active] if (active) return resolveTheme(active, store.mode) - - const saved = kv.get("theme") - if (typeof saved === "string") { - const theme = store.themes[saved] - if (theme) return resolveTheme(theme, store.mode) - } - return resolveTheme(store.themes.opencode, store.mode) }) @@ -293,7 +301,11 @@ export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({ set(theme: string) { if (!hasTheme(theme)) return false setStore("active", theme) - kv.set("theme", theme) + void configState + .update((draft) => { + draft.theme = { ...draft.theme, name: theme } + }) + .catch(() => {}) return true }, get ready() { diff --git a/packages/tui/src/context/thinking.ts b/packages/tui/src/context/thinking.ts index bb1c2a6929..3d630fb62a 100644 --- a/packages/tui/src/context/thinking.ts +++ b/packages/tui/src/context/thinking.ts @@ -1,6 +1,3 @@ -import { createMemo, type Setter } from "solid-js" -import { useKV } from "./kv" - export type ThinkingMode = "show" | "hide" const MODES: readonly ThinkingMode[] = ["show", "hide"] as const @@ -16,52 +13,8 @@ export function reasoningSummary(text: string) { return { title: match[1].trim(), body: content.slice(match[0].length).trimEnd() } } -export function isThinkingMode(value: unknown): value is ThinkingMode { - return typeof value === "string" && (MODES as readonly string[]).includes(value) -} - // Cycle order matches the slash command: show → hide → show. export function nextThinkingMode(current: ThinkingMode): ThinkingMode { const idx = MODES.indexOf(current) return MODES[(idx + 1) % MODES.length] ?? "show" } - -export function useThinkingMode() { - const kv = useKV() - // Capture pre-state before `kv.signal` seeds a default, so we can detect - // first-time users with a legacy `thinking_visibility` boolean and migrate. - // The KVProvider only renders children once kv.ready, so reads here are safe. - const hadStored = kv.get("thinking_mode") !== undefined - const legacy = kv.get("thinking_visibility") - const [stored, setStored] = kv.signal("thinking_mode", "hide") - - // The kv signal exposes its setter typed as `Setter` which carries Solid's - // overload set; passing an updater fn through a property access loses the - // bivariance trick the existing `setX((prev) => ...)` callsites rely on. - // Wrap it in a sane shape so consumers can just call `set(next)` or pass - // an updater. - const set = (next: ThinkingMode | ((prev: ThinkingMode) => ThinkingMode)) => { - if (typeof next === "function") setStored(next as Setter) - else setStored(() => next) - } - - // Preserve previous experience for users who had explicitly toggled the - // legacy `thinking_visibility` boolean. First-time users (no legacy key) - // get the new "hide" default (collapsed thinking). - if (!hadStored) { - if (legacy === true) set("show") - else if (legacy === false) set("hide") - } - - if ((stored() as string) === "minimal") set("hide") - - const mode = createMemo(() => { - const value = stored() - return isThinkingMode(value) ? value : "hide" - }) - - return { - mode, - set, - } -} diff --git a/packages/tui/src/feature-plugins/home/tips-view.tsx b/packages/tui/src/feature-plugins/home/tips-view.tsx index 867d292350..92623cde29 100644 --- a/packages/tui/src/feature-plugins/home/tips-view.tsx +++ b/packages/tui/src/feature-plugins/home/tips-view.tsx @@ -25,7 +25,6 @@ type Shortcuts = { messagesLast: TipShortcut messagesPageDown: TipShortcut messagesPageUp: TipShortcut - messagesToggleConceal: TipShortcut modelCycleRecent: TipShortcut modelList: TipShortcut sessionExport: TipShortcut @@ -115,7 +114,6 @@ export function Tips(props: { api: TuiPluginApi; connected?: boolean }) { messagesLast: configShortcut(props.api, "session.last"), messagesPageDown: configShortcut(props.api, "session.page.down"), messagesPageUp: configShortcut(props.api, "session.page.up"), - messagesToggleConceal: configShortcut(props.api, "session.toggle.conceal"), modelCycleRecent: useCommandShortcut("model.cycle_recent"), modelList: useCommandShortcut("model.list"), sessionExport: configShortcut(props.api, "session.export"), @@ -267,9 +265,8 @@ const TIPS: Tip[] = [ "Run {highlight}opencode debug config{/highlight} to troubleshoot configuration", "Use {highlight}--print-logs{/highlight} flag to see detailed logs in stderr", (shortcuts) => `Use ${commandText("/timeline", shortcuts.sessionTimeline())} to jump to specific messages`, - (shortcuts) => press(shortcuts.messagesToggleConceal(), "to toggle code block visibility in messages"), (shortcuts) => `Use ${commandText("/status", shortcuts.statusView())} to see system status info`, - "Enable {highlight}scroll_acceleration{/highlight} in {highlight}tui.json{/highlight} for smooth scrolling", + "Enable {highlight}scroll.acceleration{/highlight} in {highlight}cli.json{/highlight} for smooth scrolling", (shortcuts) => shortcuts.commandList() ? `Toggle username display in chat via the command palette (${shortcutText(shortcuts.commandList())})` diff --git a/packages/tui/src/feature-plugins/home/tips.tsx b/packages/tui/src/feature-plugins/home/tips.tsx index 7b67d4aac5..2c516b8f32 100644 --- a/packages/tui/src/feature-plugins/home/tips.tsx +++ b/packages/tui/src/feature-plugins/home/tips.tsx @@ -5,10 +5,12 @@ import { Tips } from "./tips-view" import { useBindings } from "../../keymap" import { useData } from "../../context/data" import { hasConnectedProvider } from "../../util/connected-provider" +import { useConfig } from "../../config" const id = "internal:home-tips" function View(props: { api: TuiPluginApi; hidden: boolean; show: boolean; connected: boolean }) { + const config = useConfig() useBindings(() => ({ commands: [ { @@ -16,8 +18,13 @@ function View(props: { api: TuiPluginApi; hidden: boolean; show: boolean; connec title: props.hidden ? "Show tips" : "Hide tips", category: "System", namespace: "palette", + hidden: true, run() { - props.api.kv.set("tips_hidden", !props.api.kv.get("tips_hidden", false)) + void config + .update((draft) => { + draft.hints = { ...draft.hints, tips: props.hidden } + }) + .catch(() => {}) props.api.ui.dialog.clear() }, }, @@ -40,7 +47,8 @@ const tui: TuiPlugin = async (api) => { slots: { home_bottom() { const data = useData() - const hidden = createMemo(() => api.kv.get("tips_hidden", false)) + const config = useConfig().data + const hidden = createMemo(() => !(config.hints?.tips ?? true)) const first = createMemo(() => api.state.session.count() === 0) const connected = createMemo(() => hasConnectedProvider(data.location.integration.list() ?? [])) const show = createMemo(() => (!first() || !connected()) && !hidden()) diff --git a/packages/tui/src/feature-plugins/sidebar/footer.tsx b/packages/tui/src/feature-plugins/sidebar/footer.tsx index b0fb681153..2cb81e4cc6 100644 --- a/packages/tui/src/feature-plugins/sidebar/footer.tsx +++ b/packages/tui/src/feature-plugins/sidebar/footer.tsx @@ -4,18 +4,20 @@ import { createMemo, Show } from "solid-js" import { abbreviateHome } from "../../runtime" import { useTuiPaths } from "../../context/runtime" import { FilePath } from "../../ui/file-path" +import { useConfig } from "../../config" const id = "internal:sidebar-footer" function View(props: { api: TuiPluginApi; directory: string }) { const paths = useTuiPaths() + const config = useConfig() const theme = () => props.api.theme.current const has = createMemo(() => props.api.state.provider.some( (item) => item.id !== "opencode" || Object.values(item.models).some((model) => model.cost?.input !== 0), ), ) - const done = createMemo(() => props.api.kv.get("dismissed_getting_started", false)) + const done = createMemo(() => !(config.data.hints?.onboarding ?? true)) const show = createMemo(() => !has() && !done()) const location = createMemo(() => { const branch = props.directory === props.api.state.path.directory ? props.api.state.vcs?.branch : undefined @@ -44,7 +46,16 @@ function View(props: { api: TuiPluginApi; directory: string }) { Getting started - props.api.kv.set("dismissed_getting_started", true)}> + + void config + .update((draft) => { + draft.hints = { ...draft.hints, onboarding: false } + }) + .catch(() => {}) + } + > ✕ diff --git a/packages/tui/src/feature-plugins/system/diff-viewer.tsx b/packages/tui/src/feature-plugins/system/diff-viewer.tsx index d5e1e56e7b..6444a86950 100644 --- a/packages/tui/src/feature-plugins/system/diff-viewer.tsx +++ b/packages/tui/src/feature-plugins/system/diff-viewer.tsx @@ -19,6 +19,7 @@ import { DiffViewerFileTree } from "./diff-viewer-file-tree" import { Panel, PanelGroup, Separator } from "./diff-viewer-ui" import { DialogSelect } from "../../ui/dialog-select" import { getScrollAcceleration } from "../../util/scroll" +import { useConfig } from "../../config" import { allExpandedFileTreeDirectories, buildFileTree, @@ -41,9 +42,6 @@ const MIN_SPLIT_WIDTH = 100 const FILE_TREE_WIDTH = 32 const PLAIN_TEXT_FILETYPE = "opencode-plain-text" const VCS_DIFF_CONTEXT_LINES = 12 -const KV_SHOW_FILE_TREE = "diff_viewer_show_file_tree" -const KV_SINGLE_PATCH = "diff_viewer_single_patch" -const KV_VIEW = "diff_viewer_view" type DiffMode = "working" | "branch" | "last-turn" type DiffViewerFocus = "patches" | "files" type DiffView = "split" | "unified" @@ -92,6 +90,7 @@ function diffSourceLabel(mode: DiffMode) { function DiffViewer(props: { api: TuiPluginApi }) { const dimensions = useTerminalDimensions() const sdk = useSDK() + const config = useConfig() const themeState = useTheme() const theme = () => props.api.theme.current const params = () => @@ -135,19 +134,18 @@ function DiffViewer(props: { api: TuiPluginApi }) { }) const files = createMemo(() => diff() ?? []) const [focus, setFocus] = createSignal("patches") - const [fileTreeEnabled, setFileTreeEnabled] = createSignal( - props.api.kv.get(KV_SHOW_FILE_TREE, true) !== false, - ) + const [fileTreeEnabled, setFileTreeEnabled] = createSignal(config.data.diffs?.tree ?? true) const showFileTree = createMemo(() => showDiffViewerFileTree(fileTreeEnabled(), files().length)) - const [singlePatch, setSinglePatch] = createSignal(props.api.kv.get(KV_SINGLE_PATCH, false) === true) + const [singlePatch, setSinglePatch] = createSignal(config.data.diffs?.single ?? false) const patchPaneWidth = createMemo(() => dimensions().width - (showFileTree() ? 33 : 0) - 4) const patchLeftBorder = createMemo(() => (showFileTree() ? ["left"] : [])) const splitAvailable = createMemo(() => patchPaneWidth() >= MIN_SPLIT_WIDTH) const defaultView = createMemo(() => { - if (props.api.tuiConfig.diff_style === "stacked") return "unified" + if (props.api.tuiConfig.diffs?.view === "unified") return "unified" + if (props.api.tuiConfig.diffs?.view === "split") return "split" return splitAvailable() ? "split" : "unified" }) - const [viewOverride, setViewOverride] = createSignal(storedView(props.api.kv.get(KV_VIEW))) + const [viewOverride, setViewOverride] = createSignal(storedView(config.data.diffs?.view)) const view = createMemo(() => (splitAvailable() ? (viewOverride() ?? defaultView()) : "unified")) const fileTree = createMemo(() => buildFileTree(files())) const [expandedFileNodes, setExpandedFileNodes] = createSignal>(new Set()) @@ -622,23 +620,33 @@ function DiffViewer(props: { api: TuiPluginApi }) { name: "diff.toggle_file_tree", title: "Toggle diff viewer file tree", category: "VCS", + hidden: true, run() { const next = !fileTreeEnabled() if (!next) setFocus("patches") setFileTreeEnabled(next) - props.api.kv.set(KV_SHOW_FILE_TREE, next) + void config + .update((draft) => { + draft.diffs = { ...draft.diffs, tree: next } + }) + .catch(() => {}) }, }, { name: "diff.single_patch", title: "Toggle single patch view", category: "VCS", + hidden: true, run() { setSelectedHunk(undefined) if (!singlePatch()) { ensureHighlightedPatchFile() setSinglePatch(true) - props.api.kv.set(KV_SINGLE_PATCH, true) + void config + .update((draft) => { + draft.diffs = { ...draft.diffs, single: true } + }) + .catch(() => {}) scrollSinglePatchToTop() return } @@ -652,7 +660,11 @@ function DiffViewer(props: { api: TuiPluginApi }) { ) if (fileIndex !== undefined) selectPatchFile(fileIndex) setSinglePatch(false) - props.api.kv.set(KV_SINGLE_PATCH, false) + void config + .update((draft) => { + draft.diffs = { ...draft.diffs, single: false } + }) + .catch(() => {}) if (fileIndex !== undefined) scrollToPatchFileIndexAfterRender(fileIndex) }, }, @@ -668,12 +680,17 @@ function DiffViewer(props: { api: TuiPluginApi }) { name: "diff.toggle_view", title: "Toggle diff viewer split or unified view", category: "VCS", + hidden: true, run() { if (!splitAvailable()) return setSelectedHunk(undefined) const next = view() === "split" ? "unified" : "split" setViewOverride(next) - props.api.kv.set(KV_VIEW, next) + void config + .update((draft) => { + draft.diffs = { ...draft.diffs, view: next } + }) + .catch(() => {}) }, }, { diff --git a/packages/tui/src/feature-plugins/system/plugins.tsx b/packages/tui/src/feature-plugins/system/plugins.tsx index 78611e034b..2e73f5f28d 100644 --- a/packages/tui/src/feature-plugins/system/plugins.tsx +++ b/packages/tui/src/feature-plugins/system/plugins.tsx @@ -217,6 +217,7 @@ function View(props: { api: TuiPluginApi }) { { title: "install", command: "dialog.plugins.install", + selection: "none", hidden: lock(), onTrigger: () => { showInstall(props.api) diff --git a/packages/tui/src/feature-plugins/system/which-key.tsx b/packages/tui/src/feature-plugins/system/which-key.tsx index d16dfc28d2..248f49ff29 100644 --- a/packages/tui/src/feature-plugins/system/which-key.tsx +++ b/packages/tui/src/feature-plugins/system/which-key.tsx @@ -22,8 +22,6 @@ const command = { } as const const LAYER_PRIORITY = 900 -const KV_LAYOUT = "which_key_layout" -const KV_PENDING_PREVIEW = "which_key_pending_preview" const toggleCommands = [command.toggle, command.toggleLayout, command.togglePending] as const const scrollCommands = [ command.scrollUp, @@ -531,8 +529,8 @@ function WhichKeyPanel(props: { const tui: TuiPlugin = async (api) => { const [pinned, setPinned] = createSignal(false) - const [mode, setMode] = createSignal(layout(api.kv.get(KV_LAYOUT, "dock"))) - const [pendingPreview, setPendingPreview] = createSignal(api.kv.get(KV_PENDING_PREVIEW, false)) + const [mode, setMode] = createSignal(layout("dock")) + const [pendingPreview, setPendingPreview] = createSignal(false) api.keymap.registerLayer({ priority: LAYER_PRIORITY, @@ -554,7 +552,6 @@ const tui: TuiPlugin = async (api) => { run() { setMode((value) => { const next = value === "dock" ? "overlay" : "dock" - api.kv.set(KV_LAYOUT, next) return next }) }, @@ -566,7 +563,6 @@ const tui: TuiPlugin = async (api) => { category: "System", run() { setPendingPreview((value) => { - api.kv.set(KV_PENDING_PREVIEW, !value) return !value }) }, diff --git a/packages/tui/src/keymap.tsx b/packages/tui/src/keymap.tsx index e4dc694935..71286dbe02 100644 --- a/packages/tui/src/keymap.tsx +++ b/packages/tui/src/keymap.tsx @@ -14,8 +14,8 @@ import { } from "@opentui/keymap/extras" import { KeymapProvider, useKeymap, useKeymapSelector, useBindings } from "@opentui/keymap/solid" import { createMemo, type Accessor } from "solid-js" -import { useTuiConfig } from "./config/v1" -import { TuiKeybind } from "./config/v1/keybind" +import { useConfig } from "./config" +import { TuiKeybind } from "./config/keybind" export const LEADER_TOKEN = "leader" export const OPENCODE_BASE_MODE = "base" @@ -42,7 +42,7 @@ type BindingLookup = { gather(name: string, commands: readonly string[]): readonly Binding[] } type FormatConfig = { keybinds: BindingLookup } -type ResolvedKeymapConfig = FormatConfig & { leader_timeout: number } +type ResolvedKeymapConfig = FormatConfig & ({ leader: { timeout: number } } | { leader_timeout: number }) const modeStacks = new WeakMap() @@ -225,7 +225,7 @@ export function registerOpencodeKeymap(keymap: OpenTuiKeymap, renderer: CliRende ? registerTimedLeader(keymap, { trigger: leader, name: LEADER_TOKEN, - timeoutMs: config.leader_timeout, + timeoutMs: "leader" in config ? config.leader.timeout : config.leader_timeout, }) : () => {} const offEscape = registerEscapeClearsPendingSequence(keymap) @@ -252,7 +252,7 @@ export function useLeaderActive(): Accessor { } export function useCommandShortcut(command: string): Accessor { - const config = useTuiConfig() + const config = useConfig().data return useKeymapSelector((keymap: OpenTuiKeymap) => formatKeySequence( keymap.getCommandBindings({ visibility: "registered", commands: [command] }).get(command)?.[0]?.sequence, diff --git a/packages/tui/src/plugin/adapters.tsx b/packages/tui/src/plugin/adapters.tsx index 088557e931..4c27c7f9b4 100644 --- a/packages/tui/src/plugin/adapters.tsx +++ b/packages/tui/src/plugin/adapters.tsx @@ -1,14 +1,13 @@ import type { TuiDialogSelectOption, TuiPluginApi, TuiSlotProps } from "@opencode-ai/plugin/tui" -import type { TuiConfig } from "../config/v1" +import type { Config } from "../config" import type { useEvent } from "../context/event" import type { useRoute } from "../context/route" import type { useSDK } from "../context/sdk" -import type { useSync } from "../context/sync" import type { useData } from "../context/data" +import type { useProject } from "../context/project" import type { useTheme } from "../context/theme" import { Dialog as DialogUI, type useDialog } from "../ui/dialog" import type { useOpencodeKeymap } from "../keymap" -import type { useKV } from "../context/kv" import { DialogAlert } from "../ui/dialog-alert" import { DialogConfirm } from "../ui/dialog-confirm" import { DialogPrompt } from "../ui/dialog-prompt" @@ -23,15 +22,14 @@ export { createPluginRoutes, createTuiApi } from "./api" type Input = { version: string - tuiConfig: TuiConfig.Resolved + tuiConfig: Config.Resolved dialog: ReturnType keymap: ReturnType - kv: ReturnType route: ReturnType routes: PluginRoutes event: ReturnType sdk: ReturnType - sync: ReturnType + project: ReturnType data: ReturnType theme: ReturnType toast: ReturnType @@ -97,57 +95,51 @@ function mapOptionCb(cb?: (item: TuiDialogSelectOption) => void) { return (item: SelectOption) => cb(pickOption(item)) } -function stateApi(sync: ReturnType, data: ReturnType): TuiPluginApi["state"] { +function stateApi(project: ReturnType, data: ReturnType): TuiPluginApi["state"] { return { get ready() { return true }, get config() { - return sync.data.config + return {} }, get provider() { - return sync.data.provider + return [] }, get path() { - return sync.path + return project.instance.path() }, get vcs() { - if (!sync.data.vcs) return - return { - branch: sync.data.vcs.branch, - default_branch: sync.data.vcs.default_branch, - } + return undefined }, session: { count() { return data.session.list().length }, - get(sessionID) { - return sync.session.get(sessionID) + get(_sessionID) { + return undefined }, - diff(sessionID) { - return (sync.data.session_diff[sessionID] ?? []).flatMap((item) => - item.file === undefined ? [] : [{ ...item, file: item.file }], - ) + diff(_sessionID) { + return [] }, - messages(sessionID) { - return sync.data.message[sessionID] ?? [] + messages(_sessionID) { + return [] }, status(sessionID) { return data.session.status(sessionID) === "running" ? { type: "busy" } : { type: "idle" } }, - permission(sessionID) { - return sync.data.permission[sessionID] ?? [] + permission(_sessionID) { + return [] }, - question(sessionID) { - return sync.data.question[sessionID] ?? [] + question(_sessionID) { + return [] }, }, - part(messageID) { - return sync.data.part[messageID] ?? [] + part(_messageID) { + return [] }, lsp() { - return sync.data.lsp.map((item) => ({ id: item.id, root: item.root, status: item.status })) + return [] }, mcp() { return (data.location.mcp.server.list() ?? []) @@ -292,17 +284,14 @@ export function createTuiApiAdapters(input: Input): Omit export type TuiPluginHost = { start(input: { api: TuiPluginApi - config: TuiConfig.Resolved runtime: PluginRuntime dispose?: () => void }): Promise diff --git a/packages/tui/src/routes/home.tsx b/packages/tui/src/routes/home.tsx index e6fc75dfb3..623772fcb6 100644 --- a/packages/tui/src/routes/home.tsx +++ b/packages/tui/src/routes/home.tsx @@ -8,8 +8,6 @@ import { usePromptRef } from "../context/prompt" import { useLocal } from "../context/local" import { usePluginRuntime } from "../plugin/runtime" import { useEditorContext } from "../context/editor" -import { useTerminalDimensions } from "@opentui/solid" -import { useTuiConfig } from "../config/v1" import { HomeSessionDestinationProvider } from "./home/session-destination" import { useData } from "../context/data" import { LocationProvider } from "../context/location" @@ -29,16 +27,9 @@ export function Home() { const args = useArgs() const local = useLocal() const editor = useEditorContext() - const dimensions = useTerminalDimensions() - const tuiConfig = useTuiConfig() const data = useData() // Global MCP elicitations can arrive without a session route, so keep them reachable from Home. const forms = createMemo(() => data.session.form.list("global", data.location.default()) ?? []) - const promptMaxWidth = createMemo(() => { - const configured = tuiConfig.prompt?.max_width - if (configured === "auto") return Math.max(75, Math.floor(dimensions().width * 0.7)) - return configured ?? 75 - }) let sent = false onMount(() => { @@ -83,7 +74,7 @@ export function Home() { - + void +}) { const data = useData() const clipboard = useClipboard() const toast = useToast() @@ -22,9 +27,26 @@ export function DialogMessage(props: { messageID: string; sessionID: string }) { title: "Revert", value: "session.revert", description: "undo messages and file changes", - onSelect: async (dialog) => { - await sdk.api.session - .revert.stage({ sessionID: props.sessionID, messageID: props.messageID }) + onSelect: (dialog) => { + const value = message() + if (value?.type === "user") { + props.setPrompt?.({ + text: value.text, + files: value.files?.map((file) => ({ + uri: file.source.type === "uri" ? file.source.uri : `data:${file.mime};base64,${file.data}`, + name: file.name, + description: file.description, + mention: file.mention ? { ...file.mention } : undefined, + })), + agents: value.agents?.map((agent) => ({ + name: agent.name, + mention: agent.mention ? { ...agent.mention } : undefined, + })), + pasted: [], + }) + } + void sdk.api.session.revert + .stage({ sessionID: props.sessionID, messageID: props.messageID }) .catch((error) => toast.show({ message: errorMessage(error), variant: "error", duration: 5000 })) dialog.clear() }, diff --git a/packages/tui/src/routes/session/form.tsx b/packages/tui/src/routes/session/form.tsx index 90f8a75d6e..d09673d775 100644 --- a/packages/tui/src/routes/session/form.tsx +++ b/packages/tui/src/routes/session/form.tsx @@ -10,7 +10,7 @@ import { useSDK } from "../../context/sdk" import { useClipboard } from "../../context/clipboard" import { SplitBorder } from "../../ui/border" import { useToast } from "../../ui/toast" -import { useTuiConfig } from "../../config/v1" +import { useConfig } from "../../config" import { useBindings, useOpencodeModeStack } from "../../keymap" const FORM_MODE = "form" @@ -149,7 +149,7 @@ export function FormPrompt(props: { form: FormWithLocation }) { const { theme } = useTheme() const renderer = useRenderer() const dimensions = useTerminalDimensions() - const tuiConfig = useTuiConfig() + const config = useConfig().data const modeStack = useOpencodeModeStack() const clipboard = useClipboard() const toast = useToast() @@ -590,7 +590,7 @@ export function FormPrompt(props: { form: FormWithLocation }) { setStore("editing", false) }, }, - ...tuiConfig.keybinds.get("prompt.clear"), + ...config.keybinds.get("prompt.clear"), { key: "tab", desc: "Next field", @@ -691,7 +691,7 @@ export function FormPrompt(props: { form: FormWithLocation }) { }, { key: "c", desc: "Copy link", group: "Form", cmd: copyExternal }, { key: "escape", desc: "Dismiss form", group: "Form", cmd: cancel }, - ...tuiConfig.keybinds.get("app.exit"), + ...config.keybinds.get("app.exit"), ] : confirm() ? [ @@ -711,7 +711,7 @@ export function FormPrompt(props: { form: FormWithLocation }) { { key: "k", desc: "Scroll review", group: "Form", cmd: () => review?.scrollBy(-1) }, { key: "down", desc: "Scroll review", group: "Form", cmd: () => review?.scrollBy(1) }, { key: "j", desc: "Scroll review", group: "Form", cmd: () => review?.scrollBy(1) }, - ...tuiConfig.keybinds.get("app.exit"), + ...config.keybinds.get("app.exit"), ] : [ ...Array.from({ length: max }, (_, index) => ({ @@ -754,7 +754,7 @@ export function FormPrompt(props: { form: FormWithLocation }) { group: "Form", cmd: cancel, }, - ...tuiConfig.keybinds.get("app.exit"), + ...config.keybinds.get("app.exit"), ]), ], } diff --git a/packages/tui/src/routes/session/index.tsx b/packages/tui/src/routes/session/index.tsx index 5e6fc8eaf3..45fdded0ad 100644 --- a/packages/tui/src/routes/session/index.tsx +++ b/packages/tui/src/routes/session/index.tsx @@ -54,7 +54,6 @@ import { filetype } from "../../util/filetype" import parsers from "../../parsers-config" import { errorMessage } from "../../util/error" import { Toast, useToast } from "../../ui/toast" -import { useKV } from "../../context/kv.tsx" import stripAnsi from "strip-ansi" import { usePromptRef } from "../../context/prompt" import { useEpilogue } from "../../context/epilogue" @@ -64,9 +63,9 @@ import { FormPrompt } from "./form" import { DialogExportOptions } from "../../ui/dialog-export-options" import { DialogExportResult } from "../../ui/dialog-export-result" import { sessionEpilogue } from "../../util/presentation" -import { useTuiConfig } from "../../config/v1" +import { useConfig } from "../../config" import { useClipboard } from "../../context/clipboard" -import { nextThinkingMode, reasoningSummary, useThinkingMode, type ThinkingMode } from "../../context/thinking" +import { nextThinkingMode, reasoningSummary, type ThinkingMode } from "../../context/thinking" import { getScrollAcceleration } from "../../util/scroll" import { collapseToolOutput } from "../../util/collapse-tool-output" import { usePluginRuntime } from "../../plugin/runtime" @@ -88,7 +87,6 @@ const sessionBindingCommands = [ "session.undo", "session.redo", "session.sidebar.toggle", - "session.toggle.conceal", "session.toggle.thinking", "session.toggle.scrollbar", "session.toggle.exploration_grouping", @@ -121,13 +119,13 @@ const sessionGlobalUnfocusedBindingCommands = ["session.first", "session.last"] const context = createContext<{ width: number sessionID: string - conceal: () => boolean thinkingMode: () => ThinkingMode showThinking: () => boolean + markdownMode: () => "source" | "rendered" groupExploration: () => boolean diffWrapMode: () => "word" | "none" models: () => ModelInfo[] - tui: ReturnType + config: ReturnType["data"] }>() function use() { @@ -149,8 +147,8 @@ export function Session() { const data = useData() const project = useProject() const paths = useTuiPaths() - const tuiConfig = useTuiConfig() - const kv = useKV() + const configState = useConfig() + const config = configState.data const { theme } = useTheme() const promptRef = usePromptRef() const session = createMemo(() => data.session.get(route.sessionID)) @@ -196,16 +194,14 @@ export function Session() { }) const dimensions = useTerminalDimensions() - const [sidebar, setSidebar] = kv.signal<"auto" | "hide">("sidebar", "auto") + const sidebar = createMemo(() => config.session?.sidebar ?? "auto") const [sidebarOpen, setSidebarOpen] = createSignal(false) - const [conceal, setConceal] = createSignal(true) - const thinking = useThinkingMode() - const thinkingMode = thinking.mode + const thinkingMode = createMemo(() => config.session?.thinking ?? "hide") const showThinking = createMemo(() => true) - const [showScrollbar, setShowScrollbar] = kv.signal("scrollbar_visible", false) - const [diffWrapMode] = kv.signal<"word" | "none">("diff_wrap_mode", "word") - const [_animationsEnabled, _setAnimationsEnabled] = kv.signal("animations_enabled", true) - const [groupExploration, setGroupExploration] = kv.signal("exploration_grouping", true) + const showScrollbar = createMemo(() => config.session?.scrollbar ?? false) + const markdownMode = createMemo(() => config.session?.markdown ?? "rendered") + const diffWrapMode = createMemo(() => config.diffs?.wrap ?? "word") + const groupExploration = createMemo(() => config.session?.grouping !== "none") const wide = createMemo(() => dimensions().width > 120) const sidebarVisible = createMemo(() => { @@ -217,7 +213,7 @@ export function Session() { const contentWidth = createMemo(() => dimensions().width - (sidebarVisible() ? 42 : 0) - 4) const models = createMemo(() => data.location.model.list(location()) ?? []) - const scrollAcceleration = createMemo(() => getScrollAcceleration(tuiConfig)) + const scrollAcceleration = createMemo(() => getScrollAcceleration(config)) const toast = useToast() const sdk = useSDK() const editor = useEditorContext() @@ -465,21 +461,16 @@ export function Session() { run: () => { batch(() => { const isVisible = sidebarVisible() - setSidebar(() => (isVisible ? "hide" : "auto")) + void configState + .update((draft) => { + draft.session = { ...draft.session, sidebar: isVisible ? "hide" : "auto" } + }) + .catch(toast.error) setSidebarOpen(!isVisible) }) dialog.clear() }, }, - { - title: conceal() ? "Disable code concealment" : "Enable code concealment", - value: "session.toggle.conceal", - category: "Session", - run: () => { - setConceal((prev) => !prev) - dialog.clear() - }, - }, { title: (() => { const next = nextThinkingMode(thinkingMode()) @@ -488,12 +479,17 @@ export function Session() { })(), value: "session.toggle.thinking", category: "Session", + hidden: true, slash: { name: "thinking", aliases: ["toggle-thinking"], }, run: () => { - thinking.set(nextThinkingMode(thinkingMode())) + void configState + .update((draft) => { + draft.session = { ...draft.session, thinking: nextThinkingMode(thinkingMode()) } + }) + .catch(toast.error) dialog.clear() }, }, @@ -501,17 +497,27 @@ export function Session() { title: "Toggle session scrollbar", value: "session.toggle.scrollbar", category: "Session", + hidden: true, run: () => { - setShowScrollbar((prev) => !prev) + void configState + .update((draft) => { + draft.session = { ...draft.session, scrollbar: !showScrollbar() } + }) + .catch(toast.error) dialog.clear() }, }, { - title: groupExploration() ? "Show exploration tools individually" : "Group exploration tools", + title: groupExploration() ? "Show tool calls individually" : "Group related tool calls", value: "session.toggle.exploration_grouping", category: "Session", + hidden: true, run: () => { - setGroupExploration((prev) => !prev) + void configState + .update((draft) => { + draft.session = { ...draft.session, grouping: groupExploration() ? "none" : "auto" } + }) + .catch(toast.error) dialog.clear() }, }, @@ -835,17 +841,17 @@ export function Session() { })) useBindings(() => ({ - bindings: tuiConfig.keybinds.gather("session.global", sessionGlobalBindingCommands), + bindings: config.keybinds.gather("session.global", sessionGlobalBindingCommands), })) useBindings(() => ({ enabled: () => renderer.currentFocusedEditor === null, - bindings: tuiConfig.keybinds.gather("session.global.unfocused", sessionGlobalUnfocusedBindingCommands), + bindings: config.keybinds.gather("session.global.unfocused", sessionGlobalUnfocusedBindingCommands), })) useBindings(() => ({ mode: OPENCODE_BASE_MODE, - bindings: tuiConfig.keybinds.gather("session", sessionBindingCommands), + bindings: config.keybinds.gather("session", sessionBindingCommands), })) // snap to bottom when session changes @@ -865,13 +871,13 @@ export function Session() { return contentWidth() }, sessionID: route.sessionID, - conceal, thinkingMode, showThinking, + markdownMode, groupExploration, diffWrapMode, models, - tui: tuiConfig, + config, }} > @@ -1244,21 +1250,27 @@ function SessionSwitchMessageV2(props: { message: SessionMessageInfo }) { } function SessionNoticeMessageV2(props: { message: SessionMessageInfo }) { + const ctx = use() const { theme } = useTheme() const metadata = () => (props.message.type === "synthetic" ? props.message.metadata : undefined) - const completion = () => metadata()?.source === "subagent" + const source = () => stringValue(metadata()?.source) + const completion = () => source() === "subagent" || source() === "shell" const state = () => stringValue(metadata()?.state) - const agent = () => Locale.titlecase(stringValue(metadata()?.agent) ?? "Subagent") + const actor = () => (source() === "shell" ? "Shell" : Locale.titlecase(stringValue(metadata()?.agent) ?? "Subagent")) const text = () => { if (props.message.type === "system") return props.message.text if (props.message.type === "synthetic") return props.message.description ?? "" return "" } + const description = () => (source() === "shell" ? text().replace(/\s+/g, " ").trim() : text()) const status = () => { if (state() === "completed") return "finished" if (state() === "error") return "failed" return state() ?? "finished" } + const heading = () => `${state() === "completed" ? "↳" : "!"} ${actor()} ${status()}` + const suffix = () => + Locale.truncateWidth(` · ${description()}`, Math.max(0, ctx.width - 3 - Bun.stringWidth(heading()))) const color = () => { if (state() === "error") return theme.error if (state() === "cancelled") return theme.warning @@ -1274,11 +1286,9 @@ function SessionNoticeMessageV2(props: { message: SessionMessageInfo }) { } > - - - {state() === "completed" ? "↳" : "!"} {agent()} {status()} - - · {text()} + + {heading()} + {suffix()} @@ -1296,7 +1306,6 @@ function SessionSkillMessage(props: { message: Extract }) { const ctx = use() - const kv = useKV() const { theme, syntax } = useTheme() const status = () => props.message.status const text = () => (props.message.status === "failed" ? props.message.error.message : props.message.summary) @@ -1309,7 +1318,7 @@ function CompactionMessage(props: { message: Extract - ⋯}> + ⋯}> @@ -1329,7 +1338,7 @@ function CompactionMessage(props: { message: Extract @@ -1477,6 +1486,7 @@ function UserMessage(props: { message: SessionMessageUser }) { ) const dialog = useDialog() const renderer = useRenderer() + const promptRef = usePromptRef() return ( @@ -1495,7 +1505,13 @@ function UserMessage(props: { message: SessionMessageUser }) { }} onMouseUp={() => { if (renderer.getSelection()?.getSelectedText()) return - dialog.replace(() => ) + dialog.replace(() => ( + promptRef.current?.set(value)} + /> + )) }} paddingTop={1} paddingBottom={1} @@ -1747,7 +1763,7 @@ function ReasoningPart(props: { streaming={true} syntaxStyle={syntax()} content={summary().body} - conceal={ctx.conceal()} + conceal={ctx.markdownMode() === "rendered"} fg={theme.textMuted} /> @@ -1813,7 +1829,7 @@ function TextPart(props: { last: boolean; part: SessionMessageAssistantText }) { internalBlockMode="top-level" content={props.part.text.trim()} tableOptions={{ style: "grid" }} - conceal={ctx.conceal()} + conceal={ctx.markdownMode() === "rendered"} fg={theme.markdownText} bg={theme.background} /> @@ -2382,10 +2398,6 @@ function Subagent(props: ToolProps) { ) } -export function formatSubagentToolcalls(count: number) { - return `${count} toolcall${count === 1 ? "" : "s"}` -} - export function formatSubagentTitle(agent: string, description: string, background: boolean) { return `${agent} Subagent — ${description}${background ? " [background]" : ""}` } @@ -2394,11 +2406,6 @@ export function formatSubagentRetry(attempt: number, message: string) { return `Retrying (attempt ${attempt}) · ${message}` } -export function formatCompletedSubagentDetail(toolcalls: number, duration: string) { - if (toolcalls === 0) return duration - return `${formatSubagentToolcalls(toolcalls)} · ${duration}` -} - type ExecuteCall = { tool: string; status: "running" | "completed" | "error"; input?: Record } function executeCalls(value: unknown): ExecuteCall[] { @@ -2465,8 +2472,9 @@ function Edit(props: ToolProps) { const pathFormatter = usePathFormatter() const view = createMemo(() => { - const diffStyle = ctx.tui.diff_style - if (diffStyle === "stacked") return "unified" + const diffView = ctx.config.diffs?.view + if (diffView === "unified") return "unified" + if (diffView === "split") return "split" // Default to "auto" behavior return ctx.width > 120 ? "split" : "unified" }) @@ -2541,7 +2549,8 @@ function ApplyPatch(props: ToolProps) { }) }) const view = createMemo(() => { - if (ctx.tui.diff_style === "stacked") return "unified" + if (ctx.config.diffs?.view === "unified") return "unified" + if (ctx.config.diffs?.view === "split") return "split" return ctx.width > 120 ? "split" : "unified" }) diff --git a/packages/tui/src/routes/session/permission.tsx b/packages/tui/src/routes/session/permission.tsx index 51553a97d9..a9382c85d3 100644 --- a/packages/tui/src/routes/session/permission.tsx +++ b/packages/tui/src/routes/session/permission.tsx @@ -12,7 +12,7 @@ import { filetype } from "../../util/filetype" import { Locale } from "../../util/locale" import { webSearchProviderLabel } from "../../util/tool-display" import { getScrollAcceleration } from "../../util/scroll" -import { useTuiConfig } from "../../config/v1" +import { useConfig } from "../../config" import { OPENCODE_BASE_MODE, useBindings, useCommandShortcut } from "../../keymap" import { usePathFormatter } from "../../context/path-format" @@ -22,7 +22,7 @@ function EditBody(props: { request: PermissionV2Request; patch?: string }) { const themeState = useTheme() const theme = themeState.theme const syntax = themeState.syntax - const config = useTuiConfig() + const config = useConfig().data const dimensions = useTerminalDimensions() const filepath = createMemo(() => { @@ -34,8 +34,9 @@ function EditBody(props: { request: PermissionV2Request; patch?: string }) { }) const view = createMemo(() => { - const diffStyle = config.diff_style - if (diffStyle === "stacked") return "unified" + const diffView = config.diffs?.view + if (diffView === "unified") return "unified" + if (diffView === "split") return "split" return dimensions().width > 120 ? "split" : "unified" }) @@ -469,7 +470,7 @@ export function PermissionPrompt(props: { request: PermissionV2Request; director function RejectPrompt(props: { onConfirm: (message: string) => void; onCancel: () => void }) { let input: TextareaRenderable const { theme } = useTheme() - const tuiConfig = useTuiConfig() + const config = useConfig().data const dimensions = useTerminalDimensions() const narrow = createMemo(() => dimensions().width < 80) useBindings(() => ({ @@ -486,7 +487,7 @@ function RejectPrompt(props: { onConfirm: (message: string) => void; onCancel: ( ], bindings: [ { key: "escape", desc: "Cancel permission rejection", group: "Permission", cmd: () => props.onCancel() }, - ...tuiConfig.keybinds.get("app.exit"), + ...config.keybinds.get("app.exit"), { key: "return", desc: "Confirm permission rejection", @@ -557,7 +558,7 @@ function Prompt>(props: { onSelect: (option: keyof T) => void }) { const { theme } = useTheme() - const tuiConfig = useTuiConfig() + const config = useConfig().data const dimensions = useTerminalDimensions() const keys = Object.keys(props.options) as (keyof T)[] const [store, setStore] = createStore({ @@ -646,8 +647,8 @@ function Prompt>(props: { }, ] : []), - ...(props.escapeKey ? tuiConfig.keybinds.get("app.exit") : []), - ...(props.fullscreen ? tuiConfig.keybinds.get("permission.prompt.fullscreen") : []), + ...(props.escapeKey ? config.keybinds.get("app.exit") : []), + ...(props.fullscreen ? config.keybinds.get("permission.prompt.fullscreen") : []), ], })) diff --git a/packages/tui/src/routes/session/rows.ts b/packages/tui/src/routes/session/rows.ts index 1df58d3539..c7afea1252 100644 --- a/packages/tui/src/routes/session/rows.ts +++ b/packages/tui/src/routes/session/rows.ts @@ -177,7 +177,9 @@ export function createSessionRows(sessionID: Accessor) { } const queuedStart = (rows: SessionRow[]) => { - const index = rows.findIndex((row) => row.type === "message" && isPending(row.messageID)) + const index = rows.findIndex( + (row) => row.type === "compaction-queued" || (row.type === "message" && isPending(row.messageID)), + ) return index === -1 ? rows.length : index } diff --git a/packages/tui/src/routes/session/sidebar.tsx b/packages/tui/src/routes/session/sidebar.tsx index b1f7744b9a..c32e47af6e 100644 --- a/packages/tui/src/routes/session/sidebar.tsx +++ b/packages/tui/src/routes/session/sidebar.tsx @@ -1,7 +1,7 @@ import { useData } from "../../context/data" import { createMemo, Show } from "solid-js" import { useTheme } from "../../context/theme" -import { useTuiConfig } from "../../config/v1" +import { useConfig } from "../../config" import { InstallationVersion } from "@opencode-ai/core/installation/version" import { usePluginRuntime } from "../../plugin/runtime" @@ -11,9 +11,9 @@ export function Sidebar(props: { sessionID: string; overlay?: boolean }) { const pluginRuntime = usePluginRuntime() const data = useData() const { theme } = useTheme() - const tuiConfig = useTuiConfig() + const config = useConfig().data const session = createMemo(() => data.session.get(props.sessionID)) - const scrollAcceleration = createMemo(() => getScrollAcceleration(tuiConfig)) + const scrollAcceleration = createMemo(() => getScrollAcceleration(config)) return ( diff --git a/packages/tui/src/theme/index.ts b/packages/tui/src/theme/index.ts index e8a5f2c5a9..efdf02357a 100644 --- a/packages/tui/src/theme/index.ts +++ b/packages/tui/src/theme/index.ts @@ -914,12 +914,6 @@ function getSyntaxRules(theme: Theme) { foreground: theme.text, }, }, - { - scope: ["conceal"], - style: { - foreground: theme.textMuted, - }, - }, // Additional common highlight groups { scope: ["string.special", "string.special.url"], diff --git a/packages/tui/src/ui/dialog-prompt.tsx b/packages/tui/src/ui/dialog-prompt.tsx index 0898b0ae42..76cef90070 100644 --- a/packages/tui/src/ui/dialog-prompt.tsx +++ b/packages/tui/src/ui/dialog-prompt.tsx @@ -3,7 +3,7 @@ import { useTheme } from "../context/theme" import { useDialog, type DialogContext } from "./dialog" import { Show, createEffect, createSignal, onMount, type JSX } from "solid-js" import { Spinner } from "../component/spinner" -import { useTuiConfig } from "../config/v1" +import { useConfig } from "../config" import { useBindings, useCommandShortcut } from "../keymap" export type DialogPromptProps = { @@ -20,7 +20,7 @@ export type DialogPromptProps = { export function DialogPrompt(props: DialogPromptProps) { const dialog = useDialog() const { theme } = useTheme() - const tuiConfig = useTuiConfig() + const config = useConfig().data const submitShortcut = useCommandShortcut("dialog.prompt.submit") const [textareaTarget, setTextareaTarget] = createSignal() let textarea: TextareaRenderable @@ -43,7 +43,7 @@ export function DialogPrompt(props: DialogPromptProps) { run: confirm, }, ], - bindings: tuiConfig.keybinds.gather("dialog.prompt", ["dialog.prompt.submit"]), + bindings: config.keybinds.gather("dialog.prompt", ["dialog.prompt.submit"]), })) onMount(() => { diff --git a/packages/tui/src/ui/dialog-select.tsx b/packages/tui/src/ui/dialog-select.tsx index ef13bc1d50..40ad0f24b1 100644 --- a/packages/tui/src/ui/dialog-select.tsx +++ b/packages/tui/src/ui/dialog-select.tsx @@ -17,7 +17,7 @@ import { isDeepEqual } from "remeda" import { useDialog, type DialogContext } from "./dialog" import { Locale } from "../util/locale" import { getScrollAcceleration } from "../util/scroll" -import { useTuiConfig } from "../config/v1" +import { useConfig } from "../config" import { formatKeyBindings, useBindings, useKeymapSelector } from "../keymap" export interface DialogSelectProps { @@ -36,14 +36,7 @@ export interface DialogSelectProps { renderFilter?: boolean locked?: boolean preserveSelection?: boolean - actions?: { - command: string - title: string - side?: "left" | "right" - hidden?: boolean - disabled?: boolean | ((option: DialogSelectOption | undefined) => boolean) - onTrigger: (option: DialogSelectOption) => void - }[] + actions?: DialogSelectAction[] footerHints?: { title: string label: string @@ -51,8 +44,27 @@ export interface DialogSelectProps { }[] bindings?: readonly Binding[] current?: T + focusCurrent?: boolean } +type DialogSelectActionBase = { + command: string + title: string + side?: "left" | "right" + hidden?: boolean + disabled?: boolean | ((option: DialogSelectOption | undefined) => boolean) +} + +type DialogSelectAction = + | (DialogSelectActionBase & { + selection?: "required" + onTrigger: (option: DialogSelectOption) => void + }) + | (DialogSelectActionBase & { + selection: "none" + onTrigger: () => void + }) + export interface DialogSelectOption { title: string titleView?: JSX.Element @@ -86,8 +98,8 @@ export function DialogSelect(props: DialogSelectProps) { const dialog = useDialog() const { theme } = useTheme() - const tuiConfig = useTuiConfig() - const scrollAcceleration = createMemo(() => getScrollAcceleration(tuiConfig)) + const config = useConfig().data + const scrollAcceleration = createMemo(() => getScrollAcceleration(config)) const [store, setStore] = createStore({ selected: 0, @@ -104,6 +116,7 @@ export function DialogSelect(props: DialogSelectProps) { on( () => props.current, (current) => { + if (props.focusCurrent === false) return if (current) { const currentIndex = flat().findIndex((opt) => isDeepEqual(opt.value, current)) if (currentIndex >= 0) { @@ -130,7 +143,7 @@ export function DialogSelect(props: DialogSelectProps) { const labels = new Map() for (const action of shownActions()) { - const label = formatKeyBindings(actionBindings().get(action.command), tuiConfig) + const label = formatKeyBindings(actionBindings().get(action.command), config) if (label) labels.set(action.command, label) } @@ -220,7 +233,11 @@ export function DialogSelect(props: DialogSelectProps) { on( () => props.options, () => { - if (!props.preserveSelection) return + if (!props.preserveSelection) { + const next = Math.min(store.selected, flat().length - 1) + if (next >= 0 && next !== store.selected) setStore("selected", next) + return + } if (resetSelection && store.filter.length > 0) { const option = flat()[0] if (!option) return @@ -229,7 +246,7 @@ export function DialogSelect(props: DialogSelectProps) { return } if (!selection) { - if (props.current !== undefined) { + if (props.focusCurrent !== false && props.current !== undefined) { const index = flat().findIndex((option) => isDeepEqual(option.value, props.current)) if (index >= 0) { setStore("selected", index) @@ -279,7 +296,7 @@ export function DialogSelect(props: DialogSelectProps) { setTimeout(() => { if (filter.length > 0) { moveTo(0, true, false) - } else if (current) { + } else if (current && props.focusCurrent !== false) { const currentIndex = flat().findIndex((opt) => isDeepEqual(opt.value, current)) if (currentIndex >= 0) { moveTo(currentIndex, true) @@ -348,7 +365,7 @@ export function DialogSelect(props: DialogSelectProps) { setStore("input", "keyboard") const index = focusedAction() if (index !== undefined) { - triggerAction(actionItems()[index]) + trigger(actionItems()[index]) return } const option = selected() @@ -439,18 +456,11 @@ export function DialogSelect(props: DialogSelectProps) { name: item.command, title: item.title, category: "Dialog", - run() { - if (props.locked) return - if (isActionDisabled(item)) return - setStore("input", "keyboard") - const option = selected() - if (!option) return - item.onTrigger(option) - }, + run: () => trigger(item), })), ], bindings: [ - ...tuiConfig.keybinds.gather("dialog.select", [ + ...config.keybinds.gather("dialog.select", [ "dialog.select.prev", "dialog.select.next", "dialog.select.page_up", @@ -459,7 +469,7 @@ export function DialogSelect(props: DialogSelectProps) { "dialog.select.end", "dialog.select.submit", ]), - ...visible.flatMap((item) => tuiConfig.keybinds.get(item.command)), + ...visible.flatMap((item) => config.keybinds.get(item.command)), ...(visible.length ? [ { @@ -502,10 +512,13 @@ export function DialogSelect(props: DialogSelectProps) { const left = createMemo(() => visibleActions().filter((item) => item.side !== "right")) const right = createMemo(() => visibleActions().filter((item) => item.side === "right")) - function triggerAction(item: VisibleAction | undefined) { - if (props.locked) return - if (!item || !isActionItem(item) || isActionDisabled(item)) return + function trigger(item: Action | undefined) { + if (props.locked || !item || isActionDisabled(item)) return setStore("input", "keyboard") + if (item.selection === "none") { + item.onTrigger() + return + } const option = selected() if (!option) return item.onTrigger(option) @@ -516,7 +529,9 @@ export function DialogSelect(props: DialogSelectProps) { } function isActionDisabled(item: Action) { - return typeof item.disabled === "function" ? item.disabled(selected()) : item.disabled + const option = selected() + if (item.selection !== "none" && !option) return true + return typeof item.disabled === "function" ? item.disabled(option) : item.disabled } function isActionFocused(item: VisibleAction) { @@ -543,7 +558,7 @@ export function DialogSelect(props: DialogSelectProps) { triggerAction(item)} + onMouseUp={() => trigger(item)} > { + if (store.stack.length > 0) return if (!focus) return if (focus.isDestroyed) return function find(item: Renderable) { diff --git a/packages/tui/src/util/locale.ts b/packages/tui/src/util/locale.ts index ddcb973e74..79537410f5 100644 --- a/packages/tui/src/util/locale.ts +++ b/packages/tui/src/util/locale.ts @@ -63,6 +63,24 @@ export function truncate(str: string, len: number): string { return str.slice(0, len - 1) + "…" } +const graphemeSegmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" }) + +export function truncateWidth(str: string, width: number): string { + if (width <= 0) return "" + if (Bun.stringWidth(str) <= width) return str + if (width === 1) return "…" + + const result: string[] = [] + let used = 0 + for (const item of graphemeSegmenter.segment(str)) { + const next = Bun.stringWidth(item.segment) + if (used + next > width - 1) break + result.push(item.segment) + used += next + } + return result.join("") + "…" +} + export function truncateLeft(str: string, len: number): string { if (str.length <= len) return str return "…" + str.slice(-(len - 1)) diff --git a/packages/tui/src/util/scroll.ts b/packages/tui/src/util/scroll.ts index 44645932df..6d0d0ce080 100644 --- a/packages/tui/src/util/scroll.ts +++ b/packages/tui/src/util/scroll.ts @@ -1,8 +1,10 @@ import { MacOSScrollAccel, type ScrollAcceleration } from "@opentui/core" export type ScrollConfig = { - scroll_acceleration?: { enabled?: boolean } - scroll_speed?: number + scroll?: { + acceleration?: boolean + speed?: number + } } export class CustomSpeedScroll implements ScrollAcceleration { @@ -15,12 +17,12 @@ export class CustomSpeedScroll implements ScrollAcceleration { reset(): void {} } -export function getScrollAcceleration(tuiConfig?: ScrollConfig): ScrollAcceleration { - if (tuiConfig?.scroll_acceleration?.enabled) { +export function getScrollAcceleration(config?: ScrollConfig): ScrollAcceleration { + if (config?.scroll?.acceleration) { return new MacOSScrollAccel() } - if (tuiConfig?.scroll_speed !== undefined) { - return new CustomSpeedScroll(tuiConfig.scroll_speed) + if (config?.scroll?.speed !== undefined) { + return new CustomSpeedScroll(config.scroll.speed) } return new CustomSpeedScroll(3) diff --git a/packages/tui/test/app-lifecycle.test.tsx b/packages/tui/test/app-lifecycle.test.tsx index 48d4a48aba..3827333856 100644 --- a/packages/tui/test/app-lifecycle.test.tsx +++ b/packages/tui/test/app-lifecycle.test.tsx @@ -4,7 +4,6 @@ import { createTestRenderer } from "@opentui/core/testing" import { Effect } from "effect" import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder" import { Global } from "@opencode-ai/core/global" -import { createTuiResolvedConfig } from "./fixture/tui-runtime" import { createEventStream, createFetch, directory, json } from "./fixture/tui-sdk" test("SIGHUP clears title and disposes scoped resources once", async () => { @@ -32,7 +31,7 @@ test("SIGHUP clears title and disposes scoped resources once", async () => { const task = Effect.runPromise( run({ server: { endpoint: { url: server.url.toString() } }, - config: createTuiResolvedConfig({ plugin_enabled: {} }), + config: { get: async () => ({}), update: async () => ({}) }, args: {}, log: () => {}, pluginHost: { @@ -118,7 +117,7 @@ test("session lifecycle updates the terminal title and prints the epilogue after const task = Effect.runPromise( run({ server: { endpoint: { url: server.url.toString() } }, - config: createTuiResolvedConfig({ plugin_enabled: {} }), + config: { get: async () => ({}), update: async () => ({}) }, args: { sessionID: "dummy" }, log: () => {}, pluginHost: { diff --git a/packages/tui/test/cli/cmd/tui/sync-fixture.tsx b/packages/tui/test/cli/cmd/tui/sync-fixture.tsx deleted file mode 100644 index 5749113856..0000000000 --- a/packages/tui/test/cli/cmd/tui/sync-fixture.tsx +++ /dev/null @@ -1,70 +0,0 @@ -/** @jsxImportSource @opentui/solid */ -import { testRender } from "@opentui/solid" -import { onMount } from "solid-js" -import { ArgsProvider } from "../../../../src/context/args" -import { KVProvider, useKV } from "../../../../src/context/kv" -import { ProjectProvider, useProject } from "../../../../src/context/project" -import { SDKProvider } from "../../../../src/context/sdk" -import { SyncProvider, useSync } from "../../../../src/context/sync" -import { PermissionProvider } from "../../../../src/context/permission" -import { ExitProvider } from "../../../../src/context/exit" -import { createApi, createClient, createEventStream, createFetch, type FetchHandler } from "../../../fixture/tui-sdk" -import { TestTuiContexts } from "../../../fixture/tui-environment" -export { createEventStream, createFetch, directory, json, worktree } from "../../../fixture/tui-sdk" - -export async function wait(fn: () => boolean, timeout = 2000) { - const start = Date.now() - while (!fn()) { - if (Date.now() - start > timeout) throw new Error("timed out waiting for condition") - await Bun.sleep(10) - } -} - -type Ctx = { kv: ReturnType; project: ReturnType; sync: ReturnType } - -export async function mount(override?: FetchHandler, state?: string) { - const events = createEventStream() - const calls = createFetch(override, events) - let sync!: ReturnType - let project!: ReturnType - let kv!: ReturnType - let done!: () => void - const ready = new Promise((resolve) => { - done = resolve - }) - - function Probe() { - const ctx: Ctx = { kv: useKV(), project: useProject(), sync: useSync() } - onMount(() => { - sync = ctx.sync - project = ctx.project - kv = ctx.kv - done() - }) - return - } - - const app = await testRender(() => ( - - - - - - - {}}> - - - - - - - - - - - )) - - await ready - await wait(() => sync.status === "complete") - return { app, emit: events.emit, kv, project, sync, session: calls.session } -} diff --git a/packages/tui/test/cli/cmd/tui/sync.test.tsx b/packages/tui/test/cli/cmd/tui/sync.test.tsx deleted file mode 100644 index e93c81f091..0000000000 --- a/packages/tui/test/cli/cmd/tui/sync.test.tsx +++ /dev/null @@ -1,24 +0,0 @@ -/** @jsxImportSource @opentui/solid */ -import { expect, test } from "bun:test" -import { mount } from "./sync-fixture" - -test("legacy sync is an inert compatibility context", async () => { - const { app, session, sync } = await mount() - - try { - expect(sync.status).toBe("complete") - expect(sync.ready).toBe(true) - expect(sync.data.session).toEqual([]) - expect(sync.data.message).toEqual({}) - expect(sync.data.provider).toEqual([]) - expect(sync.session.get("ses_test")).toBeUndefined() - - await sync.bootstrap() - await sync.session.refresh() - await sync.session.sync("ses_test") - - expect(session).toEqual([]) - } finally { - app.renderer.destroy() - } -}) diff --git a/packages/tui/test/cli/tui/data.test.tsx b/packages/tui/test/cli/tui/data.test.tsx index 416085bdb4..0e4102d30c 100644 --- a/packages/tui/test/cli/tui/data.test.tsx +++ b/packages/tui/test/cli/tui/data.test.tsx @@ -1202,6 +1202,21 @@ test("restores queued compaction from durable pending input", async () => { { type: "compaction-queued", inputID: "message-compaction-later" }, ]) + emitEvent(events, { + id: "evt_text_ended", + created: 2, + type: "session.text.ended", + durable: durable(sessionID, 5), + data: { + sessionID, + assistantMessageID: "message-assistant", + ordinal: 0, + text: "Active output", + }, + }) + await wait(() => rows.some((row) => row.type === "part")) + expect(rows.map((row) => row.type)).toEqual(["part", "compaction-queued", "compaction-queued"]) + emitEvent(events, { id: "evt_compaction_started", created: 2, diff --git a/packages/tui/test/cli/tui/dialog-prompt.test.tsx b/packages/tui/test/cli/tui/dialog-prompt.test.tsx index 160f660e38..d648258746 100644 --- a/packages/tui/test/cli/tui/dialog-prompt.test.tsx +++ b/packages/tui/test/cli/tui/dialog-prompt.test.tsx @@ -8,7 +8,7 @@ import path from "node:path" import { onCleanup } from "solid-js" import { tmpdir } from "../../fixture/fixture" import { createTuiResolvedConfig } from "../../fixture/tui-runtime" -import type { TuiKeybind } from "../../../src/config/v1/keybind" +import type { TuiKeybind } from "../../../src/config/keybind" import { TestTuiContexts } from "../../fixture/tui-environment" async function wait(fn: () => boolean, timeout = 2000) { @@ -26,22 +26,19 @@ async function mountPrompt(input: { }) { const state = path.join(input.root, "state") await mkdir(state, { recursive: true }) - await Bun.write(path.join(state, "kv.json"), "{}") const [ { DialogProvider }, { DialogPrompt }, - { KVProvider }, { ThemeProvider }, - { TuiConfigProvider }, + { ConfigProvider }, { ToastProvider }, { OpencodeKeymapProvider, registerOpencodeKeymap }, ] = await Promise.all([ import("../../../src/ui/dialog"), import("../../../src/ui/dialog-prompt"), - import("../../../src/context/kv"), import("../../../src/context/theme"), - import("../../../src/config/v1"), + import("../../../src/config"), import("../../../src/ui/toast"), import("../../../src/keymap"), ]) @@ -51,7 +48,7 @@ async function mountPrompt(input: { const keymap = createDefaultOpenTuiKeymap(renderer) const resolvedConfig = createTuiResolvedConfig({ keybinds: input.keybinds, - leader_timeout: 1000, + leader: { timeout: 1000 }, }) const off = registerOpencodeKeymap(keymap, renderer, resolvedConfig) onCleanup(off) @@ -66,17 +63,15 @@ async function mountPrompt(input: { }} > - - - - - - - - - - - + + + + + + + + + ) diff --git a/packages/tui/test/cli/tui/dialog-select.test.tsx b/packages/tui/test/cli/tui/dialog-select.test.tsx new file mode 100644 index 0000000000..96c849186d --- /dev/null +++ b/packages/tui/test/cli/tui/dialog-select.test.tsx @@ -0,0 +1,264 @@ +/** @jsxImportSource @opentui/solid */ +import { InputRenderable } from "@opentui/core" +import { createDefaultOpenTuiKeymap } from "@opentui/keymap/opentui" +import { testRender, useRenderer } from "@opentui/solid" +import { expect, test } from "bun:test" +import { mkdir } from "node:fs/promises" +import path from "node:path" +import { createSignal, onCleanup, onMount } from "solid-js" +import type { DialogSelectOption } from "../../../src/ui/dialog-select" +import { tmpdir } from "../../fixture/fixture" +import { TestTuiContexts } from "../../fixture/tui-environment" +import { createTuiResolvedConfig } from "../../fixture/tui-runtime" + +async function renderSelect( + root: string, + options: DialogSelectOption[], + onGlobal: () => void, + onRow: (option: DialogSelectOption) => void, +) { + const state = path.join(root, "state") + await mkdir(state, { recursive: true }) + const config = createTuiResolvedConfig() + const [ + { ConfigProvider }, + { ThemeProvider }, + { OpencodeKeymapProvider, registerOpencodeKeymap }, + { DialogProvider }, + { DialogSelect }, + { ToastProvider }, + ] = await Promise.all([ + import("../../../src/config"), + import("../../../src/context/theme"), + import("../../../src/keymap"), + import("../../../src/ui/dialog"), + import("../../../src/ui/dialog-select"), + import("../../../src/ui/toast"), + ]) + + function Harness() { + const renderer = useRenderer() + const keymap = createDefaultOpenTuiKeymap(renderer) + const off = registerOpencodeKeymap(keymap, renderer, config) + onCleanup(off) + + return ( + + + + Promise.resolve({}) }}> + + + + + + + + + + ) + } + + const app = await testRender(() => , { width: 80, height: 20, kittyKeyboard: true }) + app.renderer.start() + await app.waitForFrame((frame) => frame.includes("Items")) + await app.waitFor(() => app.renderer.currentFocusedEditor instanceof InputRenderable) + return app +} + +async function mountSelect(root: string, initial: DialogSelectOption[]) { + const state = path.join(root, "state") + await mkdir(state, { recursive: true }) + const config = createTuiResolvedConfig() + const [ + { ConfigProvider }, + { ThemeProvider }, + { OpencodeKeymapProvider, registerOpencodeKeymap }, + { DialogProvider, useDialog }, + { DialogSelect }, + { ToastProvider }, + ] = await Promise.all([ + import("../../../src/config"), + import("../../../src/context/theme"), + import("../../../src/keymap"), + import("../../../src/ui/dialog"), + import("../../../src/ui/dialog-select"), + import("../../../src/ui/toast"), + ]) + + const selected: string[] = [] + const moved: string[] = [] + let replaceOptions!: (options: DialogSelectOption[]) => void + + function Harness() { + const renderer = useRenderer() + const keymap = createDefaultOpenTuiKeymap(renderer) + const off = registerOpencodeKeymap(keymap, renderer, config) + const [options, setOptions] = createSignal(initial) + replaceOptions = setOptions + onCleanup(off) + + function Fixture() { + const dialog = useDialog() + onMount(() => + dialog.replace(() => ( + moved.push(option.value)} + onSelect={(option) => selected.push(option.value)} + /> + )), + ) + return null + } + + return ( + + + + Promise.resolve({}) }}> + + + + + + + + + + ) + } + + const app = await testRender(() => , { width: 80, height: 24, kittyKeyboard: true }) + app.renderer.start() + await app.waitForFrame((frame) => frame.includes("Mutable options")) + await app.waitFor(() => app.renderer.currentFocusedEditor instanceof InputRenderable) + return { app, moved, replaceOptions, selected } +} + +test("dialog actions run without options while row actions still require a selection", async () => { + await using tmp = await tmpdir() + let global = 0 + const rows: string[] = [] + const app = await renderSelect( + tmp.path, + [], + () => global++, + (option) => rows.push(option.value), + ) + + try { + app.mockInput.pressKey("m", { ctrl: true }) + app.mockInput.pressKey("d", { ctrl: true }) + + expect(global).toBe(1) + expect(rows).toEqual([]) + } finally { + app.renderer.destroy() + } +}) + +test("footer actions run when filtering leaves no selected row", async () => { + await using tmp = await tmpdir() + let global = 0 + const rows: string[] = [] + const app = await renderSelect( + tmp.path, + [{ title: "Alpha", value: "alpha" }], + () => global++, + (option) => rows.push(option.value), + ) + + try { + for (const key of "missing") app.mockInput.pressKey(key) + await app.waitForFrame((frame) => frame.includes("No results found")) + + app.mockInput.pressKey("d", { ctrl: true }) + app.mockInput.pressTab() + app.mockInput.pressEnter() + + expect(global).toBe(1) + expect(rows).toEqual([]) + } finally { + app.renderer.destroy() + } +}) + +test("row actions receive the selected option", async () => { + await using tmp = await tmpdir() + const rows: string[] = [] + const app = await renderSelect( + tmp.path, + [{ title: "Alpha", value: "alpha" }], + () => {}, + (option) => rows.push(option.value), + ) + + try { + app.mockInput.pressKey("d", { ctrl: true }) + + expect(rows).toEqual(["alpha"]) + } finally { + app.renderer.destroy() + } +}) + +test("selects the new final option immediately after removing the selected final option", async () => { + await using tmp = await tmpdir() + const options = ["first", "second", "third"].map((value) => ({ title: value, value })) + const select = await mountSelect(tmp.path, options) + + try { + select.app.mockInput.pressArrow("down") + await select.app.waitFor(() => select.moved.at(-1) === "second") + select.app.mockInput.pressArrow("down") + await select.app.waitFor(() => select.moved.at(-1) === "third") + select.replaceOptions(options.slice(0, -1)) + await select.app.waitForFrame((frame) => !frame.includes("third")) + + select.app.mockInput.pressEnter() + await select.app.waitFor(() => select.selected.length === 1) + + expect(select.selected).toEqual(["second"]) + } finally { + select.app.renderer.destroy() + } +}) + +test("selects a repopulated option after removing the only option", async () => { + await using tmp = await tmpdir() + const select = await mountSelect(tmp.path, [{ title: "only", value: "only" }]) + + try { + select.replaceOptions([]) + await select.app.waitForFrame((frame) => frame.includes("No results found")) + select.app.mockInput.pressEnter() + expect(select.selected).toEqual([]) + + select.replaceOptions([{ title: "replacement", value: "replacement" }]) + await select.app.waitForFrame((frame) => frame.includes("replacement")) + select.app.mockInput.pressEnter() + await select.app.waitFor(() => select.selected.length === 1) + + expect(select.selected).toEqual(["replacement"]) + } finally { + select.app.renderer.destroy() + } +}) diff --git a/packages/tui/test/cli/tui/diff-viewer-file-tree.test.tsx b/packages/tui/test/cli/tui/diff-viewer-file-tree.test.tsx index 3c6fc8a84a..6a7cb174a9 100644 --- a/packages/tui/test/cli/tui/diff-viewer-file-tree.test.tsx +++ b/packages/tui/test/cli/tui/diff-viewer-file-tree.test.tsx @@ -4,9 +4,8 @@ import { RGBA } from "@opentui/core" import { testRender } from "@opentui/solid" import type { JSX } from "solid-js" import { createTuiResolvedConfig } from "../../fixture/tui-runtime" -import { KVProvider } from "../../../src/context/kv" import { ThemeProvider } from "../../../src/context/theme" -import { TuiConfigProvider } from "../../../src/config/v1" +import { ConfigProvider } from "../../../src/config" import { DiffViewerFileTree } from "../../../src/feature-plugins/system/diff-viewer-file-tree" import { TestTuiContexts } from "../../fixture/tui-environment" import { @@ -181,11 +180,9 @@ async function captureSettledFrame(app: Awaited>) function withTheme(component: () => JSX.Element) { return ( - - - {component()} - - + + {component()} + ) } diff --git a/packages/tui/test/cli/tui/diff-viewer.test.tsx b/packages/tui/test/cli/tui/diff-viewer.test.tsx index 3b77f7ed21..494837d9a0 100644 --- a/packages/tui/test/cli/tui/diff-viewer.test.tsx +++ b/packages/tui/test/cli/tui/diff-viewer.test.tsx @@ -5,11 +5,10 @@ import { DiffRenderable, type Renderable, ScrollBoxRenderable } from "@opentui/c import { testRender, useRenderer } from "@opentui/solid" import type { TuiPluginApi, TuiPluginMeta, TuiRouteCurrent, TuiRouteDefinition } from "@opencode-ai/plugin/tui" import type { Session } from "@opencode-ai/sdk/v2" -import { KVProvider } from "../../../src/context/kv" import { ThemeProvider } from "../../../src/context/theme" -import { TuiConfigProvider } from "../../../src/config/v1" +import { ConfigProvider } from "../../../src/config" import { SDKProvider } from "../../../src/context/sdk" -import { TuiKeybind } from "../../../src/config/v1/keybind" +import { TuiKeybind } from "../../../src/config/keybind" import { OpencodeKeymapProvider } from "../../../src/keymap" import diffViewerPlugin from "../../../src/feature-plugins/system/diff-viewer" import { createTuiPluginApi } from "../../fixture/tui-plugin" @@ -173,13 +172,11 @@ async function renderDiffViewer(vcsDiff: unknown[], height = 20, initialRoute?: - - - - {renderDiff?.({ params: "params" in current ? current.params : undefined })} - - - + + + {renderDiff?.({ params: "params" in current ? current.params : undefined })} + + diff --git a/packages/tui/test/cli/tui/form.test.tsx b/packages/tui/test/cli/tui/form.test.tsx index e73ef9973d..b4ab1f57f3 100644 --- a/packages/tui/test/cli/tui/form.test.tsx +++ b/packages/tui/test/cli/tui/form.test.tsx @@ -7,10 +7,9 @@ import path from "node:path" import { onCleanup } from "solid-js" import { ClipboardProvider } from "../../../src/context/clipboard" import type { FormWithLocation } from "../../../src/context/data" -import { KVProvider } from "../../../src/context/kv" import { SDKProvider } from "../../../src/context/sdk" import { ThemeProvider } from "../../../src/context/theme" -import { TuiConfigProvider } from "../../../src/config/v1" +import { ConfigProvider } from "../../../src/config" import { OpencodeKeymapProvider, registerOpencodeKeymap } from "../../../src/keymap" import { ToastProvider } from "../../../src/ui/toast" import { tmpdir } from "../../fixture/fixture" @@ -21,7 +20,6 @@ import { createApi, createClient, createEventStream, createFetch } from "../../f async function mountForm(root: string, width = 80) { const state = path.join(root, "state") await mkdir(state, { recursive: true }) - await Bun.write(path.join(state, "kv.json"), "{}") const replies: unknown[] = [] const copied: string[] = [] @@ -76,17 +74,15 @@ async function mountForm(root: string, width = 80) { }} > - + - - Promise.resolve({}) }}> - - - - - + Promise.resolve({}) }}> + + + + - + diff --git a/packages/tui/test/cli/tui/inline-tool-wrap-snapshot.test.tsx b/packages/tui/test/cli/tui/inline-tool-wrap-snapshot.test.tsx index 8f07f81847..2ff61fa1c3 100644 --- a/packages/tui/test/cli/tui/inline-tool-wrap-snapshot.test.tsx +++ b/packages/tui/test/cli/tui/inline-tool-wrap-snapshot.test.tsx @@ -2,10 +2,8 @@ import { afterEach, describe, expect, test } from "bun:test" import { For } from "solid-js" import { testRender, type JSX } from "@opentui/solid" import { - formatCompletedSubagentDetail, formatSubagentRetry, formatSubagentTitle, - formatSubagentToolcalls, InlineToolRow, parseApplyPatchFiles, parseDiagnostics, @@ -182,13 +180,6 @@ describe("TUI inline tool wrapping", () => { ).toEqual([{ message: "valid", range: { start: { line: 2, character: 3 } } }]) }) - test("formats completed subagent toolcall details", () => { - expect(formatCompletedSubagentDetail(0, "501ms")).toBe("501ms") - expect(formatCompletedSubagentDetail(1, "501ms")).toBe("1 toolcall · 501ms") - expect(formatCompletedSubagentDetail(2, "501ms")).toBe("2 toolcalls · 501ms") - expect(formatSubagentToolcalls(0)).toBe("0 toolcalls") - }) - test("keeps background state attached to the subagent identity", () => { expect(formatSubagentTitle("Explore", "Inspect renderer", false)).toBe("Explore Subagent — Inspect renderer") expect(formatSubagentTitle("Explore", "Inspect renderer", true)).toBe( @@ -207,5 +198,4 @@ describe("TUI inline tool wrapping", () => { test("snapshots expanded tool errors under the tool text", async () => { expect(await renderFrame(() => , { width: 72, height: 12 })).toMatchSnapshot() }) - }) diff --git a/packages/tui/test/config-v2.test.tsx b/packages/tui/test/config-v2.test.tsx new file mode 100644 index 0000000000..c8c36f89ac --- /dev/null +++ b/packages/tui/test/config-v2.test.tsx @@ -0,0 +1,65 @@ +/** @jsxImportSource @opentui/solid */ +import { testRender } from "@opentui/solid" +import { expect, test } from "bun:test" +import { + resolve, + ConfigProvider, + useConfig, + type Interface, +} from "../src/config" + +test("resolves nested config and keybind defaults", () => { + const config = resolve( + { + keybinds: { leader: "ctrl+o" }, + leader: { timeout: 500 }, + scroll: { speed: 2, acceleration: true }, + diffs: { view: "split" }, + }, + { terminalSuspend: true }, + ) + + expect(config.leader.timeout).toBe(500) + expect(config.keybinds.get("leader")?.[0]?.key).toBe("ctrl+o") + expect(config.scroll).toEqual({ speed: 2, acceleration: true }) + expect(config.diffs).toEqual({ view: "split" }) +}) + +test("provides config and its host interface", async () => { + const config = resolve({}, { terminalSuspend: true }) + let current = {} + const service: Interface = { + get: async () => current, + update: async (update) => { + const draft: Record = { ...current } + update(draft) + current = draft + return draft + }, + } + let context: ReturnType | undefined + + function Consumer() { + context = useConfig() + return {`${context.data.mouse ? "mouse" : "none"} ${context.data.keybinds.get("leader")?.[0]?.key}`} + } + + const app = await testRender(() => ( + + + + )) + try { + await app.renderOnce() + expect(app.captureCharFrame()).toContain("mouse ctrl+x") + if (!context) throw new Error("Config context was not provided") + await context.update((draft) => { + draft.mouse = false + draft.keybinds = { leader: "ctrl+o" } + }) + await app.renderOnce() + expect(app.captureCharFrame()).toContain("none ctrl+o") + } finally { + app.renderer.destroy() + } +}) diff --git a/packages/tui/test/fixture/tui-plugin.ts b/packages/tui/test/fixture/tui-plugin.ts index e9fce425be..23dbf526ae 100644 --- a/packages/tui/test/fixture/tui-plugin.ts +++ b/packages/tui/test/fixture/tui-plugin.ts @@ -11,7 +11,6 @@ type Opts = { } export function createTuiPluginApi(opts: Opts = {}) { - const values = new Map() const color = RGBA.fromInts(200, 200, 200) const dialog = { clear() {}, replace() {}, setSize() {}, size: "medium" as const, depth: 0, open: false } return { @@ -19,15 +18,6 @@ export function createTuiPluginApi(opts: Opts = {}) { client: opts.client, event: opts.event, keymap: opts.keymap, - kv: { - get(name: string, fallback?: unknown) { - return values.has(name) ? values.get(name) : fallback - }, - set(name: string, value: unknown) { - values.set(name, value) - }, - ready: true, - }, state: { session: { get: () => undefined, ...opts.state?.session } }, theme: { current: new Proxy({}, { get: () => color }) }, tuiConfig: createTuiResolvedConfig(), diff --git a/packages/tui/test/fixture/tui-runtime.ts b/packages/tui/test/fixture/tui-runtime.ts index 028396c57d..26a5fc8e4f 100644 --- a/packages/tui/test/fixture/tui-runtime.ts +++ b/packages/tui/test/fixture/tui-runtime.ts @@ -1,10 +1,10 @@ -import { resolve, type Info, type Resolved } from "../../src/config/v1" -import { TuiKeybind } from "../../src/config/v1/keybind" +import { resolve, type Info, type Resolved } from "../../src/config" +import { TuiKeybind } from "../../src/config/keybind" -type ResolvedInput = Omit & { +type ResolvedInput = Omit & { attention?: Partial keybinds?: Partial - leader_timeout?: number + leader?: { timeout?: number } } export function createTuiResolvedConfig(input: ResolvedInput = {}) { diff --git a/packages/tui/test/keymap.test.tsx b/packages/tui/test/keymap.test.tsx index 70fbd6e6b2..fb3b685dbd 100644 --- a/packages/tui/test/keymap.test.tsx +++ b/packages/tui/test/keymap.test.tsx @@ -4,7 +4,7 @@ import { createBindingLookup } from "@opentui/keymap/extras" import { testRender, useRenderer } from "@opentui/solid" import { expect, test } from "bun:test" import { onCleanup } from "solid-js" -import { TuiKeybind } from "../src/config/v1/keybind" +import { TuiKeybind } from "../src/config/keybind" import { formatKeySequence, getOpencodeModeStack, diff --git a/packages/tui/test/util/locale.test.ts b/packages/tui/test/util/locale.test.ts new file mode 100644 index 0000000000..0da9dac7b3 --- /dev/null +++ b/packages/tui/test/util/locale.test.ts @@ -0,0 +1,9 @@ +import { expect, test } from "bun:test" +import { Locale } from "../../src/util/locale" + +test("truncates text from the right by terminal width", () => { + expect(Locale.truncateWidth("abcdefgh", 5)).toBe("abcd…") + expect(Locale.truncateWidth("ab界cd", 5)).toBe("ab界…") + expect(Locale.truncateWidth("abcdefgh", 1)).toBe("…") + expect(Locale.truncateWidth("abcdefgh", 0)).toBe("") +}) diff --git a/packages/web/src/content/docs/mcp-servers.mdx b/packages/web/src/content/docs/mcp-servers.mdx index 215938ec3b..97cf08e285 100644 --- a/packages/web/src/content/docs/mcp-servers.mdx +++ b/packages/web/src/content/docs/mcp-servers.mdx @@ -292,7 +292,7 @@ The `mcp debug` command shows the current auth status, tests HTTP connectivity, ## Manage -Your MCPs are available as tools in OpenCode, alongside built-in tools. So you can manage them through the OpenCode config like any other tool. +Tools from your MCP servers are available in OpenCode alongside built-in tools. You can manage them through the OpenCode config like any other tool. --- @@ -319,7 +319,7 @@ This means that you can enable or disable them globally. } ``` -We can also use a glob pattern to disable all matching MCPs. +We can also use a glob pattern to disable all matching MCP tools. ```json title="opencode.json" {14} { @@ -340,7 +340,7 @@ We can also use a glob pattern to disable all matching MCPs. } ``` -Here we are using the glob pattern `my-mcp*` to disable all MCPs. +Here we are using the glob pattern `my-mcp*` to disable all matching MCP tools. --- diff --git a/packages/www/.gitignore b/packages/www/.gitignore new file mode 100644 index 0000000000..311f12a418 --- /dev/null +++ b/packages/www/.gitignore @@ -0,0 +1,6 @@ +.source +.tanstack +.wrangler +dist +node_modules +worker-configuration.d.ts diff --git a/packages/www/README.md b/packages/www/README.md new file mode 100644 index 0000000000..e9411d6607 --- /dev/null +++ b/packages/www/README.md @@ -0,0 +1,22 @@ +# OpenCode website + +The server-rendered `opencode.ai` website. It uses TanStack Start on Cloudflare Workers and serves the V2 documentation with Fumadocs. + +## Development + +From this directory, run: + +```bash +bun dev +``` + +The site opens at `http://localhost:3000`; documentation is available at `http://localhost:3000/docs`. + +## Verification + +```bash +bun typecheck +bun run build +``` + +The existing `packages/web`, `packages/docs`, and `packages/stats/app` deployments remain in place until their routes have been migrated and verified. diff --git a/packages/www/content/docs/(docs)/agents.mdx b/packages/www/content/docs/(docs)/agents.mdx new file mode 100644 index 0000000000..b1275960cb --- /dev/null +++ b/packages/www/content/docs/(docs)/agents.mdx @@ -0,0 +1,284 @@ +--- +title: "Agents" +description: "" +--- + +Agents combine a system prompt, model preference, tool permissions, and display +metadata into a reusable assistant profile. OpenCode includes agents for common +workflows, and you can override them or add your own in configuration or +Markdown files. + +## Built-in agents + +| Agent | Mode | Purpose | +| --- | --- | --- | +| **Build** (`build`) | `primary` | Default coding agent. Tools are allowed by default, sensitive environment-file reads ask for approval, and access outside the workspace asks for approval. | +| **Plan** (`plan`) | `primary` | Planning agent. File edits are denied except for OpenCode plan files. Shell commands are not generally denied. | +| **General** (`general`) | `subagent` | General-purpose research and multi-step work. It has broad tool access but cannot launch more subagents. | +| **Explore** (`explore`) | `subagent` | Read-only code and web exploration using `read`, `glob`, `grep`, `webfetch`, and `websearch`. | + +OpenCode also has hidden `compaction`, `title`, and `summary` system agents. +They run internal maintenance tasks and are not selectable. There is no built-in +`scout` agent in V2. + +You can override a built-in agent with an entry of the same ID. Set +`disabled: true` to remove one. + +## Default agent + +Set the primary agent used when a session has not selected one: + +```jsonc title="opencode.jsonc" +{ + "$schema": "https://opencode.ai/config.json", + "default_agent": "reviewer" +} +``` + +The configured agent must exist, must not have `mode: "subagent"`, and must not +be hidden. If it is unavailable, OpenCode falls back to `build`, then to the +first visible agent that can run as a primary agent. This selection does not +rewrite the agent already stored on an existing session. + +## Modes + +An agent's `mode` controls where it can run: + +| Mode | Behavior | +| --- | --- | +| `primary` | Can be selected as the main agent for a session. It cannot be launched as a subagent. | +| `subagent` | Can run in a child session through the `subagent` tool, but cannot be selected as the main agent. | +| `all` | Can be used either way. This is the default for a custom agent when `mode` is omitted. | + +In the TUI, press Tab and Shift+Tab to cycle +through visible primary and `all` agents, or use `/agents` to choose one. + +Subagents run in child sessions with fresh context. A primary agent can invoke +one with the `subagent` tool, either in the foreground or in the background. +You can also `@` mention a visible subagent to ask the current agent to delegate +work to it: + +```text +@explore find where authentication errors are handled +``` + +The parent agent's `subagent` permission controls which agents it may launch. +The child currently uses its own configured permissions, not a restricted copy +of the parent's permissions. + +## Configure agents + +### Markdown files + +The recommended file locations are: + +```text +~/.config/opencode/agents/.md +.opencode/agents/.md +``` + +OpenCode discovers project `.opencode` directories from the current directory +up to the project root. The path below `agents/` becomes the agent ID, so +`.opencode/agents/team/reviewer.md` defines `team/reviewer`. + +Frontmatter uses the same fields as an entry under `agents`. The Markdown body +becomes `system`: + +```md title=".opencode/agents/reviewer.md" +--- +description: Reviews changes without modifying files +mode: subagent +model: anthropic/claude-sonnet-4-5#high +color: warning +steps: 8 +permissions: + - action: edit + resource: "*" + effect: deny + - action: shell + resource: "*" + effect: deny +--- + +Review for correctness, security, regressions, and missing tests. +List findings in severity order with file and line references. +``` + +### JSON or JSONC + +Use the `agents` field in any [OpenCode configuration file](/docs/config): + +```jsonc title="opencode.jsonc" +{ + "$schema": "https://opencode.ai/config.json", + "default_agent": "reviewer", + "agents": { + "reviewer": { + "description": "Reviews changes for correctness, security, and missing tests", + "mode": "all", + "model": "anthropic/claude-sonnet-4-5#high", + "system": "Review the current changes. Report findings before any summary.", + "color": "warning", + "steps": 8, + "permissions": [ + { "action": "edit", "resource": "*", "effect": "deny" }, + { "action": "shell", "resource": "*", "effect": "deny" } + ] + }, + "build": { + "permissions": [ + { "action": "shell", "resource": "git push *", "effect": "ask" } + ] + } + } +} +``` + +Agent definitions merge in configuration order. Later scalar fields replace +earlier values, request maps merge by key, and permission rules are appended. +Global `permissions` are applied to every agent before its agent-specific rules, +so a later agent rule can refine a global rule. + +## Options + +### `description` + +Explains the agent's purpose. It is optional, but strongly recommended for +subagents because OpenCode includes it in the subagent catalog shown to the +model. + +### `mode` + +Accepts `primary`, `subagent`, or `all`. The default is `all`. + +### `model` + +Selects a model using `provider/model` with an optional `#variant`: + +```jsonc +{ + "agents": { + "reviewer": { + "model": "anthropic/claude-sonnet-4-5#high" + } + } +} +``` + +The equivalent expanded form is: + +```jsonc +{ + "agents": { + "reviewer": { + "model": { + "providerID": "anthropic", + "model": "claude-sonnet-4-5", + "variant": "high" + } + } + } +} +``` + +The TUI uses this as the preferred model when the agent is selected. A child +session uses its subagent's configured model, or inherits the parent session's +model when none is configured. In the API, the session's selected model is +stored separately; creating or switching a primary session with only an agent +ID does not itself change that session model. + +### `system` + +Sets the agent's system prompt. A non-empty value replaces OpenCode's +provider-specific base prompt for that agent. Project instructions, skills, +references, and other instruction sources are still added separately. + +For a Markdown agent, use the document body instead of a `system` frontmatter +field. + +### `permissions` + +Permissions are an ordered array of rules: + +```jsonc +{ + "agents": { + "orchestrator": { + "permissions": [ + { "action": "subagent", "resource": "*", "effect": "deny" }, + { "action": "subagent", "resource": "explore", "effect": "allow" }, + { "action": "shell", "resource": "git *", "effect": "ask" } + ] + } + } +} +``` + +Each rule has: + +| Field | Meaning | +| --- | --- | +| `action` | Tool or permission action, with `*` wildcards supported. | +| `resource` | The path, command, agent ID, or other resource matched by the action. Wildcards are supported. | +| `effect` | `allow`, `ask`, or `deny`. | + +The last matching rule wins. Important V2 action names include `shell` for +shell commands, `edit` for all edit/write/patch tools, and `subagent` for child +agents. Other tools generally use their tool name, such as `read`, `glob`, +`grep`, `webfetch`, `websearch`, and `skill`. + + + Put broad wildcard rules first and exceptions afterward. For example, deny + all subagents first, then allow `explore`. + + +`~` and `$HOME` are expanded in filesystem resources for `read`, `edit`, and +`external_directory`. Shell resources are raw command text and are not +expanded. + +### `steps` + +Sets a positive maximum number of model steps. On the final allowed step, +OpenCode removes tools and asks the model to summarize its work in text. New +user input resets the allowance. + +### `hidden` + +When `true`, removes the agent from normal selectors, `@` autocomplete, and the +subagent catalog advertised to models. It is a visibility setting, not a +security boundary. + +### `color` + +Sets the agent's UI color. Use a six-digit hex color such as `#ff6b6b`, or one +of `primary`, `secondary`, `accent`, `success`, `warning`, `error`, or `info`. + +### `disabled` + +When `true`, removes the agent definition at that point in configuration +loading. This works for built-in and custom agents. + +### `request` + +The V2 schema accepts per-agent request `headers` and JSON `body` overlays: + +```jsonc +{ + "agents": { + "reviewer": { + "request": { + "headers": { "x-agent": "reviewer" }, + "body": { "temperature": 0.1 } + } + } + } +} +``` + + + The current V2 session runner preserves these overlays on the agent + definition but does not yet apply them to model requests. Configure effective + request settings on the provider, model, or model variant instead. Do not use + legacy top-level agent fields such as `temperature`, `top_p`, `prompt`, + `permission`, `tools`, `disable`, or `maxSteps` in new V2 configuration. + diff --git a/packages/www/content/docs/(docs)/attachments.mdx b/packages/www/content/docs/(docs)/attachments.mdx new file mode 100644 index 0000000000..7f97859bf4 --- /dev/null +++ b/packages/www/content/docs/(docs)/attachments.mdx @@ -0,0 +1,168 @@ +--- +title: "Attachments" +description: "" +--- + +OpenCode can add local context to a prompt as text or image media. Current V2 +sessions make these attachment types visible to the model: + +| Input | Model receives | +| --- | --- | +| UTF-8 text file | The filename and decoded text | +| Directory | A non-recursive listing of its immediate files and directories | +| PNG, JPEG, GIF, or WebP | Image media | + +SVG files are treated as text, not image media. PDF, AVIF, BMP, audio, video, +and other binary prompt attachments are not currently included in the model +request. Some clients may let you select a PDF, but V2 does not yet make that +PDF visible to the model. + + + Use a model that supports image input before attaching an image. OpenCode + passes supported image media to the selected provider, but the provider and + model still enforce their own formats, dimensions, file counts, and size + limits. A text-only model may reject the request. + + +## Add attachments + +### TUI + +Type `@` followed by a filename and select the result to attach a project file. +This is the preferred way to add source code and other text files: + +```text +Explain the error handling in @src/server.ts +``` + +Paste an image from the clipboard with the configured paste key, `Ctrl+V` by +default. You can also drag a supported image into a terminal that exposes the +dropped file path to the TUI. The TUI reads PNG, JPEG, GIF, and WebP as image +attachments; a dropped SVG is inserted as text. + +### Desktop and web + +Use **Attach file**, paste, or drag and drop. Attach UTF-8 text or a PNG, JPEG, +GIF, or WebP image. The desktop file picker limits one selection to 20 MiB in +total; the server also applies the per-attachment limit described below. + +### CLI + +Pass `--file` or `-f` to `opencode2 run`. Repeat the flag for multiple files: + +```bash +opencode2 run -f src/server.ts -f screenshot.png "Explain the failure" +``` + +The run command accepts at most 100 file flags and reads at most 10 MiB per +file. Use it for text files and the four supported image formats; other binary +files do not become model context. + +### API + +The V2 prompt and command payloads accept a `files` array. Each item requires a +`uri` and can include `name` and `description`: + +```bash +opencode2 api post /api/session/ses_example/prompt --data '{ + "text": "Review this file", + "files": [ + { + "uri": "file:///home/me/project/src/server.ts", + "name": "server.ts", + "description": "Request handler" + } + ] +}' +``` + +Use an absolute `file:` URL for a file available to the server, or an inline +data URL: + +```json +{ + "text": "What is wrong with this layout?", + "files": [ + { + "uri": "data:image/png;base64,", + "name": "layout.png" + } + ] +} +``` + +HTTP and HTTPS attachment URLs are not supported. OpenCode materializes each +attachment before admitting the prompt and rejects invalid URLs, unreadable +paths, non-files other than directories, and attachments over 20 MiB decoded. +For a text `file:` URL, optional positive `start` and `end` query parameters +select one-based lines: + +```text +file:///home/me/project/src/server.ts?start=20&end=60 +``` + +The server infers the media type from the bytes. A supplied filename or data +URL media type does not make an unsupported binary format model-visible. + +## Configure image processing + +Configure image normalization in `opencode.json` or `opencode.jsonc`: + +```jsonc title="opencode.jsonc" +{ + "$schema": "https://opencode.ai/config.json", + "attachments": { + "image": { + "auto_resize": true, + "max_width": 2000, + "max_height": 2000, + "max_base64_bytes": 5242880 + } + } +} +``` + +All fields are optional: + +| Field | Default | Behavior | +| --- | ---: | --- | +| `auto_resize` | `true` | Resize an image that exceeds any configured limit. If `false`, reject it. | +| `max_width` | `2000` | Maximum width in pixels. Must be a positive integer. | +| `max_height` | `2000` | Maximum height in pixels. Must be a positive integer. | +| `max_base64_bytes` | `5242880` | Maximum byte length of the Base64-encoded image string. Must be a positive integer. | + + + In the current V2 runtime, these settings apply to image media produced by + the built-in `read` tool. Images attached directly through the TUI, desktop, + web, CLI, or API bypass this normalization. Resize direct attachments before + adding them if the provider requires smaller media. + + +The `read` tool recognizes PNG, JPEG, GIF, and WebP by their contents and will +ingest at most 20 MiB of source image bytes. It decodes the image and compares +its width, height, and encoded Base64 length with all three configured limits. + +When `auto_resize` is `true`, OpenCode preserves the aspect ratio, scales the +image down to the dimension limits, and tries progressively smaller PNG and +JPEG encodings until the Base64 limit is met. The resulting media type can +therefore change to PNG or JPEG. If no encoding fits, the tool call fails. + +When `auto_resize` is `false`, exceeding any limit fails the tool call without +modifying the image. An image that cannot be decoded also fails. If the image +resizer cannot be loaded, the `read` tool returns the +original image instead, so these settings are processing limits rather than an +upload or security boundary. + +## Limits and provider behavior + +- Direct prompt attachments are limited to 20 MiB decoded per item by the V2 + server. Client-specific limits can be lower. +- `max_base64_bytes` counts the encoded Base64 characters in bytes, not the + decoded file size and not the complete `data:` URL. +- Text attachments are inserted into the prompt as text and do not require a + multimodal model. Large text read through the `read` tool has separate + paging and truncation limits. +- Image attachments use provider-native image input. Provider errors can still + occur when OpenCode's limits pass but the selected model's limits do not. +- PDFs and other unsupported binary prompt attachments should be converted to + text or supported images before attaching them. diff --git a/packages/www/content/docs/(docs)/commands.mdx b/packages/www/content/docs/(docs)/commands.mdx new file mode 100644 index 0000000000..398f7e7405 --- /dev/null +++ b/packages/www/content/docs/(docs)/commands.mdx @@ -0,0 +1,163 @@ +--- +title: "Commands" +description: "" +--- + +Custom commands turn a named prompt template into a slash command. Type the +command in the TUI, followed by any arguments: + +```text +/review src/auth +``` + +## Configure with Markdown + +OpenCode discovers `.md` command files in `commands/` directories: + +```text +~/.config/opencode/commands/ # Global +.opencode/commands/ # Project +``` + +Files may be nested; for example, `.opencode/commands/team/review.md` defines +`/team/review`. Files with other extensions, including `.mdx`, are not +discovered. + +```md title=".opencode/commands/review.md" +--- +description: Review code for correctness and missing tests +agent: plan +model: anthropic/claude-sonnet-4-5#high +--- + +Review $ARGUMENTS. Report bugs first, then missing tests. +``` + +The file body, with surrounding whitespace removed, is the command template. +JSON and Markdown commands share one registry. Project definitions take +precedence over global definitions, and a later definition can override a +built-in or earlier command with the same name. Changes are reloaded +automatically. + +Run it with: + +```text +/review src/auth +``` + +## Configure with JSON + +Add commands under the `commands` key in any OpenCode JSON or JSONC +[configuration file](/docs/config). Each entry's key is the command name and +`template` is required. + +```jsonc title="opencode.jsonc" +{ + "$schema": "https://opencode.ai/config.json", + "commands": { + "review": { + "description": "Review code for correctness and missing tests", + "template": "Review $ARGUMENTS. Report bugs first, then missing tests.", + "agent": "plan", + "model": "anthropic/claude-sonnet-4-5#high" + } + } +} +``` + +## Fields + +| Field | Required | Behavior | +| --- | --- | --- | +| `template` | JSON only | Prompt template. In a Markdown command, the file body supplies it. | +| `description` | No | Text shown with the command in autocomplete. | +| `agent` | No | Agent selected before the prompt runs. | +| `model` | No | Model override in `provider/model` or `provider/model#variant` format. | +| `subtask` | No | Accepted as a boolean, but currently has no execution effect in V2. | + +The four optional fields can be used in JSON or YAML frontmatter. Do not put +`template` in frontmatter because the Markdown body always supplies it. + +## Arguments + +Use `$ARGUMENTS` for the complete argument string: + +```md title=".opencode/commands/component.md" +--- +description: Create a component +--- + +Create a typed React component named $ARGUMENTS. +``` + +```text +/component Button +``` + +Use `$1`, `$2`, and higher numbers for parsed positional arguments. Single and +double quotes group text containing spaces and are removed during parsing. + +```md title=".opencode/commands/check.md" +--- +description: Check one area with a specific focus +--- + +Check $1. Focus on $2. +``` + +```text +/check src/auth "error handling and missing tests" +``` + +The highest-numbered positional placeholder present in the template consumes +that argument and all remaining arguments. For example, if a template contains +only `$1`, then `$1` receives the full parsed argument list. Missing positions +become empty strings. + +If a template contains neither positional placeholders nor `$ARGUMENTS`, +OpenCode appends non-empty arguments to the template after a blank line. + +## Shell interpolation + +Wrap a shell command in `!` followed by backticks to insert its output before +the prompt is submitted: + +```md title=".opencode/commands/review-diff.md" +--- +description: Review the current diff +--- + +Review this diff: + +!`git diff --stat && git diff` +``` + +OpenCode runs each interpolation with the configured shell in the active +project location and inserts its combined output into the template. Argument +interpolation happens first, so avoid placing untrusted arguments inside shell +interpolations. + + + Shell interpolations run when the command is evaluated, outside the agent's + tool permission flow. Only use commands from sources you trust. + + +No other template interpolation is performed. In particular, an `@path` +written into a stored template remains ordinary prompt text; V2 does not +automatically attach that file. + +## Agent, model, and execution + +Running a command evaluates its arguments and shell blocks, submits the result +as a durable user prompt in the current session, and schedules normal model +execution. + +If `agent` is set, it overrides the agent selected when the command was +invoked and becomes the session's active agent. If `model` is set, it overrides +the model. Otherwise, a model configured on the command's agent takes +precedence over the model selected at invocation. + +Although `subtask` is accepted in JSON and frontmatter, V2 currently ignores +it: commands run in the current session and do not create a child session. +Selecting an agent whose mode is `subagent` also does not turn the command into +a subtask. diff --git a/packages/www/content/docs/(docs)/compaction.mdx b/packages/www/content/docs/(docs)/compaction.mdx new file mode 100644 index 0000000000..693dff8c75 --- /dev/null +++ b/packages/www/content/docs/(docs)/compaction.mdx @@ -0,0 +1,153 @@ +--- +title: "Compaction" +description: "" +--- + +Compaction replaces the active model context from an older part of a session +with a generated checkpoint. The checkpoint contains a structured summary and +a serialized tail of recent context, so the agent can continue with more room +in the model's context window. + +Compaction is lossy, but it does not delete the earlier durable session +messages. After a successful compaction, V2 builds model requests from the +latest completed checkpoint and the messages that follow it. + +## Automatic compaction + +Automatic compaction is enabled by default. Before a model call, V2 estimates +the size of the final system prompt, messages, and advertised tools. It starts +compaction when: + +```text +estimated tokens > context limit - max(requested output tokens, buffer) +``` + +The estimate is approximate: V2 JSON-serializes the request and assumes four +characters per token. When compaction succeeds, V2 rebuilds the request from +the new checkpoint and retries the step without promoting the input again. + +V2 also recognizes provider errors classified as context overflow. If an +overflow occurs before the provider produces assistant output or other retry +evidence, V2 can compact and retry that step once. This recovery is attempted +even when `auto` is `false`; `auto` controls only the preflight size check. A +second overflow after recovery is returned as an error. + +## Manual compaction + +In the TUI, run: + +```text +/compact +``` + +`/summarize` is an alias. The default keybind is `c`, configured as +`session_compact`. + +A manual request is durably admitted and wakes the session runner. It can +compact short histories that would not trigger automatic compaction. If the +session is busy, compaction runs at the next safe drain boundary before later +steered or queued prompts are promoted. Repeated requests while one is pending +coalesce into that pending request. Whether compaction completes or fails, the +barrier is then settled so later prompts can proceed. + +The CLI has no separate `compact` subcommand. Use the TUI command or the server +API. For example: + +```bash +opencode2 api v2.session.compact \ + --param sessionID=ses_example \ + --data '{}' +``` + +The equivalent raw request is: + +```bash +opencode2 api post /api/session/ses_example/compact --data '{}' +``` + +`POST /api/session/:sessionID/compact` returns the admitted compaction input; +it does not wait for summary generation. Clients can call +`client.session.compact({ sessionID })` and then wait for the session or follow +the `session.compaction.*` events. Supplying an optional message `id` makes an +exact retry idempotent, but reusing an ID owned by another record returns a +conflict. + +## Configuration + +Add `compaction` to any [OpenCode configuration file](/docs/config): + +```jsonc title="opencode.jsonc" +{ + "$schema": "https://opencode.ai/config.json", + "compaction": { + "auto": true, + "prune": false, + "keep": { + "tokens": 8000 + }, + "buffer": 20000 + } +} +``` + +| Field | Default | V2 behavior | +| --- | ---: | --- | +| `auto` | `true` | Runs the preflight context-size check. It does not disable manual compaction or one-shot provider-overflow recovery. | +| `prune` | None | Accepted by the V2 schema, but currently has no runtime effect. V2 does not prune old tool outputs in place. | +| `keep.tokens` | `8000` | Approximate number of tokens from the newest serialized conversation context to retain beside the summary. | +| `buffer` | `20000` | Token reserve used by the automatic threshold. The requested model output allowance wins when it is larger. | + +`keep.tokens` and `buffer` accept non-negative integers. Larger `keep.tokens` +preserves more recent detail but leaves less room for future work. Larger +`buffer` triggers preflight compaction earlier. + +## Checkpoint contents + +V2 uses the session's selected or default model to generate the summary, with +tools disabled and at most 4096 output tokens. The summary records the +objective, important details, completed and active work, blockers, next moves, +and relevant files. + +The newest serialized context up to `keep.tokens` is retained separately. This +is not a byte-for-byte transcript: tool output is limited to 2000 characters, +and file or media attachments become textual descriptors rather than embedded +data. On later compactions, V2 updates the previous summary and carries forward +its retained recent context before selecting a new tail. + +The completed compaction is presented to the model as historical conversation +context, explicitly not as new instructions. Running and failed compactions are +not included in model context. + +## Compaction advances the instruction epoch + +Conversation compaction and instruction synchronization are separate. Before +promoting pending input, V2 compares live instruction sources with the latest +admitted values. Ordinary changes become durable value deltas; their +model-facing System messages are derived during request assembly rather than +persisted. + +Completed compaction advances the instruction epoch at the exact ended-event +sequence and makes the currently admitted values initial. It does not reread +sources or publish an instruction event. Session movement and committed revert +clear the instruction fold so the next safe boundary requires one complete +source read. See [Instructions](/docs/instructions) for source ordering and update +behavior. + +## Current limitations + +- `prune` is reserved configuration; V1-style in-place tool-output pruning is + not implemented in V2. +- Compaction requires a resolvable model with a positive catalog context limit. + There is no separate compaction-model setting or fallback model. +- Summary generation can fail if the summary prompt itself cannot fit beside + its output allowance, the model returns no summary, or the provider fails. +- Automatic and overflow compaction need older conversation context that can be + replaced. A provider overflow can still surface when there is no compressible + head or fixed instructions and tool schemas dominate the request. +- Overflow recovery retries only once per step. Token estimation is heuristic, + so it cannot prevent every provider-specific overflow. +- Earlier durable messages remain stored even though they are no longer in the + active model context. + +V1 used additional tail-turn and pruning behavior. Those V1 details are only +migration context; the settings and behavior on this page describe V2. diff --git a/packages/www/content/docs/(docs)/config.mdx b/packages/www/content/docs/(docs)/config.mdx new file mode 100644 index 0000000000..fc0db5bbed --- /dev/null +++ b/packages/www/content/docs/(docs)/config.mdx @@ -0,0 +1,450 @@ +--- +title: "Config" +description: "" +--- + + + You shouldn't have to configure OpenCode manually. Ask OpenCode to update its configuration for you. + + +## Format + +OpenCode supports both **JSON** and **JSONC** (JSON with Comments) configuration files. + +```jsonc title="opencode.jsonc" +{ + "$schema": "https://opencode.ai/config.json", + "model": "openai/gpt-5.2-custom", + "providers": { + "openai": { + "models": { + "gpt-5.2-custom": { + "modelID": "gpt-5.2", + "name": "GPT-5.2 Custom" + } + } + } + } +} +``` + +## Locations + +OpenCode loads global configuration from: + +```text +~/.config/opencode/opencode.json(c) +``` + +Project-specific configuration can use either form: + +```text +/home/user/projects/my-app/opencode.json(c) +/home/user/projects/my-app/.opencode/opencode.json(c) +``` + +When OpenCode starts, it searches for configuration files from the current +directory upward to the project root. It merges direct `opencode.json(c)` files +from the project root toward the current directory, then does the same for +files inside `.opencode` directories. A `.opencode` config therefore overrides +every direct config, even when the direct config is closer to the current +directory. Avoid mixing the two forms across one project hierarchy unless this +precedence is intentional. + +For example, consider a monorepo with OpenCode started from +`/home/user/projects/acme/packages/web`: + +```text +~/.config/opencode/opencode.json + +/home/user/projects/acme/ +├── opencode.json +└── packages/ + └── web/ + ├── opencode.json + └── src/ +``` + +OpenCode applies these files from lowest to highest precedence: + +1. `~/.config/opencode/opencode.json` +2. `/home/user/projects/acme/opencode.json` +3. `/home/user/projects/acme/packages/web/opencode.json` + +In this direct-config example, the package config overrides matching settings +from the repository config, which overrides matching settings from the global +config. Settings that do not conflict are preserved from every file. + +## Schema + +The complete OpenCode configuration schema is available at +[opencode.ai/config.json](https://opencode.ai/config.json). + +Add the `$schema` field to your configuration file to enable validation and +autocomplete in editors that support JSON Schema: + +```json title="opencode.json" +{ + "$schema": "https://opencode.ai/config.json" +} +``` + +Use the schema as the source of truth for available fields, accepted values, +and nested configuration shapes. + +### Shell + +Set the shell used by the terminal and shell tools. + +```jsonc +{ + "shell": "/bin/zsh" +} +``` + +### Model + +Set the default model in `provider/model` format. The root default currently +does not retain a `#variant`; select variants in the TUI or on an agent or command. + +```jsonc +{ + "model": "anthropic/claude-sonnet-4-5" +} +``` + +See the [models guide](/docs/models) for model selection +and local models. + +### Default agent + +Choose the primary agent used when a session does not select one explicitly. + +```jsonc +{ + "default_agent": "build" +} +``` + +See the [agents guide](/docs/agents) for built-in and custom +agents. + +### Autoupdate + +Control automatic updates from the global config. Set this to `false` to +disable updates. The current beta treats `true` and `"notify"` identically and +automatically installs compatible non-major updates; project-level values are +ignored. + +```jsonc +{ + "autoupdate": false +} +``` + +### Sharing + +Set the intended session sharing policy. V2 accepts this field, but session +sharing is not implemented yet. + +```jsonc +{ + "share": "manual" +} +``` + +See the [sharing guide](/docs/sharing) for more details. + +### Username + +Set a username for future display behavior. V2 accepts this field but does not +currently display it in conversations. + +```jsonc +{ + "username": "alice" +} +``` + +### Permissions + +Define ordered rules that allow, deny, or ask before an agent uses a tool on a +matching resource. + +```jsonc +{ + "permissions": [ + { + "action": "shell", + "resource": "git push *", + "effect": "ask" + } + ] +} +``` + +See the [permissions guide](/docs/permissions) for rule matching and available actions. + +### Agents + +Override built-in agents or define specialized agents with their own model, +instructions, mode, and permissions. + +```jsonc +{ + "agents": { + "reviewer": { + "description": "Review changes without editing files", + "mode": "subagent", + "system": "Focus on correctness, security, and missing tests.", + "permissions": [ + { "action": "edit", "resource": "*", "effect": "deny" } + ] + } + } +} +``` + +See the [agents guide](/docs/agents) for all agent options and file-based agents. + +### Snapshots + +Enable or disable filesystem snapshots used by undo and revert behavior. + +```jsonc +{ + "snapshots": false +} +``` + +See the [snapshots guide](/docs/snapshots) for undo and redo behavior. + +### Watcher + +Ignore files and directories that should not trigger filesystem updates. + +```jsonc +{ + "watcher": { + "ignore": ["dist/**", "coverage/**"] + } +} +``` + +### Formatter + +Define formatter settings for compatibility and future use. V2 accepts this +field, but it does not run formatters yet. + +```jsonc +{ + "formatter": { + "prettier": { + "command": ["bunx", "prettier", "--write", "$FILE"], + "extensions": [".js", ".ts", ".tsx"] + } + } +} +``` + +See the [formatters guide](/docs/formatters) for accepted fields and current limitations. + +### LSP + +Define language server settings for compatibility and future use. V2 accepts +this field, but it does not start language servers yet. + +```jsonc +{ + "lsp": { + "typescript": { + "command": ["typescript-language-server", "--stdio"], + "extensions": [".ts", ".tsx"] + } + } +} +``` + +See the [LSP guide](/docs/lsp) for accepted fields and current limitations. + +### Attachments + +Control how oversized images loaded by the `read` tool are resized or rejected +before they are sent to a model. + +```jsonc +{ + "attachments": { + "image": { + "auto_resize": true, + "max_width": 2000, + "max_height": 2000, + "max_base64_bytes": 5242880 + } + } +} +``` + +See the [attachments guide](/docs/attachments) for image processing and limits. + +### Tool output + +Set the maximum number of lines and bytes retained from a tool result. + +```jsonc +{ + "tool_output": { + "max_lines": 2000, + "max_bytes": 51200 + } +} +``` + +### MCP + +Configure local and remote Model Context Protocol servers. Global timeouts can +be overridden by an individual server. + +```jsonc +{ + "mcp": { + "servers": { + "playwright": { + "type": "local", + "command": ["bunx", "@playwright/mcp"] + } + } + } +} +``` + +See the [MCP guide](/docs/mcp-servers) for remote servers, OAuth, environment variables, and timeouts. + +### Compaction + +Control automatic context compaction and how much recent context it preserves. + +```jsonc +{ + "compaction": { + "auto": true, + "keep": { + "tokens": 8000 + }, + "buffer": 20000 + } +} +``` + +See the [compaction guide](/docs/compaction) for automatic context management. + +### Skills + +Add directories or URLs that OpenCode should search for agent skills. + +```jsonc +{ + "skills": ["./team-skills", "https://example.com/.well-known/skills/"] +} +``` + +See the [skills guide](/docs/skills) for skill structure and automatic discovery under `.opencode/skills/`. + +### Commands + +Define reusable slash commands as named prompt templates. + +```jsonc +{ + "commands": { + "review": { + "description": "Review the current changes", + "template": "Review the current diff for correctness and missing tests." + } + } +} +``` + +See the [commands guide](/docs/commands) for arguments, models, agents, and file-based commands. + +### Instructions + +Declare additional instruction files, globs, or URLs. V2 accepts this field, +but does not load these entries yet; use `AGENTS.md` for active instructions. + +```jsonc +{ + "instructions": ["CONTRIBUTING.md", "docs/guidelines/*.md"] +} +``` + +See the [instructions guide](/docs/instructions) for project instructions and `AGENTS.md`. + +### References + +Make local directories or Git repositories available as named supporting +context. + +```jsonc +{ + "references": { + "docs": { + "path": "../product-docs", + "description": "Product behavior and terminology" + }, + "effect": { + "repository": "Effect-TS/effect", + "branch": "main" + } + } +} +``` + +See the [references guide](/docs/references) for shorthand, visibility, and path resolution. + +### Plugins + +Load plugins from packages or local files. Use the object form when a plugin +accepts options. + +```jsonc +{ + "plugins": [ + "opencode-example-plugin", + { + "package": "./plugins/local.ts", + "options": { + "enabled": true + } + } + ] +} +``` + +See the [plugins guide](/docs/build/plugins) for plugin development and configuration. + +### Providers + +Configure providers and add or override their models, request settings, +headers, and model variants. + +```jsonc +{ + "providers": { + "openai": { + "models": { + "gpt-5.2-custom": { + "modelID": "gpt-5.2", + "name": "GPT-5.2 Custom", + "limit": { + "context": 200000, + "output": 32000 + } + } + } + } + } +} +``` + +See the [providers guide](/docs/providers) for credentials, custom endpoints, provider packages, and model configuration. diff --git a/packages/www/content/docs/(docs)/formatters.mdx b/packages/www/content/docs/(docs)/formatters.mdx new file mode 100644 index 0000000000..984c5a9297 --- /dev/null +++ b/packages/www/content/docs/(docs)/formatters.mdx @@ -0,0 +1,93 @@ +--- +title: "Formatters" +description: "" +--- + +OpenCode V2 accepts formatter configuration, but it does not yet include a +formatter runtime. File writes and edits are not automatically formatted. + + + V2 currently has no built-in formatters. The built-in formatter list and + automatic post-edit formatting documented for V1 do not apply to V2. + + +## Configuration + +The `formatter` field accepts a boolean or an object keyed by formatter name: + +```jsonc title="opencode.jsonc" +{ + "$schema": "https://opencode.ai/config.json", + "formatter": { + "prettier": { + "disabled": false, + "command": ["prettier", "--write", "$FILE"], + "environment": { + "NODE_ENV": "development" + }, + "extensions": [".js", ".jsx", ".ts", ".tsx"] + } + } +} +``` + +This example is valid V2 configuration, but V2 does not currently execute the +command. + +Each named formatter entry supports these optional fields: + +| Field | Type | Current V2 behavior | +| --- | --- | --- | +| `disabled` | `boolean` | Accepted, but there is no runtime formatter to enable or disable. | +| `command` | `string[]` | Accepted as an argument array, but not executed. | +| `environment` | `Record` | Accepts string environment variable names and values, but they are not applied. | +| `extensions` | `string[]` | Accepted without extension-specific validation, but files are not matched against it. | + +All entry fields are optional. The schema therefore also accepts an empty entry +such as `"prettier": {}`. + +## Enable and disable + +The schema accepts all of the following forms: + +```jsonc +// Omit `formatter`, or use false, when formatting is not requested. +{ + "formatter": false +} +``` + +```jsonc +// Reserved for enabling all built-ins once a V2 runtime provides them. +{ + "formatter": true +} +``` + +```jsonc +// Configure named entries or mark one as disabled. +{ + "formatter": { + "prettier": { "disabled": true }, + "custom": { + "command": ["custom-fmt", "$FILE"], + "extensions": [".foo"] + } + } +} +``` + +At present, omitted, `false`, `true`, and object forms have the same runtime +result: V2 runs no formatter. `disabled` is retained as configuration data but +does not control an executable formatter. + +## Commands and placeholders + +`command` is an array of strings, not a shell command string. `$FILE` is the V1 +file-path placeholder and is often retained in migrated configuration. V2 does +not currently substitute `$FILE` or define another formatter placeholder. + +Likewise, V2 does not currently use `extensions` to select commands, merge +`environment` into a child process, discover formatter executables or project +configuration, or run multiple matching formatters. These behaviors will only +be available after a V2 formatter runtime is implemented. diff --git a/packages/www/content/docs/(docs)/index.mdx b/packages/www/content/docs/(docs)/index.mdx new file mode 100644 index 0000000000..9101c3d269 --- /dev/null +++ b/packages/www/content/docs/(docs)/index.mdx @@ -0,0 +1,160 @@ +--- +title: "Intro" +description: "Get started with OpenCode." +--- + + + These docs are for the beta version of OpenCode, which will become OpenCode 2.0. The beta is still changing: we may + wipe your data, things may break, and APIs, configuration, and plugin APIs may change. + + +## Install + +The curl install script is not available in beta. + +You can also install it with the following package managers. + + + + ```bash + npm install -g @opencode-ai/cli@next + ``` + + + ```bash + bun install -g @opencode-ai/cli@next + ``` + + + ```bash + pnpm install -g @opencode-ai/cli@next + ``` + + + ```bash + yarn global add @opencode-ai/cli@next + ``` + + + +During beta, the binary is called `opencode2`. + +### Homebrew + +Homebrew installation is not available in beta. + +### Arch Linux + +Arch Linux installation is not available in beta. + +### Windows + + + For the best experience on Windows, install [Windows Subsystem for Linux + (WSL)](https://learn.microsoft.com/windows/wsl/install), open your Linux distribution, and use one of the beta package + manager commands above. + + + + + Chocolatey installation is not available in beta. + + + Scoop installation is not available in beta. + + + Mise installation is not available in beta. + + + Docker installation is not available in beta. + + + +Standalone binaries are not available in beta. + +--- + +#### Prerequisites + +To use OpenCode in your terminal, you'll need: + +1. A modern terminal emulator like: + - [Ghostty](https://ghostty.org), Linux and macOS + - [WezTerm](https://wezterm.org), cross-platform + - [Alacritty](https://alacritty.org), cross-platform + - [Kitty](https://sw.kovidgoyal.net/kitty/), Linux and macOS +2. API keys for the LLM providers you want to use. + +--- + +## Connect + +With OpenCode you can use any LLM provider by configuring its API key. + +Run `/connect` in the TUI and select your provider. + +```text +/connect +``` + +If you'd like easy access to all the best coding models you can try out +[OpenCode Console](https://console.opencode.ai). + +You can also try [OpenCode Go](https://opencode.ai/go) a $10/month subscription +plan that grants you access to the best open source models. + +Use `/models` to browse the providers and models available to your project. See [Providers](/docs/providers) for connection and +configuration details. + +--- + +## Usage + +You are now ready to use OpenCode in your project. Here are a few common workflows. + +### Ask questions + +Ask OpenCode to explain your codebase. + +Use `@` to fuzzy search for files in the project. + +```text +How is authentication handled in @packages/functions/src/api/index.ts +``` + +### Add features + +Ask OpenCode to add a feature by describing the desired behavior and providing relevant context. + +```text +When a user deletes a note, flag it as deleted in the database. +Create a screen that shows recently deleted notes. +From this screen, the user can restore a note or permanently delete it. +``` + +Give OpenCode plenty of context and examples. + +### Undo changes + +Use `/undo` when a change isn't what you wanted. + +```text +/undo +``` + +OpenCode stages a conversation revert and restores your original message so you can revise it. In a Git repository, it +also restores file changes when snapshots were captured successfully. Run `/undo` multiple times to move the conversation +boundary back, or use `/redo` to restore the staged conversation and files. See [Undo](/docs/snapshots) for +limitations and safety details. + +```text +/redo +``` + +--- + +## Customize + +Make OpenCode your own by [picking a theme](https://opencode.ai/docs/themes), [customizing +keybinds](https://opencode.ai/docs/keybinds), [configuring formatters](/docs/formatters), [creating commands](/docs/commands), or +editing the [OpenCode config](/docs/config). diff --git a/packages/www/content/docs/(docs)/instructions.mdx b/packages/www/content/docs/(docs)/instructions.mdx new file mode 100644 index 0000000000..d61d6cf753 --- /dev/null +++ b/packages/www/content/docs/(docs)/instructions.mdx @@ -0,0 +1,128 @@ +--- +title: "Instructions" +description: "" +--- + +Instructions are privileged context that guide an agent throughout a session. +V2 combines built-in context, discovered `AGENTS.md` files, and dynamic sources +such as skill, reference, MCP, and session context. It stores source values as +durable deltas, then renders initial instructions and chronological updates when +assembling each model request. + +## AGENTS.md + +Use `AGENTS.md` for persistent guidance such as build commands, architecture, +code conventions, and verification requirements. Commit project files so the +whole team receives the same instructions. + +V2 loads: + +1. The global file at `$XDG_CONFIG_HOME/opencode/AGENTS.md`, normally + `~/.config/opencode/AGENTS.md`. +2. Every `AGENTS.md` from the current Location up to and including the project + root. + +For example, when the Location is `packages/web`, OpenCode can load all three +project files below: + +```text +my-project/ +├── AGENTS.md +└── packages/ + ├── AGENTS.md + └── web/ + └── AGENTS.md +``` + +The files are combined rather than selecting a single winner. They are rendered +in this order: global, then project files from the Location toward the project +root. OpenCode does not resolve conflicts between their contents, so keep broad +guidance global and put scoped guidance in the relevant project directory. + +If the Location is outside the project root, only the global file is loaded. +Setting `OPENCODE_DISABLE_PROJECT_CONFIG=1` also skips project `AGENTS.md` +discovery but does not disable the global file. + + + Current V2 discovery only recognizes `AGENTS.md`. The `CLAUDE.md` fallback + and related precedence described by older OpenCode documentation do not apply. + + +### Nested instructions + +An `AGENTS.md` below the Location is not part of the initial upward scan. When +the read tool successfully reads a file or lists a directory, OpenCode discovers +`AGENTS.md` files from that target upward to, but not including, the Location. +It adds newly discovered files to the session in nearest-first order. + +Each nested file is injected once per session and recorded in durable session +history. Reading the same area again does not inject it again. Consequently, +editing an already injected nested `AGENTS.md` does not replace its earlier +session entry automatically; start a new session if the updated text must apply +immediately. + +## Config entries + +The V2 config schema accepts an `instructions` array of strings: + +```jsonc title="opencode.jsonc" +{ + "$schema": "https://opencode.ai/config.json", + "instructions": [ + "CONTRIBUTING.md", + "docs/guidelines/*.md", + "https://example.com/shared-instructions.md" + ] +} +``` + +Configuration is loaded from global through project-local files. If more than +one config defines `instructions`, the highest-precedence, closest config's +entire array is selected; arrays are not merged. + + + V2 currently parses and retains this field but does not resolve its entries + into instruction sources. Local files, glob patterns, and HTTP or HTTPS URLs + in `instructions` therefore do not reach the model yet. Use `AGENTS.md` for + active V2 instructions. URL fetching and timeout behavior documented for V1 + are not supported by the current V2 implementation. + + +See [Config](/docs/config) for config locations and general precedence. + +## Ordering + +The selected agent or provider system prompt is sent first. OpenCode then sends +the session's initial instructions, composed in this order: + +1. Built-in environment and date context. +2. Ambient `AGENTS.md` discovery. +3. Available skill, reference, and MCP guidance. +4. Session-specific instruction entries supplied through the API. + +These sources are combined; ordering is not an override mechanism. Nested +`AGENTS.md` files discovered by reads are chronological session entries rather +than part of the initial instructions. + +## Changes + +Before promoting pending input, V2 compares live instruction sources with the +latest admitted source values: + +- A new or changed ambient `AGENTS.md` aggregate is announced as a system update + that replaces the previous ambient aggregate. +- Removing all ambient files announces that the previous ambient instructions + no longer apply. +- A temporary read or discovery failure preserves the session's last known + instructions instead of treating them as deleted. If no instruction epoch + exists yet, pending input waits until every source is available. +- Completed conversation compaction advances the instruction epoch, making the + currently admitted values initial without rereading sources or authoring an + instruction event. +- Moving a session or committing a revert clears the instruction fold. The next + safe boundary requires one complete source read before promoting input. + +The durable event stores changed source keys and value hashes, not rendered +prose. During request assembly, OpenCode renders the epoch's initial values and +interleaves later changes as chronological System messages. Clients see changed +keys but never the privileged value bodies. diff --git a/packages/www/content/docs/(docs)/lsp.mdx b/packages/www/content/docs/(docs)/lsp.mdx new file mode 100644 index 0000000000..f7b683dc1e --- /dev/null +++ b/packages/www/content/docs/(docs)/lsp.mdx @@ -0,0 +1,105 @@ +--- +title: "LSP" +description: "" +--- + +Language Server Protocol (LSP) integrations can provide code diagnostics, +symbols, definitions, references, and other language-aware context. + + + OpenCode V2 does not yet have an LSP runtime or built-in language servers. + The `lsp` configuration is accepted and preserved, but it does not currently + start or download servers, expose an LSP tool, or add diagnostics to file tool + results. + + +## Built-in servers + +There are no built-in LSP servers in the current V2 implementation. Setting +`lsp` to `true` declares that built-ins should be enabled, but has no runtime +effect until V2 provides a server registry and LSP runtime. + +```jsonc title="opencode.jsonc" +{ + "$schema": "https://opencode.ai/config.json", + "lsp": true +} +``` + +## Configuration + +The `lsp` field accepts a boolean or an object keyed by server name: + +```jsonc title="opencode.jsonc" +{ + "$schema": "https://opencode.ai/config.json", + "lsp": { + "custom-typescript": { + "command": ["typescript-language-server", "--stdio"], + "extensions": [".ts", ".tsx"], + "env": { + "TSS_LOG": "-level verbose" + }, + "initialization": { + "preferences": { + "importModuleSpecifierPreference": "relative" + } + } + } + } +} +``` + +Each enabled server entry has this shape: + +| Property | Type | Required | Description | +| --- | --- | --- | --- | +| `command` | `string[]` | Yes | Executable followed by any arguments. | +| `extensions` | `string[]` | No | File extensions associated with the server, including the leading dot. | +| `disabled` | `boolean` | No | Disables the entry when `true`. | +| `env` | `Record` | No | Environment variables for the server process. The property is named `env`, not `environment`. | +| `initialization` | `Record` | No | Server-specific options for the LSP `initialize` request. | + +The only entry that may omit `command` is the disable-only form: + +```jsonc +{ + "lsp": { + "typescript": { + "disabled": true + } + } +} +``` + +Server names are arbitrary. The V2 schema permits `extensions` to be omitted, +including for a custom server, although a future runtime will need a way to +associate that server with files. + +## Disable LSP + +Omit `lsp` when no configuration is needed. Set it to `false` to explicitly +disable the whole integration, including when a lower-priority configuration +set it to `true` or supplied an object: + +```jsonc title="opencode.jsonc" +{ + "$schema": "https://opencode.ai/config.json", + "lsp": false +} +``` + +Use `{ "disabled": true }` under a server name to disable one server while +retaining the object form. `OPENCODE_DISABLE_LSP_DOWNLOAD` is not used by V2; +V2 currently performs no automatic LSP downloads. + +## Current usage + +V2 loads and validates the configuration shape for compatibility and future +integration. It does not currently use LSP when reading, writing, editing, or +patching files, and those tools do not notify a language server or return LSP +diagnostics. + +For reliable feedback today, have the agent run the project's lint, typecheck, +test, or compiler commands. Record those commands in an `AGENTS.md` file or a +skill so the agent knows when and where to run them. diff --git a/packages/www/content/docs/(docs)/mcp-servers.mdx b/packages/www/content/docs/(docs)/mcp-servers.mdx new file mode 100644 index 0000000000..93fac75bea --- /dev/null +++ b/packages/www/content/docs/(docs)/mcp-servers.mdx @@ -0,0 +1,262 @@ +--- +title: "MCP servers" +description: "" +--- + +OpenCode can connect to [Model Context Protocol](https://modelcontextprotocol.io/) servers and make their tools, prompts, and instructions available to agents. MCP tools consume model context, so enable only the servers you need. + +## Configure servers + +Define each server by a unique name under `mcp.servers` in your [OpenCode configuration](/docs/config). V2 does not place server names directly under `mcp`. + +```jsonc title="opencode.jsonc" +{ + "$schema": "https://opencode.ai/config.json", + "mcp": { + "servers": { + "my-server": { + "type": "local", + "command": ["npx", "-y", "example-mcp-server"] + } + } + } +} +``` + +Servers connect automatically unless `disabled` is `true`. There is no V2 `enabled` field. + +```jsonc +{ + "mcp": { + "servers": { + "my-server": { + "type": "local", + "command": ["npx", "-y", "example-mcp-server"], + "disabled": true + } + } + } +} +``` + +As with other configuration, a server in a higher-precedence project config replaces a server with the same name from a lower-precedence config. Use different names when you need separate connections or accounts. + +## Local servers + +A local server is a command that OpenCode starts using the MCP stdio transport. + +```jsonc title="opencode.jsonc" +{ + "$schema": "https://opencode.ai/config.json", + "mcp": { + "servers": { + "everything": { + "type": "local", + "command": [ + "npx", + "-y", + "@modelcontextprotocol/server-everything" + ], + "cwd": ".", + "environment": { + "LOG_LEVEL": "info", + "MCP_API_KEY": "{env:MCP_API_KEY}" + } + } + } + } +} +``` + +| Field | Required | Description | +| --- | --- | --- | +| `type` | Yes | Must be `"local"`. | +| `command` | Yes | Executable followed by its arguments. | +| `cwd` | No | Process working directory. Relative paths resolve from the workspace directory; the workspace is the default. | +| `environment` | No | String environment variables added to the inherited OpenCode process environment. | +| `disabled` | No | Set to `true` to prevent the server from connecting. Defaults to `false`. | +| `timeout` | No | Per-server timeout overrides. | + +Use `{env:NAME}` to substitute an environment variable while loading config. Shell expressions such as `$NAME` are not expanded in JSON strings. + +## Remote servers + +A remote server uses the MCP Streamable HTTP transport. Its `url` must be a valid absolute URL. + +```jsonc title="opencode.jsonc" +{ + "$schema": "https://opencode.ai/config.json", + "mcp": { + "servers": { + "context7": { + "type": "remote", + "url": "https://mcp.context7.com/mcp", + "oauth": false, + "headers": { + "CONTEXT7_API_KEY": "{env:CONTEXT7_API_KEY}" + } + } + } + } +} +``` + +| Field | Required | Description | +| --- | --- | --- | +| `type` | Yes | Must be `"remote"`. | +| `url` | Yes | Streamable HTTP endpoint. | +| `headers` | No | String HTTP headers sent to the MCP endpoint. | +| `oauth` | No | OAuth client settings, or `false` to disable OAuth support. | +| `disabled` | No | Set to `true` to prevent the server from connecting. Defaults to `false`. | +| `timeout` | No | Per-server timeout overrides. | + +Use `oauth: false` for a server that exclusively uses an API key or another header-based credential. + +## OAuth + +OAuth support is enabled for remote servers unless `oauth` is `false`. OpenCode discovers the authorization server, uses PKCE, refreshes tokens, and attempts dynamic client registration when the server supports it. OAuth credentials are stored outside project configuration. + +For a server that supports dynamic client registration, only the remote server is required: + +```jsonc title="opencode.jsonc" +{ + "$schema": "https://opencode.ai/config.json", + "mcp": { + "servers": { + "sentry": { + "type": "remote", + "url": "https://mcp.sentry.dev/mcp" + } + } + } +} +``` + +When the server reports that it needs authentication, run `/connect` in the TUI: + +```text +/connect +``` + +Select the MCP server under **Services**, then complete the browser authorization flow. + +You can also authenticate from the command line: + +```bash +opencode2 mcp auth sentry +``` + +The CLI command prints the authorization URL and waits for the redirect to OpenCode's loopback callback server. + +If the provider issued client credentials, configure them using V2's snake_case field names: + +```jsonc title="opencode.jsonc" +{ + "$schema": "https://opencode.ai/config.json", + "mcp": { + "servers": { + "company-tools": { + "type": "remote", + "url": "https://mcp.example.com/mcp", + "oauth": { + "client_id": "{env:MCP_CLIENT_ID}", + "client_secret": "{env:MCP_CLIENT_SECRET}", + "scope": "tools:read tools:execute", + "callback_port": 19876, + "redirect_uri": "http://127.0.0.1:19876/callback" + } + } + } + } +} +``` + +| OAuth field | Description | +| --- | --- | +| `client_id` | Pre-registered OAuth client ID. If omitted, OpenCode attempts dynamic client registration. | +| `client_secret` | Client secret for a pre-registered client. | +| `scope` | Space-delimited scopes to request. | +| `callback_port` | Local callback port, from `1` through `65535`. An available ephemeral port is used by default. | +| `redirect_uri` | Pre-registered loopback redirect URI. Its path and port must reach the local callback listener. | + +Remove stored credentials with: + +```bash +opencode2 mcp logout sentry +``` + +## Timeouts + +Timeouts are positive integer milliseconds. Configure defaults under `mcp.timeout`; a server's `timeout` fields override matching defaults. + +```jsonc title="opencode.jsonc" +{ + "$schema": "https://opencode.ai/config.json", + "mcp": { + "timeout": { + "startup": 45000, + "catalog": 30000, + "execution": 600000 + }, + "servers": { + "slow-tools": { + "type": "remote", + "url": "https://mcp.example.com/mcp", + "timeout": { + "catalog": 60000 + } + } + } + } +} +``` + +| Timeout | Default | Applies to | +| --- | --- | --- | +| `startup` | 30 seconds | Establishing the transport and initializing the server. | +| `catalog` | 30 seconds | Listing tools, prompts, resources, and resource templates. | +| `execution` | 12 hours | Calling tools, getting prompts, and reading resources. | + +## Names and permissions + +OpenCode combines the server name and MCP tool name as `_`. Characters other than letters, numbers, `_`, and `-` are replaced with `_`; for example, server `context 7` and tool `resolve.library/id` become `context_7_resolve_library_id`. MCP prompts appear as slash commands named `:` using the same normalization. + +Choose short server names that remain unique after normalization. Under the default Code Mode, MCP tools are grouped by the normalized server name. + +Use permission actions to hide or deny a server's tools without stopping its connection: + +```jsonc +{ + "permissions": [ + { + "action": "context7_*", + "resource": "*", + "effect": "deny" + } + ] +} +``` + +## CLI commands + +V2 provides these MCP management commands: + +```bash +# Add a local server to the project config +opencode2 mcp add everything --env LOG_LEVEL=info -- npx -y @modelcontextprotocol/server-everything + +# Add a remote server to the project config +opencode2 mcp add context7 --url https://mcp.context7.com/mcp --header 'CONTEXT7_API_KEY={env:CONTEXT7_API_KEY}' + +# Add to the global config instead +opencode2 mcp add context7 --global --url https://mcp.context7.com/mcp + +# List configured servers and connection status +opencode2 mcp list + +# Authenticate or remove OAuth credentials +opencode2 mcp auth context7 +opencode2 mcp logout context7 +``` + +`mcp add` accepts either `--url` for a remote server or a command after `--` for a local server, not both. Use `--header NAME=VALUE` only with remote servers and `--env NAME=VALUE` only with local servers. Edit the config directly for OAuth, timeout, working-directory, or enablement settings. diff --git a/packages/www/content/docs/(docs)/meta.json b/packages/www/content/docs/(docs)/meta.json new file mode 100644 index 0000000000..7226307441 --- /dev/null +++ b/packages/www/content/docs/(docs)/meta.json @@ -0,0 +1,28 @@ +{ + "title": "Docs", + "description": "Use and configure OpenCode.", + "root": true, + "pages": [ + "---Get started---", + "index", + "migrate-v1", + "config", + "troubleshooting", + "---Configure---", + "providers", + "models", + "agents", + "permissions", + "sharing", + "snapshots", + "commands", + "skills", + "instructions", + "mcp-servers", + "attachments", + "compaction", + "formatters", + "lsp", + "references" + ] +} diff --git a/packages/www/content/docs/(docs)/migrate-v1.mdx b/packages/www/content/docs/(docs)/migrate-v1.mdx new file mode 100644 index 0000000000..22c1973b73 --- /dev/null +++ b/packages/www/content/docs/(docs)/migrate-v1.mdx @@ -0,0 +1,576 @@ +--- +title: "Migrate from V1" +description: "Move from OpenCode V1 to the OpenCode 2.0 beta." +--- + +## Breaking changes + +V2 has three intentional breaking changes: + +- [Plugins](#plugins) use a new plugin API. +- The [server API and clients](#server-api-and-clients) have new contracts. +- [TUI configuration](#tui-configuration) moves from layered `tui.json(c)` files to one global `cli.json` file (auto migrated). + +All other functionality is intended to remain compatible with V1. + +Existing server config files, agent definitions, command definitions, skills, and other files in `.opencode/` should +continue to work without changes. If one of these stops working in V2, treat it as a beta compatibility bug rather than +an expected migration requirement. + + + Run `/report` if existing V1 functionality does not work in V2. The report skill collects diagnostics and helps you file + a compatibility issue. + + + + OpenCode 2.0 is in beta. Beta data may be wiped, features may break unintentionally, and the server and plugin APIs may + continue to change. + + +During the beta, OpenCode V1 and V2 use different executable names. You can keep using `opencode` for V1 while trying V2 +with `opencode2`. + +## Install the beta + +Install the beta from the `next` distribution tag: + +```bash +npm install -g @opencode-ai/cli@next +``` + +Start it in your project with: + +```bash +opencode2 +``` + +## Configuration + +This section covers both JSON/JSONC configuration and file-based definitions under `.opencode/`. + +### Use your existing configuration + +V2 reads existing global and project configuration from the same locations as V1: + +```text +~/.config/opencode/opencode.json(c) +/opencode.json(c) +/.opencode/opencode.json(c) +``` + +V2 reads these same locations. It detects V1-shaped configuration and translates it in memory without rewriting the +source file. Existing V1 configuration is intended to keep working, so you do not need to convert it to try or adopt V2. + +### Ask OpenCode to migrate + +The V1 config format remains supported. The native V2 format is optional and makes several settings more explicit and +ergonomic. + +The recommended migration path is to ask OpenCode to update the configuration for you: + +```text +Migrate my OpenCode configuration, including file-based definitions, from the V1 format to the native V2 format. +Preserve its behavior and all unrelated settings. +``` + +OpenCode can inspect the complete file, apply the relevant changes below, and avoid rewriting settings that do not need to +change. Do not mix V1 and V2 field names manually in one file. + +### Sharing + +The deprecated V1 `autoshare` boolean becomes the explicit `share` policy: + +```jsonc +// V1 +{ "autoshare": true } + +// V2 +{ "share": "auto" } +``` + +Use `"manual"`, `"auto"`, or `"disabled"`. If the V1 file already uses `share`, no change is needed. + +### Permissions and tools + +V1 groups permission effects by tool. V2 uses one ordered `permissions` array, making precedence and exceptions explicit: + +```jsonc +// V1 +{ + "permission": { + "bash": { + "git push *": "ask" + }, + "edit": "allow" + }, + "tools": { + "websearch": false + } +} + +// V2 +{ + "permissions": [ + { "action": "shell", "resource": "git push *", "effect": "ask" }, + { "action": "edit", "resource": "*", "effect": "allow" }, + { "action": "websearch", "resource": "*", "effect": "deny" } + ] +} +``` + +Permission actions also changed: `bash` is now `shell`, `task` is now `subagent`, and `write` and `patch` are now `edit`. +See [Permissions](/docs/permissions) for the ordered V2 rule format. + +### Agents and modes + +The singular `agent` and deprecated `mode` maps become `agents`. Agent fields become more consistent with the rest of the +V2 config: + +```jsonc +// V1 +{ + "agent": { + "reviewer": { + "prompt": "Review for correctness and missing tests.", + "model": "anthropic/claude-sonnet-4-5", + "variant": "high", + "disable": false, + "permission": { + "edit": "deny" + } + } + } +} + +// V2 +{ + "agents": { + "reviewer": { + "system": "Review for correctness and missing tests.", + "model": "anthropic/claude-sonnet-4-5#high", + "disabled": false, + "permissions": [ + { "action": "edit", "resource": "*", "effect": "deny" } + ] + } + } +} +``` + +`prompt` becomes `system`, `disable` becomes `disabled`, and a separate `variant` joins the model reference after `#`. +`temperature`, `top_p`, and provider-specific `options` move under `request.body`. `maxSteps` becomes `steps`. Entries from +the old `mode` map become primary agents. + +### Snapshots + +Rename the singular `snapshot` field to `snapshots`. Its boolean value does not change: + +```jsonc +// V1 +{ "snapshot": false } + +// V2 +{ "snapshots": false } +``` + +### Attachments + +Rename the singular `attachment` object to `attachments`. Nested image settings keep the same names: + +```jsonc +// V1 +{ "attachment": { "image": { "auto_resize": true } } } + +// V2 +{ "attachments": { "image": { "auto_resize": true } } } +``` + +### MCP servers + +V2 groups servers under `mcp.servers`, replaces `enabled` with the inverse `disabled`, and separates timeout purposes: + +```jsonc +// V1 +{ + "mcp": { + "playwright": { + "type": "local", + "command": ["npx", "@playwright/mcp"], + "enabled": true, + "timeout": 30000 + } + } +} + +// V2 +{ + "mcp": { + "servers": { + "playwright": { + "type": "local", + "command": ["npx", "@playwright/mcp"], + "disabled": false, + "timeout": { + "catalog": 30000, + "execution": 30000 + } + } + } + } +} +``` + +Remote OAuth fields use snake case: `clientId` becomes `client_id`, `clientSecret` becomes `client_secret`, +`callbackPort` becomes `callback_port`, and `redirectUri` becomes `redirect_uri`. The V1 `experimental.mcp_timeout` value +also becomes the default `mcp.timeout.catalog` and `mcp.timeout.execution` values. See [MCP servers](/docs/mcp-servers). + +### Compaction + +V2 groups the retained-context token budget under `keep` and gives the reserve a clearer name: + +```jsonc +// V1 +{ + "compaction": { + "preserve_recent_tokens": 8000, + "reserved": 20000 + } +} + +// V2 +{ + "compaction": { + "keep": { + "tokens": 8000 + }, + "buffer": 20000 + } +} +``` + +`auto` and `prune` keep their names. V2 has no native `tail_turns` field; recent context is retained by token budget instead. +See [Compaction](/docs/compaction). + +### Skills + +V1 separates extra skill paths and URLs. V2 combines both into one ordered array: + +```jsonc +// V1 +{ + "skills": { + "paths": ["./team-skills"], + "urls": ["https://example.com/skills/"] + } +} + +// V2 +{ + "skills": ["./team-skills", "https://example.com/skills/"] +} +``` + +Existing skill files and automatic `.opencode/skills/` discovery do not change. See [Skills](/docs/skills). + +### Commands + +Rename the singular `command` map to `commands`. Join a separate model `variant` to the model reference: + +```jsonc +// V1 +{ + "command": { + "review": { + "template": "Review the current changes.", + "model": "anthropic/claude-sonnet-4-5", + "variant": "high" + } + } +} + +// V2 +{ + "commands": { + "review": { + "template": "Review the current changes.", + "model": "anthropic/claude-sonnet-4-5#high" + } + } +} +``` + +`template`, `description`, `agent`, and `subtask` keep their names. Existing Markdown command definitions remain supported. +See [Commands](/docs/commands). + +### References + +Rename the deprecated singular `reference` map to `references`: + +```jsonc +// V1 +{ "reference": { "docs": "../docs" } } + +// V2 +{ "references": { "docs": "../docs" } } +``` + +V1 already accepts `references`, so no change is needed when the file uses it. Reference entries keep the +same shapes. See [References](/docs/references). + +### Providers + +Rename the singular `provider` map to `providers`. V2 separates the runtime package, endpoint, and request settings: + +```jsonc +// V1 +{ + "provider": { + "acme": { + "npm": "@ai-sdk/openai-compatible", + "api": "https://llm.example.com/v1", + "options": { + "apiKey": "{env:ACME_API_KEY}" + } + } + } +} + +// V2 +{ + "providers": { + "acme": { + "package": "aisdk:@ai-sdk/openai-compatible", + "settings": { + "baseURL": "https://llm.example.com/v1", + "apiKey": "{env:ACME_API_KEY}" + } + } + } +} +``` + +V1 `npm` becomes `package`, and AI SDK packages receive the `aisdk:` prefix. `api` becomes `settings.baseURL`. Provider +`options` are separated into `settings`, `headers`, and `body` according to their request role. See [Providers](/docs/providers). + +### Models and variants + +Models remain nested under their provider, but several model fields become more explicit: + +- `id` becomes `modelID`. +- `tool_call` and `modalities` become `capabilities.tools`, `capabilities.input`, and `capabilities.output`. +- A `status` of `"deprecated"` becomes `disabled: true`. +- Cache costs move from `cache_read` and `cache_write` to `cache.read` and `cache.write`. +- Provider-specific `options` become `settings`. +- A V1 variants object becomes a V2 array with an `id` on each entry. + +```jsonc +// V1 +{ + "variants": { + "high": { + "reasoningEffort": "high" + } + } +} + +// V2 +{ + "variants": [ + { + "id": "high", + "settings": { + "reasoningEffort": "high" + } + } + ] +} +``` + +See [Models](/docs/models) for the complete native model shape. + +### Fields without native equivalents + +Most fields that keep the same shape, including `shell`, `model`, `default_agent`, `autoupdate`, `watcher`, `formatter`, +`lsp`, `instructions`, `enterprise`, and `tool_output`, require no migration. + +These V1 fields do not have one-to-one native V2 config fields: + +- `logLevel`: use `OPENCODE_LOG_LEVEL` when starting OpenCode. +- `server`: use the V2 service and explicit server options; the server API is an intentional breaking change. +- `layout`: remove it; V1 already treated it as deprecated and always used stretch layout. +- `enabled_providers` and `disabled_providers`: there is no native provider allowlist or denylist field yet. +- `small_model`: V2 selects models for internal maintenance agents without a separate top-level field. +- `compaction.tail_turns`: V2 uses `compaction.keep.tokens` instead. + +If your V1 configuration relies on a field without a native equivalent, keep using the supported V1 format rather than +forcing a manual conversion. Run `/report` if V2 does not preserve the behavior you rely on. + +### Agent files + +V1 agent files may use `agent/`, `agents/`, `mode/`, or `modes/`. V2 still discovers all four directories. The preferred +V2 location is: + +```text +.opencode/agents/.md +``` + +Files under a V1 `mode/` or `modes/` directory represent primary agents. When moving one into `agents/`, add +`mode: primary` to its frontmatter. Files under `agent/` can move to `agents/` without changing their path-derived ID. + +When converting the frontmatter to native V2 fields: + +- Keep the Markdown body as the agent's system instructions. +- Rename `prompt` to `system` when it appears in JSON configuration; file bodies do not need a `system` field. +- Rename `disable` to `disabled` and `permission` to `permissions`. +- Join `model` and `variant` as `provider/model#variant`. +- Move `temperature`, `top_p`, and provider-specific options under `request.body`. + +V2 translates legacy agent frontmatter automatically, so these edits are optional. See [Agents](/docs/agents). + +### Command files + +V1 command files may use `command/` or `commands/`. V2 discovers both. The preferred location is: + +```text +.opencode/commands/.md +``` + +Move files from `command/` to the same relative path under `commands/` to preserve command names. The Markdown body remains +the command template, and `description`, `agent`, and `subtask` frontmatter keep the same names. If frontmatter has separate +`model` and `variant` fields, append the variant to the model and remove `variant`: + +```yaml +# V1 +model: anthropic/claude-sonnet-4-5 +variant: high + +# V2 +model: anthropic/claude-sonnet-4-5#high +``` + +See [Commands](/docs/commands). + +### Skill files + +V2 discovers skills from both `.opencode/skill/` and `.opencode/skills/`. The preferred layout is: + +```text +.opencode/skills//SKILL.md +``` + +Move the complete skill directory, not only `SKILL.md`, so relative scripts, references, and other supporting files remain +available. Keep the directory name stable to preserve the skill ID. Existing skill frontmatter and Markdown bodies do not +require a V2 rewrite. See [Skills](/docs/skills). + +### Instruction files + +Existing `AGENTS.md` files stay in place. V2 discovers the global `~/.config/opencode/AGENTS.md` and project `AGENTS.md` +files from the current directory up to the project root. + +If a V1 setup relied on a `CLAUDE.md` fallback, move that guidance into the applicable `AGENTS.md`. V2 currently only +discovers `AGENTS.md`; because non-API V1 behavior is intended to remain compatible, also run `/report` with the affected +project details. See [Instructions](/docs/instructions). + +## TUI configuration + +V1 loaded `tui.json(c)` from the global config directory and from project directories discovered while walking up from +the current directory. V2 instead stores CLI and TUI settings in one global file: + +```text +~/.config/opencode/cli.json +``` + +The CLI owns this file. The background service does not load it, and V2 does not discover or merge project-local +`tui.json(c)` or `cli.json` files. + +The native V2 format groups related settings. For example: + +```jsonc +// V1: ~/.config/opencode/tui.json +{ + "theme": "tokyonight", + "scroll_speed": 2, + "scroll_acceleration": { + "enabled": true + } +} + +// V2: ~/.config/opencode/cli.json +{ + "theme": { + "name": "tokyonight" + }, + "scroll": { + "speed": 2, + "acceleration": true + } +} +``` + +V2 migrates the global TUI configuration automatically. On the first CLI or TUI startup, when `cli.json` does not already +exist, it: + +- Reads `~/.config/opencode/tui.json`. +- Reads persisted TUI preferences from the legacy `kv.json` state file. +- Converts supported settings to the native grouped format and writes `~/.config/opencode/cli.json`. +- Leaves the V1 files unchanged so V1 can continue using them. + +Migration runs only while `cli.json` is absent. Once that file exists, V2 treats it as the source of truth and does not +continually synchronize later changes from `tui.json` or `kv.json`. If you created `cli.json` before starting V2, merge any +V1 settings you still need into it manually. + +Project-local V1 TUI configuration is not migrated because V2 has no project-local CLI configuration. Move settings you +still want into the global `cli.json`; when multiple projects used different values for the same setting, choose the +global behavior you want V2 to use. + +## Plugins + +Rename `plugin` to `plugins`. Replace a package-and-options tuple with an object: + +```jsonc +// V1 +{ + "plugin": [ + "opencode-example-plugin", + ["./plugin/local.ts", { "enabled": true }] + ] +} + +// V2 +{ + "plugins": [ + "opencode-example-plugin", + { + "package": "./plugin/local.ts", + "options": { "enabled": true } + } + ] +} +``` + +V2 discovers local plugins from both `.opencode/plugin/` and `.opencode/plugins/`; use `.opencode/plugins/` for V2 files. +Moving a file between these directories does not migrate its implementation. + +V1 plugins will not work in V2. + +The config entry can be translated automatically, but plugin implementation code must be ported to the new API. The V2 +plugin API is still being finalized during beta, and detailed plugin migration guidance will be published when it is +ready. + +Once the V2 plugin API is finalized, OpenCode should be able to migrate the majority of V1 plugins while keeping related +local modules and dependencies together. See the current beta [Plugins guide](/docs/build/plugins). + +## Server API and clients + +OpenCode 2 has a revised, more ergonomic server API and a new set of clients. Integrations that call the V1 server API +must migrate to the V2 API. + +Use the `@opencode-ai/client` package to access the new clients. The server API and clients are still being finalized +during beta, so their contracts may continue to change. See the generated [API reference](/docs/api) for the current endpoints, +request types, and responses. + +## Verify your setup + +Start `opencode2` in a project and verify your model, provider credentials, agents, permissions, MCP servers, and plugins +before relying on the beta for regular work. Keep your V1 setup until you have confirmed the V2 behavior you need, and do +not point V1 at configuration that you have converted to the native V2 shape. diff --git a/packages/www/content/docs/(docs)/models.mdx b/packages/www/content/docs/(docs)/models.mdx new file mode 100644 index 0000000000..26737745be --- /dev/null +++ b/packages/www/content/docs/(docs)/models.mdx @@ -0,0 +1,213 @@ +--- +title: "Models" +description: "" +--- + +OpenCode builds its model catalog from [Models.dev](https://models.dev), provider integrations, and your configuration. +Only enabled models whose provider is available for the current project appear in the model picker. + +Connect a provider with `/connect` in the TUI, or configure it in [Providers](/docs/providers). + +## Choose a model + +Open the model picker with `/models` or the default `m` keybind. The picker shows the models available from +providers connected to the current project. + +Select a model to use it in the current session. Switching models updates that session without changing your config. Use +the catalog entries shown in the picker rather than guessing a provider or model name. + +## Per-run model + +Select a model for one non-interactive run with `--model` or `-m`: + +```bash +opencode2 run --model openai/gpt-5.2 "Explain this repository" +opencode2 run -m openai/gpt-5.2#high "Review the current changes" +``` + +Agents and commands can also select their own model. See [Agents](/docs/agents) and [Commands](/docs/commands). + +## Variants + +Variants are named request overlays for one model, commonly used for reasoning effort or token budgets. Available names +are model-specific and are derived from current catalog metadata. Do not assume that names such as `low`, `high`, or +`max` exist for every model; `/variants` shows the valid choices. + +Use `/variants` to choose one for the current model, or press `ctrl+t` to cycle through available variants. + +## Configure + +### Default model + +Set `model` in `opencode.json` or `opencode.jsonc`: + +```jsonc title="opencode.jsonc" +{ + "$schema": "https://opencode.ai/config.json", + "model": "anthropic/claude-sonnet-4-5" +} +``` + +The configured model becomes the catalog default when its provider is available and the model is enabled. Otherwise, +session execution falls back to the newest available supported model. An explicit model already selected on a session +takes precedence over the default; switching models changes that session and does not rewrite your config. + +See [Config](/docs/config) for configuration locations and precedence. + +### Model settings + +Provider and model entries can supply three kinds of request configuration: + +- `settings` contains provider-package options such as `baseURL`, `reasoningEffort`, or `thinkingConfig`. +- `headers` adds HTTP request headers. +- `body` adds provider-specific fields to the request body. + +These values are provider-specific JSON. OpenCode applies provider values first, then model values, then the selected +variant. Nested `settings` and `body` objects are merged; later array and scalar values replace earlier values. Header +names are matched case-insensitively. + +You can also map a friendly catalog ID to a different API model ID with `modelID`: + +```jsonc title="opencode.jsonc" +{ + "$schema": "https://opencode.ai/config.json", + "model": "openai/coding-default", + "providers": { + "openai": { + "models": { + "coding-default": { + "modelID": "gpt-5.2", + "name": "Coding default", + "capabilities": { + "tools": true, + "input": ["text", "image"], + "output": ["text"] + }, + "limit": { + "context": 200000, + "output": 32000 + } + } + } + } + } +} +``` + +Here `openai/coding-default` is the selectable catalog reference, while `gpt-5.2` is sent to the provider. When adding a +model that is not already in the catalog, set accurate `capabilities` and `limit` values so OpenCode can expose tools and +enforce the correct context limits. Set `disabled: true` on a model entry to hide it from the available catalog. + +### Custom variants + +Add a variant, or override a catalog variant with the same ID, under the model's `variants` array: + +```jsonc title="opencode.jsonc" +{ + "$schema": "https://opencode.ai/config.json", + "providers": { + "openai": { + "models": { + "gpt-5.2": { + "settings": { + "reasoningEffort": "medium" + }, + "variants": [ + { + "id": "fast", + "settings": { + "reasoningEffort": "low" + } + }, + { + "id": "deep", + "settings": { + "reasoningEffort": "high", + "reasoningSummary": "auto" + } + } + ] + } + } + } + } +} +``` + +Variant entries support `settings`, `headers`, and `body`. Selecting one deeply overlays its values on the effective +provider and model configuration. An unknown variant fails model resolution instead of silently using the base model. + +### Local models + +For an OpenAI-compatible server, define a provider package, endpoint, and at least one model: + +```jsonc title="opencode.jsonc" +{ + "$schema": "https://opencode.ai/config.json", + "model": "local/coder", + "providers": { + "local": { + "name": "Local server", + "package": "aisdk:@ai-sdk/openai-compatible", + "settings": { + "baseURL": "http://127.0.0.1:1234/v1" + }, + "models": { + "coder": { + "modelID": "model-name-on-server", + "capabilities": { + "tools": true, + "input": ["text"], + "output": ["text"] + }, + "limit": { + "context": 32768, + "output": 8192 + } + } + } + } + } +} +``` + +Use the server's real model name, limits, modalities, and tool support. OpenCode cannot infer these for a model you add +manually. If the endpoint requires a key, add `apiKey` to provider `settings` using an environment substitution such as +`"apiKey": "{env:LOCAL_API_KEY}"`; do not commit secrets. + +### Model references + +Configuration and CLI options identify a model as `provider/model`, with an optional `#variant`: + +```text +openai/gpt-5.2 +openai/gpt-5.2#high +openrouter/anthropic/claude-sonnet-4.5#high +``` + +OpenCode splits the reference at the first `/`, so model IDs may contain additional slashes. Provider and model IDs are +case-sensitive. Provider IDs cannot contain `/` or `#`, and model IDs cannot contain `#`. + +The expanded config form is equivalent when generated or programmatic configuration is more convenient: + +```jsonc +{ + "model": { + "providerID": "openrouter", + "model": "anthropic/claude-sonnet-4.5" + } +} +``` + +Root, agent, and command `model` fields accept both forms. Use the IDs shown by `/models`, not provider display names. + +### Caveats + +- The selector object uses `model`, while a provider catalog entry uses `modelID` for the upstream API identifier. +- The root `model` currently sets the default provider and model only. Although its selection shape accepts a variant, + the V2 catalog default does not retain it; select a variant in the TUI, with `opencode2 run`, or on an agent or command. +- Model options are provider-specific. A setting accepted by one provider package may be ignored or rejected by another. +- Catalog data, credentials, and config are location-scoped. A model available in one project may be unavailable in + another. +- Configuration files are watched and normally reload automatically, but an in-flight model request keeps the settings + with which it started. diff --git a/packages/www/content/docs/(docs)/permissions.mdx b/packages/www/content/docs/(docs)/permissions.mdx new file mode 100644 index 0000000000..6b9659deb2 --- /dev/null +++ b/packages/www/content/docs/(docs)/permissions.mdx @@ -0,0 +1,209 @@ +--- +title: "Permissions" +description: "" +--- + +Permissions control whether an agent may perform an action on a resource. V2 +configuration uses the `permissions` field and an ordered array of rules. + + + The V1 object syntax uses different field and action names. Do not use + `permission`, `bash`, or `task` in V2 configuration; use `permissions`, + `shell`, and `subagent`. + + +## Rule schema + +Each rule has three required string fields: + +```jsonc +{ + "$schema": "https://opencode.ai/config.json", + "permissions": [ + { "action": "*", "resource": "*", "effect": "ask" }, + { "action": "read", "resource": "*", "effect": "allow" }, + { "action": "read", "resource": "*.env", "effect": "deny" }, + { "action": "shell", "resource": "git status *", "effect": "allow" }, + { "action": "shell", "resource": "git push *", "effect": "deny" }, + { "action": "edit", "resource": "packages/docs/*.mdx", "effect": "allow" } + ] +} +``` + +- `action` matches a tool permission action. +- `resource` matches the value the tool is trying to use, such as a path, + command, URL, query, or agent ID. +- `effect` is `"allow"`, `"deny"`, or `"ask"`. + +`allow` proceeds without prompting, `deny` blocks the operation, and `ask` +waits for a user decision. If no rule matches, the result is `ask`. + +## Matching and order + +Both `action` and `resource` support simple wildcards: + +- `*` matches zero or more characters, including `/`. +- `?` matches exactly one character. +- All other characters are literal. + +Matches cover the entire value. Slashes are normalized, and matching is +case-insensitive on Windows. For shell convenience, a pattern ending in +`" *"` also matches the command without arguments: `"git status *"` matches +both `git status` and `git status --short`. + +The **last matching rule wins**. Put broad rules first and exceptions later. +Rules from lower-priority configuration files are loaded first. OpenCode then +appends all global rules before agent-specific rules, so a matching agent rule +overrides a global rule. + +Some operations check several resources at once, such as a patch touching +multiple files. OpenCode denies the operation if any resource resolves to +`deny`; otherwise it asks if any resolves to `ask`; otherwise it allows it. + +## Actions and resources + +V2 action names are strings, so plugins may introduce additional actions. The +current built-in actions use these resources: + +| Action | Resource matched | +| --- | --- | +| `read` | Location-relative path for an internal file or directory; canonical absolute path for an external target | +| `edit` | Target path for `edit`, `write`, and `patch`; all three tools share this action | +| `glob` | The requested glob pattern | +| `grep` | The requested regular expression, not the search path | +| `shell` | The complete raw shell command string | +| `subagent` | The target agent ID | +| `skill` | The skill ID | +| `question` | `*` | +| `webfetch` | The requested URL | +| `websearch` | The search query | +| `external_directory` | A canonical external directory boundary, normally ending in `/*` | +| `_` | `*` for an MCP tool; unsupported characters in both names become `_` | +| `execute` | `*`; controls availability of the Code Mode dispatcher, while each nested tool still enforces its own permission | + +Built-in agent policy also reserves `plan_enter` and `plan_exit` for plan-mode +transitions. `doom_loop` and `lsp` are not current V2 Core permission actions. + +## External directories + +An external path requires a separate `external_directory` decision before the +tool's own `read` or `edit` decision. This applies to external paths used by +`read`, `edit`, `write`, and `patch`, and to an external `shell` working +directory. + +```jsonc +{ + "$schema": "https://opencode.ai/config.json", + "permissions": [ + { + "action": "external_directory", + "resource": "~/projects/reference/*", + "effect": "allow" + }, + { + "action": "read", + "resource": "~/projects/reference/*", + "effect": "allow" + }, + { + "action": "edit", + "resource": "~/projects/reference/*", + "effect": "deny" + } + ] +} +``` + +For `external_directory`, `read`, and `edit` resources, a leading `~`, `~/`, +`$HOME`, or `$HOME/` is expanded when configuration loads. Shell resources are +raw command text and are **not** home-expanded. + + + `shell` runs with the host user's filesystem, process, and network authority. + Its resource is raw text, not a parsed command. External command arguments + produce only best-effort warnings; `external_directory` is enforced for the + working directory, not every path embedded in a command. Prefer a narrow + shell allowlist over patterns intended to identify every dangerous command. + + +Relative mutation paths cannot escape the active Location, and symlink escapes +from inside it are rejected. Explicit external paths are canonicalized before +matching, so authorize only trusted directory boundaries. + +## Defaults + +The evaluator's fallback is `ask`, but shipped agents include ordered defaults: + +| Agent | Effective default policy | +| --- | --- | +| `build` | Allows most actions; asks for external directories and `.env` reads; allows questions and entering plan mode; denies exiting plan mode | +| `plan` | Uses the same base, allows questions and exiting plan mode, and denies edits except OpenCode plan files | +| `general` | Uses the base policy but cannot launch another subagent; questions and plan transitions remain denied | +| `explore` | Denies everything except `read`, `glob`, `grep`, `webfetch`, and `websearch`; cannot launch subagents and asks for external directories | +| Hidden maintenance agents | Deny all actions | + +The base read rules are ordered as follows: + +```jsonc +[ + { "action": "read", "resource": "*", "effect": "allow" }, + { "action": "read", "resource": "*.env", "effect": "ask" }, + { "action": "read", "resource": "*.env.*", "effect": "ask" }, + { "action": "read", "resource": "*.env.example", "effect": "allow" } +] +``` + +OpenCode also permits its managed tool-output and temporary directories where +needed. These exceptions do not grant general external-directory access. + +## Agent overrides + +Configure shared policy at the top level and append narrower rules to a named +agent under `agents..permissions`: + +```jsonc +{ + "$schema": "https://opencode.ai/config.json", + "permissions": [ + { "action": "shell", "resource": "*", "effect": "ask" }, + { "action": "shell", "resource": "git diff *", "effect": "allow" }, + { "action": "shell", "resource": "git status *", "effect": "allow" } + ], + "agents": { + "reviewer": { + "description": "Review code without changing it", + "mode": "subagent", + "permissions": [ + { "action": "edit", "resource": "*", "effect": "deny" }, + { "action": "shell", "resource": "git diff *", "effect": "allow" }, + { "action": "shell", "resource": "git status *", "effect": "allow" } + ] + } + } +} +``` + +Agent rules do not replace the global array; they are appended after it. A +custom subagent executes with its own permissions, not a permission subset +derived from the parent agent. + +## Approval choices + +When an `ask` rule matches, clients can reply with: + +- **Allow once** (`once`): approve only the pending request. +- **Allow always** (`always`): approve this request and save the patterns + proposed by the tool for the current project. +- **Reject** (`reject`): reject the request. Rejecting also rejects other + pending permission requests in the same session; clients may attach feedback. + +Saved approvals are durable and project-scoped. They are additional `allow` +rules, but they can never override a configured `deny`. The proposed saved +pattern may be broader than the displayed resource: several tools propose `*`, +shell proposes the exact command text, and skills and subagents propose their +IDs. Review the confirmation carefully and remove saved approvals that are no +longer needed. + +For non-interactive runs, `opencode2 run --auto` replies `once` to permission +requests. It does not save approvals, and explicit `deny` rules remain enforced. +Without `--auto`, a non-interactive run rejects permission requests. diff --git a/packages/www/content/docs/(docs)/providers.mdx b/packages/www/content/docs/(docs)/providers.mdx new file mode 100644 index 0000000000..c9b7000698 --- /dev/null +++ b/packages/www/content/docs/(docs)/providers.mdx @@ -0,0 +1,204 @@ +--- +title: "Providers" +description: "" +--- + +OpenCode builds its provider and model catalog from [Models.dev](https://models.dev), then applies the `providers` +overlays from your [configuration](/docs/config). A provider needs both a usable runtime package and, when required, an +active connection. + +## Connect a provider + +Run `/connect` in the TUI, choose an integration, and complete one of the methods it offers: + +```text +/connect +``` + +An integration may support an API key, OAuth, environment variables, or a combination of them. API keys and OAuth +tokens entered through `/connect` are stored by the OpenCode service in its database. Run `/connect` again to replace +or remove a stored credential. + +Providers from Models.dev also declare their standard environment variables. A non-empty declared variable is exposed +as an environment connection automatically, so common providers usually need no config: + +```bash +export ANTHROPIC_API_KEY="your-key" +``` + +For a custom provider, `env` declares the variables that can supply its key: + +```jsonc title="opencode.jsonc" +{ + "$schema": "https://opencode.ai/config.json", + "providers": { + "acme": { + "env": ["ACME_API_KEY"] + } + } +} +``` + +When several credential sources exist, OpenCode uses the stored credential first, then the first non-empty variable in +`env`, then `settings.apiKey`. Use config substitution instead of committing a literal key: + +```jsonc title="opencode.jsonc" +{ + "$schema": "https://opencode.ai/config.json", + "providers": { + "acme": { + "settings": { + "apiKey": "{env:ACME_API_KEY}" + } + } + } +} +``` + +Do not commit API keys or authorization headers to your repository. + +## Configure + +The `providers` object is keyed by provider ID. Each provider accepts these fields: + +| Field | Purpose | +| --- | --- | +| `name` | Display name. | +| `env` | Ordered environment variable names that provide a connection. | +| `package` | Runtime provider package. | +| `settings` | JSON settings passed to the runtime package, such as `baseURL`. | +| `headers` | String-valued HTTP headers added to requests. | +| `body` | JSON fields merged into request bodies. | +| `models` | Models to add or override, keyed by catalog model ID. | + +Configuration files are applied from lowest to highest precedence. `settings` and `body` are deep-merged. Headers are +merged case-insensitively. At request time, provider values are inherited by the model, model values override them, and +the selected variant is applied last. + +### Endpoint + +Override `settings.baseURL` to send an existing provider through a proxy or compatible endpoint. Its existing package, +models, and connection continue to apply: + +```jsonc title="opencode.jsonc" +{ + "$schema": "https://opencode.ai/config.json", + "providers": { + "anthropic": { + "settings": { + "baseURL": "https://llm-proxy.example.com/anthropic" + } + } + } +} +``` + +`settings` is package-specific. A field only has an effect when the selected package supports it. + +### Headers and body + +Headers and body fields can be set at provider, model, or variant scope: + +```jsonc title="opencode.jsonc" +{ + "$schema": "https://opencode.ai/config.json", + "providers": { + "openai": { + "headers": { + "X-Gateway-Tenant": "engineering" + }, + "body": { + "metadata": { + "application": "opencode" + } + }, + "models": { + "gpt-5.2": { + "headers": { + "X-Model-Policy": "coding" + } + } + } + } + } +} +``` + +These are request overlays, not a generic authentication scheme. Prefer `/connect`, `env`, or `settings.apiKey` for +provider credentials unless the endpoint explicitly requires a custom header. + +### Provider packages + +For an OpenAI-compatible service, use the V2 native compatible package. The model map is explicit because a custom +provider has no Models.dev catalog entries: + +```jsonc title="opencode.jsonc" +{ + "$schema": "https://opencode.ai/config.json", + "model": "acme/qwen3-coder", + "providers": { + "acme": { + "name": "Acme Gateway", + "env": ["ACME_API_KEY"], + "package": "@opencode-ai/llm/providers/openai-compatible", + "settings": { + "baseURL": "https://llm.acme.example/v1" + }, + "models": { + "qwen3-coder": { + "name": "Qwen 3 Coder", + "capabilities": { + "tools": true, + "input": ["text"], + "output": ["text"] + }, + "limit": { + "context": 131072, + "output": 32768 + } + } + } + } + } +} +``` + +Omit `env` for an endpoint that does not require authentication. The native compatible package requires +`settings.baseURL` and uses bearer authentication when a key is available. + +The `package` field supports two runtime contracts: + +| Form | Contract | +| --- | --- | +| `"@opencode-ai/llm/providers/openai-compatible"` | A V2 native package exporting `model(modelID, settings)`. An npm specifier or absolute `file://` URL may use the same contract. | +| `"aisdk:@ai-sdk/openai-compatible"` | An AI SDK provider package. The `aisdk:` prefix is required. | + +Native packages receive the merged `settings` plus the resolved `apiKey`, `headers`, `body`, and `limits`. AI SDK +packages receive their merged provider options. Use a package's own documentation for accepted settings; OpenCode does +not validate package-specific keys. + +`package` may also be set on one model to override the provider package for that model. + +### Models + +Add a model under a provider's `models` map. The object key is the model ID used in OpenCode; `modelID` is the ID sent to +the provider: + +```jsonc title="opencode.jsonc" +{ + "$schema": "https://opencode.ai/config.json", + "model": "openai/coding", + "providers": { + "openai": { + "models": { + "coding": { + "modelID": "gpt-5.2", + "name": "GPT-5.2 Coding" + } + } + } + } +} +``` + +See [Models](/docs/models) for model selection, defaults, capabilities, limits, costs, and variants. diff --git a/packages/www/content/docs/(docs)/references.mdx b/packages/www/content/docs/(docs)/references.mdx new file mode 100644 index 0000000000..ca904f7df2 --- /dev/null +++ b/packages/www/content/docs/(docs)/references.mdx @@ -0,0 +1,177 @@ +--- +title: "References" +description: "" +--- + +References give OpenCode named access to directories outside the current +project. Use them for documentation, shared libraries, examples, or source from +another repository. + +Configure references by alias in `opencode.json` or `opencode.jsonc`: + +```jsonc title="opencode.jsonc" +{ + "$schema": "https://opencode.ai/config.json", + "references": { + "docs": { + "path": "../product-docs", + "description": "Use for product behavior and terminology" + }, + "effect": { + "repository": "Effect-TS/effect", + "branch": "main", + "description": "Use for Effect implementation details" + } + } +} +``` + +## Local directories + +Use `path` for a local directory: + +```jsonc +{ + "references": { + "design-system": { + "path": "../design-system", + "description": "Use when working with components or design tokens" + } + } +} +``` + +Relative paths resolve from the directory containing the config file that +defines them. Absolute paths and home-relative paths such as `~/docs` are also +supported. + +The string shorthand is useful when no other fields are needed: + +```jsonc +{ + "references": { + "docs": "../docs", + "shared": "~/work/shared" + } +} +``` + + + A shorthand string is treated as a local path only when it starts with `.`, + `/`, or `~`. Use `./docs`, not `docs`; a bare `docs` value is interpreted as + a Git repository. + + +## Git repositories + +Use `repository` for a remote Git repository. GitHub `owner/repo` shorthand, +Git URLs, host/path forms, and SCP-style remotes are supported. + +```jsonc +{ + "references": { + "effect": { + "repository": "Effect-TS/effect", + "branch": "main" + }, + "internal-sdk": { + "repository": "git@gitlab.example.com:platform/sdk.git", + "branch": "release/v2" + } + } +} +``` + +Without `branch`, OpenCode checks out and refreshes the remote's default +branch. Branch names may contain letters, numbers, `/`, `_`, `.`, and `-`, but +cannot start with `-` or contain `..`. Local `file:` repositories are not +supported. + +Git references also support shorthand: + +```jsonc +{ + "references": { + "effect": "Effect-TS/effect", + "sdk": "gitlab.com/platform/sdk" + } +} +``` + +### Cloning and storage + +OpenCode normalizes a remote and stores one checkout under its global data +directory at `opencode/repos//`. On a typical Linux +installation, for example, `Effect-TS/effect` is stored at: + +```text +~/.local/share/opencode/repos/github.com/Effect-TS/effect +``` + +Missing repositories are cloned. Existing checkouts are fetched and reset to +the requested branch, or to the remote default branch when `branch` is omitted. +Materialization runs asynchronously when references load or reload, so a new +reference can appear before its checkout is ready. Clone and refresh failures +are logged and do not stop other references from loading. + + + The cache has one checkout per normalized remote, not one per branch. Do not + configure the same repository at multiple branches; only one branch can be + exposed. Avoid editing cached checkouts because a refresh resets them. + + +## Description and visibility + +`description` tells agents when a reference is relevant. References with a +description are included in agent instructions with their alias and resolved +path. References without one remain available in `@` autocomplete but are not +advertised automatically. + +Set `hidden` to `true` to remove a reference from TUI `@` autocomplete: + +```jsonc +{ + "references": { + "internal": { + "path": "../internal", + "description": "Use for internal service behavior", + "hidden": true + } + } +} +``` + +`hidden` controls only autocomplete visibility. It does not remove the +reference from the reference API or agent instructions when a description is +present. + +## Use references + +Type `@` in the TUI and select a reference alias to attach its root directory: + +```text +Compare the current implementation with @effect +``` + +The attachment provides a non-recursive listing of the root's immediate files +and directories. V2 currently attaches references by root alias; +`@alias/path` is not a reference-specific file browser. Ask the agent to +inspect a particular path when more detail is needed. + +References do not grant extra tool permissions. Access outside the active +Location remains subject to the agent's normal tool rules and the +`external_directory` permission. Editing a reference additionally requires the +applicable edit permission. + +## Fields + +| Field | Local | Git | Description | +| --- | --- | --- | --- | +| `path` | Required | No | Local directory path | +| `repository` | No | Required | Remote Git repository | +| `branch` | No | Optional | Branch to fetch and check out | +| `description` | Optional | Optional | Guidance describing when agents should use it | +| `hidden` | Optional | Optional | Hide it from TUI `@` autocomplete | + +An alias cannot be empty or contain `/`, `\`, whitespace, a backtick, or a +comma. diff --git a/packages/www/content/docs/(docs)/sharing.mdx b/packages/www/content/docs/(docs)/sharing.mdx new file mode 100644 index 0000000000..666d527bc0 --- /dev/null +++ b/packages/www/content/docs/(docs)/sharing.mdx @@ -0,0 +1,39 @@ +--- +title: "Session sharing" +description: "" +--- + +Session sharing is not yet available in OpenCode V2. V2 does not currently +publish sessions, upload conversation history to a sharing service, or create +public links. + + + The V2 TUI registers `/share`, but it currently only reports that sharing is unavailable. There is no functional + share/unshare command or server API endpoint. + + +## Configuration + +The V2 configuration schema accepts a `share` field with three values: + +```jsonc title="opencode.jsonc" +{ + "$schema": "https://opencode.ai/config.json", + "share": "manual" +} +``` + +- `"manual"` represents sharing only when explicitly requested. +- `"auto"` represents automatically sharing new sessions. +- `"disabled"` represents preventing session sharing. + +These values are parsed but are not acted on by the current V2 runtime. In +particular, setting `"auto"` does not publish sessions. If `share` is omitted, +V2 leaves the sharing policy unspecified. + +## Beta limitations + +V2 currently provides no public session viewer, share URL, history sync, +retention controls, or unshare/delete operation. Until those surfaces are +implemented in the V2 server and protocol, keep using sessions locally and do +not treat the `share` configuration field as a privacy or publishing control. diff --git a/packages/www/content/docs/(docs)/skills.mdx b/packages/www/content/docs/(docs)/skills.mdx new file mode 100644 index 0000000000..b2860a16c4 --- /dev/null +++ b/packages/www/content/docs/(docs)/skills.mdx @@ -0,0 +1,227 @@ +--- +title: "Skills" +description: "" +--- + +Skills are Markdown instructions that OpenCode can advertise to an agent and +load when they are relevant. A skill can include supporting scripts, +references, and other files in the same directory. + +## Create a skill + +Create one directory per skill with a `SKILL.md` file: + +```text +.opencode/skills/ +└── git-release/ + ├── SKILL.md + ├── scripts/ + │ └── changelog.ts + └── references/ + └── release-policy.md +``` + +```markdown title=".opencode/skills/git-release/SKILL.md" +--- +name: Git Release +description: Prepare release notes, version bumps, and GitHub releases +metadata: + opencode/slash: "true" +--- + +## Workflow + +1. Read `references/release-policy.md`. +2. Summarize merged changes since the previous tag. +3. Propose the version bump before changing files. +4. Run `scripts/changelog.ts` only after the user approves the version. +``` + +Paths in a skill are relative to the directory containing `SKILL.md`. + +## Discovery + +OpenCode automatically adds the following source directories: + +| Scope | Sources | +| --- | --- | +| Global | `~/.config/opencode/skills` | +| Global compatibility | `~/.claude/skills`, `~/.agents/skills` | +| Project | `.opencode/skills` | +| Project compatibility | `.claude/skills`, `.agents/skills` | + +For project sources, OpenCode searches from the current directory upward to +the project root and includes matching directories at every level. + +Within each source directory, OpenCode discovers: + +- Markdown files at the source root, such as `skills/git-release.md` +- `SKILL.md` files at any depth, such as `skills/git-release/SKILL.md` + +The directory form is recommended because it gives the skill a private base +directory for supporting files. + +## Configure sources + +Use the `skills` array in any `opencode.json` or `opencode.jsonc` to add local +directories or HTTP catalogs: + +```jsonc title="opencode.jsonc" +{ + "$schema": "https://opencode.ai/config.json", + "skills": [ + "./team-skills", + "~/shared/opencode-skills", + "/opt/company-skills", + "https://example.com/opencode/skills/" + ] +} +``` + +Relative paths are resolved from the active OpenCode working directory, not +from the directory containing the config file. Paths beginning with `~/` use +the current user's home directory. Only `http://` and `https://` values are +treated as URL sources. + +Every discovered config document contributes its `skills` entries; the arrays +are additive rather than replacing one another. + +### HTTP catalogs + +An HTTP source is a base URL containing an `index.json`: + +```json title="index.json" +{ + "skills": [ + { + "name": "git-release", + "version": "3", + "files": [ + "git-release.md", + "references/release-policy.md" + ] + } + ] +} +``` + +OpenCode downloads those files from +`/git-release/`. File paths must be safe, relative, +same-origin paths. Each entry must include either `SKILL.md` or a Markdown file +named after the index entry, such as `git-release.md`. + +Use the named Markdown form for HTTP catalogs. Each downloaded skill directory +is itself a source root, so `git-release.md` produces the ID `git-release`; a +root-level `SKILL.md` produces the literal ID `SKILL` in the current V2 +implementation. Increment `version` when files change so OpenCode refreshes +the cached copy. + +## Frontmatter + +V2 reads these fields: + +| Field | Purpose | +| --- | --- | +| `name` | Display name; defaults to the path-derived ID | +| `description` | Summary shown to the model and command catalog | +| `slash` | Set to `false` to hide the skill from the V2 slash-command catalog | +| `metadata.opencode/slash` | Boolean or `"true"`/`"false"`; overrides `slash` | +| `metadata.opencode/autoinvoke` | Set to `false` to omit the skill from model-facing discovery | + +Frontmatter, `name`, and `description` are optional at runtime. However, a +clear `description` is strongly recommended: skills without one are not +advertised to the model. `license`, `compatibility`, and other metadata may be +included for portability, but V2 does not interpret them. + +`opencode/autoinvoke: false` only removes the skill from the model's available +skills list. The skill remains registered and can still be activated explicitly +by its ID. + +## IDs and validation + +The skill ID comes from its path, not its frontmatter: + +| File | ID | +| --- | --- | +| `/git-release.md` | `git-release` | +| `/git-release/SKILL.md` | `git-release` | +| `/teams/release/SKILL.md` | `release` | + +IDs are exact and case-sensitive. V2 currently does not enforce the Agent +Skills name regex, length limits, a match between `name` and the directory, or +a maximum description length. The frontmatter `name` is only a display label. + +For portable, predictable skills, use a unique lowercase kebab-case ID of 1-64 +characters and keep it aligned with the directory name: + +```text +^[a-z0-9]+(-[a-z0-9]+)*$ +``` + +## Precedence + +Skills are keyed by ID. If several sources define the same ID, the later source +wins. Sources are registered in this order, from lower to higher precedence: + +1. Built-in skills +2. `.claude/skills` sources, global first and then from the current directory upward +3. `.agents/skills` sources, global first and then from the current directory upward +4. `~/.config/opencode/skills` +5. Project `.opencode/skills`, from the project root toward the current directory +6. Explicit `skills` config entries, in config priority and array order + +Avoid duplicate IDs unless an override is intentional. + +## Runtime loading + +At each model step, OpenCode advertises permitted skills that have a +description and do not set `opencode/autoinvoke` to `false`. The advertisement +contains only each skill's ID, name, and description; it does not add every +skill body to the prompt. + +When the model calls the `skill` tool with an exact ID, OpenCode: + +1. Resolves the current winning definition for that ID +2. Checks the `skill` permission for the selected agent +3. Adds the Markdown body, without frontmatter, to the conversation +4. Provides the skill's base directory and a sample of up to ten supporting file paths + +Supporting file contents are not loaded automatically. The agent can read a +referenced file when the skill instructs it to do so. The supporting-file +sample is available for directory-based `SKILL.md` skills; flat Markdown skills +receive no neighboring file list. + +In the V2 CLI, skills appear as `/id` commands unless `slash` resolves to +`false`. Selecting one appends the skill body as a skill message and resumes +the session. + +## Permissions + +Permission rules use the `skill` action and the skill ID as the resource. Rules +are evaluated in order, with the last matching rule winning: + +```jsonc title="opencode.jsonc" +{ + "permissions": [ + { "action": "skill", "resource": "*", "effect": "allow" }, + { "action": "skill", "resource": "internal-*", "effect": "deny" }, + { "action": "skill", "resource": "experimental-*", "effect": "ask" } + ] +} +``` + +`deny` removes matching skills from model-facing discovery and rejects skill +tool loading. `ask` advertises the skill but requests approval when the model +loads it. The same rules can be placed under an individual +`agents..permissions` array. + +## Troubleshooting + +If a skill is missing or loads the wrong content: + +1. Confirm the file is either a root-level `*.md` or a nested file named exactly `SKILL.md`. +2. Check the path-derived ID rather than the frontmatter `name`. +3. Add a `description` if the skill should be advertised to the model. +4. Check `opencode/autoinvoke` and the selected agent's `skill` permissions. +5. Look for a later source defining the same ID. +6. For HTTP catalogs, verify `index.json`, same-origin file paths, and a changed `version`. diff --git a/packages/www/content/docs/(docs)/snapshots.mdx b/packages/www/content/docs/(docs)/snapshots.mdx new file mode 100644 index 0000000000..6f178eb9cc --- /dev/null +++ b/packages/www/content/docs/(docs)/snapshots.mdx @@ -0,0 +1,108 @@ +--- +title: "Undo" +description: "" +--- + +OpenCode snapshots let the default interactive TUI roll back conversation history and related file changes. They are a +convenience for revising recent work, not a replacement for Git commits or backups. + +## Configuration + +Snapshots are enabled by default. Set `snapshots` to `false` in your [configuration](/docs/config#snapshots) to stop capturing +filesystem state: + +```jsonc title="opencode.jsonc" +{ + "$schema": "https://opencode.ai/config.json", + "snapshots": false +} +``` + +Filesystem snapshots require a Git repository. With snapshots disabled, unavailable, or missing, undo can still stage a +conversation rollback, but it has no captured file state to restore. Disabling snapshots does not delete snapshots that +were already stored. + +## What is captured + +For each model step, OpenCode attempts to capture the worktree immediately before the model call and after a cleanly +completed step. It records the paths changed between those two points on the assistant message. + +Snapshots use a separate internal Git object database in the OpenCode data directory. They do not create commits, move +branches, or intentionally modify your repository's Git index. Capture is limited to the session's active directory, which +may be a subdirectory of the repository. + +Within that directory, snapshots include tracked files and untracked files that are not ignored by Git. An individual +untracked file larger than 2 MiB is excluded. Ignored files, files outside the active directory, and changes to Git +metadata are not captured. + +During undo, OpenCode does not check out an entire tree. It restores only paths attributed to cleanly completed assistant +steps after the selected conversation boundary. Each path is restored to its state before the first affected step. + +## Undo + +Wait for the session to become idle, then run: + +```text +/undo +``` + +The TUI finds the latest non-empty user message and stages a revert at that message: + +- The selected user message and every later message are hidden, but not deleted yet. +- The selected message's text, attachments, and agent mentions are placed in the composer for revision. +- Captured files changed by the affected assistant steps are restored to their earlier contents. Files created by those + steps are removed when they did not exist in the earlier snapshot. +- A summary shows the staged message count and restored paths. + +Running `/undo` again moves the staged boundary to an earlier user message. OpenCode keeps the filesystem state from +immediately before the first undo as the redo baseline, so repeated undos form one wider staged revert rather than a redo +stack. + + + Sending a new prompt while an undo is staged commits the revert. The hidden message range is removed from the active + session history, the currently reverted files are kept, and redo is no longer available. + + +## Redo + +While a revert is staged, run: + +```text +/redo +``` + +Redo clears the staged boundary, makes the hidden messages visible again, and restores affected files to their exact state +immediately before the first undo. It does not rerun the model. After multiple undos, one redo restores the whole staged +range; there is no step-by-step redo stack. + +## Revert a message + +The TUI's **Message Actions** menu also provides **Revert**. It stages the selected message as the conversation boundary +and uses the same file restoration and redo behavior, but it does not copy that message into the composer. + +For a conversation-and-files rollback, select a user message. If an assistant message is selected, that message is hidden, +but only file changes attributed to later assistant steps are restored; the selected assistant message's own file changes +are not included. + +## Limitations and safety + +- Capture is best effort. A failed capture is logged and the model step continues, so conversation rollback may have no + matching file rollback. +- Interrupted or failed steps do not receive a completed end snapshot. File changes made before the failure may remain. +- Shell commands can change databases, services, processes, network resources, Git state, ignored build output, or files + outside the active directory. Undo and redo do not reverse those side effects. +- Undo overwrites the current contents of affected paths with older contents. Redo likewise overwrites those paths with + the pre-undo state, including edits made after running undo. +- Other processes can edit the worktree between capture and restore. The server rejects revert operations while the + session is actively running, but it cannot protect against external editors or commands. +- Snapshot objects can contain complete contents of tracked and non-ignored untracked files. They are stored locally in + the OpenCode data directory; do not treat snapshots as secret-free metadata. +- Undo is not secure erasure. Committing a revert removes messages from the active projection, not from durable session + history or existing snapshot storage. + +Review the staged file summary and your Git diff before continuing. Commit or back up important work independently before +using undo on a dirty worktree. + + + `/undo` and `/redo` are interactive TUI commands. The non-interactive `run` command does not provide them. + diff --git a/packages/www/content/docs/(docs)/troubleshooting.mdx b/packages/www/content/docs/(docs)/troubleshooting.mdx new file mode 100644 index 0000000000..dd3b4362fc --- /dev/null +++ b/packages/www/content/docs/(docs)/troubleshooting.mdx @@ -0,0 +1,177 @@ +--- +title: "Troubleshooting" +description: "Diagnose OpenCode startup, server, and session issues." +--- + + + You can ask OpenCode to debug itself. Describe the problem and ask it to use this troubleshooting page; it can read the + steps below, inspect its service and logs, and help identify the issue. + + +OpenCode runs as two processes: the TUI is a client, while a background server owns sessions, plugins, permissions, and +other application state. Start by determining whether an issue is in the client, the shared server, or a specific project. + +## Check the background service + +Show the current server status: + +```bash +opencode2 service status +``` + +Verify that its API is healthy: + +```bash +opencode2 api get /api/health +``` + +If the service is stuck or unhealthy, restart it: + +```bash +opencode2 service restart +``` + +From inside the TUI, run `/reload` to restart the managed service and reconnect: + +```text +/reload +``` + +You can also stop and start it explicitly: + +```bash +opencode2 service stop +opencode2 service start +``` + + + OpenCode normally discovers or starts the shared background service automatically. The service commands are only needed + when diagnosing its lifecycle. + + +## Run an isolated session + +Use standalone mode to run the TUI with a private server that exits with it: + +```bash +opencode2 --standalone +``` + +If an issue disappears in standalone mode, it is likely related to the shared background service rather than the TUI or +project itself. + +## Inspect the API + +The `api` command uses the same discovery and authentication flow as the TUI. It accepts either an HTTP method and path or +an OpenAPI operation ID. + +See the [API reference](/docs/api) for all endpoints and operation IDs. + +Pass a JSON request body with `--data` or `-d`, and add headers with `--header` or `-H`. + + + Running `opencode2 api` may start the background service when no compatible healthy service is available. + + +## Read logs + +Installed builds write logs to: + +```text +~/.local/share/opencode/log/opencode.log +``` + +Follow the log while reproducing the problem: + +```bash +tail -f ~/.local/share/opencode/log/opencode.log +``` + +Each line includes a process `run` ID and a `role` field. Use `role=cli` for TUI and command startup, and `role=server` for +session, provider, plugin, permission, and tool activity. + +```bash +grep 'role=cli' ~/.local/share/opencode/log/opencode.log +grep 'role=server' ~/.local/share/opencode/log/opencode.log +grep 'run=8fc3b1d5' ~/.local/share/opencode/log/opencode.log +``` + +Increase verbosity for one reproduction: + +```bash +OPENCODE_LOG_LEVEL=DEBUG opencode2 +``` + +## Service files + +The shared server registers itself at: + +```text +~/.local/state/opencode/service.json +``` + +Its private service configuration is stored separately at: + +```text +~/.config/opencode/service.json +``` + +The database normally lives at: + +```text +~/.local/share/opencode/opencode-next.db +``` + +`OPENCODE_DB` can override the database location. + + + Do not delete or edit service files or the database while troubleshooting. Use the service commands to manage the daemon, + and make a backup before inspecting persistent data with external tools. + + +## Explicit servers + +When connecting with `--server`, set `OPENCODE_PASSWORD` if the server requires authentication: + +```bash +OPENCODE_PASSWORD=secret opencode2 --server http://127.0.0.1:4096 +``` + +The CLI checks the server before opening the TUI and reports whether it is unreachable, requires a password, or rejected +the supplied password. + +## Report an issue + +Include the following when reporting a reproducible problem: + +- Output from `opencode2 --version` +- Output from `opencode2 service status` +- The smallest sequence of steps that reproduces the issue +- Whether the issue also occurs with `opencode2 --standalone` +- Relevant log lines, including their `run` and `role` fields + +Remove API keys, authorization headers, prompts, file contents, and other sensitive data before sharing logs. + +## Local development + +When working from the OpenCode repository, run V2 commands from the repository root: + +```bash +bun dev +``` + +The local development channel keeps its logs, SQLite database, and service registration separate from installed builds: + +```text +~/.local/share/opencode/log/opencode-local.log +~/.local/share/opencode/opencode-local.db +~/.local/state/opencode/service-local.json +``` + +Use the same diagnostics through the package development command: + +```bash +bun dev service status +bun dev service restart +bun dev api get /api/health +``` diff --git a/packages/www/content/docs/api/index.mdx b/packages/www/content/docs/api/index.mdx new file mode 100644 index 0000000000..102ff30324 --- /dev/null +++ b/packages/www/content/docs/api/index.mdx @@ -0,0 +1,6 @@ +--- +title: "API Reference" +description: "OpenCode HTTP API." +--- + +The endpoint reference is generated from the current OpenCode V2 [OpenAPI specification](/openapi.json). diff --git a/packages/www/content/docs/api/meta.json b/packages/www/content/docs/api/meta.json new file mode 100644 index 0000000000..bd04fc44ff --- /dev/null +++ b/packages/www/content/docs/api/meta.json @@ -0,0 +1,6 @@ +{ + "title": "API", + "description": "Use the OpenCode HTTP API.", + "root": true, + "pages": ["index"] +} diff --git a/packages/www/content/docs/build/client.mdx b/packages/www/content/docs/build/client.mdx new file mode 100644 index 0000000000..0c251ca386 --- /dev/null +++ b/packages/www/content/docs/build/client.mdx @@ -0,0 +1,145 @@ +--- +title: "Client" +description: "Connect an application to the OpenCode HTTP API." +--- + +`@opencode-ai/client` is the generated TypeScript client for the OpenCode HTTP +API. Use it when your application connects to an OpenCode server over the +network. Its types and methods are generated from the same contract as the +[API reference](/docs/api). + + + The V2 API and client are beta. Method names, inputs, and outputs may change + before the stable release. + + +## Install + +```sh +bun add @opencode-ai/client@next +``` + +## Create a client + +Create a client with the server URL, then call methods grouped by API resource: + +```ts +import { OpenCode } from "@opencode-ai/client" + +const client = OpenCode.make({ + baseUrl: "http://localhost:4096", +}) + +const session = await client.session.create({ + location: { directory: "/workspace" }, +}) + +await client.session.prompt({ + sessionID: session.id, + text: "Review the current changes", +}) +``` + +## Headers and requests + +Pass default authentication or application headers to `OpenCode.make` with +`headers`. You can also supply a custom `fetch` implementation. Each operation +accepts request options as its final argument for an `AbortSignal` or +per-request headers. + +```ts +const client = OpenCode.make({ + baseUrl: "https://opencode.example.com", + headers: { + authorization: `Bearer ${process.env.OPENCODE_TOKEN}`, + }, +}) + +await client.session.list(undefined, { + signal: AbortSignal.timeout(10_000), +}) +``` + +## Stream events + +Streaming endpoints return async iterables: + +```ts +for await (const event of client.event.subscribe()) { + console.log(event.type) +} +``` + +## Effect + +OpenCode provides a first-class Effect client through the +`@opencode-ai/client/effect` entrypoint. It returns typed Effects and Streams +and decodes responses into OpenCode schema values. + +```sh +bun add @opencode-ai/client@next effect +``` + +### Create a client + +```ts +import { AbsolutePath, Location, OpenCode } from "@opencode-ai/client/effect" +import { Effect } from "effect" +import { FetchHttpClient } from "effect/unstable/http" + +const program = Effect.gen(function* () { + const client = yield* OpenCode.make({ baseUrl: "http://localhost:4096" }) + const session = yield* client.session.create({ + location: Location.Ref.make({ + directory: AbsolutePath.make("/workspace"), + }), + }) + + return yield* client.session.get({ sessionID: session.id }) +}) + +const session = await Effect.runPromise( + program.pipe(Effect.provide(FetchHttpClient.layer)), +) +``` + +Streaming operations, including `client.event.subscribe()` and +`client.session.log(...)`, return Effect `Stream` values. + +### Service + +`Service` discovers and manages the local OpenCode background service from a +Node application: + +- `Service.discover()` returns a healthy registered endpoint without starting + a process. +- `Service.start()` reuses a compatible service or starts one when needed. +- `Service.stop()` stops the registered service. +- `Service.headers(endpoint)` creates the authentication headers for a client. + +```sh +bun add @effect/platform-node +``` + +```ts +import { NodeFileSystem } from "@effect/platform-node" +import { OpenCode, Service } from "@opencode-ai/client/effect" +import { Effect } from "effect" +import { FetchHttpClient } from "effect/unstable/http" + +const program = Effect.gen(function* () { + const endpoint = yield* Service.start() + const client = yield* OpenCode.make({ + baseUrl: endpoint.url, + headers: Service.headers(endpoint), + }) + return yield* client.health.get() +}) + +const health = await Effect.runPromise( + program.pipe( + Effect.provide(FetchHttpClient.layer), + Effect.provide(NodeFileSystem.layer), + ), +) +``` diff --git a/packages/www/content/docs/build/index.mdx b/packages/www/content/docs/build/index.mdx new file mode 100644 index 0000000000..ee92d3acdc --- /dev/null +++ b/packages/www/content/docs/build/index.mdx @@ -0,0 +1,24 @@ +--- +title: "Build" +description: "Build on the engine used by millions daily." +mode: "wide" +--- + + + + Build plugins that add tools, integrations, commands, agents, and custom behavior while keeping the rest of OpenCode + intact. + + + Connect to OpenCode with the same client used by the TUI and desktop app, then build any interface, workflow, or agent + experience around it. + + + Embed OpenCode directly into your application and build a completely custom agent, interface, or developer product + around it. + + + + + The plugin API, client, and SDK are still being finalized during beta and may change before OpenCode 2.0 is stable. + diff --git a/packages/www/content/docs/build/meta.json b/packages/www/content/docs/build/meta.json new file mode 100644 index 0000000000..44436a37ea --- /dev/null +++ b/packages/www/content/docs/build/meta.json @@ -0,0 +1,7 @@ +{ + "title": "Build", + "description": "Extend and embed OpenCode.", + "root": true, + "defaultOpen": true, + "pages": ["index", "plugins", "client", "sdk"] +} diff --git a/packages/www/content/docs/build/plugins.mdx b/packages/www/content/docs/build/plugins.mdx new file mode 100644 index 0000000000..e5adbc92d7 --- /dev/null +++ b/packages/www/content/docs/build/plugins.mdx @@ -0,0 +1,430 @@ +--- +title: "Plugins" +description: "Extend OpenCode with plugins." +--- + +Plugins extend OpenCode in-process. They can transform agents, models, commands, +integrations, references, skills, and tools; intercept model requests and tool +execution; and call a subset of the V2 client. + + + The V2 plugin API is beta. Entrypoints, hooks, draft shapes, and configuration may change before the stable release. + Use the `/v2` exports described on this page. + + +## Load plugins + +Plugins can be loaded from npm packages, explicit local paths, or config +directories. Each module must have one default export containing a unique +plugin `id` and a `setup` function. + +### Configuration + +Add ordered entries to the `plugins` field in `opencode.json(c)`: + +```jsonc title="opencode.jsonc" +{ + "$schema": "https://opencode.ai/config.json", + "plugins": [ + "opencode-acme-plugin@1.2.0", + "@acme/opencode-plugin", + "./plugins/local.ts", + { + "package": "./plugins/reviewer.ts", + "options": { + "agent": "reviewer", + "strict": true, + }, + }, + ], +} +``` + +A string is either a package specifier or a local path. Local paths must start +with `./` or `../` and resolve relative to the configuration file containing +the entry. Absolute paths and `file://` URLs are also supported. Both scoped +packages and versioned package specifiers are supported. + +Use the object form to pass JSON configuration to the plugin. OpenCode passes +`options` unchanged as `ctx.options`; omitted options become an empty object. +The plugin owns validation and defaults for its options. + +See [Config](/docs/config#locations) for configuration locations and precedence. +Entries from all applicable files are processed from lowest to highest +precedence rather than replacing the entire array. + +### Local discovery + +OpenCode automatically scans this directory in every discovered OpenCode config +directory: + +```text +.opencode/plugins/ +``` + +The equivalent global directory is `~/.config/opencode/plugins/`. Direct `.ts` +and `.js` children are loaded. An immediate child directory is also loaded as a +package when OpenCode can resolve a string `exports`, `module`, or `main` +entrypoint, or an `index.ts` or `index.js` file. + +A `plugins/` directory beside a project-root `opencode.json` is not discovered +automatically. Put it under `.opencode/`, or add its file explicitly with a +relative config entry. + +### Enable and disable + +A string beginning with `-` disables plugins by their exported `id`. `*` +matches every ID, and a suffix of `.*` matches an ID prefix. Directives are +applied in order: + +```jsonc title="opencode.jsonc" +{ + "plugins": ["./plugins/reviewer.ts", "-acme.reviewer", "-opencode.provider.*", "opencode.provider.openai"], +} +``` + +Package specifiers and local paths locate plugin modules; they are not disable +selectors. Use the `id` from the plugin's default export to disable it. A later +ID entry re-enables a loaded or built-in plugin. Explicit config directives run +after local auto-discovery, so they can disable discovered plugins by ID. + +User plugins are activated in configured order between OpenCode's internal +plugin phases. Hooks run sequentially in registration order, and later hooks +observe earlier mutations. Do not depend on the internal phase ordering while +the API is beta. + +### Installation and dependencies + +OpenCode installs bare package entries and their production dependencies into +an isolated cache. Package installation does not run lifecycle scripts. +Published packages should expose their plugin entrypoint and include every +runtime import in `dependencies`. + +Local files and local package directories are imported directly. OpenCode does +**not** install their dependencies. Install dependencies in a `package.json` +visible from the plugin file, for example: + +```sh +cd .opencode +bun add @opencode-ai/plugin@next +``` + +Match the plugin package version to the OpenCode release you target. + +Configuration and discovered plugin files under watched config directories are +reloaded when they change. Reloading replaces the active plugin generation and +releases its scoped registrations. Restart OpenCode after changing an npm +package version or a local dependency when no watched file changed. + +## Create a plugin + +Export the result of `Plugin.define` as the module default: + +```ts title=".opencode/plugins/reviewer.ts" +import { Plugin } from "@opencode-ai/plugin/v2" + +export default Plugin.define({ + id: "acme.reviewer", + setup: async (ctx) => { + const description = + typeof ctx.options.description === "string" ? ctx.options.description : "Reviews code for regressions" + + await ctx.agent.transform((agents) => { + agents.update("reviewer", (agent) => { + agent.description = description + agent.mode = "subagent" + }) + }) + }, +}) +``` + +`setup` runs each time the plugin is activated. Register long-lived behavior +during setup; do not wait there on an infinite event stream. It may return a +synchronous or asynchronous cleanup function. OpenCode awaits that cleanup +when the plugin is disabled, reloaded, or shut down: + +```ts +setup: async (ctx) => { + const controller = new AbortController() + const task = synchronize(ctx, controller.signal) + + return async () => { + controller.abort() + await task + } +} +``` + +Hook registrations are released automatically with the same plugin scope. Use +the returned cleanup for resources the plugin owns, such as timers, watchers, +connections, and background tasks. + +### Context + +The plugin context is essentially an [OpenCode server client](/docs/build/client). +Its read and action methods use the same inputs and responses as the client. It +adds plugin-only methods for transforms, runtime hooks, reloads, registrations, +and plugin options. + +| Capability | Available operations | +| ---------------------- | -------------------------------------------------------------------------------------------- | +| `ctx.agent` | `list`, `transform`, `reload` | +| `ctx.catalog.provider` | `list`, `get` | +| `ctx.catalog.model` | `list`, `default` | +| `ctx.catalog` | `transform`, `reload` | +| `ctx.command` | `list`, `transform`, `reload` | +| `ctx.integration` | `list`, `get`, `connect`, `attempt`, `transform`, `reload`, and connection lookup/resolution | +| `ctx.plugin` | `list` currently active plugin IDs | +| `ctx.reference` | `list`, `transform`, `reload` | +| `ctx.session` | `create`, `get`, `prompt`, `command`, `interrupt`, and `hook` | +| `ctx.skill` | `list`, `transform`, `reload` | +| `ctx.tool` | `transform` and `hook` | +| `ctx.aisdk` | `hook` | +| `ctx.event` | `subscribe` to the current public server event stream | +| `ctx.options` | Readonly options from the matching config object | + +### Transform hooks + +Transform hooks let a plugin modify how OpenCode is configured. Use them to add +or remove definitions, override settings, choose defaults, and provide tools or +other sources. + +| Transform | Draft operations | +| ----------------------- | ------------------------------------------------------------------------------------------------------- | +| `agent.transform` | `list`, `get`, `default`, `update`, `remove` | +| `catalog.transform` | Provider `list`, `get`, `update`, `remove`; model `get`, `update`, `remove`; default model `get`, `set` | +| `command.transform` | `list`, `get`, `update`, `remove` | +| `integration.transform` | Integration `list`, `get`, `update`, `remove`; method `list`, `update`, `remove` | +| `reference.transform` | `add`, `remove`, `list` | +| `skill.transform` | `source`, `list` | +| `tool.transform` | `add` | + +Here's an example that keeps models synced from a remote source: + +```js title=".opencode/plugins/remote-models.js" +import { Plugin } from "@opencode-ai/plugin/v2" + +export default Plugin.define({ + id: "acme.remote-models", + setup: async (ctx) => { + let models = [] + + await ctx.catalog.transform((catalog) => { + for (const model of models) { + catalog.model.update(model.providerID, model.id, (draft) => Object.assign(draft, model)) + } + }) + + const refresh = async () => { + const response = await fetch("https://example.com/opencode/models.json", { + signal: AbortSignal.timeout(10_000), + }) + if (!response.ok) return + models = await response.json() + await ctx.catalog.reload() + } + + await refresh() + const timer = setInterval(() => void refresh().catch(console.error), 60_000) + return () => clearInterval(timer) + }, +}) +``` + +`ctx.catalog.reload()` replays every catalog transform to derive the new +catalog. Each plugin's logic remains composed with the others, so a later +plugin can still modify models added by an earlier one. The catalog updates +without restarting OpenCode. + +### Runtime hooks + +Runtime hooks intercept live operations. Their event objects expose specific +mutable fields: + +| Hook | Mutable fields | +| ------------------------------------------- | ------------------------------------------------------------------------------ | +| `ctx.aisdk.hook("sdk", callback)` | `sdk`, after inspecting `model`, `package`, and `options` | +| `ctx.aisdk.hook("language", callback)` | `language`, after inspecting `model`, `sdk`, and `options` | +| `ctx.session.hook("request", callback)` | `system`, `messages`, and the `tools` record immediately before model dispatch | +| `ctx.tool.hook("execute.before", callback)` | `input`, before the selected tool executes | +| `ctx.tool.hook("execute.after", callback)` | `result`, `output`, and `outputPaths`, after execution settles | + +For example, remove a tool from selected model requests and normalize another +tool's input: + +```ts title=".opencode/plugins/guards.ts" +import { Plugin } from "@opencode-ai/plugin/v2" + +export default Plugin.define({ + id: "acme.guards", + setup: async (ctx) => { + await ctx.session.hook("request", (event) => { + delete event.tools.write + }) + + await ctx.tool.hook("execute.before", (event) => { + if (event.tool !== "lookup" || typeof event.input !== "object" || event.input === null) return + event.input = { ...event.input, source: "plugin" } + }) + }, +}) +``` + +A hook failure fails the operation it intercepts. Keep runtime hooks fast and +handle expected errors inside the callback. + +## Examples + +### Add a tool + +Pass a tool declaration to `tools.add`. Define its input with JSON Schema and +use an async executor: + +```js title=".opencode/plugins/greeting.js" +import { Plugin } from "@opencode-ai/plugin/v2" + +export default Plugin.define({ + id: "acme.greeting", + setup: async (ctx) => { + await ctx.tool.transform((tools) => { + tools.add({ + name: "greeting", + description: "Create a greeting", + jsonSchema: { + type: "object", + properties: { + name: { type: "string" }, + }, + required: ["name"], + additionalProperties: false, + }, + execute: async ({ name }) => { + const text = `Hello, ${name}!` + return { + structured: { greeting: text }, + content: [{ type: "text", text }], + } + }, + }) + }) + }, +}) +``` + +Unsupported characters in tool and group names are normalized to underscores. +The resulting exposed key must begin with a letter and contain at most 64 +letters, digits, underscores, or hyphens. Set `options` on the declaration to +configure registration with `{ group, deferred }`: + +- `group` prefixes and groups the exposed tool name. +- `deferred: true` makes the tool available through the deferred `execute` + tool instead of exposing it directly. + +The executor receives a second context argument containing `sessionID`, +`agent`, `assistantMessageID`, and `toolCallID`. + +### Add a command + +```js title=".opencode/plugins/review-command.js" +import { Plugin } from "@opencode-ai/plugin/v2" + +export default Plugin.define({ + id: "acme.review-command", + setup: async (ctx) => { + await ctx.command.transform((commands) => { + commands.update("review", (command) => { + command.description = "Review the current changes" + command.template = "Review the current changes for correctness and missing tests." + }) + }) + }, +}) +``` + +### Set the default model + +```js title=".opencode/plugins/default-model.js" +import { Plugin } from "@opencode-ai/plugin/v2" + +export default Plugin.define({ + id: "acme.default-model", + setup: async (ctx) => { + await ctx.catalog.transform((catalog) => { + catalog.model.default.set("anthropic", "claude-sonnet-4-5") + }) + }, +}) +``` + +## Publish a package + +A package plugin uses the same default export as a local plugin. A minimal +manifest is: + +```json title="package.json" +{ + "name": "opencode-acme-plugin", + "version": "1.0.0", + "type": "module", + "exports": "./src/index.ts", + "dependencies": { + "@opencode-ai/plugin": "next" + } +} +``` + +Use versions compatible with the OpenCode release you target and test the +installed package, not only a workspace-linked copy. Because the plugin API is +beta, publish compatible plugin updates when V2 entrypoints or contracts +change. + +## Verify loading + +List active plugin IDs through the V2 API: + +```sh +opencode2 api get /api/plugin +``` + +If a plugin is absent, check the server log described in +[Troubleshooting](/docs/troubleshooting#read-logs). Invalid modules and setup failures are +logged; one failing package does not prevent unrelated valid packages from +being resolved. + +## Effect + +OpenCode provides a first-class Effect API for plugins through the +`@opencode-ai/plugin/v2/effect` entrypoint. Install `effect` alongside the +plugin package and export an `effect` function instead of `setup`: + +```sh +bun add @opencode-ai/plugin@next effect +``` + +```ts title=".opencode/plugins/reviewer-effect.ts" +import { Plugin } from "@opencode-ai/plugin/v2/effect" +import { Effect } from "effect" + +export default Plugin.define({ + id: "acme.reviewer-effect", + effect: (ctx) => + Effect.gen(function* () { + yield* ctx.agent.transform((agents) => { + agents.update("reviewer", (agent) => { + agent.description = "Reviews code for regressions" + agent.mode = "subagent" + }) + }) + }), +}) +``` + +Context operations return Effects. The plugin effect is scoped, so finalizers, +fibers, and registrations are released when the plugin reloads or unloads. +OpenCode does not expose its private Core services to the plugin; use the +capabilities on `ctx`. + +Typed tools can use `Schema` from `effect` and the contracts exported from +`@opencode-ai/plugin/v2/effect/tool`. Their executors return an Effect and may +fail with the typed tool failure channel. diff --git a/packages/www/content/docs/build/sdk.mdx b/packages/www/content/docs/build/sdk.mdx new file mode 100644 index 0000000000..0262cc3c78 --- /dev/null +++ b/packages/www/content/docs/build/sdk.mdx @@ -0,0 +1,75 @@ +--- +title: "SDK" +description: "Embed an OpenCode host in an Effect application." +--- + +`@opencode-ai/sdk-next` is the Effect-native SDK for applications that need to +host OpenCode in-process. Unlike the [network client](/docs/build/client), it assembles the +OpenCode server and routes API calls through its HTTP router in memory. It opens +no HTTP listener and adds no network hop between the client and server. + + + The V2 SDK is beta and currently private to the OpenCode workspace. It is not + published for external installation yet, and its package name and API may + change before release. + + +## Create a host + +`OpenCode.create()` creates a scoped host. Closing its Effect Scope releases +the router, location services, fibers, and scoped plugin registrations. + +```ts +import { + AbsolutePath, + Location, + OpenCode, +} from "@opencode-ai/sdk-next" +import { Effect } from "effect" + +const program = Effect.scoped( + Effect.gen(function* () { + const opencode = yield* OpenCode.create() + const session = yield* opencode.sessions.create({ + location: Location.Ref.make({ + directory: AbsolutePath.make("/workspace"), + }), + }) + + return yield* opencode.sessions.get({ sessionID: session.id }) + }), +) + +const session = await Effect.runPromise(program) +``` + +The embedded host uses the same routes, middleware, codecs, errors, and schema +values as `@opencode-ai/client/effect`. It exposes the full generated client and +adds the convenience aliases `sessions` and `events` for the session and event +groups. + +## Use as a service + +Use `OpenCode.layer` when the host should be provided through Effect dependency +injection: + +```ts +import { OpenCode } from "@opencode-ai/sdk-next" +import { Effect } from "effect" + +const program = Effect.gen(function* () { + const opencode = yield* OpenCode.Service + return yield* opencode.sessions.active() +}) + +const active = await Effect.runPromise( + program.pipe(Effect.provide(OpenCode.layer)), +) +``` + +## Register plugins + +Call `opencode.plugin(...)` to register an embedded V2 plugin. Embedded plugins +use the same discovery and location-scoped activation path as configured +plugins. The SDK also exports `Tool` for plugin-defined tools. See the +[Plugins guide](/docs/build/plugins) for the plugin shape and available hooks. diff --git a/packages/www/content/docs/meta.json b/packages/www/content/docs/meta.json new file mode 100644 index 0000000000..0971e69803 --- /dev/null +++ b/packages/www/content/docs/meta.json @@ -0,0 +1,4 @@ +{ + "title": "Documentation", + "pages": ["(docs)", "build", "api"] +} diff --git a/packages/www/package.json b/packages/www/package.json new file mode 100644 index 0000000000..de5219829b --- /dev/null +++ b/packages/www/package.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://json.schemastore.org/package.json", + "name": "@opencode-ai/www", + "version": "1.17.18", + "private": true, + "type": "module", + "scripts": { + "dev": "vite dev --host 0.0.0.0 --port 3000", + "build": "vite build", + "preview": "vite preview --host 0.0.0.0", + "pretypecheck": "fumadocs-mdx", + "typecheck": "tsgo --noEmit" + }, + "dependencies": { + "@cloudflare/vite-plugin": "1.44.0", + "@tailwindcss/vite": "4.3.2", + "@tanstack/react-router": "1.170.17", + "@tanstack/react-start": "1.168.27", + "@tanstack/router-plugin": "1.168.19", + "fumadocs-core": "16.11.1", + "fumadocs-mdx": "15.1.0", + "fumadocs-ui": "16.11.1", + "react": "19.2.7", + "react-dom": "19.2.7", + "tailwindcss": "4.3.2", + "vite": "8.1.4" + }, + "devDependencies": { + "@types/mdx": "2.0.14", + "@types/node": "catalog:", + "@types/react": "19.2.17", + "@types/react-dom": "19.2.3", + "@typescript/native-preview": "catalog:", + "@vitejs/plugin-react": "6.0.3", + "typescript": "catalog:", + "wrangler": "4.110.0" + }, + "engines": { + "node": ">=22" + } +} diff --git a/packages/www/public/assets/favicon.svg b/packages/www/public/assets/favicon.svg new file mode 100644 index 0000000000..1beb80483d --- /dev/null +++ b/packages/www/public/assets/favicon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/www/public/assets/logo-dark.svg b/packages/www/public/assets/logo-dark.svg new file mode 100644 index 0000000000..9812b2308d --- /dev/null +++ b/packages/www/public/assets/logo-dark.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/www/public/assets/logo-light.svg b/packages/www/public/assets/logo-light.svg new file mode 100644 index 0000000000..0fa652b24b --- /dev/null +++ b/packages/www/public/assets/logo-light.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/www/public/openapi.json b/packages/www/public/openapi.json new file mode 100644 index 0000000000..543a418173 --- /dev/null +++ b/packages/www/public/openapi.json @@ -0,0 +1,27151 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "opencode HttpApi", + "version": "0.0.1", + "description": "Experimental HttpApi surface for selected instance routes." + }, + "paths": { + "/api/health": { + "get": { + "tags": [ + "health" + ], + "operationId": "v2.health.get", + "parameters": [], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "healthy": { + "type": "boolean", + "enum": [ + true + ] + }, + "version": { + "type": "string" + }, + "pid": { + "type": "integer", + "allOf": [ + { + "exclusiveMinimum": 0 + } + ] + } + }, + "required": [ + "healthy", + "version", + "pid" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "Check whether the API server is ready to accept requests.", + "summary": "Check server health" + } + }, + "/api/server": { + "get": { + "tags": [ + "server" + ], + "operationId": "v2.server.get", + "parameters": [], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "urls": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "urls" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "Return the URLs that can be used to connect to this server.", + "summary": "Get server information" + } + }, + "/api/location": { + "get": { + "tags": [ + "location" + ], + "operationId": "v2.location.get", + "parameters": [ + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Location.Info", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Location.Info" + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "Resolve the requested location or the server default location.", + "summary": "Get location" + } + }, + "/api/agent": { + "get": { + "tags": [ + "agent" + ], + "operationId": "v2.agent.list", + "parameters": [ + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "location": { + "$ref": "#/components/schemas/Location.Info" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Agent.Info" + } + } + }, + "required": [ + "location", + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "Retrieve currently registered agents.", + "summary": "List agents" + } + }, + "/api/plugin": { + "get": { + "tags": [ + "plugin" + ], + "operationId": "v2.plugin.list", + "parameters": [ + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "location": { + "$ref": "#/components/schemas/Location.Info" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Plugin.Info" + } + } + }, + "required": [ + "location", + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "Retrieve currently loaded plugins.", + "summary": "List plugins" + } + }, + "/api/session": { + "get": { + "tags": [ + "session" + ], + "operationId": "v2.session.list", + "parameters": [ + { + "name": "workspace", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "string", + "allOf": [ + { + "pattern": "^wrk" + } + ] + }, + { + "type": "null" + } + ] + }, + "required": false + }, + { + "name": "limit", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Maximum number of sessions to return. Defaults to the newest 50 sessions." + }, + "required": false + }, + { + "name": "order", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "string", + "enum": [ + "asc", + "desc" + ] + }, + { + "type": "null" + } + ], + "description": "Session order for the first page. Use desc for newest first or asc for oldest first." + }, + "required": false + }, + { + "name": "search", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "required": false + }, + { + "name": "parentID", + "in": "query", + "schema": { + "anyOf": [ + { + "anyOf": [ + { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + { + "type": "string", + "enum": [ + "null" + ] + } + ], + "description": "Filter by parent session. Use null to return only root sessions." + }, + { + "type": "null" + } + ] + }, + "required": false + }, + { + "name": "directory", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "required": false + }, + { + "name": "project", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "required": false + }, + { + "name": "subpath", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "required": false + }, + { + "name": "cursor", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "string", + "description": "Opaque pagination cursor returned as cursor.previous or cursor.next in the previous response." + }, + { + "type": "null" + } + ] + }, + "required": false + } + ], + "security": [], + "responses": { + "200": { + "description": "SessionsResponse", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionsResponse" + } + } + } + }, + "400": { + "description": "InvalidCursorError | InvalidRequestError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/InvalidCursorError" + }, + { + "$ref": "#/components/schemas/InvalidRequestError1" + }, + { + "$ref": "#/components/schemas/InvalidRequestError" + } + ] + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "Retrieve sessions in the requested order. Items keep that order across pages; use cursor.next or cursor.previous to move through the ordered list.", + "summary": "List sessions" + }, + "post": { + "tags": [ + "session" + ], + "operationId": "v2.session.create", + "parameters": [], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/Session.Info" + } + }, + "required": [ + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "Create a session at the requested location.", + "summary": "Create session", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "anyOf": [ + { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + { + "type": "null" + } + ] + }, + "agent": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "model": { + "anyOf": [ + { + "$ref": "#/components/schemas/Model.Ref" + }, + { + "type": "null" + } + ] + }, + "location": { + "anyOf": [ + { + "$ref": "#/components/schemas/Location.Ref" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/api/session/active": { + "get": { + "tags": [ + "session" + ], + "operationId": "v2.session.active", + "parameters": [], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "object", + "patternProperties": { + "^ses": { + "$ref": "#/components/schemas/SessionActive" + } + } + } + }, + "required": [ + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "Retrieve foreground Session drains currently owned by this OpenCode process. Sessions absent from the result are inactive.", + "summary": "List active sessions" + } + }, + "/api/session/{sessionID}": { + "get": { + "tags": [ + "session" + ], + "operationId": "v2.session.get", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/Session.Info" + } + }, + "required": [ + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + } + }, + "description": "Retrieve a session by ID.", + "summary": "Get session" + }, + "delete": { + "tags": [ + "session" + ], + "operationId": "v2.session.remove", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "required": true + } + ], + "security": [], + "responses": { + "204": { + "description": "" + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + } + }, + "description": "Delete a session and its child sessions.", + "summary": "Delete session" + } + }, + "/api/session/{sessionID}/fork": { + "post": { + "tags": [ + "session" + ], + "operationId": "v2.session.fork", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/Session.Info" + } + }, + "required": [ + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError | MessageNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/MessageNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + } + }, + "description": "Create a child session by copying projected history from the parent. When messageID is supplied, copy messages before that boundary.", + "summary": "Fork session", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "messageID": { + "anyOf": [ + { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/api/session/{sessionID}/agent": { + "post": { + "tags": [ + "session" + ], + "operationId": "v2.session.switchAgent", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "required": true + } + ], + "security": [], + "responses": { + "204": { + "description": "" + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + } + }, + "description": "Switch the agent used by subsequent provider turns.", + "summary": "Switch session agent", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "agent": { + "type": "string" + } + }, + "required": [ + "agent" + ], + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/api/session/{sessionID}/model": { + "post": { + "tags": [ + "session" + ], + "operationId": "v2.session.switchModel", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "required": true + } + ], + "security": [], + "responses": { + "204": { + "description": "" + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + } + }, + "description": "Switch the model used by subsequent provider turns.", + "summary": "Switch session model", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "model": { + "$ref": "#/components/schemas/Model.Ref" + } + }, + "required": [ + "model" + ], + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/api/session/{sessionID}/rename": { + "post": { + "tags": [ + "session" + ], + "operationId": "v2.session.rename", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "required": true + } + ], + "security": [], + "responses": { + "204": { + "description": "" + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + } + }, + "description": "Update the session title.", + "summary": "Rename session", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "title": { + "type": "string" + } + }, + "required": [ + "title" + ], + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/api/session/{sessionID}/move": { + "post": { + "tags": [ + "session" + ], + "operationId": "v2.session.move", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "required": true + } + ], + "security": [], + "responses": { + "204": { + "description": "" + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/InvalidRequestError1" + }, + { + "$ref": "#/components/schemas/InvalidRequestError" + } + ] + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + } + }, + "description": "Move a session to another project directory, optionally transferring local changes.", + "summary": "Move session", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "destination": { + "type": "object", + "properties": { + "directory": { + "type": "string" + } + }, + "required": [ + "directory" + ], + "additionalProperties": false + }, + "moveChanges": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "destination" + ], + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/api/session/{sessionID}/prompt": { + "post": { + "tags": [ + "session" + ], + "operationId": "v2.session.prompt", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/SessionInput.Admitted" + } + }, + "required": [ + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/InvalidRequestError1" + }, + { + "$ref": "#/components/schemas/InvalidRequestError" + } + ] + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + }, + "409": { + "description": "ConflictError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConflictError" + } + } + } + } + }, + "description": "Durably admit one session input and schedule agent-loop execution unless resume is false.", + "summary": "Send message", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "anyOf": [ + { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + { + "type": "null" + } + ] + }, + "prompt": { + "$ref": "#/components/schemas/PromptInput" + }, + "delivery": { + "anyOf": [ + { + "type": "string", + "enum": [ + "steer", + "queue" + ] + }, + { + "type": "null" + } + ] + }, + "resume": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "prompt" + ], + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/api/session/{sessionID}/command": { + "post": { + "tags": [ + "session" + ], + "operationId": "v2.session.command", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/SessionInput.Admitted" + } + }, + "required": [ + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/InvalidRequestError1" + }, + { + "$ref": "#/components/schemas/InvalidRequestError" + } + ] + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError | CommandNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/CommandNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + }, + "409": { + "description": "ConflictError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConflictError" + } + } + } + }, + "500": { + "description": "CommandEvaluationError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CommandEvaluationError" + } + } + } + } + }, + "description": "Resolve a slash command into prompt input, admit it durably, and schedule execution unless resume is false.", + "summary": "Run command", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "anyOf": [ + { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + { + "type": "null" + } + ] + }, + "command": { + "type": "string" + }, + "arguments": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "agent": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "model": { + "anyOf": [ + { + "$ref": "#/components/schemas/Model.Ref" + }, + { + "type": "null" + } + ] + }, + "files": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PromptInput.FileAttachment" + } + }, + "agents": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Prompt.AgentAttachment" + } + }, + "delivery": { + "anyOf": [ + { + "type": "string", + "enum": [ + "steer", + "queue" + ] + }, + { + "type": "null" + } + ] + }, + "resume": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "command" + ], + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/api/session/{sessionID}/skill": { + "post": { + "tags": [ + "session" + ], + "operationId": "v2.session.skill", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "required": true + } + ], + "security": [], + "responses": { + "204": { + "description": "" + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError | SkillNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/SkillNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + } + }, + "description": "Activate a skill for a session by appending a skill message and resuming execution.", + "summary": "Activate skill", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "anyOf": [ + { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + { + "type": "null" + } + ] + }, + "skill": { + "type": "string" + }, + "resume": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "skill" + ], + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/api/session/{sessionID}/synthetic": { + "post": { + "tags": [ + "session" + ], + "operationId": "v2.session.synthetic", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "required": true + } + ], + "security": [], + "responses": { + "204": { + "description": "" + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + } + }, + "description": "Append a synthetic message to a session and resume execution.", + "summary": "Add synthetic message", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "text": { + "type": "string" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "metadata": { + "type": "object" + }, + "resume": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "text" + ], + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/api/session/{sessionID}/shell": { + "post": { + "tags": [ + "session" + ], + "operationId": "v2.session.shell", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "required": true + } + ], + "security": [], + "responses": { + "204": { + "description": "" + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + } + }, + "description": "Execute one shell command in the session's working directory. Emits a shell.started event before execution and a shell.ended event with the merged output after.", + "summary": "Run shell command", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "anyOf": [ + { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + { + "type": "null" + } + ] + }, + "command": { + "type": "string" + } + }, + "required": [ + "command" + ], + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/api/session/{sessionID}/compact": { + "post": { + "tags": [ + "session" + ], + "operationId": "v2.session.compact", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/SessionInput.Compaction" + } + }, + "required": [ + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + }, + "409": { + "description": "ConflictError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConflictError" + } + } + } + } + }, + "description": "Queue a durable session compaction request.", + "summary": "Compact session", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "anyOf": [ + { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/api/session/{sessionID}/wait": { + "post": { + "tags": [ + "session" + ], + "operationId": "v2.session.wait", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "required": true + } + ], + "security": [], + "responses": { + "204": { + "description": "" + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + }, + "503": { + "description": "ServiceUnavailableError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServiceUnavailableError" + } + } + } + } + }, + "description": "Wait for a session agent loop to become idle.", + "summary": "Wait for session" + } + }, + "/api/session/{sessionID}/revert/stage": { + "post": { + "tags": [ + "session" + ], + "operationId": "v2.session.revert.stage", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/Session.Revert" + } + }, + "required": [ + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "MessageNotFoundError | SessionNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/MessageNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + }, + "409": { + "description": "SessionBusyError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionBusyError" + } + } + } + }, + "500": { + "description": "UnknownError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnknownError" + } + } + } + } + }, + "description": "Stage or move a reversible session boundary and optionally apply its file changes.", + "summary": "Stage session revert", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "messageID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + "files": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "messageID" + ], + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/api/session/{sessionID}/revert/clear": { + "post": { + "tags": [ + "session" + ], + "operationId": "v2.session.revert.clear", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "required": true + } + ], + "security": [], + "responses": { + "204": { + "description": "" + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + }, + "409": { + "description": "SessionBusyError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionBusyError" + } + } + } + }, + "500": { + "description": "UnknownError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnknownError" + } + } + } + } + }, + "summary": "Clear staged revert" + } + }, + "/api/session/{sessionID}/revert/commit": { + "post": { + "tags": [ + "session" + ], + "operationId": "v2.session.revert.commit", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "required": true + } + ], + "security": [], + "responses": { + "204": { + "description": "" + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + }, + "409": { + "description": "SessionBusyError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionBusyError" + } + } + } + } + }, + "summary": "Commit staged revert" + } + }, + "/api/session/{sessionID}/context": { + "get": { + "tags": [ + "session" + ], + "operationId": "v2.session.context", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Session.Message.Info" + } + } + }, + "required": [ + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + }, + "500": { + "description": "UnknownError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnknownError" + } + } + } + } + }, + "description": "Retrieve the active context messages for a session (all messages after the last compaction).", + "summary": "Get session context" + } + }, + "/api/session/{sessionID}/instructions/entries": { + "get": { + "tags": [ + "session" + ], + "operationId": "v2.session.instructions.entry.list", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/InstructionEntry.Info" + } + } + }, + "required": [ + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + } + }, + "description": "List API-managed instruction entries attached to the session.", + "summary": "List instruction entries" + } + }, + "/api/session/{sessionID}/instructions/entries/{key}": { + "put": { + "tags": [ + "session" + ], + "operationId": "v2.session.instructions.entry.put", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "required": true + }, + { + "name": "key", + "in": "path", + "schema": { + "$ref": "#/components/schemas/InstructionEntry.Key" + }, + "required": true + } + ], + "security": [], + "responses": { + "204": { + "description": "" + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + } + }, + "description": "Attach or replace one durable instruction entry. Changes announce as updates at the next step boundary.", + "summary": "Put instruction entry", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "value": {} + }, + "required": [ + "value" + ], + "additionalProperties": false + } + } + }, + "required": true + } + }, + "delete": { + "tags": [ + "session" + ], + "operationId": "v2.session.instructions.entry.remove", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "required": true + }, + { + "name": "key", + "in": "path", + "schema": { + "$ref": "#/components/schemas/InstructionEntry.Key" + }, + "required": true + } + ], + "security": [], + "responses": { + "204": { + "description": "" + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + } + }, + "description": "Remove one instruction entry; the removal is announced to the model at the next step boundary.", + "summary": "Remove instruction entry" + } + }, + "/api/experimental/session/{sessionID}/log": { + "get": { + "tags": [ + "session" + ], + "operationId": "v2.session.log", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "required": true + }, + { + "name": "after", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "required": false + }, + { + "name": "follow", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "string", + "enum": [ + "true", + "false" + ] + }, + { + "type": "null" + } + ] + }, + "required": false + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "text/event-stream": { + "schema": { + "type": "object", + "properties": { + "id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "event": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/SessionLogItemStream" + } + }, + "required": [ + "id", + "event", + "data" + ], + "additionalProperties": false + }, + "x-effect-stream": { + "encoding": "sse", + "causeSchema": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "Fail" + ] + }, + "error": { + "not": {} + } + }, + "required": [ + "_tag", + "error" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "Die" + ] + }, + "defect": {} + }, + "required": [ + "_tag", + "defect" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "Interrupt" + ] + }, + "fiberId": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "fiberId" + ], + "additionalProperties": false + } + ] + } + }, + "errorSchema": { + "not": {} + }, + "failureEvent": "effect/httpapi/stream/failure" + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + } + }, + "description": "Experimental durable session event log. Reads events after an exclusive aggregate sequence and continues with live events when follow=true.", + "summary": "Read the session log" + } + }, + "/api/session/{sessionID}/interrupt": { + "post": { + "tags": [ + "session" + ], + "operationId": "v2.session.interrupt", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "required": true + } + ], + "security": [], + "responses": { + "204": { + "description": "" + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + } + }, + "description": "Interrupt active execution owned by this OpenCode process. Idle interruption is a no-op.", + "summary": "Interrupt session execution" + } + }, + "/api/session/{sessionID}/background": { + "post": { + "tags": [ + "session" + ], + "operationId": "v2.session.background", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "required": true + } + ], + "security": [], + "responses": { + "204": { + "description": "" + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + } + }, + "description": "Move active foreground backgroundable tools for this session into background observation. Idle requests are a no-op.", + "summary": "Background blocking session tools" + } + }, + "/api/session/{sessionID}/message/{messageID}": { + "get": { + "tags": [ + "session" + ], + "operationId": "v2.session.message", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "required": true + }, + { + "name": "messageID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/Session.Message.Info" + } + }, + "required": [ + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError | MessageNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/MessageNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + } + }, + "description": "Retrieve one projected message owned by the Session.", + "summary": "Get session message" + } + }, + "/api/session/{sessionID}/message": { + "get": { + "tags": [ + "session" + ], + "operationId": "v2.message.list", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "required": true + }, + { + "name": "limit", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Maximum number of messages to return. When omitted, the endpoint returns its default page size." + }, + "required": false + }, + { + "name": "order", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "string", + "enum": [ + "asc", + "desc" + ] + }, + { + "type": "null" + } + ], + "description": "Message order for the first page. Use desc for newest first or asc for oldest first." + }, + "required": false + }, + { + "name": "cursor", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "string", + "description": "Opaque pagination cursor returned as cursor.previous or cursor.next in the previous response. Do not combine with order." + }, + { + "type": "null" + } + ] + }, + "required": false + } + ], + "security": [], + "responses": { + "200": { + "description": "SessionMessagesResponse", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionMessagesResponse" + } + } + } + }, + "400": { + "description": "InvalidCursorError | InvalidRequestError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/InvalidCursorError" + }, + { + "$ref": "#/components/schemas/InvalidRequestError" + } + ] + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + }, + "500": { + "description": "UnknownError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnknownError" + } + } + } + } + }, + "description": "Retrieve projected messages for a session. Items keep the requested order across pages; use cursor.next or cursor.previous to move through the ordered timeline.", + "summary": "Get session messages" + } + }, + "/api/model": { + "get": { + "tags": [ + "model" + ], + "operationId": "v2.model.list", + "parameters": [ + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "location": { + "$ref": "#/components/schemas/Location.Info" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Model.Info" + } + } + }, + "required": [ + "location", + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "503": { + "description": "ServiceUnavailableError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServiceUnavailableError" + } + } + } + } + }, + "description": "Retrieve available models ordered by release date.", + "summary": "List models" + } + }, + "/api/model/default": { + "get": { + "tags": [ + "model" + ], + "operationId": "v2.model.default", + "parameters": [ + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "location": { + "$ref": "#/components/schemas/Location.Info" + }, + "data": { + "anyOf": [ + { + "$ref": "#/components/schemas/Model.Info" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "location", + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "503": { + "description": "ServiceUnavailableError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServiceUnavailableError" + } + } + } + } + }, + "description": "Retrieve the model used when a session has no explicit model selection.", + "summary": "Get default model" + } + }, + "/api/generate": { + "post": { + "tags": [ + "generate" + ], + "operationId": "v2.generate.text", + "parameters": [ + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "200": { + "description": "GenerateTextResponse", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenerateTextResponse" + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/InvalidRequestError1" + }, + { + "$ref": "#/components/schemas/InvalidRequestError" + } + ] + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "503": { + "description": "ServiceUnavailableError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServiceUnavailableError" + } + } + } + } + }, + "description": "Run one stateless model generation at the requested location and return the assistant text. Uses the location's default model when none is specified.", + "summary": "Generate text", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "prompt": { + "type": "string" + }, + "model": { + "anyOf": [ + { + "$ref": "#/components/schemas/Model.Ref" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "prompt" + ], + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/api/provider": { + "get": { + "tags": [ + "provider" + ], + "operationId": "v2.provider.list", + "parameters": [ + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "location": { + "$ref": "#/components/schemas/Location.Info" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProviderV2.Info" + } + } + }, + "required": [ + "location", + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "503": { + "description": "ServiceUnavailableError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServiceUnavailableError" + } + } + } + } + }, + "description": "Retrieve active AI providers so clients can show provider availability and configuration.", + "summary": "List providers" + } + }, + "/api/provider/{providerID}": { + "get": { + "tags": [ + "provider" + ], + "operationId": "v2.provider.get", + "parameters": [ + { + "name": "providerID", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "location": { + "$ref": "#/components/schemas/Location.Info" + }, + "data": { + "$ref": "#/components/schemas/ProviderV2.Info" + } + }, + "required": [ + "location", + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "ProviderNotFoundError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProviderNotFoundError" + } + } + } + }, + "503": { + "description": "ServiceUnavailableError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServiceUnavailableError" + } + } + } + } + }, + "description": "Retrieve a single AI provider so clients can inspect its availability and endpoint settings.", + "summary": "Get provider" + } + }, + "/api/integration": { + "get": { + "tags": [ + "integration" + ], + "operationId": "v2.integration.list", + "parameters": [ + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "location": { + "$ref": "#/components/schemas/Location.Info" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Integration.Info" + } + } + }, + "required": [ + "location", + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "Retrieve available integrations and their authentication methods.", + "summary": "List integrations" + } + }, + "/api/integration/{integrationID}": { + "get": { + "tags": [ + "integration" + ], + "operationId": "v2.integration.get", + "parameters": [ + { + "name": "integrationID", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "location": { + "$ref": "#/components/schemas/Location.Info" + }, + "data": { + "anyOf": [ + { + "$ref": "#/components/schemas/Integration.Info" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "location", + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "Retrieve one integration and its authentication methods.", + "summary": "Get integration" + } + }, + "/api/integration/{integrationID}/connect/key": { + "post": { + "tags": [ + "integration" + ], + "operationId": "v2.integration.connect.key", + "parameters": [ + { + "name": "integrationID", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "204": { + "description": "" + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/InvalidRequestError1" + }, + { + "$ref": "#/components/schemas/InvalidRequestError" + } + ] + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "Run a key authentication method and store the resulting credential.", + "summary": "Connect with key", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "label": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "key" + ], + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/api/integration/{integrationID}/connect/oauth": { + "post": { + "tags": [ + "integration" + ], + "operationId": "v2.integration.connect.oauth", + "parameters": [ + { + "name": "integrationID", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "location": { + "$ref": "#/components/schemas/Location.Info" + }, + "data": { + "$ref": "#/components/schemas/Integration.Attempt" + } + }, + "required": [ + "location", + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/InvalidRequestError1" + }, + { + "$ref": "#/components/schemas/InvalidRequestError" + } + ] + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "Start an OAuth attempt and return the authorization details.", + "summary": "Begin OAuth connection", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "methodID": { + "type": "string" + }, + "inputs": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "label": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "methodID", + "inputs" + ], + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/api/integration/attempt/{attemptID}": { + "get": { + "tags": [ + "integration" + ], + "operationId": "v2.integration.attempt.status", + "parameters": [ + { + "name": "attemptID", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "location": { + "$ref": "#/components/schemas/Location.Info" + }, + "data": { + "$ref": "#/components/schemas/Integration.AttemptStatus" + } + }, + "required": [ + "location", + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "Poll the current status of an OAuth attempt.", + "summary": "Get OAuth attempt status" + }, + "delete": { + "tags": [ + "integration" + ], + "operationId": "v2.integration.attempt.cancel", + "parameters": [ + { + "name": "attemptID", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "204": { + "description": "" + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "Cancel an OAuth attempt and release its resources.", + "summary": "Cancel OAuth connection" + } + }, + "/api/integration/attempt/{attemptID}/complete": { + "post": { + "tags": [ + "integration" + ], + "operationId": "v2.integration.attempt.complete", + "parameters": [ + { + "name": "attemptID", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "204": { + "description": "" + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/InvalidRequestError1" + }, + { + "$ref": "#/components/schemas/InvalidRequestError" + } + ] + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "Complete a code-based OAuth attempt and store the resulting credential.", + "summary": "Complete OAuth connection", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "code": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/api/mcp": { + "get": { + "tags": [ + "mcp" + ], + "operationId": "v2.mcp.list", + "parameters": [ + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "location": { + "$ref": "#/components/schemas/Location.Info" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Mcp.Server" + } + } + }, + "required": [ + "location", + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "Retrieve configured MCP servers and their connection status.", + "summary": "List MCP servers" + } + }, + "/api/mcp/resource": { + "get": { + "tags": [ + "mcp" + ], + "operationId": "v2.mcp.resource.catalog", + "parameters": [ + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "location": { + "$ref": "#/components/schemas/Location.Info" + }, + "data": { + "$ref": "#/components/schemas/Mcp.ResourceCatalog" + } + }, + "required": [ + "location", + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "Retrieve resources and resource templates from connected MCP servers.", + "summary": "List MCP resources" + } + }, + "/api/credential/{credentialID}": { + "patch": { + "tags": [ + "credential" + ], + "operationId": "v2.credential.update", + "parameters": [ + { + "name": "credentialID", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "204": { + "description": "" + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "Update a stored credential label.", + "summary": "Update credential", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "label": { + "type": "string" + } + }, + "required": [ + "label" + ], + "additionalProperties": false + } + } + }, + "required": true + } + }, + "delete": { + "tags": [ + "credential" + ], + "operationId": "v2.credential.remove", + "parameters": [ + { + "name": "credentialID", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "204": { + "description": "" + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "Remove a stored integration credential.", + "summary": "Remove credential" + } + }, + "/api/project": { + "get": { + "tags": [ + "project" + ], + "operationId": "v2.project.list", + "parameters": [], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Project" + } + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "List known projects.", + "summary": "List projects" + } + }, + "/api/project/current": { + "get": { + "tags": [ + "project" + ], + "operationId": "v2.project.current", + "parameters": [ + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Project.Current", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Project.Current" + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "Resolve the project for the requested location.", + "summary": "Get current project" + } + }, + "/api/project/{projectID}/directories": { + "get": { + "tags": [ + "project" + ], + "operationId": "v2.project.directories", + "parameters": [ + { + "name": "projectID", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Project.Directories", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Project.Directories" + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "List known local absolute directories for a project.", + "summary": "List project directories" + } + }, + "/api/form/request": { + "get": { + "tags": [ + "form" + ], + "operationId": "v2.form.request.list", + "parameters": [ + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "location": { + "$ref": "#/components/schemas/Location.Info" + }, + "data": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/Form.FormInfo" + }, + { + "$ref": "#/components/schemas/Form.UrlInfo" + } + ] + } + } + }, + "required": [ + "location", + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "Retrieve pending forms for a location.", + "summary": "List pending form requests" + } + }, + "/api/session/{sessionID}/form": { + "get": { + "tags": [ + "form" + ], + "operationId": "v2.session.form.list", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/Form.FormInfo" + }, + { + "$ref": "#/components/schemas/Form.UrlInfo" + } + ] + } + } + }, + "required": [ + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + } + }, + "description": "Retrieve pending forms for a session.", + "summary": "List session forms" + }, + "post": { + "tags": [ + "form" + ], + "operationId": "v2.session.form.create", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "anyOf": [ + { + "$ref": "#/components/schemas/Form.FormInfo" + }, + { + "$ref": "#/components/schemas/Form.UrlInfo" + } + ] + } + }, + "required": [ + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/InvalidRequestError1" + }, + { + "$ref": "#/components/schemas/InvalidRequestError" + } + ] + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + }, + "409": { + "description": "ConflictError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConflictError" + } + } + } + } + }, + "description": "Create a form for a session.", + "summary": "Create session form", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Form.CreatePayload" + } + } + }, + "required": true + } + } + }, + "/api/session/{sessionID}/form/{formID}": { + "get": { + "tags": [ + "form" + ], + "operationId": "v2.session.form.get", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "formID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^frm_" + } + ] + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "anyOf": [ + { + "$ref": "#/components/schemas/Form.FormInfo" + }, + { + "$ref": "#/components/schemas/Form.UrlInfo" + } + ] + } + }, + "required": [ + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError | FormNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/FormNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + } + }, + "description": "Retrieve a form for a session.", + "summary": "Get session form" + } + }, + "/api/session/{sessionID}/form/{formID}/state": { + "get": { + "tags": [ + "form" + ], + "operationId": "v2.session.form.state", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "formID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^frm_" + } + ] + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/Form.State" + } + }, + "required": [ + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError | FormNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/FormNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + } + }, + "description": "Retrieve the current state for a form.", + "summary": "Get form state" + } + }, + "/api/session/{sessionID}/form/{formID}/reply": { + "post": { + "tags": [ + "form" + ], + "operationId": "v2.session.form.reply", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "formID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^frm_" + } + ] + }, + "required": true + } + ], + "security": [], + "responses": { + "204": { + "description": "" + }, + "400": { + "description": "FormInvalidAnswerError | InvalidRequestError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/FormInvalidAnswerError" + }, + { + "$ref": "#/components/schemas/InvalidRequestError" + } + ] + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError | FormNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/FormNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + }, + "409": { + "description": "FormAlreadySettledError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FormAlreadySettledError" + } + } + } + } + }, + "description": "Submit an answer to a pending form.", + "summary": "Reply to form", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Form.Reply" + } + } + }, + "required": true + } + } + }, + "/api/session/{sessionID}/form/{formID}/cancel": { + "post": { + "tags": [ + "form" + ], + "operationId": "v2.session.form.cancel", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "formID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^frm_" + } + ] + }, + "required": true + } + ], + "security": [], + "responses": { + "204": { + "description": "" + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError | FormNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/FormNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + }, + "409": { + "description": "FormAlreadySettledError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FormAlreadySettledError" + } + } + } + } + }, + "description": "Cancel a pending form.", + "summary": "Cancel form" + } + }, + "/api/permission/request": { + "get": { + "tags": [ + "permission" + ], + "operationId": "v2.permission.request.list", + "parameters": [ + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "location": { + "$ref": "#/components/schemas/Location.Info" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PermissionV2.Request" + } + } + }, + "required": [ + "location", + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "Retrieve pending permission requests for a location.", + "summary": "List pending permission requests" + } + }, + "/api/permission/saved": { + "get": { + "tags": [ + "permission" + ], + "operationId": "v2.permission.saved.list", + "parameters": [ + { + "name": "projectID", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "required": false + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PermissionSaved.Info" + } + } + }, + "required": [ + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "Retrieve saved permissions, optionally filtered by project.", + "summary": "List saved permissions" + } + }, + "/api/permission/saved/{id}": { + "delete": { + "tags": [ + "permission" + ], + "operationId": "v2.permission.saved.remove", + "parameters": [ + { + "name": "id", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "204": { + "description": "" + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "Remove a saved permission by ID.", + "summary": "Remove saved permission" + } + }, + "/api/session/{sessionID}/permission": { + "post": { + "tags": [ + "permission" + ], + "operationId": "v2.session.permission.create", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^per" + } + ] + }, + "effect": { + "$ref": "#/components/schemas/PermissionV2.Effect" + } + }, + "required": [ + "id", + "effect" + ], + "additionalProperties": false + } + }, + "required": [ + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + } + }, + "description": "Evaluate and, when approval is required, create a permission request for a session.", + "summary": "Create permission request", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "anyOf": [ + { + "type": "string", + "allOf": [ + { + "pattern": "^per" + } + ] + }, + { + "type": "null" + } + ] + }, + "action": { + "type": "string" + }, + "resources": { + "type": "array", + "items": { + "type": "string" + } + }, + "save": { + "type": "array", + "items": { + "type": "string" + } + }, + "metadata": { + "type": "object" + }, + "source": { + "$ref": "#/components/schemas/PermissionV2.Source" + }, + "agent": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "action", + "resources" + ], + "additionalProperties": false + } + } + }, + "required": true + } + }, + "get": { + "tags": [ + "permission" + ], + "operationId": "v2.session.permission.list", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PermissionV2.Request" + } + } + }, + "required": [ + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + } + }, + "description": "Retrieve pending permission requests owned by a session.", + "summary": "List session permission requests" + } + }, + "/api/session/{sessionID}/permission/{requestID}": { + "get": { + "tags": [ + "permission" + ], + "operationId": "v2.session.permission.get", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "required": true + }, + { + "name": "requestID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^per" + } + ] + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/PermissionV2.Request" + } + }, + "required": [ + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError | PermissionNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/PermissionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + } + }, + "description": "Retrieve a pending permission request owned by a session.", + "summary": "Get permission request" + } + }, + "/api/session/{sessionID}/permission/{requestID}/reply": { + "post": { + "tags": [ + "permission" + ], + "operationId": "v2.session.permission.reply", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "required": true + }, + { + "name": "requestID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^per" + } + ] + }, + "required": true + } + ], + "security": [], + "responses": { + "204": { + "description": "" + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError | PermissionNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/PermissionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + } + }, + "description": "Respond to a pending permission request owned by a session.", + "summary": "Reply to pending permission request", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "reply": { + "$ref": "#/components/schemas/PermissionV2.Reply" + }, + "message": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "reply" + ], + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/api/fs/read/*": { + "get": { + "tags": [ + "filesystem" + ], + "operationId": "v2.fs.read", + "parameters": [ + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/octet-stream": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "Serve one file relative to the requested location.", + "summary": "Read file" + } + }, + "/api/fs/list": { + "get": { + "tags": [ + "filesystem" + ], + "operationId": "v2.fs.list", + "parameters": [ + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + }, + { + "name": "path", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "required": false + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "location": { + "$ref": "#/components/schemas/Location.Info" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FileSystem.Entry" + } + } + }, + "required": [ + "location", + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "List direct children of one directory relative to the requested location.", + "summary": "List directory" + } + }, + "/api/fs/find": { + "get": { + "tags": [ + "filesystem" + ], + "operationId": "v2.fs.find", + "parameters": [ + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + }, + { + "name": "query", + "in": "query", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "type", + "in": "query", + "schema": { + "type": "string", + "enum": [ + "file", + "directory" + ] + }, + "required": false + }, + { + "name": "limit", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "required": false + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "location": { + "$ref": "#/components/schemas/Location.Info" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FileSystem.Entry" + } + } + }, + "required": [ + "location", + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "Find recursively ranked filesystem entries relative to the requested location.", + "summary": "Find files" + } + }, + "/api/command": { + "get": { + "tags": [ + "command" + ], + "operationId": "v2.command.list", + "parameters": [ + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "location": { + "$ref": "#/components/schemas/Location.Info" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Command.Info" + } + } + }, + "required": [ + "location", + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "Retrieve currently registered commands.", + "summary": "List commands" + } + }, + "/api/skill": { + "get": { + "tags": [ + "skill" + ], + "operationId": "v2.skill.list", + "parameters": [ + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "location": { + "$ref": "#/components/schemas/Location.Info" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Skill.Info" + } + } + }, + "required": [ + "location", + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "Retrieve currently registered skills.", + "summary": "List skills" + } + }, + "/api/event": { + "get": { + "tags": [ + "event" + ], + "operationId": "v2.event.subscribe", + "parameters": [], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "text/event-stream": { + "schema": { + "type": "object", + "properties": { + "id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "event": { + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/V2EventStream" + } + }, + "required": [ + "id", + "event", + "data" + ], + "additionalProperties": false + }, + "x-effect-stream": { + "encoding": "sse", + "causeSchema": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "Fail" + ] + }, + "error": { + "not": {} + } + }, + "required": [ + "_tag", + "error" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "Die" + ] + }, + "defect": {} + }, + "required": [ + "_tag", + "defect" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "Interrupt" + ] + }, + "fiberId": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "fiberId" + ], + "additionalProperties": false + } + ] + } + }, + "errorSchema": { + "not": {} + }, + "failureEvent": "effect/httpapi/stream/failure" + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "Subscribe to native event payloads for the server. Volatile by contract: a slow consumer overflows and fails the stream, and events during disconnection are missed.", + "summary": "Subscribe to events" + } + }, + "/api/pty": { + "get": { + "tags": [ + "pty" + ], + "operationId": "v2.pty.list", + "parameters": [ + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "location": { + "$ref": "#/components/schemas/Location.Info" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pty" + } + } + }, + "required": [ + "location", + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "List PTY sessions for a location, including exited sessions retained until removal.", + "summary": "List PTY sessions" + }, + "post": { + "tags": [ + "pty" + ], + "operationId": "v2.pty.create", + "parameters": [ + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "location": { + "$ref": "#/components/schemas/Location.Info" + }, + "data": { + "$ref": "#/components/schemas/Pty" + } + }, + "required": [ + "location", + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "Create a pseudo-terminal session for a location.", + "summary": "Create PTY session", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "command": { + "type": "string" + }, + "args": { + "type": "array", + "items": { + "type": "string" + } + }, + "cwd": { + "type": "string" + }, + "title": { + "type": "string" + }, + "env": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/api/pty/{ptyID}": { + "get": { + "tags": [ + "pty" + ], + "operationId": "v2.pty.get", + "parameters": [ + { + "name": "ptyID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^pty" + } + ] + }, + "required": true + }, + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "location": { + "$ref": "#/components/schemas/Location.Info" + }, + "data": { + "$ref": "#/components/schemas/Pty" + } + }, + "required": [ + "location", + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "PtyNotFoundError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PtyNotFoundError" + } + } + } + } + }, + "description": "Get one PTY session, including its exit code once exited.", + "summary": "Get PTY session" + }, + "put": { + "tags": [ + "pty" + ], + "operationId": "v2.pty.update", + "parameters": [ + { + "name": "ptyID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^pty" + } + ] + }, + "required": true + }, + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "location": { + "$ref": "#/components/schemas/Location.Info" + }, + "data": { + "$ref": "#/components/schemas/Pty" + } + }, + "required": [ + "location", + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "PtyNotFoundError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PtyNotFoundError" + } + } + } + } + }, + "description": "Update the title or viewport size of one PTY session.", + "summary": "Update PTY session", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "size": { + "type": "object", + "properties": { + "rows": { + "type": "integer", + "allOf": [ + { + "exclusiveMinimum": 0 + } + ] + }, + "cols": { + "type": "integer", + "allOf": [ + { + "exclusiveMinimum": 0 + } + ] + } + }, + "required": [ + "rows", + "cols" + ], + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + }, + "required": true + } + }, + "delete": { + "tags": [ + "pty" + ], + "operationId": "v2.pty.remove", + "parameters": [ + { + "name": "ptyID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^pty" + } + ] + }, + "required": true + }, + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "204": { + "description": "" + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "PtyNotFoundError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PtyNotFoundError" + } + } + } + } + }, + "description": "Terminate and remove one PTY session.", + "summary": "Remove PTY session" + } + }, + "/api/pty/{ptyID}/connect-token": { + "post": { + "tags": [ + "pty" + ], + "operationId": "v2.pty.connect.token", + "parameters": [ + { + "name": "ptyID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^pty" + } + ] + }, + "required": true + }, + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "location": { + "$ref": "#/components/schemas/Location.Info" + }, + "data": { + "$ref": "#/components/schemas/PtyTicket.ConnectToken" + } + }, + "required": [ + "location", + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "403": { + "description": "ForbiddenError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForbiddenError" + } + } + } + }, + "404": { + "description": "PtyNotFoundError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PtyNotFoundError" + } + } + } + } + }, + "description": "Create a short-lived single-use ticket for opening a PTY WebSocket connection.", + "summary": "Create PTY WebSocket token" + } + }, + "/api/pty/{ptyID}/connect": { + "get": { + "tags": [ + "pty" + ], + "operationId": "v2.pty.connect", + "parameters": [ + { + "name": "ptyID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^pty" + } + ] + }, + "required": true + }, + { + "in": "query", + "name": "location[directory]", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "location[workspace]", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "cursor", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "ticket", + "schema": { + "type": "string" + } + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "boolean" + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "403": { + "description": "ForbiddenError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForbiddenError" + } + } + } + }, + "404": { + "description": "PtyNotFoundError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PtyNotFoundError" + } + } + } + } + }, + "description": "Establish a WebSocket connection streaming PTY output and accepting terminal input.", + "summary": "Connect to PTY session", + "x-websocket": true + } + }, + "/api/shell": { + "get": { + "tags": [ + "shell" + ], + "operationId": "v2.shell.list", + "parameters": [ + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "location": { + "$ref": "#/components/schemas/Location.Info" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Shell1" + } + } + }, + "required": [ + "location", + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "List currently running shell commands for a location. Exited commands are not included.", + "summary": "List running shell commands" + }, + "post": { + "tags": [ + "shell" + ], + "operationId": "v2.shell.create", + "parameters": [ + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "location": { + "$ref": "#/components/schemas/Location.Info" + }, + "data": { + "$ref": "#/components/schemas/Shell1" + } + }, + "required": [ + "location", + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "Spawn one non-interactive shell command for a location. Combined stdout/stderr is captured to a file pageable via output.", + "summary": "Run shell command", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "command": { + "type": "string" + }, + "cwd": { + "type": "string" + }, + "timeout": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "metadata": { + "type": "object" + } + }, + "required": [ + "command", + "timeout" + ], + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/api/shell/{id}": { + "get": { + "tags": [ + "shell" + ], + "operationId": "v2.shell.get", + "parameters": [ + { + "name": "id", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^sh_" + } + ] + }, + "required": true + }, + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "location": { + "$ref": "#/components/schemas/Location.Info" + }, + "data": { + "$ref": "#/components/schemas/Shell1" + } + }, + "required": [ + "location", + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "ShellNotFoundError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ShellNotFoundError" + } + } + } + } + }, + "description": "Get one shell command, including its status and exit code once exited.", + "summary": "Get shell command" + }, + "delete": { + "tags": [ + "shell" + ], + "operationId": "v2.shell.remove", + "parameters": [ + { + "name": "id", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^sh_" + } + ] + }, + "required": true + }, + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "204": { + "description": "" + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "ShellNotFoundError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ShellNotFoundError" + } + } + } + } + }, + "description": "Terminate and remove one shell command and its retained output.", + "summary": "Remove shell command" + } + }, + "/api/shell/{id}/timeout": { + "patch": { + "tags": [ + "shell" + ], + "operationId": "v2.shell.timeout", + "parameters": [ + { + "name": "id", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^sh_" + } + ] + }, + "required": true + }, + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "location": { + "$ref": "#/components/schemas/Location.Info" + }, + "data": { + "$ref": "#/components/schemas/Shell1" + } + }, + "required": [ + "location", + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "ShellNotFoundError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ShellNotFoundError" + } + } + } + } + }, + "description": "Replace a running shell command's timeout from now, or clear it with zero.", + "summary": "Update shell timeout", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "timeout": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + } + }, + "required": [ + "timeout" + ], + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/api/shell/{id}/output": { + "get": { + "tags": [ + "shell" + ], + "operationId": "v2.shell.output", + "parameters": [ + { + "name": "id", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^sh_" + } + ] + }, + "required": true + }, + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + }, + { + "name": "cursor", + "in": "query", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^[+-]?\\d*\\.?\\d+(?:[Ee][+-]?\\d+)?$" + } + ] + }, + "required": false + }, + { + "name": "limit", + "in": "query", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^[+-]?\\d*\\.?\\d+(?:[Ee][+-]?\\d+)?$" + } + ] + }, + "required": false + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "location": { + "$ref": "#/components/schemas/Location.Info" + }, + "data": { + "type": "object", + "properties": { + "output": { + "type": "string" + }, + "cursor": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "size": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "truncated": { + "type": "boolean" + } + }, + "required": [ + "output", + "cursor", + "size", + "truncated" + ], + "additionalProperties": false + } + }, + "required": [ + "location", + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "ShellNotFoundError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ShellNotFoundError" + } + } + } + } + }, + "description": "Page through captured combined output by absolute byte cursor.", + "summary": "Read shell output" + } + }, + "/api/question/request": { + "get": { + "tags": [ + "question" + ], + "operationId": "v2.question.request.list", + "parameters": [ + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "location": { + "$ref": "#/components/schemas/Location.Info" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/QuestionV2.Request" + } + } + }, + "required": [ + "location", + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "Retrieve pending question requests for a location.", + "summary": "List pending question requests" + } + }, + "/api/session/{sessionID}/question": { + "get": { + "tags": [ + "question" + ], + "operationId": "v2.session.question.list", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/QuestionV2.Request" + } + } + }, + "required": [ + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + } + }, + "description": "Retrieve pending question requests owned by a session.", + "summary": "List session question requests" + } + }, + "/api/session/{sessionID}/question/{requestID}/reply": { + "post": { + "tags": [ + "question" + ], + "operationId": "v2.session.question.reply", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "required": true + }, + { + "name": "requestID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^que" + } + ] + }, + "required": true + } + ], + "security": [], + "responses": { + "204": { + "description": "" + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError | QuestionNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/QuestionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + } + }, + "description": "Answer a pending question request owned by a session.", + "summary": "Reply to pending question request", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/QuestionV2.Reply" + } + } + }, + "required": true + } + } + }, + "/api/session/{sessionID}/question/{requestID}/reject": { + "post": { + "tags": [ + "question" + ], + "operationId": "v2.session.question.reject", + "parameters": [ + { + "name": "sessionID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "required": true + }, + { + "name": "requestID", + "in": "path", + "schema": { + "type": "string", + "allOf": [ + { + "pattern": "^que" + } + ] + }, + "required": true + } + ], + "security": [], + "responses": { + "204": { + "description": "" + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + }, + "404": { + "description": "SessionNotFoundError | QuestionNotFoundError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/QuestionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + }, + { + "$ref": "#/components/schemas/SessionNotFoundError" + } + ] + } + } + } + } + }, + "description": "Reject a pending question request owned by a session.", + "summary": "Reject pending question request" + } + }, + "/api/reference": { + "get": { + "tags": [ + "reference" + ], + "operationId": "v2.reference.list", + "parameters": [ + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "location": { + "$ref": "#/components/schemas/Location.Info" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Reference.Info" + } + } + }, + "required": [ + "location", + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "List references available in the requested location.", + "summary": "List references" + } + }, + "/experimental/project/{projectID}/copy": { + "post": { + "tags": [ + "projectCopy" + ], + "operationId": "v2.projectCopy.create", + "parameters": [ + { + "name": "projectID", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "200": { + "description": "ProjectCopy.Copy", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProjectCopy.Copy" + } + } + } + }, + "400": { + "description": "ProjectCopyError | InvalidRequestError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/ProjectCopyError" + }, + { + "$ref": "#/components/schemas/InvalidRequestError" + } + ] + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "strategy": { + "type": "string" + }, + "directory": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "strategy", + "directory" + ], + "additionalProperties": false + } + } + }, + "required": true + } + }, + "delete": { + "tags": [ + "projectCopy" + ], + "operationId": "v2.projectCopy.remove", + "parameters": [ + { + "name": "projectID", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "204": { + "description": "" + }, + "400": { + "description": "ProjectCopyError | InvalidRequestError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/ProjectCopyError" + }, + { + "$ref": "#/components/schemas/InvalidRequestError" + } + ] + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "directory": { + "type": "string" + }, + "force": { + "type": "boolean" + } + }, + "required": [ + "directory", + "force" + ], + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/experimental/project/{projectID}/copy/refresh": { + "post": { + "tags": [ + "projectCopy" + ], + "operationId": "v2.projectCopy.refresh", + "parameters": [ + { + "name": "projectID", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "204": { + "description": "" + }, + "400": { + "description": "ProjectCopyError | InvalidRequestError", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/ProjectCopyError" + }, + { + "$ref": "#/components/schemas/InvalidRequestError" + } + ] + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + } + } + }, + "/api/vcs/status": { + "get": { + "tags": [ + "vcs" + ], + "operationId": "v2.vcs.status", + "parameters": [ + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "location": { + "$ref": "#/components/schemas/Location.Info" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Vcs.FileStatus" + } + } + }, + "required": [ + "location", + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "List uncommitted working-copy changes relative to the requested location.", + "summary": "VCS status" + } + }, + "/api/vcs/diff": { + "get": { + "tags": [ + "vcs" + ], + "operationId": "v2.vcs.diff", + "parameters": [ + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + }, + { + "name": "mode", + "in": "query", + "schema": { + "$ref": "#/components/schemas/Vcs.Mode" + }, + "required": true + }, + { + "name": "context", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "required": false + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "location": { + "$ref": "#/components/schemas/Location.Info" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FileDiff.Info" + } + } + }, + "required": [ + "location", + "data" + ], + "additionalProperties": false + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "Diff the working copy against HEAD (mode git) or the default-branch merge base (mode branch) for the requested location.", + "summary": "VCS diff" + } + }, + "/api/debug/location": { + "get": { + "tags": [ + "debug" + ], + "operationId": "v2.debug.location.list", + "parameters": [], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Location.Ref" + } + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "List locations currently loaded by the server.", + "summary": "List loaded locations" + }, + "delete": { + "tags": [ + "debug" + ], + "operationId": "v2.debug.location.evict", + "parameters": [ + { + "name": "location", + "in": "query", + "schema": { + "anyOf": [ + { + "type": "object", + "properties": { + "directory": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "workspace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "required": false, + "style": "deepObject", + "explode": true + } + ], + "security": [], + "responses": { + "204": { + "description": "" + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestError" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedError" + } + } + } + } + }, + "description": "Dispose the requested location's cached services so its next use boots them fresh.", + "summary": "Evict a loaded location" + } + } + }, + "components": { + "schemas": { + "UnauthorizedError": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "UnauthorizedError" + ] + }, + "message": { + "type": "string" + } + }, + "required": [ + "_tag", + "message" + ], + "additionalProperties": false + }, + "InvalidRequestError": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "InvalidRequestError" + ] + }, + "message": { + "type": "string" + }, + "kind": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "field": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "message" + ], + "additionalProperties": false + }, + "Location.Info": { + "type": "object", + "properties": { + "directory": { + "type": "string" + }, + "workspaceID": { + "type": "string", + "allOf": [ + { + "pattern": "^wrk" + } + ] + }, + "project": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "directory": { + "type": "string" + } + }, + "required": [ + "id", + "directory" + ], + "additionalProperties": false + } + }, + "required": [ + "directory", + "project" + ], + "additionalProperties": false + }, + "Model.Ref": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "providerID": { + "type": "string" + }, + "variant": { + "type": "string" + } + }, + "required": [ + "id", + "providerID" + ], + "additionalProperties": false + }, + "Provider.Settings": { + "type": "object" + }, + "Provider.Request": { + "type": "object", + "properties": { + "settings": { + "$ref": "#/components/schemas/Provider.Settings" + }, + "headers": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "body": { + "type": "object" + } + }, + "required": [ + "settings", + "headers", + "body" + ], + "additionalProperties": false + }, + "Agent.Color": { + "anyOf": [ + { + "type": "string", + "allOf": [ + { + "pattern": "^#[0-9a-fA-F]{6}$" + } + ] + }, + { + "type": "string", + "enum": [ + "primary", + "secondary", + "accent", + "success", + "warning", + "error", + "info" + ] + } + ] + }, + "PermissionV2.Effect": { + "type": "string", + "enum": [ + "allow", + "deny", + "ask" + ] + }, + "PermissionV2.Rule": { + "type": "object", + "properties": { + "action": { + "type": "string" + }, + "resource": { + "type": "string" + }, + "effect": { + "$ref": "#/components/schemas/PermissionV2.Effect" + } + }, + "required": [ + "action", + "resource", + "effect" + ], + "additionalProperties": false + }, + "PermissionV2.Ruleset": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PermissionV2.Rule" + } + }, + "Agent.Info": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "model": { + "$ref": "#/components/schemas/Model.Ref" + }, + "request": { + "$ref": "#/components/schemas/Provider.Request" + }, + "system": { + "type": "string" + }, + "description": { + "type": "string" + }, + "mode": { + "type": "string", + "enum": [ + "subagent", + "primary", + "all" + ] + }, + "hidden": { + "type": "boolean" + }, + "color": { + "$ref": "#/components/schemas/Agent.Color" + }, + "steps": { + "type": "integer", + "allOf": [ + { + "exclusiveMinimum": 0 + } + ] + }, + "permissions": { + "$ref": "#/components/schemas/PermissionV2.Ruleset" + } + }, + "required": [ + "id", + "name", + "request", + "mode", + "hidden", + "permissions" + ], + "additionalProperties": false + }, + "Plugin.Info": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "additionalProperties": false + }, + "Money.USD": { + "type": "number" + }, + "TokenUsage.Info": { + "type": "object", + "properties": { + "input": { + "type": "number" + }, + "output": { + "type": "number" + }, + "reasoning": { + "type": "number" + }, + "cache": { + "type": "object", + "properties": { + "read": { + "type": "number" + }, + "write": { + "type": "number" + } + }, + "required": [ + "read", + "write" + ], + "additionalProperties": false + } + }, + "required": [ + "input", + "output", + "reasoning", + "cache" + ], + "additionalProperties": false + }, + "Location.Ref": { + "type": "object", + "properties": { + "directory": { + "type": "string" + }, + "workspaceID": { + "type": "string", + "allOf": [ + { + "pattern": "^wrk" + } + ] + } + }, + "required": [ + "directory" + ], + "additionalProperties": false + }, + "FileDiff.Info": { + "type": "object", + "properties": { + "file": { + "type": "string" + }, + "patch": { + "type": "string" + }, + "additions": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "deletions": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "status": { + "type": "string", + "enum": [ + "added", + "deleted", + "modified" + ] + } + }, + "required": [ + "file", + "patch", + "additions", + "deletions", + "status" + ], + "additionalProperties": false + }, + "Session.Revert": { + "type": "object", + "properties": { + "messageID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + "partID": { + "type": "string" + }, + "snapshot": { + "type": "string" + }, + "files": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FileDiff.Info" + } + } + }, + "required": [ + "messageID" + ], + "additionalProperties": false + }, + "Session.Info": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "parentID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "fork": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "messageID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + } + }, + "required": [ + "sessionID" + ], + "additionalProperties": false + }, + "projectID": { + "type": "string" + }, + "agent": { + "type": "string" + }, + "model": { + "$ref": "#/components/schemas/Model.Ref" + }, + "cost": { + "$ref": "#/components/schemas/Money.USD" + }, + "tokens": { + "$ref": "#/components/schemas/TokenUsage.Info" + }, + "time": { + "type": "object", + "properties": { + "created": { + "type": "number" + }, + "updated": { + "type": "number" + }, + "archived": { + "type": "number" + } + }, + "required": [ + "created", + "updated" + ], + "additionalProperties": false + }, + "title": { + "type": "string" + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "subpath": { + "type": "string" + }, + "revert": { + "$ref": "#/components/schemas/Session.Revert" + } + }, + "required": [ + "id", + "projectID", + "cost", + "tokens", + "time", + "title", + "location" + ], + "additionalProperties": false + }, + "SessionsResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Session.Info" + } + }, + "cursor": { + "type": "object", + "properties": { + "previous": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "next": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + }, + "required": [ + "data", + "cursor" + ], + "additionalProperties": false + }, + "InvalidCursorError": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "InvalidCursorError" + ] + }, + "message": { + "type": "string" + } + }, + "required": [ + "_tag", + "message" + ], + "additionalProperties": false + }, + "InvalidRequestError1": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "InvalidRequestError" + ] + }, + "message": { + "type": "string" + }, + "kind": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "field": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "message" + ], + "additionalProperties": false + }, + "SessionActive": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "running" + ] + } + }, + "required": [ + "type" + ], + "additionalProperties": false + }, + "SessionNotFoundError": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "SessionNotFoundError" + ] + }, + "sessionID": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": [ + "_tag", + "sessionID", + "message" + ], + "additionalProperties": false + }, + "MessageNotFoundError": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "MessageNotFoundError" + ] + }, + "sessionID": { + "type": "string" + }, + "messageID": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": [ + "_tag", + "sessionID", + "messageID", + "message" + ], + "additionalProperties": false + }, + "Prompt.Mention": { + "type": "object", + "properties": { + "start": { + "type": "number" + }, + "end": { + "type": "number" + }, + "text": { + "type": "string" + } + }, + "required": [ + "start", + "end", + "text" + ], + "additionalProperties": false + }, + "PromptInput.FileAttachment": { + "type": "object", + "properties": { + "uri": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "mention": { + "$ref": "#/components/schemas/Prompt.Mention" + } + }, + "required": [ + "uri" + ], + "additionalProperties": false + }, + "Prompt.AgentAttachment": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "mention": { + "$ref": "#/components/schemas/Prompt.Mention" + } + }, + "required": [ + "name" + ], + "additionalProperties": false + }, + "PromptInput": { + "type": "object", + "properties": { + "text": { + "type": "string" + }, + "files": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PromptInput.FileAttachment" + } + }, + "agents": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Prompt.AgentAttachment" + } + } + }, + "required": [ + "text" + ], + "additionalProperties": false + }, + "Prompt.Base64": { + "type": "string", + "allOf": [ + { + "pattern": "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$" + } + ] + }, + "Prompt.FileSource": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "inline" + ] + } + }, + "required": [ + "type" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "uri" + ] + }, + "uri": { + "type": "string" + } + }, + "required": [ + "type", + "uri" + ], + "additionalProperties": false + } + ] + }, + "Prompt.FileAttachment": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/Prompt.Base64" + }, + "mime": { + "type": "string" + }, + "source": { + "$ref": "#/components/schemas/Prompt.FileSource" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "mention": { + "$ref": "#/components/schemas/Prompt.Mention" + } + }, + "required": [ + "data", + "mime", + "source" + ], + "additionalProperties": false + }, + "Prompt": { + "type": "object", + "properties": { + "text": { + "type": "string" + }, + "files": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Prompt.FileAttachment" + } + }, + "agents": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Prompt.AgentAttachment" + } + } + }, + "required": [ + "text" + ], + "additionalProperties": false + }, + "SessionInput.Admitted": { + "type": "object", + "properties": { + "admittedSeq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "prompt": { + "$ref": "#/components/schemas/Prompt" + }, + "delivery": { + "type": "string", + "enum": [ + "steer", + "queue" + ] + }, + "timeCreated": { + "type": "number" + }, + "promotedSeq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + } + }, + "required": [ + "admittedSeq", + "id", + "sessionID", + "prompt", + "delivery", + "timeCreated" + ], + "additionalProperties": false + }, + "ConflictError": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "ConflictError" + ] + }, + "message": { + "type": "string" + }, + "resource": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "message" + ], + "additionalProperties": false + }, + "CommandNotFoundError": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "CommandNotFoundError" + ] + }, + "command": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": [ + "_tag", + "command", + "message" + ], + "additionalProperties": false + }, + "CommandEvaluationError": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "CommandEvaluationError" + ] + }, + "command": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": [ + "_tag", + "command", + "message" + ], + "additionalProperties": false + }, + "SkillNotFoundError": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "SkillNotFoundError" + ] + }, + "skill": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": [ + "_tag", + "skill", + "message" + ], + "additionalProperties": false + }, + "SessionInput.Compaction": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "compaction" + ] + }, + "admittedSeq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "timeCreated": { + "type": "number" + }, + "handledSeq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + } + }, + "required": [ + "type", + "admittedSeq", + "id", + "sessionID", + "timeCreated" + ], + "additionalProperties": false + }, + "ServiceUnavailableError": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "ServiceUnavailableError" + ] + }, + "message": { + "type": "string" + }, + "service": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "message" + ], + "additionalProperties": false + }, + "SessionBusyError": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "SessionBusyError" + ] + }, + "sessionID": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": [ + "_tag", + "sessionID", + "message" + ], + "additionalProperties": false + }, + "UnknownError": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "UnknownError" + ] + }, + "message": { + "type": "string" + }, + "ref": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "_tag", + "message" + ], + "additionalProperties": false + }, + "Session.Message.AgentSelected": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + "metadata": { + "type": "object" + }, + "time": { + "type": "object", + "properties": { + "created": { + "type": "number" + } + }, + "required": [ + "created" + ], + "additionalProperties": false + }, + "type": { + "type": "string", + "enum": [ + "agent-switched" + ] + }, + "agent": { + "type": "string" + } + }, + "required": [ + "id", + "time", + "type", + "agent" + ], + "additionalProperties": false + }, + "Session.Message.ModelSelected": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + "metadata": { + "type": "object" + }, + "time": { + "type": "object", + "properties": { + "created": { + "type": "number" + } + }, + "required": [ + "created" + ], + "additionalProperties": false + }, + "type": { + "type": "string", + "enum": [ + "model-switched" + ] + }, + "model": { + "$ref": "#/components/schemas/Model.Ref" + }, + "previous": { + "$ref": "#/components/schemas/Model.Ref" + } + }, + "required": [ + "id", + "time", + "type", + "model" + ], + "additionalProperties": false + }, + "Session.Message.User": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + "metadata": { + "type": "object" + }, + "time": { + "type": "object", + "properties": { + "created": { + "type": "number" + } + }, + "required": [ + "created" + ], + "additionalProperties": false + }, + "text": { + "type": "string" + }, + "files": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Prompt.FileAttachment" + } + }, + "agents": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Prompt.AgentAttachment" + } + }, + "type": { + "type": "string", + "enum": [ + "user" + ] + } + }, + "required": [ + "id", + "time", + "text", + "type" + ], + "additionalProperties": false + }, + "Session.Message.Synthetic": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + "metadata": { + "type": "object" + }, + "time": { + "type": "object", + "properties": { + "created": { + "type": "number" + } + }, + "required": [ + "created" + ], + "additionalProperties": false + }, + "text": { + "type": "string" + }, + "description": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "synthetic" + ] + } + }, + "required": [ + "id", + "time", + "text", + "type" + ], + "additionalProperties": false + }, + "Session.Message.System": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + "metadata": { + "type": "object" + }, + "time": { + "type": "object", + "properties": { + "created": { + "type": "number" + } + }, + "required": [ + "created" + ], + "additionalProperties": false + }, + "type": { + "type": "string", + "enum": [ + "system" + ] + }, + "text": { + "type": "string" + } + }, + "required": [ + "id", + "time", + "type", + "text" + ], + "additionalProperties": false + }, + "Session.Message.Skill": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + "metadata": { + "type": "object" + }, + "time": { + "type": "object", + "properties": { + "created": { + "type": "number" + } + }, + "required": [ + "created" + ], + "additionalProperties": false + }, + "type": { + "type": "string", + "enum": [ + "skill" + ] + }, + "skill": { + "type": "string" + }, + "name": { + "type": "string" + }, + "text": { + "type": "string" + } + }, + "required": [ + "id", + "time", + "type", + "skill", + "name", + "text" + ], + "additionalProperties": false + }, + "Session.Message.Shell": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + "metadata": { + "type": "object" + }, + "time": { + "type": "object", + "properties": { + "created": { + "type": "number" + }, + "completed": { + "type": "number" + } + }, + "required": [ + "created" + ], + "additionalProperties": false + }, + "type": { + "type": "string", + "enum": [ + "shell" + ] + }, + "shellID": { + "type": "string", + "allOf": [ + { + "pattern": "^sh_" + } + ] + }, + "command": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "running", + "exited", + "timeout", + "killed" + ] + }, + "exit": { + "anyOf": [ + { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "NaN" + ] + }, + { + "type": "string", + "enum": [ + "Infinity" + ] + }, + { + "type": "string", + "enum": [ + "-Infinity" + ] + } + ] + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "output": { + "type": "object", + "properties": { + "output": { + "type": "string" + }, + "cursor": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "size": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "truncated": { + "type": "boolean" + } + }, + "required": [ + "output", + "cursor", + "size", + "truncated" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "time", + "type", + "shellID", + "command", + "status" + ], + "additionalProperties": false + }, + "Session.Message.Assistant.Text": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "text" + ] + }, + "text": { + "type": "string" + } + }, + "required": [ + "type", + "text" + ], + "additionalProperties": false + }, + "Session.Message.ProviderState": { + "type": "object" + }, + "Session.Message.Assistant.Reasoning": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "reasoning" + ] + }, + "text": { + "type": "string" + }, + "state": { + "$ref": "#/components/schemas/Session.Message.ProviderState" + }, + "time": { + "type": "object", + "properties": { + "created": { + "type": "number" + }, + "completed": { + "type": "number" + } + }, + "required": [ + "created" + ], + "additionalProperties": false + } + }, + "required": [ + "type", + "text" + ], + "additionalProperties": false + }, + "Session.Message.ToolState.Streaming": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "streaming" + ] + }, + "input": { + "type": "string" + } + }, + "required": [ + "status", + "input" + ], + "additionalProperties": false + }, + "Tool.TextContent": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "text" + ] + }, + "text": { + "type": "string" + } + }, + "required": [ + "type", + "text" + ], + "additionalProperties": false + }, + "Tool.FileContent": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "file" + ] + }, + "uri": { + "type": "string" + }, + "mime": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "type", + "uri", + "mime" + ], + "additionalProperties": false + }, + "LLM.ToolContent": { + "anyOf": [ + { + "$ref": "#/components/schemas/Tool.TextContent" + }, + { + "$ref": "#/components/schemas/Tool.FileContent" + } + ] + }, + "Session.Message.ToolState.Running": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "running" + ] + }, + "input": { + "type": "object" + }, + "structured": { + "type": "object" + }, + "content": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LLM.ToolContent" + } + } + }, + "required": [ + "status", + "input", + "structured", + "content" + ], + "additionalProperties": false + }, + "Session.Message.ToolState.Completed": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "completed" + ] + }, + "input": { + "type": "object" + }, + "content": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LLM.ToolContent" + } + }, + "structured": { + "type": "object" + }, + "result": {} + }, + "required": [ + "status", + "input", + "content", + "structured" + ], + "additionalProperties": false + }, + "Session.StructuredError": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": [ + "type", + "message" + ], + "additionalProperties": false + }, + "Session.Message.ToolState.Error": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "error" + ] + }, + "input": { + "type": "object" + }, + "content": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LLM.ToolContent" + } + }, + "structured": { + "type": "object" + }, + "error": { + "$ref": "#/components/schemas/Session.StructuredError" + }, + "result": {} + }, + "required": [ + "status", + "input", + "content", + "structured", + "error" + ], + "additionalProperties": false + }, + "Session.Message.Assistant.Tool": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "tool" + ] + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "executed": { + "type": "boolean" + }, + "providerState": { + "$ref": "#/components/schemas/Session.Message.ProviderState" + }, + "providerResultState": { + "$ref": "#/components/schemas/Session.Message.ProviderState" + }, + "state": { + "anyOf": [ + { + "$ref": "#/components/schemas/Session.Message.ToolState.Streaming" + }, + { + "$ref": "#/components/schemas/Session.Message.ToolState.Running" + }, + { + "$ref": "#/components/schemas/Session.Message.ToolState.Completed" + }, + { + "$ref": "#/components/schemas/Session.Message.ToolState.Error" + } + ] + }, + "time": { + "type": "object", + "properties": { + "created": { + "type": "number" + }, + "ran": { + "type": "number" + }, + "completed": { + "type": "number" + } + }, + "required": [ + "created" + ], + "additionalProperties": false + } + }, + "required": [ + "type", + "id", + "name", + "state", + "time" + ], + "additionalProperties": false + }, + "Session.Message.Assistant.Retry": { + "type": "object", + "properties": { + "attempt": { + "type": "integer", + "allOf": [ + { + "exclusiveMinimum": 0 + } + ] + }, + "at": { + "type": "number" + }, + "error": { + "$ref": "#/components/schemas/Session.StructuredError" + } + }, + "required": [ + "attempt", + "at", + "error" + ], + "additionalProperties": false + }, + "Session.Message.Assistant": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + "metadata": { + "type": "object" + }, + "time": { + "type": "object", + "properties": { + "created": { + "type": "number" + }, + "completed": { + "type": "number" + } + }, + "required": [ + "created" + ], + "additionalProperties": false + }, + "type": { + "type": "string", + "enum": [ + "assistant" + ] + }, + "agent": { + "type": "string" + }, + "model": { + "$ref": "#/components/schemas/Model.Ref" + }, + "content": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/Session.Message.Assistant.Text" + }, + { + "$ref": "#/components/schemas/Session.Message.Assistant.Reasoning" + }, + { + "$ref": "#/components/schemas/Session.Message.Assistant.Tool" + } + ] + } + }, + "snapshot": { + "type": "object", + "properties": { + "start": { + "type": "string" + }, + "end": { + "type": "string" + }, + "files": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + }, + "finish": { + "type": "string", + "enum": [ + "stop", + "length", + "tool-calls", + "content-filter", + "error", + "unknown" + ] + }, + "cost": { + "$ref": "#/components/schemas/Money.USD" + }, + "tokens": { + "$ref": "#/components/schemas/TokenUsage.Info" + }, + "error": { + "$ref": "#/components/schemas/Session.StructuredError" + }, + "retry": { + "$ref": "#/components/schemas/Session.Message.Assistant.Retry" + } + }, + "required": [ + "id", + "time", + "type", + "agent", + "model", + "content" + ], + "additionalProperties": false + }, + "Session.Message.Compaction.Running": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "compaction" + ] + }, + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + "metadata": { + "type": "object" + }, + "time": { + "type": "object", + "properties": { + "created": { + "type": "number" + } + }, + "required": [ + "created" + ], + "additionalProperties": false + }, + "status": { + "type": "string", + "enum": [ + "running" + ] + }, + "reason": { + "type": "string", + "enum": [ + "auto", + "manual" + ] + }, + "summary": { + "type": "string" + }, + "recent": { + "type": "string" + } + }, + "required": [ + "type", + "id", + "time", + "status", + "reason", + "summary", + "recent" + ], + "additionalProperties": false + }, + "Session.Message.Compaction.Completed": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "compaction" + ] + }, + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + "metadata": { + "type": "object" + }, + "time": { + "type": "object", + "properties": { + "created": { + "type": "number" + } + }, + "required": [ + "created" + ], + "additionalProperties": false + }, + "status": { + "type": "string", + "enum": [ + "completed" + ] + }, + "reason": { + "type": "string", + "enum": [ + "auto", + "manual" + ] + }, + "summary": { + "type": "string" + }, + "recent": { + "type": "string" + } + }, + "required": [ + "type", + "id", + "time", + "status", + "reason", + "summary", + "recent" + ], + "additionalProperties": false + }, + "Session.Message.Compaction.Failed": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "compaction" + ] + }, + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + "metadata": { + "type": "object" + }, + "time": { + "type": "object", + "properties": { + "created": { + "type": "number" + } + }, + "required": [ + "created" + ], + "additionalProperties": false + }, + "status": { + "type": "string", + "enum": [ + "failed" + ] + }, + "reason": { + "type": "string", + "enum": [ + "auto", + "manual" + ] + }, + "error": { + "$ref": "#/components/schemas/Session.StructuredError" + } + }, + "required": [ + "type", + "id", + "time", + "status", + "reason", + "error" + ], + "additionalProperties": false + }, + "Session.Message.Compaction": { + "anyOf": [ + { + "$ref": "#/components/schemas/Session.Message.Compaction.Running" + }, + { + "$ref": "#/components/schemas/Session.Message.Compaction.Completed" + }, + { + "$ref": "#/components/schemas/Session.Message.Compaction.Failed" + } + ] + }, + "Session.Message.Info": { + "anyOf": [ + { + "$ref": "#/components/schemas/Session.Message.AgentSelected" + }, + { + "$ref": "#/components/schemas/Session.Message.ModelSelected" + }, + { + "$ref": "#/components/schemas/Session.Message.User" + }, + { + "$ref": "#/components/schemas/Session.Message.Synthetic" + }, + { + "$ref": "#/components/schemas/Session.Message.System" + }, + { + "$ref": "#/components/schemas/Session.Message.Skill" + }, + { + "$ref": "#/components/schemas/Session.Message.Shell" + }, + { + "$ref": "#/components/schemas/Session.Message.Assistant" + }, + { + "$ref": "#/components/schemas/Session.Message.Compaction" + } + ] + }, + "InstructionEntry.Key": { + "type": "string", + "allOf": [ + { + "pattern": "^[a-z0-9][a-z0-9._-]*$", + "description": "Instruction entry key (lowercase alphanumerics plus . _ -)" + } + ] + }, + "InstructionEntry.Info": { + "type": "object", + "properties": { + "key": { + "$ref": "#/components/schemas/InstructionEntry.Key" + }, + "value": {} + }, + "required": [ + "key", + "value" + ], + "additionalProperties": false + }, + "session.agent.selected": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.agent.selected" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "agent": { + "type": "string" + } + }, + "required": [ + "sessionID", + "agent" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "session.model.selected": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.model.selected" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "model": { + "$ref": "#/components/schemas/Model.Ref" + } + }, + "required": [ + "sessionID", + "model" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "session.moved": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.moved" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "subpath": { + "type": "string" + } + }, + "required": [ + "sessionID", + "location" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "session.renamed": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.renamed" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "title": { + "type": "string" + } + }, + "required": [ + "sessionID", + "title" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "session.deleted": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.deleted" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 2 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + } + }, + "required": [ + "sessionID" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "session.forked": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.forked" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "parentID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "from": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + } + }, + "required": [ + "sessionID", + "parentID" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "session.prompt.promoted": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.prompt.promoted" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "inputID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + } + }, + "required": [ + "sessionID", + "inputID" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "session.prompt.admitted": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.prompt.admitted" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "inputID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + "prompt": { + "$ref": "#/components/schemas/Prompt" + }, + "delivery": { + "type": "string", + "enum": [ + "steer", + "queue" + ] + } + }, + "required": [ + "sessionID", + "inputID", + "prompt", + "delivery" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "session.execution.started": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.execution.started" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + } + }, + "required": [ + "sessionID" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "session.execution.succeeded": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.execution.succeeded" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + } + }, + "required": [ + "sessionID" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "session.execution.failed": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.execution.failed" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "error": { + "$ref": "#/components/schemas/Session.StructuredError" + } + }, + "required": [ + "sessionID", + "error" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "session.execution.interrupted": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.execution.interrupted" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "reason": { + "type": "string", + "enum": [ + "user", + "shutdown", + "superseded" + ] + } + }, + "required": [ + "sessionID", + "reason" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "session.instructions.updated": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.instructions.updated" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "text": { + "type": "string" + } + }, + "required": [ + "sessionID", + "text" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "session.synthetic": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.synthetic" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "text": { + "type": "string" + }, + "description": { + "type": "string" + }, + "metadata": { + "type": "object" + } + }, + "required": [ + "sessionID", + "text" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "session.skill.activated": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.skill.activated" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "text": { + "type": "string" + } + }, + "required": [ + "sessionID", + "id", + "name", + "text" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "Shell": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^sh_" + } + ] + }, + "status": { + "type": "string", + "enum": [ + "running", + "exited", + "timeout", + "killed" + ] + }, + "command": { + "type": "string" + }, + "cwd": { + "type": "string" + }, + "shell": { + "type": "string" + }, + "file": { + "type": "string" + }, + "pid": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "exit": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "NaN" + ] + }, + { + "type": "string", + "enum": [ + "Infinity" + ] + }, + { + "type": "string", + "enum": [ + "-Infinity" + ] + } + ] + }, + "metadata": { + "type": "object" + }, + "time": { + "type": "object", + "properties": { + "started": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "NaN" + ] + }, + { + "type": "string", + "enum": [ + "Infinity" + ] + }, + { + "type": "string", + "enum": [ + "-Infinity" + ] + } + ] + }, + "completed": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "NaN" + ] + }, + { + "type": "string", + "enum": [ + "Infinity" + ] + }, + { + "type": "string", + "enum": [ + "-Infinity" + ] + } + ] + } + }, + "required": [ + "started" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "status", + "command", + "cwd", + "shell", + "file", + "metadata", + "time" + ], + "additionalProperties": false + }, + "session.shell.started": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.shell.started" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "shell": { + "$ref": "#/components/schemas/Shell" + } + }, + "required": [ + "sessionID", + "shell" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "session.shell.ended": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.shell.ended" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "shell": { + "$ref": "#/components/schemas/Shell" + }, + "output": { + "type": "object", + "properties": { + "output": { + "type": "string" + }, + "cursor": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "size": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "truncated": { + "type": "boolean" + } + }, + "required": [ + "output", + "cursor", + "size", + "truncated" + ], + "additionalProperties": false + } + }, + "required": [ + "sessionID", + "shell", + "output" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "session.step.started": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.step.started" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "assistantMessageID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + "agent": { + "type": "string" + }, + "model": { + "$ref": "#/components/schemas/Model.Ref" + }, + "snapshot": { + "type": "string" + } + }, + "required": [ + "sessionID", + "assistantMessageID", + "agent", + "model" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "session.step.ended": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.step.ended" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "assistantMessageID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + "finish": { + "type": "string", + "enum": [ + "stop", + "length", + "tool-calls", + "content-filter", + "error", + "unknown" + ] + }, + "cost": { + "$ref": "#/components/schemas/Money.USD" + }, + "tokens": { + "$ref": "#/components/schemas/TokenUsage.Info" + }, + "snapshot": { + "type": "string" + }, + "files": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "sessionID", + "assistantMessageID", + "finish", + "cost", + "tokens" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "session.step.failed": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.step.failed" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "assistantMessageID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + "error": { + "$ref": "#/components/schemas/Session.StructuredError" + }, + "cost": { + "$ref": "#/components/schemas/Money.USD" + }, + "tokens": { + "$ref": "#/components/schemas/TokenUsage.Info" + } + }, + "required": [ + "sessionID", + "assistantMessageID", + "error" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "session.text.started": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.text.started" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "assistantMessageID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + "ordinal": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + } + }, + "required": [ + "sessionID", + "assistantMessageID", + "ordinal" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "session.text.ended": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.text.ended" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "assistantMessageID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + "ordinal": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "text": { + "type": "string" + } + }, + "required": [ + "sessionID", + "assistantMessageID", + "ordinal", + "text" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "Session.Message.ProviderState3": { + "type": "object" + }, + "session.reasoning.started": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.reasoning.started" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "assistantMessageID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + "ordinal": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "state": { + "$ref": "#/components/schemas/Session.Message.ProviderState3" + } + }, + "required": [ + "sessionID", + "assistantMessageID", + "ordinal" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "Session.Message.ProviderState4": { + "type": "object" + }, + "session.reasoning.ended": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.reasoning.ended" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "assistantMessageID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + "ordinal": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "text": { + "type": "string" + }, + "state": { + "$ref": "#/components/schemas/Session.Message.ProviderState4" + } + }, + "required": [ + "sessionID", + "assistantMessageID", + "ordinal", + "text" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "session.tool.input.started": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.tool.input.started" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "assistantMessageID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + "callID": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "sessionID", + "assistantMessageID", + "callID", + "name" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "session.tool.input.ended": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.tool.input.ended" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "assistantMessageID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + "callID": { + "type": "string" + }, + "text": { + "type": "string" + } + }, + "required": [ + "sessionID", + "assistantMessageID", + "callID", + "text" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "Session.Message.ProviderState5": { + "type": "object" + }, + "session.tool.called": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.tool.called" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "assistantMessageID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + "callID": { + "type": "string" + }, + "input": { + "type": "object" + }, + "executed": { + "type": "boolean" + }, + "state": { + "$ref": "#/components/schemas/Session.Message.ProviderState5" + } + }, + "required": [ + "sessionID", + "assistantMessageID", + "callID", + "input", + "executed" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "session.tool.progress": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.tool.progress" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "assistantMessageID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + "callID": { + "type": "string" + }, + "structured": { + "type": "object" + }, + "content": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LLM.ToolContent" + } + } + }, + "required": [ + "sessionID", + "assistantMessageID", + "callID", + "structured", + "content" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "Session.Message.ProviderState6": { + "type": "object" + }, + "session.tool.success": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.tool.success" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "assistantMessageID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + "callID": { + "type": "string" + }, + "structured": { + "type": "object" + }, + "content": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LLM.ToolContent" + } + }, + "result": {}, + "executed": { + "type": "boolean" + }, + "resultState": { + "$ref": "#/components/schemas/Session.Message.ProviderState6" + } + }, + "required": [ + "sessionID", + "assistantMessageID", + "callID", + "structured", + "content", + "executed" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "Session.Message.ProviderState7": { + "type": "object" + }, + "session.tool.failed": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.tool.failed" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "assistantMessageID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + "callID": { + "type": "string" + }, + "error": { + "$ref": "#/components/schemas/Session.StructuredError" + }, + "result": {}, + "executed": { + "type": "boolean" + }, + "resultState": { + "$ref": "#/components/schemas/Session.Message.ProviderState7" + } + }, + "required": [ + "sessionID", + "assistantMessageID", + "callID", + "error", + "executed" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "session.retry.scheduled": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.retry.scheduled" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "assistantMessageID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + "attempt": { + "type": "integer", + "allOf": [ + { + "exclusiveMinimum": 0 + } + ] + }, + "at": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "error": { + "$ref": "#/components/schemas/Session.StructuredError" + } + }, + "required": [ + "sessionID", + "assistantMessageID", + "attempt", + "at", + "error" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "session.compaction.admitted": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.compaction.admitted" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "inputID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + } + }, + "required": [ + "sessionID", + "inputID" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "session.compaction.started": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.compaction.started" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "reason": { + "type": "string", + "enum": [ + "auto", + "manual" + ] + }, + "recent": { + "type": "string" + }, + "inputID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + } + }, + "required": [ + "sessionID", + "reason", + "recent" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "session.compaction.ended": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.compaction.ended" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "reason": { + "type": "string", + "enum": [ + "auto", + "manual" + ] + }, + "text": { + "type": "string" + }, + "recent": { + "type": "string" + } + }, + "required": [ + "sessionID", + "reason", + "text", + "recent" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "session.compaction.failed": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.compaction.failed" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "reason": { + "type": "string", + "enum": [ + "auto", + "manual" + ] + }, + "error": { + "$ref": "#/components/schemas/Session.StructuredError" + }, + "inputID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + } + }, + "required": [ + "sessionID", + "reason", + "error" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "session.revert.staged": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.revert.staged" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "revert": { + "$ref": "#/components/schemas/Session.Revert" + } + }, + "required": [ + "sessionID", + "revert" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "session.revert.cleared": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.revert.cleared" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + } + }, + "required": [ + "sessionID" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "session.revert.committed": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.revert.committed" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "to": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + } + }, + "required": [ + "sessionID", + "to" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "Session.Event.Durable": { + "oneOf": [ + { + "$ref": "#/components/schemas/session.agent.selected" + }, + { + "$ref": "#/components/schemas/session.model.selected" + }, + { + "$ref": "#/components/schemas/session.moved" + }, + { + "$ref": "#/components/schemas/session.renamed" + }, + { + "$ref": "#/components/schemas/session.deleted" + }, + { + "$ref": "#/components/schemas/session.forked" + }, + { + "$ref": "#/components/schemas/session.prompt.promoted" + }, + { + "$ref": "#/components/schemas/session.prompt.admitted" + }, + { + "$ref": "#/components/schemas/session.execution.started" + }, + { + "$ref": "#/components/schemas/session.execution.succeeded" + }, + { + "$ref": "#/components/schemas/session.execution.failed" + }, + { + "$ref": "#/components/schemas/session.execution.interrupted" + }, + { + "$ref": "#/components/schemas/session.instructions.updated" + }, + { + "$ref": "#/components/schemas/session.synthetic" + }, + { + "$ref": "#/components/schemas/session.skill.activated" + }, + { + "$ref": "#/components/schemas/session.shell.started" + }, + { + "$ref": "#/components/schemas/session.shell.ended" + }, + { + "$ref": "#/components/schemas/session.step.started" + }, + { + "$ref": "#/components/schemas/session.step.ended" + }, + { + "$ref": "#/components/schemas/session.step.failed" + }, + { + "$ref": "#/components/schemas/session.text.started" + }, + { + "$ref": "#/components/schemas/session.text.ended" + }, + { + "$ref": "#/components/schemas/session.reasoning.started" + }, + { + "$ref": "#/components/schemas/session.reasoning.ended" + }, + { + "$ref": "#/components/schemas/session.tool.input.started" + }, + { + "$ref": "#/components/schemas/session.tool.input.ended" + }, + { + "$ref": "#/components/schemas/session.tool.called" + }, + { + "$ref": "#/components/schemas/session.tool.progress" + }, + { + "$ref": "#/components/schemas/session.tool.success" + }, + { + "$ref": "#/components/schemas/session.tool.failed" + }, + { + "$ref": "#/components/schemas/session.retry.scheduled" + }, + { + "$ref": "#/components/schemas/session.compaction.admitted" + }, + { + "$ref": "#/components/schemas/session.compaction.started" + }, + { + "$ref": "#/components/schemas/session.compaction.ended" + }, + { + "$ref": "#/components/schemas/session.compaction.failed" + }, + { + "$ref": "#/components/schemas/session.revert.staged" + }, + { + "$ref": "#/components/schemas/session.revert.cleared" + }, + { + "$ref": "#/components/schemas/session.revert.committed" + } + ] + }, + "EventLog.Synced": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "log.synced" + ] + }, + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + } + }, + "required": [ + "type", + "aggregateID" + ], + "additionalProperties": false, + "description": "Marker emitted once when a log read reaches its captured watermark. The reader holds every event committed at or below seq." + }, + "SessionLogItem": { + "anyOf": [ + { + "$ref": "#/components/schemas/Session.Event.Durable" + }, + { + "$ref": "#/components/schemas/EventLog.Synced" + } + ] + }, + "SessionLogItemStream": { + "type": "string", + "contentSchema": { + "$ref": "#/components/schemas/SessionLogItem" + }, + "contentMediaType": "application/json" + }, + "SessionMessagesResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Session.Message.Info" + } + }, + "cursor": { + "type": "object", + "properties": { + "previous": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "next": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + }, + "required": [ + "data", + "cursor" + ], + "additionalProperties": false + }, + "Model.Capabilities": { + "type": "object", + "properties": { + "tools": { + "type": "boolean" + }, + "input": { + "type": "array", + "items": { + "type": "string" + } + }, + "output": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "tools", + "input", + "output" + ], + "additionalProperties": false + }, + "Model.Variant": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "settings": { + "type": "object" + }, + "headers": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "body": { + "type": "object" + } + }, + "required": [ + "id" + ], + "additionalProperties": false + }, + "Money.USDPerMillionTokens": { + "type": "number" + }, + "Model.Cost": { + "type": "object", + "properties": { + "tier": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "context" + ] + }, + "size": { + "type": "integer" + } + }, + "required": [ + "type", + "size" + ], + "additionalProperties": false + }, + "input": { + "$ref": "#/components/schemas/Money.USDPerMillionTokens" + }, + "output": { + "$ref": "#/components/schemas/Money.USDPerMillionTokens" + }, + "cache": { + "type": "object", + "properties": { + "read": { + "$ref": "#/components/schemas/Money.USDPerMillionTokens" + }, + "write": { + "$ref": "#/components/schemas/Money.USDPerMillionTokens" + } + }, + "required": [ + "read", + "write" + ], + "additionalProperties": false + } + }, + "required": [ + "input", + "output", + "cache" + ], + "additionalProperties": false + }, + "Model.Info": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "modelID": { + "type": "string" + }, + "providerID": { + "type": "string" + }, + "family": { + "type": "string" + }, + "name": { + "type": "string" + }, + "package": { + "type": "string" + }, + "settings": { + "type": "object" + }, + "headers": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "body": { + "type": "object" + }, + "capabilities": { + "$ref": "#/components/schemas/Model.Capabilities" + }, + "variants": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Model.Variant" + } + }, + "time": { + "type": "object", + "properties": { + "released": { + "type": "number" + } + }, + "required": [ + "released" + ], + "additionalProperties": false + }, + "cost": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Model.Cost" + } + }, + "status": { + "type": "string", + "enum": [ + "alpha", + "beta", + "deprecated", + "active" + ] + }, + "enabled": { + "type": "boolean" + }, + "limit": { + "type": "object", + "properties": { + "context": { + "type": "integer" + }, + "input": { + "type": "integer" + }, + "output": { + "type": "integer" + } + }, + "required": [ + "context", + "output" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "modelID", + "providerID", + "name", + "capabilities", + "variants", + "time", + "cost", + "status", + "enabled", + "limit" + ], + "additionalProperties": false + }, + "GenerateTextResponse": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "text": { + "type": "string" + } + }, + "required": [ + "text" + ], + "additionalProperties": false + } + }, + "required": [ + "data" + ], + "additionalProperties": false + }, + "ProviderV2.Info": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "integrationID": { + "type": "string" + }, + "name": { + "type": "string" + }, + "disabled": { + "type": "boolean" + }, + "package": { + "type": "string" + }, + "settings": { + "type": "object" + }, + "headers": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "body": { + "type": "object" + } + }, + "required": [ + "id", + "name", + "package" + ], + "additionalProperties": false + }, + "ProviderNotFoundError": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "ProviderNotFoundError" + ] + }, + "providerID": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": [ + "_tag", + "providerID", + "message" + ], + "additionalProperties": false + }, + "Integration.When": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "op": { + "type": "string", + "enum": [ + "eq", + "neq" + ] + }, + "value": { + "type": "string" + } + }, + "required": [ + "key", + "op", + "value" + ], + "additionalProperties": false + }, + "Integration.TextPrompt": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "text" + ] + }, + "key": { + "type": "string" + }, + "message": { + "type": "string" + }, + "placeholder": { + "type": "string" + }, + "when": { + "$ref": "#/components/schemas/Integration.When" + } + }, + "required": [ + "type", + "key", + "message" + ], + "additionalProperties": false + }, + "Integration.SelectPrompt": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "select" + ] + }, + "key": { + "type": "string" + }, + "message": { + "type": "string" + }, + "options": { + "type": "array", + "items": { + "type": "object", + "properties": { + "label": { + "type": "string" + }, + "value": { + "type": "string" + }, + "hint": { + "type": "string" + } + }, + "required": [ + "label", + "value" + ], + "additionalProperties": false + } + }, + "when": { + "$ref": "#/components/schemas/Integration.When" + } + }, + "required": [ + "type", + "key", + "message", + "options" + ], + "additionalProperties": false + }, + "Integration.OAuthMethod": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "oauth" + ] + }, + "label": { + "type": "string" + }, + "prompts": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/Integration.TextPrompt" + }, + { + "$ref": "#/components/schemas/Integration.SelectPrompt" + } + ] + } + } + }, + "required": [ + "id", + "type", + "label" + ], + "additionalProperties": false + }, + "Integration.KeyMethod": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "key" + ] + }, + "label": { + "type": "string" + } + }, + "required": [ + "type" + ], + "additionalProperties": false + }, + "Integration.EnvMethod": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "env" + ] + }, + "names": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "type", + "names" + ], + "additionalProperties": false + }, + "Integration.Method": { + "anyOf": [ + { + "$ref": "#/components/schemas/Integration.OAuthMethod" + }, + { + "$ref": "#/components/schemas/Integration.KeyMethod" + }, + { + "$ref": "#/components/schemas/Integration.EnvMethod" + } + ] + }, + "Connection.CredentialInfo": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "credential" + ] + }, + "id": { + "type": "string" + }, + "label": { + "type": "string" + } + }, + "required": [ + "type", + "id", + "label" + ], + "additionalProperties": false + }, + "Connection.EnvInfo": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "env" + ] + }, + "name": { + "type": "string" + } + }, + "required": [ + "type", + "name" + ], + "additionalProperties": false + }, + "Connection.Info": { + "anyOf": [ + { + "$ref": "#/components/schemas/Connection.CredentialInfo" + }, + { + "$ref": "#/components/schemas/Connection.EnvInfo" + } + ] + }, + "Integration.Info": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "methods": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Integration.Method" + } + }, + "connections": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Connection.Info" + } + } + }, + "required": [ + "id", + "name", + "methods", + "connections" + ], + "additionalProperties": false + }, + "Integration.Attempt": { + "type": "object", + "properties": { + "attemptID": { + "type": "string" + }, + "url": { + "type": "string" + }, + "instructions": { + "type": "string" + }, + "mode": { + "type": "string", + "enum": [ + "auto", + "code" + ] + }, + "time": { + "type": "object", + "properties": { + "created": { + "anyOf": [ + { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "NaN" + ] + }, + { + "type": "string", + "enum": [ + "Infinity" + ] + }, + { + "type": "string", + "enum": [ + "-Infinity" + ] + } + ] + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "expires": { + "anyOf": [ + { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "NaN" + ] + }, + { + "type": "string", + "enum": [ + "Infinity" + ] + }, + { + "type": "string", + "enum": [ + "-Infinity" + ] + } + ] + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + } + }, + "required": [ + "created", + "expires" + ], + "additionalProperties": false + } + }, + "required": [ + "attemptID", + "url", + "instructions", + "mode", + "time" + ], + "additionalProperties": false + }, + "Integration.AttemptStatus": { + "anyOf": [ + { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "pending" + ] + }, + "time": { + "type": "object", + "properties": { + "created": { + "anyOf": [ + { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "NaN" + ] + }, + { + "type": "string", + "enum": [ + "Infinity" + ] + }, + { + "type": "string", + "enum": [ + "-Infinity" + ] + } + ] + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "expires": { + "anyOf": [ + { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "NaN" + ] + }, + { + "type": "string", + "enum": [ + "Infinity" + ] + }, + { + "type": "string", + "enum": [ + "-Infinity" + ] + } + ] + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + } + }, + "required": [ + "created", + "expires" + ], + "additionalProperties": false + } + }, + "required": [ + "status", + "time" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "complete" + ] + }, + "time": { + "type": "object", + "properties": { + "created": { + "anyOf": [ + { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "NaN" + ] + }, + { + "type": "string", + "enum": [ + "Infinity" + ] + }, + { + "type": "string", + "enum": [ + "-Infinity" + ] + } + ] + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "expires": { + "anyOf": [ + { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "NaN" + ] + }, + { + "type": "string", + "enum": [ + "Infinity" + ] + }, + { + "type": "string", + "enum": [ + "-Infinity" + ] + } + ] + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + } + }, + "required": [ + "created", + "expires" + ], + "additionalProperties": false + } + }, + "required": [ + "status", + "time" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "failed" + ] + }, + "message": { + "type": "string" + }, + "time": { + "type": "object", + "properties": { + "created": { + "anyOf": [ + { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "NaN" + ] + }, + { + "type": "string", + "enum": [ + "Infinity" + ] + }, + { + "type": "string", + "enum": [ + "-Infinity" + ] + } + ] + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "expires": { + "anyOf": [ + { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "NaN" + ] + }, + { + "type": "string", + "enum": [ + "Infinity" + ] + }, + { + "type": "string", + "enum": [ + "-Infinity" + ] + } + ] + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + } + }, + "required": [ + "created", + "expires" + ], + "additionalProperties": false + } + }, + "required": [ + "status", + "message", + "time" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "expired" + ] + }, + "time": { + "type": "object", + "properties": { + "created": { + "anyOf": [ + { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "NaN" + ] + }, + { + "type": "string", + "enum": [ + "Infinity" + ] + }, + { + "type": "string", + "enum": [ + "-Infinity" + ] + } + ] + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "expires": { + "anyOf": [ + { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "NaN" + ] + }, + { + "type": "string", + "enum": [ + "Infinity" + ] + }, + { + "type": "string", + "enum": [ + "-Infinity" + ] + } + ] + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + } + }, + "required": [ + "created", + "expires" + ], + "additionalProperties": false + } + }, + "required": [ + "status", + "time" + ], + "additionalProperties": false + } + ] + }, + "Mcp.Status.Connected": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "connected" + ] + } + }, + "required": [ + "status" + ], + "additionalProperties": false + }, + "Mcp.Status.Pending": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "pending" + ] + } + }, + "required": [ + "status" + ], + "additionalProperties": false + }, + "Mcp.Status.Disabled": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "disabled" + ] + } + }, + "required": [ + "status" + ], + "additionalProperties": false + }, + "Mcp.Status.Failed": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "failed" + ] + }, + "error": { + "type": "string" + } + }, + "required": [ + "status", + "error" + ], + "additionalProperties": false + }, + "Mcp.Status.NeedsAuth": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "needs_auth" + ] + } + }, + "required": [ + "status" + ], + "additionalProperties": false + }, + "Mcp.Status.NeedsClientRegistration": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "needs_client_registration" + ] + }, + "error": { + "type": "string" + } + }, + "required": [ + "status", + "error" + ], + "additionalProperties": false + }, + "Mcp.Server": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "status": { + "anyOf": [ + { + "$ref": "#/components/schemas/Mcp.Status.Connected" + }, + { + "$ref": "#/components/schemas/Mcp.Status.Pending" + }, + { + "$ref": "#/components/schemas/Mcp.Status.Disabled" + }, + { + "$ref": "#/components/schemas/Mcp.Status.Failed" + }, + { + "$ref": "#/components/schemas/Mcp.Status.NeedsAuth" + }, + { + "$ref": "#/components/schemas/Mcp.Status.NeedsClientRegistration" + } + ] + }, + "integrationID": { + "type": "string" + } + }, + "required": [ + "name", + "status" + ], + "additionalProperties": false + }, + "Mcp.Resource": { + "type": "object", + "properties": { + "server": { + "type": "string" + }, + "name": { + "type": "string" + }, + "uri": { + "type": "string" + }, + "description": { + "type": "string" + }, + "mimeType": { + "type": "string" + } + }, + "required": [ + "server", + "name", + "uri" + ], + "additionalProperties": false + }, + "Mcp.ResourceTemplate": { + "type": "object", + "properties": { + "server": { + "type": "string" + }, + "name": { + "type": "string" + }, + "uriTemplate": { + "type": "string" + }, + "description": { + "type": "string" + }, + "mimeType": { + "type": "string" + } + }, + "required": [ + "server", + "name", + "uriTemplate" + ], + "additionalProperties": false + }, + "Mcp.ResourceCatalog": { + "type": "object", + "properties": { + "resources": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Mcp.Resource" + } + }, + "templates": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Mcp.ResourceTemplate" + } + } + }, + "required": [ + "resources", + "templates" + ], + "additionalProperties": false + }, + "Project.Vcs": { + "type": "string", + "enum": [ + "git", + "hg" + ] + }, + "Project.Icon": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "override": { + "type": "string" + }, + "color": { + "type": "string" + } + }, + "additionalProperties": false + }, + "Project.Commands": { + "type": "object", + "properties": { + "start": { + "type": "string", + "description": "Startup script to run when creating a new workspace (worktree)" + } + }, + "additionalProperties": false + }, + "Project.Time": { + "type": "object", + "properties": { + "created": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "updated": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "initialized": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + } + }, + "required": [ + "created", + "updated" + ], + "additionalProperties": false + }, + "Project": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "worktree": { + "type": "string" + }, + "vcs": { + "$ref": "#/components/schemas/Project.Vcs" + }, + "name": { + "type": "string" + }, + "icon": { + "$ref": "#/components/schemas/Project.Icon" + }, + "commands": { + "$ref": "#/components/schemas/Project.Commands" + }, + "time": { + "$ref": "#/components/schemas/Project.Time" + }, + "sandboxes": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "id", + "worktree", + "time", + "sandboxes" + ], + "additionalProperties": false + }, + "Project.Current": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "directory": { + "type": "string" + } + }, + "required": [ + "id", + "directory" + ], + "additionalProperties": false + }, + "Project.Directory": { + "type": "object", + "properties": { + "directory": { + "type": "string" + }, + "strategy": { + "type": "string" + } + }, + "required": [ + "directory" + ], + "additionalProperties": false + }, + "Project.Directories": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Project.Directory" + } + }, + "Form.Metadata": { + "type": "object" + }, + "Form.When": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "op": { + "type": "string", + "enum": [ + "eq", + "neq" + ] + }, + "value": { + "anyOf": [ + { + "type": "string" + }, + { + "anyOf": [ + { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "NaN" + ] + }, + { + "type": "string", + "enum": [ + "Infinity" + ] + }, + { + "type": "string", + "enum": [ + "-Infinity" + ] + } + ] + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + { + "type": "boolean" + } + ] + } + }, + "required": [ + "key", + "op", + "value" + ], + "additionalProperties": false + }, + "Form.Option": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "label": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": [ + "value", + "label" + ], + "additionalProperties": false + }, + "Form.StringField": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "required": { + "type": "boolean" + }, + "when": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Form.When" + } + }, + "type": { + "type": "string", + "enum": [ + "string" + ] + }, + "format": { + "type": "string", + "enum": [ + "email", + "uri", + "date", + "date-time" + ] + }, + "minLength": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "maxLength": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "pattern": { + "type": "string" + }, + "placeholder": { + "type": "string" + }, + "default": { + "type": "string" + }, + "options": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Form.Option" + } + }, + "custom": { + "type": "boolean" + } + }, + "required": [ + "key", + "type" + ], + "additionalProperties": false + }, + "Form.NumberField": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "required": { + "type": "boolean" + }, + "when": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Form.When" + } + }, + "type": { + "type": "string", + "enum": [ + "number" + ] + }, + "minimum": { + "anyOf": [ + { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "NaN" + ] + }, + { + "type": "string", + "enum": [ + "Infinity" + ] + }, + { + "type": "string", + "enum": [ + "-Infinity" + ] + } + ] + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "maximum": { + "anyOf": [ + { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "NaN" + ] + }, + { + "type": "string", + "enum": [ + "Infinity" + ] + }, + { + "type": "string", + "enum": [ + "-Infinity" + ] + } + ] + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "default": { + "anyOf": [ + { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "NaN" + ] + }, + { + "type": "string", + "enum": [ + "Infinity" + ] + }, + { + "type": "string", + "enum": [ + "-Infinity" + ] + } + ] + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + } + }, + "required": [ + "key", + "type" + ], + "additionalProperties": false + }, + "Form.IntegerField": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "required": { + "type": "boolean" + }, + "when": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Form.When" + } + }, + "type": { + "type": "string", + "enum": [ + "integer" + ] + }, + "minimum": { + "anyOf": [ + { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "NaN" + ] + }, + { + "type": "string", + "enum": [ + "Infinity" + ] + }, + { + "type": "string", + "enum": [ + "-Infinity" + ] + } + ] + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "maximum": { + "anyOf": [ + { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "NaN" + ] + }, + { + "type": "string", + "enum": [ + "Infinity" + ] + }, + { + "type": "string", + "enum": [ + "-Infinity" + ] + } + ] + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "default": { + "anyOf": [ + { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "NaN" + ] + }, + { + "type": "string", + "enum": [ + "Infinity" + ] + }, + { + "type": "string", + "enum": [ + "-Infinity" + ] + } + ] + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + } + }, + "required": [ + "key", + "type" + ], + "additionalProperties": false + }, + "Form.BooleanField": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "required": { + "type": "boolean" + }, + "when": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Form.When" + } + }, + "type": { + "type": "string", + "enum": [ + "boolean" + ] + }, + "default": { + "type": "boolean" + } + }, + "required": [ + "key", + "type" + ], + "additionalProperties": false + }, + "Form.MultiselectField": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "required": { + "type": "boolean" + }, + "when": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Form.When" + } + }, + "type": { + "type": "string", + "enum": [ + "multiselect" + ] + }, + "options": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Form.Option" + } + }, + "minItems": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "maxItems": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "custom": { + "type": "boolean" + }, + "default": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "key", + "type", + "options" + ], + "additionalProperties": false + }, + "Form.FormInfo": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^frm_" + } + ] + }, + "sessionID": { + "type": "string" + }, + "title": { + "type": "string" + }, + "metadata": { + "$ref": "#/components/schemas/Form.Metadata" + }, + "mode": { + "type": "string", + "enum": [ + "form" + ] + }, + "fields": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/Form.StringField" + }, + { + "$ref": "#/components/schemas/Form.NumberField" + }, + { + "$ref": "#/components/schemas/Form.IntegerField" + }, + { + "$ref": "#/components/schemas/Form.BooleanField" + }, + { + "$ref": "#/components/schemas/Form.MultiselectField" + } + ] + } + } + }, + "required": [ + "id", + "sessionID", + "title", + "mode", + "fields" + ], + "additionalProperties": false + }, + "Form.UrlInfo": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^frm_" + } + ] + }, + "sessionID": { + "type": "string" + }, + "title": { + "type": "string" + }, + "metadata": { + "$ref": "#/components/schemas/Form.Metadata" + }, + "mode": { + "type": "string", + "enum": [ + "url" + ] + }, + "url": { + "type": "string" + } + }, + "required": [ + "id", + "sessionID", + "title", + "mode", + "url" + ], + "additionalProperties": false + }, + "Form.CreatePayload": { + "type": "object", + "properties": { + "id": { + "anyOf": [ + { + "type": "string", + "allOf": [ + { + "pattern": "^frm_" + } + ] + }, + { + "type": "null" + } + ] + }, + "title": { + "type": "string" + }, + "metadata": { + "$ref": "#/components/schemas/Form.Metadata" + }, + "mode": { + "type": "string", + "enum": [ + "form", + "url" + ] + }, + "fields": { + "anyOf": [ + { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/Form.StringField" + }, + { + "$ref": "#/components/schemas/Form.NumberField" + }, + { + "$ref": "#/components/schemas/Form.IntegerField" + }, + { + "$ref": "#/components/schemas/Form.BooleanField" + }, + { + "$ref": "#/components/schemas/Form.MultiselectField" + } + ] + } + }, + { + "type": "null" + } + ] + }, + "url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "title", + "mode" + ], + "additionalProperties": false + }, + "FormNotFoundError": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "FormNotFoundError" + ] + }, + "id": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": [ + "_tag", + "id", + "message" + ], + "additionalProperties": false + }, + "Form.Value": { + "anyOf": [ + { + "type": "string" + }, + { + "anyOf": [ + { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "NaN" + ] + }, + { + "type": "string", + "enum": [ + "Infinity" + ] + }, + { + "type": "string", + "enum": [ + "-Infinity" + ] + } + ] + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + { + "type": "boolean" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "Form.Answer": { + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/Form.Value" + } + }, + "Form.State": { + "anyOf": [ + { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "pending" + ] + } + }, + "required": [ + "status" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "answered" + ] + }, + "answer": { + "$ref": "#/components/schemas/Form.Answer" + } + }, + "required": [ + "status", + "answer" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "cancelled" + ] + } + }, + "required": [ + "status" + ], + "additionalProperties": false + } + ] + }, + "Form.Reply": { + "type": "object", + "properties": { + "answer": { + "$ref": "#/components/schemas/Form.Answer" + } + }, + "required": [ + "answer" + ], + "additionalProperties": false + }, + "FormAlreadySettledError": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "FormAlreadySettledError" + ] + }, + "id": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": [ + "_tag", + "id", + "message" + ], + "additionalProperties": false + }, + "FormInvalidAnswerError": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "FormInvalidAnswerError" + ] + }, + "id": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": [ + "_tag", + "id", + "message" + ], + "additionalProperties": false + }, + "PermissionV2.Source": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "tool" + ] + }, + "messageID": { + "type": "string" + }, + "callID": { + "type": "string" + } + }, + "required": [ + "type", + "messageID", + "callID" + ], + "additionalProperties": false + } + ] + }, + "PermissionV2.Request": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^per" + } + ] + }, + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "action": { + "type": "string" + }, + "resources": { + "type": "array", + "items": { + "type": "string" + } + }, + "save": { + "type": "array", + "items": { + "type": "string" + } + }, + "metadata": { + "type": "object" + }, + "source": { + "$ref": "#/components/schemas/PermissionV2.Source" + } + }, + "required": [ + "id", + "sessionID", + "action", + "resources" + ], + "additionalProperties": false + }, + "PermissionSaved.Info": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "projectID": { + "type": "string" + }, + "action": { + "type": "string" + }, + "resource": { + "type": "string" + } + }, + "required": [ + "id", + "projectID", + "action", + "resource" + ], + "additionalProperties": false + }, + "PermissionNotFoundError": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "PermissionNotFoundError" + ] + }, + "requestID": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": [ + "_tag", + "requestID", + "message" + ], + "additionalProperties": false + }, + "PermissionV2.Reply": { + "type": "string", + "enum": [ + "once", + "always", + "reject" + ] + }, + "FileSystem.Entry": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "file", + "directory" + ] + } + }, + "required": [ + "path", + "type" + ], + "additionalProperties": false + }, + "Command.Info": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "template": { + "type": "string" + }, + "description": { + "type": "string" + }, + "agent": { + "type": "string" + }, + "model": { + "$ref": "#/components/schemas/Model.Ref" + }, + "subtask": { + "type": "boolean" + } + }, + "required": [ + "name", + "template" + ], + "additionalProperties": false + }, + "Skill.Info": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "slash": { + "type": "boolean" + }, + "autoinvoke": { + "type": "boolean" + }, + "location": { + "type": "string" + }, + "content": { + "type": "string" + } + }, + "required": [ + "id", + "name", + "location", + "content" + ], + "additionalProperties": false + }, + "models-dev.refreshed": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "models-dev.refreshed" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "array" + } + ] + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "integration.updated": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "integration.updated" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "array" + } + ] + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "integration.connection.updated": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "integration.connection.updated" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "integrationID": { + "type": "string" + } + }, + "required": [ + "integrationID" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "catalog.updated": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "catalog.updated" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "array" + } + ] + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "agent.updated": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "agent.updated" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "array" + } + ] + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "FileDiff.LegacyInfo": { + "type": "object", + "properties": { + "file": { + "type": "string" + }, + "patch": { + "type": "string" + }, + "additions": { + "type": "number" + }, + "deletions": { + "type": "number" + }, + "status": { + "type": "string", + "enum": [ + "added", + "deleted", + "modified" + ] + } + }, + "required": [ + "additions", + "deletions" + ], + "additionalProperties": false + }, + "PermissionAction": { + "type": "string", + "enum": [ + "allow", + "deny", + "ask" + ] + }, + "PermissionRule": { + "type": "object", + "properties": { + "permission": { + "type": "string" + }, + "pattern": { + "type": "string" + }, + "action": { + "$ref": "#/components/schemas/PermissionAction" + } + }, + "required": [ + "permission", + "pattern", + "action" + ], + "additionalProperties": false + }, + "PermissionRuleset": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PermissionRule" + } + }, + "SessionV1.Info": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "slug": { + "type": "string" + }, + "projectID": { + "type": "string" + }, + "workspaceID": { + "type": "string", + "allOf": [ + { + "pattern": "^wrk" + } + ] + }, + "directory": { + "type": "string" + }, + "path": { + "type": "string" + }, + "parentID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "summary": { + "type": "object", + "properties": { + "additions": { + "type": "number" + }, + "deletions": { + "type": "number" + }, + "files": { + "type": "number" + }, + "diffs": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FileDiff.LegacyInfo" + } + } + }, + "required": [ + "additions", + "deletions", + "files" + ], + "additionalProperties": false + }, + "cost": { + "type": "number" + }, + "tokens": { + "type": "object", + "properties": { + "input": { + "type": "number" + }, + "output": { + "type": "number" + }, + "reasoning": { + "type": "number" + }, + "cache": { + "type": "object", + "properties": { + "read": { + "type": "number" + }, + "write": { + "type": "number" + } + }, + "required": [ + "read", + "write" + ], + "additionalProperties": false + } + }, + "required": [ + "input", + "output", + "reasoning", + "cache" + ], + "additionalProperties": false + }, + "share": { + "type": "object", + "properties": { + "url": { + "type": "string" + } + }, + "required": [ + "url" + ], + "additionalProperties": false + }, + "title": { + "type": "string" + }, + "agent": { + "type": "string" + }, + "model": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "providerID": { + "type": "string" + }, + "variant": { + "type": "string" + } + }, + "required": [ + "id", + "providerID" + ], + "additionalProperties": false + }, + "version": { + "type": "string" + }, + "metadata": { + "type": "object" + }, + "time": { + "type": "object", + "properties": { + "created": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "updated": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "compacting": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "archived": { + "type": "number" + } + }, + "required": [ + "created", + "updated" + ], + "additionalProperties": false + }, + "permission": { + "$ref": "#/components/schemas/PermissionRuleset" + }, + "revert": { + "type": "object", + "properties": { + "messageID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg" + } + ] + }, + "partID": { + "type": "string", + "allOf": [ + { + "pattern": "^prt" + } + ] + }, + "snapshot": { + "type": "string" + }, + "diff": { + "type": "string" + } + }, + "required": [ + "messageID" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "slug", + "projectID", + "directory", + "title", + "version", + "time" + ], + "additionalProperties": false + }, + "session.created": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.created" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "info": { + "$ref": "#/components/schemas/SessionV1.Info" + } + }, + "required": [ + "sessionID", + "info" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "session.updated": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.updated" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "info": { + "$ref": "#/components/schemas/SessionV1.Info" + } + }, + "required": [ + "sessionID", + "info" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "session.deleted1": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.deleted" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "info": { + "$ref": "#/components/schemas/SessionV1.Info" + } + }, + "required": [ + "sessionID", + "info" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "JSONSchema": { + "type": "object" + }, + "OutputFormat": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "text" + ] + } + }, + "required": [ + "type" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "json_schema" + ] + }, + "schema": { + "$ref": "#/components/schemas/JSONSchema" + }, + "retryCount": { + "anyOf": [ + { + "anyOf": [ + { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + { + "type": "null" + } + ] + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "type", + "schema" + ], + "additionalProperties": false + } + ] + }, + "UserMessage": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^msg" + } + ] + }, + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "role": { + "type": "string", + "enum": [ + "user" + ] + }, + "time": { + "type": "object", + "properties": { + "created": { + "type": "number", + "allOf": [ + { + "minimum": 0 + } + ] + } + }, + "required": [ + "created" + ], + "additionalProperties": false + }, + "format": { + "anyOf": [ + { + "$ref": "#/components/schemas/OutputFormat" + }, + { + "type": "null" + } + ] + }, + "summary": { + "anyOf": [ + { + "type": "object", + "properties": { + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "body": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "diffs": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FileDiff.LegacyInfo" + } + } + }, + "required": [ + "diffs" + ], + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "agent": { + "type": "string" + }, + "model": { + "type": "object", + "properties": { + "providerID": { + "type": "string" + }, + "modelID": { + "type": "string" + }, + "variant": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "providerID", + "modelID" + ], + "additionalProperties": false + }, + "system": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "tools": { + "anyOf": [ + { + "type": "object", + "additionalProperties": { + "type": "boolean" + } + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "sessionID", + "role", + "time", + "agent", + "model" + ], + "additionalProperties": false + }, + "ProviderAuthError": { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "ProviderAuthError" + ] + }, + "data": { + "type": "object", + "properties": { + "providerID": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": [ + "providerID", + "message" + ], + "additionalProperties": false + } + }, + "required": [ + "name", + "data" + ], + "additionalProperties": false + }, + "UnknownError1": { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "UnknownError" + ] + }, + "data": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "ref": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "message" + ], + "additionalProperties": false + } + }, + "required": [ + "name", + "data" + ], + "additionalProperties": false + }, + "MessageOutputLengthError": { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "MessageOutputLengthError" + ] + }, + "data": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "array" + } + ] + } + }, + "required": [ + "name", + "data" + ], + "additionalProperties": false + }, + "MessageAbortedError": { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "MessageAbortedError" + ] + }, + "data": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + }, + "required": [ + "message" + ], + "additionalProperties": false + } + }, + "required": [ + "name", + "data" + ], + "additionalProperties": false + }, + "StructuredOutputError": { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "StructuredOutputError" + ] + }, + "data": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "retries": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + } + }, + "required": [ + "message", + "retries" + ], + "additionalProperties": false + } + }, + "required": [ + "name", + "data" + ], + "additionalProperties": false + }, + "ContextOverflowError": { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "ContextOverflowError" + ] + }, + "data": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "responseBody": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "message" + ], + "additionalProperties": false + } + }, + "required": [ + "name", + "data" + ], + "additionalProperties": false + }, + "ContentFilterError": { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "ContentFilterError" + ] + }, + "data": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + }, + "required": [ + "message" + ], + "additionalProperties": false + } + }, + "required": [ + "name", + "data" + ], + "additionalProperties": false + }, + "APIError": { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "APIError" + ] + }, + "data": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "statusCode": { + "anyOf": [ + { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + { + "type": "null" + } + ] + }, + "isRetryable": { + "type": "boolean" + }, + "responseHeaders": { + "anyOf": [ + { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "responseBody": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "metadata": { + "anyOf": [ + { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "message", + "isRetryable" + ], + "additionalProperties": false + } + }, + "required": [ + "name", + "data" + ], + "additionalProperties": false + }, + "AssistantMessage": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^msg" + } + ] + }, + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "role": { + "type": "string", + "enum": [ + "assistant" + ] + }, + "time": { + "type": "object", + "properties": { + "created": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "completed": { + "anyOf": [ + { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "created" + ], + "additionalProperties": false + }, + "error": { + "anyOf": [ + { + "anyOf": [ + { + "$ref": "#/components/schemas/ProviderAuthError" + }, + { + "$ref": "#/components/schemas/UnknownError1" + }, + { + "$ref": "#/components/schemas/MessageOutputLengthError" + }, + { + "$ref": "#/components/schemas/MessageAbortedError" + }, + { + "$ref": "#/components/schemas/StructuredOutputError" + }, + { + "$ref": "#/components/schemas/ContextOverflowError" + }, + { + "$ref": "#/components/schemas/ContentFilterError" + }, + { + "$ref": "#/components/schemas/APIError" + } + ] + }, + { + "type": "null" + } + ] + }, + "parentID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg" + } + ] + }, + "modelID": { + "type": "string" + }, + "providerID": { + "type": "string" + }, + "mode": { + "type": "string" + }, + "agent": { + "type": "string" + }, + "path": { + "type": "object", + "properties": { + "cwd": { + "type": "string" + }, + "root": { + "type": "string" + } + }, + "required": [ + "cwd", + "root" + ], + "additionalProperties": false + }, + "summary": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "cost": { + "type": "number" + }, + "tokens": { + "type": "object", + "properties": { + "total": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "input": { + "type": "number" + }, + "output": { + "type": "number" + }, + "reasoning": { + "type": "number" + }, + "cache": { + "type": "object", + "properties": { + "read": { + "type": "number" + }, + "write": { + "type": "number" + } + }, + "required": [ + "read", + "write" + ], + "additionalProperties": false + } + }, + "required": [ + "input", + "output", + "reasoning", + "cache" + ], + "additionalProperties": false + }, + "structured": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + }, + "variant": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "finish": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "sessionID", + "role", + "time", + "parentID", + "modelID", + "providerID", + "mode", + "agent", + "path", + "cost", + "tokens" + ], + "additionalProperties": false + }, + "Message": { + "anyOf": [ + { + "$ref": "#/components/schemas/UserMessage" + }, + { + "$ref": "#/components/schemas/AssistantMessage" + } + ] + }, + "message.updated": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "message.updated" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "info": { + "$ref": "#/components/schemas/Message" + } + }, + "required": [ + "sessionID", + "info" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "message.removed": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "message.removed" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "messageID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg" + } + ] + } + }, + "required": [ + "sessionID", + "messageID" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "TextPart": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^prt" + } + ] + }, + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "messageID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "text" + ] + }, + "text": { + "type": "string" + }, + "synthetic": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "ignored": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "time": { + "anyOf": [ + { + "type": "object", + "properties": { + "start": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "end": { + "anyOf": [ + { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "start" + ], + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "metadata": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "sessionID", + "messageID", + "type", + "text" + ], + "additionalProperties": false + }, + "SubtaskPart": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^prt" + } + ] + }, + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "messageID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "subtask" + ] + }, + "prompt": { + "type": "string" + }, + "description": { + "type": "string" + }, + "agent": { + "type": "string" + }, + "model": { + "anyOf": [ + { + "type": "object", + "properties": { + "providerID": { + "type": "string" + }, + "modelID": { + "type": "string" + } + }, + "required": [ + "providerID", + "modelID" + ], + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "command": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "sessionID", + "messageID", + "type", + "prompt", + "description", + "agent" + ], + "additionalProperties": false + }, + "ReasoningPart": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^prt" + } + ] + }, + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "messageID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "reasoning" + ] + }, + "text": { + "type": "string" + }, + "metadata": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "null" + } + ] + }, + "time": { + "type": "object", + "properties": { + "start": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "end": { + "anyOf": [ + { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "start" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "sessionID", + "messageID", + "type", + "text", + "time" + ], + "additionalProperties": false + }, + "FilePartSourceText": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "start": { + "type": "number" + }, + "end": { + "type": "number" + } + }, + "required": [ + "value", + "start", + "end" + ], + "additionalProperties": false + }, + "FileSource": { + "type": "object", + "properties": { + "text": { + "$ref": "#/components/schemas/FilePartSourceText" + }, + "type": { + "type": "string", + "enum": [ + "file" + ] + }, + "path": { + "type": "string" + } + }, + "required": [ + "text", + "type", + "path" + ], + "additionalProperties": false + }, + "Range": { + "type": "object", + "properties": { + "start": { + "type": "object", + "properties": { + "line": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "character": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + } + }, + "required": [ + "line", + "character" + ], + "additionalProperties": false + }, + "end": { + "type": "object", + "properties": { + "line": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "character": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + } + }, + "required": [ + "line", + "character" + ], + "additionalProperties": false + } + }, + "required": [ + "start", + "end" + ], + "additionalProperties": false + }, + "SymbolSource": { + "type": "object", + "properties": { + "text": { + "$ref": "#/components/schemas/FilePartSourceText" + }, + "type": { + "type": "string", + "enum": [ + "symbol" + ] + }, + "path": { + "type": "string" + }, + "range": { + "$ref": "#/components/schemas/Range" + }, + "name": { + "type": "string" + }, + "kind": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + } + }, + "required": [ + "text", + "type", + "path", + "range", + "name", + "kind" + ], + "additionalProperties": false + }, + "ResourceSource": { + "type": "object", + "properties": { + "text": { + "$ref": "#/components/schemas/FilePartSourceText" + }, + "type": { + "type": "string", + "enum": [ + "resource" + ] + }, + "clientName": { + "type": "string" + }, + "uri": { + "type": "string" + } + }, + "required": [ + "text", + "type", + "clientName", + "uri" + ], + "additionalProperties": false + }, + "FilePartSource": { + "anyOf": [ + { + "$ref": "#/components/schemas/FileSource" + }, + { + "$ref": "#/components/schemas/SymbolSource" + }, + { + "$ref": "#/components/schemas/ResourceSource" + } + ] + }, + "FilePart": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^prt" + } + ] + }, + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "messageID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "file" + ] + }, + "mime": { + "type": "string" + }, + "filename": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "url": { + "type": "string" + }, + "source": { + "anyOf": [ + { + "$ref": "#/components/schemas/FilePartSource" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "sessionID", + "messageID", + "type", + "mime", + "url" + ], + "additionalProperties": false + }, + "ToolStatePending": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "pending" + ] + }, + "input": { + "type": "object" + }, + "raw": { + "type": "string" + } + }, + "required": [ + "status", + "input", + "raw" + ], + "additionalProperties": false + }, + "ToolStateRunning": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "running" + ] + }, + "input": { + "type": "object" + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "metadata": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "null" + } + ] + }, + "time": { + "type": "object", + "properties": { + "start": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + } + }, + "required": [ + "start" + ], + "additionalProperties": false + } + }, + "required": [ + "status", + "input", + "time" + ], + "additionalProperties": false + }, + "ToolStateCompleted": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "completed" + ] + }, + "input": { + "type": "object" + }, + "output": { + "type": "string" + }, + "title": { + "type": "string" + }, + "metadata": { + "type": "object" + }, + "time": { + "type": "object", + "properties": { + "start": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "end": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "compacted": { + "anyOf": [ + { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "start", + "end" + ], + "additionalProperties": false + }, + "attachments": { + "anyOf": [ + { + "type": "array", + "items": { + "$ref": "#/components/schemas/FilePart" + } + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "status", + "input", + "output", + "title", + "metadata", + "time" + ], + "additionalProperties": false + }, + "ToolStateError": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "error" + ] + }, + "input": { + "type": "object" + }, + "error": { + "type": "string" + }, + "metadata": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "null" + } + ] + }, + "time": { + "type": "object", + "properties": { + "start": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "end": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + } + }, + "required": [ + "start", + "end" + ], + "additionalProperties": false + } + }, + "required": [ + "status", + "input", + "error", + "time" + ], + "additionalProperties": false + }, + "ToolState": { + "anyOf": [ + { + "$ref": "#/components/schemas/ToolStatePending" + }, + { + "$ref": "#/components/schemas/ToolStateRunning" + }, + { + "$ref": "#/components/schemas/ToolStateCompleted" + }, + { + "$ref": "#/components/schemas/ToolStateError" + } + ] + }, + "ToolPart": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^prt" + } + ] + }, + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "messageID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "tool" + ] + }, + "callID": { + "type": "string" + }, + "tool": { + "type": "string" + }, + "state": { + "$ref": "#/components/schemas/ToolState" + }, + "metadata": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "sessionID", + "messageID", + "type", + "callID", + "tool", + "state" + ], + "additionalProperties": false + }, + "StepStartPart": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^prt" + } + ] + }, + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "messageID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "step-start" + ] + }, + "snapshot": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "sessionID", + "messageID", + "type" + ], + "additionalProperties": false + }, + "StepFinishPart": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^prt" + } + ] + }, + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "messageID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "step-finish" + ] + }, + "reason": { + "type": "string" + }, + "snapshot": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "cost": { + "type": "number" + }, + "tokens": { + "type": "object", + "properties": { + "total": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "input": { + "type": "number" + }, + "output": { + "type": "number" + }, + "reasoning": { + "type": "number" + }, + "cache": { + "type": "object", + "properties": { + "read": { + "type": "number" + }, + "write": { + "type": "number" + } + }, + "required": [ + "read", + "write" + ], + "additionalProperties": false + } + }, + "required": [ + "input", + "output", + "reasoning", + "cache" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "sessionID", + "messageID", + "type", + "reason", + "cost", + "tokens" + ], + "additionalProperties": false + }, + "SnapshotPart": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^prt" + } + ] + }, + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "messageID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "snapshot" + ] + }, + "snapshot": { + "type": "string" + } + }, + "required": [ + "id", + "sessionID", + "messageID", + "type", + "snapshot" + ], + "additionalProperties": false + }, + "PatchPart": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^prt" + } + ] + }, + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "messageID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "patch" + ] + }, + "hash": { + "type": "string" + }, + "files": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "id", + "sessionID", + "messageID", + "type", + "hash", + "files" + ], + "additionalProperties": false + }, + "AgentPart": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^prt" + } + ] + }, + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "messageID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "agent" + ] + }, + "name": { + "type": "string" + }, + "source": { + "anyOf": [ + { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "start": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "end": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + } + }, + "required": [ + "value", + "start", + "end" + ], + "additionalProperties": false + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "sessionID", + "messageID", + "type", + "name" + ], + "additionalProperties": false + }, + "RetryPart": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^prt" + } + ] + }, + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "messageID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "retry" + ] + }, + "attempt": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "error": { + "$ref": "#/components/schemas/APIError" + }, + "time": { + "type": "object", + "properties": { + "created": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + } + }, + "required": [ + "created" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "sessionID", + "messageID", + "type", + "attempt", + "error", + "time" + ], + "additionalProperties": false + }, + "CompactionPart": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^prt" + } + ] + }, + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "messageID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "compaction" + ] + }, + "auto": { + "type": "boolean" + }, + "overflow": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "tail_start_id": { + "anyOf": [ + { + "type": "string", + "allOf": [ + { + "pattern": "^msg" + } + ] + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "sessionID", + "messageID", + "type", + "auto" + ], + "additionalProperties": false + }, + "Part": { + "anyOf": [ + { + "$ref": "#/components/schemas/TextPart" + }, + { + "$ref": "#/components/schemas/SubtaskPart" + }, + { + "$ref": "#/components/schemas/ReasoningPart" + }, + { + "$ref": "#/components/schemas/FilePart" + }, + { + "$ref": "#/components/schemas/ToolPart" + }, + { + "$ref": "#/components/schemas/StepStartPart" + }, + { + "$ref": "#/components/schemas/StepFinishPart" + }, + { + "$ref": "#/components/schemas/SnapshotPart" + }, + { + "$ref": "#/components/schemas/PatchPart" + }, + { + "$ref": "#/components/schemas/AgentPart" + }, + { + "$ref": "#/components/schemas/RetryPart" + }, + { + "$ref": "#/components/schemas/CompactionPart" + } + ] + }, + "message.part.updated": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "message.part.updated" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "part": { + "$ref": "#/components/schemas/Part" + }, + "time": { + "type": "number" + } + }, + "required": [ + "sessionID", + "part", + "time" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "message.part.removed": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "message.part.removed" + ] + }, + "durable": { + "type": "object", + "properties": { + "aggregateID": { + "type": "string" + }, + "seq": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "version": { + "type": "number", + "enum": [ + 1 + ] + } + }, + "required": [ + "aggregateID", + "seq", + "version" + ], + "additionalProperties": false + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "messageID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg" + } + ] + }, + "partID": { + "type": "string", + "allOf": [ + { + "pattern": "^prt" + } + ] + } + }, + "required": [ + "sessionID", + "messageID", + "partID" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "durable", + "data" + ], + "additionalProperties": false + }, + "session.usage.updated": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.usage.updated" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "cost": { + "$ref": "#/components/schemas/Money.USD" + }, + "tokens": { + "$ref": "#/components/schemas/TokenUsage.Info" + } + }, + "required": [ + "sessionID", + "cost", + "tokens" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "session.text.delta": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.text.delta" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "assistantMessageID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + "ordinal": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "delta": { + "type": "string" + } + }, + "required": [ + "sessionID", + "assistantMessageID", + "ordinal", + "delta" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "session.reasoning.delta": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.reasoning.delta" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "assistantMessageID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + "ordinal": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "delta": { + "type": "string" + } + }, + "required": [ + "sessionID", + "assistantMessageID", + "ordinal", + "delta" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "session.tool.input.delta": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.tool.input.delta" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "assistantMessageID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg_" + } + ] + }, + "callID": { + "type": "string" + }, + "delta": { + "type": "string" + } + }, + "required": [ + "sessionID", + "assistantMessageID", + "callID", + "delta" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "session.compaction.delta": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.compaction.delta" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "text": { + "type": "string" + } + }, + "required": [ + "sessionID", + "text" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "filesystem.changed": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "filesystem.changed" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "file": { + "type": "string" + }, + "event": { + "type": "string", + "enum": [ + "add", + "change", + "unlink" + ] + } + }, + "required": [ + "file", + "event" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "reference.updated": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "reference.updated" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "array" + } + ] + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "permission.v2.asked": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "permission.v2.asked" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^per" + } + ] + }, + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "action": { + "type": "string" + }, + "resources": { + "type": "array", + "items": { + "type": "string" + } + }, + "save": { + "type": "array", + "items": { + "type": "string" + } + }, + "metadata": { + "type": "object" + }, + "source": { + "$ref": "#/components/schemas/PermissionV2.Source" + } + }, + "required": [ + "id", + "sessionID", + "action", + "resources" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "permission.v2.replied": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "permission.v2.replied" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "requestID": { + "type": "string", + "allOf": [ + { + "pattern": "^per" + } + ] + }, + "reply": { + "$ref": "#/components/schemas/PermissionV2.Reply" + } + }, + "required": [ + "sessionID", + "requestID", + "reply" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "plugin.added": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "plugin.added" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "plugin.updated": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "plugin.updated" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "array" + } + ] + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "project.directories.updated": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "project.directories.updated" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "projectID": { + "type": "string" + } + }, + "required": [ + "projectID" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "command.updated": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "command.updated" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "array" + } + ] + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "config.updated": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "config.updated" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "array" + } + ] + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "skill.updated": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "skill.updated" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "array" + } + ] + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "Pty": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^pty" + } + ] + }, + "title": { + "type": "string" + }, + "command": { + "type": "string" + }, + "args": { + "type": "array", + "items": { + "type": "string" + } + }, + "cwd": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "running", + "exited" + ] + }, + "pid": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "exitCode": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + } + }, + "required": [ + "id", + "title", + "command", + "args", + "cwd", + "status", + "pid" + ], + "additionalProperties": false + }, + "pty.created": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "pty.created" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "info": { + "$ref": "#/components/schemas/Pty" + } + }, + "required": [ + "info" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "pty.updated": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "pty.updated" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "info": { + "$ref": "#/components/schemas/Pty" + } + }, + "required": [ + "info" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "pty.exited": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "pty.exited" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^pty" + } + ] + }, + "exitCode": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + } + }, + "required": [ + "id", + "exitCode" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "pty.deleted": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "pty.deleted" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^pty" + } + ] + } + }, + "required": [ + "id" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "shell.created": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "shell.created" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "info": { + "$ref": "#/components/schemas/Shell" + } + }, + "required": [ + "info" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "shell.exited": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "shell.exited" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^sh_" + } + ] + }, + "exit": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "NaN" + ] + }, + { + "type": "string", + "enum": [ + "Infinity" + ] + }, + { + "type": "string", + "enum": [ + "-Infinity" + ] + } + ] + }, + "status": { + "type": "string", + "enum": [ + "running", + "exited", + "timeout", + "killed" + ] + } + }, + "required": [ + "id", + "status" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "shell.deleted": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "shell.deleted" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^sh_" + } + ] + } + }, + "required": [ + "id" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "QuestionV2.Option": { + "type": "object", + "properties": { + "label": { + "type": "string", + "description": "Display text (1-5 words, concise)" + }, + "description": { + "type": "string", + "description": "Explanation of choice" + } + }, + "required": [ + "label", + "description" + ], + "additionalProperties": false + }, + "QuestionV2.Info": { + "type": "object", + "properties": { + "question": { + "type": "string", + "description": "Complete question" + }, + "header": { + "type": "string", + "description": "Very short label (max 30 chars)" + }, + "options": { + "type": "array", + "items": { + "$ref": "#/components/schemas/QuestionV2.Option" + }, + "description": "Available choices" + }, + "multiple": { + "type": "boolean" + }, + "custom": { + "type": "boolean" + } + }, + "required": [ + "question", + "header", + "options" + ], + "additionalProperties": false + }, + "QuestionV2.Tool": { + "type": "object", + "properties": { + "messageID": { + "type": "string" + }, + "callID": { + "type": "string" + } + }, + "required": [ + "messageID", + "callID" + ], + "additionalProperties": false + }, + "question.v2.asked": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "question.v2.asked" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^que" + } + ] + }, + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "questions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/QuestionV2.Info" + }, + "description": "Questions to ask" + }, + "tool": { + "$ref": "#/components/schemas/QuestionV2.Tool" + } + }, + "required": [ + "id", + "sessionID", + "questions" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "QuestionV2.Answer": { + "type": "array", + "items": { + "type": "string" + } + }, + "question.v2.replied": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "question.v2.replied" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "requestID": { + "type": "string", + "allOf": [ + { + "pattern": "^que" + } + ] + }, + "answers": { + "type": "array", + "items": { + "$ref": "#/components/schemas/QuestionV2.Answer" + } + } + }, + "required": [ + "sessionID", + "requestID", + "answers" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "question.v2.rejected": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "question.v2.rejected" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "requestID": { + "type": "string", + "allOf": [ + { + "pattern": "^que" + } + ] + } + }, + "required": [ + "sessionID", + "requestID" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "Form.Metadata1": { + "type": "object" + }, + "Form.When1": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "op": { + "type": "string", + "enum": [ + "eq", + "neq" + ] + }, + "value": { + "anyOf": [ + { + "type": "string" + }, + { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "NaN" + ] + }, + { + "type": "string", + "enum": [ + "Infinity" + ] + }, + { + "type": "string", + "enum": [ + "-Infinity" + ] + } + ] + }, + { + "type": "boolean" + } + ] + } + }, + "required": [ + "key", + "op", + "value" + ], + "additionalProperties": false + }, + "Form.StringField1": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "required": { + "type": "boolean" + }, + "when": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Form.When1" + } + }, + "type": { + "type": "string", + "enum": [ + "string" + ] + }, + "format": { + "type": "string", + "enum": [ + "email", + "uri", + "date", + "date-time" + ] + }, + "minLength": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "maxLength": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "pattern": { + "type": "string" + }, + "placeholder": { + "type": "string" + }, + "default": { + "type": "string" + }, + "options": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Form.Option" + } + }, + "custom": { + "type": "boolean" + } + }, + "required": [ + "key", + "type" + ], + "additionalProperties": false + }, + "Form.NumberField1": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "required": { + "type": "boolean" + }, + "when": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Form.When1" + } + }, + "type": { + "type": "string", + "enum": [ + "number" + ] + }, + "minimum": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "NaN" + ] + }, + { + "type": "string", + "enum": [ + "Infinity" + ] + }, + { + "type": "string", + "enum": [ + "-Infinity" + ] + } + ] + }, + "maximum": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "NaN" + ] + }, + { + "type": "string", + "enum": [ + "Infinity" + ] + }, + { + "type": "string", + "enum": [ + "-Infinity" + ] + } + ] + }, + "default": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "NaN" + ] + }, + { + "type": "string", + "enum": [ + "Infinity" + ] + }, + { + "type": "string", + "enum": [ + "-Infinity" + ] + } + ] + } + }, + "required": [ + "key", + "type" + ], + "additionalProperties": false + }, + "Form.IntegerField1": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "required": { + "type": "boolean" + }, + "when": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Form.When1" + } + }, + "type": { + "type": "string", + "enum": [ + "integer" + ] + }, + "minimum": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "NaN" + ] + }, + { + "type": "string", + "enum": [ + "Infinity" + ] + }, + { + "type": "string", + "enum": [ + "-Infinity" + ] + } + ] + }, + "maximum": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "NaN" + ] + }, + { + "type": "string", + "enum": [ + "Infinity" + ] + }, + { + "type": "string", + "enum": [ + "-Infinity" + ] + } + ] + }, + "default": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "NaN" + ] + }, + { + "type": "string", + "enum": [ + "Infinity" + ] + }, + { + "type": "string", + "enum": [ + "-Infinity" + ] + } + ] + } + }, + "required": [ + "key", + "type" + ], + "additionalProperties": false + }, + "Form.BooleanField1": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "required": { + "type": "boolean" + }, + "when": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Form.When1" + } + }, + "type": { + "type": "string", + "enum": [ + "boolean" + ] + }, + "default": { + "type": "boolean" + } + }, + "required": [ + "key", + "type" + ], + "additionalProperties": false + }, + "Form.MultiselectField1": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "required": { + "type": "boolean" + }, + "when": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Form.When1" + } + }, + "type": { + "type": "string", + "enum": [ + "multiselect" + ] + }, + "options": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Form.Option" + } + }, + "minItems": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "maxItems": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "custom": { + "type": "boolean" + }, + "default": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "key", + "type", + "options" + ], + "additionalProperties": false + }, + "Form.FormInfo1": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^frm_" + } + ] + }, + "sessionID": { + "type": "string" + }, + "title": { + "type": "string" + }, + "metadata": { + "$ref": "#/components/schemas/Form.Metadata1" + }, + "mode": { + "type": "string", + "enum": [ + "form" + ] + }, + "fields": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/Form.StringField1" + }, + { + "$ref": "#/components/schemas/Form.NumberField1" + }, + { + "$ref": "#/components/schemas/Form.IntegerField1" + }, + { + "$ref": "#/components/schemas/Form.BooleanField1" + }, + { + "$ref": "#/components/schemas/Form.MultiselectField1" + } + ] + } + } + }, + "required": [ + "id", + "sessionID", + "title", + "mode", + "fields" + ], + "additionalProperties": false + }, + "Form.UrlInfo1": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^frm_" + } + ] + }, + "sessionID": { + "type": "string" + }, + "title": { + "type": "string" + }, + "metadata": { + "$ref": "#/components/schemas/Form.Metadata1" + }, + "mode": { + "type": "string", + "enum": [ + "url" + ] + }, + "url": { + "type": "string" + } + }, + "required": [ + "id", + "sessionID", + "title", + "mode", + "url" + ], + "additionalProperties": false + }, + "form.created": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "form.created" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "form": { + "anyOf": [ + { + "$ref": "#/components/schemas/Form.FormInfo1" + }, + { + "$ref": "#/components/schemas/Form.UrlInfo1" + } + ] + } + }, + "required": [ + "form" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "Form.Value1": { + "anyOf": [ + { + "type": "string" + }, + { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "NaN" + ] + }, + { + "type": "string", + "enum": [ + "Infinity" + ] + }, + { + "type": "string", + "enum": [ + "-Infinity" + ] + } + ] + }, + { + "type": "boolean" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "Form.Answer1": { + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/Form.Value1" + } + }, + "form.replied": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "form.replied" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^frm_" + } + ] + }, + "sessionID": { + "type": "string" + }, + "answer": { + "$ref": "#/components/schemas/Form.Answer1" + } + }, + "required": [ + "id", + "sessionID", + "answer" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "form.cancelled": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "form.cancelled" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^frm_" + } + ] + }, + "sessionID": { + "type": "string" + } + }, + "required": [ + "id", + "sessionID" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "SessionStatus": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "idle" + ] + } + }, + "required": [ + "type" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "retry" + ] + }, + "attempt": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "message": { + "type": "string" + }, + "action": { + "type": "object", + "properties": { + "reason": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "title": { + "type": "string" + }, + "message": { + "type": "string" + }, + "label": { + "type": "string" + }, + "link": { + "type": "string" + } + }, + "required": [ + "reason", + "provider", + "title", + "message", + "label" + ], + "additionalProperties": false + }, + "next": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + } + }, + "required": [ + "type", + "attempt", + "message", + "next" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "busy" + ] + } + }, + "required": [ + "type" + ], + "additionalProperties": false + } + ] + }, + "session.status": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.status" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "status": { + "$ref": "#/components/schemas/SessionStatus" + } + }, + "required": [ + "sessionID", + "status" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "session.idle": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.idle" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + } + }, + "required": [ + "sessionID" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "tui.prompt.append": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "tui.prompt.append" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "text": { + "type": "string" + } + }, + "required": [ + "text" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "tui.command.execute": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "tui.command.execute" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "command": { + "anyOf": [ + { + "type": "string", + "enum": [ + "session.list", + "session.new", + "session.share", + "session.interrupt", + "session.background", + "session.compact", + "session.page.up", + "session.page.down", + "session.line.up", + "session.line.down", + "session.half.page.up", + "session.half.page.down", + "session.first", + "session.last", + "prompt.clear", + "prompt.submit", + "agent.cycle" + ] + }, + { + "type": "string" + } + ] + } + }, + "required": [ + "command" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "tui.toast.show": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "tui.toast.show" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "message": { + "type": "string" + }, + "variant": { + "type": "string", + "enum": [ + "info", + "success", + "warning", + "error" + ] + }, + "duration": { + "anyOf": [ + { + "type": "integer", + "allOf": [ + { + "exclusiveMinimum": 0 + } + ] + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "message", + "variant" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "tui.session.select": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "tui.session.select" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses", + "description": "Session ID to navigate to" + } + ] + } + }, + "required": [ + "sessionID" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "installation.updated": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "installation.updated" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "version": { + "type": "string" + } + }, + "required": [ + "version" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "installation.update-available": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "installation.update-available" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "version": { + "type": "string" + } + }, + "required": [ + "version" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "vcs.branch.updated": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "vcs.branch.updated" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "branch": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "mcp.status.changed": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "mcp.status.changed" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "server": { + "type": "string" + } + }, + "required": [ + "server" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "mcp.resources.changed": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "mcp.resources.changed" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "server": { + "type": "string" + } + }, + "required": [ + "server" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "permission.asked": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "permission.asked" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^per" + } + ] + }, + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "permission": { + "type": "string" + }, + "patterns": { + "type": "array", + "items": { + "type": "string" + } + }, + "metadata": { + "type": "object" + }, + "always": { + "type": "array", + "items": { + "type": "string" + } + }, + "tool": { + "anyOf": [ + { + "type": "object", + "properties": { + "messageID": { + "type": "string" + }, + "callID": { + "type": "string" + } + }, + "required": [ + "messageID", + "callID" + ], + "additionalProperties": false + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "sessionID", + "permission", + "patterns", + "metadata", + "always" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "permission.replied": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "permission.replied" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "requestID": { + "type": "string", + "allOf": [ + { + "pattern": "^per" + } + ] + }, + "reply": { + "type": "string", + "enum": [ + "once", + "always", + "reject" + ] + } + }, + "required": [ + "sessionID", + "requestID", + "reply" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "QuestionOption": { + "type": "object", + "properties": { + "label": { + "type": "string", + "description": "Display text (1-5 words, concise)" + }, + "description": { + "type": "string", + "description": "Explanation of choice" + } + }, + "required": [ + "label", + "description" + ], + "additionalProperties": false + }, + "QuestionInfo": { + "type": "object", + "properties": { + "question": { + "type": "string", + "description": "Complete question" + }, + "header": { + "type": "string", + "description": "Very short label (max 30 chars)" + }, + "options": { + "type": "array", + "items": { + "$ref": "#/components/schemas/QuestionOption" + }, + "description": "Available choices" + }, + "multiple": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "description": "Allow selecting multiple choices" + }, + "custom": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "description": "Allow typing a custom answer (default: true)" + } + }, + "required": [ + "question", + "header", + "options" + ], + "additionalProperties": false + }, + "QuestionTool": { + "type": "object", + "properties": { + "messageID": { + "type": "string", + "allOf": [ + { + "pattern": "^msg" + } + ] + }, + "callID": { + "type": "string" + } + }, + "required": [ + "messageID", + "callID" + ], + "additionalProperties": false + }, + "question.asked": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "question.asked" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^que" + } + ] + }, + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "questions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/QuestionInfo" + }, + "description": "Questions to ask" + }, + "tool": { + "anyOf": [ + { + "$ref": "#/components/schemas/QuestionTool" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "sessionID", + "questions" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "QuestionAnswer": { + "type": "array", + "items": { + "type": "string" + } + }, + "question.replied": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "question.replied" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "requestID": { + "type": "string", + "allOf": [ + { + "pattern": "^que" + } + ] + }, + "answers": { + "type": "array", + "items": { + "$ref": "#/components/schemas/QuestionAnswer" + } + } + }, + "required": [ + "sessionID", + "requestID", + "answers" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "question.rejected": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "question.rejected" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "requestID": { + "type": "string", + "allOf": [ + { + "pattern": "^que" + } + ] + } + }, + "required": [ + "sessionID", + "requestID" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "session.error": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "created": { + "type": "number" + }, + "metadata": { + "type": "object" + }, + "type": { + "type": "string", + "enum": [ + "session.error" + ] + }, + "location": { + "$ref": "#/components/schemas/Location.Ref" + }, + "data": { + "type": "object", + "properties": { + "sessionID": { + "anyOf": [ + { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + { + "type": "null" + } + ] + }, + "error": { + "anyOf": [ + { + "anyOf": [ + { + "$ref": "#/components/schemas/ProviderAuthError" + }, + { + "$ref": "#/components/schemas/UnknownError1" + }, + { + "$ref": "#/components/schemas/MessageOutputLengthError" + }, + { + "$ref": "#/components/schemas/MessageAbortedError" + }, + { + "$ref": "#/components/schemas/StructuredOutputError" + }, + { + "$ref": "#/components/schemas/ContextOverflowError" + }, + { + "$ref": "#/components/schemas/ContentFilterError" + }, + { + "$ref": "#/components/schemas/APIError" + } + ] + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + }, + "required": [ + "id", + "created", + "type", + "data" + ], + "additionalProperties": false + }, + "V2Event.server.connected": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^evt_" + } + ] + }, + "metadata": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "null" + } + ] + }, + "location": { + "anyOf": [ + { + "$ref": "#/components/schemas/Location.Ref" + }, + { + "type": "null" + } + ] + }, + "type": { + "type": "string", + "enum": [ + "server.connected" + ] + }, + "data": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "array" + } + ] + } + }, + "required": [ + "id", + "type", + "data" + ], + "additionalProperties": false + }, + "V2Event": { + "anyOf": [ + { + "$ref": "#/components/schemas/models-dev.refreshed" + }, + { + "$ref": "#/components/schemas/integration.updated" + }, + { + "$ref": "#/components/schemas/integration.connection.updated" + }, + { + "$ref": "#/components/schemas/catalog.updated" + }, + { + "$ref": "#/components/schemas/agent.updated" + }, + { + "$ref": "#/components/schemas/session.created" + }, + { + "$ref": "#/components/schemas/session.updated" + }, + { + "$ref": "#/components/schemas/session.deleted1" + }, + { + "$ref": "#/components/schemas/message.updated" + }, + { + "$ref": "#/components/schemas/message.removed" + }, + { + "$ref": "#/components/schemas/message.part.updated" + }, + { + "$ref": "#/components/schemas/message.part.removed" + }, + { + "$ref": "#/components/schemas/session.agent.selected" + }, + { + "$ref": "#/components/schemas/session.model.selected" + }, + { + "$ref": "#/components/schemas/session.moved" + }, + { + "$ref": "#/components/schemas/session.renamed" + }, + { + "$ref": "#/components/schemas/session.usage.updated" + }, + { + "$ref": "#/components/schemas/session.deleted" + }, + { + "$ref": "#/components/schemas/session.forked" + }, + { + "$ref": "#/components/schemas/session.prompt.promoted" + }, + { + "$ref": "#/components/schemas/session.prompt.admitted" + }, + { + "$ref": "#/components/schemas/session.execution.started" + }, + { + "$ref": "#/components/schemas/session.execution.succeeded" + }, + { + "$ref": "#/components/schemas/session.execution.failed" + }, + { + "$ref": "#/components/schemas/session.execution.interrupted" + }, + { + "$ref": "#/components/schemas/session.instructions.updated" + }, + { + "$ref": "#/components/schemas/session.synthetic" + }, + { + "$ref": "#/components/schemas/session.skill.activated" + }, + { + "$ref": "#/components/schemas/session.shell.started" + }, + { + "$ref": "#/components/schemas/session.shell.ended" + }, + { + "$ref": "#/components/schemas/session.step.started" + }, + { + "$ref": "#/components/schemas/session.step.ended" + }, + { + "$ref": "#/components/schemas/session.step.failed" + }, + { + "$ref": "#/components/schemas/session.text.started" + }, + { + "$ref": "#/components/schemas/session.text.delta" + }, + { + "$ref": "#/components/schemas/session.text.ended" + }, + { + "$ref": "#/components/schemas/session.reasoning.started" + }, + { + "$ref": "#/components/schemas/session.reasoning.delta" + }, + { + "$ref": "#/components/schemas/session.reasoning.ended" + }, + { + "$ref": "#/components/schemas/session.tool.input.started" + }, + { + "$ref": "#/components/schemas/session.tool.input.delta" + }, + { + "$ref": "#/components/schemas/session.tool.input.ended" + }, + { + "$ref": "#/components/schemas/session.tool.called" + }, + { + "$ref": "#/components/schemas/session.tool.progress" + }, + { + "$ref": "#/components/schemas/session.tool.success" + }, + { + "$ref": "#/components/schemas/session.tool.failed" + }, + { + "$ref": "#/components/schemas/session.retry.scheduled" + }, + { + "$ref": "#/components/schemas/session.compaction.admitted" + }, + { + "$ref": "#/components/schemas/session.compaction.started" + }, + { + "$ref": "#/components/schemas/session.compaction.delta" + }, + { + "$ref": "#/components/schemas/session.compaction.ended" + }, + { + "$ref": "#/components/schemas/session.compaction.failed" + }, + { + "$ref": "#/components/schemas/session.revert.staged" + }, + { + "$ref": "#/components/schemas/session.revert.cleared" + }, + { + "$ref": "#/components/schemas/session.revert.committed" + }, + { + "$ref": "#/components/schemas/filesystem.changed" + }, + { + "$ref": "#/components/schemas/reference.updated" + }, + { + "$ref": "#/components/schemas/permission.v2.asked" + }, + { + "$ref": "#/components/schemas/permission.v2.replied" + }, + { + "$ref": "#/components/schemas/plugin.added" + }, + { + "$ref": "#/components/schemas/plugin.updated" + }, + { + "$ref": "#/components/schemas/project.directories.updated" + }, + { + "$ref": "#/components/schemas/command.updated" + }, + { + "$ref": "#/components/schemas/config.updated" + }, + { + "$ref": "#/components/schemas/skill.updated" + }, + { + "$ref": "#/components/schemas/pty.created" + }, + { + "$ref": "#/components/schemas/pty.updated" + }, + { + "$ref": "#/components/schemas/pty.exited" + }, + { + "$ref": "#/components/schemas/pty.deleted" + }, + { + "$ref": "#/components/schemas/shell.created" + }, + { + "$ref": "#/components/schemas/shell.exited" + }, + { + "$ref": "#/components/schemas/shell.deleted" + }, + { + "$ref": "#/components/schemas/question.v2.asked" + }, + { + "$ref": "#/components/schemas/question.v2.replied" + }, + { + "$ref": "#/components/schemas/question.v2.rejected" + }, + { + "$ref": "#/components/schemas/form.created" + }, + { + "$ref": "#/components/schemas/form.replied" + }, + { + "$ref": "#/components/schemas/form.cancelled" + }, + { + "$ref": "#/components/schemas/session.status" + }, + { + "$ref": "#/components/schemas/session.idle" + }, + { + "$ref": "#/components/schemas/tui.prompt.append" + }, + { + "$ref": "#/components/schemas/tui.command.execute" + }, + { + "$ref": "#/components/schemas/tui.toast.show" + }, + { + "$ref": "#/components/schemas/tui.session.select" + }, + { + "$ref": "#/components/schemas/installation.updated" + }, + { + "$ref": "#/components/schemas/installation.update-available" + }, + { + "$ref": "#/components/schemas/vcs.branch.updated" + }, + { + "$ref": "#/components/schemas/mcp.status.changed" + }, + { + "$ref": "#/components/schemas/mcp.resources.changed" + }, + { + "$ref": "#/components/schemas/permission.asked" + }, + { + "$ref": "#/components/schemas/permission.replied" + }, + { + "$ref": "#/components/schemas/question.asked" + }, + { + "$ref": "#/components/schemas/question.replied" + }, + { + "$ref": "#/components/schemas/question.rejected" + }, + { + "$ref": "#/components/schemas/session.error" + }, + { + "$ref": "#/components/schemas/V2Event.server.connected" + } + ] + }, + "V2EventStream": { + "type": "string", + "contentSchema": { + "$ref": "#/components/schemas/V2Event" + }, + "contentMediaType": "application/json" + }, + "PtyNotFoundError": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "PtyNotFoundError" + ] + }, + "ptyID": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": [ + "_tag", + "ptyID", + "message" + ], + "additionalProperties": false + }, + "PtyTicket.ConnectToken": { + "type": "object", + "properties": { + "ticket": { + "type": "string" + }, + "expires_in": { + "type": "integer", + "allOf": [ + { + "exclusiveMinimum": 0 + } + ] + } + }, + "required": [ + "ticket", + "expires_in" + ], + "additionalProperties": false + }, + "ForbiddenError": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "ForbiddenError" + ] + }, + "message": { + "type": "string" + } + }, + "required": [ + "_tag", + "message" + ], + "additionalProperties": false + }, + "Shell1": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^sh_" + } + ] + }, + "status": { + "type": "string", + "enum": [ + "running", + "exited", + "timeout", + "killed" + ] + }, + "command": { + "type": "string" + }, + "cwd": { + "type": "string" + }, + "shell": { + "type": "string" + }, + "file": { + "type": "string" + }, + "pid": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "exit": { + "anyOf": [ + { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "NaN" + ] + }, + { + "type": "string", + "enum": [ + "Infinity" + ] + }, + { + "type": "string", + "enum": [ + "-Infinity" + ] + } + ] + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "metadata": { + "type": "object" + }, + "time": { + "type": "object", + "properties": { + "started": { + "anyOf": [ + { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "NaN" + ] + }, + { + "type": "string", + "enum": [ + "Infinity" + ] + }, + { + "type": "string", + "enum": [ + "-Infinity" + ] + } + ] + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + }, + "completed": { + "anyOf": [ + { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": [ + "NaN" + ] + }, + { + "type": "string", + "enum": [ + "Infinity" + ] + }, + { + "type": "string", + "enum": [ + "-Infinity" + ] + } + ] + }, + { + "type": "string", + "enum": [ + "Infinity", + "-Infinity", + "NaN" + ] + } + ] + } + }, + "required": [ + "started" + ], + "additionalProperties": false + } + }, + "required": [ + "id", + "status", + "command", + "cwd", + "shell", + "file", + "metadata", + "time" + ], + "additionalProperties": false + }, + "ShellNotFoundError": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "ShellNotFoundError" + ] + }, + "id": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": [ + "_tag", + "id", + "message" + ], + "additionalProperties": false + }, + "QuestionV2.Request": { + "type": "object", + "properties": { + "id": { + "type": "string", + "allOf": [ + { + "pattern": "^que" + } + ] + }, + "sessionID": { + "type": "string", + "allOf": [ + { + "pattern": "^ses" + } + ] + }, + "questions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/QuestionV2.Info" + }, + "description": "Questions to ask" + }, + "tool": { + "$ref": "#/components/schemas/QuestionV2.Tool" + } + }, + "required": [ + "id", + "sessionID", + "questions" + ], + "additionalProperties": false + }, + "QuestionV2.Reply": { + "type": "object", + "properties": { + "answers": { + "type": "array", + "items": { + "$ref": "#/components/schemas/QuestionV2.Answer" + }, + "description": "User answers in order of questions (each answer is an array of selected labels)" + } + }, + "required": [ + "answers" + ], + "additionalProperties": false + }, + "QuestionNotFoundError": { + "type": "object", + "properties": { + "_tag": { + "type": "string", + "enum": [ + "QuestionNotFoundError" + ] + }, + "requestID": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": [ + "_tag", + "requestID", + "message" + ], + "additionalProperties": false + }, + "Reference.LocalSource": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "local" + ] + }, + "path": { + "type": "string" + }, + "description": { + "type": "string" + }, + "hidden": { + "type": "boolean" + } + }, + "required": [ + "type", + "path" + ], + "additionalProperties": false + }, + "Reference.GitSource": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "git" + ] + }, + "repository": { + "type": "string" + }, + "branch": { + "type": "string" + }, + "description": { + "type": "string" + }, + "hidden": { + "type": "boolean" + } + }, + "required": [ + "type", + "repository" + ], + "additionalProperties": false + }, + "Reference.Source": { + "anyOf": [ + { + "$ref": "#/components/schemas/Reference.LocalSource" + }, + { + "$ref": "#/components/schemas/Reference.GitSource" + } + ] + }, + "Reference.Info": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "path": { + "type": "string" + }, + "description": { + "type": "string" + }, + "hidden": { + "type": "boolean" + }, + "source": { + "$ref": "#/components/schemas/Reference.Source" + } + }, + "required": [ + "name", + "path", + "source" + ], + "additionalProperties": false + }, + "ProjectCopy.Copy": { + "type": "object", + "properties": { + "directory": { + "type": "string" + } + }, + "required": [ + "directory" + ], + "additionalProperties": false + }, + "ProjectCopyError": { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "ProjectCopyError" + ] + }, + "data": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "forceRequired": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "message" + ], + "additionalProperties": false + } + }, + "required": [ + "name", + "data" + ], + "additionalProperties": false + }, + "Vcs.FileStatus": { + "type": "object", + "properties": { + "file": { + "type": "string" + }, + "additions": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "deletions": { + "type": "integer", + "allOf": [ + { + "minimum": 0 + } + ] + }, + "status": { + "type": "string", + "enum": [ + "added", + "deleted", + "modified" + ] + } + }, + "required": [ + "file", + "additions", + "deletions", + "status" + ], + "additionalProperties": false + }, + "Vcs.Mode": { + "type": "string", + "enum": [ + "working", + "branch" + ] + } + }, + "securitySchemes": {} + }, + "security": [], + "tags": [ + { + "name": "health" + }, + { + "name": "server" + }, + { + "name": "location" + }, + { + "name": "agent" + }, + { + "name": "plugin", + "description": "Experimental plugin routes." + }, + { + "name": "session", + "description": "Experimental session routes." + }, + { + "name": "session", + "description": "Experimental message routes." + }, + { + "name": "model", + "description": "Experimental model routes." + }, + { + "name": "generate", + "description": "Experimental one-shot generation routes." + }, + { + "name": "provider", + "description": "Experimental provider routes." + }, + { + "name": "integration", + "description": "Integration discovery and authentication routes." + }, + { + "name": "mcp", + "description": "MCP server and resource routes." + }, + { + "name": "credential" + }, + { + "name": "project", + "description": "Location-scoped project routes." + }, + { + "name": "form", + "description": "Session form routes." + }, + { + "name": "permission", + "description": "Experimental permission routes." + }, + { + "name": "filesystem", + "description": "Experimental location-scoped filesystem routes." + }, + { + "name": "command", + "description": "Experimental command routes." + }, + { + "name": "skill", + "description": "Experimental skill routes." + }, + { + "name": "event", + "description": "Experimental event stream routes." + }, + { + "name": "pty", + "description": "Experimental location-scoped PTY routes." + }, + { + "name": "shell", + "description": "Experimental location-scoped shell command routes." + }, + { + "name": "question", + "description": "Experimental session question routes." + }, + { + "name": "reference", + "description": "Location-scoped project references." + }, + { + "name": "projectCopy", + "description": "Project copy management routes." + }, + { + "name": "vcs", + "description": "Location-scoped version control routes." + }, + { + "name": "debug" + } + ] +} diff --git a/packages/www/source.config.ts b/packages/www/source.config.ts new file mode 100644 index 0000000000..b163c92dbf --- /dev/null +++ b/packages/www/source.config.ts @@ -0,0 +1,5 @@ +import { defineDocs } from "fumadocs-mdx/config" + +export const docs = defineDocs({ + dir: "content/docs", +}) diff --git a/packages/www/src/components/mdx.tsx b/packages/www/src/components/mdx.tsx new file mode 100644 index 0000000000..443947fa9f --- /dev/null +++ b/packages/www/src/components/mdx.tsx @@ -0,0 +1,55 @@ +import { Children, isValidElement, type ComponentProps, type ReactNode } from "react" +import { Callout } from "fumadocs-ui/components/callout" +import { Card, Cards } from "fumadocs-ui/components/card" +import { Tab as FumadocsTab, Tabs as FumadocsTabs } from "fumadocs-ui/components/tabs" +import defaultMdxComponents from "fumadocs-ui/mdx" +import type { MDXComponents } from "mdx/types" + +function Tip(props: { children: ReactNode }) { + return {props.children} +} + +function Note(props: { children: ReactNode }) { + return {props.children} +} + +function Warning(props: { children: ReactNode }) { + return {props.children} +} + +function Tab(props: { title?: string; value?: string; children: ReactNode }) { + return {props.children} +} + +function Tabs(props: { children: ReactNode }) { + const items = Children.toArray(props.children).flatMap((child) => { + if (!isValidElement>(child)) return [] + const value = child.props.value ?? child.props.title + return value ? [value] : [] + }) + return {props.children} +} + +function CardGroup(props: { children: ReactNode; cols?: number }) { + return {props.children} +} + +export function getMdxComponents(components?: MDXComponents) { + return { + ...defaultMdxComponents, + Tip, + Note, + Warning, + Tabs, + Tab, + CardGroup, + Card, + ...components, + } satisfies MDXComponents +} + +export const useMDXComponents = getMdxComponents + +declare global { + type MDXProvidedComponents = ReturnType +} diff --git a/packages/www/src/lib/layout.tsx b/packages/www/src/lib/layout.tsx new file mode 100644 index 0000000000..c226f0f708 --- /dev/null +++ b/packages/www/src/lib/layout.tsx @@ -0,0 +1,17 @@ +import type { BaseLayoutProps } from "fumadocs-ui/layouts/shared" + +export function baseOptions(): BaseLayoutProps { + return { + nav: { + title: "OpenCode", + url: "/", + }, + links: [ + { + text: "GitHub", + url: "https://github.com/anomalyco/opencode", + external: true, + }, + ], + } +} diff --git a/packages/www/src/lib/source.ts b/packages/www/src/lib/source.ts new file mode 100644 index 0000000000..8a72b43aaf --- /dev/null +++ b/packages/www/src/lib/source.ts @@ -0,0 +1,7 @@ +import { docs } from "collections/server" +import { loader } from "fumadocs-core/source" + +export const source = loader({ + baseUrl: "/docs", + source: docs.toFumadocsSource(), +}) diff --git a/packages/www/src/routeTree.gen.ts b/packages/www/src/routeTree.gen.ts new file mode 100644 index 0000000000..6a5e345a96 --- /dev/null +++ b/packages/www/src/routeTree.gen.ts @@ -0,0 +1,104 @@ +/* eslint-disable */ + +// @ts-nocheck + +// noinspection JSUnusedGlobalSymbols + +// This file was automatically generated by TanStack Router. +// You should NOT make any changes in this file as it will be overwritten. +// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. + +import { Route as rootRouteImport } from './routes/__root' +import { Route as IndexRouteImport } from './routes/index' +import { Route as DocsSplatRouteImport } from './routes/docs/$' +import { Route as ApiSearchRouteImport } from './routes/api/search' + +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) +const DocsSplatRoute = DocsSplatRouteImport.update({ + id: '/docs/$', + path: '/docs/$', + getParentRoute: () => rootRouteImport, +} as any) +const ApiSearchRoute = ApiSearchRouteImport.update({ + id: '/api/search', + path: '/api/search', + getParentRoute: () => rootRouteImport, +} as any) + +export interface FileRoutesByFullPath { + '/': typeof IndexRoute + '/api/search': typeof ApiSearchRoute + '/docs/$': typeof DocsSplatRoute +} +export interface FileRoutesByTo { + '/': typeof IndexRoute + '/api/search': typeof ApiSearchRoute + '/docs/$': typeof DocsSplatRoute +} +export interface FileRoutesById { + __root__: typeof rootRouteImport + '/': typeof IndexRoute + '/api/search': typeof ApiSearchRoute + '/docs/$': typeof DocsSplatRoute +} +export interface FileRouteTypes { + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: '/' | '/api/search' | '/docs/$' + fileRoutesByTo: FileRoutesByTo + to: '/' | '/api/search' | '/docs/$' + id: '__root__' | '/' | '/api/search' | '/docs/$' + fileRoutesById: FileRoutesById +} +export interface RootRouteChildren { + IndexRoute: typeof IndexRoute + ApiSearchRoute: typeof ApiSearchRoute + DocsSplatRoute: typeof DocsSplatRoute +} + +declare module '@tanstack/react-router' { + interface FileRoutesByPath { + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } + '/docs/$': { + id: '/docs/$' + path: '/docs/$' + fullPath: '/docs/$' + preLoaderRoute: typeof DocsSplatRouteImport + parentRoute: typeof rootRouteImport + } + '/api/search': { + id: '/api/search' + path: '/api/search' + fullPath: '/api/search' + preLoaderRoute: typeof ApiSearchRouteImport + parentRoute: typeof rootRouteImport + } + } +} + +const rootRouteChildren: RootRouteChildren = { + IndexRoute: IndexRoute, + ApiSearchRoute: ApiSearchRoute, + DocsSplatRoute: DocsSplatRoute, +} +export const routeTree = rootRouteImport + ._addFileChildren(rootRouteChildren) + ._addFileTypes() + +import type { getRouter } from './router.tsx' +import type { createStart } from '@tanstack/react-start' +declare module '@tanstack/react-start' { + interface Register { + ssr: true + router: Awaited> + } +} diff --git a/packages/www/src/router.tsx b/packages/www/src/router.tsx new file mode 100644 index 0000000000..0e0657259f --- /dev/null +++ b/packages/www/src/router.tsx @@ -0,0 +1,16 @@ +import { createRouter } from "@tanstack/react-router" +import { routeTree } from "./routeTree.gen" + +export function getRouter() { + return createRouter({ + routeTree, + scrollRestoration: true, + defaultPreload: "intent", + }) +} + +declare module "@tanstack/react-router" { + interface Register { + router: ReturnType + } +} diff --git a/packages/www/src/routes/__root.tsx b/packages/www/src/routes/__root.tsx new file mode 100644 index 0000000000..3af1ba498f --- /dev/null +++ b/packages/www/src/routes/__root.tsx @@ -0,0 +1,34 @@ +import { HeadContent, Scripts, createRootRoute } from "@tanstack/react-router" +import { RootProvider } from "fumadocs-ui/provider/tanstack" +import type { ReactNode } from "react" +import styles from "../styles.css?url" + +export const Route = createRootRoute({ + head: () => ({ + meta: [ + { charSet: "utf-8" }, + { name: "viewport", content: "width=device-width, initial-scale=1" }, + { title: "OpenCode" }, + { + name: "description", + content: "The open source AI coding agent.", + }, + ], + links: [{ rel: "stylesheet", href: styles }], + }), + shellComponent: RootDocument, +}) + +function RootDocument(props: { children: ReactNode }) { + return ( + + + + + + {props.children} + + + + ) +} diff --git a/packages/www/src/routes/api/search.ts b/packages/www/src/routes/api/search.ts new file mode 100644 index 0000000000..2e29939209 --- /dev/null +++ b/packages/www/src/routes/api/search.ts @@ -0,0 +1,15 @@ +import { createFileRoute } from "@tanstack/react-router" +import { createFromSource } from "fumadocs-core/search/server" +import { source } from "@/lib/source" + +const search = createFromSource(source, { + language: "english", +}) + +export const Route = createFileRoute("/api/search")({ + server: { + handlers: { + GET: ({ request }) => search.GET(request), + }, + }, +}) diff --git a/packages/www/src/routes/docs/$.tsx b/packages/www/src/routes/docs/$.tsx new file mode 100644 index 0000000000..9d0060a034 --- /dev/null +++ b/packages/www/src/routes/docs/$.tsx @@ -0,0 +1,62 @@ +import { createFileRoute, notFound } from "@tanstack/react-router" +import { createServerFn } from "@tanstack/react-start" +import browserCollections from "collections/browser" +import { useFumadocsLoader } from "fumadocs-core/source/client" +import { DocsLayout } from "fumadocs-ui/layouts/docs" +import { DocsBody, DocsDescription, DocsPage, DocsTitle } from "fumadocs-ui/layouts/docs/page" +import { Suspense } from "react" +import { getMdxComponents } from "@/components/mdx" +import { baseOptions } from "@/lib/layout" +import { source } from "@/lib/source" + +export const Route = createFileRoute("/docs/$")({ + loader: async ({ params }) => { + const slugs = params._splat?.split("/").filter(Boolean) ?? [] + const data = await loadPage({ data: slugs }) + await clientLoader.preload(data.path) + return data + }, + head: ({ loaderData }) => ({ + meta: [ + { title: `${loaderData?.title ?? "Docs"} | OpenCode` }, + { name: "description", content: loaderData?.description ?? "OpenCode documentation." }, + ], + }), + component: Documentation, +}) + +const loadPage = createServerFn({ method: "GET" }) + .validator((slugs: string[]) => slugs) + .handler(async ({ data: slugs }) => { + const page = source.getPage(slugs) + if (!page) throw notFound() + return { + path: page.path, + title: page.data.title, + description: page.data.description, + tree: await source.serializePageTree(source.getPageTree()), + } + }) + +const clientLoader = browserCollections.docs.createClientLoader({ + component({ toc, frontmatter, default: Content }) { + return ( + + {frontmatter.title} + {frontmatter.description} + + + + + ) + }, +}) + +function Documentation() { + const data = useFumadocsLoader(Route.useLoaderData()) + return ( + + {clientLoader.useContent(data.path)} + + ) +} diff --git a/packages/www/src/routes/index.tsx b/packages/www/src/routes/index.tsx new file mode 100644 index 0000000000..d674a30d72 --- /dev/null +++ b/packages/www/src/routes/index.tsx @@ -0,0 +1,18 @@ +import { createFileRoute } from "@tanstack/react-router" + +export const Route = createFileRoute("/")({ + component: Home, +}) + +function Home() { + return ( +
+

OpenCode

+

The open source AI coding agent

+

The new opencode.ai SSR site is running. The V2 documentation is available now.

+ + Read the docs + +
+ ) +} diff --git a/packages/www/src/styles.css b/packages/www/src/styles.css new file mode 100644 index 0000000000..9fab42c420 --- /dev/null +++ b/packages/www/src/styles.css @@ -0,0 +1,85 @@ +@import "tailwindcss"; +@import "fumadocs-ui/css/neutral.css"; +@import "fumadocs-ui/css/preset.css"; + +:root { + --site-background: #f7f7f5; + --site-foreground: #171717; + --site-border: #d8d8d2; + --site-accent: #3b7dd8; + --fd-layout-width: 90rem; +} + +* { + box-sizing: border-box; +} + +html, +body { + min-height: 100%; +} + +body { + margin: 0; +} + +/* Fumadocs 16.11 stretches top layout tabs across the main grid row. */ +#nd-docs-layout > div:has(> a[href="/docs/build"], > a[href="/docs/api"]) { + align-self: start; + height: 3rem; +} + +.home { + min-height: 100vh; + display: grid; + align-content: center; + justify-items: start; + gap: 1.5rem; + padding: clamp(1.5rem, 7vw, 7rem); + color: var(--site-foreground); + background: var(--site-background); +} + +.home-kicker, +.home-copy, +.home h1 { + margin: 0; +} + +.home-kicker { + font: + 600 0.75rem/1.2 ui-monospace, + monospace; + letter-spacing: 0.12em; + text-transform: uppercase; +} + +.home h1 { + max-width: 13ch; + font: + 500 clamp(3rem, 9vw, 8rem) / 0.92 ui-monospace, + monospace; + letter-spacing: -0.07em; +} + +.home-copy { + max-width: 42rem; + font: + 400 clamp(1rem, 2vw, 1.25rem) / 1.5 ui-monospace, + monospace; +} + +.home-link { + display: inline-block; + padding: 0.8rem 1rem; + color: var(--site-background); + background: var(--site-foreground); + font: + 500 0.875rem/1 ui-monospace, + monospace; + text-decoration: none; +} + +.home-link:hover { + background: var(--site-accent); +} diff --git a/packages/www/tsconfig.json b/packages/www/tsconfig.json new file mode 100644 index 0000000000..fa688da72b --- /dev/null +++ b/packages/www/tsconfig.json @@ -0,0 +1,24 @@ +{ + "include": ["src/**/*.ts", "src/**/*.tsx", "source.config.ts", "vite.config.ts", ".source/**/*.ts"], + "compilerOptions": { + "target": "ES2022", + "jsx": "react-jsx", + "module": "ESNext", + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + "skipLibCheck": true, + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "types": ["vite/client"], + "paths": { + "@/*": ["./src/*"], + "collections/*": ["./.source/*"] + } + } +} diff --git a/packages/www/vite.config.ts b/packages/www/vite.config.ts new file mode 100644 index 0000000000..1fdc270427 --- /dev/null +++ b/packages/www/vite.config.ts @@ -0,0 +1,22 @@ +import { cloudflare } from "@cloudflare/vite-plugin" +import tailwindcss from "@tailwindcss/vite" +import { tanstackStart } from "@tanstack/react-start/plugin/vite" +import viteReact from "@vitejs/plugin-react" +import mdx from "fumadocs-mdx/vite" +import { defineConfig } from "vite" + +export default defineConfig({ + resolve: { + tsconfigPaths: true, + }, + plugins: [ + cloudflare({ + viteEnvironment: { name: "ssr" }, + configPath: process.env.SST_WRANGLER_PATH, + }), + tailwindcss(), + mdx(), + tanstackStart(), + viteReact(), + ], +}) diff --git a/packages/www/wrangler.jsonc b/packages/www/wrangler.jsonc new file mode 100644 index 0000000000..b1b96c9934 --- /dev/null +++ b/packages/www/wrangler.jsonc @@ -0,0 +1,7 @@ +{ + "$schema": "node_modules/wrangler/config-schema.json", + "name": "opencode-www", + "compatibility_date": "2026-07-12", + "compatibility_flags": ["nodejs_compat"], + "main": "@tanstack/react-start/server-entry", +}