Compare commits

..

33 Commits

Author SHA1 Message Date
Kit Langton 129a646308 test(core): cover fixed plugin readiness 2026-07-07 22:34:07 -04:00
Kit Langton 00335982ad fix(core): narrow plugin readiness barrier 2026-07-07 22:17:13 -04:00
Kit Langton 83f25e88b6 fix(core): continue after unavailable tool calls 2026-07-07 22:17:13 -04:00
Kit Langton 1f9305b3af fix(core): wait for plugin readiness 2026-07-07 22:17:13 -04:00
Kit Langton a142d76eec test(core): reproduce explorer tool leak 2026-07-07 22:17:13 -04:00
Kit Langton d27746e5b3 test(core): simplify runner test fixtures (#35832) 2026-07-07 21:58:52 -04:00
Kit Langton eb661e6e6d fix(core): settle unadvertised tool calls and repair session hook test contexts 2026-07-07 21:51:23 -04:00
Dax Raad 3379e44abb refactor(core): rename ApplyPatchTool to PatchTool 2026-07-07 21:16:00 -04:00
Dax e1abf59c59 fix(core): provide PluginHooks in plugin test layer (#35811) 2026-07-07 20:03:41 -04:00
Dax 4f976bcf1a feat(plugin): add session request hook (#35794) 2026-07-07 19:56:47 -04:00
Kit Langton e25a724bc2 test(tui): cover patch diff display aliases (#35810) 2026-07-07 19:44:54 -04:00
Kit Langton 521ea87192 fix(core): retain reasoning delta state (#35758) 2026-07-07 19:44:40 -04:00
Aiden Cline 3100701488 fix(tui): restore prompt context usage (#35795) 2026-07-07 17:49:33 -05:00
Kit Langton 9c54291ae8 fix(tui): restore forms on reconnect
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
2026-07-07 22:47:13 +00:00
Dax Raad 52ad916ba4 fix(tui): restore permissions on reconnect
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
2026-07-07 22:44:26 +00:00
Dax Raad 5db320b02d refactor: rename apply_patch tool 2026-07-07 16:27:25 -04:00
Aiden Cline 910e37f6d8 fix: update v2 session usage metrics (#35468) 2026-07-07 14:31:54 -05:00
Kit Langton 81f6e06681 feat: enable background subagents by default 2026-07-07 15:18:46 -04:00
James Long da68e2865e feat(tui): wire move session command (#35203) 2026-07-07 14:49:11 -04:00
Aiden Cline f86e1cc1ff fix(codemode): preserve collection value identity (#35776) 2026-07-07 13:40:55 -05:00
Aiden Cline 947bbf9490 feat(mcp): expose resource catalog API (#35773) 2026-07-07 13:34:29 -05:00
James Long 3e57b43a4a feat(session): support admit-only synthetic messages (#35774) 2026-07-07 14:17:20 -04:00
opencode-agent[bot] cb18a42a47 docs(codemode): clarify implicit return guidance (#35763)
Co-authored-by: Kit Langton <kit.langton@gmail.com>
2026-07-07 12:48:05 -05:00
Aiden Cline 1fb2837fd3 feat(codemode): expand numeric standard library (#35749) 2026-07-07 12:11:57 -05:00
Aiden Cline 66a8d830aa feat(core): support MCP resources (#35656) 2026-07-07 11:55:06 -05:00
opencode-agent[bot] d0733f83bf fix(codemode): return final top-level expression (#35759)
Co-authored-by: Kit Langton <kit.langton@gmail.com>
2026-07-07 11:44:50 -05:00
Kit Langton b2ce195bda fix(core): normalize Location.Ref keys in LocationServiceMap (#35757) 2026-07-07 12:25:29 -04:00
James Long 6c065ca6a5 feat(simulation): render driven UI screenshots and videos (#35739) 2026-07-07 12:15:01 -04:00
Dax Raad dd1c007877 docs 2026-07-07 12:08:45 -04:00
Kit Langton 8deb0e5780 fix(core): preserve SDK plugins across location eviction (#35725) 2026-07-07 11:43:40 -04:00
Aiden Cline f488089179 fix: preserve compaction files and classify Z.AI overflow (#35747)
Co-authored-by: opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: 冯基魁 <56265583+fengjikui@users.noreply.github.com>
2026-07-07 09:54:31 -05:00
Kit Langton e3a39b214f fix(core): lower failed reasoning to text (#35735) 2026-07-07 09:49:17 -04:00
James Long b6a2912bb1 refactor(simulation): type RPC request payloads (#35734) 2026-07-07 09:21:19 -04:00
272 changed files with 11874 additions and 7228 deletions
+454
View File
@@ -0,0 +1,454 @@
# Plugin Readiness and Generation Lifetime
## Recommendation
Revise draft PR #35755 to expose one fixed-target readiness barrier. When Session execution reaches the barrier, it captures the current requested activation revision, waits until the Location has applied at least that revision, then continues without retaining an activation lock.
Immediately afterward, the runner resolves the selected agent and fails closed with `Session.AgentNotFoundError` if it is absent. This closes the empty-generation tool leak without claiming that a complete model step or request preparation is an immutable plugin snapshot.
Do not merge the current whole-step semaphore. It serializes Sessions and deterministically deadlocks a foreground subagent: the parent holds the Location semaphore while waiting for a child Session that needs the same semaphore.
Do not replace it with a preparation lock. Request hooks are arbitrary Effects that can reenter Sessions, and model resolution may perform credential/network work. Holding the Location semaphore across either can recreate deadlocks or block every Session.
Do not add generation leases in this PR. A correct lease design requires generation-local executable state or owner borrowing for tools, hooks, and AI SDK models. Effect supplies useful scope and reference-counting pieces, but no primitive can make the current shared mutable modules generation-isolated.
This document records the evidence, alternatives, and migration plan behind that recommendation.
## Problem
A Location becomes available before its initial plugin generation finishes activating. Session execution can therefore resolve an absent selected agent or materialize tools from incomplete plugin-owned state.
The original failure looked like this:
1. A Session selected `explore`.
2. Plugin activation had not installed the `explore` agent yet.
3. `AgentV2.select("explore")` returned an ID with no agent definition.
4. Tool materialization received `permissions: undefined` and advertised tools such as `shell`.
5. Tool execution later failed closed because permission evaluation could not find the agent.
Location eviction makes this more than a process-start race. Reopening an evicted Location reconstructs Location-scoped agent, catalog, hook, and tool state asynchronously.
SDK registration and Config updates add an ordering requirement: if update publication completes before Session execution begins synchronization, the barrier must apply at least that update's revision before returning.
## Required Semantics
### Progressive Location startup
Location acquisition and TUI bootstrap must remain non-blocking. Plugin imports, provider discovery, and external setup must not become a prerequisite for first paint or unrelated filesystem operations.
### Session readiness
Before constructing a model request, Session execution captures the current requested activation revision and waits until the Location has applied at least that revision.
Readiness includes:
- plugin planning and module import
- top-level plugin setup
- initial agent, model, command, skill, hook, and tool contributions
- final Config projection plugins
- all tracked updates ordered before the captured target revision
Readiness excludes:
- future registrations
- arbitrary background fibers
- ongoing listeners
- MCP connection completion unless setup deliberately awaits it
- global process quiescence
### Readiness is a barrier, not a snapshot
The barrier guarantees that activation requests observed when the wait begins have completed before authoritative agent resolution starts. It does not freeze plugin-owned state after the barrier.
Config, SDK, and credential updates may occur immediately afterward. Current registries and caches retain their existing live-reload semantics. This PR must not claim coherent request or whole-step generations because the current shared mutable modules cannot provide them.
### Missing agents fail closed
After readiness settles, a missing selected agent must produce typed `Session.AgentNotFoundError`. It must not fall back to another agent, a generic system prompt, or unrestricted tool materialization.
### Failure and boundedness
The barrier must not hang forever on network-dependent activation. Existing optional-plugin import/setup failures may still be logged and skipped; selected agents and models fail closed if their required contribution is absent after the barrier.
## Current Implementation
`PluginSupervisor` maintains two process-local counters per Location:
```text
requested = number of observed initial/config/SDK activation requests
applied = latest request boundary completed by activation
```
Its activation loop snapshots `requested`, plans and activates a generation, assigns the snapshot to `applied`, and repeats if another update arrived during activation.
```text
requested = 1
activate generation 1
SDK update -> requested = 2
finish generation 1 -> applied = 1
activate generation 2
finish generation 2 -> applied = 2
return
```
`SessionRunnerLLM` wraps each complete model-step attempt in:
```ts
plugins.withGeneration(attemptStep(...))
```
`withGeneration` currently:
1. acquires a Location-wide semaphore
2. drains pending activation requests
3. runs the complete model step while retaining the semaphore
4. releases the semaphore after provider streaming and owned tool settlement
This prevents plugin scope replacement from invalidating tool identities during a step.
The OpenAI and OpenCode provider plugins now await their initial refresh because a completed generation must contain the initial catalog required for Session model resolution. The OpenCode config request has a 10-second timeout and no retry. Errors are logged and remote providers remain absent.
`PluginSupervisorNode` is not a new runtime module. The existing node definition was moved out of `location-services.ts` so both the Location graph and `SessionRunnerLLM.node` can depend on it without a module import cycle.
## Current Strengths
- The initial empty-generation race is closed.
- Config and SDK updates ordered before synchronization begins are applied.
- Missing selected agents fail before model execution.
- Location acquisition and TUI startup remain progressive.
- The revision accounting is localized in the supervisor.
## Current Risks
### Location-wide serialization and nested-Session deadlock
The semaphore is held across provider streaming and tool execution. Different Sessions in one Location cannot execute model steps concurrently even though Session coordination otherwise permits that concurrency.
Foreground subagents make this a correctness bug, not only a performance cost:
1. A parent Session holds the Location semaphore while settling the subagent tool.
2. The subagent tool starts a child Session and waits for it to finish.
3. The child Session needs the same Location semaphore before its model step.
4. The parent waits for the child while the child waits for the parent-held semaphore.
### Delayed reloads
A long model call or tool execution prevents Config and SDK updates from activating. This is safe but potentially surprising and can make reload latency unbounded.
### Moving drain target
The activation loop drains until `applied === requested`. A continuous update stream can prevent every Session from starting. The contract cannot guarantee both bounded completion and inclusion of every event published before return. Synchronization needs a target revision captured when the wait begins.
### Remote work inside activation
Awaiting initial provider refresh makes catalog readiness truthful, but network and credential resolution now sit on the activation path. OpenCode fetch is bounded to 10 seconds; credential resolution is not separately bounded, and there is no transient retry policy.
### Forced full replacement
Config and SDK updates force replacement even when plugin IDs and versions compare equal. This is currently required for Config projections and same-ID SDK replacement, but it repeats all plugin setup and provider refresh work.
### Failure semantics
The registry currently logs and skips individual plugin setup failures. A generation can therefore be "complete" while configured plugins are absent. We need to distinguish optional plugin failure from failure of a required selected agent or model.
## Design Questions
1. Can Effect v4 model an atomic swappable scoped resource whose old scope closes only after all readers release it?
2. Does `LayerMap`, `RcMap`, `ScopedRef`, `Resource`, `Pool`, `Scope.fork`, or another Effect primitive already provide the required lease semantics?
3. Can tool materialization own everything needed for later settlement, removing the need to retain the plugin generation?
4. Can plugin scopes remain alive across replacement while domain state atomically switches generations?
5. Which executable contributions need owner borrowing if seamless hot reload becomes a product requirement?
6. What is the precise linearization point for Config and SDK updates?
7. Should synchronization drain updates that arrive during activation, or only the revision captured when synchronization begins?
8. How should concurrent Session readers interact with a writer without serializing each other?
9. How should activation failure affect the previous healthy generation?
10. What bounded retry policy, if any, should initial remote provider discovery use?
## Candidate Design Families
These are starting points for research, not recommendations.
### A. Semaphore pinning
Keep the current implementation. One writer/reader semaphore makes correctness obvious but serializes all readers.
### B. Read/write locking
Allow concurrent Session steps as readers and activation as an exclusive writer. This preserves generation pinning but a long reader still delays reload. Fairness and cancellation become part of the interface.
### C. Reference-counted generation lease
Activation builds a new scoped generation, atomically swaps the current pointer, and retires the old generation. Old scopes close after their active leases reach zero. Readers do not block one another, and reload does not wait for old model steps.
### D. Immutable materialization
Request construction snapshots stable agent/model/hook/tool values. Tool settlement calls captured handlers directly rather than checking current registry identity. Reload may close plugin scopes immediately only if captured handlers do not depend on those scopes.
### E. Stable registration cells
Tool and hook registrations use stable indirection cells whose implementation can be versioned or swapped. A request captures a version or cell lease. This may reduce whole-generation lifetime management but can move complexity into every plugin-owned domain.
### F. Admit-only readiness
Drain readiness only before request construction and accept stale-tool failures after reload as an explicit semantic. This is simplest, but likely violates the promise that an advertised tool remains callable during the step.
## Pressure Tests
Any accepted design must explain these paths:
1. Fresh Location, Session begins before initial activation completes.
2. SDK plugin B registers before or after a Session captures its target while generation A is activating.
3. Config update arrives before or after target capture while synchronization is waiting.
4. Two Sessions in the same Location execute long model steps concurrently.
5. Reload occurs while both Sessions hold tools from the old generation.
6. One old-generation tool runs for minutes after the new generation is active.
7. Plugin setup fails while a previous healthy generation exists.
8. The selected agent is removed by the new generation.
9. Remote provider discovery times out, then credentials change later.
10. A continuous stream of Config updates arrives faster than activation.
11. Location scope closes while activation, readers, or retired generations remain.
12. A Session is interrupted while holding a generation lease.
## Evaluation Criteria
The preferred design should:
- make the required ordering obvious at the interface
- allow concurrent Sessions in one Location
- close the initial and previously published activation ordering race
- avoid making stronger snapshot or executable-lifetime claims
- avoid blocking reload behind model streams or tools
- preserve interruption safety
- keep Location startup progressive
- bound network-dependent readiness
- centralize activation ordering in one deep module
- remain testable with deterministic activation barriers
## Research Findings
### Effect has no direct generation-swap primitive
Effect v4 has useful pieces, but no public primitive that atomically installs a prebuilt scoped replacement, allows concurrent readers to lease the old value, and closes the old scope after its final lease.
- `ScopedRef.set` closes the old scope before installing its replacement. Reads are not leases.
- `Resource` is built on `ScopedRef` and has the same replacement semantics.
- `RcRef` and `RcMap` preserve an invalidated resource until active scoped borrowers release it, but replacement is lazy and they do not provide an atomic current-generation pointer.
- `LayerMap` is a keyed `RcMap` wrapper, not an intra-Location generation manager.
- `Scope`, `Ref`, `Deferred`, `Effect.acquireUseRelease`, and a writer semaphore could implement a generation manager if one becomes necessary.
The correct custom lease design would build a candidate in a child scope, atomically publish it through a `Ref`, mark the previous generation retired, and close retired scopes after their reader count reaches zero. Readers would acquire and release through `Effect.acquireUseRelease`. This is implementable, but it is a substantial new lifecycle module.
### Whole-generation leasing does not fit the current state model
Plugin activation does not currently build one isolated generation object. It mutates shared Location-scoped agent, catalog, hook, tool, command, skill, and reference modules through `State.batch` and scoped registrations.
Retaining only an old plugin scope would preserve its resources, but new request reads would still use shared current state. A true whole-generation lease would require generation-owned snapshots or adapters for every plugin-derived module. Wrapping the current modules in a generation context would not make them isolated.
That design may eventually be valuable, but it is much larger than the readiness bug and would replace the current plugin state architecture.
### Per-registration leases are narrower but still invasive
Another viable design is to make materialized executable values retain their owning plugin scopes:
- capture tool handlers rather than rechecking the live registry
- capture tool hooks and AI SDK hooks
- retain the plugin owners backing those closures
- retire registration visibility immediately on reload
- close plugin scopes after all captured executable values release them
This permits concurrent Sessions and immediate reload, but old and new plugin listeners may coexist, and every executable contribution needs owner provenance. The public plugin contract permits closures over scoped resources, so simply copying handler functions without retaining owners is unsound.
### Existing stale-tool behavior is incomplete lifetime protection
`ToolRegistry.materialize` captures registration identities. Settlement compares that identity with the live registry and returns `tool.stale` when replacement completed before the check.
The check prevents many invocations of removed handlers, but it has a time-of-check/time-of-use window: reload may close the plugin scope after identity validation and before or during handler execution. Tool hooks may also change during one settlement. The readiness barrier neither solves nor depends on this behavior; executable lifetime needs separate work.
### A Location-epoch design is simpler but changes shipped behavior
The simplest possible design would activate plugins once per Location and require reopening the Location for SDK or plugin-topology changes. Ordinary Config projections could continue through their existing listeners.
This removes generation management entirely, but late SDK registration and live plugin code reload are existing behaviors with regression coverage. Changing them is a product decision, not an implementation simplification appropriate for this PR.
## Recommended Design
Use one **fixed-target readiness barrier**. Do not run caller effects while holding the supervisor semaphore.
The supervisor interface has one operation:
```ts
export interface PluginSupervisor {
/** Apply at least every activation request observed when this Effect begins. */
readonly synchronize: Effect.Effect<void>
}
```
Session execution uses it immediately before authoritative agent resolution:
```ts
yield * plugins.synchronize
const agent = yield * agents.select(session.agent)
if (!agent.info) {
return (
yield *
new AgentNotFoundError({
sessionID: session.id,
agent: session.agent ?? agent.id,
})
)
}
// Existing live state and reload semantics apply from here onward.
```
Each call to `synchronize`:
1. Captures `target = requested` when the Effect begins, before waiting for the semaphore.
2. Acquires the per-Location activation semaphore.
3. If `applied < target`, plans and activates from the current stores once, recording the target covered by that activation.
4. Releases the semaphore when `applied >= target`.
The implementation may activate a source state newer than `target` because Config and SDK stores mutate before publishing their update event. That is valid: `target` is a minimum required revision, not an exact generation.
Background activation follows the same fixed-target rule. Each event handler captures one target before waiting for the permit, applies through that target, and releases. It must not use a moving `while (applied < requested)` drain that can monopolize the semaphore indefinitely.
### Readiness linearization
```text
update publication -> requested increments synchronously
synchronize begins -> target = requested
activation commit -> applied advances to that activation's target
barrier returns -> applied >= target
```
Updates ordered before target capture are required. Updates ordered afterward are not required for that wait, but may be coalesced because Config and SDK stores mutate before their update events publish. The barrier promises a minimum revision, not an exact snapshot.
This closes the issue's ordering paths:
- A fresh Location has initial `requested = 1`; the first Session waits for activation.
- A reopened Location after eviction has a new supervisor and the same initial barrier.
- SDK or Config publication increments `requested` synchronously before the publisher returns, so a later barrier captures it.
- Continuous later updates cannot move an already captured completion target.
### No protected preparation callback
The supervisor must not expose `prepare(effect)`, `read(effect)`, or `withGeneration(effect)` in this PR.
Request hooks are arbitrary Effects and can start or await nested Sessions. Model resolution may refresh credentials over the network. Instruction loading may touch files, MCP state, and the database. Letting callers place this work under the activation semaphore recreates deadlocks and unbounded Location-wide serialization.
The barrier intentionally permits a reload immediately after it returns. This is the existing live-reload product behavior. The selected agent is still checked fail-closed, so the original absent-agent path cannot advertise unrestricted tools.
### Activation failure
Preserve existing optional-plugin behavior in this PR. Import and setup failures may be logged and skipped. Required selected resources fail at use:
- missing selected agent -> `Session.AgentNotFoundError`
- missing selected model -> existing model resolution error
Do not claim transactional activation or last-known-good fallback. Current activation closes old scopes before rebuilding shared state. A truthful transactional design requires isolated candidate state and is separate work.
### Remote provider discovery
Readiness and retry remain separate policies.
For this PR:
- await one bounded initial refresh so the first activation attempts to populate the initial catalog
- bound the entire OpenCode discovery operation, including credential resolution, not only the final HTTP fetch
- do not retry indefinitely inside activation
- allow later integration connection updates to refresh again
There is currently no retry in `fetchProviders` or `FetchHttpClient`. A follow-up may add one short retry for transient transport/5xx failures within the same total timeout. Authentication failures, schema errors, and ordinary 4xx responses should not retry.
## Why This Design
It closes the demonstrated readiness bug without making claims the current architecture cannot satisfy:
- Initial and previously published activation requests complete before agent resolution.
- Missing selected agents fail before model or tool materialization.
- Location startup remains progressive.
- Different Sessions do not hold a shared permit during model work.
- Foreground subagents cannot deadlock on the supervisor semaphore.
- Continuous updates cannot starve a captured wait target.
- No new Effect lifecycle abstraction is needed.
The module remains deep despite its one-operation interface: it hides synchronous update accounting, target capture, serialized activation, and initial boot behavior from every authoritative caller.
## Separate Hot-Reload Defects
The reviews found existing lifetime problems that the readiness barrier does not solve and must not obscure:
1. `ToolRegistry.settleWith` checks registration identity and then invokes the handler. Reload can close the plugin scope between the check and invocation.
2. Tool before/after hooks can cross generations during one settlement.
3. `AISDK.language` caches plugin-produced executable model objects without generation identity or invalidation.
4. Credential and Config projection listeners mutate shared Location state outside supervisor activation.
5. Activation failure destroys the prior healthy generation before skipping failed replacements.
6. Title and compaction model requests have their own readiness requirements.
These need separate product contracts and regression tests. Fixing them correctly may require per-registration owner borrowing, cache generation keys, or generation-local state. They are not reasons to retain a whole-step lock, and they are not solved by a preparation lock.
## Rejected Designs
### Whole-step semaphore
Rejected. It serializes Sessions, blocks reload behind long tools, and deadlocks foreground subagents.
### Preparation callback under the semaphore
Rejected. Arbitrary request hooks and model resolution can reenter Sessions or perform unbounded work.
### Read/write lock
Rejected. It still blocks reload behind the longest reader and introduces writer/read reentrancy hazards.
### Reference-counted whole generations
Deferred. It is internally coherent only after Agent, Catalog, permissions, hooks, and ToolRegistry become generation-addressable and immutable.
### Captured handlers without owner retention
Rejected as unsound. Plugin handlers and AI SDK models may close over scope-owned resources.
### Per-registration owner leases
Potential follow-up for seamless hot reload, but ownership provenance must cover tools, hooks, and AI SDK models.
### Location-epoch topology
Architecturally simple, but removes existing live SDK/plugin-topology reload and requires a product decision.
## Pressure-Test Results
1. **Fresh Location:** the initial target waits for initial activation.
2. **SDK update before target capture:** the barrier requires it.
3. **SDK update after target capture:** it is not required but may be coalesced.
4. **Config update before/after capture:** same minimum-revision semantics.
5. **Two long Sessions:** both pass the short barrier and stream concurrently.
6. **Foreground subagent:** parent holds no supervisor permit while waiting for the child.
7. **Continuous updates:** later revisions cannot move the captured target.
8. **Plugin setup failure:** optional failure behavior remains unchanged; selected resources fail closed.
9. **Agent removed after barrier:** existing live-reload race remains; the next authoritative resolution observes current state.
10. **Location closes:** normal scoped interruption applies; no Session-held supervisor lease exists.
11. **Session interruption:** no supervisor resource needs release after the barrier.
12. **Reload during tool/AI SDK execution:** explicitly outside this PR and covered by separate lifetime work.
## Migration From PR #35755
1. Remove `withGeneration` and the whole-attempt wrapper.
2. Keep `synchronize` as the V2 interface; retain `ready` only as a deprecated V1 compatibility alias until V1 is removed.
3. Capture a fixed target when each synchronization or background reload Effect begins.
4. Change activation to apply through that target rather than drain a moving `requested` value.
5. Await `plugins.synchronize` immediately before selected-agent resolution in each model request path.
6. Keep typed missing-agent failure.
7. Bound the complete initial OpenCode provider discovery operation.
8. Revert runner changes added only to accommodate unavailable-tool continuation if they are unrelated to readiness.
9. Add production-layer tests for concurrent Sessions and foreground subagents.
10. Add fixed-target tests for before-capture, after-capture, and continuous updates.
11. Keep fresh-Location, eviction/reopen, embedded SDK first-Session, and missing-agent coverage.
12. Add separate follow-up issues/tests for tool settlement TOCTOU, AI SDK cache lifetime, transactional activation, title readiness, and compaction readiness.
13. Re-run the full core suite and update the PR body with the reduced contract.
## Final Recommendation
Revise PR #35755 to implement only the fixed-target readiness barrier and fail-closed selected-agent resolution. Do not merge either the whole-step lock or a protected preparation callback. Do not introduce generation leases in this PR.
+116 -6
View File
@@ -101,7 +101,6 @@
"dependencies": {
"@effect/platform-node": "catalog:",
"@opencode-ai/client": "workspace:*",
"@opencode-ai/codemode": "workspace:*",
"@opencode-ai/core": "workspace:*",
"@opencode-ai/plugin": "workspace:*",
"@opencode-ai/schema": "workspace:*",
@@ -874,6 +873,7 @@
"name": "@opencode-ai/simulation",
"version": "1.17.13",
"dependencies": {
"@napi-rs/canvas": "1.0.2",
"@opencode-ai/core": "workspace:*",
"@opencode-ai/llm": "workspace:*",
"@opentui/core": "catalog:",
@@ -1612,11 +1612,11 @@
"@emmetio/stream-reader-utils": ["@emmetio/stream-reader-utils@0.1.0", "", {}, "sha512-ZsZ2I9Vzso3Ho/pjZFsmmZ++FWeEd/txqybHTm4OgaZzdS8V9V/YYWQwg5TC38Z7uLWUV1vavpLLbjJtKubR1A=="],
"@emnapi/core": ["@emnapi/core@1.9.2", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA=="],
"@emnapi/core": ["@emnapi/core@1.11.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ=="],
"@emnapi/runtime": ["@emnapi/runtime@1.9.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw=="],
"@emnapi/runtime": ["@emnapi/runtime@1.11.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw=="],
"@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="],
"@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA=="],
"@emotion/is-prop-valid": ["@emotion/is-prop-valid@0.8.8", "", { "dependencies": { "@emotion/memoize": "0.7.4" } }, "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA=="],
@@ -1984,6 +1984,30 @@
"@msgpackr-extract/msgpackr-extract-win32-x64": ["@msgpackr-extract/msgpackr-extract-win32-x64@3.0.4", "", { "os": "win32", "cpu": "x64" }, "sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ=="],
"@napi-rs/canvas": ["@napi-rs/canvas@1.0.2", "", { "optionalDependencies": { "@napi-rs/canvas-android-arm64": "1.0.2", "@napi-rs/canvas-darwin-arm64": "1.0.2", "@napi-rs/canvas-darwin-x64": "1.0.2", "@napi-rs/canvas-linux-arm-gnueabihf": "1.0.2", "@napi-rs/canvas-linux-arm64-gnu": "1.0.2", "@napi-rs/canvas-linux-arm64-musl": "1.0.2", "@napi-rs/canvas-linux-riscv64-gnu": "1.0.2", "@napi-rs/canvas-linux-x64-gnu": "1.0.2", "@napi-rs/canvas-linux-x64-musl": "1.0.2", "@napi-rs/canvas-win32-arm64-msvc": "1.0.2", "@napi-rs/canvas-win32-x64-msvc": "1.0.2" } }, "sha512-EYEqlMYaCbpZDz+IgDH5xp9MTd3ui4dmGqbQYryhMLnSRxrhHKq5KQWHHKxFUcEP4Hp8/BWgvqXocX4j7iSbOQ=="],
"@napi-rs/canvas-android-arm64": ["@napi-rs/canvas-android-arm64@1.0.2", "", { "os": "android", "cpu": "arm64" }, "sha512-IMXKVQod0ol4vt3gmClUfXz4JAgHYESGPCUqmH3lQxBoL0K/2greJaQE1HVBVxWWFKfLc4OLZVdxg7kXVyXv+g=="],
"@napi-rs/canvas-darwin-arm64": ["@napi-rs/canvas-darwin-arm64@1.0.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Sc8tPi6cF+5lqOzCCKFALJHhDiRwyMzTPYm3bbhdXsOunU0lQO5f05ucyOzN2r55I23Hg5bsjH63uSCvWp3EgQ=="],
"@napi-rs/canvas-darwin-x64": ["@napi-rs/canvas-darwin-x64@1.0.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-niDXZ9LhKB1zLrUdYB64RHQFDGz9rr0eGx061qtJJU3U20EMMIx28ADF5fVYbhtOgkWQrBjFicfaye1yM0U62A=="],
"@napi-rs/canvas-linux-arm-gnueabihf": ["@napi-rs/canvas-linux-arm-gnueabihf@1.0.2", "", { "os": "linux", "cpu": "arm" }, "sha512-sgatQL9JxGRH/Amzcvu0P3t8Am3duou74CisfuJ41Dwt8cWy723z/9KZ8LlgmxfypEwEZxSTNFJtU8d281lmhQ=="],
"@napi-rs/canvas-linux-arm64-gnu": ["@napi-rs/canvas-linux-arm64-gnu@1.0.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-dgKuX0peF3xwY6ZF5QxGS4wbfDqpoFAJYXiLSp+guZKARQUKMkRqZSDrXKj7nfrec3UCMzC0PFCPte0ES98AiA=="],
"@napi-rs/canvas-linux-arm64-musl": ["@napi-rs/canvas-linux-arm64-musl@1.0.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-qwROoDIC9upfvDoRLuPn2aNg9CGW1x0Ygr4k2Or+8paA9d0qBLwk87U+g8KQpoOviKoPoiwl97kvBYuYD7qZoA=="],
"@napi-rs/canvas-linux-riscv64-gnu": ["@napi-rs/canvas-linux-riscv64-gnu@1.0.2", "", { "os": "linux", "cpu": "none" }, "sha512-fXRjnPihdnbO6qy1QQOgxAonb68A0TCEG7rj1x7v7rxNElsE8EVIKIEUTvyDtU+sthYSbX+8e7g3oZiLGnOmxw=="],
"@napi-rs/canvas-linux-x64-gnu": ["@napi-rs/canvas-linux-x64-gnu@1.0.2", "", { "os": "linux", "cpu": "x64" }, "sha512-nPR97DXhbWIAy7yazF3jc06kEPMqYMLmPzFOVNlwKPfIoSChnI+x7dc0hTLaihz3jxrjL6j4BbA7earxfx4X3g=="],
"@napi-rs/canvas-linux-x64-musl": ["@napi-rs/canvas-linux-x64-musl@1.0.2", "", { "os": "linux", "cpu": "x64" }, "sha512-l7zZY5+jL5qnBZtDz7CoBtY6p7EkHu422g/0zWwrOrzIwWyWxZFRfZZORY1UG7YApymPLx+UbOkN206xXn/c1Q=="],
"@napi-rs/canvas-win32-arm64-msvc": ["@napi-rs/canvas-win32-arm64-msvc@1.0.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-yE0koHCFF4PIbMc2o2SEALhnipz7WBISh5glLvQiomtIoCcW0np3H4Lw93ceJAfJttTTeIIWFbwH84F7EVzjMQ=="],
"@napi-rs/canvas-win32-x64-msvc": ["@napi-rs/canvas-win32-x64-msvc@1.0.2", "", { "os": "win32", "cpu": "x64" }, "sha512-okU8/t2foV6C31n0GtvEMbfD5rOFc70+/6xUNME9Guld29sgSOIGUEDScAWFlcP3k5TYQRl9TNkwJEEjh15w8A=="],
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="],
"@noble/hashes": ["@noble/hashes@2.2.0", "", {}, "sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg=="],
@@ -2278,7 +2302,7 @@
"@oxc-parser/binding-win32-x64-msvc": ["@oxc-parser/binding-win32-x64-msvc@0.127.0", "", { "os": "win32", "cpu": "x64" }, "sha512-rOrnSQSCbhI2kowr9XxE7m9a8oQXnBHjnS6j95LxxAnEZ0+Fz20WlRXG4ondQb+ejjt2KOsa65sE6++L6kUd+w=="],
"@oxc-project/types": ["@oxc-project/types@0.127.0", "", {}, "sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ=="],
"@oxc-project/types": ["@oxc-project/types@0.138.0", "", {}, "sha512-1a7ZKmrRTCoN1XMZ4L0PyyqrMnrNlLyPuOkdSX2MZg7IiIGRUyurNhAm73ptDOraoBcIordsIGKNPKUzy3ZmfA=="],
"@oxc-resolver/binding-android-arm-eabi": ["@oxc-resolver/binding-android-arm-eabi@11.20.0", "", { "os": "android", "cpu": "arm" }, "sha512-IjfWOXRgJFNdORDl+Uf1aibNgZY2guOD3zmOhx1BGVb/MIiqlFTdmjpQNplSN58lhWehnX4UNqC3QwpUo8pjJg=="],
@@ -2570,7 +2594,37 @@
"@remix-run/router": ["@remix-run/router@1.9.0", "", {}, "sha512-bV63itrKBC0zdT27qYm6SDZHlkXwFL1xMBuhkn+X7l0+IIhNaH5wuuvZKp6eKhCD4KFhujhfhCT1YxXW6esUIA=="],
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="],
"@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.1.4", "", { "os": "android", "cpu": "arm64" }, "sha512-EZLpf/8y7GXkkra90ML47kzik/GMP3EMcE9bPyHmRfxLC6z9+aW5A8poCsoxjrT5GfEcNAAvWwUHjvP1pUQkfw=="],
"@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.1.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-aUi+HBvmYb7j8krl1+qJgkG8C17fO79gk3c+jPw4S8glRFc1DTija9S3EyaTSQUm5GJXYKDAsugBEhFHH2vYiQ=="],
"@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.1.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-F7hHC3gwY11+vByKPRWqwGbeXWVgKmL+pTGCinaEhdihzBV2aQ0fvZOch9cXYUOKuKKq429HeYXOqQLc7wFCEg=="],
"@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.1.4", "", { "os": "freebsd", "cpu": "x64" }, "sha512-sI5yw+7s92SK6odiEhD5lKCBlWcpjHS5qyqpVQbZAJ0fIzEUXrmbl3DH2ybR3PZogulNJF+COLtmA8hUfvkCCQ=="],
"@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.1.4", "", { "os": "linux", "cpu": "arm" }, "sha512-mCi0OKgEieFircrtVYmQAFGszRtMnZ6fpZAXrxanXAu7lqZcsK1E1RAaZNG0uKAnxox3B1f4EyQNnoyMfN1vAA=="],
"@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.1.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-B9Ial3Kv5sh0SHnB1g/QWcUQCEvCF6QKGAl4zXypYj65mVI+B4AhFBwPtSN7pDrJeIx8Z7zdy4ntx+wQABom7w=="],
"@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.1.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-lZVym0PuHE1KZ22gmFTC15lAkrg9iTszR617oYRB/iPY1A56ywoJzVKOJBKaot5RiikCObmur6pogpse3gRcng=="],
"@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.1.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-t2DNiLJWNTbnEHyUzTumldML6ET4/g16467LZoDDJ3tSxGvguL5/NyC2lCsNKuyRycg9XeDQF5SSv+TNOhQEXg=="],
"@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.1.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-0WIRnL1Uw4BvTZRLQt+PVgo6ZKTJadlC2btP+/EOXv2f/DWbY0rEgl+y834mIVwP1FkTlWVTrGGJXf12lru7EQ=="],
"@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.1.4", "", { "os": "linux", "cpu": "x64" }, "sha512-JWtGshGfX+oENAKonoNkqEJX+7hC8yfhi9GUyPX1VX4mdh1y5r+ZiJLR5XzAB0aoP6s/PcILsGjKq8O0mm24bw=="],
"@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.1.4", "", { "os": "linux", "cpu": "x64" }, "sha512-rT6yQcxUuXs4CnbofqwHRRV0iem349rLMYpTjkgQGLjrY4ado/eDzwPZPTCgTOlF6Nkp8NEv70yLMTn6qkWxsQ=="],
"@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.1.4", "", { "os": "none", "cpu": "arm64" }, "sha512-KXMGoboq5cyaCQjDA4GLuRiOwBQ0EyFnJoVViLeZ45/3rFItRODEr+NdsBcVpll40hhNArlm/speWGRvj08LzA=="],
"@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.1.4", "", { "dependencies": { "@emnapi/core": "1.11.1", "@emnapi/runtime": "1.11.1", "@napi-rs/wasm-runtime": "^1.1.6" }, "cpu": "none" }, "sha512-5K83rb36oJiY7BCyE9zLZtGcPV4g5wvq+xwdO0XPIwDVZI8cyB/AUjkNXGb92/rnmezEkjMOpgY61rtwjQtFwg=="],
"@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.1.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-PnWBtw3TV5KOg69HQQDR0mnQuyCmSGR2pAB4DC1rPF808fgKeTUMj2EOEyKATpgiuxuR5APQmiDO7PDgEjTFSA=="],
"@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.1.4", "", { "os": "win32", "cpu": "x64" }, "sha512-M1lpniBePobTfsa7Ks9a199e1akxsXn+GYBUKsEzv3YFzOm1HJAMNwKI3qr0Zq+mxwx9gOZoTdP1yXRYsZUocQ=="],
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.1", "", {}, "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw=="],
"@rollup/pluginutils": ["@rollup/pluginutils@5.4.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg=="],
@@ -3112,6 +3166,8 @@
"@types/react": ["@types/react@18.0.25", "", { "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", "csstype": "^3.0.2" } }, "sha512-xD6c0KDT4m7n9uD4ZHi02lzskaiqcBxf4zi+tXZY98a04wvc0hi/TcCPC2FOESZi51Nd7tlUeOJY8RofL799/g=="],
"@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="],
"@types/responselike": ["@types/responselike@1.0.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw=="],
"@types/retry": ["@types/retry@0.12.0", "", {}, "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="],
@@ -3184,6 +3240,12 @@
"@valibot/to-json-schema": ["@valibot/to-json-schema@1.6.0", "", { "peerDependencies": { "valibot": "^1.3.0" } }, "sha512-d6rYyK5KVa2XdqamWgZ4/Nr+cXhxjy7lmpe6Iajw15J/jmU+gyxl2IEd1Otg1d7Rl3gOQL5reulnSypzBtYy1A=="],
"@vercel/cli-config": ["@vercel/cli-config@0.2.0", "", { "dependencies": { "xdg-app-paths": "5", "zod": "4.1.11" } }, "sha512-fJRRRB7734BDuXZ89yBEaA2ncYhH7bWX30mk04W80J6VAfQc+4iB8lyzAdaGpFV3/vNlkt9VZt+/uoQoWX6UsQ=="],
"@vercel/cli-exec": ["@vercel/cli-exec@1.0.0", "", { "dependencies": { "execa": "5.1.1" } }, "sha512-kQF8LGie/Hbdq9/psJxLE7owRTcqMQMhgybU04gCeR7cbQAr5t8OrjefDNColJv1QSSucFt4pLwRiARVmlOnug=="],
"@vercel/functions": ["@vercel/functions@3.7.5", "", { "dependencies": { "@vercel/oidc": "3.8.0" }, "peerDependencies": { "@aws-sdk/credential-provider-web-identity": "*", "ws": ">=8" }, "optionalPeers": ["@aws-sdk/credential-provider-web-identity", "ws"] }, "sha512-ESf8BbeDebqRUyMi09JwRbQqpLn4g6fjcVVHPsHB56j2dSqRrSHO4h3X4aaxJf6iQQjzhAtDGI2xCWQ27JE8PA=="],
"@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=="],
@@ -4988,6 +5050,8 @@
"opentui-spinner": ["opentui-spinner@0.0.7", "", { "dependencies": { "cli-spinners": "^3.3.0" }, "peerDependencies": { "@opentui/core": "^0.3.4", "@opentui/react": "^0.3.4", "@opentui/solid": "^0.3.4", "typescript": "^5" }, "optionalPeers": ["@opentui/react", "@opentui/solid"] }, "sha512-nPzwAvJG+y9rVEwwHLHqbsMzLnIk2zw+F9LqwA7aYJvpM5gsrKC2rrGi36A+tZpA+1RnWxXeWEgVZMchnaH18Q=="],
"os-paths": ["os-paths@4.4.0", "", {}, "sha512-wrAwOeXp1RRMFfQY8Sy7VaGVmPocaLwSFOYCGKSyo8qmJ+/yaafCl5BCA1IQZWqFSRBrKDYFeR9d/VyQzfH/jg=="],
"own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="],
"oxc-minify": ["oxc-minify@0.96.0", "", { "optionalDependencies": { "@oxc-minify/binding-android-arm64": "0.96.0", "@oxc-minify/binding-darwin-arm64": "0.96.0", "@oxc-minify/binding-darwin-x64": "0.96.0", "@oxc-minify/binding-freebsd-x64": "0.96.0", "@oxc-minify/binding-linux-arm-gnueabihf": "0.96.0", "@oxc-minify/binding-linux-arm-musleabihf": "0.96.0", "@oxc-minify/binding-linux-arm64-gnu": "0.96.0", "@oxc-minify/binding-linux-arm64-musl": "0.96.0", "@oxc-minify/binding-linux-riscv64-gnu": "0.96.0", "@oxc-minify/binding-linux-s390x-gnu": "0.96.0", "@oxc-minify/binding-linux-x64-gnu": "0.96.0", "@oxc-minify/binding-linux-x64-musl": "0.96.0", "@oxc-minify/binding-wasm32-wasi": "0.96.0", "@oxc-minify/binding-win32-arm64-msvc": "0.96.0", "@oxc-minify/binding-win32-x64-msvc": "0.96.0" } }, "sha512-dXeeGrfPJJ4rMdw+NrqiCRtbzVX2ogq//R0Xns08zql2HjV3Zi2SBJ65saqfDaJzd2bcHqvGWH+M44EQCHPAcA=="],
@@ -5402,6 +5466,8 @@
"roarr": ["roarr@2.15.4", "", { "dependencies": { "boolean": "^3.0.1", "detect-node": "^2.0.4", "globalthis": "^1.0.1", "json-stringify-safe": "^5.0.1", "semver-compare": "^1.0.0", "sprintf-js": "^1.1.2" } }, "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A=="],
"rolldown": ["rolldown@1.1.4", "", { "dependencies": { "@oxc-project/types": "=0.138.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.1.4", "@rolldown/binding-darwin-arm64": "1.1.4", "@rolldown/binding-darwin-x64": "1.1.4", "@rolldown/binding-freebsd-x64": "1.1.4", "@rolldown/binding-linux-arm-gnueabihf": "1.1.4", "@rolldown/binding-linux-arm64-gnu": "1.1.4", "@rolldown/binding-linux-arm64-musl": "1.1.4", "@rolldown/binding-linux-ppc64-gnu": "1.1.4", "@rolldown/binding-linux-s390x-gnu": "1.1.4", "@rolldown/binding-linux-x64-gnu": "1.1.4", "@rolldown/binding-linux-x64-musl": "1.1.4", "@rolldown/binding-openharmony-arm64": "1.1.4", "@rolldown/binding-wasm32-wasi": "1.1.4", "@rolldown/binding-win32-arm64-msvc": "1.1.4", "@rolldown/binding-win32-x64-msvc": "1.1.4" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-IjZYiLxZwpnhwhdBH2ugdTGVSdhCQUmLxLoqyjiL0JxYjyRst+5a0P3xfrTxJ5F638j4Mvvw5FAX5XE6eHpXbA=="],
"rollup": ["rollup@4.60.4", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.60.4", "@rollup/rollup-android-arm64": "4.60.4", "@rollup/rollup-darwin-arm64": "4.60.4", "@rollup/rollup-darwin-x64": "4.60.4", "@rollup/rollup-freebsd-arm64": "4.60.4", "@rollup/rollup-freebsd-x64": "4.60.4", "@rollup/rollup-linux-arm-gnueabihf": "4.60.4", "@rollup/rollup-linux-arm-musleabihf": "4.60.4", "@rollup/rollup-linux-arm64-gnu": "4.60.4", "@rollup/rollup-linux-arm64-musl": "4.60.4", "@rollup/rollup-linux-loong64-gnu": "4.60.4", "@rollup/rollup-linux-loong64-musl": "4.60.4", "@rollup/rollup-linux-ppc64-gnu": "4.60.4", "@rollup/rollup-linux-ppc64-musl": "4.60.4", "@rollup/rollup-linux-riscv64-gnu": "4.60.4", "@rollup/rollup-linux-riscv64-musl": "4.60.4", "@rollup/rollup-linux-s390x-gnu": "4.60.4", "@rollup/rollup-linux-x64-gnu": "4.60.4", "@rollup/rollup-linux-x64-musl": "4.60.4", "@rollup/rollup-openbsd-x64": "4.60.4", "@rollup/rollup-openharmony-arm64": "4.60.4", "@rollup/rollup-win32-arm64-msvc": "4.60.4", "@rollup/rollup-win32-ia32-msvc": "4.60.4", "@rollup/rollup-win32-x64-gnu": "4.60.4", "@rollup/rollup-win32-x64-msvc": "4.60.4", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g=="],
"rou3": ["rou3@0.7.12", "", {}, "sha512-iFE4hLDuloSWcD7mjdCDhx2bKcIsYbtOTpfH5MHHLSKMOUyjqQXTeZVa289uuwEGEKFoE/BAPbhaU4B774nceg=="],
@@ -6022,8 +6088,12 @@
"wsl-utils": ["wsl-utils@0.3.1", "", { "dependencies": { "is-wsl": "^3.1.0", "powershell-utils": "^0.1.0" } }, "sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg=="],
"xdg-app-paths": ["xdg-app-paths@5.5.1", "", { "dependencies": { "os-paths": "^4.0.1", "xdg-portable": "^7.2.0" } }, "sha512-hI3flOB4PLZIy5prbtTpirobtPE2ZtZ52szO+2mM9Efp6ErM398La+C1lIpNWDfNoQk+6Lsi6nMcCwVB7pxeMQ=="],
"xdg-basedir": ["xdg-basedir@5.1.0", "", {}, "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ=="],
"xdg-portable": ["xdg-portable@7.3.0", "", { "dependencies": { "os-paths": "^4.0.1" } }, "sha512-sqMMuL1rc0FmMBOzCpd0yuy9trqF2yTTVe+E9ogwCSWQCdDEtQUwrZPT6AxqtsFGRNxycgncbP/xmOOSPw5ZUw=="],
"xml-naming": ["xml-naming@0.1.0", "", {}, "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw=="],
"xml2js": ["xml2js@0.5.0", "", { "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" } }, "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA=="],
@@ -6620,6 +6690,10 @@
"@oslojs/jwt/@oslojs/encoding": ["@oslojs/encoding@0.4.1", "", {}, "sha512-hkjo6MuIK/kQR5CrGNdAPZhS01ZCXuWDRJ187zh6qqF2+yMHZpD9fAYpX8q2bOO6Ryhl3XpCT6kUX76N8hhm4Q=="],
"@oxc-parser/binding-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.9.2", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA=="],
"@oxc-parser/binding-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.9.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw=="],
"@oxc-resolver/binding-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="],
"@oxc-resolver/binding-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="],
@@ -6640,6 +6714,8 @@
"@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=="],
"@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=="],
"@sentry/bundler-plugin-core/glob": ["glob@9.3.5", "", { "dependencies": { "fs.realpath": "^1.0.0", "minimatch": "^8.0.2", "minipass": "^4.2.4", "path-scurry": "^1.6.1" } }, "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q=="],
@@ -6748,6 +6824,14 @@
"@types/plist/xmlbuilder": ["xmlbuilder@15.1.1", "", {}, "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg=="],
"@vercel/cli-config/zod": ["zod@4.1.11", "", {}, "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg=="],
"@vercel/cli-exec/execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="],
"@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=="],
"@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=="],
"@vitest/coverage-v8/magicast": ["magicast@0.5.3", "", { "dependencies": { "@babel/parser": "^7.29.3", "@babel/types": "^7.29.0", "source-map-js": "^1.2.1" } }, "sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw=="],
@@ -7044,6 +7128,8 @@
"openid-client/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="],
"oxc-parser/@oxc-project/types": ["@oxc-project/types@0.127.0", "", {}, "sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ=="],
"p-any/p-cancelable": ["p-cancelable@3.0.0", "", {}, "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw=="],
"p-locate/p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
@@ -7708,6 +7794,10 @@
"@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=="],
"@oxc-resolver/binding-wasm32-wasi/@emnapi/core/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="],
"@pierre/diffs/@shikijs/transformers/@shikijs/core": ["@shikijs/core@3.20.0", "", { "dependencies": { "@shikijs/types": "3.20.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-f2ED7HYV4JEk827mtMDwe/yQ25pRiXZmtHjWF8uzZKuKiEsJR7Ce1nuQ+HhV9FzDcbIo4ObBCD9GPTzNuy9S1g=="],
"@pierre/diffs/@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=="],
@@ -7716,6 +7806,8 @@
"@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=="],
"@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=="],
"@sentry/bundler-plugin-core/glob/minipass": ["minipass@4.2.8", "", {}, "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ=="],
@@ -7786,6 +7878,22 @@
"@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=="],
"@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=="],
"@vercel/cli-exec/execa/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
"@vercel/cli-exec/execa/npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="],
"@vercel/cli-exec/execa/onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="],
"@vercel/cli-exec/execa/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
"@vercel/cli-exec/execa/strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="],
"@vercel/functions/@vercel/oidc/jose": ["jose@5.9.6", "", {}, "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ=="],
"@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=="],
@@ -8352,6 +8460,8 @@
"@stoplight/spectral-core/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
"@vercel/cli-exec/execa/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
"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=="],
@@ -20,7 +20,7 @@ const profiles = [
{ name: "edit", tool: "edit", input: { filePath: "src/edit.ts" } },
{
name: "multi patch",
tool: "apply_patch",
tool: "patch",
input: { files: ["src/a.ts", "src/b.ts", "src/old.ts", "src/moved.ts"] },
},
] as const
@@ -25,7 +25,7 @@ test("adds patch files incrementally without resetting outer expansion", async (
userMessage(),
assistantMessage(
[
toolPart(patchID, "apply_patch", "running", { files: [first.filePath] }, { metadata: { files: [first] } }),
toolPart(patchID, "patch", "running", { files: [first.filePath] }, { metadata: { files: [first] } }),
textPart(followingID, "Following incremental patch"),
],
{ completed: false },
@@ -49,7 +49,7 @@ test("adds patch files incrementally without resetting outer expansion", async (
partUpdated(
toolPart(
patchID,
"apply_patch",
"patch",
"running",
{ files: [first.filePath, second.filePath] },
{ metadata: { files: [first, second] } },
@@ -61,7 +61,7 @@ test("adds patch files incrementally without resetting outer expansion", async (
partUpdated(
toolPart(
patchID,
"apply_patch",
"patch",
"completed",
{ files: [first.filePath, second.filePath, third.filePath] },
{ metadata: { files: [first, second, third] } },
@@ -295,7 +295,7 @@ function performanceTurn(index: number) {
messageID: assistantID,
type: "tool",
callID: `call_0000_${suffix}_patch`,
tool: "apply_patch",
tool: "patch",
state: {
status: "completed",
input: { patchText: realisticPatch(index) },
@@ -131,7 +131,7 @@ function toolPart(
): MessagePart {
const metadata =
metadataOverride ??
(tool === "apply_patch"
(tool === "patch"
? { files: [patchFile(index, "update"), patchFile(index + 1, index % 2 === 0 ? "add" : "delete")] }
: tool === "edit" || tool === "write"
? {
@@ -219,7 +219,7 @@ function turn(index: number): Message[] {
? [toolPart(index, 7, "write", { filePath: `src/generated/write-${index}.ts`, content: code(index, 28) }, 560)]
: []),
...(index % 8 === 0
? [toolPart(index, 8, "apply_patch", { files: [`src/generated/patch-${index}.ts`] }, 620)]
? [toolPart(index, 8, "patch", { files: [`src/generated/patch-${index}.ts`] }, 620)]
: []),
...(index % 7 === 0
? [toolPart(index, 4, "bash", { command: "bun typecheck", description: "Verify generated output" }, 620)]
@@ -24,7 +24,7 @@ test("renders a completed single-file patch", async ({ page }) => {
assistantMessage([
toolPart(
id,
"apply_patch",
"patch",
"completed",
{ files: ["src/a.ts"] },
{
@@ -35,7 +35,7 @@ test("preserves nested patch file state through outer collapse and reopen", asyn
assistantMessage([
toolPart(
patchID,
"apply_patch",
"patch",
"completed",
{ files: files.map((file) => file.filePath) },
{ metadata: { files } },
@@ -246,7 +246,7 @@ function editPart(id: string) {
function patchPart(id: string) {
return toolPart(
id,
"apply_patch",
"patch",
"completed",
{ files: ["src/a.ts", "src/b.ts"] },
{
@@ -8,7 +8,7 @@ import {
} from "../performance/timeline-stability/fixture"
test("renders every tool error outcome without leaking hidden tools", async ({ page }) => {
const ordinary = ["bash", "edit", "write", "apply_patch", "webfetch", "websearch", "task", "skill", "mcp_probe"]
const ordinary = ["bash", "edit", "write", "patch", "webfetch", "websearch", "task", "skill", "mcp_probe"]
const parts = ordinary.map((tool, index) =>
toolPart(`prt_error_${index}`, tool, "error", errorInput(tool), { error: `${tool} failed visibly` }),
)
@@ -90,7 +90,7 @@ function questionInput() {
function errorInput(tool: string) {
if (tool === "bash") return { command: "exit 1" }
if (["edit", "write"].includes(tool)) return { filePath: "src/error.ts", content: "" }
if (tool === "apply_patch") return { files: ["src/error.ts"] }
if (tool === "patch") return { files: ["src/error.ts"] }
if (tool === "webfetch") return { url: "https://example.com" }
if (tool === "websearch") return { query: "failure" }
if (tool === "task") return { description: "Fail task", subagent_type: "explore" }
@@ -120,7 +120,7 @@ function toolPart(
outputLength = 160,
): MessagePart {
const metadata =
tool === "apply_patch"
tool === "patch"
? { files: [patchFile(index, "update"), patchFile(index + 1, index % 2 === 0 ? "add" : "delete")] }
: tool === "edit" || tool === "write"
? {
@@ -199,7 +199,7 @@ function turn(index: number): Message[] {
? [toolPart(index, 7, "write", { filePath: `src/generated/write-${index}.ts`, content: code(index, 28) }, 560)]
: []),
...(index % 8 === 0
? [toolPart(index, 8, "apply_patch", { files: [`src/generated/patch-${index}.ts`] }, 620)]
? [toolPart(index, 8, "patch", { files: [`src/generated/patch-${index}.ts`] }, 620)]
: []),
...(index % 7 === 0 ? [toolPart(index, 4, "bash", { command: "bun typecheck" }, 620)] : []),
...(index % 10 === 0 ? [toolPart(index, 9, "webfetch", { url: "https://example.com/docs/sample" }, 120)] : []),
+1 -1
View File
@@ -55,7 +55,7 @@ export async function mockOpenCodeServer(page: Page, config: MockServerConfig) {
const path = url.pathname
if (path === "/global/event" || path === "/event") return sse(route, config.events?.(), config.eventRetry)
if (path === "/global/health") return json(route, { healthy: true })
if (path === "/experimental/capabilities") return json(route, { backgroundSubagents: false })
if (path === "/experimental/capabilities") return json(route, { backgroundSubagents: true })
if (path === "/permission")
return json(route, typeof config.permissions === "function" ? config.permissions() : (config.permissions ?? []))
if (path === "/question")
@@ -1245,7 +1245,7 @@ export function MessageTimeline(props: {
const value = row()
if (value._tag !== "AssistantPart" || value.group.type !== "part") return false
const part = getMsgPart(value.group.ref.messageID, value.group.ref.partID)
return part?.type === "tool" && ["edit", "write", "apply_patch"].includes(part.tool)
return part?.type === "tool" && ["edit", "write", "patch", "apply_patch"].includes(part.tool)
}
const [ready, setReady] = createSignal(initialItem.size <= timelineFallbackItemSize || !asyncFile())
let contentMeasureFrame: number | undefined
Regular → Executable
View File
-1
View File
@@ -33,7 +33,6 @@
"dependencies": {
"@effect/platform-node": "catalog:",
"@opencode-ai/client": "workspace:*",
"@opencode-ai/codemode": "workspace:*",
"@opencode-ai/core": "workspace:*",
"@opencode-ai/plugin": "workspace:*",
"@opencode-ai/schema": "workspace:*",
-66
View File
@@ -1,66 +0,0 @@
export * as CodeModeHost from "./code-mode"
import { NodeHttpClient } from "@effect/platform-node"
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { ToolRegistry } from "@opencode-ai/core/tool/registry"
import { OpenAPI, Tool } from "@opencode-ai/codemode"
import { Api } from "@opencode-ai/server/api"
import { ServerAuth } from "@opencode-ai/server/auth"
import { Effect, Layer } from "effect"
import { HttpClient, HttpClientRequest } from "effect/unstable/http"
import { OpenApi } from "effect/unstable/httpapi"
import type { Server } from "node:http"
export function replacements(server: Server, password: string): LayerNode.Replacements {
return [ToolRegistry.codeModeReplacement(makeTools(client(server), password))]
}
export function makeTools(client: Layer.Layer<HttpClient.HttpClient>, password: string): ToolRegistry.CodeModeTools {
return {
opencode: bindTools(
OpenAPI.fromSpec({
spec: { ...OpenApi.fromApi(Api) },
baseUrl: "http://opencode.local",
headers: ServerAuth.headers({ username: "opencode", password }),
}).tools,
client,
),
}
}
function client(server: Server) {
return Layer.effect(
HttpClient.HttpClient,
Effect.gen(function* () {
const client = yield* HttpClient.HttpClient
return HttpClient.mapRequest(client, (request) => {
const address = server.address()
if (!address || typeof address === "string") throw new Error("OpenCode server is not listening")
const local =
address.address === "0.0.0.0" ? "127.0.0.1" : address.address === "::" ? "::1" : address.address
const host = local.includes(":") && !local.startsWith("[") ? `[${local}]` : local
const url = new URL(request.url)
return HttpClientRequest.setUrl(
request,
new URL(`${url.pathname}${url.search}${url.hash}`, `http://${host}:${address.port}`),
)
})
}),
).pipe(Layer.provide(NodeHttpClient.layerNodeHttp))
}
function bindTools(tools: OpenAPI.Tools, client: Layer.Layer<HttpClient.HttpClient>): ToolRegistry.CodeModeTools {
return Object.fromEntries(
Object.entries(tools).map(([name, value]) => [
name,
Tool.isDefinition<HttpClient.HttpClient>(value)
? Tool.make({
description: value.description,
input: value.input,
output: value.output,
run: (input) => value.run(input).pipe(Effect.provide(client)),
})
: bindTools(value, client),
]),
)
}
+2 -2
View File
@@ -628,11 +628,11 @@ function emitEdit(state: State): void {
function emitPatch(state: State): void {
const file = path.join(process.cwd(), "src", "demo-format.ts")
const ref = make(state, "apply_patch", {
const ref = make(state, "patch", {
patchText: "*** Begin Patch\n*** End Patch",
})
doneTool(state, ref, {
title: "apply_patch",
title: "patch",
output: "",
metadata: {
files: [
-2
View File
@@ -84,7 +84,6 @@ type RunFooterOptions = {
theme: RunTheme
keymap: Keymap<Renderable, KeyEvent>
tuiConfig: RunTuiConfig
backgroundSubagents: boolean
diffStyle: RunDiffStyle
onPermissionReply: (input: PermissionReply) => void | Promise<void>
onQuestionReply: (input: QuestionReply) => void | Promise<void>
@@ -326,7 +325,6 @@ export class RunFooter implements FooterApi {
theme: footer.theme,
diffStyle: options.diffStyle,
tuiConfig: options.tuiConfig,
backgroundSubagents: options.backgroundSubagents,
history: footer.history,
agent: options.agentLabel,
onSubmit: footer.handlePrompt,
+1 -4
View File
@@ -89,7 +89,6 @@ type RunFooterViewProps = {
theme: () => RunTheme
diffStyle?: RunDiffStyle
tuiConfig: RunTuiConfig
backgroundSubagents: boolean
history?: () => RunPrompt[]
agent: string
onSubmit: (input: RunPrompt) => boolean
@@ -169,9 +168,7 @@ export function RunFooterView(props: RunFooterViewProps) {
return tabs().findIndex((item) => item.sessionID === sessionID) + 1
})
const foregroundSubagents = createMemo(
() => props.backgroundSubagents && activeTabs().some((item) => !item.background),
)
const foregroundSubagents = createMemo(() => activeTabs().some((item) => !item.background))
const model = createMemo(() => {
const current = props.currentModel()
return current ? modelInfo(props.providers(), current) : { model: props.state().model, provider: undefined }
-4
View File
@@ -1,6 +1,5 @@
import { NodeFileSystem } from "@effect/platform-node"
import { OpenCode, type OpenCodeClient } from "@opencode-ai/client/promise"
import { truthy } from "@opencode-ai/core/flag/flag"
import { Global } from "@opencode-ai/core/global"
import { Effect } from "effect"
import path from "node:path"
@@ -84,9 +83,6 @@ export async function runMini(input: MiniCommandInput) {
files: [],
initialInput,
thinking: true,
backgroundSubagents:
truthy("OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS") ||
(process.env.OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS === undefined && truthy("OPENCODE_EXPERIMENTAL")),
replay: input.replay ?? true,
replayLimit: input.replayLimit,
demo: input.demo,
@@ -64,7 +64,6 @@ export type LifecycleInput = {
model: RunInput["model"]
variant: string | undefined
tuiConfig: RunTuiConfig | Promise<RunTuiConfig>
backgroundSubagents: boolean
onPermissionReply: (input: PermissionReply) => void | Promise<void>
onQuestionReply: (input: QuestionReply) => void | Promise<void>
onQuestionReject: (input: QuestionReject) => void | Promise<void>
@@ -236,7 +235,6 @@ export async function createRuntimeLifecycle(input: LifecycleInput): Promise<Lif
wrote,
keymap,
tuiConfig,
backgroundSubagents: input.backgroundSubagents,
diffStyle: tuiConfig.diff_style ?? "auto",
onPermissionReply: input.onPermissionReply,
onQuestionReply: input.onQuestionReply,
-5
View File
@@ -56,7 +56,6 @@ type RunRuntimeInput = {
files: RunInput["files"]
initialInput?: string
thinking: boolean
backgroundSubagents: boolean
replay?: boolean
replayLimit?: number
demo?: RunInput["demo"]
@@ -75,7 +74,6 @@ type RunDeferredInput = {
files: RunInput["files"]
initialInput?: string
thinking: boolean
backgroundSubagents: boolean
replay?: boolean
replayLimit?: number
demo?: RunInput["demo"]
@@ -270,7 +268,6 @@ async function runInteractiveRuntime(input: RunRuntimeInput, deps: RunRuntimeDep
model: state.model,
variant: state.activeVariant,
tuiConfig: tuiConfigTask,
backgroundSubagents: input.backgroundSubagents,
onPermissionReply: async (next) => {
if (state.demo?.permission(next)) {
return
@@ -876,7 +873,6 @@ export async function runInteractiveDeferredMode(input: RunDeferredInput, deps?:
files: input.files,
initialInput: input.initialInput,
thinking: input.thinking,
backgroundSubagents: input.backgroundSubagents,
replay: input.replay,
replayLimit: input.replayLimit,
demo: input.demo,
@@ -928,7 +924,6 @@ export async function runInteractiveMode(
files: input.files,
initialInput: input.initialInput,
thinking: input.thinking,
backgroundSubagents: input.backgroundSubagents,
replay: input.replay,
replayLimit: input.replayLimit,
demo: input.demo,
+2 -2
View File
@@ -119,7 +119,7 @@ type ToolName =
| "bash"
| "write"
| "edit"
| "apply_patch"
| "patch"
| "batch"
| "task"
| "todowrite"
@@ -1094,7 +1094,7 @@ const TOOL_RULES = {
},
permission: permEdit,
},
apply_patch: {
patch: {
view: {
output: false,
final: true,
-1
View File
@@ -135,7 +135,6 @@ export type RunInput = {
files: RunFilePart[]
initialInput?: string
thinking: boolean
backgroundSubagents: boolean
demo?: boolean
}
-11
View File
@@ -7,13 +7,11 @@ import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { Global } from "@opencode-ai/core/global"
import { InstallationVersion } from "@opencode-ai/core/installation/version"
import { AppProcess } from "@opencode-ai/core/process"
import { Flock } from "@opencode-ai/core/util/flock"
import { start } from "@opencode-ai/server/process"
import { randomBytes, randomUUID } from "node:crypto"
import path from "node:path"
import { Effect, FileSystem, Logger, Option, Redacted, Schedule, Schema } from "effect"
import { HttpServer } from "effect/unstable/http"
import { CodeModeHost } from "./code-mode"
import { Env } from "./env"
import { ServiceConfig } from "./services/service-config"
import { Updater } from "./services/updater"
@@ -38,14 +36,6 @@ const processEffect = Effect.fnUntraced(function* (options: Options) {
if (options.mode === "service") yield* Effect.sync(() => process.chdir(Global.Path.home))
return yield* Effect.scoped(
Effect.gen(function* () {
if (options.mode === "service") {
const service = yield* ServiceConfig.options()
yield* Flock.effect(path.basename(service.file, ".json") + "-process", {
dir: path.dirname(service.file),
staleMs: 3_000,
timeoutMs: 15_000,
})
}
const environmentPassword = yield* Env.password
// Keep the lease credential out of the environment inherited by tools.
if (options.mode === "stdio") {
@@ -64,7 +54,6 @@ const processEffect = Effect.fnUntraced(function* (options: Options) {
hostname: options.hostname ?? config.hostname ?? "127.0.0.1",
port: Option.fromNullishOr(options.port ?? config.port),
password,
replacements: (server) => CodeModeHost.replacements(server, password),
}).pipe(Effect.provide(Logger.layer([], { mergeWithExisting: false })))
if (options.mode === "service") yield* register(address, password)
const url = HttpServer.formatAddress(address)
-39
View File
@@ -1,39 +0,0 @@
import { expect, test } from "bun:test"
import { CodeMode } from "@opencode-ai/codemode"
import { Effect, Layer } from "effect"
import { HttpClient, HttpClientResponse } from "effect/unstable/http"
import { CodeModeHost } from "../src/code-mode"
test("exposes the authenticated OpenCode API through CodeMode", async () => {
const requests: Array<{ readonly url: string; readonly authorization?: string }> = []
const client = Layer.succeed(
HttpClient.HttpClient,
HttpClient.make((request) => {
requests.push({ url: request.url, authorization: request.headers.authorization })
return Effect.succeed(
HttpClientResponse.fromWeb(
request,
Response.json(
{ healthy: true, version: "test", pid: 1 },
{ headers: { "content-type": "application/json" } },
),
),
)
}),
)
const result = await CodeMode.make({ tools: CodeModeHost.makeTools(client, "secret") })
.execute("return await tools.opencode.v2.health.get({})")
.pipe(Effect.runPromise)
expect(result).toEqual({
ok: true,
value: { healthy: true, version: "test", pid: 1 },
toolCalls: [{ name: "opencode.v2.health.get" }],
})
expect(requests).toEqual([
{
url: "http://opencode.local/api/health",
authorization: `Basic ${Buffer.from("opencode:secret").toString("base64")}`,
},
])
})
+2 -3
View File
@@ -3,15 +3,14 @@ import { compile, emitEffectImported, emitEffectShape, emitPromise, write } from
import {
ClientApi,
effectOmitEndpoints,
endpointNames,
groupNames,
promiseOmitEndpoints,
} from "@opencode-ai/protocol/client"
import { Effect } from "effect"
import { fileURLToPath } from "url"
const promiseContract = compile(ClientApi, { groupNames, endpointNames, omitEndpoints: promiseOmitEndpoints })
const effectContract = compile(ClientApi, { groupNames, endpointNames, omitEndpoints: effectOmitEndpoints })
const promiseContract = compile(ClientApi, { groupNames, omitEndpoints: promiseOmitEndpoints })
const effectContract = compile(ClientApi, { groupNames, omitEndpoints: effectOmitEndpoints })
await Effect.runPromise(
Effect.all(
-1
View File
@@ -1,7 +1,6 @@
export {
ClientApi,
effectOmitEndpoints,
endpointNames,
groupNames,
promiseOmitEndpoints,
} from "@opencode-ai/protocol/client"
+141 -112
View File
@@ -111,157 +111,167 @@ export type Endpoint4_8Input = {
export type Endpoint4_8Output = EffectValue<ReturnType<RawClient["server.session"]["session.rename"]>>
export type SessionRenameOperation<E = never> = (input: Endpoint4_8Input) => Effect.Effect<Endpoint4_8Output, E>
type Endpoint4_9Request = Parameters<RawClient["server.session"]["session.prompt"]>[0]
type Endpoint4_9Request = Parameters<RawClient["server.session"]["session.move"]>[0]
export type Endpoint4_9Input = {
readonly sessionID: Endpoint4_9Request["params"]["sessionID"]
readonly id?: Endpoint4_9Request["payload"]["id"]
readonly prompt: Endpoint4_9Request["payload"]["prompt"]
readonly delivery?: Endpoint4_9Request["payload"]["delivery"]
readonly resume?: Endpoint4_9Request["payload"]["resume"]
readonly destination: Endpoint4_9Request["payload"]["destination"]
readonly moveChanges?: Endpoint4_9Request["payload"]["moveChanges"]
}
export type Endpoint4_9Output = EffectValue<ReturnType<RawClient["server.session"]["session.prompt"]>>["data"]
export type SessionPromptOperation<E = never> = (input: Endpoint4_9Input) => Effect.Effect<Endpoint4_9Output, E>
export type Endpoint4_9Output = EffectValue<ReturnType<RawClient["server.session"]["session.move"]>>
export type SessionMoveOperation<E = never> = (input: Endpoint4_9Input) => Effect.Effect<Endpoint4_9Output, E>
type Endpoint4_10Request = Parameters<RawClient["server.session"]["session.command"]>[0]
type Endpoint4_10Request = Parameters<RawClient["server.session"]["session.prompt"]>[0]
export type Endpoint4_10Input = {
readonly sessionID: Endpoint4_10Request["params"]["sessionID"]
readonly id?: Endpoint4_10Request["payload"]["id"]
readonly command: Endpoint4_10Request["payload"]["command"]
readonly arguments?: Endpoint4_10Request["payload"]["arguments"]
readonly agent?: Endpoint4_10Request["payload"]["agent"]
readonly model?: Endpoint4_10Request["payload"]["model"]
readonly files?: Endpoint4_10Request["payload"]["files"]
readonly agents?: Endpoint4_10Request["payload"]["agents"]
readonly prompt: Endpoint4_10Request["payload"]["prompt"]
readonly delivery?: Endpoint4_10Request["payload"]["delivery"]
readonly resume?: Endpoint4_10Request["payload"]["resume"]
}
export type Endpoint4_10Output = EffectValue<ReturnType<RawClient["server.session"]["session.command"]>>["data"]
export type SessionCommandOperation<E = never> = (input: Endpoint4_10Input) => Effect.Effect<Endpoint4_10Output, E>
export type Endpoint4_10Output = EffectValue<ReturnType<RawClient["server.session"]["session.prompt"]>>["data"]
export type SessionPromptOperation<E = never> = (input: Endpoint4_10Input) => Effect.Effect<Endpoint4_10Output, E>
type Endpoint4_11Request = Parameters<RawClient["server.session"]["session.skill"]>[0]
type Endpoint4_11Request = Parameters<RawClient["server.session"]["session.command"]>[0]
export type Endpoint4_11Input = {
readonly sessionID: Endpoint4_11Request["params"]["sessionID"]
readonly id?: Endpoint4_11Request["payload"]["id"]
readonly skill: Endpoint4_11Request["payload"]["skill"]
readonly command: Endpoint4_11Request["payload"]["command"]
readonly arguments?: Endpoint4_11Request["payload"]["arguments"]
readonly agent?: Endpoint4_11Request["payload"]["agent"]
readonly model?: Endpoint4_11Request["payload"]["model"]
readonly files?: Endpoint4_11Request["payload"]["files"]
readonly agents?: Endpoint4_11Request["payload"]["agents"]
readonly delivery?: Endpoint4_11Request["payload"]["delivery"]
readonly resume?: Endpoint4_11Request["payload"]["resume"]
}
export type Endpoint4_11Output = EffectValue<ReturnType<RawClient["server.session"]["session.skill"]>>
export type SessionSkillOperation<E = never> = (input: Endpoint4_11Input) => Effect.Effect<Endpoint4_11Output, E>
export type Endpoint4_11Output = EffectValue<ReturnType<RawClient["server.session"]["session.command"]>>["data"]
export type SessionCommandOperation<E = never> = (input: Endpoint4_11Input) => Effect.Effect<Endpoint4_11Output, E>
type Endpoint4_12Request = Parameters<RawClient["server.session"]["session.synthetic"]>[0]
type Endpoint4_12Request = Parameters<RawClient["server.session"]["session.skill"]>[0]
export type Endpoint4_12Input = {
readonly sessionID: Endpoint4_12Request["params"]["sessionID"]
readonly text: Endpoint4_12Request["payload"]["text"]
readonly description?: Endpoint4_12Request["payload"]["description"]
readonly metadata?: Endpoint4_12Request["payload"]["metadata"]
readonly id?: Endpoint4_12Request["payload"]["id"]
readonly skill: Endpoint4_12Request["payload"]["skill"]
readonly resume?: Endpoint4_12Request["payload"]["resume"]
}
export type Endpoint4_12Output = EffectValue<ReturnType<RawClient["server.session"]["session.synthetic"]>>
export type SessionSyntheticOperation<E = never> = (input: Endpoint4_12Input) => Effect.Effect<Endpoint4_12Output, E>
export type Endpoint4_12Output = EffectValue<ReturnType<RawClient["server.session"]["session.skill"]>>
export type SessionSkillOperation<E = never> = (input: Endpoint4_12Input) => Effect.Effect<Endpoint4_12Output, E>
type Endpoint4_13Request = Parameters<RawClient["server.session"]["session.shell"]>[0]
type Endpoint4_13Request = Parameters<RawClient["server.session"]["session.synthetic"]>[0]
export type Endpoint4_13Input = {
readonly sessionID: Endpoint4_13Request["params"]["sessionID"]
readonly id?: Endpoint4_13Request["payload"]["id"]
readonly command: Endpoint4_13Request["payload"]["command"]
readonly text: Endpoint4_13Request["payload"]["text"]
readonly description?: Endpoint4_13Request["payload"]["description"]
readonly metadata?: Endpoint4_13Request["payload"]["metadata"]
readonly resume?: Endpoint4_13Request["payload"]["resume"]
}
export type Endpoint4_13Output = EffectValue<ReturnType<RawClient["server.session"]["session.shell"]>>
export type SessionShellOperation<E = never> = (input: Endpoint4_13Input) => Effect.Effect<Endpoint4_13Output, E>
export type Endpoint4_13Output = EffectValue<ReturnType<RawClient["server.session"]["session.synthetic"]>>
export type SessionSyntheticOperation<E = never> = (input: Endpoint4_13Input) => Effect.Effect<Endpoint4_13Output, E>
type Endpoint4_14Request = Parameters<RawClient["server.session"]["session.compact"]>[0]
type Endpoint4_14Request = Parameters<RawClient["server.session"]["session.shell"]>[0]
export type Endpoint4_14Input = {
readonly sessionID: Endpoint4_14Request["params"]["sessionID"]
readonly id?: Endpoint4_14Request["payload"]["id"]
readonly command: Endpoint4_14Request["payload"]["command"]
}
export type Endpoint4_14Output = EffectValue<ReturnType<RawClient["server.session"]["session.compact"]>>["data"]
export type SessionCompactOperation<E = never> = (input: Endpoint4_14Input) => Effect.Effect<Endpoint4_14Output, E>
export type Endpoint4_14Output = EffectValue<ReturnType<RawClient["server.session"]["session.shell"]>>
export type SessionShellOperation<E = never> = (input: Endpoint4_14Input) => Effect.Effect<Endpoint4_14Output, E>
type Endpoint4_15Request = Parameters<RawClient["server.session"]["session.wait"]>[0]
export type Endpoint4_15Input = { readonly sessionID: Endpoint4_15Request["params"]["sessionID"] }
export type Endpoint4_15Output = EffectValue<ReturnType<RawClient["server.session"]["session.wait"]>>
export type SessionWaitOperation<E = never> = (input: Endpoint4_15Input) => Effect.Effect<Endpoint4_15Output, E>
type Endpoint4_16Request = Parameters<RawClient["server.session"]["session.revert.stage"]>[0]
export type Endpoint4_16Input = {
readonly sessionID: Endpoint4_16Request["params"]["sessionID"]
readonly messageID: Endpoint4_16Request["payload"]["messageID"]
readonly files?: Endpoint4_16Request["payload"]["files"]
type Endpoint4_15Request = Parameters<RawClient["server.session"]["session.compact"]>[0]
export type Endpoint4_15Input = {
readonly sessionID: Endpoint4_15Request["params"]["sessionID"]
readonly id?: Endpoint4_15Request["payload"]["id"]
}
export type Endpoint4_16Output = EffectValue<ReturnType<RawClient["server.session"]["session.revert.stage"]>>["data"]
export type SessionRevertStageOperation<E = never> = (input: Endpoint4_16Input) => Effect.Effect<Endpoint4_16Output, E>
export type Endpoint4_15Output = EffectValue<ReturnType<RawClient["server.session"]["session.compact"]>>["data"]
export type SessionCompactOperation<E = never> = (input: Endpoint4_15Input) => Effect.Effect<Endpoint4_15Output, E>
type Endpoint4_17Request = Parameters<RawClient["server.session"]["session.revert.clear"]>[0]
export type Endpoint4_17Input = { readonly sessionID: Endpoint4_17Request["params"]["sessionID"] }
export type Endpoint4_17Output = EffectValue<ReturnType<RawClient["server.session"]["session.revert.clear"]>>
export type SessionRevertClearOperation<E = never> = (input: Endpoint4_17Input) => Effect.Effect<Endpoint4_17Output, E>
type Endpoint4_16Request = Parameters<RawClient["server.session"]["session.wait"]>[0]
export type Endpoint4_16Input = { readonly sessionID: Endpoint4_16Request["params"]["sessionID"] }
export type Endpoint4_16Output = EffectValue<ReturnType<RawClient["server.session"]["session.wait"]>>
export type SessionWaitOperation<E = never> = (input: Endpoint4_16Input) => Effect.Effect<Endpoint4_16Output, E>
type Endpoint4_18Request = Parameters<RawClient["server.session"]["session.revert.commit"]>[0]
type Endpoint4_17Request = Parameters<RawClient["server.session"]["session.revert.stage"]>[0]
export type Endpoint4_17Input = {
readonly sessionID: Endpoint4_17Request["params"]["sessionID"]
readonly messageID: Endpoint4_17Request["payload"]["messageID"]
readonly files?: Endpoint4_17Request["payload"]["files"]
}
export type Endpoint4_17Output = EffectValue<ReturnType<RawClient["server.session"]["session.revert.stage"]>>["data"]
export type SessionRevertStageOperation<E = never> = (input: Endpoint4_17Input) => Effect.Effect<Endpoint4_17Output, E>
type Endpoint4_18Request = Parameters<RawClient["server.session"]["session.revert.clear"]>[0]
export type Endpoint4_18Input = { readonly sessionID: Endpoint4_18Request["params"]["sessionID"] }
export type Endpoint4_18Output = EffectValue<ReturnType<RawClient["server.session"]["session.revert.commit"]>>
export type SessionRevertCommitOperation<E = never> = (input: Endpoint4_18Input) => Effect.Effect<Endpoint4_18Output, E>
export type Endpoint4_18Output = EffectValue<ReturnType<RawClient["server.session"]["session.revert.clear"]>>
export type SessionRevertClearOperation<E = never> = (input: Endpoint4_18Input) => Effect.Effect<Endpoint4_18Output, E>
type Endpoint4_19Request = Parameters<RawClient["server.session"]["session.context"]>[0]
type Endpoint4_19Request = Parameters<RawClient["server.session"]["session.revert.commit"]>[0]
export type Endpoint4_19Input = { readonly sessionID: Endpoint4_19Request["params"]["sessionID"] }
export type Endpoint4_19Output = EffectValue<ReturnType<RawClient["server.session"]["session.context"]>>["data"]
export type SessionContextOperation<E = never> = (input: Endpoint4_19Input) => Effect.Effect<Endpoint4_19Output, E>
export type Endpoint4_19Output = EffectValue<ReturnType<RawClient["server.session"]["session.revert.commit"]>>
export type SessionRevertCommitOperation<E = never> = (input: Endpoint4_19Input) => Effect.Effect<Endpoint4_19Output, E>
type Endpoint4_20Request = Parameters<RawClient["server.session"]["session.instructions.entry.list"]>[0]
type Endpoint4_20Request = Parameters<RawClient["server.session"]["session.context"]>[0]
export type Endpoint4_20Input = { readonly sessionID: Endpoint4_20Request["params"]["sessionID"] }
export type Endpoint4_20Output = EffectValue<
export type Endpoint4_20Output = EffectValue<ReturnType<RawClient["server.session"]["session.context"]>>["data"]
export type SessionContextOperation<E = never> = (input: Endpoint4_20Input) => Effect.Effect<Endpoint4_20Output, E>
type Endpoint4_21Request = Parameters<RawClient["server.session"]["session.instructions.entry.list"]>[0]
export type Endpoint4_21Input = { readonly sessionID: Endpoint4_21Request["params"]["sessionID"] }
export type Endpoint4_21Output = EffectValue<
ReturnType<RawClient["server.session"]["session.instructions.entry.list"]>
>["data"]
export type SessionInstructionsEntryListOperation<E = never> = (
input: Endpoint4_20Input,
) => Effect.Effect<Endpoint4_20Output, E>
type Endpoint4_21Request = Parameters<RawClient["server.session"]["session.instructions.entry.put"]>[0]
export type Endpoint4_21Input = {
readonly sessionID: Endpoint4_21Request["params"]["sessionID"]
readonly key: Endpoint4_21Request["params"]["key"]
readonly value: Endpoint4_21Request["payload"]["value"]
}
export type Endpoint4_21Output = EffectValue<ReturnType<RawClient["server.session"]["session.instructions.entry.put"]>>
export type SessionInstructionsEntryPutOperation<E = never> = (
input: Endpoint4_21Input,
) => Effect.Effect<Endpoint4_21Output, E>
type Endpoint4_22Request = Parameters<RawClient["server.session"]["session.instructions.entry.remove"]>[0]
type Endpoint4_22Request = Parameters<RawClient["server.session"]["session.instructions.entry.put"]>[0]
export type Endpoint4_22Input = {
readonly sessionID: Endpoint4_22Request["params"]["sessionID"]
readonly key: Endpoint4_22Request["params"]["key"]
readonly value: Endpoint4_22Request["payload"]["value"]
}
export type Endpoint4_22Output = EffectValue<
ReturnType<RawClient["server.session"]["session.instructions.entry.remove"]>
>
export type SessionInstructionsEntryRemoveOperation<E = never> = (
export type Endpoint4_22Output = EffectValue<ReturnType<RawClient["server.session"]["session.instructions.entry.put"]>>
export type SessionInstructionsEntryPutOperation<E = never> = (
input: Endpoint4_22Input,
) => Effect.Effect<Endpoint4_22Output, E>
type Endpoint4_23Request = Parameters<RawClient["server.session"]["session.log"]>[0]
type Endpoint4_23Request = Parameters<RawClient["server.session"]["session.instructions.entry.remove"]>[0]
export type Endpoint4_23Input = {
readonly sessionID: Endpoint4_23Request["params"]["sessionID"]
readonly after?: Endpoint4_23Request["query"]["after"]
readonly follow?: Endpoint4_23Request["query"]["follow"]
readonly key: Endpoint4_23Request["params"]["key"]
}
export type Endpoint4_23Output = StreamValue<EffectValue<ReturnType<RawClient["server.session"]["session.log"]>>>
export type SessionLogOperation<E = never> = (input: Endpoint4_23Input) => Stream.Stream<Endpoint4_23Output, E>
export type Endpoint4_23Output = EffectValue<
ReturnType<RawClient["server.session"]["session.instructions.entry.remove"]>
>
export type SessionInstructionsEntryRemoveOperation<E = never> = (
input: Endpoint4_23Input,
) => Effect.Effect<Endpoint4_23Output, E>
type Endpoint4_24Request = Parameters<RawClient["server.session"]["session.interrupt"]>[0]
export type Endpoint4_24Input = { readonly sessionID: Endpoint4_24Request["params"]["sessionID"] }
export type Endpoint4_24Output = EffectValue<ReturnType<RawClient["server.session"]["session.interrupt"]>>
export type SessionInterruptOperation<E = never> = (input: Endpoint4_24Input) => Effect.Effect<Endpoint4_24Output, E>
type Endpoint4_24Request = Parameters<RawClient["server.session"]["session.log"]>[0]
export type Endpoint4_24Input = {
readonly sessionID: Endpoint4_24Request["params"]["sessionID"]
readonly after?: Endpoint4_24Request["query"]["after"]
readonly follow?: Endpoint4_24Request["query"]["follow"]
}
export type Endpoint4_24Output = StreamValue<EffectValue<ReturnType<RawClient["server.session"]["session.log"]>>>
export type SessionLogOperation<E = never> = (input: Endpoint4_24Input) => Stream.Stream<Endpoint4_24Output, E>
type Endpoint4_25Request = Parameters<RawClient["server.session"]["session.background"]>[0]
type Endpoint4_25Request = Parameters<RawClient["server.session"]["session.interrupt"]>[0]
export type Endpoint4_25Input = { readonly sessionID: Endpoint4_25Request["params"]["sessionID"] }
export type Endpoint4_25Output = EffectValue<ReturnType<RawClient["server.session"]["session.background"]>>
export type SessionBackgroundOperation<E = never> = (input: Endpoint4_25Input) => Effect.Effect<Endpoint4_25Output, E>
export type Endpoint4_25Output = EffectValue<ReturnType<RawClient["server.session"]["session.interrupt"]>>
export type SessionInterruptOperation<E = never> = (input: Endpoint4_25Input) => Effect.Effect<Endpoint4_25Output, E>
type Endpoint4_26Request = Parameters<RawClient["server.session"]["session.message"]>[0]
export type Endpoint4_26Input = {
readonly sessionID: Endpoint4_26Request["params"]["sessionID"]
readonly messageID: Endpoint4_26Request["params"]["messageID"]
type Endpoint4_26Request = Parameters<RawClient["server.session"]["session.background"]>[0]
export type Endpoint4_26Input = { readonly sessionID: Endpoint4_26Request["params"]["sessionID"] }
export type Endpoint4_26Output = EffectValue<ReturnType<RawClient["server.session"]["session.background"]>>
export type SessionBackgroundOperation<E = never> = (input: Endpoint4_26Input) => Effect.Effect<Endpoint4_26Output, E>
type Endpoint4_27Request = Parameters<RawClient["server.session"]["session.message"]>[0]
export type Endpoint4_27Input = {
readonly sessionID: Endpoint4_27Request["params"]["sessionID"]
readonly messageID: Endpoint4_27Request["params"]["messageID"]
}
export type Endpoint4_26Output = EffectValue<ReturnType<RawClient["server.session"]["session.message"]>>["data"]
export type SessionMessageOperation<E = never> = (input: Endpoint4_26Input) => Effect.Effect<Endpoint4_26Output, E>
export type Endpoint4_27Output = EffectValue<ReturnType<RawClient["server.session"]["session.message"]>>["data"]
export type SessionMessageOperation<E = never> = (input: Endpoint4_27Input) => Effect.Effect<Endpoint4_27Output, E>
export interface SessionApi<E = never> {
readonly list: SessionListOperation<E>
@@ -273,6 +283,7 @@ export interface SessionApi<E = never> {
readonly switchAgent: SessionSwitchAgentOperation<E>
readonly switchModel: SessionSwitchModelOperation<E>
readonly rename: SessionRenameOperation<E>
readonly move: SessionMoveOperation<E>
readonly prompt: SessionPromptOperation<E>
readonly command: SessionCommandOperation<E>
readonly skill: SessionSkillOperation<E>
@@ -280,9 +291,11 @@ export interface SessionApi<E = never> {
readonly shell: SessionShellOperation<E>
readonly compact: SessionCompactOperation<E>
readonly wait: SessionWaitOperation<E>
readonly revertStage: SessionRevertStageOperation<E>
readonly revertClear: SessionRevertClearOperation<E>
readonly revertCommit: SessionRevertCommitOperation<E>
readonly revert: {
readonly stage: SessionRevertStageOperation<E>
readonly clear: SessionRevertClearOperation<E>
readonly commit: SessionRevertCommitOperation<E>
}
readonly context: SessionContextOperation<E>
readonly instructions: {
readonly entry: {
@@ -427,11 +440,15 @@ export type IntegrationAttemptCancelOperation<E = never> = (
export interface IntegrationApi<E = never> {
readonly list: IntegrationListOperation<E>
readonly get: IntegrationGetOperation<E>
readonly connectKey: IntegrationConnectKeyOperation<E>
readonly connectOauth: IntegrationConnectOauthOperation<E>
readonly attemptStatus: IntegrationAttemptStatusOperation<E>
readonly attemptComplete: IntegrationAttemptCompleteOperation<E>
readonly attemptCancel: IntegrationAttemptCancelOperation<E>
readonly connect: {
readonly key: IntegrationConnectKeyOperation<E>
readonly oauth: IntegrationConnectOauthOperation<E>
}
readonly attempt: {
readonly status: IntegrationAttemptStatusOperation<E>
readonly complete: IntegrationAttemptCompleteOperation<E>
readonly cancel: IntegrationAttemptCancelOperation<E>
}
}
type Endpoint10_0Request = Parameters<RawClient["server.mcp"]["mcp.list"]>[0]
@@ -439,8 +456,16 @@ export type Endpoint10_0Input = { readonly location?: Endpoint10_0Request["query
export type Endpoint10_0Output = EffectValue<ReturnType<RawClient["server.mcp"]["mcp.list"]>>
export type ServerMcpListOperation<E = never> = (input?: Endpoint10_0Input) => Effect.Effect<Endpoint10_0Output, E>
type Endpoint10_1Request = Parameters<RawClient["server.mcp"]["mcp.resource.catalog"]>[0]
export type Endpoint10_1Input = { readonly location?: Endpoint10_1Request["query"]["location"] }
export type Endpoint10_1Output = EffectValue<ReturnType<RawClient["server.mcp"]["mcp.resource.catalog"]>>
export type ServerMcpResourceCatalogOperation<E = never> = (
input?: Endpoint10_1Input,
) => Effect.Effect<Endpoint10_1Output, E>
export interface ServerMcpApi<E = never> {
readonly list: ServerMcpListOperation<E>
readonly resource: { readonly catalog: ServerMcpResourceCatalogOperation<E> }
}
type Endpoint11_0Request = Parameters<RawClient["server.credential"]["credential.update"]>[0]
@@ -490,7 +515,7 @@ export interface ProjectApi<E = never> {
type Endpoint13_0Request = Parameters<RawClient["server.form"]["form.request.list"]>[0]
export type Endpoint13_0Input = { readonly location?: Endpoint13_0Request["query"]["location"] }
export type Endpoint13_0Output = EffectValue<ReturnType<RawClient["server.form"]["form.request.list"]>>
export type FormListRequestsOperation<E = never> = (input?: Endpoint13_0Input) => Effect.Effect<Endpoint13_0Output, E>
export type FormRequestListOperation<E = never> = (input?: Endpoint13_0Input) => Effect.Effect<Endpoint13_0Output, E>
type Endpoint13_1Request = Parameters<RawClient["server.form"]["session.form.list"]>[0]
export type Endpoint13_1Input = { readonly sessionID: Endpoint13_1Request["params"]["sessionID"] }
@@ -544,7 +569,7 @@ export type Endpoint13_6Output = EffectValue<ReturnType<RawClient["server.form"]
export type FormCancelOperation<E = never> = (input: Endpoint13_6Input) => Effect.Effect<Endpoint13_6Output, E>
export interface FormApi<E = never> {
readonly listRequests: FormListRequestsOperation<E>
readonly request: { readonly list: FormRequestListOperation<E> }
readonly list: FormListOperation<E>
readonly create: FormCreateOperation<E>
readonly get: FormGetOperation<E>
@@ -556,7 +581,7 @@ export interface FormApi<E = never> {
type Endpoint14_0Request = Parameters<RawClient["server.permission"]["permission.request.list"]>[0]
export type Endpoint14_0Input = { readonly location?: Endpoint14_0Request["query"]["location"] }
export type Endpoint14_0Output = EffectValue<ReturnType<RawClient["server.permission"]["permission.request.list"]>>
export type PermissionListRequestsOperation<E = never> = (
export type PermissionRequestListOperation<E = never> = (
input?: Endpoint14_0Input,
) => Effect.Effect<Endpoint14_0Output, E>
@@ -565,14 +590,14 @@ export type Endpoint14_1Input = { readonly projectID?: Endpoint14_1Request["quer
export type Endpoint14_1Output = EffectValue<
ReturnType<RawClient["server.permission"]["permission.saved.list"]>
>["data"]
export type PermissionListSavedOperation<E = never> = (
export type PermissionSavedListOperation<E = never> = (
input?: Endpoint14_1Input,
) => Effect.Effect<Endpoint14_1Output, E>
type Endpoint14_2Request = Parameters<RawClient["server.permission"]["permission.saved.remove"]>[0]
export type Endpoint14_2Input = { readonly id: Endpoint14_2Request["params"]["id"] }
export type Endpoint14_2Output = EffectValue<ReturnType<RawClient["server.permission"]["permission.saved.remove"]>>
export type PermissionRemoveSavedOperation<E = never> = (
export type PermissionSavedRemoveOperation<E = never> = (
input: Endpoint14_2Input,
) => Effect.Effect<Endpoint14_2Output, E>
@@ -620,9 +645,8 @@ export type Endpoint14_6Output = EffectValue<ReturnType<RawClient["server.permis
export type PermissionReplyOperation<E = never> = (input: Endpoint14_6Input) => Effect.Effect<Endpoint14_6Output, E>
export interface PermissionApi<E = never> {
readonly listRequests: PermissionListRequestsOperation<E>
readonly listSaved: PermissionListSavedOperation<E>
readonly removeSaved: PermissionRemoveSavedOperation<E>
readonly request: { readonly list: PermissionRequestListOperation<E> }
readonly saved: { readonly list: PermissionSavedListOperation<E>; readonly remove: PermissionSavedRemoveOperation<E> }
readonly create: PermissionCreateOperation<E>
readonly list: PermissionListOperation<E>
readonly get: PermissionGetOperation<E>
@@ -791,7 +815,7 @@ export interface ShellApi<E = never> {
type Endpoint21_0Request = Parameters<RawClient["server.question"]["question.request.list"]>[0]
export type Endpoint21_0Input = { readonly location?: Endpoint21_0Request["query"]["location"] }
export type Endpoint21_0Output = EffectValue<ReturnType<RawClient["server.question"]["question.request.list"]>>
export type QuestionListRequestsOperation<E = never> = (
export type QuestionRequestListOperation<E = never> = (
input?: Endpoint21_0Input,
) => Effect.Effect<Endpoint21_0Output, E>
@@ -818,7 +842,7 @@ export type Endpoint21_3Output = EffectValue<ReturnType<RawClient["server.questi
export type QuestionRejectOperation<E = never> = (input: Endpoint21_3Input) => Effect.Effect<Endpoint21_3Output, E>
export interface QuestionApi<E = never> {
readonly listRequests: QuestionListRequestsOperation<E>
readonly request: { readonly list: QuestionRequestListOperation<E> }
readonly list: QuestionListOperation<E>
readonly reply: QuestionReplyOperation<E>
readonly reject: QuestionRejectOperation<E>
@@ -888,10 +912,15 @@ export interface VcsApi<E = never> {
}
export type Endpoint25_0Output = EffectValue<ReturnType<RawClient["server.debug"]["debug.location"]>>
export type DebugLocationOperation<E = never> = () => Effect.Effect<Endpoint25_0Output, E>
export type DebugLocationListOperation<E = never> = () => Effect.Effect<Endpoint25_0Output, E>
type Endpoint25_1Request = Parameters<RawClient["server.debug"]["debug.location.evict"]>[0]
export type Endpoint25_1Input = { readonly location?: Endpoint25_1Request["query"]["location"] }
export type Endpoint25_1Output = EffectValue<ReturnType<RawClient["server.debug"]["debug.location.evict"]>>
export type DebugLocationEvictOperation<E = never> = (input?: Endpoint25_1Input) => Effect.Effect<Endpoint25_1Output, E>
export interface DebugApi<E = never> {
readonly location: DebugLocationOperation<E>
readonly location: { readonly list: DebugLocationListOperation<E>; readonly evict: DebugLocationEvictOperation<E> }
}
export interface AppApi<E = never> {
+150 -122
View File
@@ -141,15 +141,27 @@ const Endpoint4_8 = (raw: RawClient["server.session"]) => (input: Endpoint4_8Inp
Effect.mapError(mapClientError),
)
type Endpoint4_9Request = Parameters<RawClient["server.session"]["session.prompt"]>[0]
type Endpoint4_9Request = Parameters<RawClient["server.session"]["session.move"]>[0]
type Endpoint4_9Input = {
readonly sessionID: Endpoint4_9Request["params"]["sessionID"]
readonly id?: Endpoint4_9Request["payload"]["id"]
readonly prompt: Endpoint4_9Request["payload"]["prompt"]
readonly delivery?: Endpoint4_9Request["payload"]["delivery"]
readonly resume?: Endpoint4_9Request["payload"]["resume"]
readonly destination: Endpoint4_9Request["payload"]["destination"]
readonly moveChanges?: Endpoint4_9Request["payload"]["moveChanges"]
}
const Endpoint4_9 = (raw: RawClient["server.session"]) => (input: Endpoint4_9Input) =>
raw["session.move"]({
params: { sessionID: input["sessionID"] },
payload: { destination: input["destination"], moveChanges: input["moveChanges"] },
}).pipe(Effect.mapError(mapClientError))
type Endpoint4_10Request = Parameters<RawClient["server.session"]["session.prompt"]>[0]
type Endpoint4_10Input = {
readonly sessionID: Endpoint4_10Request["params"]["sessionID"]
readonly id?: Endpoint4_10Request["payload"]["id"]
readonly prompt: Endpoint4_10Request["payload"]["prompt"]
readonly delivery?: Endpoint4_10Request["payload"]["delivery"]
readonly resume?: Endpoint4_10Request["payload"]["resume"]
}
const Endpoint4_10 = (raw: RawClient["server.session"]) => (input: Endpoint4_10Input) =>
raw["session.prompt"]({
params: { sessionID: input["sessionID"] },
payload: { id: input["id"], prompt: input["prompt"], delivery: input["delivery"], resume: input["resume"] },
@@ -158,20 +170,20 @@ const Endpoint4_9 = (raw: RawClient["server.session"]) => (input: Endpoint4_9Inp
Effect.map((value) => value.data),
)
type Endpoint4_10Request = Parameters<RawClient["server.session"]["session.command"]>[0]
type Endpoint4_10Input = {
readonly sessionID: Endpoint4_10Request["params"]["sessionID"]
readonly id?: Endpoint4_10Request["payload"]["id"]
readonly command: Endpoint4_10Request["payload"]["command"]
readonly arguments?: Endpoint4_10Request["payload"]["arguments"]
readonly agent?: Endpoint4_10Request["payload"]["agent"]
readonly model?: Endpoint4_10Request["payload"]["model"]
readonly files?: Endpoint4_10Request["payload"]["files"]
readonly agents?: Endpoint4_10Request["payload"]["agents"]
readonly delivery?: Endpoint4_10Request["payload"]["delivery"]
readonly resume?: Endpoint4_10Request["payload"]["resume"]
type Endpoint4_11Request = Parameters<RawClient["server.session"]["session.command"]>[0]
type Endpoint4_11Input = {
readonly sessionID: Endpoint4_11Request["params"]["sessionID"]
readonly id?: Endpoint4_11Request["payload"]["id"]
readonly command: Endpoint4_11Request["payload"]["command"]
readonly arguments?: Endpoint4_11Request["payload"]["arguments"]
readonly agent?: Endpoint4_11Request["payload"]["agent"]
readonly model?: Endpoint4_11Request["payload"]["model"]
readonly files?: Endpoint4_11Request["payload"]["files"]
readonly agents?: Endpoint4_11Request["payload"]["agents"]
readonly delivery?: Endpoint4_11Request["payload"]["delivery"]
readonly resume?: Endpoint4_11Request["payload"]["resume"]
}
const Endpoint4_10 = (raw: RawClient["server.session"]) => (input: Endpoint4_10Input) =>
const Endpoint4_11 = (raw: RawClient["server.session"]) => (input: Endpoint4_11Input) =>
raw["session.command"]({
params: { sessionID: input["sessionID"] },
payload: {
@@ -190,67 +202,73 @@ const Endpoint4_10 = (raw: RawClient["server.session"]) => (input: Endpoint4_10I
Effect.map((value) => value.data),
)
type Endpoint4_11Request = Parameters<RawClient["server.session"]["session.skill"]>[0]
type Endpoint4_11Input = {
readonly sessionID: Endpoint4_11Request["params"]["sessionID"]
readonly id?: Endpoint4_11Request["payload"]["id"]
readonly skill: Endpoint4_11Request["payload"]["skill"]
readonly resume?: Endpoint4_11Request["payload"]["resume"]
type Endpoint4_12Request = Parameters<RawClient["server.session"]["session.skill"]>[0]
type Endpoint4_12Input = {
readonly sessionID: Endpoint4_12Request["params"]["sessionID"]
readonly id?: Endpoint4_12Request["payload"]["id"]
readonly skill: Endpoint4_12Request["payload"]["skill"]
readonly resume?: Endpoint4_12Request["payload"]["resume"]
}
const Endpoint4_11 = (raw: RawClient["server.session"]) => (input: Endpoint4_11Input) =>
const Endpoint4_12 = (raw: RawClient["server.session"]) => (input: Endpoint4_12Input) =>
raw["session.skill"]({
params: { sessionID: input["sessionID"] },
payload: { id: input["id"], skill: input["skill"], resume: input["resume"] },
}).pipe(Effect.mapError(mapClientError))
type Endpoint4_12Request = Parameters<RawClient["server.session"]["session.synthetic"]>[0]
type Endpoint4_12Input = {
readonly sessionID: Endpoint4_12Request["params"]["sessionID"]
readonly text: Endpoint4_12Request["payload"]["text"]
readonly description?: Endpoint4_12Request["payload"]["description"]
readonly metadata?: Endpoint4_12Request["payload"]["metadata"]
}
const Endpoint4_12 = (raw: RawClient["server.session"]) => (input: Endpoint4_12Input) =>
raw["session.synthetic"]({
params: { sessionID: input["sessionID"] },
payload: { text: input["text"], description: input["description"], metadata: input["metadata"] },
}).pipe(Effect.mapError(mapClientError))
type Endpoint4_13Request = Parameters<RawClient["server.session"]["session.shell"]>[0]
type Endpoint4_13Request = Parameters<RawClient["server.session"]["session.synthetic"]>[0]
type Endpoint4_13Input = {
readonly sessionID: Endpoint4_13Request["params"]["sessionID"]
readonly id?: Endpoint4_13Request["payload"]["id"]
readonly command: Endpoint4_13Request["payload"]["command"]
readonly text: Endpoint4_13Request["payload"]["text"]
readonly description?: Endpoint4_13Request["payload"]["description"]
readonly metadata?: Endpoint4_13Request["payload"]["metadata"]
readonly resume?: Endpoint4_13Request["payload"]["resume"]
}
const Endpoint4_13 = (raw: RawClient["server.session"]) => (input: Endpoint4_13Input) =>
raw["session.synthetic"]({
params: { sessionID: input["sessionID"] },
payload: {
text: input["text"],
description: input["description"],
metadata: input["metadata"],
resume: input["resume"],
},
}).pipe(Effect.mapError(mapClientError))
type Endpoint4_14Request = Parameters<RawClient["server.session"]["session.shell"]>[0]
type Endpoint4_14Input = {
readonly sessionID: Endpoint4_14Request["params"]["sessionID"]
readonly id?: Endpoint4_14Request["payload"]["id"]
readonly command: Endpoint4_14Request["payload"]["command"]
}
const Endpoint4_14 = (raw: RawClient["server.session"]) => (input: Endpoint4_14Input) =>
raw["session.shell"]({
params: { sessionID: input["sessionID"] },
payload: { id: input["id"], command: input["command"] },
}).pipe(Effect.mapError(mapClientError))
type Endpoint4_14Request = Parameters<RawClient["server.session"]["session.compact"]>[0]
type Endpoint4_14Input = {
readonly sessionID: Endpoint4_14Request["params"]["sessionID"]
readonly id?: Endpoint4_14Request["payload"]["id"]
type Endpoint4_15Request = Parameters<RawClient["server.session"]["session.compact"]>[0]
type Endpoint4_15Input = {
readonly sessionID: Endpoint4_15Request["params"]["sessionID"]
readonly id?: Endpoint4_15Request["payload"]["id"]
}
const Endpoint4_14 = (raw: RawClient["server.session"]) => (input: Endpoint4_14Input) =>
const Endpoint4_15 = (raw: RawClient["server.session"]) => (input: Endpoint4_15Input) =>
raw["session.compact"]({ params: { sessionID: input["sessionID"] }, payload: { id: input["id"] } }).pipe(
Effect.mapError(mapClientError),
Effect.map((value) => value.data),
)
type Endpoint4_15Request = Parameters<RawClient["server.session"]["session.wait"]>[0]
type Endpoint4_15Input = { readonly sessionID: Endpoint4_15Request["params"]["sessionID"] }
const Endpoint4_15 = (raw: RawClient["server.session"]) => (input: Endpoint4_15Input) =>
type Endpoint4_16Request = Parameters<RawClient["server.session"]["session.wait"]>[0]
type Endpoint4_16Input = { readonly sessionID: Endpoint4_16Request["params"]["sessionID"] }
const Endpoint4_16 = (raw: RawClient["server.session"]) => (input: Endpoint4_16Input) =>
raw["session.wait"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError))
type Endpoint4_16Request = Parameters<RawClient["server.session"]["session.revert.stage"]>[0]
type Endpoint4_16Input = {
readonly sessionID: Endpoint4_16Request["params"]["sessionID"]
readonly messageID: Endpoint4_16Request["payload"]["messageID"]
readonly files?: Endpoint4_16Request["payload"]["files"]
type Endpoint4_17Request = Parameters<RawClient["server.session"]["session.revert.stage"]>[0]
type Endpoint4_17Input = {
readonly sessionID: Endpoint4_17Request["params"]["sessionID"]
readonly messageID: Endpoint4_17Request["payload"]["messageID"]
readonly files?: Endpoint4_17Request["payload"]["files"]
}
const Endpoint4_16 = (raw: RawClient["server.session"]) => (input: Endpoint4_16Input) =>
const Endpoint4_17 = (raw: RawClient["server.session"]) => (input: Endpoint4_17Input) =>
raw["session.revert.stage"]({
params: { sessionID: input["sessionID"] },
payload: { messageID: input["messageID"], files: input["files"] },
@@ -259,61 +277,61 @@ const Endpoint4_16 = (raw: RawClient["server.session"]) => (input: Endpoint4_16I
Effect.map((value) => value.data),
)
type Endpoint4_17Request = Parameters<RawClient["server.session"]["session.revert.clear"]>[0]
type Endpoint4_17Input = { readonly sessionID: Endpoint4_17Request["params"]["sessionID"] }
const Endpoint4_17 = (raw: RawClient["server.session"]) => (input: Endpoint4_17Input) =>
raw["session.revert.clear"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError))
type Endpoint4_18Request = Parameters<RawClient["server.session"]["session.revert.commit"]>[0]
type Endpoint4_18Request = Parameters<RawClient["server.session"]["session.revert.clear"]>[0]
type Endpoint4_18Input = { readonly sessionID: Endpoint4_18Request["params"]["sessionID"] }
const Endpoint4_18 = (raw: RawClient["server.session"]) => (input: Endpoint4_18Input) =>
raw["session.revert.commit"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError))
raw["session.revert.clear"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError))
type Endpoint4_19Request = Parameters<RawClient["server.session"]["session.context"]>[0]
type Endpoint4_19Request = Parameters<RawClient["server.session"]["session.revert.commit"]>[0]
type Endpoint4_19Input = { readonly sessionID: Endpoint4_19Request["params"]["sessionID"] }
const Endpoint4_19 = (raw: RawClient["server.session"]) => (input: Endpoint4_19Input) =>
raw["session.revert.commit"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError))
type Endpoint4_20Request = Parameters<RawClient["server.session"]["session.context"]>[0]
type Endpoint4_20Input = { readonly sessionID: Endpoint4_20Request["params"]["sessionID"] }
const Endpoint4_20 = (raw: RawClient["server.session"]) => (input: Endpoint4_20Input) =>
raw["session.context"]({ params: { sessionID: input["sessionID"] } }).pipe(
Effect.mapError(mapClientError),
Effect.map((value) => value.data),
)
type Endpoint4_20Request = Parameters<RawClient["server.session"]["session.instructions.entry.list"]>[0]
type Endpoint4_20Input = { readonly sessionID: Endpoint4_20Request["params"]["sessionID"] }
const Endpoint4_20 = (raw: RawClient["server.session"]) => (input: Endpoint4_20Input) =>
type Endpoint4_21Request = Parameters<RawClient["server.session"]["session.instructions.entry.list"]>[0]
type Endpoint4_21Input = { readonly sessionID: Endpoint4_21Request["params"]["sessionID"] }
const Endpoint4_21 = (raw: RawClient["server.session"]) => (input: Endpoint4_21Input) =>
raw["session.instructions.entry.list"]({ params: { sessionID: input["sessionID"] } }).pipe(
Effect.mapError(mapClientError),
Effect.map((value) => value.data),
)
type Endpoint4_21Request = Parameters<RawClient["server.session"]["session.instructions.entry.put"]>[0]
type Endpoint4_21Input = {
readonly sessionID: Endpoint4_21Request["params"]["sessionID"]
readonly key: Endpoint4_21Request["params"]["key"]
readonly value: Endpoint4_21Request["payload"]["value"]
type Endpoint4_22Request = Parameters<RawClient["server.session"]["session.instructions.entry.put"]>[0]
type Endpoint4_22Input = {
readonly sessionID: Endpoint4_22Request["params"]["sessionID"]
readonly key: Endpoint4_22Request["params"]["key"]
readonly value: Endpoint4_22Request["payload"]["value"]
}
const Endpoint4_21 = (raw: RawClient["server.session"]) => (input: Endpoint4_21Input) =>
const Endpoint4_22 = (raw: RawClient["server.session"]) => (input: Endpoint4_22Input) =>
raw["session.instructions.entry.put"]({
params: { sessionID: input["sessionID"], key: input["key"] },
payload: { value: input["value"] },
}).pipe(Effect.mapError(mapClientError))
type Endpoint4_22Request = Parameters<RawClient["server.session"]["session.instructions.entry.remove"]>[0]
type Endpoint4_22Input = {
readonly sessionID: Endpoint4_22Request["params"]["sessionID"]
readonly key: Endpoint4_22Request["params"]["key"]
type Endpoint4_23Request = Parameters<RawClient["server.session"]["session.instructions.entry.remove"]>[0]
type Endpoint4_23Input = {
readonly sessionID: Endpoint4_23Request["params"]["sessionID"]
readonly key: Endpoint4_23Request["params"]["key"]
}
const Endpoint4_22 = (raw: RawClient["server.session"]) => (input: Endpoint4_22Input) =>
const Endpoint4_23 = (raw: RawClient["server.session"]) => (input: Endpoint4_23Input) =>
raw["session.instructions.entry.remove"]({ params: { sessionID: input["sessionID"], key: input["key"] } }).pipe(
Effect.mapError(mapClientError),
)
type Endpoint4_23Request = Parameters<RawClient["server.session"]["session.log"]>[0]
type Endpoint4_23Input = {
readonly sessionID: Endpoint4_23Request["params"]["sessionID"]
readonly after?: Endpoint4_23Request["query"]["after"]
readonly follow?: Endpoint4_23Request["query"]["follow"]
type Endpoint4_24Request = Parameters<RawClient["server.session"]["session.log"]>[0]
type Endpoint4_24Input = {
readonly sessionID: Endpoint4_24Request["params"]["sessionID"]
readonly after?: Endpoint4_24Request["query"]["after"]
readonly follow?: Endpoint4_24Request["query"]["follow"]
}
const Endpoint4_23 = (raw: RawClient["server.session"]) => (input: Endpoint4_23Input) =>
const Endpoint4_24 = (raw: RawClient["server.session"]) => (input: Endpoint4_24Input) =>
Stream.unwrap(
raw["session.log"]({
params: { sessionID: input["sessionID"] },
@@ -324,22 +342,22 @@ const Endpoint4_23 = (raw: RawClient["server.session"]) => (input: Endpoint4_23I
),
)
type Endpoint4_24Request = Parameters<RawClient["server.session"]["session.interrupt"]>[0]
type Endpoint4_24Input = { readonly sessionID: Endpoint4_24Request["params"]["sessionID"] }
const Endpoint4_24 = (raw: RawClient["server.session"]) => (input: Endpoint4_24Input) =>
raw["session.interrupt"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError))
type Endpoint4_25Request = Parameters<RawClient["server.session"]["session.background"]>[0]
type Endpoint4_25Request = Parameters<RawClient["server.session"]["session.interrupt"]>[0]
type Endpoint4_25Input = { readonly sessionID: Endpoint4_25Request["params"]["sessionID"] }
const Endpoint4_25 = (raw: RawClient["server.session"]) => (input: Endpoint4_25Input) =>
raw["session.interrupt"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError))
type Endpoint4_26Request = Parameters<RawClient["server.session"]["session.background"]>[0]
type Endpoint4_26Input = { readonly sessionID: Endpoint4_26Request["params"]["sessionID"] }
const Endpoint4_26 = (raw: RawClient["server.session"]) => (input: Endpoint4_26Input) =>
raw["session.background"]({ params: { sessionID: input["sessionID"] } }).pipe(Effect.mapError(mapClientError))
type Endpoint4_26Request = Parameters<RawClient["server.session"]["session.message"]>[0]
type Endpoint4_26Input = {
readonly sessionID: Endpoint4_26Request["params"]["sessionID"]
readonly messageID: Endpoint4_26Request["params"]["messageID"]
type Endpoint4_27Request = Parameters<RawClient["server.session"]["session.message"]>[0]
type Endpoint4_27Input = {
readonly sessionID: Endpoint4_27Request["params"]["sessionID"]
readonly messageID: Endpoint4_27Request["params"]["messageID"]
}
const Endpoint4_26 = (raw: RawClient["server.session"]) => (input: Endpoint4_26Input) =>
const Endpoint4_27 = (raw: RawClient["server.session"]) => (input: Endpoint4_27Input) =>
raw["session.message"]({ params: { sessionID: input["sessionID"], messageID: input["messageID"] } }).pipe(
Effect.mapError(mapClientError),
Effect.map((value) => value.data),
@@ -355,22 +373,21 @@ const adaptGroup4 = (raw: RawClient["server.session"]) => ({
switchAgent: Endpoint4_6(raw),
switchModel: Endpoint4_7(raw),
rename: Endpoint4_8(raw),
prompt: Endpoint4_9(raw),
command: Endpoint4_10(raw),
skill: Endpoint4_11(raw),
synthetic: Endpoint4_12(raw),
shell: Endpoint4_13(raw),
compact: Endpoint4_14(raw),
wait: Endpoint4_15(raw),
revertStage: Endpoint4_16(raw),
revertClear: Endpoint4_17(raw),
revertCommit: Endpoint4_18(raw),
context: Endpoint4_19(raw),
instructions: { entry: { list: Endpoint4_20(raw), put: Endpoint4_21(raw), remove: Endpoint4_22(raw) } },
log: Endpoint4_23(raw),
interrupt: Endpoint4_24(raw),
background: Endpoint4_25(raw),
message: Endpoint4_26(raw),
move: Endpoint4_9(raw),
prompt: Endpoint4_10(raw),
command: Endpoint4_11(raw),
skill: Endpoint4_12(raw),
synthetic: Endpoint4_13(raw),
shell: Endpoint4_14(raw),
compact: Endpoint4_15(raw),
wait: Endpoint4_16(raw),
revert: { stage: Endpoint4_17(raw), clear: Endpoint4_18(raw), commit: Endpoint4_19(raw) },
context: Endpoint4_20(raw),
instructions: { entry: { list: Endpoint4_21(raw), put: Endpoint4_22(raw), remove: Endpoint4_23(raw) } },
log: Endpoint4_24(raw),
interrupt: Endpoint4_25(raw),
background: Endpoint4_26(raw),
message: Endpoint4_27(raw),
})
type Endpoint5_0Request = Parameters<RawClient["server.message"]["session.messages"]>[0]
@@ -517,11 +534,8 @@ const Endpoint9_6 = (raw: RawClient["server.integration"]) => (input: Endpoint9_
const adaptGroup9 = (raw: RawClient["server.integration"]) => ({
list: Endpoint9_0(raw),
get: Endpoint9_1(raw),
connectKey: Endpoint9_2(raw),
connectOauth: Endpoint9_3(raw),
attemptStatus: Endpoint9_4(raw),
attemptComplete: Endpoint9_5(raw),
attemptCancel: Endpoint9_6(raw),
connect: { key: Endpoint9_2(raw), oauth: Endpoint9_3(raw) },
attempt: { status: Endpoint9_4(raw), complete: Endpoint9_5(raw), cancel: Endpoint9_6(raw) },
})
type Endpoint10_0Request = Parameters<RawClient["server.mcp"]["mcp.list"]>[0]
@@ -529,7 +543,15 @@ type Endpoint10_0Input = { readonly location?: Endpoint10_0Request["query"]["loc
const Endpoint10_0 = (raw: RawClient["server.mcp"]) => (input?: Endpoint10_0Input) =>
raw["mcp.list"]({ query: { location: input?.["location"] } }).pipe(Effect.mapError(mapClientError))
const adaptGroup10 = (raw: RawClient["server.mcp"]) => ({ list: Endpoint10_0(raw) })
type Endpoint10_1Request = Parameters<RawClient["server.mcp"]["mcp.resource.catalog"]>[0]
type Endpoint10_1Input = { readonly location?: Endpoint10_1Request["query"]["location"] }
const Endpoint10_1 = (raw: RawClient["server.mcp"]) => (input?: Endpoint10_1Input) =>
raw["mcp.resource.catalog"]({ query: { location: input?.["location"] } }).pipe(Effect.mapError(mapClientError))
const adaptGroup10 = (raw: RawClient["server.mcp"]) => ({
list: Endpoint10_0(raw),
resource: { catalog: Endpoint10_1(raw) },
})
type Endpoint11_0Request = Parameters<RawClient["server.credential"]["credential.update"]>[0]
type Endpoint11_0Input = {
@@ -666,7 +688,7 @@ const Endpoint13_6 = (raw: RawClient["server.form"]) => (input: Endpoint13_6Inpu
)
const adaptGroup13 = (raw: RawClient["server.form"]) => ({
listRequests: Endpoint13_0(raw),
request: { list: Endpoint13_0(raw) },
list: Endpoint13_1(raw),
create: Endpoint13_2(raw),
get: Endpoint13_3(raw),
@@ -754,9 +776,8 @@ const Endpoint14_6 = (raw: RawClient["server.permission"]) => (input: Endpoint14
}).pipe(Effect.mapError(mapClientError))
const adaptGroup14 = (raw: RawClient["server.permission"]) => ({
listRequests: Endpoint14_0(raw),
listSaved: Endpoint14_1(raw),
removeSaved: Endpoint14_2(raw),
request: { list: Endpoint14_0(raw) },
saved: { list: Endpoint14_1(raw), remove: Endpoint14_2(raw) },
create: Endpoint14_3(raw),
list: Endpoint14_4(raw),
get: Endpoint14_5(raw),
@@ -989,7 +1010,7 @@ const Endpoint21_3 = (raw: RawClient["server.question"]) => (input: Endpoint21_3
)
const adaptGroup21 = (raw: RawClient["server.question"]) => ({
listRequests: Endpoint21_0(raw),
request: { list: Endpoint21_0(raw) },
list: Endpoint21_1(raw),
reply: Endpoint21_2(raw),
reject: Endpoint21_3(raw),
@@ -1069,7 +1090,14 @@ const adaptGroup24 = (raw: RawClient["server.vcs"]) => ({ status: Endpoint24_0(r
const Endpoint25_0 = (raw: RawClient["server.debug"]) => () =>
raw["debug.location"]({}).pipe(Effect.mapError(mapClientError))
const adaptGroup25 = (raw: RawClient["server.debug"]) => ({ location: Endpoint25_0(raw) })
type Endpoint25_1Request = Parameters<RawClient["server.debug"]["debug.location.evict"]>[0]
type Endpoint25_1Input = { readonly location?: Endpoint25_1Request["query"]["location"] }
const Endpoint25_1 = (raw: RawClient["server.debug"]) => (input?: Endpoint25_1Input) =>
raw["debug.location.evict"]({ query: { location: input?.["location"] } }).pipe(Effect.mapError(mapClientError))
const adaptGroup25 = (raw: RawClient["server.debug"]) => ({
location: { list: Endpoint25_0(raw), evict: Endpoint25_1(raw) },
})
const adaptClient = (raw: RawClient) => ({
health: adaptGroup0(raw["server.health"]),
+244 -179
View File
@@ -23,6 +23,8 @@ import type {
SessionSwitchModelOutput,
SessionRenameInput,
SessionRenameOutput,
SessionMoveInput,
SessionMoveOutput,
SessionPromptInput,
SessionPromptOutput,
SessionCommandInput,
@@ -87,6 +89,8 @@ import type {
IntegrationAttemptCancelOutput,
ServerMcpListInput,
ServerMcpListOutput,
ServerMcpResourceCatalogInput,
ServerMcpResourceCatalogOutput,
CredentialUpdateInput,
CredentialUpdateOutput,
CredentialRemoveInput,
@@ -96,8 +100,8 @@ import type {
ProjectCurrentOutput,
ProjectDirectoriesInput,
ProjectDirectoriesOutput,
FormListRequestsInput,
FormListRequestsOutput,
FormRequestListInput,
FormRequestListOutput,
FormListInput,
FormListOutput,
FormCreateInput,
@@ -110,12 +114,12 @@ import type {
FormReplyOutput,
FormCancelInput,
FormCancelOutput,
PermissionListRequestsInput,
PermissionListRequestsOutput,
PermissionListSavedInput,
PermissionListSavedOutput,
PermissionRemoveSavedInput,
PermissionRemoveSavedOutput,
PermissionRequestListInput,
PermissionRequestListOutput,
PermissionSavedListInput,
PermissionSavedListOutput,
PermissionSavedRemoveInput,
PermissionSavedRemoveOutput,
PermissionCreateInput,
PermissionCreateOutput,
PermissionListInput,
@@ -157,8 +161,8 @@ import type {
ShellOutputOutput,
ShellRemoveInput,
ShellRemoveOutput,
QuestionListRequestsInput,
QuestionListRequestsOutput,
QuestionRequestListInput,
QuestionRequestListOutput,
QuestionListInput,
QuestionListOutput,
QuestionReplyInput,
@@ -177,7 +181,9 @@ import type {
VcsStatusOutput,
VcsDiffInput,
VcsDiffOutput,
DebugLocationOutput,
DebugLocationListOutput,
DebugLocationEvictInput,
DebugLocationEvictOutput,
} from "./types"
import { ClientError } from "./client-error"
@@ -485,6 +491,18 @@ export function make(options: ClientOptions) {
},
requestOptions,
),
move: (input: SessionMoveInput, requestOptions?: RequestOptions) =>
request<SessionMoveOutput>(
{
method: "POST",
path: `/api/session/${encodeURIComponent(input.sessionID)}/move`,
body: { destination: input["destination"], moveChanges: input["moveChanges"] },
successStatus: 204,
declaredStatuses: [404, 400, 401],
empty: true,
},
requestOptions,
),
prompt: (input: SessionPromptInput, requestOptions?: RequestOptions) =>
request<{ readonly data: SessionPromptOutput }>(
{
@@ -536,7 +554,12 @@ export function make(options: ClientOptions) {
{
method: "POST",
path: `/api/session/${encodeURIComponent(input.sessionID)}/synthetic`,
body: { text: input["text"], description: input["description"], metadata: input["metadata"] },
body: {
text: input["text"],
description: input["description"],
metadata: input["metadata"],
resume: input["resume"],
},
successStatus: 204,
declaredStatuses: [404, 400, 401],
empty: true,
@@ -578,40 +601,42 @@ export function make(options: ClientOptions) {
},
requestOptions,
),
revertStage: (input: SessionRevertStageInput, requestOptions?: RequestOptions) =>
request<{ readonly data: SessionRevertStageOutput }>(
{
method: "POST",
path: `/api/session/${encodeURIComponent(input.sessionID)}/revert/stage`,
body: { messageID: input["messageID"], files: input["files"] },
successStatus: 200,
declaredStatuses: [404, 409, 500, 400, 401],
empty: false,
},
requestOptions,
).then((value) => value.data),
revertClear: (input: SessionRevertClearInput, requestOptions?: RequestOptions) =>
request<SessionRevertClearOutput>(
{
method: "POST",
path: `/api/session/${encodeURIComponent(input.sessionID)}/revert/clear`,
successStatus: 204,
declaredStatuses: [404, 409, 500, 400, 401],
empty: true,
},
requestOptions,
),
revertCommit: (input: SessionRevertCommitInput, requestOptions?: RequestOptions) =>
request<SessionRevertCommitOutput>(
{
method: "POST",
path: `/api/session/${encodeURIComponent(input.sessionID)}/revert/commit`,
successStatus: 204,
declaredStatuses: [404, 409, 400, 401],
empty: true,
},
requestOptions,
),
revert: {
stage: (input: SessionRevertStageInput, requestOptions?: RequestOptions) =>
request<{ readonly data: SessionRevertStageOutput }>(
{
method: "POST",
path: `/api/session/${encodeURIComponent(input.sessionID)}/revert/stage`,
body: { messageID: input["messageID"], files: input["files"] },
successStatus: 200,
declaredStatuses: [404, 409, 500, 400, 401],
empty: false,
},
requestOptions,
).then((value) => value.data),
clear: (input: SessionRevertClearInput, requestOptions?: RequestOptions) =>
request<SessionRevertClearOutput>(
{
method: "POST",
path: `/api/session/${encodeURIComponent(input.sessionID)}/revert/clear`,
successStatus: 204,
declaredStatuses: [404, 409, 500, 400, 401],
empty: true,
},
requestOptions,
),
commit: (input: SessionRevertCommitInput, requestOptions?: RequestOptions) =>
request<SessionRevertCommitOutput>(
{
method: "POST",
path: `/api/session/${encodeURIComponent(input.sessionID)}/revert/commit`,
successStatus: 204,
declaredStatuses: [404, 409, 400, 401],
empty: true,
},
requestOptions,
),
},
context: (input: SessionContextInput, requestOptions?: RequestOptions) =>
request<{ readonly data: SessionContextOutput }>(
{
@@ -813,69 +838,73 @@ export function make(options: ClientOptions) {
},
requestOptions,
),
connectKey: (input: IntegrationConnectKeyInput, requestOptions?: RequestOptions) =>
request<IntegrationConnectKeyOutput>(
{
method: "POST",
path: `/api/integration/${encodeURIComponent(input.integrationID)}/connect/key`,
query: { location: input["location"] },
body: { key: input["key"], label: input["label"] },
successStatus: 204,
declaredStatuses: [400, 401],
empty: true,
},
requestOptions,
),
connectOauth: (input: IntegrationConnectOauthInput, requestOptions?: RequestOptions) =>
request<IntegrationConnectOauthOutput>(
{
method: "POST",
path: `/api/integration/${encodeURIComponent(input.integrationID)}/connect/oauth`,
query: { location: input["location"] },
body: { methodID: input["methodID"], inputs: input["inputs"], label: input["label"] },
successStatus: 200,
declaredStatuses: [400, 401],
empty: false,
},
requestOptions,
),
attemptStatus: (input: IntegrationAttemptStatusInput, requestOptions?: RequestOptions) =>
request<IntegrationAttemptStatusOutput>(
{
method: "GET",
path: `/api/integration/attempt/${encodeURIComponent(input.attemptID)}`,
query: { location: input["location"] },
successStatus: 200,
declaredStatuses: [401, 400],
empty: false,
},
requestOptions,
),
attemptComplete: (input: IntegrationAttemptCompleteInput, requestOptions?: RequestOptions) =>
request<IntegrationAttemptCompleteOutput>(
{
method: "POST",
path: `/api/integration/attempt/${encodeURIComponent(input.attemptID)}/complete`,
query: { location: input["location"] },
body: { code: input["code"] },
successStatus: 204,
declaredStatuses: [400, 401],
empty: true,
},
requestOptions,
),
attemptCancel: (input: IntegrationAttemptCancelInput, requestOptions?: RequestOptions) =>
request<IntegrationAttemptCancelOutput>(
{
method: "DELETE",
path: `/api/integration/attempt/${encodeURIComponent(input.attemptID)}`,
query: { location: input["location"] },
successStatus: 204,
declaredStatuses: [401, 400],
empty: true,
},
requestOptions,
),
connect: {
key: (input: IntegrationConnectKeyInput, requestOptions?: RequestOptions) =>
request<IntegrationConnectKeyOutput>(
{
method: "POST",
path: `/api/integration/${encodeURIComponent(input.integrationID)}/connect/key`,
query: { location: input["location"] },
body: { key: input["key"], label: input["label"] },
successStatus: 204,
declaredStatuses: [400, 401],
empty: true,
},
requestOptions,
),
oauth: (input: IntegrationConnectOauthInput, requestOptions?: RequestOptions) =>
request<IntegrationConnectOauthOutput>(
{
method: "POST",
path: `/api/integration/${encodeURIComponent(input.integrationID)}/connect/oauth`,
query: { location: input["location"] },
body: { methodID: input["methodID"], inputs: input["inputs"], label: input["label"] },
successStatus: 200,
declaredStatuses: [400, 401],
empty: false,
},
requestOptions,
),
},
attempt: {
status: (input: IntegrationAttemptStatusInput, requestOptions?: RequestOptions) =>
request<IntegrationAttemptStatusOutput>(
{
method: "GET",
path: `/api/integration/attempt/${encodeURIComponent(input.attemptID)}`,
query: { location: input["location"] },
successStatus: 200,
declaredStatuses: [401, 400],
empty: false,
},
requestOptions,
),
complete: (input: IntegrationAttemptCompleteInput, requestOptions?: RequestOptions) =>
request<IntegrationAttemptCompleteOutput>(
{
method: "POST",
path: `/api/integration/attempt/${encodeURIComponent(input.attemptID)}/complete`,
query: { location: input["location"] },
body: { code: input["code"] },
successStatus: 204,
declaredStatuses: [400, 401],
empty: true,
},
requestOptions,
),
cancel: (input: IntegrationAttemptCancelInput, requestOptions?: RequestOptions) =>
request<IntegrationAttemptCancelOutput>(
{
method: "DELETE",
path: `/api/integration/attempt/${encodeURIComponent(input.attemptID)}`,
query: { location: input["location"] },
successStatus: 204,
declaredStatuses: [401, 400],
empty: true,
},
requestOptions,
),
},
},
"server.mcp": {
list: (input?: ServerMcpListInput, requestOptions?: RequestOptions) =>
@@ -890,6 +919,20 @@ export function make(options: ClientOptions) {
},
requestOptions,
),
resource: {
catalog: (input?: ServerMcpResourceCatalogInput, requestOptions?: RequestOptions) =>
request<ServerMcpResourceCatalogOutput>(
{
method: "GET",
path: `/api/mcp/resource`,
query: { location: input?.["location"] },
successStatus: 200,
declaredStatuses: [401, 400],
empty: false,
},
requestOptions,
),
},
},
credential: {
update: (input: CredentialUpdateInput, requestOptions?: RequestOptions) =>
@@ -950,18 +993,20 @@ export function make(options: ClientOptions) {
),
},
form: {
listRequests: (input?: FormListRequestsInput, requestOptions?: RequestOptions) =>
request<FormListRequestsOutput>(
{
method: "GET",
path: `/api/form/request`,
query: { location: input?.["location"] },
successStatus: 200,
declaredStatuses: [401, 400],
empty: false,
},
requestOptions,
),
request: {
list: (input?: FormRequestListInput, requestOptions?: RequestOptions) =>
request<FormRequestListOutput>(
{
method: "GET",
path: `/api/form/request`,
query: { location: input?.["location"] },
successStatus: 200,
declaredStatuses: [401, 400],
empty: false,
},
requestOptions,
),
},
list: (input: FormListInput, requestOptions?: RequestOptions) =>
request<{ readonly data: FormListOutput }>(
{
@@ -1039,41 +1084,45 @@ export function make(options: ClientOptions) {
),
},
permission: {
listRequests: (input?: PermissionListRequestsInput, requestOptions?: RequestOptions) =>
request<PermissionListRequestsOutput>(
{
method: "GET",
path: `/api/permission/request`,
query: { location: input?.["location"] },
successStatus: 200,
declaredStatuses: [401, 400],
empty: false,
},
requestOptions,
),
listSaved: (input?: PermissionListSavedInput, requestOptions?: RequestOptions) =>
request<{ readonly data: PermissionListSavedOutput }>(
{
method: "GET",
path: `/api/permission/saved`,
query: { projectID: input?.["projectID"] },
successStatus: 200,
declaredStatuses: [401, 400],
empty: false,
},
requestOptions,
).then((value) => value.data),
removeSaved: (input: PermissionRemoveSavedInput, requestOptions?: RequestOptions) =>
request<PermissionRemoveSavedOutput>(
{
method: "DELETE",
path: `/api/permission/saved/${encodeURIComponent(input.id)}`,
successStatus: 204,
declaredStatuses: [401, 400],
empty: true,
},
requestOptions,
),
request: {
list: (input?: PermissionRequestListInput, requestOptions?: RequestOptions) =>
request<PermissionRequestListOutput>(
{
method: "GET",
path: `/api/permission/request`,
query: { location: input?.["location"] },
successStatus: 200,
declaredStatuses: [401, 400],
empty: false,
},
requestOptions,
),
},
saved: {
list: (input?: PermissionSavedListInput, requestOptions?: RequestOptions) =>
request<{ readonly data: PermissionSavedListOutput }>(
{
method: "GET",
path: `/api/permission/saved`,
query: { projectID: input?.["projectID"] },
successStatus: 200,
declaredStatuses: [401, 400],
empty: false,
},
requestOptions,
).then((value) => value.data),
remove: (input: PermissionSavedRemoveInput, requestOptions?: RequestOptions) =>
request<PermissionSavedRemoveOutput>(
{
method: "DELETE",
path: `/api/permission/saved/${encodeURIComponent(input.id)}`,
successStatus: 204,
declaredStatuses: [401, 400],
empty: true,
},
requestOptions,
),
},
create: (input: PermissionCreateInput, requestOptions?: RequestOptions) =>
request<{ readonly data: PermissionCreateOutput }>(
{
@@ -1355,18 +1404,20 @@ export function make(options: ClientOptions) {
),
},
question: {
listRequests: (input?: QuestionListRequestsInput, requestOptions?: RequestOptions) =>
request<QuestionListRequestsOutput>(
{
method: "GET",
path: `/api/question/request`,
query: { location: input?.["location"] },
successStatus: 200,
declaredStatuses: [401, 400],
empty: false,
},
requestOptions,
),
request: {
list: (input?: QuestionRequestListInput, requestOptions?: RequestOptions) =>
request<QuestionRequestListOutput>(
{
method: "GET",
path: `/api/question/request`,
query: { location: input?.["location"] },
successStatus: 200,
declaredStatuses: [401, 400],
empty: false,
},
requestOptions,
),
},
list: (input: QuestionListInput, requestOptions?: RequestOptions) =>
request<{ readonly data: QuestionListOutput }>(
{
@@ -1483,17 +1534,31 @@ export function make(options: ClientOptions) {
),
},
debug: {
location: (requestOptions?: RequestOptions) =>
request<DebugLocationOutput>(
{
method: "GET",
path: `/api/debug/location`,
successStatus: 200,
declaredStatuses: [401, 400],
empty: false,
},
requestOptions,
),
location: {
list: (requestOptions?: RequestOptions) =>
request<DebugLocationListOutput>(
{
method: "GET",
path: `/api/debug/location`,
successStatus: 200,
declaredStatuses: [401, 400],
empty: false,
},
requestOptions,
),
evict: (input?: DebugLocationEvictInput, requestOptions?: RequestOptions) =>
request<DebugLocationEvictOutput>(
{
method: "DELETE",
path: `/api/debug/location`,
query: { location: input?.["location"] },
successStatus: 204,
declaredStatuses: [401, 400],
empty: true,
},
requestOptions,
),
},
},
}
}
+111 -11
View File
@@ -526,6 +526,20 @@ export type SessionRenameInput = {
export type SessionRenameOutput = void
export type SessionMoveInput = {
readonly sessionID: { readonly sessionID: string }["sessionID"]
readonly destination: {
readonly destination: { readonly directory: string }
readonly moveChanges?: boolean | undefined
}["destination"]
readonly moveChanges?: {
readonly destination: { readonly directory: string }
readonly moveChanges?: boolean | undefined
}["moveChanges"]
}
export type SessionMoveOutput = void
export type SessionPromptInput = {
readonly sessionID: { readonly sessionID: string }["sessionID"]
readonly id?: {
@@ -856,17 +870,26 @@ export type SessionSyntheticInput = {
readonly text: string
readonly description?: string | null
readonly metadata?: { readonly [x: string]: JsonValue }
readonly resume?: boolean | null
}["text"]
readonly description?: {
readonly text: string
readonly description?: string | null
readonly metadata?: { readonly [x: string]: JsonValue }
readonly resume?: boolean | null
}["description"]
readonly metadata?: {
readonly text: string
readonly description?: string | null
readonly metadata?: { readonly [x: string]: JsonValue }
readonly resume?: boolean | null
}["metadata"]
readonly resume?: {
readonly text: string
readonly description?: string | null
readonly metadata?: { readonly [x: string]: JsonValue }
readonly resume?: boolean | null
}["resume"]
}
export type SessionSyntheticOutput = void
@@ -1416,6 +1439,13 @@ export type SessionLogOutput =
readonly sessionID: string
readonly assistantMessageID: string
readonly error: { readonly type: string; readonly message: string }
readonly cost?: number
readonly tokens?: {
readonly input: number
readonly output: number
readonly reasoning: number
readonly cache: { readonly read: number; readonly write: number }
}
}
}
| {
@@ -2478,6 +2508,36 @@ export type ServerMcpListOutput = {
}>
}
export type ServerMcpResourceCatalogInput = {
readonly location?: {
readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
}["location"]
}
export type ServerMcpResourceCatalogOutput = {
readonly location: {
readonly directory: string
readonly workspaceID?: string
readonly project: { readonly id: string; readonly directory: string }
}
readonly data: {
readonly resources: ReadonlyArray<{
readonly server: string
readonly name: string
readonly uri: string
readonly description?: string
readonly mimeType?: string
}>
readonly templates: ReadonlyArray<{
readonly server: string
readonly name: string
readonly uriTemplate: string
readonly description?: string
readonly mimeType?: string
}>
}
}
export type CredentialUpdateInput = {
readonly credentialID: { readonly credentialID: string }["credentialID"]
readonly location?: {
@@ -2525,13 +2585,13 @@ export type ProjectDirectoriesInput = {
export type ProjectDirectoriesOutput = ReadonlyArray<{ readonly directory: string; readonly strategy?: string }>
export type FormListRequestsInput = {
export type FormRequestListInput = {
readonly location?: {
readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
}["location"]
}
export type FormListRequestsOutput = {
export type FormRequestListOutput = {
readonly location: {
readonly directory: string
readonly workspaceID?: string
@@ -3599,13 +3659,13 @@ export type FormCancelInput = {
export type FormCancelOutput = void
export type PermissionListRequestsInput = {
export type PermissionRequestListInput = {
readonly location?: {
readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
}["location"]
}
export type PermissionListRequestsOutput = {
export type PermissionRequestListOutput = {
readonly location: {
readonly directory: string
readonly workspaceID?: string
@@ -3622,9 +3682,9 @@ export type PermissionListRequestsOutput = {
}>
}
export type PermissionListSavedInput = { readonly projectID?: { readonly projectID?: string | undefined }["projectID"] }
export type PermissionSavedListInput = { readonly projectID?: { readonly projectID?: string | undefined }["projectID"] }
export type PermissionListSavedOutput = {
export type PermissionSavedListOutput = {
readonly data: ReadonlyArray<{
readonly id: string
readonly projectID: string
@@ -3633,9 +3693,9 @@ export type PermissionListSavedOutput = {
}>
}["data"]
export type PermissionRemoveSavedInput = { readonly id: { readonly id: string }["id"] }
export type PermissionSavedRemoveInput = { readonly id: { readonly id: string }["id"] }
export type PermissionRemoveSavedOutput = void
export type PermissionSavedRemoveOutput = void
export type PermissionCreateInput = {
readonly sessionID: { readonly sessionID: string }["sessionID"]
@@ -4489,6 +4549,23 @@ export type EventSubscribeOutput =
readonly location?: { readonly directory: string; readonly workspaceID?: string }
readonly data: { readonly sessionID: string; readonly title: string }
}
| {
readonly id: string
readonly created: number
readonly metadata?: { readonly [x: string]: unknown }
readonly type: "session.usage.updated"
readonly location?: { readonly directory: string; readonly workspaceID?: string }
readonly data: {
readonly sessionID: string
readonly cost: number
readonly tokens: {
readonly input: number
readonly output: number
readonly reasoning: number
readonly cache: { readonly read: number; readonly write: number }
}
}
}
| {
readonly id: string
readonly created: number
@@ -4712,6 +4789,13 @@ export type EventSubscribeOutput =
readonly sessionID: string
readonly assistantMessageID: string
readonly error: { readonly type: string; readonly message: string }
readonly cost?: number
readonly tokens?: {
readonly input: number
readonly output: number
readonly reasoning: number
readonly cache: { readonly read: number; readonly write: number }
}
}
}
| {
@@ -5508,6 +5592,14 @@ export type EventSubscribeOutput =
readonly location?: { readonly directory: string; readonly workspaceID?: string }
readonly data: { readonly server: string }
}
| {
readonly id: string
readonly created: number
readonly metadata?: { readonly [x: string]: unknown }
readonly type: "mcp.resources.changed"
readonly location?: { readonly directory: string; readonly workspaceID?: string }
readonly data: { readonly server: string }
}
| {
readonly id: string
readonly created: number
@@ -5957,13 +6049,13 @@ export type ShellRemoveInput = {
export type ShellRemoveOutput = void
export type QuestionListRequestsInput = {
export type QuestionRequestListInput = {
readonly location?: {
readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
}["location"]
}
export type QuestionListRequestsOutput = {
export type QuestionRequestListOutput = {
readonly location: {
readonly directory: string
readonly workspaceID?: string
@@ -6129,4 +6221,12 @@ export type VcsDiffOutput = {
}>
}
export type DebugLocationOutput = ReadonlyArray<{ readonly directory: string; readonly workspaceID?: string }>
export type DebugLocationListOutput = ReadonlyArray<{ readonly directory: string; readonly workspaceID?: string }>
export type DebugLocationEvictInput = {
readonly location?: {
readonly location?: { readonly directory?: string | undefined; readonly workspace?: string | undefined } | undefined
}["location"]
}
export type DebugLocationEvictOutput = void
@@ -19,7 +19,7 @@ import { SessionMessage } from "@opencode-ai/schema/session-message"
import { Workspace } from "@opencode-ai/schema/workspace"
import { Api } from "@opencode-ai/server/api"
import { compile, emitPromise } from "@opencode-ai/httpapi-codegen"
import { ClientApi, endpointNames, groupNames, promiseOmitEndpoints } from "../src/contract"
import { ClientApi, groupNames, promiseOmitEndpoints } from "../src/contract"
const Client = await import("../src/effect")
@@ -49,8 +49,8 @@ test("Core and Server reuse the authoritative Schema and Protocol values", () =>
})
test("client and Server contracts generate identically", () => {
const server = compile(Api, { groupNames, endpointNames, omitEndpoints: promiseOmitEndpoints })
const client = compile(ClientApi, { groupNames, endpointNames, omitEndpoints: promiseOmitEndpoints })
const server = compile(Api, { groupNames, omitEndpoints: promiseOmitEndpoints })
const client = compile(ClientApi, { groupNames, omitEndpoints: promiseOmitEndpoints })
expect(emitPromise(client)).toEqual(emitPromise(server))
})
+27 -9
View File
@@ -33,16 +33,11 @@ test("exposes every standard HTTP API group", () => {
"debug",
])
expect(Object.keys(client.debug)).toEqual(["location"])
expect(Object.keys(client.debug.location)).toEqual(["list", "evict"])
expect(Object.keys(client.message)).toEqual(["list"])
expect(Object.keys(client.integration)).toEqual([
"list",
"get",
"connectKey",
"connectOauth",
"attemptStatus",
"attemptComplete",
"attemptCancel",
])
expect(Object.keys(client.integration)).toEqual(["list", "get", "connect", "attempt"])
expect(Object.keys(client.integration.connect)).toEqual(["key", "oauth"])
expect(Object.keys(client.integration.attempt)).toEqual(["status", "complete", "cancel"])
expect(Object.keys(client.file)).toEqual(["read", "list", "find"])
expect(Object.keys(client.vcs)).toEqual(["status", "diff"])
expect(Object.keys(client.pty)).toEqual(["list", "create", "get", "update", "remove"])
@@ -50,6 +45,29 @@ test("exposes every standard HTTP API group", () => {
expect(Object.keys(client.project)).toEqual(["list", "current", "directories"])
})
test("MCP resource catalog uses the public HTTP contract", async () => {
let request: Request | undefined
const client = OpenCode.make({
baseUrl: "http://localhost:3000",
fetch: async (input) => {
request = input instanceof Request ? input : new Request(input)
return Response.json({
location: { directory: "/tmp/project", project: { id: "proj_test", directory: "/tmp/project" } },
data: {
resources: [{ server: "docs", name: "Readme", uri: "docs://readme" }],
templates: [{ server: "docs", name: "File", uriTemplate: "docs://{path}" }],
},
})
},
})
const result = await client["server.mcp"].resource.catalog({ location: { directory: "/tmp/project" } })
expect(result.data.resources[0]?.uri).toBe("docs://readme")
expect(request?.method).toBe("GET")
expect(request?.url).toBe("http://localhost:3000/api/mcp/resource?location%5Bdirectory%5D=%2Ftmp%2Fproject")
})
test("file.read returns binary content from the public HTTP contract", async () => {
let request: Request | undefined
const client = OpenCode.make({
+2 -2
View File
@@ -62,7 +62,7 @@ const result =
`result` is always a `CodeMode.Result`. Program, validation, limit, and tool failures are returned as diagnostics rather than failing the Effect. Host interruption remains interruption.
Successful result values are JSON-safe data. A program that returns `undefined`, including by reaching the end without `return`, produces `null`; nested `undefined` values are normalized to `null` as well.
Successful result values are JSON-safe data. An explicit `return` produces the program result; when it is omitted, the final executable top-level expression is returned as a model-friendly REPL convenience. Otherwise reaching the end produces `null`. Returned `undefined` and nested `undefined` values are normalized to `null` as well.
## API
@@ -241,7 +241,7 @@ CodeMode executes a deliberately bounded JavaScript subset. It supports:
- `if`, conditional expressions, `switch`, `for`, `for...of` (arrays, strings, Maps, Sets, including assignment-form destructuring such as `for ([key, value] of entries)`), `for...in` (own keys of plain objects, index strings of arrays, and namespace/tool names of `tools` references - anything else is an error suggesting `for...of` or `Object.keys`, rather than real JS's surprising behavior of indices for strings and zero iterations for Maps/Sets), `while`, and `do...while`.
- Arrow functions and function declarations with closures, defaults, rest parameters, and destructuring.
- Optional chaining, nullish coalescing, templates, spread (arrays, strings, Maps, Sets), and `try`/`catch`.
- Common array, string, number, `Object`, `Math`, and `JSON` operations. Mutating array methods include `push`/`pop`/`shift`/`unshift`/`splice` (removes in place and returns the removed elements)/`fill`/`copyWithin`; array `keys`/`values`/`entries` return **arrays** (matching the Map/Set convention) and work with `for...of` and spread. String methods include `localeCompare` (locale/options arguments ignored), `normalize`, and the `trimLeft`/`trimRight` aliases. `Object.keys` also accepts arrays (index strings, as in JS) and tool references: `Object.keys(tools)` lists the top-level namespaces, including `$codemode`, and `Object.keys(tools.ns)` lists the names at that node (a callable tool enumerates as `[]`; an unknown path is an `UnknownTool` diagnostic). `Object.values`/`Object.entries` on a tool reference fail with a pointer at `Object.keys(tools)` and `tools.$codemode.search`.
- Common array, string, number, `Object`, `Math`, and `JSON` operations, including primitive-number `valueOf`, the standard non-finite `Number` constants, and host-backed `Math.random`. Mutating array methods include `push`/`pop`/`shift`/`unshift`/`splice` (removes in place and returns the removed elements)/`fill`/`copyWithin`; array `keys`/`values`/`entries` return **arrays** (matching the Map/Set convention) and work with `for...of` and spread. String methods include `localeCompare` (locale/options arguments ignored), `normalize`, and the `trimLeft`/`trimRight` aliases. `Object.keys` also accepts arrays (index strings, as in JS) and tool references: `Object.keys(tools)` lists the top-level namespaces, including `$codemode`, and `Object.keys(tools.ns)` lists the names at that node (a callable tool enumerates as `[]`; an unknown path is an `UnknownTool` diagnostic). `Object.values`/`Object.entries` on a tool reference fail with a pointer at `Object.keys(tools)` and `tools.$codemode.search`.
- `Date` - `Date.now()`/`Date.parse()`/`Date.UTC()`, `new Date(...)`, the getter methods, and date arithmetic/comparison via the time value. Dates stringify as ISO (`toString` included, for determinism across host timezones).
- Regular expressions - `/literals/` and `new RegExp(...)` with `test`/`exec` (stateful `lastIndex` for `g`), plus string `match`/`matchAll`/`replace`/`replaceAll`/`split`/`search` with patterns. Match results are arrays carrying `index` and named `groups` as own properties (`input` is omitted). `replace` and `replaceAll` accept function replacers with captures, offset, input, and named groups; callbacks run sequentially, may await tool calls, and have their results coerced to strings. Invalid patterns, invalid flags, and missing-`g` calls fail with catchable errors that say what was wrong and how to fix it (escaping hints, the exact `/pattern/g` to write). Patterns run on the host engine, so pathological backtracking is bounded only by the execution timeout.
- `Map` and `Set` - construction from entries/arrays/strings, `get`/`set`/`add`/`has`/`delete`/`clear`/`size`/`forEach`, and `keys`/`values`/`entries` returning **arrays** (not iterators).
+1 -1
View File
@@ -158,7 +158,7 @@ current omissions to implement, not intentional product boundaries.
- [ ] Add `Object.is` after runtime method and tool references have stable identity semantics.
- [ ] Add deterministic modern collection conveniences where they improve orchestration: `Object.groupBy`, Set
composition methods, and `Array.prototype.toSpliced`.
- [ ] Decide whether nondeterministic `Math.random` and iterable `Math.sumPrecise` belong in the runtime.
- [ ] Decide whether iterable `Math.sumPrecise` belongs in the runtime.
- [ ] Refine diagnostics so user throws, expected tool failures, unexpected host/tool defects, and genuine interpreter
defects are distinguishable without leaking private causes.
@@ -30,7 +30,6 @@ export type Binding = {
export type StatementResult =
| { kind: "none" }
| { kind: "value"; value: unknown }
| { kind: "return"; value: unknown }
| { kind: "break" }
| { kind: "continue" }
+24 -37
View File
@@ -530,17 +530,29 @@ const invokeArrayStatic = (name: string, args: Array<unknown>, node: AstNode): u
if (args[0] instanceof SandboxURLSearchParams) {
return Array.from(args[0].params.entries(), ([key, value]) => [key, value])
}
const source = boundedData(args[0], "Array.from input")
const source = args[0]
if (source instanceof SandboxPromise) {
throw new InterpreterRuntimeError(
"Array.from received an un-awaited Promise; await it before creating the array.",
node,
"InvalidDataValue",
)
}
if (typeof source === "string") return Array.from(source)
if (Array.isArray(source)) return [...source]
if (
source !== null &&
typeof source === "object" &&
(Object.getPrototypeOf(source) === Object.prototype || Object.getPrototypeOf(source) === null) &&
typeof (source as { length?: unknown }).length === "number"
) {
return Array.from(source as ArrayLike<unknown>)
}
throw new InterpreterRuntimeError("Array.from expects an array, string, Map, Set, or array-like value.", node)
throw new InterpreterRuntimeError(
"Array.from expects an array, string, Map, Set, or array-like value.",
node,
"InvalidDataValue",
)
}
default:
throw new InterpreterRuntimeError(`Array.${name} is not available in CodeMode.`, node)
@@ -606,7 +618,6 @@ class Interpreter<R> {
// ToolRuntime.make like invokeTool: the interpreter never holds the tree itself.
private readonly toolKeys: (path: ReadonlyArray<string>) => ReadonlyArray<string>
private readonly logs: Array<string>
private lastValue: unknown
// Caps how many eagerly forked tool calls run at once (the parallel-call concurrency cap).
private readonly callPermits: Semaphore.Semaphore
// Fiber-backed promises whose settlement no program construct has observed yet. Successful
@@ -625,7 +636,6 @@ class Interpreter<R> {
this.invokeTool = invokeTool
this.toolKeys = toolKeys
this.logs = logs
this.lastValue = undefined
this.callPermits = shared?.callPermits ?? Semaphore.makeUnsafe(TOOL_CALL_CONCURRENCY)
this.pendingSettlements = shared?.pendingSettlements ?? new Set<SandboxPromise>()
globalScope.set("tools", { mutable: false, value: new ToolReference([]) })
@@ -671,13 +681,15 @@ class Interpreter<R> {
return Effect.gen(function* () {
self.hoistFunctions(program.body)
let value: unknown = undefined
let returned = false
for (const statement of program.body) {
for (const [index, statement] of program.body.entries()) {
if (index === program.body.length - 1 && statement.type === "ExpressionStatement") {
value = yield* self.evaluateExpression(getNode(statement, "expression"))
break
}
const result = yield* self.evaluateStatement(statement)
if (result.kind === "return") {
value = result.value
returned = true
break
}
@@ -685,11 +697,7 @@ class Interpreter<R> {
throw new InterpreterRuntimeError(`Unexpected '${result.kind}' outside of a loop.`, statement)
}
if (result.kind === "value") {
self.lastValue = result.value
}
}
if (!returned) value = self.lastValue
// The program body runs inside an implicit async function, so a returned promise
// resolves before crossing the data boundary - `return tools.ns.tool(...)` works
@@ -780,7 +788,7 @@ class Interpreter<R> {
private evaluateStatement(node: AstNode): Effect.Effect<StatementResult, unknown, R> {
switch (node.type) {
case "ExpressionStatement":
return Effect.map(this.evaluateExpression(getNode(node, "expression")), (value) => ({ kind: "value", value }))
return Effect.as(this.evaluateExpression(getNode(node, "expression")), { kind: "none" })
case "VariableDeclaration":
return Effect.map(this.evaluateVariableDeclaration(node), () => ({ kind: "none" }))
case "ReturnStatement": {
@@ -833,11 +841,6 @@ class Interpreter<R> {
const statement = asNode(statementValue, "body")
const result = yield* self.evaluateStatement(statement)
if (result.kind === "value") {
self.lastValue = result.value
continue
}
if (result.kind !== "none") {
return result
}
@@ -929,7 +932,6 @@ class Interpreter<R> {
const result = yield* self.evaluateStatement(asNode(statementValue, "consequent"))
if (result.kind === "break") return { kind: "none" } satisfies StatementResult
if (result.kind === "return" || result.kind === "continue") return result
if (result.kind === "value") self.lastValue = result.value
}
}
return { kind: "none" } satisfies StatementResult
@@ -957,9 +959,6 @@ class Interpreter<R> {
return result
}
if (result.kind === "value") {
self.lastValue = result.value
}
}
return { kind: "none" } satisfies StatementResult
@@ -987,9 +986,6 @@ class Interpreter<R> {
return result
}
if (result.kind === "value") {
self.lastValue = result.value
}
} while (yield* self.evaluateExpression(testNode))
return { kind: "none" } satisfies StatementResult
@@ -1045,10 +1041,6 @@ class Interpreter<R> {
return { kind: "none" } satisfies StatementResult
}
if (result.kind === "value") {
self.lastValue = result.value
}
if (iterationScope) {
const loopScope = self.currentScope()
for (const name of perIterationBindings) {
@@ -1133,10 +1125,6 @@ class Interpreter<R> {
return { kind: "none" }
}
if (result.kind === "value") {
self.lastValue = result.value
}
if (result.kind === "continue") {
continue
}
@@ -1226,10 +1214,6 @@ class Interpreter<R> {
return { kind: "none" }
}
if (result.kind === "value") {
self.lastValue = result.value
}
if (result.kind === "continue") {
continue
}
@@ -2033,6 +2017,9 @@ class Interpreter<R> {
if (callable.namespace === "Object" && objectMethodsPreservingIdentity.has(callable.name)) {
return invokeGlobalMethod(callable, args, node)
}
if (callable.namespace === "Array" && (callable.name === "from" || callable.name === "of")) {
return invokeGlobalMethod(callable, args, node)
}
return boundedData(invokeGlobalMethod(callable, args, node), `${callable.namespace}.${callable.name} result`)
}
if (callable instanceof CoercionFunction) {
@@ -2372,7 +2359,7 @@ class Interpreter<R> {
if (fn.body.type === "BlockStatement") {
const result = yield* invocation.evaluateStatement(fn.body)
return result.kind === "return" || result.kind === "value" ? result.value : undefined
return result.kind === "return" ? result.value : undefined
}
return yield* invocation.evaluateExpression(fn.body)
+2
View File
@@ -1,6 +1,7 @@
export const mathConstants = new Set(["PI", "E", "LN2", "LN10", "LOG2E", "LOG10E", "SQRT2", "SQRT1_2"])
export const mathMethods = new Set([
"random",
"max",
"min",
"abs",
@@ -40,6 +41,7 @@ export const mathMethods = new Set([
export const invokeMathMethod = (name: string, args: Array<unknown>, node: AstNode): number => {
if (!mathMethods.has(name)) throw new InterpreterRuntimeError(`Math.${name} is not available in CodeMode.`, node)
if (name === "random") return Math.random()
const nums = args.map((arg) => {
if (typeof arg !== "number") throw new InterpreterRuntimeError(`Math.${name} expects number arguments.`, node)
return arg
+14 -2
View File
@@ -1,6 +1,15 @@
export const numberMethods = new Set(["toFixed", "toPrecision", "toExponential", "toString"])
export const numberMethods = new Set(["toFixed", "toPrecision", "toExponential", "toString", "valueOf"])
export const numberConstants = new Set(["MAX_SAFE_INTEGER", "MIN_SAFE_INTEGER", "MAX_VALUE", "MIN_VALUE", "EPSILON"])
export const numberConstants = new Set([
"MAX_SAFE_INTEGER",
"MIN_SAFE_INTEGER",
"MAX_VALUE",
"MIN_VALUE",
"EPSILON",
"NaN",
"POSITIVE_INFINITY",
"NEGATIVE_INFINITY",
])
export const numberStatics = new Set(["isInteger", "isFinite", "isNaN", "isSafeInteger", "parseInt", "parseFloat"])
@@ -32,6 +41,9 @@ export const invokeNumberMethod = (value: number, name: string, args: Array<unkn
result = value.toString(radix)
break
}
case "valueOf":
result = value
break
default:
throw new InterpreterRuntimeError(`Number method '${name}' is not available in CodeMode.`, node)
}
+18 -15
View File
@@ -1,6 +1,6 @@
import { type AstNode, InterpreterRuntimeError } from "../interpreter/model.js"
import { isBlockedMember } from "../tool-runtime.js"
import { isSandboxValue, SandboxMap, SandboxSet, SandboxURLSearchParams } from "../values.js"
import { isSandboxValue, SandboxMap, SandboxPromise, SandboxSet, SandboxURLSearchParams } from "../values.js"
import { boundedData, coerceToString } from "./value.js"
export const objectStatics = new Set(["keys", "values", "entries", "hasOwn", "assign", "fromEntries"])
@@ -10,13 +10,23 @@ export const invokeObjectMethod = (name: string, args: Array<unknown>, node: Ast
if (!objectStatics.has(name)) throw new InterpreterRuntimeError(`Object.${name} is not available in CodeMode.`, node)
const requireObject = (): Record<string, unknown> => {
const input = args[0]
const value = boundedData(args[0], `Object.${name} input`)
if (Array.isArray(input)) return input as unknown as Record<string, unknown>
if (isSandboxValue(value)) return {}
if (value === null || typeof value !== "object") {
throw new InterpreterRuntimeError(`Object.${name} expects a data object or array.`, node)
if (isSandboxValue(input)) return {}
if (input instanceof SandboxPromise) {
throw new InterpreterRuntimeError(
`Object.${name} received an un-awaited Promise; await it before inspecting the result.`,
node,
"InvalidDataValue",
)
}
return value as Record<string, unknown>
if (input === null || typeof input !== "object") {
throw new InterpreterRuntimeError(`Object.${name} expects a data object or array.`, node, "InvalidDataValue")
}
const prototype = Object.getPrototypeOf(input)
if (prototype !== null && prototype !== Object.prototype) {
throw new InterpreterRuntimeError(`Object.${name} expects a data object or array.`, node, "InvalidDataValue")
}
return input as Record<string, unknown>
}
const guardedSet = (out: Record<string, unknown>, key: string, item: unknown): void => {
if (isBlockedMember(key)) throw new InterpreterRuntimeError(`Property '${key}' is not available in CodeMode.`, node)
@@ -28,15 +38,8 @@ export const invokeObjectMethod = (name: string, args: Array<unknown>, node: Ast
guardedSet(out, coerceToString(key), item)
}
switch (name) {
case "keys": {
const value = boundedData(args[0], "Object.keys input")
if (isSandboxValue(value)) return []
if (Array.isArray(value)) return Object.keys(value)
if (value === null || typeof value !== "object") {
throw new InterpreterRuntimeError("Object.keys expects a data object or array.", node)
}
return Object.keys(value)
}
case "keys":
return Object.keys(requireObject())
case "values":
return Object.values(requireObject())
case "entries":
+1
View File
@@ -617,6 +617,7 @@ export const prepare = <R>(tools: HostTools<R>, catalogBudget = defaultCatalogBu
"",
"Use common JavaScript data operations, functions, control flow, selected standard-library methods, and awaited tool calls. Built-ins include Date, RegExp, Map, Set, URL, URLSearchParams, and URI encoding helpers.",
"Modules/imports, classes, generators, timers, fetch, eval, prototype access, unlisted methods, and promise chaining are unavailable. Use Code Mode tools for external operations. Use await with try/catch.",
"Prefer explicit `return`; otherwise only the final top-level expression becomes the result.",
"Dates and URLs serialize to strings at data boundaries; Map/Set/RegExp/URLSearchParams serialize to `{}`.",
]
+21
View File
@@ -658,6 +658,9 @@ describe("CodeMode public contract", () => {
expect(instructions).toContain("URL, URLSearchParams, and URI encoding helpers")
expect(instructions).not.toContain("host globals")
expect(instructions).toContain("Use Code Mode tools for external operations")
expect(instructions).toContain(
"Prefer explicit `return`; otherwise only the final top-level expression becomes the result.",
)
expect(instructions).toContain(
"Dates and URLs serialize to strings at data boundaries; Map/Set/RegExp/URLSearchParams serialize to `{}`.",
)
@@ -1081,6 +1084,24 @@ describe("CodeMode public contract", () => {
expect(Schema.decodeUnknownSync(CodeMode.Result)(JSON.parse(JSON.stringify(result)))).toStrictEqual(result)
})
test("returns the final top-level expression when return is omitted", async () => {
const result = await Effect.runPromise(CodeMode.execute({ code: `1; 2` }))
expect(result).toStrictEqual({ ok: true, value: 2, toolCalls: [] })
})
test("does not implicitly return expressions nested in control flow", async () => {
const result = await Effect.runPromise(CodeMode.execute({ code: `if (true) { 2 }` }))
expect(result).toStrictEqual({ ok: true, value: null, toolCalls: [] })
})
test("returns null when the final top-level statement is not an expression", async () => {
const result = await Effect.runPromise(CodeMode.execute({ code: `1; const value = 2` }))
expect(result).toStrictEqual({ ok: true, value: null, toolCalls: [] })
})
test("rejects invalid configuration and discovery limits", async () => {
expect(() => CodeMode.execute({ code: "return 1", limits: { timeoutMs: 0 } })).toThrow(RangeError)
expect(() => CodeMode.execute({ code: "return 1", limits: { timeoutMs: Number.POSITIVE_INFINITY } })).toThrow(
+3473 -3249
View File
@@ -9,7 +9,7 @@
"/api/health": {
"get": {
"tags": [
"server.health"
"health"
],
"operationId": "v2.health.get",
"parameters": [],
@@ -27,10 +27,23 @@
"enum": [
true
]
},
"version": {
"type": "string"
},
"pid": {
"type": "integer",
"allOf": [
{
"exclusiveMinimum": 0
}
]
}
},
"required": [
"healthy"
"healthy",
"version",
"pid"
],
"additionalProperties": false
}
@@ -65,7 +78,7 @@
"/api/location": {
"get": {
"tags": [
"server.location"
"location"
],
"operationId": "v2.location.get",
"parameters": [
@@ -150,7 +163,7 @@
"/api/agent": {
"get": {
"tags": [
"server.agent"
"agent"
],
"operationId": "v2.agent.list",
"parameters": [
@@ -251,7 +264,7 @@
"/api/plugin": {
"get": {
"tags": [
"plugins"
"plugin"
],
"operationId": "v2.plugin.list",
"parameters": [
@@ -352,7 +365,7 @@
"/api/session": {
"get": {
"tags": [
"sessions"
"session"
],
"operationId": "v2.session.list",
"parameters": [
@@ -568,7 +581,7 @@
},
"post": {
"tags": [
"sessions"
"session"
],
"operationId": "v2.session.create",
"parameters": [],
@@ -679,7 +692,7 @@
"/api/session/active": {
"get": {
"tags": [
"sessions"
"session"
],
"operationId": "v2.session.active",
"parameters": [],
@@ -699,14 +712,10 @@
"$ref": "#/components/schemas/SessionActive"
}
}
},
"watermarks": {
"$ref": "#/components/schemas/SessionWatermarks"
}
},
"required": [
"data",
"watermarks"
"data"
],
"additionalProperties": false
}
@@ -734,14 +743,14 @@
}
}
},
"description": "Retrieve foreground Session drains currently owned by this OpenCode process. Sessions absent from the result are inactive. Watermarks are the durable log positions read alongside the activity snapshot; activity itself is process state, so the pairing is advisory rather than transactional.",
"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": [
"sessions"
"session"
],
"operationId": "v2.session.get",
"parameters": [
@@ -820,12 +829,78 @@
},
"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": "<No Content>"
},
"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": [
"sessions"
"session"
],
"operationId": "v2.session.fork",
"parameters": [
@@ -940,7 +1015,7 @@
"/api/session/{sessionID}/agent": {
"post": {
"tags": [
"sessions"
"session"
],
"operationId": "v2.session.switchAgent",
"parameters": [
@@ -1027,7 +1102,7 @@
"/api/session/{sessionID}/model": {
"post": {
"tags": [
"sessions"
"session"
],
"operationId": "v2.session.switchModel",
"parameters": [
@@ -1114,7 +1189,7 @@
"/api/session/{sessionID}/rename": {
"post": {
"tags": [
"sessions"
"session"
],
"operationId": "v2.session.rename",
"parameters": [
@@ -1198,10 +1273,123 @@
}
}
},
"/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": "<No Content>"
},
"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": [
"sessions"
"session"
],
"operationId": "v2.session.prompt",
"parameters": [
@@ -1245,7 +1433,14 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
"anyOf": [
{
"$ref": "#/components/schemas/InvalidRequestError1"
},
{
"$ref": "#/components/schemas/InvalidRequestError"
}
]
}
}
}
@@ -1353,7 +1548,7 @@
"/api/session/{sessionID}/command": {
"post": {
"tags": [
"sessions"
"session"
],
"operationId": "v2.session.command",
"parameters": [
@@ -1397,7 +1592,14 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InvalidRequestError"
"anyOf": [
{
"$ref": "#/components/schemas/InvalidRequestError1"
},
{
"$ref": "#/components/schemas/InvalidRequestError"
}
]
}
}
}
@@ -1560,7 +1762,7 @@
"/api/session/{sessionID}/skill": {
"post": {
"tags": [
"sessions"
"session"
],
"operationId": "v2.session.skill",
"parameters": [
@@ -1675,7 +1877,7 @@
"/api/session/{sessionID}/synthetic": {
"post": {
"tags": [
"sessions"
"session"
],
"operationId": "v2.session.synthetic",
"parameters": [
@@ -1759,6 +1961,16 @@
},
"metadata": {
"type": "object"
},
"resume": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
]
}
},
"required": [
@@ -1772,12 +1984,12 @@
}
}
},
"/api/session/{sessionID}/compact": {
"/api/session/{sessionID}/shell": {
"post": {
"tags": [
"sessions"
"session"
],
"operationId": "v2.session.compact",
"operationId": "v2.session.shell",
"parameters": [
{
"name": "sessionID",
@@ -1818,6 +2030,124 @@
}
}
},
"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": {
@@ -1836,44 +2166,52 @@
}
},
"409": {
"description": "SessionBusyError",
"description": "ConflictError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SessionBusyError"
}
}
}
},
"500": {
"description": "UnknownError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UnknownError"
}
}
}
},
"503": {
"description": "ServiceUnavailableError",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ServiceUnavailableError"
"$ref": "#/components/schemas/ConflictError"
}
}
}
}
},
"description": "Compact a session conversation.",
"summary": "Compact session"
"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": [
"sessions"
"session"
],
"operationId": "v2.session.wait",
"parameters": [
@@ -1951,7 +2289,7 @@
"/api/session/{sessionID}/revert/stage": {
"post": {
"tags": [
"sessions"
"session"
],
"operationId": "v2.session.revert.stage",
"parameters": [
@@ -2092,7 +2430,7 @@
"/api/session/{sessionID}/revert/clear": {
"post": {
"tags": [
"sessions"
"session"
],
"operationId": "v2.session.revert.clear",
"parameters": [
@@ -2179,7 +2517,7 @@
"/api/session/{sessionID}/revert/commit": {
"post": {
"tags": [
"sessions"
"session"
],
"operationId": "v2.session.revert.commit",
"parameters": [
@@ -2256,7 +2594,7 @@
"/api/session/{sessionID}/context": {
"get": {
"tags": [
"sessions"
"session"
],
"operationId": "v2.session.context",
"parameters": [
@@ -2353,7 +2691,7 @@
"/api/session/{sessionID}/instructions/entries": {
"get": {
"tags": [
"sessions"
"session"
],
"operationId": "v2.session.instructions.entry.list",
"parameters": [
@@ -2440,7 +2778,7 @@
"/api/session/{sessionID}/instructions/entries/{key}": {
"put": {
"tags": [
"sessions"
"session"
],
"operationId": "v2.session.instructions.entry.put",
"parameters": [
@@ -2531,7 +2869,7 @@
},
"delete": {
"tags": [
"sessions"
"session"
],
"operationId": "v2.session.instructions.entry.remove",
"parameters": [
@@ -2604,10 +2942,10 @@
"summary": "Remove instruction entry"
}
},
"/api/session/{sessionID}/log": {
"/api/experimental/session/{sessionID}/log": {
"get": {
"tags": [
"sessions"
"session"
],
"operationId": "v2.session.log",
"parameters": [
@@ -2809,14 +3147,14 @@
}
}
},
"description": "Durable, ordered, gap-free read of public session events after an exclusive aggregate sequence. Emits a synced marker once replay reaches the captured watermark, then completes; with follow=true it continues with live events instead. The only event API that promises reliability: attach after a snapshot watermark to compose fetch and stream without a race window.",
"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": [
"sessions"
"session"
],
"operationId": "v2.session.interrupt",
"parameters": [
@@ -2884,7 +3222,7 @@
"/api/session/{sessionID}/background": {
"post": {
"tags": [
"sessions"
"session"
],
"operationId": "v2.session.background",
"parameters": [
@@ -2952,7 +3290,7 @@
"/api/session/{sessionID}/message/{messageID}": {
"get": {
"tags": [
"sessions"
"session"
],
"operationId": "v2.session.message",
"parameters": [
@@ -3052,9 +3390,9 @@
"/api/session/{sessionID}/message": {
"get": {
"tags": [
"messages"
"session"
],
"operationId": "v2.session.messages",
"operationId": "v2.message.list",
"parameters": [
{
"name": "sessionID",
@@ -3196,7 +3534,7 @@
"/api/model": {
"get": {
"tags": [
"models"
"model"
],
"operationId": "v2.model.list",
"parameters": [
@@ -3307,7 +3645,7 @@
"/api/model/default": {
"get": {
"tags": [
"models"
"model"
],
"operationId": "v2.model.default",
"parameters": [
@@ -3553,7 +3891,7 @@
"/api/provider": {
"get": {
"tags": [
"providers"
"provider"
],
"operationId": "v2.provider.list",
"parameters": [
@@ -3664,7 +4002,7 @@
"/api/provider/{providerID}": {
"get": {
"tags": [
"providers"
"provider"
],
"operationId": "v2.provider.get",
"parameters": [
@@ -3790,7 +4128,7 @@
"/api/integration": {
"get": {
"tags": [
"integrations"
"integration"
],
"operationId": "v2.integration.list",
"parameters": [
@@ -3891,7 +4229,7 @@
"/api/integration/{integrationID}": {
"get": {
"tags": [
"integrations"
"integration"
],
"operationId": "v2.integration.get",
"parameters": [
@@ -4004,7 +4342,7 @@
"/api/integration/{integrationID}/connect/key": {
"post": {
"tags": [
"integrations"
"integration"
],
"operationId": "v2.integration.connect.key",
"parameters": [
@@ -4126,7 +4464,7 @@
"/api/integration/{integrationID}/connect/oauth": {
"post": {
"tags": [
"integrations"
"integration"
],
"operationId": "v2.integration.connect.oauth",
"parameters": [
@@ -4275,7 +4613,7 @@
"/api/integration/attempt/{attemptID}": {
"get": {
"tags": [
"integrations"
"integration"
],
"operationId": "v2.integration.attempt.status",
"parameters": [
@@ -4379,7 +4717,7 @@
},
"delete": {
"tags": [
"integrations"
"integration"
],
"operationId": "v2.integration.attempt.cancel",
"parameters": [
@@ -4465,7 +4803,7 @@
"/api/integration/attempt/{attemptID}/complete": {
"post": {
"tags": [
"integrations"
"integration"
],
"operationId": "v2.integration.attempt.complete",
"parameters": [
@@ -4679,10 +5017,108 @@
"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": [
"server.credential"
"credential"
],
"operationId": "v2.credential.update",
"parameters": [
@@ -4785,7 +5221,7 @@
},
"delete": {
"tags": [
"server.credential"
"credential"
],
"operationId": "v2.credential.remove",
"parameters": [
@@ -4868,10 +5304,57 @@
"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": [
"projects"
"project"
],
"operationId": "v2.project.current",
"parameters": [
@@ -4956,7 +5439,7 @@
"/api/project/{projectID}/directories": {
"get": {
"tags": [
"projects"
"project"
],
"operationId": "v2.project.directories",
"parameters": [
@@ -5049,7 +5532,7 @@
"/api/form/request": {
"get": {
"tags": [
"forms"
"form"
],
"operationId": "v2.form.request.list",
"parameters": [
@@ -5157,7 +5640,7 @@
"/api/session/{sessionID}/form": {
"get": {
"tags": [
"forms"
"form"
],
"operationId": "v2.session.form.list",
"parameters": [
@@ -5244,7 +5727,7 @@
},
"post": {
"tags": [
"forms"
"form"
],
"operationId": "v2.session.form.create",
"parameters": [
@@ -5357,7 +5840,7 @@
"/api/session/{sessionID}/form/{formID}": {
"get": {
"tags": [
"forms"
"form"
],
"operationId": "v2.session.form.get",
"parameters": [
@@ -5459,7 +5942,7 @@
"/api/session/{sessionID}/form/{formID}/state": {
"get": {
"tags": [
"forms"
"form"
],
"operationId": "v2.session.form.state",
"parameters": [
@@ -5554,7 +6037,7 @@
"/api/session/{sessionID}/form/{formID}/reply": {
"post": {
"tags": [
"forms"
"form"
],
"operationId": "v2.session.form.reply",
"parameters": [
@@ -5660,7 +6143,7 @@
"/api/session/{sessionID}/form/{formID}/cancel": {
"post": {
"tags": [
"forms"
"form"
],
"operationId": "v2.session.form.cancel",
"parameters": [
@@ -5749,7 +6232,7 @@
"/api/permission/request": {
"get": {
"tags": [
"permissions"
"permission"
],
"operationId": "v2.permission.request.list",
"parameters": [
@@ -5850,7 +6333,7 @@
"/api/permission/saved": {
"get": {
"tags": [
"permissions"
"permission"
],
"operationId": "v2.permission.saved.list",
"parameters": [
@@ -5922,7 +6405,7 @@
"/api/permission/saved/{id}": {
"delete": {
"tags": [
"permissions"
"permission"
],
"operationId": "v2.permission.saved.remove",
"parameters": [
@@ -5968,7 +6451,7 @@
"/api/session/{sessionID}/permission": {
"post": {
"tags": [
"permissions"
"permission"
],
"operationId": "v2.session.permission.create",
"parameters": [
@@ -6131,7 +6614,7 @@
},
"get": {
"tags": [
"permissions"
"permission"
],
"operationId": "v2.session.permission.list",
"parameters": [
@@ -6218,7 +6701,7 @@
"/api/session/{sessionID}/permission/{requestID}": {
"get": {
"tags": [
"permissions"
"permission"
],
"operationId": "v2.session.permission.get",
"parameters": [
@@ -6318,7 +6801,7 @@
"/api/session/{sessionID}/permission/{requestID}/reply": {
"post": {
"tags": [
"permissions"
"permission"
],
"operationId": "v2.session.permission.reply",
"parameters": [
@@ -6769,7 +7252,7 @@
"/api/command": {
"get": {
"tags": [
"commands"
"command"
],
"operationId": "v2.command.list",
"parameters": [
@@ -6870,7 +7353,7 @@
"/api/skill": {
"get": {
"tags": [
"skills"
"skill"
],
"operationId": "v2.skill.list",
"parameters": [
@@ -6971,7 +7454,7 @@
"/api/event": {
"get": {
"tags": [
"events"
"event"
],
"operationId": "v2.event.subscribe",
"parameters": [],
@@ -7108,154 +7591,10 @@
}
}
},
"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. Consumers that need reliability should combine the changes feed with durable session log reads.",
"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/event/changes": {
"get": {
"tags": [
"events"
],
"operationId": "v2.event.changes",
"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/EventLog.ChangeStream"
}
},
"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": "Payload-free hint channel: after an event commits, a subscriber eventually receives a hint for that aggregate with seq at or beyond the event, or a sweep-required marker. Hints coalesce to the latest seq per aggregate under backpressure and the stream never fails from overflow. No consumer may derive correctness from receiving a hint; correctness always comes from durable log reads plus the consumer's own checkpoint. A sweep-required marker is emitted first on every (re)subscribe and whenever hint retention is exceeded: treat every aggregate as potentially dirty and recover via bounded sweep plus log reads.",
"summary": "Subscribe to change hints"
}
},
"/api/pty": {
"get": {
"tags": [
@@ -7873,7 +8212,7 @@
"tags": [
"pty"
],
"operationId": "v2.pty.connectToken",
"operationId": "v2.pty.connect.token",
"parameters": [
{
"name": "ptyID",
@@ -8005,7 +8344,6 @@
"pty"
],
"operationId": "v2.pty.connect",
"x-websocket": true,
"parameters": [
{
"name": "ptyID",
@@ -8103,7 +8441,8 @@
}
},
"description": "Establish a WebSocket connection streaming PTY output and accepting terminal input.",
"summary": "Connect to PTY session"
"summary": "Connect to PTY session",
"x-websocket": true
}
},
"/api/shell": {
@@ -8169,7 +8508,7 @@
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Shell1"
"$ref": "#/components/schemas/Shell"
}
}
},
@@ -8266,7 +8605,7 @@
"$ref": "#/components/schemas/Location.Info"
},
"data": {
"$ref": "#/components/schemas/Shell1"
"$ref": "#/components/schemas/Shell"
}
},
"required": [
@@ -8326,7 +8665,8 @@
}
},
"required": [
"command"
"command",
"timeout"
],
"additionalProperties": false
}
@@ -8410,7 +8750,7 @@
"$ref": "#/components/schemas/Location.Info"
},
"data": {
"$ref": "#/components/schemas/Shell1"
"$ref": "#/components/schemas/Shell"
}
},
"required": [
@@ -8556,6 +8896,151 @@
"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/Shell"
}
},
"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": [
@@ -8737,7 +9222,7 @@
"/api/question/request": {
"get": {
"tags": [
"session questions"
"question"
],
"operationId": "v2.question.request.list",
"parameters": [
@@ -8838,7 +9323,7 @@
"/api/session/{sessionID}/question": {
"get": {
"tags": [
"session questions"
"question"
],
"operationId": "v2.session.question.list",
"parameters": [
@@ -8925,7 +9410,7 @@
"/api/session/{sessionID}/question/{requestID}/reply": {
"post": {
"tags": [
"session questions"
"question"
],
"operationId": "v2.session.question.reply",
"parameters": [
@@ -9019,7 +9504,7 @@
"/api/session/{sessionID}/question/{requestID}/reject": {
"post": {
"tags": [
"session questions"
"question"
],
"operationId": "v2.session.question.reject",
"parameters": [
@@ -9752,6 +10237,129 @@
"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": "<No Content>"
},
"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": {
@@ -10133,6 +10741,31 @@
}
]
},
"fork": {
"type": "object",
"properties": {
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
}
},
"required": [
"sessionID"
],
"additionalProperties": false
},
"projectID": {
"type": "string"
},
@@ -10225,20 +10858,6 @@
],
"additionalProperties": false
},
"SessionWatermarks": {
"type": "object",
"patternProperties": {
"^ses": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
}
},
"description": "Durable log seq each session's snapshot was computed at. Attach a live log read after the watermark to compose fetch and stream gap-free; apply a snapshot only where its watermark is at or beyond already-applied events. Sessions without durable events are absent."
},
"SessionsResponse": {
"type": "object",
"properties": {
@@ -10248,9 +10867,6 @@
"$ref": "#/components/schemas/SessionV2.Info"
}
},
"watermarks": {
"$ref": "#/components/schemas/SessionWatermarks"
},
"cursor": {
"type": "object",
"properties": {
@@ -10280,7 +10896,6 @@
},
"required": [
"data",
"watermarks",
"cursor"
],
"additionalProperties": false
@@ -10408,7 +11023,7 @@
],
"additionalProperties": false
},
"Prompt.Source": {
"Prompt.Mention": {
"type": "object",
"properties": {
"start": {
@@ -10440,8 +11055,8 @@
"description": {
"type": "string"
},
"source": {
"$ref": "#/components/schemas/Prompt.Source"
"mention": {
"$ref": "#/components/schemas/Prompt.Mention"
}
},
"required": [
@@ -10455,8 +11070,8 @@
"name": {
"type": "string"
},
"source": {
"$ref": "#/components/schemas/Prompt.Source"
"mention": {
"$ref": "#/components/schemas/Prompt.Mention"
}
},
"required": [
@@ -10488,28 +11103,78 @@
],
"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": {
"uri": {
"type": "string"
"data": {
"$ref": "#/components/schemas/Prompt.Base64"
},
"mime": {
"type": "string"
},
"source": {
"$ref": "#/components/schemas/Prompt.FileSource"
},
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"source": {
"$ref": "#/components/schemas/Prompt.Source"
"mention": {
"$ref": "#/components/schemas/Prompt.Mention"
}
},
"required": [
"uri",
"mime"
"data",
"mime",
"source"
],
"additionalProperties": false
},
@@ -10694,26 +11359,57 @@
],
"additionalProperties": false
},
"SessionBusyError": {
"SessionInput.Compaction": {
"type": "object",
"properties": {
"_tag": {
"type": {
"type": "string",
"enum": [
"SessionBusyError"
"compaction"
]
},
"admittedSeq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"id": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"sessionID": {
"type": "string"
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"message": {
"type": "string"
"timeCreated": {
"type": "number"
},
"handledSeq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
}
},
"required": [
"_tag",
"type",
"admittedSeq",
"id",
"sessionID",
"message"
"timeCreated"
],
"additionalProperties": false
},
@@ -10746,6 +11442,29 @@
],
"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": {
@@ -10775,7 +11494,7 @@
],
"additionalProperties": false
},
"Session.Message.AgentSwitched": {
"Session.Message.AgentSelected": {
"type": "object",
"properties": {
"id": {
@@ -10819,7 +11538,7 @@
],
"additionalProperties": false
},
"Session.Message.ModelSwitched": {
"Session.Message.ModelSelected": {
"type": "object",
"properties": {
"id": {
@@ -10853,6 +11572,9 @@
},
"model": {
"$ref": "#/components/schemas/Model.Ref"
},
"previous": {
"$ref": "#/components/schemas/Model.Ref"
}
},
"required": [
@@ -11067,6 +11789,182 @@
],
"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": [
{
"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
},
"Session.Message.Shell": {
"type": "object",
"properties": {
@@ -11102,23 +12000,49 @@
"shell"
]
},
"callID": {
"type": "string"
},
"command": {
"type": "string"
"shell": {
"$ref": "#/components/schemas/Shell"
},
"output": {
"type": "string"
"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",
"callID",
"command",
"output"
"shell"
],
"additionalProperties": false
},
@@ -11131,25 +12055,18 @@
"text"
]
},
"id": {
"type": "string"
},
"text": {
"type": "string"
}
},
"required": [
"type",
"id",
"text"
],
"additionalProperties": false
},
"LLM.ProviderMetadata": {
"type": "object",
"additionalProperties": {
"type": "object"
}
"Session.Message.ProviderState": {
"type": "object"
},
"Session.Message.Assistant.Reasoning": {
"type": "object",
@@ -11160,14 +12077,11 @@
"reasoning"
]
},
"id": {
"type": "string"
},
"text": {
"type": "string"
},
"providerMetadata": {
"$ref": "#/components/schemas/LLM.ProviderMetadata"
"state": {
"$ref": "#/components/schemas/Session.Message.ProviderState"
},
"time": {
"type": "object",
@@ -11187,7 +12101,6 @@
},
"required": [
"type",
"id",
"text"
],
"additionalProperties": false
@@ -11339,14 +12252,11 @@
],
"additionalProperties": false
},
"Session.Error.Unknown": {
"Session.StructuredError": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"unknown"
]
"type": "string"
},
"message": {
"type": "string"
@@ -11380,7 +12290,7 @@
"type": "object"
},
"error": {
"$ref": "#/components/schemas/Session.Error.Unknown"
"$ref": "#/components/schemas/Session.StructuredError"
},
"result": {}
},
@@ -11408,23 +12318,14 @@
"name": {
"type": "string"
},
"provider": {
"type": "object",
"properties": {
"executed": {
"type": "boolean"
},
"metadata": {
"$ref": "#/components/schemas/LLM.ProviderMetadata"
},
"resultMetadata": {
"$ref": "#/components/schemas/LLM.ProviderMetadata"
}
},
"required": [
"executed"
],
"additionalProperties": false
"executed": {
"type": "boolean"
},
"providerState": {
"$ref": "#/components/schemas/Session.Message.ProviderState"
},
"providerResultState": {
"$ref": "#/components/schemas/Session.Message.ProviderState"
},
"state": {
"anyOf": [
@@ -11473,6 +12374,31 @@
],
"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": {
@@ -11549,7 +12475,15 @@
"additionalProperties": false
},
"finish": {
"type": "string"
"type": "string",
"enum": [
"stop",
"length",
"tool-calls",
"content-filter",
"error",
"unknown"
]
},
"cost": {
"type": "number"
@@ -11592,7 +12526,10 @@
"additionalProperties": false
},
"error": {
"$ref": "#/components/schemas/Session.Error.Unknown"
"$ref": "#/components/schemas/Session.StructuredError"
},
"retry": {
"$ref": "#/components/schemas/Session.Message.Assistant.Retry"
}
},
"required": [
@@ -11614,6 +12551,15 @@
"compaction"
]
},
"status": {
"type": "string",
"enum": [
"queued",
"running",
"completed",
"failed"
]
},
"reason": {
"type": "string",
"enum": [
@@ -11653,6 +12599,7 @@
},
"required": [
"type",
"status",
"reason",
"summary",
"recent",
@@ -11664,10 +12611,10 @@
"Session.Message": {
"anyOf": [
{
"$ref": "#/components/schemas/Session.Message.AgentSwitched"
"$ref": "#/components/schemas/Session.Message.AgentSelected"
},
{
"$ref": "#/components/schemas/Session.Message.ModelSwitched"
"$ref": "#/components/schemas/Session.Message.ModelSelected"
},
{
"$ref": "#/components/schemas/Session.Message.User"
@@ -11697,7 +12644,7 @@
"allOf": [
{
"pattern": "^[a-z0-9][a-z0-9._-]*$",
"description": "Context entry key (lowercase alphanumerics plus . _ -)"
"description": "Instruction entry key (lowercase alphanumerics plus . _ -)"
}
]
},
@@ -11715,7 +12662,7 @@
],
"additionalProperties": false
},
"session.next.agent.switched": {
"session.agent.selected": {
"type": "object",
"properties": {
"id": {
@@ -11726,13 +12673,16 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"session.next.agent.switched"
"session.agent.selected"
]
},
"durable": {
@@ -11771,9 +12721,6 @@
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
@@ -11782,22 +12729,12 @@
}
]
},
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"agent": {
"type": "string"
}
},
"required": [
"timestamp",
"sessionID",
"messageID",
"agent"
],
"additionalProperties": false
@@ -11805,12 +12742,14 @@
},
"required": [
"id",
"created",
"type",
"durable",
"data"
],
"additionalProperties": false
},
"session.next.model.switched": {
"session.model.selected": {
"type": "object",
"properties": {
"id": {
@@ -11821,13 +12760,16 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"session.next.model.switched"
"session.model.selected"
]
},
"durable": {
@@ -11866,9 +12808,6 @@
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
@@ -11877,22 +12816,12 @@
}
]
},
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"model": {
"$ref": "#/components/schemas/Model.Ref"
}
},
"required": [
"timestamp",
"sessionID",
"messageID",
"model"
],
"additionalProperties": false
@@ -11900,12 +12829,14 @@
},
"required": [
"id",
"created",
"type",
"durable",
"data"
],
"additionalProperties": false
},
"session.next.moved": {
"session.moved": {
"type": "object",
"properties": {
"id": {
@@ -11916,13 +12847,16 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"session.next.moved"
"session.moved"
]
},
"durable": {
@@ -11961,9 +12895,6 @@
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
@@ -11975,12 +12906,11 @@
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"subdirectory": {
"subpath": {
"type": "string"
}
},
"required": [
"timestamp",
"sessionID",
"location"
],
@@ -11989,12 +12919,14 @@
},
"required": [
"id",
"created",
"type",
"durable",
"data"
],
"additionalProperties": false
},
"session.next.renamed": {
"session.renamed": {
"type": "object",
"properties": {
"id": {
@@ -12005,13 +12937,16 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"session.next.renamed"
"session.renamed"
]
},
"durable": {
@@ -12050,9 +12985,6 @@
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
@@ -12066,7 +12998,6 @@
}
},
"required": [
"timestamp",
"sessionID",
"title"
],
@@ -12075,12 +13006,14 @@
},
"required": [
"id",
"created",
"type",
"durable",
"data"
],
"additionalProperties": false
},
"session.next.forked": {
"session.deleted": {
"type": "object",
"properties": {
"id": {
@@ -12091,13 +13024,16 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"session.next.forked"
"session.deleted"
]
},
"durable": {
@@ -12136,9 +13072,89 @@
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
"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": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"sessionID": {
"type": "string",
"allOf": [
@@ -12155,7 +13171,7 @@
}
]
},
"messageID": {
"from": {
"type": "string",
"allOf": [
{
@@ -12165,7 +13181,6 @@
}
},
"required": [
"timestamp",
"sessionID",
"parentID"
],
@@ -12174,12 +13189,14 @@
},
"required": [
"id",
"created",
"type",
"durable",
"data"
],
"additionalProperties": false
},
"session.next.prompted": {
"session.prompt.promoted": {
"type": "object",
"properties": {
"id": {
@@ -12190,13 +13207,16 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"session.next.prompted"
"session.prompt.promoted"
]
},
"durable": {
@@ -12235,9 +13255,6 @@
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
@@ -12246,7 +13263,99 @@
}
]
},
"messageID": {
"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": "integer",
"allOf": [
{
"minimum": 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": [
{
@@ -12266,9 +13375,8 @@
}
},
"required": [
"timestamp",
"sessionID",
"messageID",
"inputID",
"prompt",
"delivery"
],
@@ -12277,12 +13385,14 @@
},
"required": [
"id",
"created",
"type",
"durable",
"data"
],
"additionalProperties": false
},
"session.next.prompt.admitted": {
"session.execution.started": {
"type": "object",
"properties": {
"id": {
@@ -12293,13 +13403,16 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"session.next.prompt.admitted"
"session.execution.started"
]
},
"durable": {
@@ -12338,9 +13451,172 @@
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
"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": "integer",
"allOf": [
{
"minimum": 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": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"sessionID": {
"type": "string",
"allOf": [
@@ -12349,43 +13625,119 @@
}
]
},
"messageID": {
"type": "string",
"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": [
{
"pattern": "^msg_"
"minimum": 0
}
]
},
"prompt": {
"$ref": "#/components/schemas/Prompt"
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"delivery": {
"reason": {
"type": "string",
"enum": [
"steer",
"queue"
"user",
"shutdown",
"superseded"
]
}
},
"required": [
"timestamp",
"sessionID",
"messageID",
"prompt",
"delivery"
"reason"
],
"additionalProperties": false
}
},
"required": [
"id",
"created",
"type",
"durable",
"data"
],
"additionalProperties": false
},
"session.next.instructions.updated": {
"session.instructions.updated": {
"type": "object",
"properties": {
"id": {
@@ -12396,13 +13748,16 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"session.next.instructions.updated"
"session.instructions.updated"
]
},
"durable": {
@@ -12441,9 +13796,6 @@
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
@@ -12452,22 +13804,12 @@
}
]
},
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"text": {
"type": "string"
}
},
"required": [
"timestamp",
"sessionID",
"messageID",
"text"
],
"additionalProperties": false
@@ -12475,12 +13817,14 @@
},
"required": [
"id",
"created",
"type",
"durable",
"data"
],
"additionalProperties": false
},
"session.next.synthetic": {
"session.synthetic": {
"type": "object",
"properties": {
"id": {
@@ -12491,13 +13835,16 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"session.next.synthetic"
"session.synthetic"
]
},
"durable": {
@@ -12536,9 +13883,6 @@
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
@@ -12547,14 +13891,6 @@
}
]
},
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"text": {
"type": "string"
},
@@ -12566,9 +13902,7 @@
}
},
"required": [
"timestamp",
"sessionID",
"messageID",
"text"
],
"additionalProperties": false
@@ -12576,12 +13910,14 @@
},
"required": [
"id",
"created",
"type",
"durable",
"data"
],
"additionalProperties": false
},
"session.next.skill.activated": {
"session.skill.activated": {
"type": "object",
"properties": {
"id": {
@@ -12592,13 +13928,16 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"session.next.skill.activated"
"session.skill.activated"
]
},
"durable": {
@@ -12637,9 +13976,6 @@
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
@@ -12648,14 +13984,6 @@
}
]
},
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"name": {
"type": "string"
},
@@ -12664,9 +13992,7 @@
}
},
"required": [
"timestamp",
"sessionID",
"messageID",
"name",
"text"
],
@@ -12675,111 +14001,154 @@
},
"required": [
"id",
"created",
"type",
"durable",
"data"
],
"additionalProperties": false
},
"session.next.shell.started": {
"Shell1": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
"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"
},
"type": {
"type": "string",
"enum": [
"session.next.shell.started"
]
},
"durable": {
"time": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
"started": {
"anyOf": [
{
"minimum": 0
"type": "number"
},
{
"type": "string",
"enum": [
"NaN"
]
},
{
"type": "string",
"enum": [
"Infinity"
]
},
{
"type": "string",
"enum": [
"-Infinity"
]
}
]
},
"version": {
"type": "integer",
"allOf": [
"completed": {
"anyOf": [
{
"minimum": 1
"type": "number"
},
{
"type": "string",
"enum": [
"NaN"
]
},
{
"type": "string",
"enum": [
"Infinity"
]
},
{
"type": "string",
"enum": [
"-Infinity"
]
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"callID": {
"type": "string"
},
"command": {
"type": "string"
}
},
"required": [
"timestamp",
"sessionID",
"messageID",
"callID",
"command"
"started"
],
"additionalProperties": false
}
},
"required": [
"id",
"type",
"data"
"status",
"command",
"cwd",
"shell",
"file",
"metadata",
"time"
],
"additionalProperties": false
},
"session.next.shell.ended": {
"session.shell.started": {
"type": "object",
"properties": {
"id": {
@@ -12790,13 +14159,16 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"session.next.shell.ended"
"session.shell.started"
]
},
"durable": {
@@ -12835,9 +14207,6 @@
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
@@ -12846,17 +14215,134 @@
}
]
},
"callID": {
"type": "string"
},
"output": {
"type": "string"
"shell": {
"$ref": "#/components/schemas/Shell1"
}
},
"required": [
"timestamp",
"sessionID",
"callID",
"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": "integer",
"allOf": [
{
"minimum": 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/Shell1"
},
"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
@@ -12864,12 +14350,14 @@
},
"required": [
"id",
"created",
"type",
"durable",
"data"
],
"additionalProperties": false
},
"session.next.step.started": {
"session.step.started": {
"type": "object",
"properties": {
"id": {
@@ -12880,13 +14368,16 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"session.next.step.started"
"session.step.started"
]
},
"durable": {
@@ -12925,9 +14416,6 @@
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
@@ -12955,7 +14443,6 @@
}
},
"required": [
"timestamp",
"sessionID",
"assistantMessageID",
"agent",
@@ -12966,12 +14453,14 @@
},
"required": [
"id",
"created",
"type",
"durable",
"data"
],
"additionalProperties": false
},
"session.next.step.ended": {
"session.step.ended": {
"type": "object",
"properties": {
"id": {
@@ -12982,13 +14471,16 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"session.next.step.ended"
"session.step.ended"
]
},
"durable": {
@@ -13027,9 +14519,6 @@
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
@@ -13047,7 +14536,15 @@
]
},
"finish": {
"type": "string"
"type": "string",
"enum": [
"stop",
"length",
"tool-calls",
"content-filter",
"error",
"unknown"
]
},
"cost": {
"type": "number"
@@ -13100,7 +14597,6 @@
}
},
"required": [
"timestamp",
"sessionID",
"assistantMessageID",
"finish",
@@ -13112,12 +14608,14 @@
},
"required": [
"id",
"created",
"type",
"durable",
"data"
],
"additionalProperties": false
},
"session.next.step.failed": {
"session.step.failed": {
"type": "object",
"properties": {
"id": {
@@ -13128,13 +14626,16 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"session.next.step.failed"
"session.step.failed"
]
},
"durable": {
@@ -13173,9 +14674,6 @@
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
@@ -13193,11 +14691,50 @@
]
},
"error": {
"$ref": "#/components/schemas/Session.Error.Unknown"
"$ref": "#/components/schemas/Session.StructuredError"
},
"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
}
},
"required": [
"timestamp",
"sessionID",
"assistantMessageID",
"error"
@@ -13207,12 +14744,14 @@
},
"required": [
"id",
"created",
"type",
"durable",
"data"
],
"additionalProperties": false
},
"session.next.text.started": {
"session.text.started": {
"type": "object",
"properties": {
"id": {
@@ -13223,13 +14762,16 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"session.next.text.started"
"session.text.started"
]
},
"durable": {
@@ -13268,9 +14810,6 @@
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
@@ -13287,27 +14826,33 @@
}
]
},
"textID": {
"type": "string"
"ordinal": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
}
},
"required": [
"timestamp",
"sessionID",
"assistantMessageID",
"textID"
"ordinal"
],
"additionalProperties": false
}
},
"required": [
"id",
"created",
"type",
"durable",
"data"
],
"additionalProperties": false
},
"session.next.text.ended": {
"session.text.ended": {
"type": "object",
"properties": {
"id": {
@@ -13318,13 +14863,16 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"session.next.text.ended"
"session.text.ended"
]
},
"durable": {
@@ -13363,9 +14911,6 @@
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
@@ -13382,18 +14927,22 @@
}
]
},
"textID": {
"type": "string"
"ordinal": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"text": {
"type": "string"
}
},
"required": [
"timestamp",
"sessionID",
"assistantMessageID",
"textID",
"ordinal",
"text"
],
"additionalProperties": false
@@ -13401,12 +14950,17 @@
},
"required": [
"id",
"created",
"type",
"durable",
"data"
],
"additionalProperties": false
},
"session.next.tool.input.started": {
"Session.Message.ProviderState3": {
"type": "object"
},
"session.reasoning.started": {
"type": "object",
"properties": {
"id": {
@@ -13417,13 +14971,16 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"session.next.tool.input.started"
"session.reasoning.started"
]
},
"durable": {
@@ -13462,9 +15019,221 @@
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
"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": "integer",
"allOf": [
{
"minimum": 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": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"sessionID": {
"type": "string",
"allOf": [
@@ -13489,7 +15258,6 @@
}
},
"required": [
"timestamp",
"sessionID",
"assistantMessageID",
"callID",
@@ -13500,12 +15268,14 @@
},
"required": [
"id",
"created",
"type",
"durable",
"data"
],
"additionalProperties": false
},
"session.next.tool.input.ended": {
"session.tool.input.ended": {
"type": "object",
"properties": {
"id": {
@@ -13516,13 +15286,16 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"session.next.tool.input.ended"
"session.tool.input.ended"
]
},
"durable": {
@@ -13561,9 +15334,6 @@
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
@@ -13588,7 +15358,6 @@
}
},
"required": [
"timestamp",
"sessionID",
"assistantMessageID",
"callID",
@@ -13599,18 +15368,17 @@
},
"required": [
"id",
"created",
"type",
"durable",
"data"
],
"additionalProperties": false
},
"LLM.ProviderMetadata3": {
"type": "object",
"additionalProperties": {
"type": "object"
}
"Session.Message.ProviderState5": {
"type": "object"
},
"session.next.tool.called": {
"session.tool.called": {
"type": "object",
"properties": {
"id": {
@@ -13621,13 +15389,16 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"session.next.tool.called"
"session.tool.called"
]
},
"durable": {
@@ -13666,9 +15437,6 @@
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
@@ -13688,48 +15456,36 @@
"callID": {
"type": "string"
},
"tool": {
"type": "string"
},
"input": {
"type": "object"
},
"provider": {
"type": "object",
"properties": {
"executed": {
"type": "boolean"
},
"metadata": {
"$ref": "#/components/schemas/LLM.ProviderMetadata3"
}
},
"required": [
"executed"
],
"additionalProperties": false
"executed": {
"type": "boolean"
},
"state": {
"$ref": "#/components/schemas/Session.Message.ProviderState5"
}
},
"required": [
"timestamp",
"sessionID",
"assistantMessageID",
"callID",
"tool",
"input",
"provider"
"executed"
],
"additionalProperties": false
}
},
"required": [
"id",
"created",
"type",
"durable",
"data"
],
"additionalProperties": false
},
"session.next.tool.progress": {
"session.tool.progress": {
"type": "object",
"properties": {
"id": {
@@ -13740,13 +15496,16 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"session.next.tool.progress"
"session.tool.progress"
]
},
"durable": {
@@ -13785,9 +15544,6 @@
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
@@ -13818,7 +15574,6 @@
}
},
"required": [
"timestamp",
"sessionID",
"assistantMessageID",
"callID",
@@ -13830,18 +15585,17 @@
},
"required": [
"id",
"created",
"type",
"durable",
"data"
],
"additionalProperties": false
},
"LLM.ProviderMetadata4": {
"type": "object",
"additionalProperties": {
"type": "object"
}
"Session.Message.ProviderState6": {
"type": "object"
},
"session.next.tool.success": {
"session.tool.success": {
"type": "object",
"properties": {
"id": {
@@ -13852,13 +15606,16 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"session.next.tool.success"
"session.tool.success"
]
},
"durable": {
@@ -13897,9 +15654,6 @@
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
@@ -13935,48 +15689,37 @@
}
},
"result": {},
"provider": {
"type": "object",
"properties": {
"executed": {
"type": "boolean"
},
"metadata": {
"$ref": "#/components/schemas/LLM.ProviderMetadata4"
}
},
"required": [
"executed"
],
"additionalProperties": false
"executed": {
"type": "boolean"
},
"resultState": {
"$ref": "#/components/schemas/Session.Message.ProviderState6"
}
},
"required": [
"timestamp",
"sessionID",
"assistantMessageID",
"callID",
"structured",
"content",
"provider"
"executed"
],
"additionalProperties": false
}
},
"required": [
"id",
"created",
"type",
"durable",
"data"
],
"additionalProperties": false
},
"LLM.ProviderMetadata5": {
"type": "object",
"additionalProperties": {
"type": "object"
}
"Session.Message.ProviderState7": {
"type": "object"
},
"session.next.tool.failed": {
"session.tool.failed": {
"type": "object",
"properties": {
"id": {
@@ -13987,13 +15730,16 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"session.next.tool.failed"
"session.tool.failed"
]
},
"durable": {
@@ -14032,9 +15778,6 @@
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
@@ -14055,50 +15798,36 @@
"type": "string"
},
"error": {
"$ref": "#/components/schemas/Session.Error.Unknown"
"$ref": "#/components/schemas/Session.StructuredError"
},
"result": {},
"provider": {
"type": "object",
"properties": {
"executed": {
"type": "boolean"
},
"metadata": {
"$ref": "#/components/schemas/LLM.ProviderMetadata5"
}
},
"required": [
"executed"
],
"additionalProperties": false
"executed": {
"type": "boolean"
},
"resultState": {
"$ref": "#/components/schemas/Session.Message.ProviderState7"
}
},
"required": [
"timestamp",
"sessionID",
"assistantMessageID",
"callID",
"error",
"provider"
"executed"
],
"additionalProperties": false
}
},
"required": [
"id",
"created",
"type",
"durable",
"data"
],
"additionalProperties": false
},
"LLM.ProviderMetadata6": {
"type": "object",
"additionalProperties": {
"type": "object"
}
},
"session.next.reasoning.started": {
"session.retry.scheduled": {
"type": "object",
"properties": {
"id": {
@@ -14109,253 +15838,16 @@
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"session.next.reasoning.started"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"assistantMessageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"reasoningID": {
"type": "string"
},
"providerMetadata": {
"$ref": "#/components/schemas/LLM.ProviderMetadata6"
}
},
"required": [
"timestamp",
"sessionID",
"assistantMessageID",
"reasoningID"
],
"additionalProperties": false
}
},
"required": [
"id",
"type",
"data"
],
"additionalProperties": false
},
"LLM.ProviderMetadata7": {
"type": "object",
"additionalProperties": {
"type": "object"
}
},
"session.next.reasoning.ended": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"session.next.reasoning.ended"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
},
"assistantMessageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"reasoningID": {
"type": "string"
},
"text": {
"type": "string"
},
"providerMetadata": {
"$ref": "#/components/schemas/LLM.ProviderMetadata7"
}
},
"required": [
"timestamp",
"sessionID",
"assistantMessageID",
"reasoningID",
"text"
],
"additionalProperties": false
}
},
"required": [
"id",
"type",
"data"
],
"additionalProperties": false
},
"session.next.retry_error": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"statusCode": {
"created": {
"type": "number"
},
"isRetryable": {
"type": "boolean"
},
"responseHeaders": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"responseBody": {
"type": "string"
},
"metadata": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"required": [
"message",
"isRetryable"
],
"additionalProperties": false
},
"session.next.retried": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"session.next.retried"
"session.retry.scheduled"
]
},
"durable": {
@@ -14394,9 +15886,6 @@
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
@@ -14405,17 +15894,39 @@
}
]
},
"assistantMessageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"attempt": {
"type": "number"
"type": "integer",
"allOf": [
{
"exclusiveMinimum": 0
}
]
},
"at": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"error": {
"$ref": "#/components/schemas/session.next.retry_error"
"$ref": "#/components/schemas/Session.StructuredError"
}
},
"required": [
"timestamp",
"sessionID",
"assistantMessageID",
"attempt",
"at",
"error"
],
"additionalProperties": false
@@ -14423,12 +15934,14 @@
},
"required": [
"id",
"created",
"type",
"durable",
"data"
],
"additionalProperties": false
},
"session.next.compaction.started": {
"session.compaction.admitted": {
"type": "object",
"properties": {
"id": {
@@ -14439,13 +15952,16 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"session.next.compaction.started"
"session.compaction.admitted"
]
},
"durable": {
@@ -14484,9 +16000,6 @@
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
@@ -14495,13 +16008,97 @@
}
]
},
"messageID": {
"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": "integer",
"allOf": [
{
"minimum": 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",
@@ -14512,9 +16109,7 @@
}
},
"required": [
"timestamp",
"sessionID",
"messageID",
"reason"
],
"additionalProperties": false
@@ -14522,12 +16117,14 @@
},
"required": [
"id",
"created",
"type",
"durable",
"data"
],
"additionalProperties": false
},
"session.next.compaction.ended": {
"session.compaction.ended": {
"type": "object",
"properties": {
"id": {
@@ -14538,13 +16135,16 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"session.next.compaction.ended"
"session.compaction.ended"
]
},
"durable": {
@@ -14583,9 +16183,6 @@
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
@@ -14594,14 +16191,6 @@
}
]
},
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"reason": {
"type": "string",
"enum": [
@@ -14617,9 +16206,7 @@
}
},
"required": [
"timestamp",
"sessionID",
"messageID",
"reason",
"text",
"recent"
@@ -14629,12 +16216,14 @@
},
"required": [
"id",
"created",
"type",
"durable",
"data"
],
"additionalProperties": false
},
"session.next.revert.staged": {
"session.compaction.failed": {
"type": "object",
"properties": {
"id": {
@@ -14645,13 +16234,16 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"session.next.revert.staged"
"session.compaction.failed"
]
},
"durable": {
@@ -14690,9 +16282,89 @@
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
"sessionID": {
"type": "string",
"allOf": [
{
"pattern": "^ses"
}
]
}
},
"required": [
"sessionID"
],
"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": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"sessionID": {
"type": "string",
"allOf": [
@@ -14706,7 +16378,6 @@
}
},
"required": [
"timestamp",
"sessionID",
"revert"
],
@@ -14715,12 +16386,14 @@
},
"required": [
"id",
"created",
"type",
"durable",
"data"
],
"additionalProperties": false
},
"session.next.revert.cleared": {
"session.revert.cleared": {
"type": "object",
"properties": {
"id": {
@@ -14731,13 +16404,16 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"session.next.revert.cleared"
"session.revert.cleared"
]
},
"durable": {
@@ -14776,9 +16452,6 @@
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
@@ -14789,7 +16462,6 @@
}
},
"required": [
"timestamp",
"sessionID"
],
"additionalProperties": false
@@ -14797,12 +16469,14 @@
},
"required": [
"id",
"created",
"type",
"durable",
"data"
],
"additionalProperties": false
},
"session.next.revert.committed": {
"session.revert.committed": {
"type": "object",
"properties": {
"id": {
@@ -14813,13 +16487,16 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"session.next.revert.committed"
"session.revert.committed"
]
},
"durable": {
@@ -14858,9 +16535,6 @@
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
@@ -14869,7 +16543,7 @@
}
]
},
"messageID": {
"to": {
"type": "string",
"allOf": [
{
@@ -14879,16 +16553,17 @@
}
},
"required": [
"timestamp",
"sessionID",
"messageID"
"to"
],
"additionalProperties": false
}
},
"required": [
"id",
"created",
"type",
"durable",
"data"
],
"additionalProperties": false
@@ -14896,97 +16571,118 @@
"SessionDurableEvent": {
"oneOf": [
{
"$ref": "#/components/schemas/session.next.agent.switched"
"$ref": "#/components/schemas/session.agent.selected"
},
{
"$ref": "#/components/schemas/session.next.model.switched"
"$ref": "#/components/schemas/session.model.selected"
},
{
"$ref": "#/components/schemas/session.next.moved"
"$ref": "#/components/schemas/session.moved"
},
{
"$ref": "#/components/schemas/session.next.renamed"
"$ref": "#/components/schemas/session.renamed"
},
{
"$ref": "#/components/schemas/session.next.forked"
"$ref": "#/components/schemas/session.deleted"
},
{
"$ref": "#/components/schemas/session.next.prompted"
"$ref": "#/components/schemas/session.forked"
},
{
"$ref": "#/components/schemas/session.next.prompt.admitted"
"$ref": "#/components/schemas/session.prompt.promoted"
},
{
"$ref": "#/components/schemas/session.next.instructions.updated"
"$ref": "#/components/schemas/session.prompt.admitted"
},
{
"$ref": "#/components/schemas/session.next.synthetic"
"$ref": "#/components/schemas/session.execution.started"
},
{
"$ref": "#/components/schemas/session.next.skill.activated"
"$ref": "#/components/schemas/session.execution.succeeded"
},
{
"$ref": "#/components/schemas/session.next.shell.started"
"$ref": "#/components/schemas/session.execution.failed"
},
{
"$ref": "#/components/schemas/session.next.shell.ended"
"$ref": "#/components/schemas/session.execution.interrupted"
},
{
"$ref": "#/components/schemas/session.next.step.started"
"$ref": "#/components/schemas/session.instructions.updated"
},
{
"$ref": "#/components/schemas/session.next.step.ended"
"$ref": "#/components/schemas/session.synthetic"
},
{
"$ref": "#/components/schemas/session.next.step.failed"
"$ref": "#/components/schemas/session.skill.activated"
},
{
"$ref": "#/components/schemas/session.next.text.started"
"$ref": "#/components/schemas/session.shell.started"
},
{
"$ref": "#/components/schemas/session.next.text.ended"
"$ref": "#/components/schemas/session.shell.ended"
},
{
"$ref": "#/components/schemas/session.next.tool.input.started"
"$ref": "#/components/schemas/session.step.started"
},
{
"$ref": "#/components/schemas/session.next.tool.input.ended"
"$ref": "#/components/schemas/session.step.ended"
},
{
"$ref": "#/components/schemas/session.next.tool.called"
"$ref": "#/components/schemas/session.step.failed"
},
{
"$ref": "#/components/schemas/session.next.tool.progress"
"$ref": "#/components/schemas/session.text.started"
},
{
"$ref": "#/components/schemas/session.next.tool.success"
"$ref": "#/components/schemas/session.text.ended"
},
{
"$ref": "#/components/schemas/session.next.tool.failed"
"$ref": "#/components/schemas/session.reasoning.started"
},
{
"$ref": "#/components/schemas/session.next.reasoning.started"
"$ref": "#/components/schemas/session.reasoning.ended"
},
{
"$ref": "#/components/schemas/session.next.reasoning.ended"
"$ref": "#/components/schemas/session.tool.input.started"
},
{
"$ref": "#/components/schemas/session.next.retried"
"$ref": "#/components/schemas/session.tool.input.ended"
},
{
"$ref": "#/components/schemas/session.next.compaction.started"
"$ref": "#/components/schemas/session.tool.called"
},
{
"$ref": "#/components/schemas/session.next.compaction.ended"
"$ref": "#/components/schemas/session.tool.progress"
},
{
"$ref": "#/components/schemas/session.next.revert.staged"
"$ref": "#/components/schemas/session.tool.success"
},
{
"$ref": "#/components/schemas/session.next.revert.cleared"
"$ref": "#/components/schemas/session.tool.failed"
},
{
"$ref": "#/components/schemas/session.next.revert.committed"
"$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"
}
]
},
@@ -15044,14 +16740,6 @@
"$ref": "#/components/schemas/Session.Message"
}
},
"watermark": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"cursor": {
"type": "object",
"properties": {
@@ -15085,65 +16773,6 @@
],
"additionalProperties": false
},
"Model.Api": {
"anyOf": [
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"type": {
"type": "string",
"enum": [
"aisdk"
]
},
"package": {
"type": "string"
},
"url": {
"type": "string"
},
"settings": {
"type": "object"
}
},
"required": [
"id",
"type",
"package"
],
"additionalProperties": false
},
{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"type": {
"type": "string",
"enum": [
"native"
]
},
"url": {
"type": "string"
},
"settings": {
"type": "object"
}
},
"required": [
"id",
"type",
"settings"
],
"additionalProperties": false
}
]
},
"Model.Capabilities": {
"type": "object",
"properties": {
@@ -15170,6 +16799,30 @@
],
"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
},
"Model.Cost": {
"type": "object",
"properties": {
@@ -15228,6 +16881,9 @@
"id": {
"type": "string"
},
"modelID": {
"type": "string"
},
"providerID": {
"type": "string"
},
@@ -15237,66 +16893,28 @@
"name": {
"type": "string"
},
"api": {
"$ref": "#/components/schemas/Model.Api"
"package": {
"type": "string"
},
"settings": {
"type": "object"
},
"headers": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"body": {
"type": "object"
},
"capabilities": {
"$ref": "#/components/schemas/Model.Capabilities"
},
"request": {
"type": "object",
"properties": {
"settings": {
"$ref": "#/components/schemas/Provider.Settings"
},
"headers": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"body": {
"type": "object"
},
"variant": {
"type": "string"
}
},
"required": [
"settings",
"headers",
"body"
],
"additionalProperties": false
},
"variants": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"settings": {
"$ref": "#/components/schemas/Provider.Settings"
},
"headers": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"body": {
"type": "object"
}
},
"required": [
"id",
"settings",
"headers",
"body"
],
"additionalProperties": false
"$ref": "#/components/schemas/Model.Variant"
}
},
"time": {
@@ -15351,11 +16969,10 @@
},
"required": [
"id",
"modelID",
"providerID",
"name",
"api",
"capabilities",
"request",
"variants",
"time",
"cost",
@@ -15386,63 +17003,6 @@
],
"additionalProperties": false
},
"Provider.AISDK": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"aisdk"
]
},
"package": {
"type": "string"
},
"url": {
"type": "string"
},
"settings": {
"type": "object"
}
},
"required": [
"type",
"package"
],
"additionalProperties": false
},
"Provider.Native": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"native"
]
},
"url": {
"type": "string"
},
"settings": {
"type": "object"
}
},
"required": [
"type",
"settings"
],
"additionalProperties": false
},
"Provider.Api": {
"anyOf": [
{
"$ref": "#/components/schemas/Provider.AISDK"
},
{
"$ref": "#/components/schemas/Provider.Native"
}
]
},
"ProviderV2.Info": {
"type": "object",
"properties": {
@@ -15458,18 +17018,26 @@
"disabled": {
"type": "boolean"
},
"api": {
"$ref": "#/components/schemas/Provider.Api"
"package": {
"type": "string"
},
"request": {
"$ref": "#/components/schemas/Provider.Request"
"settings": {
"type": "object"
},
"headers": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"body": {
"type": "object"
}
},
"required": [
"id",
"name",
"api",
"request"
"package"
],
"additionalProperties": false
},
@@ -16305,13 +17873,13 @@
],
"additionalProperties": false
},
"Mcp.Status.Disconnected": {
"Mcp.Status.Pending": {
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": [
"disconnected"
"pending"
]
}
},
@@ -16400,7 +17968,7 @@
"$ref": "#/components/schemas/Mcp.Status.Connected"
},
{
"$ref": "#/components/schemas/Mcp.Status.Disconnected"
"$ref": "#/components/schemas/Mcp.Status.Pending"
},
{
"$ref": "#/components/schemas/Mcp.Status.Disabled"
@@ -16426,6 +17994,185 @@
],
"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": {
@@ -17606,6 +19353,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -17615,36 +19365,6 @@
"models-dev.refreshed"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -17661,6 +19381,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -17677,6 +19398,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -17686,36 +19410,6 @@
"integration.updated"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -17732,6 +19426,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -17748,6 +19443,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -17757,36 +19455,6 @@
"integration.connection.updated"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -17805,6 +19473,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -17821,6 +19490,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -17830,36 +19502,6 @@
"catalog.updated"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -17876,6 +19518,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -17892,6 +19535,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -17901,36 +19547,6 @@
"agent.updated"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -17947,6 +19563,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -18258,6 +19875,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -18324,7 +19944,9 @@
},
"required": [
"id",
"created",
"type",
"durable",
"data"
],
"additionalProperties": false
@@ -18340,6 +19962,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -18406,12 +20031,14 @@
},
"required": [
"id",
"created",
"type",
"durable",
"data"
],
"additionalProperties": false
},
"session.deleted": {
"session.deleted1": {
"type": "object",
"properties": {
"id": {
@@ -18422,6 +20049,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -18488,7 +20118,9 @@
},
"required": [
"id",
"created",
"type",
"durable",
"data"
],
"additionalProperties": false
@@ -19277,6 +20909,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -19343,7 +20978,9 @@
},
"required": [
"id",
"created",
"type",
"durable",
"data"
],
"additionalProperties": false
@@ -19359,6 +20996,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -19430,7 +21070,9 @@
},
"required": [
"id",
"created",
"type",
"durable",
"data"
],
"additionalProperties": false
@@ -20824,6 +22466,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -20894,7 +22539,9 @@
},
"required": [
"id",
"created",
"type",
"durable",
"data"
],
"additionalProperties": false
@@ -20910,6 +22557,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -20990,12 +22640,14 @@
},
"required": [
"id",
"created",
"type",
"durable",
"data"
],
"additionalProperties": false
},
"session.next.execution.settled": {
"session.usage.updated": {
"type": "object",
"properties": {
"id": {
@@ -21006,54 +22658,24 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"session.next.execution.settled"
"session.usage.updated"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
@@ -21062,34 +22684,64 @@
}
]
},
"outcome": {
"type": "string",
"enum": [
"success",
"failure",
"interrupted"
]
"cost": {
"type": "number"
},
"error": {
"$ref": "#/components/schemas/Session.Error.Unknown"
"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
}
},
"required": [
"timestamp",
"sessionID",
"outcome"
"cost",
"tokens"
],
"additionalProperties": false
}
},
"required": [
"id",
"created",
"type",
"data"
],
"additionalProperties": false
},
"session.next.text.delta": {
"session.text.delta": {
"type": "object",
"properties": {
"id": {
@@ -21100,54 +22752,24 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"session.next.text.delta"
"session.text.delta"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
@@ -21164,18 +22786,22 @@
}
]
},
"textID": {
"type": "string"
"ordinal": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"delta": {
"type": "string"
}
},
"required": [
"timestamp",
"sessionID",
"assistantMessageID",
"textID",
"ordinal",
"delta"
],
"additionalProperties": false
@@ -21183,12 +22809,13 @@
},
"required": [
"id",
"created",
"type",
"data"
],
"additionalProperties": false
},
"session.next.reasoning.delta": {
"session.reasoning.delta": {
"type": "object",
"properties": {
"id": {
@@ -21199,54 +22826,24 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"session.next.reasoning.delta"
"session.reasoning.delta"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
@@ -21263,18 +22860,22 @@
}
]
},
"reasoningID": {
"type": "string"
"ordinal": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"delta": {
"type": "string"
}
},
"required": [
"timestamp",
"sessionID",
"assistantMessageID",
"reasoningID",
"ordinal",
"delta"
],
"additionalProperties": false
@@ -21282,12 +22883,13 @@
},
"required": [
"id",
"created",
"type",
"data"
],
"additionalProperties": false
},
"session.next.tool.input.delta": {
"session.tool.input.delta": {
"type": "object",
"properties": {
"id": {
@@ -21298,54 +22900,24 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"session.next.tool.input.delta"
"session.tool.input.delta"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
@@ -21370,7 +22942,6 @@
}
},
"required": [
"timestamp",
"sessionID",
"assistantMessageID",
"callID",
@@ -21381,12 +22952,13 @@
},
"required": [
"id",
"created",
"type",
"data"
],
"additionalProperties": false
},
"session.next.compaction.delta": {
"session.compaction.delta": {
"type": "object",
"properties": {
"id": {
@@ -21397,54 +22969,24 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"session.next.compaction.delta"
"session.compaction.delta"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
"data": {
"type": "object",
"properties": {
"timestamp": {
"type": "number"
},
"sessionID": {
"type": "string",
"allOf": [
@@ -21453,22 +22995,12 @@
}
]
},
"messageID": {
"type": "string",
"allOf": [
{
"pattern": "^msg_"
}
]
},
"text": {
"type": "string"
}
},
"required": [
"timestamp",
"sessionID",
"messageID",
"text"
],
"additionalProperties": false
@@ -21476,12 +23008,13 @@
},
"required": [
"id",
"created",
"type",
"data"
],
"additionalProperties": false
},
"file.edited": {
"filesystem.changed": {
"type": "object",
"properties": {
"id": {
@@ -21492,45 +23025,18 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"file.edited"
"filesystem.changed"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -21539,16 +23045,26 @@
"properties": {
"file": {
"type": "string"
},
"event": {
"type": "string",
"enum": [
"add",
"change",
"unlink"
]
}
},
"required": [
"file"
"file",
"event"
],
"additionalProperties": false
}
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -21565,6 +23081,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -21574,36 +23093,6 @@
"reference.updated"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -21620,6 +23109,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -21636,6 +23126,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -21645,36 +23138,6 @@
"permission.v2.asked"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -21730,6 +23193,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -21746,6 +23210,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -21755,36 +23222,6 @@
"permission.v2.replied"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -21821,6 +23258,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -21837,6 +23275,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -21846,36 +23287,6 @@
"plugin.added"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -21894,6 +23305,52 @@
},
"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"
],
@@ -21910,6 +23367,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -21919,36 +23379,6 @@
"project.directories.updated"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -21967,6 +23397,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -21983,6 +23414,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -21992,36 +23426,6 @@
"command.updated"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -22038,6 +23442,52 @@
},
"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"
],
@@ -22054,6 +23504,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -22063,36 +23516,6 @@
"skill.updated"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -22109,88 +23532,7 @@
},
"required": [
"id",
"type",
"data"
],
"additionalProperties": false
},
"file.watcher.updated": {
"type": "object",
"properties": {
"id": {
"type": "string",
"allOf": [
{
"pattern": "^evt_"
}
]
},
"metadata": {
"type": "object"
},
"type": {
"type": "string",
"enum": [
"file.watcher.updated"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"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"
],
@@ -22268,6 +23610,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -22277,36 +23622,6 @@
"pty.created"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -22325,6 +23640,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -22341,6 +23657,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -22350,36 +23669,6 @@
"pty.updated"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -22398,6 +23687,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -22414,6 +23704,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -22423,36 +23716,6 @@
"pty.exited"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -22485,6 +23748,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -22501,6 +23765,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -22510,36 +23777,6 @@
"pty.deleted"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -22563,151 +23800,12 @@
},
"required": [
"id",
"created",
"type",
"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
},
"shell.created": {
"type": "object",
"properties": {
@@ -22719,6 +23817,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -22728,36 +23829,6 @@
"shell.created"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -22765,7 +23836,7 @@
"type": "object",
"properties": {
"info": {
"$ref": "#/components/schemas/Shell"
"$ref": "#/components/schemas/Shell1"
}
},
"required": [
@@ -22776,6 +23847,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -22792,6 +23864,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -22801,36 +23876,6 @@
"shell.exited"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -22889,6 +23934,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -22905,6 +23951,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -22914,36 +23963,6 @@
"shell.deleted"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -22967,6 +23986,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -23049,6 +24069,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -23058,36 +24081,6 @@
"question.v2.asked"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -23131,6 +24124,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -23153,6 +24147,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -23162,36 +24159,6 @@
"question.v2.replied"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -23231,6 +24198,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -23247,6 +24215,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -23256,36 +24227,6 @@
"question.v2.rejected"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -23318,6 +24259,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -23886,6 +24828,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -23895,36 +24840,6 @@
"form.created"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -23950,6 +24865,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -24013,6 +24929,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -24022,36 +24941,6 @@
"form.replied"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -24083,6 +24972,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -24099,6 +24989,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -24108,36 +25001,6 @@
"form.cancelled"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -24165,6 +25028,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -24204,6 +25068,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -24213,36 +25080,6 @@
"todo.updated"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -24273,6 +25110,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -24391,6 +25229,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -24400,36 +25241,6 @@
"session.status"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -24457,6 +25268,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -24473,6 +25285,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -24482,36 +25297,6 @@
"session.idle"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -24535,6 +25320,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -24551,6 +25337,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -24560,36 +25349,6 @@
"tui.prompt.append"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -24608,6 +25367,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -24624,6 +25384,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -24633,36 +25396,6 @@
"tui.command.execute"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -24707,6 +25440,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -24723,6 +25457,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -24732,36 +25469,6 @@
"tui.toast.show"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -24808,6 +25515,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -24824,6 +25532,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -24833,36 +25544,6 @@
"tui.session.select"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -24887,6 +25568,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -24903,6 +25585,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -24912,36 +25597,6 @@
"installation.updated"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -24960,6 +25615,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -24976,6 +25632,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -24985,36 +25644,6 @@
"installation.update-available"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -25033,6 +25662,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -25049,6 +25679,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -25058,36 +25691,6 @@
"vcs.branch.updated"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -25103,6 +25706,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -25119,6 +25723,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -25128,36 +25735,6 @@
"mcp.status.changed"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -25176,6 +25753,54 @@
},
"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"
],
@@ -25192,6 +25817,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -25201,36 +25829,6 @@
"permission.asked"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -25308,6 +25906,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -25324,6 +25923,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -25333,36 +25935,6 @@
"permission.replied"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -25404,6 +25976,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -25507,6 +26080,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -25516,36 +26092,6 @@
"question.asked"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -25596,6 +26142,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -25618,6 +26165,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -25627,36 +26177,6 @@
"question.replied"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -25696,6 +26216,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -25712,6 +26233,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -25721,36 +26245,6 @@
"question.rejected"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -25783,6 +26277,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -25799,6 +26294,9 @@
}
]
},
"created": {
"type": "number"
},
"metadata": {
"type": "object"
},
@@ -25808,36 +26306,6 @@
"session.error"
]
},
"durable": {
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
"location": {
"$ref": "#/components/schemas/Location.Ref"
},
@@ -25900,6 +26368,7 @@
},
"required": [
"id",
"created",
"type",
"data"
],
@@ -25926,43 +26395,6 @@
}
]
},
"durable": {
"anyOf": [
{
"type": "object",
"properties": {
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
},
"version": {
"type": "integer",
"allOf": [
{
"minimum": 1
}
]
}
},
"required": [
"aggregateID",
"seq",
"version"
],
"additionalProperties": false
},
{
"type": "null"
}
]
},
"location": {
"anyOf": [
{
@@ -26021,7 +26453,7 @@
"$ref": "#/components/schemas/session.updated"
},
{
"$ref": "#/components/schemas/session.deleted"
"$ref": "#/components/schemas/session.deleted1"
},
{
"$ref": "#/components/schemas/message.updated"
@@ -26036,115 +26468,136 @@
"$ref": "#/components/schemas/message.part.removed"
},
{
"$ref": "#/components/schemas/session.next.agent.switched"
"$ref": "#/components/schemas/session.agent.selected"
},
{
"$ref": "#/components/schemas/session.next.model.switched"
"$ref": "#/components/schemas/session.model.selected"
},
{
"$ref": "#/components/schemas/session.next.moved"
"$ref": "#/components/schemas/session.moved"
},
{
"$ref": "#/components/schemas/session.next.renamed"
"$ref": "#/components/schemas/session.renamed"
},
{
"$ref": "#/components/schemas/session.next.forked"
"$ref": "#/components/schemas/session.usage.updated"
},
{
"$ref": "#/components/schemas/session.next.prompted"
"$ref": "#/components/schemas/session.deleted"
},
{
"$ref": "#/components/schemas/session.next.prompt.admitted"
"$ref": "#/components/schemas/session.forked"
},
{
"$ref": "#/components/schemas/session.next.execution.settled"
"$ref": "#/components/schemas/session.prompt.promoted"
},
{
"$ref": "#/components/schemas/session.next.instructions.updated"
"$ref": "#/components/schemas/session.prompt.admitted"
},
{
"$ref": "#/components/schemas/session.next.synthetic"
"$ref": "#/components/schemas/session.execution.started"
},
{
"$ref": "#/components/schemas/session.next.skill.activated"
"$ref": "#/components/schemas/session.execution.succeeded"
},
{
"$ref": "#/components/schemas/session.next.shell.started"
"$ref": "#/components/schemas/session.execution.failed"
},
{
"$ref": "#/components/schemas/session.next.shell.ended"
"$ref": "#/components/schemas/session.execution.interrupted"
},
{
"$ref": "#/components/schemas/session.next.step.started"
"$ref": "#/components/schemas/session.instructions.updated"
},
{
"$ref": "#/components/schemas/session.next.step.ended"
"$ref": "#/components/schemas/session.synthetic"
},
{
"$ref": "#/components/schemas/session.next.step.failed"
"$ref": "#/components/schemas/session.skill.activated"
},
{
"$ref": "#/components/schemas/session.next.text.started"
"$ref": "#/components/schemas/session.shell.started"
},
{
"$ref": "#/components/schemas/session.next.text.delta"
"$ref": "#/components/schemas/session.shell.ended"
},
{
"$ref": "#/components/schemas/session.next.text.ended"
"$ref": "#/components/schemas/session.step.started"
},
{
"$ref": "#/components/schemas/session.next.reasoning.started"
"$ref": "#/components/schemas/session.step.ended"
},
{
"$ref": "#/components/schemas/session.next.reasoning.delta"
"$ref": "#/components/schemas/session.step.failed"
},
{
"$ref": "#/components/schemas/session.next.reasoning.ended"
"$ref": "#/components/schemas/session.text.started"
},
{
"$ref": "#/components/schemas/session.next.tool.input.started"
"$ref": "#/components/schemas/session.text.delta"
},
{
"$ref": "#/components/schemas/session.next.tool.input.delta"
"$ref": "#/components/schemas/session.text.ended"
},
{
"$ref": "#/components/schemas/session.next.tool.input.ended"
"$ref": "#/components/schemas/session.reasoning.started"
},
{
"$ref": "#/components/schemas/session.next.tool.called"
"$ref": "#/components/schemas/session.reasoning.delta"
},
{
"$ref": "#/components/schemas/session.next.tool.progress"
"$ref": "#/components/schemas/session.reasoning.ended"
},
{
"$ref": "#/components/schemas/session.next.tool.success"
"$ref": "#/components/schemas/session.tool.input.started"
},
{
"$ref": "#/components/schemas/session.next.tool.failed"
"$ref": "#/components/schemas/session.tool.input.delta"
},
{
"$ref": "#/components/schemas/session.next.retried"
"$ref": "#/components/schemas/session.tool.input.ended"
},
{
"$ref": "#/components/schemas/session.next.compaction.started"
"$ref": "#/components/schemas/session.tool.called"
},
{
"$ref": "#/components/schemas/session.next.compaction.delta"
"$ref": "#/components/schemas/session.tool.progress"
},
{
"$ref": "#/components/schemas/session.next.compaction.ended"
"$ref": "#/components/schemas/session.tool.success"
},
{
"$ref": "#/components/schemas/session.next.revert.staged"
"$ref": "#/components/schemas/session.tool.failed"
},
{
"$ref": "#/components/schemas/session.next.revert.cleared"
"$ref": "#/components/schemas/session.retry.scheduled"
},
{
"$ref": "#/components/schemas/session.next.revert.committed"
"$ref": "#/components/schemas/session.compaction.admitted"
},
{
"$ref": "#/components/schemas/file.edited"
"$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"
@@ -26158,6 +26611,9 @@
{
"$ref": "#/components/schemas/plugin.added"
},
{
"$ref": "#/components/schemas/plugin.updated"
},
{
"$ref": "#/components/schemas/project.directories.updated"
},
@@ -26165,10 +26621,10 @@
"$ref": "#/components/schemas/command.updated"
},
{
"$ref": "#/components/schemas/skill.updated"
"$ref": "#/components/schemas/config.updated"
},
{
"$ref": "#/components/schemas/file.watcher.updated"
"$ref": "#/components/schemas/skill.updated"
},
{
"$ref": "#/components/schemas/pty.created"
@@ -26242,6 +26698,9 @@
{
"$ref": "#/components/schemas/mcp.status.changed"
},
{
"$ref": "#/components/schemas/mcp.resources.changed"
},
{
"$ref": "#/components/schemas/permission.asked"
},
@@ -26272,68 +26731,6 @@
},
"contentMediaType": "application/json"
},
"EventLog.Hint": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"log.hint"
]
},
"aggregateID": {
"type": "string"
},
"seq": {
"type": "integer",
"allOf": [
{
"minimum": 0
}
]
}
},
"required": [
"type",
"aggregateID",
"seq"
],
"additionalProperties": false,
"description": "Payload-free change hint: the aggregate's durable log advanced to at least seq. Hints coalesce under backpressure (latest per aggregate) and are never a delivery guarantee."
},
"EventLog.SweepRequired": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": [
"log.sweep_required"
]
}
},
"required": [
"type"
],
"additionalProperties": false,
"description": "Hints may have been lost; treat every aggregate as potentially dirty and recover via bounded sweep plus durable log reads. Emitted first on every (re)subscribe."
},
"EventLog.Change": {
"anyOf": [
{
"$ref": "#/components/schemas/EventLog.Hint"
},
{
"$ref": "#/components/schemas/EventLog.SweepRequired"
}
]
},
"EventLog.ChangeStream": {
"type": "string",
"contentSchema": {
"$ref": "#/components/schemas/EventLog.Change"
},
"contentMediaType": "application/json"
},
"PtyNotFoundError": {
"type": "object",
"properties": {
@@ -26397,182 +26794,6 @@
],
"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": {
@@ -26863,28 +27084,28 @@
"security": [],
"tags": [
{
"name": "server.health"
"name": "health"
},
{
"name": "server.location"
"name": "location"
},
{
"name": "server.agent"
"name": "agent"
},
{
"name": "plugins",
"name": "plugin",
"description": "Experimental plugin routes."
},
{
"name": "sessions",
"name": "session",
"description": "Experimental session routes."
},
{
"name": "messages",
"name": "session",
"description": "Experimental message routes."
},
{
"name": "models",
"name": "model",
"description": "Experimental model routes."
},
{
@@ -26892,30 +27113,30 @@
"description": "Experimental one-shot generation routes."
},
{
"name": "providers",
"name": "provider",
"description": "Experimental provider routes."
},
{
"name": "integrations",
"name": "integration",
"description": "Integration discovery and authentication routes."
},
{
"name": "mcp",
"description": "MCP server status routes."
"description": "MCP server and resource routes."
},
{
"name": "server.credential"
"name": "credential"
},
{
"name": "projects",
"name": "project",
"description": "Location-scoped project routes."
},
{
"name": "forms",
"name": "form",
"description": "Session form routes."
},
{
"name": "permissions",
"name": "permission",
"description": "Experimental permission routes."
},
{
@@ -26923,15 +27144,15 @@
"description": "Experimental location-scoped filesystem routes."
},
{
"name": "commands",
"name": "command",
"description": "Experimental command routes."
},
{
"name": "skills",
"name": "skill",
"description": "Experimental skill routes."
},
{
"name": "events",
"name": "event",
"description": "Experimental event stream routes."
},
{
@@ -26943,7 +27164,7 @@
"description": "Experimental location-scoped shell command routes."
},
{
"name": "session questions",
"name": "question",
"description": "Experimental session question routes."
},
{
@@ -26957,6 +27178,9 @@
{
"name": "vcs",
"description": "Location-scoped version control routes."
},
{
"name": "debug"
}
]
}
+4 -4
View File
@@ -177,13 +177,13 @@ describe("OpenAPI.fromSpec", () => {
const spec = await opencodeSpec()
const result = OpenAPI.fromSpec({ spec, baseUrl })
expect(result.skipped).toHaveLength(5)
expect(result.skipped).toHaveLength(4)
expect(result.skipped).toContainEqual({
method: "GET",
path: "/api/pty/{ptyID}/connect",
reason: "WebSocket operations are not supported",
})
expect(result.skipped.filter((item) => item.reason === "SSE operations are not supported")).toHaveLength(3)
expect(result.skipped.filter((item) => item.reason === "SSE operations are not supported")).toHaveLength(2)
expect(result.skipped).toContainEqual({
method: "GET",
path: "/api/fs/read/*",
@@ -210,11 +210,11 @@ describe("OpenAPI.fromSpec", () => {
if (!Tool.isDefinition(instructionPut)) throw new Error("v2.session.instructions.entry.put was not generated")
expect(inputTypeScript(instructionPut)).toBe("{ sessionID: string; key: string; value: unknown }")
expect(toolAt(result.tools, "v2_session_instructions_entry_put_2")).toBeUndefined()
expect(toolAt(result.tools, "v2.pty.connect")).toBeUndefined()
expect(Tool.isDefinition(toolAt(result.tools, "v2.pty.connect"))).toBe(false)
expect(toolAt(result.tools, "v2.session.log")).toBeUndefined()
expect(toolAt(result.tools, "v2.event.subscribe")).toBeUndefined()
expect(toolAt(result.tools, "v2.fs.read")).toBeUndefined()
expect(toolAt(result.tools, "v2.pty.connectToken")).not.toBeUndefined()
expect(toolAt(result.tools, "v2.pty.connect.token")).not.toBeUndefined()
})
test("preserves operation path sanitization and collision handling", () => {
+6
View File
@@ -245,6 +245,12 @@ describe("promises at data boundaries", () => {
expect(diagnostic.message).toContain("await tools.ns.tool(...)")
})
test("collection helpers do not let un-awaited promises cross the result boundary", async () => {
const diagnostic = await error(`return Array.from([Promise.resolve(1)])`)
expect(diagnostic.kind).toBe("InvalidDataValue")
expect(diagnostic.message).toContain("un-awaited Promise")
})
test("passing an un-awaited promise as a tool argument is a clear diagnostic", async () => {
const diagnostic = await error(`return await tools.host.sleepy({ id: tools.host.sleepy({ id: 1 }) })`)
expect(diagnostic.kind).toBe("InvalidDataValue")
+106
View File
@@ -19,6 +19,28 @@ const error = async (code: string) => {
return result.error
}
describe("Number and Math", () => {
test("Math.random returns a number in [0, 1)", async () => {
expect(await value(`const n = Math.random(); return typeof n === "number" && n >= 0 && n < 1`)).toBe(true)
})
test("Number exposes native non-finite constants", async () => {
expect(
await value(
`return [Number.isNaN(Number.NaN), Number.POSITIVE_INFINITY === Infinity, Number.NEGATIVE_INFINITY === -Infinity]`,
),
).toEqual([true, true, true])
})
test("Number valueOf returns its primitive receiver", async () => {
expect(await value(`return (42).valueOf()`)).toBe(42)
})
test("Number valueOf does not enable boxed numbers", async () => {
expect((await error(`return new Number(42)`)).kind).toBe("UnsupportedSyntax")
})
})
describe("Date", () => {
test("Date.now() returns a number", async () => {
expect(await value(`return typeof Date.now()`)).toBe("number")
@@ -751,6 +773,43 @@ describe("sandbox values at intra-sandbox checkpoints", () => {
)
})
test("Object.values/entries preserve nested object identity", async () => {
expect(
await value(`
const child = { selected: false }
const rows = { a: child }
Object.values(rows)[0].selected = true
return child.selected
`),
).toBe(true)
expect(
await value(`
const child = { selected: false }
const rows = { a: child }
Object.entries(rows)[0][1].selected = true
return child.selected
`),
).toBe(true)
})
test("Object enumeration preserves promises and callable references", async () => {
expect(
await value(`
const pending = Promise.resolve(1)
const source = { pending }
return [Object.keys(source), Object.hasOwn(source, "pending"), await Object.values(source)[0], await Object.entries(source)[0][1]]
`),
).toEqual([["pending"], true, 1, 1])
expect(await value(`return Object.values({ max: Math.max })[0](1, 2)`)).toBe(2)
})
test("Object enumeration rejects invalid receivers and gives promises an await hint", async () => {
const diagnostic = await error(`return Object.keys(Promise.resolve({ a: 1 }))`)
expect(diagnostic.kind).toBe("InvalidDataValue")
expect(diagnostic.message).toContain("await")
expect((await error(`return Object.keys(Math)`)).kind).toBe("InvalidDataValue")
})
test("Object.assign keeps Maps usable", async () => {
expect(await value(`const merged = Object.assign({}, { m: new Map([["a", 1]]) }); return merged.m.get("a")`)).toBe(
1,
@@ -773,6 +832,53 @@ describe("sandbox values at intra-sandbox checkpoints", () => {
expect(await value(`return Array.from([new Date(5)])[0].getTime()`)).toBe(5)
})
test("Array.from and Array.of preserve nested object identity", async () => {
expect(
await value(`
const child = { selected: false }
Array.from([child])[0].selected = true
return child.selected
`),
).toBe(true)
expect(
await value(`
const child = { selected: false }
Array.of(child)[0].selected = true
return child.selected
`),
).toBe(true)
})
test("Array.from and Array.of preserve promises and callable references", async () => {
expect(
await value(`
const pending = Promise.resolve(1)
return [await Array.from([pending])[0], await Array.of(pending)[0]]
`),
).toEqual([1, 1])
expect(await value(`return [Array.from([Math.max])[0](1, 2), Array.of(Math.max)[0](3, 4)]`)).toEqual([2, 4])
})
test("Array.from preserves identity across supported collection shapes", async () => {
expect(
await value(`
const child = { selected: false }
const fromArrayLike = Array.from({ 0: child, length: 1 })
const fromMap = Array.from(new Map([["child", child]]))
const fromSet = Array.from(new Set([child]))
fromArrayLike[0].selected = true
return [fromMap[0][1] === child, fromSet[0] === child, child.selected]
`),
).toEqual([true, true, true])
})
test("Array.from rejects invalid receivers and gives promises an await hint", async () => {
const diagnostic = await error(`return Array.from(Promise.resolve([1]))`)
expect(diagnostic.kind).toBe("InvalidDataValue")
expect(diagnostic.message).toContain("await")
expect((await error(`return Array.from(() => 1)`)).kind).toBe("InvalidDataValue")
})
test("regexes stay callable through Object.values", async () => {
expect(await value(`return Object.values({ r: /ab+/ })[0].test("abb")`)).toBe(true)
})
+3 -2
View File
@@ -6,6 +6,7 @@ import type {
JSONValue,
LanguageModelV3,
LanguageModelV3CallOptions,
LanguageModelV3FinishReason,
LanguageModelV3FunctionTool,
LanguageModelV3Message,
LanguageModelV3Prompt,
@@ -624,8 +625,8 @@ function usage(input: Extract<LanguageModelV3StreamPart, { type: "finish" }>["us
return Object.values(output).some((value) => value !== undefined) ? output : undefined
}
function finishReason(value: unknown): FinishReason {
return Schema.is(FinishReason)(value) ? value : "unknown"
function finishReason(value: LanguageModelV3FinishReason): FinishReason {
return value.unified === "other" ? "unknown" : value.unified
}
function providerMetadata(value: unknown) {
+1 -1
View File
@@ -200,6 +200,6 @@ export const node = makeLocationNode({ service: Service, layer, deps: [FSUtil.no
// TODO: Publish watcher/file-edit events after V2 watcher integration exists.
// TODO: Add snapshots / undo after V2 snapshot design exists.
// TODO: Notify LSP and collect diagnostics after V2 LSP runtime exists.
// TODO: Design multi-file transactions / rollback if apply_patch needs atomic edits.
// TODO: Design multi-file transactions / rollback if patch needs atomic edits.
// Until then, edits are sequential and report partial application.
// TODO: Define crash recovery and idempotency for side effects between Tool.Called and durable settlement.
+39 -72
View File
@@ -5,37 +5,29 @@ import { Catalog } from "./catalog"
import { CommandV2 } from "./command"
import { Config } from "./config"
import { LayerNode } from "./effect/layer-node"
import { makeLocationNode, Node } from "./effect/app-node"
import { httpClient } from "./effect/app-node-platform"
import { Node } from "./effect/app-node"
import { EventV2 } from "./event"
import { FileMutation } from "./file-mutation"
import { FileSystem } from "./filesystem"
import { FileSystemSearch } from "./filesystem/search"
import { FSUtil } from "./fs-util"
import { Generate } from "./generate"
import { Form } from "./form"
import { Global } from "./global"
import { LocationWatcher } from "./filesystem/location-watcher"
import { Image } from "./image"
import { LocationWatcher } from "./filesystem/location-watcher"
import { Integration } from "./integration"
import { Location } from "./location"
import { LocationMutation } from "./location-mutation"
import { LocationServiceMap } from "./location-service-map"
import { MCP } from "./mcp/index"
import { ModelsDev } from "./models-dev"
import { Npm } from "./npm"
import { PermissionV2 } from "./permission"
import { PluginV2 } from "./plugin"
import { PluginRuntime } from "./plugin/runtime"
import { SdkPlugins } from "./plugin/sdk"
import { PluginSupervisor } from "./plugin/supervisor"
import { PluginSupervisorNode } from "./plugin/supervisor-node"
import { ProjectCopy } from "./project/copy"
import { Pty } from "./pty"
import { QuestionV2 } from "./question"
import { Shell } from "./shell"
import { Reference } from "./reference"
import { ReferenceGuidance } from "./reference/guidance"
import { Ripgrep } from "./ripgrep"
import { SessionRunnerLLM } from "./session/runner/llm"
import { SessionRunnerModel } from "./session/runner/model"
import { SessionCompaction } from "./session/compaction"
@@ -51,49 +43,11 @@ import { SessionInstructions } from "./session/instructions"
import { McpTool } from "./tool/mcp"
import { ReadToolFileSystem } from "./tool/read-filesystem"
import { ToolRegistry } from "./tool/registry"
import { WebSearchTool } from "./tool/websearch"
import { ToolOutputStore } from "./tool-output-store"
import { Vcs } from "./vcs"
export { LocationServiceMap } from "./location-service-map"
const pluginSupervisorNode = makeLocationNode({
service: PluginSupervisor.Service,
layer: PluginSupervisor.layer,
deps: [
PluginV2.node,
SdkPlugins.node,
AgentV2.node,
Catalog.node,
CommandV2.node,
Config.node,
EventV2.node,
FileMutation.node,
FileSystem.node,
FSUtil.node,
Global.node,
httpClient,
Image.node,
Integration.node,
Location.node,
LocationMutation.node,
ModelsDev.node,
Npm.node,
PermissionV2.node,
PluginRuntime.node,
Form.node,
ReadToolFileSystem.node,
Reference.node,
Ripgrep.node,
SessionInstructions.node,
SessionTodo.node,
Shell.node,
SkillV2.node,
ToolRegistry.toolsNode,
WebSearchTool.configNode,
],
})
const locationServiceNodes = [
Location.node,
Config.node,
@@ -104,7 +58,7 @@ const locationServiceNodes = [
Catalog.node,
AISDK.node,
PluginV2.node,
pluginSupervisorNode,
PluginSupervisorNode.node,
ProjectCopy.node,
ProjectCopy.refreshNode,
FileSystemSearch.node,
@@ -150,31 +104,44 @@ export type LocationError = LayerNode.Error<typeof locationServices>
export function buildLocationServiceMap(
replacements: LayerNode.Replacements = [],
): Layer.Layer<LocationServiceMap.Service> {
// Structural Equal is own-key-set sensitive, so `{ directory }` (schema-decoded
// payloads omit optional keys) and `{ directory, workspaceID: undefined }` are
// different RcMap keys. The RcMap caches by the raw key before the build
// callback runs, so canonicalize at the map boundary to the key-present shape.
const canonical = (ref: Location.Ref) => Location.Ref.make({ directory: ref.directory, workspaceID: ref.workspaceID })
return Layer.effect(
LocationServiceMap.Service,
LayerMap.make(
(ref: Location.Ref) => {
const startedAt = performance.now()
const allReplacements = replacements.concat([[Location.node, Location.boundNode(ref)]])
// Apply replacements during hoist, not afterward: replacements can
// introduce new tagged dependencies (Location.boundNode depends on
// Project), and the hoist walk is the only pass that can still slice
// those back out.
const location = LayerNode.hoist(locationServices, Node.tags.values.global, allReplacements)
Effect.map(
LayerMap.make(
(ref: Location.Ref) => {
const startedAt = performance.now()
const allReplacements = replacements.concat([[Location.node, Location.boundNode(ref)]])
// Apply replacements during hoist, not afterward: replacements can
// introduce new tagged dependencies (Location.boundNode depends on
// Project), and the hoist walk is the only pass that can still slice
// those back out.
const location = LayerNode.hoist(locationServices, Node.tags.values.global, allReplacements)
return LayerNode.compile(location.node).pipe(
Layer.fresh,
Layer.tap(() =>
Effect.logInfo("location services booted", {
directory: ref.directory,
workspaceID: ref.workspaceID,
durationMs: Math.round(performance.now() - startedAt),
}),
),
Layer.provide(LayerNode.compile(location.hoisted)),
)
},
{ idleTimeToLive: "60 minutes" },
return LayerNode.compile(location.node).pipe(
Layer.fresh,
Layer.tap(() =>
Effect.logInfo("location services booted", {
directory: ref.directory,
workspaceID: ref.workspaceID,
durationMs: Math.round(performance.now() - startedAt),
}),
),
Layer.provide(LayerNode.compile(location.hoisted)),
)
},
{ idleTimeToLive: "60 minutes" },
),
(inner) => ({
...inner,
get: (ref: Location.Ref) => inner.get(canonical(ref)),
contextEffect: (ref: Location.Ref) => inner.contextEffect(canonical(ref)),
invalidate: (ref: Location.Ref) => inner.invalidate(canonical(ref)),
}),
),
)
}
+118 -16
View File
@@ -3,7 +3,7 @@ export * as MCPClient from "./client"
import path from "node:path"
import { execFile } from "node:child_process"
import { pathToFileURL } from "node:url"
import { Client, type ClientOptions } from "@modelcontextprotocol/sdk/client/index.js"
import { Client } from "@modelcontextprotocol/sdk/client/index.js"
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"
import { UnauthorizedError, type OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js"
@@ -21,6 +21,7 @@ import {
ListToolsResultSchema,
PromptListChangedNotificationSchema,
PromptSchema,
ResourceListChangedNotificationSchema,
type LoggingMessageNotification,
LoggingMessageNotificationSchema,
ToolListChangedNotificationSchema,
@@ -68,11 +69,13 @@ export interface ToolDefinition {
export interface PromptDefinition {
readonly name: string
readonly description: string | undefined
readonly arguments: ReadonlyArray<{
readonly name: string
readonly description: string | undefined
readonly required: boolean | undefined
}> | undefined
readonly arguments:
| ReadonlyArray<{
readonly name: string
readonly description: string | undefined
readonly required: boolean | undefined
}>
| undefined
}
export interface PromptMessage {
@@ -84,6 +87,28 @@ export interface PromptResult {
readonly messages: ReadonlyArray<PromptMessage>
}
export interface ResourceDefinition {
readonly name: string
readonly uri: string
readonly description: string | undefined
readonly mimeType: string | undefined
}
export interface ResourceTemplateDefinition {
readonly name: string
readonly uriTemplate: string
readonly description: string | undefined
readonly mimeType: string | undefined
}
export type ResourceContentPart =
| { readonly type: "text"; readonly uri: string; readonly text: string; readonly mimeType: string | undefined }
| { readonly type: "blob"; readonly uri: string; readonly blob: string; readonly mimeType: string | undefined }
export interface ReadResourceResult {
readonly contents: ReadonlyArray<ResourceContentPart>
}
export type CallToolContent =
| { readonly type: "text"; readonly text: string }
| { readonly type: "media"; readonly data: string; readonly mimeType: string }
@@ -124,6 +149,12 @@ export interface Connection {
readonly tools: () => Effect.Effect<ToolDefinition[], Error>
/** Lists the server's prompts; returns [] when the server doesn't advertise prompt support, fails on a transport error. */
readonly prompts: () => Effect.Effect<PromptDefinition[], Error>
/** Lists the server's resources; returns [] when the server doesn't advertise resource support. */
readonly resources: () => Effect.Effect<ResourceDefinition[], Error>
/** Lists the server's resource templates; returns [] when the server doesn't advertise resource support. */
readonly resourceTemplates: () => Effect.Effect<ResourceTemplateDefinition[], Error>
/** Reads one resource; returns undefined when the server doesn't advertise resource support. */
readonly readResource: (input: { readonly uri: string }) => Effect.Effect<ReadResourceResult | undefined, Error>
/** Invokes a prompt on the server. Interruption aborts the in-flight request. */
readonly prompt: (input: {
readonly name: string
@@ -141,6 +172,8 @@ export interface Connection {
readonly onToolsChanged: (callback: () => void) => void
/** Registers a callback fired when the server announces its prompt list changed; no-op if unsupported. */
readonly onPromptsChanged: (callback: () => void) => void
/** Registers a callback fired when the server announces its resource catalog changed. */
readonly onResourcesChanged: (callback: () => void) => void
}
/** Connects an MCP server; closing the calling scope tears down the transport and any spawned process. */
@@ -168,7 +201,8 @@ export const connect = Effect.fnUntraced(function* (
},
})
}
if (!URL.canParse(config.url)) return yield* new ConnectError({ server, message: `Invalid MCP URL for "${server}"` })
if (!URL.canParse(config.url))
return yield* new ConnectError({ server, message: `Invalid MCP URL for "${server}"` })
return new StreamableHTTPClientTransport(new URL(config.url), {
requestInit: config.headers ? { headers: config.headers } : undefined,
authProvider,
@@ -202,10 +236,7 @@ export const connect = Effect.fnUntraced(function* (
}).pipe(Effect.exit)
if (Exit.isSuccess(exit)) {
yield* Effect.addFinalizer(() =>
cleanupStdioDescendants(transport).pipe(
Effect.andThen(Effect.promise(() => client.close())),
Effect.ignore,
),
cleanupStdioDescendants(transport).pipe(Effect.andThen(Effect.promise(() => client.close())), Effect.ignore),
)
const catalogTimeout = config.timeout?.catalog ?? DEFAULT_CATALOG_TIMEOUT
const executionTimeout = config.timeout?.execution ?? DEFAULT_EXECUTION_TIMEOUT
@@ -257,7 +288,9 @@ export const connect = Effect.fnUntraced(function* (
),
catch: (error) => (error instanceof Error ? error : new Error(String(error))),
}).pipe(
Effect.tapError((error) => Effect.logWarning("failed to list MCP prompts", { server, error: error.message })),
Effect.tapError((error) =>
Effect.logWarning("failed to list MCP prompts", { server, error: error.message }),
),
)
return prompts.map((prompt) => ({
name: prompt.name,
@@ -269,6 +302,74 @@ export const connect = Effect.fnUntraced(function* (
})),
}))
}),
resources: () =>
Effect.gen(function* () {
if (!client.getServerCapabilities()?.resources) return []
const resources = yield* Effect.tryPromise({
try: () =>
paginate(
(cursor) =>
client.listResources(cursor === undefined ? undefined : { cursor }, { timeout: catalogTimeout }),
(result) => result.resources,
),
catch: (error) => (error instanceof Error ? error : new Error(String(error))),
}).pipe(
Effect.tapError((error) =>
Effect.logWarning("failed to list MCP resources", { server, error: error.message }),
),
)
return resources.map((resource) => ({
name: resource.name,
uri: resource.uri,
description: resource.description,
mimeType: resource.mimeType,
}))
}),
resourceTemplates: () =>
Effect.gen(function* () {
if (!client.getServerCapabilities()?.resources) return []
const templates = yield* Effect.tryPromise({
try: () =>
paginate(
(cursor) =>
client.listResourceTemplates(cursor === undefined ? undefined : { cursor }, {
timeout: catalogTimeout,
}),
(result) => result.resourceTemplates,
),
catch: (error) => (error instanceof Error ? error : new Error(String(error))),
}).pipe(
Effect.tapError((error) =>
Effect.logWarning("failed to list MCP resource templates", { server, error: error.message }),
),
)
return templates.map((template) => ({
name: template.name,
uriTemplate: template.uriTemplate,
description: template.description,
mimeType: template.mimeType,
}))
}),
readResource: (input) =>
Effect.gen(function* () {
if (!client.getServerCapabilities()?.resources) return undefined
const result = yield* Effect.tryPromise({
try: (signal) => client.readResource({ uri: input.uri }, { signal, timeout: executionTimeout }),
catch: (error) => (error instanceof Error ? error : new Error(String(error))),
}).pipe(
Effect.tapError((error) =>
Effect.logWarning("failed to read MCP resource", { server, uri: input.uri, error: error.message }),
),
)
return {
contents: result.contents.map(
(part): ResourceContentPart =>
"text" in part
? { type: "text", uri: part.uri, text: part.text, mimeType: part.mimeType }
: { type: "blob", uri: part.uri, blob: part.blob, mimeType: part.mimeType },
),
}
}),
prompt: (input) =>
Effect.tryPromise({
try: (signal) =>
@@ -328,13 +429,14 @@ export const connect = Effect.fnUntraced(function* (
if (!client.getServerCapabilities()?.prompts?.listChanged) return
client.setNotificationHandler(PromptListChangedNotificationSchema, async () => callback())
},
onResourcesChanged: (callback) => {
if (!client.getServerCapabilities()?.resources?.listChanged) return
client.setNotificationHandler(ResourceListChangedNotificationSchema, async () => callback())
},
} satisfies Connection
}
yield* cleanupStdioDescendants(transport).pipe(
Effect.andThen(Effect.promise(() => transport.close())),
Effect.ignore,
)
yield* cleanupStdioDescendants(transport).pipe(Effect.andThen(Effect.promise(() => transport.close())), Effect.ignore)
const error = Cause.squash(exit.cause)
if (error instanceof UnauthorizedError) return yield* new NeedsAuthError({ server })
return yield* new ConnectError({ server, message: error instanceof Error ? error.message : String(error) })
+80 -50
View File
@@ -83,48 +83,16 @@ export class PromptResult extends Schema.Class<PromptResult>("MCP.PromptResult")
messages: Schema.Array(PromptMessage),
}) {}
export class Resource extends Schema.Class<Resource>("MCP.Resource")({
server: ServerName,
name: Schema.String,
uri: Schema.String,
description: Schema.String.pipe(Schema.optional),
mimeType: Schema.String.pipe(Schema.optional),
}) {}
export class ResourceTemplate extends Schema.Class<ResourceTemplate>("MCP.ResourceTemplate")({
server: ServerName,
name: Schema.String,
uriTemplate: Schema.String,
description: Schema.String.pipe(Schema.optional),
mimeType: Schema.String.pipe(Schema.optional),
}) {}
export class ResourceCatalog extends Schema.Class<ResourceCatalog>("MCP.ResourceCatalog")({
resources: Schema.Array(Resource),
templates: Schema.Array(ResourceTemplate),
}) {}
export const ResourceContentPart = Schema.Union([
Schema.Struct({
type: Schema.Literal("text"),
uri: Schema.String,
text: Schema.String,
mimeType: Schema.String.pipe(Schema.optional),
}),
Schema.Struct({
type: Schema.Literal("blob"),
uri: Schema.String,
blob: Schema.String,
mimeType: Schema.String.pipe(Schema.optional),
}),
]).pipe(Schema.toTaggedUnion("type"))
export type ResourceContentPart = typeof ResourceContentPart.Type
export class ResourceContent extends Schema.Class<ResourceContent>("MCP.ResourceContent")({
server: ServerName,
uri: Schema.String,
contents: Schema.Array(ResourceContentPart),
}) {}
export const Resource = Mcp.Resource
export type Resource = Mcp.Resource
export const ResourceTemplate = Mcp.ResourceTemplate
export type ResourceTemplate = Mcp.ResourceTemplate
export const ResourceCatalog = Mcp.ResourceCatalog
export type ResourceCatalog = Mcp.ResourceCatalog
export const ResourceContentPart = Mcp.ResourceContentPart
export type ResourceContentPart = Mcp.ResourceContentPart
export const ResourceContent = Mcp.ResourceContent
export type ResourceContent = Mcp.ResourceContent
export class NotFoundError extends Schema.TaggedErrorClass<NotFoundError>()("MCP.NotFoundError", {
server: ServerName,
@@ -415,6 +383,24 @@ export const layer = Layer.effect(
),
})
const toResource = (server: ServerName, def: MCPClient.ResourceDefinition) =>
Resource.make({
server,
name: def.name,
uri: def.uri,
description: def.description,
mimeType: def.mimeType,
})
const toResourceTemplate = (server: ServerName, def: MCPClient.ResourceTemplateDefinition) =>
ResourceTemplate.make({
server,
name: def.name,
uriTemplate: def.uriTemplate,
description: def.description,
mimeType: def.mimeType,
})
const refreshTools = (name: ServerName, entry: ServerEntry, connection: MCPClient.Connection) =>
connection.tools().pipe(
Effect.map((defs) => {
@@ -443,6 +429,7 @@ export const layer = Layer.effect(
entry.prompts = undefined
entry.status = { status: "failed", error: "Connection closed" }
fork(events.publish(McpEvent.ToolsChanged, { server: name }).pipe(Effect.ignore))
fork(events.publish(McpEvent.ResourcesChanged, { server: name }).pipe(Effect.ignore))
fork(events.publish(Command.Event.Updated, {}).pipe(Effect.ignore))
fork(events.publish(McpEvent.StatusChanged, { server: name }).pipe(Effect.ignore))
})
@@ -458,6 +445,10 @@ export const layer = Layer.effect(
connection.onPromptsChanged(() => {
fork(refreshPrompts(name, entry, connection).pipe(Effect.ignore))
})
connection.onResourcesChanged(() => {
if (entry.client !== connection) return
fork(events.publish(McpEvent.ResourcesChanged, { server: name }).pipe(Effect.ignore))
})
}
const serverLog = (server: ServerName, message: MCPClient.LogMessage) => {
@@ -501,6 +492,7 @@ export const layer = Layer.effect(
// after the initial registration sweep and emits no list-changed notification would otherwise
// stay invisible to the model.
yield* events.publish(McpEvent.ToolsChanged, { server: name }).pipe(Effect.ignore)
yield* events.publish(McpEvent.ResourcesChanged, { server: name }).pipe(Effect.ignore)
yield* events.publish(McpEvent.StatusChanged, { server: name }).pipe(Effect.ignore)
fork(refreshPrompts(name, entry, result.value.connection).pipe(Effect.ignore))
return
@@ -557,11 +549,6 @@ export const layer = Layer.effect(
concurrency: "unbounded",
discard: true,
})
const gate = Effect.fnUntraced(function* (server: ServerName | string) {
const target = yield* requireServer(server)
yield* Deferred.await(target.entry.startup)
})
return Service.of({
servers: Effect.fn("MCP.servers")(function* () {
const entries = Array.from(runtime).toSorted(([a], [b]) => a.localeCompare(b))
@@ -637,11 +624,54 @@ export const layer = Layer.effect(
}),
resourceCatalog: Effect.fn("MCP.resourceCatalog")(function* () {
yield* whenAllReady
return new ResourceCatalog({ resources: [], templates: [] })
const catalogs = yield* Effect.forEach(
Array.from(runtime),
([name, entry]) => {
if (!entry.client) return Effect.succeed({ resources: [], templates: [] })
return Effect.all(
{
resources: entry.client.resources().pipe(Effect.catch(() => Effect.succeed([]))),
templates: entry.client.resourceTemplates().pipe(Effect.catch(() => Effect.succeed([]))),
},
{ concurrency: "unbounded" },
).pipe(
Effect.map((catalog) => ({
resources: catalog.resources.map((def) => toResource(name, def)),
templates: catalog.templates.map((def) => toResourceTemplate(name, def)),
})),
)
},
{ concurrency: "unbounded" },
)
return ResourceCatalog.make({
resources: catalogs
.flatMap((catalog) => catalog.resources)
.toSorted(
(a, b) => a.server.localeCompare(b.server) || a.name.localeCompare(b.name) || a.uri.localeCompare(b.uri),
),
templates: catalogs
.flatMap((catalog) => catalog.templates)
.toSorted(
(a, b) =>
a.server.localeCompare(b.server) ||
a.name.localeCompare(b.name) ||
a.uriTemplate.localeCompare(b.uriTemplate),
),
})
}),
readResource: Effect.fn("MCP.readResource")(function* (input) {
yield* gate(input.server)
return undefined
const target = yield* requireServer(input.server)
yield* Deferred.await(target.entry.startup)
if (!target.entry.client) return undefined
const result = yield* target.entry.client
.readResource({ uri: input.uri })
.pipe(Effect.catch(() => Effect.succeed(undefined)))
if (!result) return undefined
return ResourceContent.make({
server: target.name,
uri: input.uri,
contents: result.contents,
})
}),
})
}),
+19 -7
View File
@@ -1,6 +1,6 @@
export * as PluginV2 from "./plugin"
import type { Plugin } from "@opencode-ai/plugin/v2/effect"
import type { Plugin } from "@opencode-ai/plugin/v2/effect/plugin"
import { Event, ID, type Info } from "@opencode-ai/schema/plugin"
import { makeLocationNode } from "./effect/app-node"
import { Context, Effect, Exit, Layer, Scope, Semaphore } from "effect"
@@ -18,9 +18,13 @@ import { SkillV2 } from "./skill"
import { State } from "./state"
import { ToolRegistry } from "./tool/registry"
import { ToolHooks } from "./tool/hooks"
import { PluginHooks } from "./plugin/hooks"
export interface Interface {
readonly activate: (plugins: readonly { readonly plugin: Plugin; readonly version?: string }[]) => Effect.Effect<void>
readonly activate: (
plugins: readonly { readonly plugin: Plugin; readonly version?: string }[],
options?: { readonly force?: boolean },
) => Effect.Effect<void>
readonly list: () => Effect.Effect<Info[]>
}
@@ -38,6 +42,7 @@ const layer = Layer.effect(
const activate = Effect.fn("Plugin.activate")(function* (
plugins: readonly { readonly plugin: Plugin; readonly version?: string }[],
options?: { readonly force?: boolean },
) {
const definitions = plugins.map((entry) => ({
...entry.plugin,
@@ -53,16 +58,18 @@ const layer = Layer.effect(
yield* lock.withPermit(
Effect.gen(function* () {
if (
!options?.force &&
generation !== undefined &&
generation.length === definitions.length &&
generation.every(
(plugin, index) => plugin.id === definitions[index]?.id && plugin.version === definitions[index]?.version,
)
) &&
definitions.every((definition) => active.has(definition.id))
) {
return
}
generation = undefined
const exit = yield* State.batch(
yield* State.batch(
Effect.gen(function* () {
const scopes = Array.from(active.values()).toReversed()
active.clear()
@@ -81,13 +88,17 @@ const layer = Layer.effect(
Effect.onExit((exit) => (Exit.isFailure(exit) ? Scope.close(child, exit) : Effect.void)),
Effect.exit,
)
if (Exit.isFailure(loaded)) return loaded
if (Exit.isFailure(loaded)) {
yield* Effect.logWarning("failed to load plugin", {
"plugin.id": definition.id,
cause: loaded.cause,
})
continue
}
active.set(definition.id, child)
}
return Exit.void
}),
)
if (Exit.isFailure(exit)) return yield* exit
generation = definitions.map((definition) => ({
id: definition.id,
...(definition.version === undefined ? {} : { version: definition.version }),
@@ -131,6 +142,7 @@ export const node = makeLocationNode({
SkillV2.node,
ToolRegistry.toolsNode,
ToolHooks.node,
PluginHooks.node,
PluginRuntime.node,
],
})
+67
View File
@@ -0,0 +1,67 @@
export * as PluginHooks from "./hooks"
import type { AISDKHooks } from "@opencode-ai/plugin/v2/effect/aisdk"
import type { SessionHooks } from "@opencode-ai/plugin/v2/effect/session"
import type { ToolHooks } from "@opencode-ai/plugin/v2/effect/tool"
import { Context, Effect, Layer, Scope } from "effect"
import { makeLocationNode } from "../effect/app-node"
import { State } from "../state"
export interface Domains {
readonly aisdk: AISDKHooks
readonly session: SessionHooks
readonly tool: ToolHooks
}
type Callback<Event> = (event: Event) => Effect.Effect<void>
export interface Interface {
readonly register: <Domain extends keyof Domains, Name extends keyof Domains[Domain]>(
domain: Domain,
name: Name,
callback: Callback<Domains[Domain][Name]>,
) => Effect.Effect<State.Registration, never, Scope.Scope>
readonly trigger: <Domain extends keyof Domains, Name extends keyof Domains[Domain]>(
domain: Domain,
name: Name,
event: Domains[Domain][Name],
) => Effect.Effect<Domains[Domain][Name]>
}
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/PluginHooks") {}
const layer = Layer.effect(
Service,
Effect.gen(function* () {
const callbacks = new Map<string, Function[]>()
const key = (domain: keyof Domains, name: PropertyKey) => `${domain}.${String(name)}`
const register: Interface["register"] = Effect.fn("PluginHooks.register")(function* (domain, name, callback) {
const scope = yield* Scope.Scope
const id = key(domain, name)
let active = true
callbacks.set(id, [...(callbacks.get(id) ?? []), callback])
const dispose = Effect.sync(() => {
if (!active) return
active = false
const next = (callbacks.get(id) ?? []).filter((item) => item !== callback)
if (next.length === 0) callbacks.delete(id)
else callbacks.set(id, next)
})
yield* Scope.addFinalizer(scope, dispose)
return { dispose }
})
const trigger: Interface["trigger"] = Effect.fn("PluginHooks.trigger")(function* (domain, name, event) {
for (const callback of callbacks.get(key(domain, name)) ?? []) {
const result: Effect.Effect<void> = Reflect.apply(callback, undefined, [event])
yield* result
}
return event
})
return Service.of({ register, trigger })
}),
)
export const node = makeLocationNode({ service: Service, layer, deps: [] })
+74 -68
View File
@@ -1,6 +1,6 @@
export * as PluginHost from "./host"
import type { PluginContext } from "@opencode-ai/plugin/v2/effect"
import type { Plugin } from "@opencode-ai/plugin/v2/effect"
import { EventManifest } from "@opencode-ai/schema/event-manifest"
import { Effect, Schema, Stream } from "effect"
import { AgentV2 } from "../agent"
@@ -22,6 +22,7 @@ import { Tool } from "../tool/tool"
import { Tools } from "../tool/tools"
import { ToolHooks } from "../tool/hooks"
import { WorkspaceV2 } from "../workspace"
import { PluginHooks } from "./hooks"
const mutable = <T>(value: T) => value as DeepMutable<T>
export const make = Effect.fn("PluginHost.make")(function* (plugin: PluginV2.Interface) {
@@ -36,6 +37,7 @@ export const make = Effect.fn("PluginHost.make")(function* (plugin: PluginV2.Int
const skill = yield* SkillV2.Service
const tools = yield* Tools.Service
const toolHooks = yield* ToolHooks.Service
const hooks = yield* PluginHooks.Service
const runtime = yield* PluginRuntime.Service
const locationInfo = () =>
new Location.Info({
@@ -43,7 +45,7 @@ export const make = Effect.fn("PluginHost.make")(function* (plugin: PluginV2.Int
workspaceID: location.workspaceID,
project: location.project,
})
const locationRef = (input?: Parameters<PluginContext["agent"]["list"]>[0]) =>
const locationRef = (input?: Parameters<Plugin.Context["agent"]["list"]>[0]) =>
input?.location === undefined
? undefined
: Location.Ref.make({
@@ -79,32 +81,32 @@ export const make = Effect.fn("PluginHost.make")(function* (plugin: PluginV2.Int
}),
},
aisdk: {
sdk: (callback) =>
aisdk.hook.sdk((event) => {
hook: (name, callback) => {
if (name === "sdk") {
return aisdk.hook.sdk((event) => {
const output = {
model: mutable(event.model),
package: event.package,
options: event.options,
sdk: event.sdk,
}
return Reflect.apply(callback, undefined, [output]).pipe(
Effect.tap(() => Effect.sync(() => (event.sdk = output.sdk))),
)
})
}
return aisdk.hook.language((event) => {
const output = {
model: mutable(event.model),
package: event.package,
options: event.options,
sdk: event.sdk,
}
const result = callback(output)
return Effect.suspend(() => (Effect.isEffect(result) ? result : Effect.void)).pipe(
Effect.tap(() => Effect.sync(() => (event.sdk = output.sdk))),
)
}),
language: (callback) =>
aisdk.hook.language((event) => {
const output = {
model: mutable(event.model),
sdk: event.sdk,
options: event.options,
language: event.language,
}
const result = callback(output)
return Effect.suspend(() => (Effect.isEffect(result) ? result : Effect.void)).pipe(
return Reflect.apply(callback, undefined, [output]).pipe(
Effect.tap(() => Effect.sync(() => (event.language = output.language))),
)
}),
})
},
},
catalog: {
provider: {
@@ -164,25 +166,29 @@ export const make = Effect.fn("PluginHost.make")(function* (plugin: PluginV2.Int
integration: {
list: () => response(integration.list()),
get: (input) => response(integration.get(Integration.ID.make(input.integrationID))),
connectKey: (input) =>
integration.connection.key({
integrationID: Integration.ID.make(input.integrationID),
key: input.key,
label: input.label,
}),
connectOauth: (input) =>
response(
integration.connection.oauth({
connect: {
key: (input) =>
integration.connection.key({
integrationID: Integration.ID.make(input.integrationID),
methodID: Integration.MethodID.make(input.methodID),
inputs: input.inputs,
key: input.key,
label: input.label,
}),
),
attemptStatus: (input) => response(integration.attempt.status(Integration.AttemptID.make(input.attemptID))),
attemptComplete: (input) =>
integration.attempt.complete({ attemptID: Integration.AttemptID.make(input.attemptID), code: input.code }),
attemptCancel: (input) => integration.attempt.cancel(Integration.AttemptID.make(input.attemptID)),
oauth: (input) =>
response(
integration.connection.oauth({
integrationID: Integration.ID.make(input.integrationID),
methodID: Integration.MethodID.make(input.methodID),
inputs: input.inputs,
label: input.label,
}),
),
},
attempt: {
status: (input) => response(integration.attempt.status(Integration.AttemptID.make(input.attemptID))),
complete: (input) =>
integration.attempt.complete({ attemptID: Integration.AttemptID.make(input.attemptID), code: input.code }),
cancel: (input) => integration.attempt.cancel(Integration.AttemptID.make(input.attemptID)),
},
reload: integration.reload,
connection: {
active: (id) => integration.connection.active(Integration.ID.make(id)),
@@ -317,11 +323,12 @@ export const make = Effect.fn("PluginHost.make")(function* (plugin: PluginV2.Int
registrations,
(registration) => tools.register({ [registration.name]: registration.tool }, registration.options),
{ discard: true },
)
).pipe(Effect.orDie)
return { dispose: Effect.void }
}),
execute: {
before: (callback) =>
toolHooks.hook.before((event) => {
hook: (name, callback) => {
if (name === "execute.before") {
return toolHooks.hook.before((event) => {
const output = {
tool: event.tool,
sessionID: event.sessionID,
@@ -330,38 +337,37 @@ export const make = Effect.fn("PluginHost.make")(function* (plugin: PluginV2.Int
toolCallID: event.toolCallID,
input: event.input,
}
const result = callback(output)
return Effect.suspend(() => (Effect.isEffect(result) ? result : Effect.void)).pipe(
return Reflect.apply(callback, undefined, [output]).pipe(
Effect.tap(() => Effect.sync(() => (event.input = output.input))),
)
}),
after: (callback) =>
toolHooks.hook.after((event) => {
const output = {
tool: event.tool,
sessionID: event.sessionID,
agent: event.agent,
assistantMessageID: event.assistantMessageID,
toolCallID: event.toolCallID,
input: event.input,
result: event.result,
output: event.output,
outputPaths: event.outputPaths,
}
const result = callback(output)
return Effect.suspend(() => (Effect.isEffect(result) ? result : Effect.void)).pipe(
Effect.tap(() =>
Effect.sync(() => {
event.result = output.result
event.output = output.output
event.outputPaths = output.outputPaths
}),
),
)
}),
})
}
return toolHooks.hook.after((event) => {
const output = {
tool: event.tool,
sessionID: event.sessionID,
agent: event.agent,
assistantMessageID: event.assistantMessageID,
toolCallID: event.toolCallID,
input: event.input,
result: event.result,
output: event.output,
outputPaths: event.outputPaths,
}
return Reflect.apply(callback, undefined, [output]).pipe(
Effect.tap(() =>
Effect.sync(() => {
event.result = output.result
event.output = output.output
event.outputPaths = output.outputPaths
}),
),
)
})
},
},
session: {
hook: (name, callback) => hooks.register("session", name, callback),
create: (input) =>
runtime.session.create({
id: input?.id,
@@ -375,5 +381,5 @@ export const make = Effect.fn("PluginHost.make")(function* (plugin: PluginV2.Int
command: runtime.session.command,
interrupt: (input) => runtime.session.interrupt(input.sessionID),
},
} satisfies PluginContext
} satisfies Plugin.Context
})
+3 -3
View File
@@ -1,6 +1,6 @@
export * as PluginInternal from "./internal"
import type { Plugin } from "@opencode-ai/plugin/v2/effect"
import type { Plugin } from "@opencode-ai/plugin/v2/effect/plugin"
import { Context, Effect, Scope } from "effect"
import { HttpClient } from "effect/unstable/http"
import { AgentV2 } from "../agent"
@@ -31,7 +31,7 @@ import { SessionInstructions } from "../session/instructions"
import { SessionTodo } from "../session/todo"
import { Shell } from "../shell"
import { SkillV2 } from "../skill"
import { ApplyPatchTool } from "../tool/apply-patch"
import { PatchTool } from "../tool/patch"
import { EditTool } from "../tool/edit"
import { GlobTool } from "../tool/glob"
import { GrepTool } from "../tool/grep"
@@ -127,7 +127,7 @@ const pre = [
SkillPlugin.Plugin,
ModelsDevPlugin,
...ProviderPlugins,
ApplyPatchTool.Plugin,
PatchTool.Plugin,
EditTool.Plugin,
GlobTool.Plugin,
GrepTool.Plugin,
+24 -14
View File
@@ -1,11 +1,12 @@
export * as PluginPromise from "./promise"
import { define } from "@opencode-ai/plugin/v2/effect"
import type { Plugin, PluginContext } from "@opencode-ai/plugin/v2/promise"
import { Plugin } from "@opencode-ai/plugin/v2/effect"
import { Effect, Scope, Stream } from "effect"
type HostRegistration = { readonly dispose: Effect.Effect<void> }
type Registration = { readonly dispose: () => Promise<void> }
type PromisePlugin = import("@opencode-ai/plugin/v2/plugin").Plugin
type PromisePluginContext = import("@opencode-ai/plugin/v2/plugin").Context
/**
* Adapts a Promise plugin into an Effect plugin so the existing Effect-only
@@ -16,8 +17,8 @@ type Registration = { readonly dispose: () => Promise<void> }
* preserves boot-time batching, so Promise-plugin transforms still coalesce
* into one reload per domain.
*/
export function fromPromise(plugin: Plugin) {
return define({
export function fromPromise(plugin: PromisePlugin) {
return Plugin.define({
id: plugin.id,
effect: (host) =>
Effect.gen(function* () {
@@ -43,7 +44,7 @@ export function fromPromise(plugin: Plugin) {
}),
)
const context2: PluginContext = {
const context2: PromisePluginContext = {
options: host.options,
agent: {
list: (input) => run(host.agent.list(input)),
@@ -51,10 +52,8 @@ export function fromPromise(plugin: Plugin) {
reload: () => run(host.agent.reload()),
},
aisdk: {
sdk: (callback) =>
register(host.aisdk.sdk((event) => Effect.promise(() => Promise.resolve(callback(event))))),
language: (callback) =>
register(host.aisdk.language((event) => Effect.promise(() => Promise.resolve(callback(event))))),
hook: (name, callback) =>
register(host.aisdk.hook(name, (event) => Effect.promise(() => Promise.resolve(callback(event))))),
},
catalog: {
provider: {
@@ -79,11 +78,15 @@ export function fromPromise(plugin: Plugin) {
integration: {
list: (input) => run(host.integration.list(input)),
get: (input) => run(host.integration.get(input)),
connectKey: (input) => run(host.integration.connectKey(input)),
connectOauth: (input) => run(host.integration.connectOauth(input)),
attemptStatus: (input) => run(host.integration.attemptStatus(input)),
attemptComplete: (input) => run(host.integration.attemptComplete(input)),
attemptCancel: (input) => run(host.integration.attemptCancel(input)),
connect: {
key: (input) => run(host.integration.connect.key(input)),
oauth: (input) => run(host.integration.connect.oauth(input)),
},
attempt: {
status: (input) => run(host.integration.attempt.status(input)),
complete: (input) => run(host.integration.attempt.complete(input)),
cancel: (input) => run(host.integration.attempt.cancel(input)),
},
transform: transform(host.integration),
reload: () => run(host.integration.reload()),
connection: {
@@ -104,12 +107,19 @@ export function fromPromise(plugin: Plugin) {
transform: transform(host.skill),
reload: () => run(host.skill.reload()),
},
tool: {
transform: transform(host.tool),
hook: (name, callback) =>
register(host.tool.hook(name, (event) => Effect.promise(() => Promise.resolve(callback(event))))),
},
session: {
create: (input) => run(host.session.create(input)),
get: (input) => run(host.session.get(input)),
prompt: (input) => run(host.session.prompt(input)),
command: (input) => run(host.session.command(input)),
interrupt: (input) => run(host.session.interrupt(input)),
hook: (name, callback) =>
register(host.session.hook(name, (event) => Effect.promise(() => Promise.resolve(callback(event))))),
},
}
+2 -1
View File
@@ -4,7 +4,8 @@ import { define } from "@opencode-ai/plugin/v2/effect/plugin"
export const AlibabaPlugin = define({
id: "opencode.provider.alibaba",
effect: Effect.fn(function* (ctx) {
yield* ctx.aisdk.sdk(
yield* ctx.aisdk.hook(
"sdk",
Effect.fn(function* (evt) {
if (evt.package !== "@ai-sdk/alibaba") return
const mod = yield* Effect.promise(() => import("@ai-sdk/alibaba"))
@@ -75,7 +75,8 @@ export const AmazonBedrockPlugin = define({
})
}
})
yield* ctx.aisdk.sdk(
yield* ctx.aisdk.hook(
"sdk",
Effect.fn(function* (evt) {
if (!["@ai-sdk/amazon-bedrock", "@ai-sdk/amazon-bedrock/mantle"].includes(evt.package)) return
const options = { ...evt.options }
@@ -108,7 +109,8 @@ export const AmazonBedrockPlugin = define({
evt.sdk = mod.createAmazonBedrock(options)
}),
)
yield* ctx.aisdk.language(
yield* ctx.aisdk.hook(
"language",
Effect.fn(function* (evt) {
if (evt.model.providerID !== ProviderV2.ID.amazonBedrock) return
if (
@@ -17,7 +17,8 @@ export const AnthropicPlugin = define({
})
}
})
yield* ctx.aisdk.sdk(
yield* ctx.aisdk.hook(
"sdk",
Effect.fn(function* (evt) {
if (evt.package !== "@ai-sdk/anthropic") return
const mod = yield* Effect.promise(() => import("@ai-sdk/anthropic"))
+6 -3
View File
@@ -26,7 +26,8 @@ export const AzurePlugin = define({
})
}
})
yield* ctx.aisdk.sdk(
yield* ctx.aisdk.hook(
"sdk",
Effect.fn(function* (evt) {
if (evt.package !== "@ai-sdk/azure") return
if (evt.model.providerID === ProviderV2.ID.azure) {
@@ -44,7 +45,8 @@ export const AzurePlugin = define({
evt.sdk = mod.createAzure(evt.options)
}),
)
yield* ctx.aisdk.language(
yield* ctx.aisdk.hook(
"language",
Effect.fn(function* (evt) {
if (evt.model.providerID !== ProviderV2.ID.azure) return
evt.language = selectLanguage(
@@ -75,7 +77,8 @@ export const AzureCognitiveServicesPlugin = define({
})
}
})
yield* ctx.aisdk.language(
yield* ctx.aisdk.hook(
"language",
Effect.fn(function* (evt) {
if (evt.model.providerID !== ProviderV2.ID.make("azure-cognitive-services")) return
evt.language = selectLanguage(
@@ -14,7 +14,8 @@ export const CerebrasPlugin = define({
})
}
})
yield* ctx.aisdk.sdk(
yield* ctx.aisdk.hook(
"sdk",
Effect.fn(function* (evt) {
if (evt.package !== "@ai-sdk/cerebras") return
const mod = yield* Effect.promise(() => import("@ai-sdk/cerebras"))
@@ -6,7 +6,8 @@ import { define } from "@opencode-ai/plugin/v2/effect/plugin"
export const CloudflareAIGatewayPlugin = define({
id: "opencode.provider.cloudflare-ai-gateway",
effect: Effect.fn(function* (ctx) {
yield* ctx.aisdk.sdk(
yield* ctx.aisdk.hook(
"sdk",
Effect.fn(function* (evt) {
if (evt.package !== "ai-gateway-provider") return
if (evt.options.baseURL) return
@@ -19,7 +19,8 @@ export const CloudflareWorkersAIPlugin = define({
if (accountId) provider.settings = { ...provider.settings, baseURL: workersEndpoint(accountId) }
})
})
yield* ctx.aisdk.sdk(
yield* ctx.aisdk.hook(
"sdk",
Effect.fn(function* (evt) {
if (evt.model.providerID !== providerID) return
if (evt.package !== "@ai-sdk/openai-compatible") return
@@ -35,7 +36,8 @@ export const CloudflareWorkersAIPlugin = define({
)
}),
)
yield* ctx.aisdk.language(
yield* ctx.aisdk.hook(
"language",
Effect.fn(function* (evt) {
if (evt.model.providerID !== providerID) return
evt.language = evt.sdk.languageModel(evt.model.modelID ?? evt.model.id)
+2 -1
View File
@@ -4,7 +4,8 @@ import { define } from "@opencode-ai/plugin/v2/effect/plugin"
export const CoherePlugin = define({
id: "opencode.provider.cohere",
effect: Effect.fn(function* (ctx) {
yield* ctx.aisdk.sdk(
yield* ctx.aisdk.hook(
"sdk",
Effect.fn(function* (evt) {
if (evt.package !== "@ai-sdk/cohere") return
const mod = yield* Effect.promise(() => import("@ai-sdk/cohere"))
@@ -4,7 +4,8 @@ import { define } from "@opencode-ai/plugin/v2/effect/plugin"
export const DeepInfraPlugin = define({
id: "opencode.provider.deepinfra",
effect: Effect.fn(function* (ctx) {
yield* ctx.aisdk.sdk(
yield* ctx.aisdk.hook(
"sdk",
Effect.fn(function* (evt) {
if (evt.package !== "@ai-sdk/deepinfra") return
const mod = yield* Effect.promise(() => import("@ai-sdk/deepinfra"))
+2 -1
View File
@@ -7,7 +7,8 @@ export const DynamicProviderPlugin = define({
id: "opencode.provider.dynamic",
effect: Effect.fn(function* (ctx) {
const npm = yield* Npm.Service
yield* ctx.aisdk.sdk(
yield* ctx.aisdk.hook(
"sdk",
Effect.fn(function* (evt) {
if (evt.sdk) return
+2 -1
View File
@@ -4,7 +4,8 @@ import { define } from "@opencode-ai/plugin/v2/effect/plugin"
export const GatewayPlugin = define({
id: "opencode.provider.gateway",
effect: Effect.fn(function* (ctx) {
yield* ctx.aisdk.sdk(
yield* ctx.aisdk.hook(
"sdk",
Effect.fn(function* (evt) {
if (evt.package !== "@ai-sdk/gateway") return
const mod = yield* Effect.promise(() => import("@ai-sdk/gateway"))
@@ -23,14 +23,16 @@ export const GithubCopilotPlugin = define({
model.enabled = false
})
})
yield* ctx.aisdk.sdk(
yield* ctx.aisdk.hook(
"sdk",
Effect.fn(function* (evt) {
if (evt.package !== "@ai-sdk/github-copilot") return
const mod = yield* Effect.promise(() => import("../../github-copilot/copilot-provider"))
evt.sdk = mod.createOpenaiCompatible(evt.options)
}),
)
yield* ctx.aisdk.language(
yield* ctx.aisdk.hook(
"language",
Effect.fn(function* (evt) {
if (evt.model.providerID !== ProviderV2.ID.githubCopilot) return
if (evt.sdk.responses === undefined && evt.sdk.chat === undefined) {
+4 -2
View File
@@ -7,7 +7,8 @@ import { ProviderV2 } from "../../provider"
export const GitLabPlugin = define({
id: "opencode.provider.gitlab",
effect: Effect.fn(function* (ctx) {
yield* ctx.aisdk.sdk(
yield* ctx.aisdk.hook(
"sdk",
Effect.fn(function* (evt) {
if (evt.package !== "gitlab-ai-provider") return
const mod = yield* Effect.promise(() => import("gitlab-ai-provider"))
@@ -31,7 +32,8 @@ export const GitLabPlugin = define({
})
}),
)
yield* ctx.aisdk.language(
yield* ctx.aisdk.hook(
"language",
Effect.fn(function* (evt) {
if (evt.model.providerID !== ProviderV2.ID.gitlab) return
const featureFlags =
@@ -85,7 +85,8 @@ export const GoogleVertexPlugin = define({
})
}
})
yield* ctx.aisdk.sdk(
yield* ctx.aisdk.hook(
"sdk",
Effect.fn(function* (evt) {
if (evt.model.providerID === ProviderV2.ID.googleVertex && evt.package.includes("@ai-sdk/openai-compatible")) {
evt.options.fetch = authFetch(evt.options.fetch)
@@ -104,7 +105,8 @@ export const GoogleVertexPlugin = define({
})
}),
)
yield* ctx.aisdk.language(
yield* ctx.aisdk.hook(
"language",
Effect.fn(function* (evt) {
if (evt.model.providerID !== ProviderV2.ID.googleVertex) return
evt.language = evt.sdk.languageModel(String(evt.model.modelID ?? evt.model.id).trim())
@@ -135,7 +137,8 @@ export const GoogleVertexAnthropicPlugin = define({
})
}
})
yield* ctx.aisdk.sdk(
yield* ctx.aisdk.hook(
"sdk",
Effect.fn(function* (evt) {
if (evt.package !== "@ai-sdk/google-vertex/anthropic") return
const mod = yield* Effect.promise(() => import("@ai-sdk/google-vertex/anthropic"))
@@ -161,7 +164,8 @@ export const GoogleVertexAnthropicPlugin = define({
})
}),
)
yield* ctx.aisdk.language(
yield* ctx.aisdk.hook(
"language",
Effect.fn(function* (evt) {
if (evt.model.providerID !== ProviderV2.ID.make("google-vertex-anthropic")) return
evt.language = evt.sdk.languageModel(String(evt.model.modelID ?? evt.model.id).trim())
+2 -1
View File
@@ -4,7 +4,8 @@ import { define } from "@opencode-ai/plugin/v2/effect/plugin"
export const GooglePlugin = define({
id: "opencode.provider.google",
effect: Effect.fn(function* (ctx) {
yield* ctx.aisdk.sdk(
yield* ctx.aisdk.hook(
"sdk",
Effect.fn(function* (evt) {
if (evt.package !== "@ai-sdk/google") return
const mod = yield* Effect.promise(() => import("@ai-sdk/google"))
+2 -1
View File
@@ -4,7 +4,8 @@ import { define } from "@opencode-ai/plugin/v2/effect/plugin"
export const GroqPlugin = define({
id: "opencode.provider.groq",
effect: Effect.fn(function* (ctx) {
yield* ctx.aisdk.sdk(
yield* ctx.aisdk.hook(
"sdk",
Effect.fn(function* (evt) {
if (evt.package !== "@ai-sdk/groq") return
const mod = yield* Effect.promise(() => import("@ai-sdk/groq"))
+2 -1
View File
@@ -4,7 +4,8 @@ import { define } from "@opencode-ai/plugin/v2/effect/plugin"
export const MistralPlugin = define({
id: "opencode.provider.mistral",
effect: Effect.fn(function* (ctx) {
yield* ctx.aisdk.sdk(
yield* ctx.aisdk.hook(
"sdk",
Effect.fn(function* (evt) {
if (evt.package !== "@ai-sdk/mistral") return
const mod = yield* Effect.promise(() => import("@ai-sdk/mistral"))
@@ -4,7 +4,8 @@ import { define } from "@opencode-ai/plugin/v2/effect/plugin"
export const OpenAICompatiblePlugin = define({
id: "opencode.provider.openai-compatible",
effect: Effect.fn(function* (ctx) {
yield* ctx.aisdk.sdk(
yield* ctx.aisdk.hook(
"sdk",
Effect.fn(function* (evt) {
if (evt.sdk) return
if (!evt.package.includes("@ai-sdk/openai-compatible")) return
+5 -3
View File
@@ -209,15 +209,17 @@ export const OpenAIPlugin = define({
Stream.runForEach(refresh),
Effect.forkScoped({ startImmediately: true }),
)
yield* refresh().pipe(Effect.forkScoped)
yield* ctx.aisdk.sdk(
yield* refresh()
yield* ctx.aisdk.hook(
"sdk",
Effect.fn(function* (evt) {
if (evt.package !== "@ai-sdk/openai") return
const mod = yield* Effect.promise(() => import("@ai-sdk/openai"))
evt.sdk = mod.createOpenAI(evt.options)
}),
)
yield* ctx.aisdk.language(
yield* ctx.aisdk.hook(
"language",
Effect.fn(function* (evt) {
if (evt.model.providerID !== ProviderV2.ID.openai) return
evt.language = evt.sdk.responses(evt.model.modelID ?? evt.model.id)
@@ -85,12 +85,15 @@ export const OpencodePlugin = define<HttpClient.HttpClient | EventV2.Service | S
const load = Effect.fn("OpencodePlugin.load")(function* () {
const connection = yield* ctx.integration.connection.active("opencode")
const credential = connection
? yield* ctx.integration.connection.resolve(connection).pipe(Effect.catch(() => Effect.succeed(undefined)))
: undefined
connected = connection !== undefined
providers = credential
? yield* fetchProviders(http, credential).pipe(
providers = connection
? yield* Effect.gen(function* () {
const credential = yield* ctx.integration.connection
.resolve(connection)
.pipe(Effect.catch(() => Effect.succeed(undefined)))
return credential ? yield* fetchProviders(http, credential) : undefined
}).pipe(
Effect.timeout("10 seconds"),
Effect.catch((cause) =>
Effect.logWarning("failed to load OpenCode provider config", { cause }).pipe(Effect.as(undefined)),
),
@@ -186,7 +189,7 @@ export const OpencodePlugin = define<HttpClient.HttpClient | EventV2.Service | S
Stream.runForEach(refresh),
Effect.forkScoped({ startImmediately: true }),
)
yield* refresh().pipe(Effect.forkScoped)
yield* refresh()
}),
})
@@ -23,7 +23,8 @@ export const OpenRouterPlugin = define({
}
}
})
yield* ctx.aisdk.sdk(
yield* ctx.aisdk.hook(
"sdk",
Effect.fn(function* (evt) {
if (evt.package !== "@openrouter/ai-sdk-provider") return
const mod = yield* Effect.promise(() => import("@openrouter/ai-sdk-provider"))
@@ -4,7 +4,8 @@ import { define } from "@opencode-ai/plugin/v2/effect/plugin"
export const PerplexityPlugin = define({
id: "opencode.provider.perplexity",
effect: Effect.fn(function* (ctx) {
yield* ctx.aisdk.sdk(
yield* ctx.aisdk.hook(
"sdk",
Effect.fn(function* (evt) {
if (evt.package !== "@ai-sdk/perplexity") return
const mod = yield* Effect.promise(() => import("@ai-sdk/perplexity"))
@@ -8,7 +8,8 @@ export const SapAICorePlugin = define({
id: "opencode.provider.sap-ai-core",
effect: Effect.fn(function* (ctx) {
const npm = yield* Npm.Service
yield* ctx.aisdk.sdk(
yield* ctx.aisdk.hook(
"sdk",
Effect.fn(function* (evt) {
if (evt.model.providerID !== ProviderV2.ID.make("sap-ai-core")) return
const serviceKey =
@@ -37,7 +38,8 @@ export const SapAICorePlugin = define({
)
}),
)
yield* ctx.aisdk.language(
yield* ctx.aisdk.hook(
"language",
Effect.fn(function* (evt) {
if (evt.model.providerID !== ProviderV2.ID.make("sap-ai-core")) return
evt.language = evt.sdk(evt.model.modelID ?? evt.model.id)
@@ -67,7 +67,8 @@ export function cortexFetch(upstream: FetchLike = fetch) {
export const SnowflakeCortexPlugin = define({
id: "opencode.provider.snowflake-cortex",
effect: Effect.fn(function* (ctx) {
yield* ctx.aisdk.sdk(
yield* ctx.aisdk.hook(
"sdk",
Effect.fn(function* (evt) {
if (evt.model.providerID !== ProviderV2.ID.make("snowflake-cortex")) return
const token =
@@ -4,7 +4,8 @@ import { define } from "@opencode-ai/plugin/v2/effect/plugin"
export const TogetherAIPlugin = define({
id: "opencode.provider.togetherai",
effect: Effect.fn(function* (ctx) {
yield* ctx.aisdk.sdk(
yield* ctx.aisdk.hook(
"sdk",
Effect.fn(function* (evt) {
if (evt.package !== "@ai-sdk/togetherai") return
const mod = yield* Effect.promise(() => import("@ai-sdk/togetherai"))
+2 -1
View File
@@ -4,7 +4,8 @@ import { define } from "@opencode-ai/plugin/v2/effect/plugin"
export const VenicePlugin = define({
id: "opencode.provider.venice",
effect: Effect.fn(function* (ctx) {
yield* ctx.aisdk.sdk(
yield* ctx.aisdk.hook(
"sdk",
Effect.fn(function* (evt) {
if (evt.package !== "venice-ai-sdk-provider") return
const mod = yield* Effect.promise(() => import("venice-ai-sdk-provider"))
+2 -1
View File
@@ -14,7 +14,8 @@ export const VercelPlugin = define({
})
}
})
yield* ctx.aisdk.sdk(
yield* ctx.aisdk.hook(
"sdk",
Effect.fn(function* (evt) {
if (evt.package !== "@ai-sdk/vercel") return
const mod = yield* Effect.promise(() => import("@ai-sdk/vercel"))
+4 -2
View File
@@ -5,14 +5,16 @@ import { ProviderV2 } from "../../provider"
export const XAIPlugin = define({
id: "opencode.provider.xai",
effect: Effect.fn(function* (ctx) {
yield* ctx.aisdk.sdk(
yield* ctx.aisdk.hook(
"sdk",
Effect.fn(function* (evt) {
if (evt.package !== "@ai-sdk/xai") return
const mod = yield* Effect.promise(() => import("@ai-sdk/xai"))
evt.sdk = mod.createXai(evt.options)
}),
)
yield* ctx.aisdk.language(
yield* ctx.aisdk.hook(
"language",
Effect.fn(function* (evt) {
if (evt.model.providerID !== ProviderV2.ID.make("xai")) return
evt.language = evt.sdk.responses(evt.model.modelID ?? evt.model.id)
+17 -33
View File
@@ -1,20 +1,12 @@
export * as SdkPlugins from "./sdk"
import type { Plugin } from "@opencode-ai/plugin/v2/effect"
import type { Plugin } from "@opencode-ai/plugin/v2/effect/plugin"
import { Context, Effect, Layer } from "effect"
import { makeGlobalNode } from "../effect/app-node"
import { EventV2 } from "../event"
export const Updated = EventV2.ephemeral({ type: "sdk.plugin.updated", schema: {} })
export interface Store {
readonly plugins: Map<string, Plugin>
}
export const makeStore = (): Store => ({ plugins: new Map() })
const defaultStore = makeStore()
/**
* Holds the plugins an embedder (the `@opencode-ai/sdk-next` host) contributes,
* so `PluginSupervisor` can add them on every Location boot through the ordinary
@@ -22,10 +14,9 @@ const defaultStore = makeStore()
* config. Registration publishes an unlocated update so every booted Location
* reloads its plugin generation from the shared store.
*
* The store is shared explicitly between the SDK construction graph and the
* embedded route graph because `LocationServiceMap` builds Location layers lazily
* in a nested graph. Each embedded SDK creates its own store, so instances do not
* see each other's contributions.
* Each host-global layer owns one private store. Location graphs reuse that
* layer through Effect's memoization, so separate hosts remain isolated while
* every Location in one host sees the same registrations.
*/
export interface Interface {
readonly register: (plugin: Plugin) => Effect.Effect<void>
@@ -34,26 +25,19 @@ export interface Interface {
export class Service extends Context.Service<Service, Interface>()("@opencode/SdkPlugins") {}
export const layerWithStore = (store: Store) =>
Layer.effect(
Service,
Effect.gen(function* () {
const events = yield* EventV2.Service
yield* Effect.addFinalizer(() =>
export const layer = Layer.effect(
Service,
Effect.gen(function* () {
const events = yield* EventV2.Service
const plugins = new Map<string, Plugin>()
return Service.of({
register: (plugin) =>
Effect.sync(() => {
store.plugins.clear()
}),
)
return Service.of({
register: (plugin) =>
Effect.sync(() => {
store.plugins.set(plugin.id, plugin)
}).pipe(Effect.andThen(events.publish(Updated, {})), Effect.asVoid),
all: () => [...store.plugins.values()],
})
}),
)
export const layer = layerWithStore(defaultStore)
plugins.set(plugin.id, plugin)
}).pipe(Effect.andThen(events.publish(Updated, {})), Effect.asVoid),
all: () => [...plugins.values()],
})
}),
)
export const node = makeGlobalNode({ service: Service, layer, deps: [EventV2.node] })
@@ -0,0 +1,75 @@
export * as PluginSupervisorNode from "./supervisor-node"
import { AgentV2 } from "../agent"
import { Catalog } from "../catalog"
import { CommandV2 } from "../command"
import { Config } from "../config"
import { httpClient } from "../effect/app-node-platform"
import { makeLocationNode } from "../effect/app-node"
import { EventV2 } from "../event"
import { FileMutation } from "../file-mutation"
import { FileSystem } from "../filesystem"
import { FSUtil } from "../fs-util"
import { Form } from "../form"
import { Global } from "../global"
import { Image } from "../image"
import { Integration } from "../integration"
import { Location } from "../location"
import { LocationMutation } from "../location-mutation"
import { ModelsDev } from "../models-dev"
import { Npm } from "../npm"
import { PermissionV2 } from "../permission"
import { PluginV2 } from "../plugin"
import { Reference } from "../reference"
import { Ripgrep } from "../ripgrep"
import { SessionInstructions } from "../session/instructions"
import { SessionTodo } from "../session/todo"
import { Shell } from "../shell"
import { SkillV2 } from "../skill"
import { ReadToolFileSystem } from "../tool/read-filesystem"
import { ToolRegistry } from "../tool/registry"
import { WebSearchTool } from "../tool/websearch"
import { Layer } from "effect"
import { PluginRuntime } from "./runtime"
import type { PluginInternal } from "./internal"
import { SdkPlugins } from "./sdk"
import { PluginSupervisor } from "./supervisor"
const layer = PluginSupervisor.layer as Layer.Layer<PluginSupervisor.Service, never, PluginInternal.Requirements>
export const node = makeLocationNode({
service: PluginSupervisor.Service,
layer,
deps: [
PluginV2.node,
SdkPlugins.node,
AgentV2.node,
Catalog.node,
CommandV2.node,
Config.node,
EventV2.node,
FileMutation.node,
FileSystem.node,
FSUtil.node,
Global.node,
httpClient,
Image.node,
Integration.node,
Location.node,
LocationMutation.node,
ModelsDev.node,
Npm.node,
PermissionV2.node,
PluginRuntime.node,
Form.node,
ReadToolFileSystem.node,
Reference.node,
Ripgrep.node,
SessionInstructions.node,
SessionTodo.node,
Shell.node,
SkillV2.node,
ToolRegistry.toolsNode,
WebSearchTool.configNode,
],
})
+46 -24
View File
@@ -1,8 +1,8 @@
export * as PluginSupervisor from "./supervisor"
import type { Plugin } from "@opencode-ai/plugin/v2/effect"
import type { Plugin } from "@opencode-ai/plugin/v2/effect/plugin"
import { Event } from "@opencode-ai/schema/config"
import { Context, Effect, Fiber, Layer, Option, Schema, Semaphore, Stream } from "effect"
import { Context, Effect, Fiber, FiberSet, Layer, Option, Schema, Semaphore, Stream } from "effect"
import path from "path"
import { fileURLToPath, pathToFileURL } from "url"
import { Config } from "../config"
@@ -246,6 +246,9 @@ const resolvePackageEntrypoint = Effect.fnUntraced(function* (fs: FSUtil.Interfa
})
export interface Interface {
/** Apply at least every Config and SDK plugin update observed when this Effect begins. */
readonly synchronize: Effect.Effect<void>
/** @deprecated Use synchronize. */
readonly ready: Effect.Effect<void>
}
@@ -258,35 +261,54 @@ const layer = Layer.effect(
const sdk = yield* SdkPlugins.Service
const config = yield* Config.Service
const events = yield* EventV2.Service
const location = yield* Location.Service
const runActivation = yield* FiberSet.makeRuntime<never, void>()
const lock = Semaphore.makeUnsafe(1)
const reload = Effect.fn("PluginSupervisor.reload")(() =>
lock.withPermit(
Effect.gen(function* () {
// Resolve OpenCode's internal plugins with their privileged Location services.
const internal = yield* PluginInternal.list()
// Combine internal plugins with host-contributed SDK plugins in boot order.
const pre = [...internal.pre, ...sdk.all()]
// Read the current layered config before resolving plugin directives and packages.
const entries = yield* config.entries()
const operations = yield* scan(entries)
// Apply config operations and load enabled package plugins into one ordered generation.
const plugins = yield* resolve(pre, internal.post, operations)
// Replace the active generation in one scoped, batched activation.
yield* registry.activate(plugins)
}),
),
let requested = 1
let applied = 0
const unsubscribe = yield* events.listen((event) =>
Effect.sync(() => {
if (event.type !== Event.Updated.type && event.type !== SdkPlugins.Updated.type) return
if (
event.location &&
(event.location.directory !== location.directory || event.location.workspaceID !== location.workspaceID)
)
return
requested++
}),
)
yield* Effect.addFinalizer(() => unsubscribe)
const activateThrough = Effect.fn("PluginSupervisor.activateThrough")(function* (target: number) {
if (applied >= target) return
// Resolve OpenCode's internal plugins with their privileged Location services.
const internal = yield* PluginInternal.list()
// Combine internal plugins with host-contributed SDK plugins in boot order.
const pre = [...internal.pre, ...sdk.all()]
const operations = yield* scan(yield* config.entries())
// Apply config operations and load enabled package plugins into one ordered generation.
const plugins = yield* resolve(pre, internal.post, operations)
// Replace the active generation in one scoped, batched activation.
yield* registry.activate(plugins, { force: applied > 0 })
applied = target
})
const activateTarget = Effect.fn("PluginSupervisor.activateTarget")((target: number) =>
lock.withPermit(activateThrough(target)),
)
const reload = Effect.suspend(() => activateTarget(requested))
yield* events.subscribe([Event.Updated, SdkPlugins.Updated]).pipe(
Stream.runForEach(() =>
reload().pipe(Effect.catchCause((cause) => Effect.logError("failed to reload plugins", { cause }))),
reload.pipe(Effect.catchCause((cause) => Effect.logError("failed to reload plugins", { cause }))),
),
Effect.forkScoped({ startImmediately: true }),
)
const fiber = yield* reload().pipe(
Effect.withSpan("PluginSupervisor.boot"),
Effect.forkScoped({ startImmediately: true }),
)
return Service.of({ ready: Fiber.join(fiber) })
yield* reload.pipe(Effect.withSpan("PluginSupervisor.boot"), Effect.forkScoped({ startImmediately: true }))
const context = yield* Effect.context<Effect.Services<ReturnType<typeof activateTarget>>>()
const synchronize = Effect.suspend(() => {
const target = requested
const activation = activateTarget(target).pipe(Effect.provideContext(context))
return Fiber.join(runActivation(activation))
})
return Service.of({ synchronize, ready: synchronize })
}),
)
+2
View File
@@ -243,6 +243,7 @@ export interface Interface {
text: string
description?: string
metadata?: Record<string, unknown>
resume?: boolean
}) => Effect.Effect<void, NotFoundError>
readonly revert: {
readonly stage: (input: {
@@ -682,6 +683,7 @@ const layer = Layer.effect(
description: input.description,
metadata: input.metadata,
})
if (input.resume === false) return
yield* execution
.resume(input.sessionID)
.pipe(Effect.ignore, Effect.forkIn(scope, { startImmediately: true }), Effect.asVoid)
+11 -4
View File
@@ -25,20 +25,27 @@ const SUMMARY_TEMPLATE = `Output exactly the Markdown structure shown inside <te
- [constraints/preferences, decisions and why, important facts/assumptions, exact context needed to continue, or "(none)"]
## Work State
- Completed: [finished work, verified facts, or changes made; otherwise "(none)"]
- Active: [current work, partial changes, or investigation state; otherwise "(none)"]
- Blocked: [blockers, failing commands, or unknowns; otherwise "(none)"]
### Completed
- [finished work, verified facts, or changes made; otherwise "(none)"]
### Active
- [current work, partial changes, or investigation state; otherwise "(none)"]
### Blocked
- [blockers, failing commands, or unknowns; otherwise "(none)"]
## Next Move
1. [immediate concrete action, or "(none)"]
2. [next action if known, or "(none)"]
## Relevant Files
- [file or directory path: why it matters, or "(none)"]
</template>
Rules:
- Keep every section, even when empty.
- Use terse bullets, not prose paragraphs.
- Preserve exact file paths, symbols, commands, error strings, URLs, and identifiers when known.
- Put relevant files and symbols inside the section where they matter; do not add extra sections.
- Do not mention the summary process or that context was compacted.`
type Settings = {
+9
View File
@@ -12,6 +12,15 @@ export class MessageDecodeError extends Schema.TaggedErrorClass<MessageDecodeErr
}
}
export class AgentNotFoundError extends Schema.TaggedErrorClass<AgentNotFoundError>()("Session.AgentNotFoundError", {
sessionID: SessionSchema.ID,
agent: Schema.String,
}) {
override get message() {
return `Agent not found: "${this.agent}"`
}
}
export class StepFailedError extends Schema.TaggedErrorClass<StepFailedError>()("Session.StepFailedError", {
error: SessionError.Error,
}) {
@@ -143,6 +143,7 @@ export function update(adapter: Adapter, event: SessionEvent.Event) {
return Effect.gen(function* () {
yield* SessionEvent.All.match(event, {
"session.usage.updated": () => Effect.void,
"session.agent.selected": (event) => {
return adapter.appendMessage(
SessionMessage.AgentSelected.make({
@@ -296,6 +297,10 @@ export function update(adapter: Adapter, event: SessionEvent.Event) {
draft.finish = "error"
draft.error = castDraft(event.data.error)
draft.retry = undefined
if (event.data.cost !== undefined && event.data.tokens !== undefined) {
draft.cost = event.data.cost
draft.tokens = castDraft(event.data.tokens)
}
})
},
"session.text.started": (event) => {
+60 -47
View File
@@ -1,7 +1,7 @@
export * as SessionProjector from "./projector"
import { and, asc, desc, eq, gt, gte, inArray, lt, or, sql } from "drizzle-orm"
import { DateTime, Effect, Layer, Schema } from "effect"
import { DateTime, Effect, Layer, Schema, Stream } from "effect"
import { Database } from "../database/database"
import { EventV2 } from "../event"
import { makeGlobalNode } from "../effect/app-node"
@@ -48,11 +48,6 @@ type Usage = {
const ForkBatchSize = 500
const emptyUsage = (): Usage => ({
cost: 0,
tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
})
const forkTitle = (value: string) => {
const match = value.match(/^(.+) \(fork #(\d+)\)$/)
if (match) return `${match[1]} (fork #${Number.parseInt(match[2], 10) + 1})`
@@ -67,22 +62,6 @@ function usage(part: (typeof SessionV1.Event.PartUpdated.Type)["data"]["part"] |
return { cost: value.cost as Usage["cost"], tokens: value.tokens as Usage["tokens"] }
}
function addUsage(target: Usage, value: Usage) {
target.cost += value.cost
target.tokens.input += value.tokens.input
target.tokens.output += value.tokens.output
target.tokens.reasoning += value.tokens.reasoning
target.tokens.cache.read += value.tokens.cache.read
target.tokens.cache.write += value.tokens.cache.write
}
function messageUsage(row: typeof SessionMessageTable.$inferSelect): Usage | undefined {
if (row.type !== "assistant") return undefined
const message = decodeMessage({ ...row.data, id: row.id, type: row.type })
if (message.type !== "assistant" || message.cost === undefined || message.tokens === undefined) return undefined
return { cost: message.cost, tokens: message.tokens }
}
function sessionRow(info: SessionV1.SessionInfo): typeof SessionTable.$inferInsert {
return {
id: info.id,
@@ -151,6 +130,37 @@ function applyUsage(
.pipe(Effect.orDie)
}
const publishSessionUsage = Effect.fn("SessionProjector.publishUsage")(function* (
db: DatabaseService,
events: EventV2.Interface,
sessionID: (typeof SessionEvent.Step.Ended.Type)["data"]["sessionID"],
) {
const row = yield* db
.select({
cost: SessionTable.cost,
input: SessionTable.tokens_input,
output: SessionTable.tokens_output,
reasoning: SessionTable.tokens_reasoning,
cacheRead: SessionTable.tokens_cache_read,
cacheWrite: SessionTable.tokens_cache_write,
})
.from(SessionTable)
.where(eq(SessionTable.id, sessionID))
.get()
.pipe(Effect.orDie)
if (!row) return
yield* events.publish(SessionEvent.UsageUpdated, {
sessionID,
cost: row.cost,
tokens: {
input: row.input,
output: row.output,
reasoning: row.reasoning,
cache: { read: row.cacheRead, write: row.cacheWrite },
},
})
})
const projectFork = Effect.fn("SessionProjector.projectFork")(function* (
db: DatabaseService,
event: typeof SessionEvent.Forked.Type,
@@ -187,7 +197,7 @@ const projectFork = Effect.fn("SessionProjector.projectFork")(function* (
.limit(1)
.get()
.pipe(Effect.orDie)
const copiedSeq = copied?.seq ?? 0
const copiedSeq = copied?.seq
const stored = yield* db
.insert(SessionTable)
@@ -237,9 +247,8 @@ const projectFork = Effect.fn("SessionProjector.projectFork")(function* (
.pipe(Effect.orDie)
}
const usage = emptyUsage()
let cursor = -1
while (true) {
while (copiedSeq !== undefined) {
const rows = yield* db
.select()
.from(SessionMessageTable)
@@ -247,7 +256,7 @@ const projectFork = Effect.fn("SessionProjector.projectFork")(function* (
and(
eq(SessionMessageTable.session_id, event.data.parentID),
gt(SessionMessageTable.seq, cursor),
copiedSeq === 0 ? undefined : lt(SessionMessageTable.seq, copiedSeq + 1),
lt(SessionMessageTable.seq, copiedSeq + 1),
sql`${SessionMessageTable.type} != 'compaction' or json_extract(${SessionMessageTable.data}, '$.status') not in ('queued', 'running')`,
),
)
@@ -318,27 +327,9 @@ const projectFork = Effect.fn("SessionProjector.projectFork")(function* (
.pipe(Effect.orDie)
}
for (const row of rows) {
const value = messageUsage(row)
if (value) addUsage(usage, value)
}
cursor = rows.at(-1)!.seq
}
yield* db
.update(SessionTable)
.set({
cost: usage.cost,
tokens_input: usage.tokens.input,
tokens_output: usage.tokens.output,
tokens_reasoning: usage.tokens.reasoning,
tokens_cache_read: usage.tokens.cache.read,
tokens_cache_write: usage.tokens.cache.write,
})
.where(eq(SessionTable.id, event.data.sessionID))
.run()
.pipe(Effect.orDie)
if (copiedSeq > 0) yield* EventV2.reserveSequence(db, event.data.sessionID, copiedSeq)
if (copiedSeq !== undefined) yield* EventV2.reserveSequence(db, event.data.sessionID, copiedSeq)
})
function run(db: DatabaseService, event: MessageEvent) {
@@ -697,8 +688,19 @@ const layer = Layer.effectDiscard(
yield* events.project(SessionEvent.Shell.Started, (event) => run(db, event))
yield* events.project(SessionEvent.Shell.Ended, (event) => run(db, event))
yield* events.project(SessionEvent.Step.Started, (event) => run(db, event))
yield* events.project(SessionEvent.Step.Ended, (event) => run(db, event))
yield* events.project(SessionEvent.Step.Failed, (event) => run(db, event))
yield* events.project(SessionEvent.Step.Ended, (event) =>
Effect.gen(function* () {
yield* run(db, event)
yield* applyUsage(db, event.data.sessionID, event.data)
}),
)
yield* events.project(SessionEvent.Step.Failed, (event) =>
Effect.gen(function* () {
yield* run(db, event)
if (event.data.cost !== undefined && event.data.tokens !== undefined)
yield* applyUsage(db, event.data.sessionID, { cost: event.data.cost, tokens: event.data.tokens })
}),
)
yield* events.project(SessionEvent.Text.Started, (event) => run(db, event))
yield* events.project(SessionEvent.Text.Ended, (event) => run(db, event))
yield* events.project(SessionEvent.Tool.Input.Started, (event) => run(db, event))
@@ -790,6 +792,17 @@ const layer = Layer.effectDiscard(
yield* InstructionCheckpoint.reset(db, event.data.sessionID)
}),
)
yield* events.subscribe([SessionEvent.Step.Ended, SessionEvent.Step.Failed]).pipe(
Stream.runForEach((event) => {
if (
event.type === SessionEvent.Step.Failed.type &&
(event.data.cost === undefined || event.data.tokens === undefined)
)
return Effect.void
return publishSessionUsage(db, events, event.data.sessionID)
}),
Effect.forkScoped({ startImmediately: true }),
)
}),
)
+2 -1
View File
@@ -3,7 +3,7 @@ export * as SessionRunner from "./index"
import type { LLMError } from "@opencode-ai/llm"
import { Context, Effect } from "effect"
import { SessionSchema } from "../schema"
import type { MessageDecodeError, StepFailedError, UserInterruptedError } from "../error"
import type { AgentNotFoundError, MessageDecodeError, StepFailedError, UserInterruptedError } from "../error"
import { SessionRunnerModel } from "./model"
import type { Instructions } from "../../instructions/index"
import type { ToolOutputStore } from "../../tool-output-store"
@@ -12,6 +12,7 @@ export type RunError =
| LLMError
| SessionRunnerModel.Error
| MessageDecodeError
| AgentNotFoundError
| StepFailedError
| UserInterruptedError
| Instructions.InitializationBlocked
+158 -49
View File
@@ -17,6 +17,7 @@ import { Config } from "../../config"
import { Database } from "../../database/database"
import { EventV2 } from "../../event"
import { Location } from "../../location"
import { ModelV2 } from "../../model"
import { PermissionV2 } from "../../permission"
import { Instructions } from "../../instructions/index"
import { InstructionBuiltIns } from "../../instructions/builtins"
@@ -46,9 +47,37 @@ import { SessionRunnerSystemPrompt } from "./system-prompt"
import { Snapshot } from "../../snapshot"
import { makeLocationNode } from "../../effect/app-node"
import { llmClient } from "../../effect/app-node-platform"
import { StepFailedError, UserInterruptedError } from "../error"
import { AgentNotFoundError, StepFailedError, UserInterruptedError } from "../error"
import { toSessionError } from "../to-session-error"
import { SessionRunnerRetry } from "./retry"
import type { SessionHooks } from "@opencode-ai/plugin/v2/effect/session"
import { PluginHooks } from "../../plugin/hooks"
import { PluginSupervisor } from "../../plugin/supervisor"
import { PluginSupervisorNode } from "../../plugin/supervisor-node"
type StepTokens = {
readonly input: number
readonly output: number
readonly reasoning: number
readonly cache: { readonly read: number; readonly write: number }
}
// TODO(#35765): Use Copilot's reported billed amount once billing has a dedicated typed runtime contract.
export function calculateCost(costs: ModelV2.Info["cost"], tokens: StepTokens) {
const context = tokens.input + tokens.cache.read + tokens.cache.write
const tier = costs
.filter((cost) => cost.tier?.type === "context" && context > cost.tier.size)
.toSorted((a, b) => (b.tier?.size ?? 0) - (a.tier?.size ?? 0))[0]
const cost = tier ?? costs.find((cost) => cost.tier === undefined)
if (!cost) return 0
return (
(tokens.input * cost.input +
(tokens.output + tokens.reasoning) * cost.output +
tokens.cache.read * cost.cache.read +
tokens.cache.write * cost.cache.write) /
1_000_000
)
}
/**
* Runs one durable coding-agent Session until it settles.
@@ -107,6 +136,7 @@ const layer = Layer.effect(
const llm = yield* LLMClient.Service
const agents = yield* AgentV2.Service
const tools = yield* ToolRegistry.Service
const hooks = yield* PluginHooks.Service
const models = yield* SessionRunnerModel.Service
const store = yield* SessionStore.Service
const location = yield* Location.Service
@@ -120,6 +150,7 @@ const layer = Layer.effect(
const db = (yield* Database.Service).db
const compaction = yield* SessionCompaction.Service
const title = yield* SessionTitle.Service
const plugins = yield* PluginSupervisor.Service
// Title generation is a side effect of the first step; it must not delay step continuation.
// Tracked per process so repeated wakes before the second user message arrives don't
// re-fire a redundant LLM call; `SessionTitle` itself is idempotent based on durable history.
@@ -180,57 +211,104 @@ const layer = Layer.effect(
const session = yield* getSession(sessionID)
if (session.location.directory !== location.directory || session.location.workspaceID !== location.workspaceID)
return yield* Effect.interrupt
const agent = yield* agents.select(session.agent)
// Establish what the model knows before admitting what the user said, so
// a blocked first step leaves pending inputs untouched.
const checkpoint = yield* InstructionCheckpoint.prepare(
db,
events,
loadInstructions(agent, session.id),
session.id,
)
yield* plugins.synchronize
const prepared = yield* Effect.gen(function* () {
const agent = yield* agents.select(session.agent)
const agentInfo = agent.info
if (!agentInfo)
return yield* new AgentNotFoundError({ sessionID: session.id, agent: session.agent ?? agent.id })
// Establish what the model knows before admitting what the user said, so
// a blocked first step leaves pending inputs untouched.
const checkpoint = yield* InstructionCheckpoint.prepare(
db,
events,
loadInstructions(agent, session.id),
session.id,
)
let currentStep = step
if (promotion) {
let promoted = 0
if (promotion === "steer") promoted = yield* SessionInput.promoteSteers(db, events, session.id)
if (promotion === "queue") {
promoted += Number(yield* SessionInput.promoteNextQueued(db, events, session.id))
promoted += yield* SessionInput.promoteSteers(db, events, session.id)
}
if (promoted > 0) currentStep = 1
}
const resolved = yield* models.resolve(session)
const entries = yield* SessionHistory.entriesForRunner(db, session.id, checkpoint.baselineSeq)
const context = entries.map((entry) => entry.message)
const isLastStep = agentInfo.steps !== undefined && currentStep >= agentInfo.steps
const toolMaterialization = isLastStep
? undefined
: yield* tools.materialize({ permissions: agentInfo.permissions, model: resolved.model })
const promptCacheKey = /^ses_[0-9a-f]{64}$/.test(session.id) ? session.id.slice(4) : session.id
return {
agent,
agentInfo,
currentStep,
resolved,
context,
isLastStep,
toolMaterialization,
request: LLM.request({
model: resolved.model,
providerOptions: { openai: { promptCacheKey } },
system: [
agentInfo.system ? agentInfo.system : SessionRunnerSystemPrompt.provider(resolved.model),
checkpoint.baseline,
]
.filter((part): part is string => part !== undefined && part.length > 0)
.map(SystemPart.make),
messages: [
...toLLMMessages(context, resolved.ref),
...(isLastStep ? [Message.assistant(MAX_STEPS_PROMPT)] : []),
],
tools: toolMaterialization?.definitions ?? [],
toolChoice: isLastStep ? "none" : undefined,
}),
}
})
const agent = prepared.agent
const agentInfo = prepared.agentInfo
const currentStep = prepared.currentStep
const resolved = prepared.resolved
const model = resolved.model
const context = prepared.context
const isLastStep = prepared.isLastStep
const toolMaterialization = prepared.toolMaterialization
const request = prepared.request
const toolFibers = yield* FiberSet.make<void, ToolOutputStore.Error | UserInterruptedError>()
const ownedToolFibers: Array<Fiber.Fiber<void, ToolOutputStore.Error | UserInterruptedError>> = []
let needsContinuation = false
let currentStep = step
if (promotion) {
let promoted = 0
if (promotion === "steer") promoted = yield* SessionInput.promoteSteers(db, events, session.id)
if (promotion === "queue") {
promoted += Number(yield* SessionInput.promoteNextQueued(db, events, session.id))
promoted += yield* SessionInput.promoteSteers(db, events, session.id)
}
if (promoted > 0) currentStep = 1
const availableTools = new Map(request.tools.map((tool) => [tool.name, tool]))
const requestEvent: SessionHooks["request"] = {
sessionID: session.id,
agent: agent.id,
model: resolved.ref,
system: [...request.system],
messages: [...request.messages],
tools: Object.fromEntries(
request.tools.map((tool) => [tool.name, { description: tool.description, input: { ...tool.inputSchema } }]),
),
}
const resolved = yield* models.resolve(session)
const model = resolved.model
const entries = yield* SessionHistory.entriesForRunner(db, session.id, checkpoint.baselineSeq)
const context = entries.map((entry) => entry.message)
const isLastStep = agent.info?.steps !== undefined && currentStep >= agent.info.steps
const toolMaterialization = isLastStep
? undefined
: yield* tools.materialize({ permissions: agent.info?.permissions, model })
const promptCacheKey = /^ses_[0-9a-f]{64}$/.test(session.id) ? session.id.slice(4) : session.id
const request = LLM.request({
model,
providerOptions: { openai: { promptCacheKey } },
system: [
agent.info?.system ? agent.info.system : SessionRunnerSystemPrompt.provider(model),
checkpoint.baseline,
]
.filter((part): part is string => part !== undefined && part.length > 0)
.map(SystemPart.make),
messages: [
...toLLMMessages(context, resolved.ref),
...(isLastStep ? [Message.assistant(MAX_STEPS_PROMPT)] : []),
],
tools: toolMaterialization?.definitions ?? [],
toolChoice: isLastStep ? "none" : undefined,
// Plugins may reshape the draft, but cannot advertise tools excluded earlier
// by permissions or registration state.
yield* hooks.trigger("session", "request", requestEvent)
const hookedRequest = LLM.updateRequest(request, {
system: requestEvent.system,
messages: requestEvent.messages,
tools: Object.entries(requestEvent.tools).flatMap(([name, tool]) => {
const registered = availableTools.get(name)
if (!registered) return []
return [{ ...registered, description: tool.description, inputSchema: tool.input }]
}),
})
const advertisedTools = new Set(hookedRequest.tools.map((tool) => tool.name))
// Automatic compaction completed; rebuild the request from compacted history.
if (
!(yield* SessionInput.pendingCompaction(db, session.id)) &&
(yield* compaction.compactIfNeeded({ sessionID: session.id, messages: context, request }))
(yield* compaction.compactIfNeeded({ sessionID: session.id, messages: context, request: hookedRequest }))
)
return { _tag: "RestartAfterCompaction", step: currentStep } as const
const startSnapshot = yield* snapshots.capture()
@@ -251,7 +329,7 @@ const layer = Layer.effect(
const publish = (event: LLMEvent, outputPaths: ReadonlyArray<string> = [], error?: SessionError.Error) =>
serialized(publisher.publish(event, outputPaths, error))
let overflowFailure: ProviderErrorEvent | undefined
const providerStream = llm.stream(request).pipe(
const providerStream = llm.stream(hookedRequest).pipe(
Stream.runForEach((event) =>
Effect.gen(function* () {
if (overflowFailure || publisher.hasProviderError()) return
@@ -272,7 +350,31 @@ const layer = Layer.effect(
)
return
}
// A request hook hid this registered tool from the current request. Fail only
// this call durably and continue so the model can react, instead of executing
// a tool that was not advertised. Unregistered tools flow through settle, which
// durably fails them as unknown.
if (!advertisedTools.has(event.name) && availableTools.has(event.name)) {
needsContinuation = true
yield* publish(
LLMEvent.toolError({
id: event.id,
name: event.name,
message: `Tool is not available for this request: ${event.name}`,
}),
)
return
}
needsContinuation = true
if (!advertisedTools.has(event.name)) {
yield* serialized(
publisher.failUnsettledTools({
type: "tool.execution",
message: `Tool is not available for this request: ${event.name}`,
}),
)
return
}
const assistantMessageID = yield* publisher.assistantMessageID(event.id)
ownedToolFibers.push(
yield* Effect.uninterruptibleMask((restore) =>
@@ -312,6 +414,11 @@ const layer = Layer.effect(
Effect.ensuring(serialized(publisher.flush())),
)
const stepUsage = (settlement: NonNullable<ReturnType<typeof publisher.stepSettlement>>) => ({
cost: calculateCost(resolved.cost, settlement.tokens),
tokens: settlement.tokens,
})
// Captures the end snapshot, diffs it against the step's start, and durably ends the
// assistant step.
const publishStepEnd = (settlement: NonNullable<ReturnType<typeof publisher.stepSettlement>>) =>
@@ -328,8 +435,7 @@ const layer = Layer.effect(
sessionID: session.id,
assistantMessageID: yield* publisher.startAssistant(),
finish: settlement.finish,
cost: 0,
tokens: settlement.tokens,
...stepUsage(settlement),
snapshot: endSnapshot,
files,
}),
@@ -365,7 +471,7 @@ const layer = Layer.effect(
if (
SessionRunnerRetry.isRetryable(llmFailure) &&
!publisher.hasRetryEvidence() &&
(agent.info?.steps === undefined || currentStep < agent.info.steps)
(agentInfo.steps === undefined || currentStep < agentInfo.steps)
) {
return yield* new SessionRunnerRetry.RetryableFailure({
cause: llmFailure,
@@ -452,7 +558,8 @@ const layer = Layer.effect(
const stepEndedCleanly =
!streamInterrupted && !toolsInterrupted && infraError === undefined && !providerFailed && !stepFailure
if (stepSettlement && stepEndedCleanly) yield* publishStepEnd(stepSettlement)
if (stepFailure) yield* serialized(publisher.publishStepFailure())
if (stepFailure)
yield* serialized(publisher.publishStepFailure(stepSettlement ? stepUsage(stepSettlement) : undefined))
if (stream._tag === "Failure") return yield* Effect.failCause(stream.cause)
if (userDeclined) return yield* Effect.interrupt
@@ -595,6 +702,7 @@ export const node = makeLocationNode({
llmClient,
AgentV2.node,
ToolRegistry.node,
PluginHooks.node,
SessionRunnerModel.node,
SessionStore.node,
Location.node,
@@ -609,5 +717,6 @@ export const node = makeLocationNode({
Config.node,
Snapshot.node,
Database.node,
PluginSupervisorNode.node,
],
})
+5 -1
View File
@@ -82,6 +82,8 @@ export interface Resolved {
readonly model: Model
/** Selected catalog identity. Durable records and displays must use this, never the API model id. */
readonly ref: ModelV2.Ref
/** Catalog pricing in dollars per million tokens. */
readonly cost: ModelV2.Info["cost"]
}
export interface Interface {
@@ -94,13 +96,14 @@ export class Service extends Context.Service<Service, Interface>()("@opencode/v2
export const layerWith = (resolve: Interface["resolve"]) => Layer.succeed(Service, Service.of({ resolve }))
/** Builds a Resolved whose catalog identity mirrors the route model. Test or embedding seam. */
export const resolved = (model: Model, variant?: ModelV2.VariantID): Resolved => ({
export const resolved = (model: Model, variant?: ModelV2.VariantID, cost: ModelV2.Info["cost"] = []): Resolved => ({
model,
ref: ModelV2.Ref.make({
id: ModelV2.ID.make(model.id),
providerID: ProviderV2.ID.make(model.provider),
...(variant === undefined ? {} : { variant }),
}),
cost,
})
const apiKey = (model: ModelV2.Info, credential?: Credential.Value) => {
@@ -341,6 +344,7 @@ const layer = Layer.effect(
providerID: selected.providerID,
...(session.model?.variant === undefined ? {} : { variant: session.model.variant }),
}),
cost: selected.cost,
}
}),
})
@@ -101,27 +101,36 @@ export const createLLMEventPublisher = (events: EventV2.Interface, input: Input)
ended: (id: string, value: string, ordinal: number, state?: Record<string, unknown>) => Effect.Effect<void>,
single = false,
) => {
const chunks = new Map<string, { readonly ordinal: number; readonly values: string[] }>()
const chunks = new Map<
string,
{ readonly ordinal: number; readonly values: string[]; state?: Record<string, unknown> }
>()
let nextOrdinal = 0
const start = (id: string) =>
const start = (id: string, state?: Record<string, unknown>) =>
Effect.suspend(() => {
if (chunks.has(id)) return Effect.die(new Error(`Duplicate ${name} start: ${id}`))
if (single && chunks.size > 0) return Effect.die(new Error(`${name} start before end: ${id}`))
const ordinal = nextOrdinal++
chunks.set(id, { ordinal, values: [] })
chunks.set(id, { ordinal, values: [], state })
return Effect.succeed(ordinal)
})
const append = (id: string, value: string) =>
const append = (id: string, value: string, state?: Record<string, unknown>) =>
Effect.suspend(() => {
const current = chunks.get(id)
if (!current) return Effect.die(new Error(`${name} delta before start: ${id}`))
current.values.push(value)
if (state !== undefined) current.state = { ...current.state, ...state }
return Effect.succeed(current.ordinal)
})
const end = Effect.fnUntraced(function* (id: string, state?: Record<string, unknown>) {
const current = chunks.get(id)
if (!current) return yield* Effect.die(new Error(`${name} end before start: ${id}`))
yield* ended(id, current.values.join(""), current.ordinal, state)
yield* ended(
id,
current.values.join(""),
current.ordinal,
state === undefined ? current.state : { ...current.state, ...state },
)
chunks.delete(id)
})
const flush = Effect.fnUntraced(function* () {
@@ -216,7 +225,10 @@ export const createLLMEventPublisher = (events: EventV2.Interface, input: Input)
if (replace || stepFailure === undefined) stepFailure = error
})
const publishStepFailure = Effect.fnUntraced(function* () {
const publishStepFailure = Effect.fnUntraced(function* (usage?: {
readonly cost: number
readonly tokens: ReturnType<typeof tokens>
}) {
if (stepFailed || stepFailure === undefined) return
const assistantMessageID = yield* startAssistant()
stepFailed = true
@@ -224,6 +236,7 @@ export const createLLMEventPublisher = (events: EventV2.Interface, input: Input)
sessionID: input.sessionID,
assistantMessageID,
error: stepFailure,
...usage,
})
})
@@ -284,7 +297,7 @@ export const createLLMEventPublisher = (events: EventV2.Interface, input: Input)
return
case "reasoning-start":
retryEvidence = true
const startedReasoningOrdinal = yield* reasoning.start(event.id)
const startedReasoningOrdinal = yield* reasoning.start(event.id, providerState(event.providerMetadata))
yield* events.publish(SessionEvent.Reasoning.Started, {
sessionID: input.sessionID,
assistantMessageID: yield* startAssistant(),
@@ -293,7 +306,11 @@ export const createLLMEventPublisher = (events: EventV2.Interface, input: Input)
})
return
case "reasoning-delta":
const deltaReasoningOrdinal = yield* reasoning.append(event.id, event.text)
const deltaReasoningOrdinal = yield* reasoning.append(
event.id,
event.text,
providerState(event.providerMetadata),
)
yield* events.publish(SessionEvent.Reasoning.Delta, {
sessionID: input.sessionID,
assistantMessageID: yield* currentAssistantMessageID(),
@@ -409,12 +426,12 @@ export const createLLMEventPublisher = (events: EventV2.Interface, input: Input)
case "step-finish":
yield* flush()
if (stepSettlement) return yield* Effect.die(new Error("Duplicate step finish"))
stepSettlement = { finish: event.reason, tokens: tokens(event.usage) }
if (event.reason === "content-filter") {
providerFailed = true
yield* failAssistant({ type: "provider.content-filter", message: "Provider blocked the response" }, true)
return
}
stepSettlement = { finish: event.reason, tokens: tokens(event.usage) }
return
case "finish":
return
@@ -120,12 +120,12 @@ const assistant = (message: SessionMessage.Assistant, model: ModelV2.Ref) => {
const content = message.content.flatMap((item): ContentPart[] => {
if (item.type === "text") return [{ type: "text", text: item.text }]
if (item.type === "reasoning")
return sameModel
return reuseProviderMetadata
? [
{
type: "reasoning",
text: item.text,
providerMetadata: reuseProviderMetadata ? providerMetadata(model.providerID, item.state) : undefined,
providerMetadata: providerMetadata(model.providerID, item.state),
},
]
: item.text.length > 0
@@ -4,7 +4,7 @@ import { PermissionV2 } from "../permission"
import { QuestionV2 } from "../question"
import { Integration } from "../integration"
import { ToolOutputStore } from "../tool-output-store"
import { StepFailedError, UserInterruptedError } from "./error"
import { AgentNotFoundError, StepFailedError, UserInterruptedError } from "./error"
import { SessionRunnerModel } from "./runner/model"
export function toSessionError(cause: unknown): SessionError.Error {
@@ -41,6 +41,7 @@ export function toSessionError(cause: unknown): SessionError.Error {
if (cause instanceof ToolFailure)
return cause.error === undefined ? { type: "tool.execution", message: cause.message } : toSessionError(cause.error)
if (cause instanceof StepFailedError) return cause.error
if (cause instanceof AgentNotFoundError) return { type: "unknown", message: cause.message }
if (cause instanceof UserInterruptedError) return { type: "aborted", message: cause.message }
if (
cause instanceof SessionRunnerModel.ModelNotSelectedError ||
+1 -1
View File
@@ -41,7 +41,7 @@ Registrations are scoped:
## Permissions
The registry has no `PermissionV2.Service` dependency and performs no execution authorization. An internal built-in-only operation attaches a permission action solely to preserve whole-tool definition filtering; it is not part of public `Tool.make`. Most tools default to their registered name; `edit`, `write`, and `apply_patch` declare the shared `edit` action.
The registry has no `PermissionV2.Service` dependency and performs no execution authorization. An internal built-in-only operation attaches a permission action solely to preserve whole-tool definition filtering; it is not part of public `Tool.make`. Most tools default to their registered name; `edit`, `write`, and `patch` declare the shared `edit` action.
Definition filtering is catalog visibility, not execution authorization. A call still executes the captured leaf policy if it reaches settlement.

Some files were not shown because too many files have changed in this diff Show More