@openchamber/sdk 2.5.2 → 2.5.4-canary.20261102.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/API.md +13 -2
- package/DOCUMENTATION.md +60 -4
- package/GUEST_SERVICES.md +187 -4
- package/README.md +2 -2
- package/dist/host.d.ts +1 -1
- package/dist/index.d.ts +6 -4
- package/dist/index.js +3 -2
- package/dist/manifest.d.ts +232 -6
- package/dist/manifest.js +110 -4
- package/dist/parse.d.ts +186 -0
- package/dist/parse.js +161 -6
- package/dist/protocol.d.ts +4 -0
- package/dist/schemas.d.ts +1 -1
- package/dist/schemas.js +1 -1
- package/dist/service-provider-schemas.d.ts +978 -0
- package/dist/service-provider-schemas.js +159 -1
- package/dist/service-providers.d.ts +423 -2
- package/dist/service-providers.js +311 -37
- package/dist/service-surface-schemas.d.ts +2 -0
- package/dist/service-surface-schemas.js +1 -0
- package/dist/service-surface.d.ts +5 -0
- package/dist/service-surface.js +5 -1
- package/dist/session-events.d.ts +161 -0
- package/dist/session-events.js +108 -0
- package/dist/ui/progress.d.ts +4 -0
- package/dist/ui/progress.js +17 -4
- package/dist/ui/style.js +23 -16
- package/dist/workspace-schemas.d.ts +1 -0
- package/dist/workspace-schemas.js +3 -1
- package/dist/workspace.d.ts +2 -0
- package/package.json +1 -1
package/API.md
CHANGED
|
@@ -215,7 +215,9 @@ Await subscription registration to handle refusal, then retain its returned unsu
|
|
|
215
215
|
|
|
216
216
|
Snapshots carry `state: 'loading' | 'ready' | 'error'`; session snapshots also carry per-directory `coverage`. Loading/error may retain data. Only `ready` establishes complete empty success. Reads use existing shared stores and hydration, never a git scan per extension request.
|
|
217
217
|
|
|
218
|
-
Projects contain `id`, `name`, `directory`. Worktrees contain `directory`, `name`, `branch`, and `status: 'ready' | 'pending' | 'invalid' | 'missing'`. Session records contain `id`, `title`, `projectId`, `directory`, `parentId`, `createdAt`, `updatedAt`, `archivedAt`, `worktree`, `activity`, `outcome`, `items`, and `links`. Item references contain only this extension's `id` and optional `data`. `links` is every thread linked to the session, whoever linked it (the user, the agent through `session.link`, this extension or another), each with `title`, `url`, `kind: 'issue' | 'change'` (`change` is a pull, merge or change request) and `linkedAt`; read-only, and a change to it sends a new snapshot. Hosts older than this field omit it.
|
|
218
|
+
Projects contain `id`, `name`, `directory`. Worktrees contain `directory`, `name`, `branch`, and `status: 'ready' | 'pending' | 'invalid' | 'missing'`. Session records contain `id`, `title`, `projectId`, `directory`, optional `agent`, `parentId`, `createdAt`, `updatedAt`, `archivedAt`, `worktree`, `activity`, `outcome`, `items`, and `links`. Item references contain only this extension's `id` and optional `data`. `links` is every thread linked to the session, whoever linked it (the user, the agent through `session.link`, this extension or another), each with `title`, `url`, `kind: 'issue' | 'change'` (`change` is a pull, merge or change request) and `linkedAt`; read-only, and a change to it sends a new snapshot. Hosts older than this field omit it.
|
|
219
|
+
|
|
220
|
+
`agent` is the session's agent name, available for both top-level sessions and subsessions with the `sessions` capability. It is omitted when the host has no agent name or predates this field. Agent changes send a new snapshot.
|
|
219
221
|
|
|
220
222
|
`activity` is `unknown`, `idle`, `running`, `retrying`, `waiting-permission`, or `waiting-question` (the agent put a form to the user and is waiting on the answer). `outcome` is the last observed `completed` or `failed` turn, or `null` when unknown or working. Outcomes are in memory for the latest 2,000 observed sessions, reset on runtime switch, and are not reconstructed from persisted history. A later idle event preserves an observed failure until another run starts. `completed` never means the extension's task is Done. Blocking-request contents and approve/reply actions are not exposed.
|
|
221
223
|
|
|
@@ -544,7 +546,10 @@ Used by the OpenChamber host and by tools that validate packages. Guests rarely
|
|
|
544
546
|
| `fileEditors` | Optional, 1–8 editors: unique kebab-case `id`, `title` 1–60 chars, `match` 1–16 file-name patterns (no `/`, not only wildcards), `entry` package `.html` with built scripts, optional `content` `"text"` (default) or `"binary"` (`invalid-file-editors`). See File editors above |
|
|
545
547
|
| `filesystem` | Optional, 1–16 globs, each 1–256 chars, starting with `/` or `~/`; `**` spans folders, `*` / `?` stay in one segment; no `..`, empty segment, or backslash (`invalid-filesystem`). Declaring it adds the `filesystem` capability and the dialog lists the globs |
|
|
546
548
|
| `integration` | Optional. Exactly one of `oauth`, `token`, or `host` (`provider: "linear"` only) |
|
|
547
|
-
| `service` | Optional. `entry` must be a built `.js` file on disk. `provides: ["browser"]` makes it the agent's browser when the user selects it; `provides: ["source-control"]` makes it a Git host and needs `sourceControl`; `surface: true` gives it a host-drawn live panel the user can take over (no `panel.entry` then).
|
|
549
|
+
| `service` | Optional. `entry` must be a built `.js` file on disk. `provides: ["browser"]` makes it the agent's browser when the user selects it; `provides: ["source-control"]` makes it a Git host and needs `sourceControl`; `provides: ["notifications"]` makes it a notification channel and needs `notifications`; `provides: ["tracker"]` makes it a work item tracker next to Linear and needs `tracker` (a service is a Git host or a tracker, never both); `surface: true` gives it a host-drawn live panel the user can take over (no `panel.entry` then). None of these needs a panel or background entry. See [GUEST_SERVICES.md](https://github.com/openchamber/openchamber/blob/main/packages/sdk/GUEST_SERVICES.md) |
|
|
550
|
+
| `sessionEvents` | Optional, needs `service`. `{ "events": [1 to 4 distinct of "turn-finished", "turn-failed", "permission-asked", "question-asked"], "content"?: true }`. OpenChamber posts each listed event, from every project, to the service at `POST /session-events`, with no window open; the service needs no panel or background entry for it. `content: true` adds the `transcript` capability (the reply, the question, the permission's detail). Bad shape or no `service` is `invalid-session-events`. Older hosts drop the key and send nothing, so set an `engines.openchamber` floor. See [GUEST_SERVICES.md](https://github.com/openchamber/openchamber/blob/main/packages/sdk/GUEST_SERVICES.md#session-events-contributessessionevents) |
|
|
551
|
+
| `notifications` | Required with `provides: ["notifications"]`, refused without it. The notification channel the service delivers to: `label` (1 to 64), `instances` (`{ kind: "fixed", origin }` or `{ kind: "user", label, placeholder? }`), `token` (the Git host's token block: `header`, `scheme`, `username?`, `scopes?`, `createPath?`). Strict. `panel.id` is at most 28 characters (the channel id is `ext-<panel.id>`). Errors are `invalid-service`. See [GUEST_SERVICES.md](https://github.com/openchamber/openchamber/blob/main/packages/sdk/GUEST_SERVICES.md#notification-channel-provides-notifications) |
|
|
552
|
+
| `tracker` | Required with `provides: ["tracker"]`, refused without it, and refused beside `sourceControl`. The work item tracker the service is: `wire: 1`, `label` (1 to 64), `instances` (`{ kind: "fixed", origin }` or `{ kind: "user", label, placeholder? }`), `auth` (`token` block and/or `oauth` `{ authorizeUrl, tokenUrl, scopes? }`, at least one), `capabilities` (flags, each off by default), `stateFilters` (`open`, `all`, then up to 14 of yours), `identifier` (`key-number`, `number` or `short-code`), `urls.item` (1 to 8 link templates with `{id}`, `{segment}`, `{segments+}` and `?name={id}`). Strict. `panel.id` is at most 28 characters (the tracker id is `ext-<panel.id>`). Errors are `invalid-service`. See [GUEST_SERVICES.md](https://github.com/openchamber/openchamber/blob/main/packages/sdk/GUEST_SERVICES.md#tracker-provides-tracker) |
|
|
548
553
|
| `sourceControl` | Required with `provides: ["source-control"]`, refused without it. The Git host the service implements: `wire: 1`, `label`, `terminology`, `referencePrefix`, `repositoryPath`, `instances`, `capabilities`, `token`, `transport`, `limits`, `trackedItems`, `presentation`. Strict; URL templates use only `{owner}`, `{namespace+}`, `{repo}`, `{number}`. `panel.id` is at most 28 characters (the host id is `ext-<panel.id>`). Change-based hosts may add the `change` term, `capabilities.reviewModel` and `capabilities.revisions`, the `submit` merge method (alone), `presentation.features.submitRequirements`, and `token.scheme: "basic"` with `token.username: true`; older hosts refuse these, so set an `engines.openchamber` floor. Errors are `invalid-service`. See [GUEST_SERVICES.md](https://github.com/openchamber/openchamber/blob/main/packages/sdk/GUEST_SERVICES.md#source-control-provider-provides-source-control) |
|
|
549
554
|
|
|
550
555
|
|
|
@@ -610,6 +615,12 @@ A service with `provides: ["browser"]` answers the agent's `browser.*` actions a
|
|
|
610
615
|
|
|
611
616
|
A service with `provides: ["source-control"]` is a Git host next to GitHub, GitLab, Gitea and Gerrit, described by `contributes.sourceControl`. OpenChamber keeps the accounts and tokens and forwards reads and mutation steps to `POST /source-control` (`SOURCE_CONTROL_PROVIDER_PATH`); parse the body with `readSourceControlProviderRequest` and answer `{ ok, data }` or `{ ok: false, error }` (`SourceControlProviderResult`). The service reaches the forge only through `createSourceControlForge({ env: process.env }).request(handle, request)`, which goes through OpenChamber's outbound proxy (`OPENCHAMBER_HOST_CALLBACK_URL`); OpenChamber adds the token and keeps the request on the instance. Methods (`SOURCE_CONTROL_PROVIDER_METHODS`), inputs (`SourceControlProviderInputs`), deadlines (`sourceControlProviderTimeoutMs`) and caps (`SOURCE_CONTROL_*`) are exported from `@openchamber/sdk`; the host's answer schemas are `sourceControlProviderSchemasV1` in `@openchamber/sdk/schemas`. Full contract in [GUEST_SERVICES.md](https://github.com/openchamber/openchamber/blob/main/packages/sdk/GUEST_SERVICES.md#source-control-provider-provides-source-control); reference: `examples/source-control-provider`.
|
|
612
617
|
|
|
618
|
+
A service with `provides: ["notifications"]` delivers the user's notifications next to the desktop and the phone, described by `contributes.notifications`. OpenChamber keeps the account the user connects in Settings → Integrations and forwards three calls to `POST /notifications` (`NOTIFICATIONS_PROVIDER_PATH`): `auth.probe`, `auth.verify` and `notify` (`NOTIFICATIONS_PROVIDER_METHODS`, deadlines in `notificationsProviderTimeoutMs`); parse the body with `readNotificationsProviderRequest` and answer `{ ok, data }` or `{ ok: false, error }` (`NotificationsProviderResult`). The service reaches the channel's server only through `createNotificationsOutbound({ env: process.env }).request(handle, request)`, which goes through OpenChamber's outbound proxy; OpenChamber adds the token and keeps the request on the account's origin. The notification shape is `GuestNotification`, its kinds `GUEST_NOTIFICATION_KINDS`; the host's answer schemas are `notificationsProviderSchemasV1` in `@openchamber/sdk/schemas`. Full contract in [GUEST_SERVICES.md](https://github.com/openchamber/openchamber/blob/main/packages/sdk/GUEST_SERVICES.md#notification-channel-provides-notifications); reference: `examples/notification-webhook`.
|
|
619
|
+
|
|
620
|
+
A service with `provides: ["tracker"]` is a work item tracker next to Linear, described by `contributes.tracker`. OpenChamber keeps the accounts the user connects on the tracker's card under Extension accounts in Settings → Integrations (a pasted token, or a browser sign-in it runs itself with the client the user enters) and forwards nine calls to `POST /tracker` (`TRACKER_PROVIDER_PATH`): `auth.probe`, `auth.verify`, `items.list`, `items.get`, `items.updateState`, `items.liveSummaries`, `states.list`, `containers.list` and `comments.create` (`TRACKER_PROVIDER_METHODS`, deadlines in `trackerProviderTimeoutMs`); parse the body with `readTrackerProviderRequest` and answer `{ ok, data }` in the neutral item shapes (`TrackerProviderItemSummary`, `TrackerProviderItem`) or `{ ok: false, error }` (`TrackerProviderResult`). A query that is a link your templates claim, or an identifier of your declared shape, is read through `items.get` first; a bare word `items.get` answers null for reaches `items.list` as a search after all. The service reaches the tracker's server only through `createTrackerOutbound({ env: process.env }).request(handle, request)`, which goes through OpenChamber's outbound proxy; OpenChamber adds the credential and keeps the request on the account's origin, and a read's handle sends `GET` only (`isTrackerProviderWriteMethod`). The host's answer schemas are `trackerProviderSchemasV1` in `@openchamber/sdk/schemas`. Full contract in [GUEST_SERVICES.md](https://github.com/openchamber/openchamber/blob/main/packages/sdk/GUEST_SERVICES.md#tracker-provides-tracker); reference: `examples/tracker-provider`.
|
|
621
|
+
|
|
622
|
+
A service whose manifest declares `contributes.sessionEvents` is told what happens in the user's sessions: OpenChamber posts one request per subscribed event (`turn-finished`, `turn-failed`, `permission-asked`, `question-asked`) to `POST /session-events` (`SESSION_EVENTS_PATH`), from every project, whether or not a window is open. Parse the body with `readSessionEventRequest` (`GuestSessionEventRequest`: `event` with identifiers, and `content` only when the user approved the `transcript` capability that `content: true` asks for) and answer `{ ok: true }` or `{ ok: false, error }` (`GuestSessionEventResult`). Filter on `event.project.directory` yourself. Limits are the `SESSION_EVENT*` constants; the host's answer schema is `sessionEventResultSchema` in `@openchamber/sdk/schemas`. Full contract in [GUEST_SERVICES.md](https://github.com/openchamber/openchamber/blob/main/packages/sdk/GUEST_SERVICES.md#session-events-contributessessionevents); reference: `examples/session-events-log`.
|
|
623
|
+
|
|
613
624
|
A service with `surface: true` shows a live picture in the extension's rail panel: the host pulls frames from `GET /surface/frame`, draws them, sends the user's input to `POST /surface/input`, and owns who is in control (nobody, the agent, the user). Paths, event types, and the `readSurface*` parsers are exported from `@openchamber/sdk`; contract in [GUEST_SERVICES.md](https://github.com/openchamber/openchamber/blob/main/packages/sdk/GUEST_SERVICES.md#shared-surface-surface-true).
|
|
614
625
|
|
|
615
626
|
Bundle the service with the Node target:
|
package/DOCUMENTATION.md
CHANGED
|
@@ -17,7 +17,8 @@ Public author pages live in `packages/docs/content/docs/sdk.mdx`, `sdk/host.mdx`
|
|
|
17
17
|
- `dist/`: the published output (`bun run --cwd packages/sdk build`, `tsc -p tsconfig.build.json`). `package.json` `exports` point here so Node loads the SDK from `node_modules` (packaged Electron, `openchamber serve`). Inside the monorepo, Vite aliases and tsconfig `paths` resolve `@openchamber/sdk` to `src/` so builds and type-checks never depend on a stale `dist`. The server under Bun and Node uses `dist`; the root `postinstall` builds it. The package ships to npm from the release workflow with `bun publish`, right before `@openchamber/web`; `scripts/bump-version.mjs` bumps it with the app, so the SDK version always equals the OpenChamber version it was released with.
|
|
18
18
|
- `src/file-editor.ts`: the `contributes.fileEditors` contract: limits, file-name pattern check and match, the document/snapshot/change types, and `createFileSaveTracker`. Zod-free, so a guest editor bundles it.
|
|
19
19
|
- `GUEST_SERVICES.md`: local service contract (`contributes.service`, `serviceRequest`, grant, loopback).
|
|
20
|
-
- `src/service-providers.ts`: contracts a service fulfils for a host role (`contributes.service.provides`). `source-control` is described under Source-control role below. `browser`: the twelve `browser.*` actions, their parameter and answer types, the limits, `BrowserProviderContext` (the calling project and chat, filled by the host from the tool call; `null` fields mean unknown), and `readBrowserProviderRequest` (a zod-free boundary parse for the service side). `src/service-provider-schemas.ts` is the host's zod parse of the answer, exported from `@openchamber/sdk/schemas`. A providing service needs no panel or background entry (`runtimeContributions` in `parse.ts`). The host side is `packages/web/server/lib/browser-control/provider.js`.
|
|
20
|
+
- `src/service-providers.ts`: contracts a service fulfils for a host role (`contributes.service.provides`). `source-control` is described under Source-control role below, `notifications` under Notifications role, `tracker` under Tracker role. `browser`: the twelve `browser.*` actions, their parameter and answer types, the limits, `BrowserProviderContext` (the calling project and chat, filled by the host from the tool call; `null` fields mean unknown), and `readBrowserProviderRequest` (a zod-free boundary parse for the service side). `src/service-provider-schemas.ts` is the host's zod parse of the answer, exported from `@openchamber/sdk/schemas`. A providing service needs no panel or background entry (`runtimeContributions` in `parse.ts`). The host side is `packages/web/server/lib/browser-control/provider.js`.
|
|
21
|
+
- `src/session-events.ts`: the `contributes.sessionEvents` contract (`SessionEventsContribution`, the four `GUEST_SESSION_EVENT_TYPES`, the `SESSION_EVENT*` limits, `GuestSessionEventRequest` / `GuestSessionEventResult`, and the zod-free `readSessionEventRequest` for the service side); `sessionEventResultSchema` in `src/service-provider-schemas.ts` is the host's parse of the answer. The host side is `packages/web/server/lib/guests/session-events.js`; the contract is described under Session events below and in `GUEST_SERVICES.md`.
|
|
21
22
|
- `src/service-surface.ts`: the shared-surface contract (`contributes.service.surface`): loopback paths and headers, input event types, limits, and the zod-free `readSurfaceInputBatch` / `readSurfaceControlNotice` / `readSurfaceResizeRequest` for the service side. `src/service-surface-schemas.ts` (host entry) holds the viewer↔host socket messages (`surfaceViewerMessageSchema`, `surfaceHostMessageSchema`) shared by `packages/ui/src/lib/guests/surface-client.ts` and `packages/web/server/lib/guests/surface.js`, plus the service answer schemas. A surface service needs no entry of its own; with `panel.entry` that page is docked to one edge of the picture, `panel.dock` (`GUEST_SURFACE_DOCKS`) and `panel.size` px (`GUEST_SURFACE_DOCK_SIZE_*`), only valid with both. The rail panel is `packages/ui/src/components/layout/GuestSurfacePane.tsx` (host-drawn canvas, not an iframe); `ContextPanel` lays the page iframe (kept mounted) beside the pane (mounted only while shown) per `DOCK_LAYOUT`, and `lib/guests/surfaces.ts#guestSurfaceDocking` decides. Viewer identity is host-set: input carries `x-surface-viewer` and `x-surface-frame-seq` (the service's own number of the frame that viewer last drew; a 409 refuses it as stale), control notices carry `viewer`, and page `serviceRequest`s from a window with a live viewer (`packages/ui/src/lib/guests/surface-viewers.ts`) also carry `x-surface-viewer-controls`.
|
|
22
23
|
- `src/ui/`: guest drawing kit. Building blocks only (`mountButton`, `mountTextField`, `mountSearchField`, `mountSelect`, `mountCheckbox`, `mountSwitch`, `mountTabs`, `mountBadge`, `mountList`, `mountEmpty`, `mountSpinner`, `mountBanner`, `mountSeparator`, `mountProgress`, `mountMenu`, `mountText`); the guest composes screens. See `src/ui/DOCUMENTATION.md`. Import `@openchamber/sdk/ui`. Do not import this from `packages/ui`. Kit changes follow package semver, not `apiVersion`.
|
|
23
24
|
|
|
@@ -55,7 +56,7 @@ Public author pages live in `packages/docs/content/docs/sdk.mdx`, `sdk/host.mdx`
|
|
|
55
56
|
- Host → guest `resolve` is `{ id, payload: { command, args } }`; the guest answers `resolve-result` with the same `id` and `{ item }` (a clamped `AttachIssueRequest` or `null`) or `{ error }` (1 to `GUEST_RESOLVE_ERROR_MAX` characters). The host waits `GUEST_REQUEST_TIMEOUT_MS`, then treats silence as a failure and never sends a `result` for a `resolve-result`. `badge` is `{ count }` with `count` an integer 0..999 or `null`; the host schema drops anything else.
|
|
56
57
|
- `ready` always carries `session` (`{ id, title, busy }` or `null`), `surface` (`panel` on the rail, `dialog` in the attach window, `page` full screen, `background` hidden, `status` in the Work Status panel, `file` as a Files view editor), `connection` (`{ connected, account }`), and `settings` (declared keys only). Title falls back to `id` when the host record has no title. `busy` is live `session_status` `busy` or `retry`. Missing status is `busy: false`, not an empty success that clears a previous snapshot. `model` is `providerID/id` when the host session record has both. `agent` is the session agent name when present. `surface` is which host chrome mounted the iframe. It is not `openSurface`. `item` is the attached item the surface was opened for (the user clicked that item's chip on the composer; the same `AttachIssueRequest` shape the guest handed to `attach`, with `text` filled from the chip's context) or `null` from the rail icon and the + menu. The host also pushes `item` when it changes while the frame is up; `onItem` replays the latest value. `ready.theme.tokens` is the host colour bag: surfaces (`background`, `elevated`, `mutedSurface`, `subtle`), text (`foreground`, `elevatedForeground`, `muted`), interaction (`border`, `hover`, `active`, `selection`, `selectionForeground`, `focus`), `primary` / `primaryForeground`, the four status colours, `font`, `mono`, and `radius`. `background` is the page the frame sits on, not a fixed theme slot: a frame in a docked context or Work Status panel gets the panel's colour (the theme's muted surface in dark themes, halfway between it and the chat colour in light ones) and `mutedSurface` swaps to the chat colour so soft blocks stay a step off the page (`usePageColors` in `packages/ui`). `applyHostReady` writes it onto the iframe. The host also pushes `connection` and `settings` the same way as `session`. Access tokens never appear in `ready` or a result.
|
|
57
58
|
- `writeClipboard` text is 1–32000 characters. `compose` text is 1–16000 after trim. Compose default mode is `append`. Compose writes the chat composer. It does not send.
|
|
58
|
-
- `contributes.capabilities` is a list of `prompt`, `sessions`, `files`, `model`, and/or `shells` (`DECLARED_GUEST_CAPABILITIES`). `model` gates `generate`: `{ prompt, system?, maxOutputTokens? }` → `{ text }`, posted with a `GUEST_GENERATE_TIMEOUT_MS` (960s, above the Small Model's largest prompt-derived deadline) timeout instead of the usual 20s because the model answers slowly; the host answers `NO_MODEL` when the small-model service resolves nothing (or only Claude Code) and `MODEL_FAILED` when the provider call fails. `requestedGuestCapabilities` adds `conversation`, `service`, `network`, `filesystem`, and `
|
|
59
|
+
- `contributes.capabilities` is a list of `prompt`, `sessions`, `files`, `model`, and/or `shells` (`DECLARED_GUEST_CAPABILITIES`). `model` gates `generate`: `{ prompt, system?, maxOutputTokens? }` → `{ text }`, posted with a `GUEST_GENERATE_TIMEOUT_MS` (960s, above the Small Model's largest prompt-derived deadline) timeout instead of the usual 20s because the model answers slowly; the host answers `NO_MODEL` when the small-model service resolves nothing (or only Claude Code) and `MODEL_FAILED` when the provider call fails. `requestedGuestCapabilities` adds `conversation`, `service`, `network`, `filesystem`, `origins` and `transcript` from a session action with `payload: ["messages"]`, `contributes.service`, `contributes.integration`, `contributes.filesystem`, `contributes.origins` and `contributes.sessionEvents.content`; the user approves that whole list once at install (`PUT /api/guests/:id/capabilities`), and a package that later asks for more is re-approved. `hasGuestCapability` gates `prompt({ send })`, `startSession`, `request`, `serviceRequest`, the four file calls, and the three shell calls; a refusal is `NOT_GRANTED` (`NO_SERVICE` for the service).
|
|
59
60
|
- `contributes.filesystem` is 1 to 16 globs, each 1 to 256 characters, starting with `/` or `~/`, with no `..`, empty segment, NUL, or backslash (`isGuestFilesystemPattern`; parse failure is `invalid-filesystem`). `**` spans directories, `*` and `?` stay inside one segment, and a pattern ending in `/**` also matches the directory itself. The public catalog row carries the list as `filesystem` so the approval dialog can show it.
|
|
60
61
|
- `readFile` / `writeFile` / `listDir` / `stat` are `file-read` / `file-write` / `file-list` / `file-stat` on the wire. `guestFileScope` decides the grant: a relative path is `project` (capability `files`, joined to the open project; none open is `NO_DIRECTORY`), `/…` or `~/…` is `filesystem` (must match a declared pattern). The server compares canonical paths, so a symlink out of either tree is `BAD_PATH`, as is any `..` segment. Results are `{ content }`, `{ written: true }`, `{ entries: { name, kind }[] }`, and `{ kind, size, mtime }` where `kind` may be `missing`. `GUEST_FILE_PATH_MAX` (1024), `GUEST_FILE_CONTENT_MAX` (2 000 000 characters, read and write), `GUEST_FILE_LIST_MAX` (2000 entries). The client refuses an empty, oversized, NUL, or backslash path as `BAD_PATH` and oversized content as `FILE_TOO_LARGE` before posting; the host schema accepts only what `isGuestFilePath` does, so a dropped message cannot turn into `HOST_TIMEOUT`. Nothing about the write is logged, not even the path.
|
|
61
62
|
- `contributes.attach` is `true`, `false`, `"panel"`, `"dialog"`, or `{ "mode": "panel" | "dialog", "entry"?: string }`. `true` and `"panel"` add a + menu row that opens the rail. `"dialog"` opens a host window that mounts the same iframe. The object form's `entry` is an HTML page inside the package (same path rules as `panel.entry`, install checks it exists and that its scripts are built) that the dialog loads instead of `panel.entry`; it is only accepted with `mode: "dialog"`, and `resolveAttachEntry(contributes)` returns it or `null`. The public catalog row carries it as `attachEntry`. Clicking the guest's chip on the composer reopens that guest with the chip as `ready.item`: a dialog guest in the attach window, a panel guest on the rail. New Worktree shows the same dialog-mode guests next to GitHub and Linear. Omitted or `false` stays off those menus. Public 1.0 is web and desktop. VS Code and mobile mark the guest catalog `unsupported`. They do not show the row.
|
|
@@ -74,13 +75,15 @@ Built-in extension source and registration rules live in `packages/extensions/DO
|
|
|
74
75
|
|
|
75
76
|
Relay panels use `lib/guests/relay-document.ts`: fetch package HTML and its statically referenced scripts, styles, CSS imports/URLs, images, fonts, and nested documents through `runtimeFetch`, then render self-contained `srcDoc` with the same `sandbox="allow-scripts"`. A normal iframe URL bypasses the client-side tunnel and can load the local host UI instead. Local resource paths stay under that guest's package; missing files, import cycles, or resource limits fail the load. The loader allows 500 referenced files, 40 MiB of fetched bytes and 64 MiB of embedded text. Runtime-created relative URLs are not intercepted: bundle those assets into the IIFE or use SDK operations. The relay document gets `guestFramePolicy(null)` as its first `<meta>` (a `srcDoc` has no response headers), so it cannot connect anywhere. Existing authored CSP is preserved and only narrows it further. Direct iframe URLs retain their existing path.
|
|
76
77
|
|
|
77
|
-
`useGuestIconSource` loads package SVGs through authenticated `runtimeFetch` in direct and relay modes, then exposes revocable blob URLs to CSS masks. It uses the package path rather than the original URL token, so cold-start token readiness and token expiry cannot strand an icon. Cache identity follows runtime, package path and
|
|
78
|
+
`useGuestIconSource` loads package SVGs through authenticated `runtimeFetch` in direct and relay modes, then exposes revocable blob URLs to CSS masks. It uses the package path rather than the original URL token, so cold-start token readiness and token expiry cannot strand an icon. Cache identity follows runtime, package path, extension version and catalog revision; late responses are discarded and old blobs revoked. Unknown icons and SVGs that are loading or failed use the shared `FALLBACK_GUEST_ICON`, the `apps` glyph.
|
|
78
79
|
|
|
79
80
|
Git installation accepts HTTPS, `ssh://` and scp-style SSH addresses. `POST /api/guests` accepts optional `gitIdentityId`; the server stores only that ID in the git origin, and resolves the profile again for clones and update fetches. Global/omitted identity uses the server process's Git/SSH configuration and agent. The Settings picker loads identities from the active runtime, discards stale loads, and never reuses a desktop's cached profiles on a remote server. SSH parsing, public-host validation and pinned connection options belong to `packages/web/server/lib/guests/clone.js`.
|
|
80
81
|
|
|
81
82
|
Catalog loading and installation retain typed request diagnostics in `lib/guests/request-failure.ts`: fixed API route, method, HTTP status, or a transport/invalid-response classification. Settings shows catalog diagnostics and passes install diagnostics to the toast description so Copy includes them. Response bodies, headers, credentials, and arbitrary transport error messages are excluded. Catalog failures preserve a previously loaded list; diagnostics clear on success or runtime switch, and late failures from a retired runtime are ignored.
|
|
82
83
|
|
|
83
|
-
`
|
|
84
|
+
The catalog follows the server while the app runs: `hooks/useGuestCatalogSync.ts` reads it again on `openchamber:guests-changed` (any install, update, approval, pause, removal, or edit to a folder install) and on a control-stream reconnect. `reloadGuestCatalog` never joins a read that started before the announcement; it queues one more after it, so the newer answer lands last. An extension needs no code of its own for this: its frames reload when the row's `revision` moves (part of `PluginPane`'s frame key; a file editor keeps its document and loads the new files on its next open), a shared-surface pane attaches a new viewer, a background action in flight ends as unavailable, and its service starts again from the new files on the next request.
|
|
85
|
+
|
|
86
|
+
`lib/guests/useGuestFrameUrl.ts` owns iframe URL lifetime. A direct frame loads by a grant in the path (`/api/guest-files/<grant>/…`, see `packages/web/server/lib/guests/DOCUMENTATION.md`), or by the guest-scoped query token from a server without grants. It retains the credential's expiry, renews on an expired navigation, and remints after enable/version/revision/runtime changes. Expiry alone never reloads a healthy document. Recovery is bounded until the guest's next `hello`; mint failure leaves the existing load-failed UI rather than mounting an unauthenticated URL. Late mint responses from a retired owner are discarded. Guest capability grants and provider OAuth are separate from this host file-loading authorization.
|
|
84
87
|
|
|
85
88
|
`GET /api/guests` plus `PluginPane` on the desktop/web rail. The iframe loads from `/api/guest-files/<grant>/<entry>` with a grant minted for that extension (`resolveGuestFrameUrl`); against a server without grants it falls back to a token minted for `guest:<id>` scope, valid only for `GET /api/guests/<id>/*`. The session-wide URL token never enters a guest frame. Settings → Extensions installs a folder, a local `.zip`, or an https git / zip URL into that OpenChamber instance's data dir (`extensions.json`). Settings → Integrations shows a host card for each installed guest that declared `integration`. OAuth and pasted tokens live in `guest-auth.json`. Host Linear uses `linear-auth.json`. VS Code and mobile set the catalog to `unsupported`. That is not an empty success. A guest must ship a classic IIFE (`panel/main.js`). No runtime compiles guest TypeScript. The iframe cannot load ESM.
|
|
86
89
|
|
|
@@ -167,6 +170,59 @@ The envelope and the methods are unchanged; `revision` on three reads and `votes
|
|
|
167
170
|
|
|
168
171
|
The mutation store's kinds are a closed list and the digest decides whether a retry is a replay. A digest an extension computed could change between its versions and turn retained records into conflicts, or make two different writes look like one. The service resolves, performs and reconciles; the host decides what a failure means from what its proxy observed.
|
|
169
172
|
|
|
173
|
+
## Notifications role
|
|
174
|
+
|
|
175
|
+
`provides: ["notifications"]` plus `contributes.notifications` (`NotificationsContribution`, `manifest.ts`) make an extension a delivery channel for the user's notifications. The SDK owns the manifest parse, the wire (`service-providers.ts`: `NOTIFICATIONS_PROVIDER_PATH`, the three `NOTIFICATIONS_PROVIDER_METHODS`, deadlines and caps, `GuestNotification` and `GUEST_NOTIFICATION_KINDS`, `readNotificationsProviderRequest`, `createNotificationsOutbound`) and the host's answer schemas (`notificationsProviderSchemasV1`). The host side is `packages/web/server/lib/notifications/extension-channels.js` with its account store and routes; the service-side contract is in `GUEST_SERVICES.md`; `examples/notification-webhook` and its receiver are the reference, run by `scripts/notification-webhook-example.test.ts`.
|
|
176
|
+
|
|
177
|
+
- The role and the contribution come together: either alone fails as `invalid-service`, as does a `panel.id` longer than `NOTIFICATIONS_GUEST_ID_MAX` (28, the Git host's rule), because the host id is `ext-<panel.id>`. Field errors are `invalid-service` with the failing field first.
|
|
178
|
+
- The contribution is `.strict()`: `label`, `instances` (`fixed` origin or `user`-typed), and the Git host's `token` block with its two refines (`private-token` ↔ `none`, `basic` ↔ `username`). `toPublicNotifications` is what the catalog row and the approval dialog carry; it drops nothing secret because the contribution holds nothing secret.
|
|
179
|
+
- `readNotificationsProviderRequest` checks the envelope and the session (`instance`, `handle`, `account` of `{ id, login | null }` or null); the host built `input`. The answer envelope is the Git host's: `ok/data` or `ok/error { message, status?, code? }`, so a 401 is written off only when the proxy saw it.
|
|
180
|
+
- `createNotificationsOutbound` and `createSourceControlForge` share one `postOutbound` and differ in the callback route (`NOTIFICATIONS_OUTBOUND_PATH`) and the error class, so the two roles cannot drift in how they reach the listener.
|
|
181
|
+
|
|
182
|
+
### Why the channel's account is OpenChamber's and not the service's
|
|
183
|
+
|
|
184
|
+
The ticket's rule for roles: host-derived id, host-owned accounts, Disable withdraws the provider and keeps its data. A token the service held would leave with the service, could be logged by it, and would survive a withdrawn approval; one OpenChamber holds travels only through the outbound proxy, pinned to the account's origin, and is forgotten with the extension. It is the Git host's model reused, with the same handle.
|
|
185
|
+
|
|
186
|
+
### Why no switch on the Notifications page
|
|
187
|
+
|
|
188
|
+
The maintainer chose approval as the switch: an approved extension with a connected account delivers, a paused one does not, and the Notifications page's existing toggles already say which kinds exist. A per-channel switch or a kind-by-channel matrix would be a new setting with its own storage and sync; the ticket's open question about routing is answered by having none in v1.
|
|
189
|
+
|
|
190
|
+
## Tracker role
|
|
191
|
+
|
|
192
|
+
`provides: ["tracker"]` plus `contributes.tracker` (`TrackerContribution`, `manifest.ts`) make an extension a work item tracker next to Linear. The SDK owns the manifest parse (`trackerSchema` in `parse.ts`, `isTrackerUrlTemplate` in `manifest.ts`), the wire (`service-providers.ts`: `TRACKER_PROVIDER_PATH`, the nine `TRACKER_PROVIDER_METHODS` with the two writes in `TRACKER_PROVIDER_WRITE_METHODS`, deadlines and caps, the neutral item shapes, `readTrackerProviderRequest`, `createTrackerOutbound`) and the host's answer schemas (`trackerProviderSchemasV1`). The host side is `packages/web/server/lib/trackers/extension-*.js`; the service-side contract is in `GUEST_SERVICES.md`; `examples/tracker-provider` with its fake server is the reference, run by `scripts/tracker-provider-example.test.ts`.
|
|
193
|
+
|
|
194
|
+
- The role and the contribution come together: either alone fails as `invalid-service`, as does a `panel.id` longer than `TRACKER_GUEST_ID_MAX` (28), because the host id is `ext-<panel.id>`. A manifest that provides both `source-control` and `tracker` fails too: both roles would be that id in the one namespace tracked items and the read cache key by.
|
|
195
|
+
- The contribution is `.strict()`: `label`, `instances` (`fixed` origin or `user`-typed, the notifications role's shape), `auth` with `token` (the Git host's token block) and/or `oauth` (`authorizeUrl` and `tokenUrl` as https URLs or paths on the instance), `capabilities` with every flag defaulting to false, `stateFilters` starting with `open` and `all`, `identifier` (one of `TRACKER_IDENTIFIER_KINDS`) and `urls.item` templates. `toPublicTracker` is what the catalog row, the approval dialog and the account card carry; it drops nothing secret because the contribution holds nothing secret.
|
|
196
|
+
- Link templates are validated by `isTrackerUrlTemplate`, which lives in `manifest.ts` so the host can import it without zod: `{id}` once, in the path or as `?name={id}`, `{segment}` and at most one `{segments+}`. The host compiles them; an extension never supplies a regular expression, the Git host's rule.
|
|
197
|
+
- The neutral shapes in `service-providers.ts` are the UI's tracker contract without `provider`, which the host adds. `service-provider-schemas.ts` asserts each parsed shape against the type the service writes against, both ways, so the two cannot drift.
|
|
198
|
+
- `readTrackerProviderRequest` checks the envelope and the session (`instance`, `handle`, `account` of `{ id, login | null }` or null, the notification channel's shape). The answer envelope is the Git host's, so a 401 is written off only when the proxy saw it.
|
|
199
|
+
- `createTrackerOutbound` shares `postOutbound` with the other two roles and differs in the callback route (`TRACKER_OUTBOUND_PATH`) and the error class.
|
|
200
|
+
|
|
201
|
+
### Why templates and identifier shapes instead of regular expressions
|
|
202
|
+
|
|
203
|
+
Tracker links differ widely: a key in the path, a key in a query parameter (Jira's `?selectedIssue=`), a bare number, a short code, a server under a path prefix. The maintainer kept the Git host's rule all the same: a regular expression from a package runs on every pasted link, on the server and in the UI, and JavaScript cannot bound its time. Templates with bounded placeholders and three identifier shapes cover those cases, and the matcher compiled from them is the one step 5 of OPE-367 (links in messages) reuses.
|
|
204
|
+
|
|
205
|
+
## Session events
|
|
206
|
+
|
|
207
|
+
`contributes.sessionEvents` (`SessionEventsContribution`, `session-events.ts`) subscribes a package's service to what happens in the user's sessions: `events` is 1 to 4 distinct of `turn-finished`, `turn-failed`, `permission-asked`, `question-asked`, and `content: true` asks for the `transcript` capability. The host posts one `GuestSessionEventRequest` per event per subscribed service to `SESSION_EVENTS_PATH`, from every project, with no window open; the service answers `GuestSessionEventResult`. The server side (`packages/web/server/lib/guests/session-events.js`) decides who is a subscriber from the catalog at delivery time and gates `turn-finished` the way the ready notification is gated. The service-side contract is in `GUEST_SERVICES.md`; `examples/session-events-log` is the reference, run by `scripts/session-events-example.test.ts`.
|
|
208
|
+
|
|
209
|
+
- `sessionEvents` needs `contributes.service` (`invalid-session-events`), its object is `.strict()`, and it joins roles, surfaces and status sources in freeing the service from needing a frame (`runtimeContributions`). A bad shape is `invalid-session-events` too; the message names the field.
|
|
210
|
+
- `transcript` is never declared under `contributes.capabilities`: `requestedGuestCapabilities` adds it from `sessionEvents.content`, and the approval dialog shows it as its own row with the event list under the service row (`PublicSessionEvents`, `toPublicSessionEvents`, on the catalog row as `sessionEvents`).
|
|
211
|
+
- `readSessionEventRequest` checks the envelope, the type, the session, the project, the `linked` list's shape and that a `content` has the event's type; the host built the rest. A content of another type than the event is `null`, not a surprise for the handler.
|
|
212
|
+
- The answer envelope `{ ok: true } | { ok: false, error }` is the one the browser role answers; `sessionEventResultSchema` is `.strict()`, so an answer with extra data is a failed delivery, never a delivered one.
|
|
213
|
+
|
|
214
|
+
### Why metadata rides on the `service` grant and only content gets a capability
|
|
215
|
+
|
|
216
|
+
A service already runs as the user with no sandbox and could read OpenCode's storage itself, so a grant for identifiers would promise a confinement the host cannot give. What the grant can say truthfully is what OpenChamber hands over on its own: the events (recorded in the service grant's scope, so a widened list asks again) and, as a line the user reads separately, the text (`transcript`). The ticket's rule, metadata without an extra grant and content with one, maps to exactly these two.
|
|
217
|
+
|
|
218
|
+
### Why every project is sent and not a host-side project filter
|
|
219
|
+
|
|
220
|
+
A filter the host keeps per project would be a new control on the card and a new store that must follow worktrees to their project; the extension's own settings and storage already hold its configuration, and `event.project.directory` is on every request. The host sends everything and the extension drops the rest; the maintainer chose this over per-project settings in OpenChamber.
|
|
221
|
+
|
|
222
|
+
### Why `turn-finished` waits for the session to want nothing
|
|
223
|
+
|
|
224
|
+
The ticket's example posts a comment on the linked issue when the agent finishes. A parent idle while its subagent still works, a turn that ended by asking a question, a goal loop's intermediate steps and an auto-review's steps are not that moment; the ready notification already decides it with those gates, so the event uses the same ones, and the asked/settled events the server already sees keep the open asks.
|
|
225
|
+
|
|
170
226
|
## Late generate answers
|
|
171
227
|
|
|
172
228
|
`LateGenerateResult`, `ready.features.lateGenerateResults`, the guest notice `generate-late-listen` and the host push `generate-late` live here; `HostClient.onLateGenerateResult` sends the notice on registration and again on every `ready` while a listener exists. The UI host owns keeping: `packages/ui/src/lib/guests/generate-results.ts` holds the outcomes, `PluginPane` decides when a `generate` reply has no frame to go to and registers a listening frame.
|
package/GUEST_SERVICES.md
CHANGED
|
@@ -88,7 +88,7 @@ Parse rules:
|
|
|
88
88
|
- `contributes.service` is optional. A guest without `service` is HTML-only.
|
|
89
89
|
- `service.entry` is a relative path inside the package. Ship compiled JS; the host never compiles TypeScript.
|
|
90
90
|
- `service.runtime` phase 1 accepts only `"host"`.
|
|
91
|
-
- `service.provides` is optional: roles the service stands in for on the host, `"browser"
|
|
91
|
+
- `service.provides` is optional: roles the service stands in for on the host, `"browser"`, `"source-control"`, `"notifications"` and `"tracker"` (below). A service that provides a role needs no `panel.entry` or `background.entry`; the host starts it itself. An extension is a Git host or a tracker, never both: the two would share the id `ext-<panel.id>`.
|
|
92
92
|
- `service.surface` is optional (`true`): the service shows a live picture the host draws in the extension's rail panel and takes the user's input back (below). With `panel.entry` too, that page is docked to one edge of the picture (`panel.dock`: `top`, `bottom`, `left`, or `right`, default `top`; `panel.size` in CSS px across that edge, 24 to 480, default 40); without it, the picture is the whole panel.
|
|
93
93
|
- `service.permissions.sockets` and `service.permissions.exec` are shown to the user in the approval dialog. They describe intent and do not confine the process: a service runs with the user's full access. Declaring `contributes.service` adds the `service` capability to the package's request list; the user approves the whole list once when the package is installed (Settings → Extensions), and the first `serviceRequest` is refused with `NO_SERVICE` until then.
|
|
94
94
|
- The catalog adds `service.socketBindings`: `{ id, candidates, resolved, override }` for this host. The user can override a path in Extensions. Override empty clears it and the next spawn re-resolves.
|
|
@@ -125,6 +125,16 @@ The panel never receives `OPENCHAMBER_SERVICE_TOKEN` and never dials the port it
|
|
|
125
125
|
|
|
126
126
|
VS Code and mobile stay `unsupported` for the guest catalog. They do not spawn services.
|
|
127
127
|
|
|
128
|
+
| Runtime | Extension services |
|
|
129
|
+
|---|---|
|
|
130
|
+
| Web | Supported on the connected OpenChamber server |
|
|
131
|
+
| Electron desktop | Supported through the connected OpenChamber server |
|
|
132
|
+
| VS Code | Unsupported; the extension catalog is unavailable |
|
|
133
|
+
| Hosted mobile | Unsupported; the extension catalog is unavailable |
|
|
134
|
+
| Capacitor mobile | Unsupported; the extension catalog is unavailable |
|
|
135
|
+
|
|
136
|
+
Services approved on a web or desktop instance continue on that server regardless of which clients are connected. Mobile clients do not expose extension setup.
|
|
137
|
+
|
|
128
138
|
## Service process contract
|
|
129
139
|
|
|
130
140
|
Env the host sets:
|
|
@@ -132,7 +142,7 @@ Env the host sets:
|
|
|
132
142
|
- `OPENCHAMBER_SERVICE_PORT` — port to bind on `127.0.0.1`
|
|
133
143
|
- `OPENCHAMBER_SERVICE_TOKEN` — shared secret
|
|
134
144
|
- `OPENCHAMBER_SERVICE_SOCKETS` — JSON map `{ [socketId]: absolutePath }` for every binding that resolved (override or first existing candidate)
|
|
135
|
-
- `OPENCHAMBER_HOST_CALLBACK_URL` — only for a service that provides `source-control`: OpenChamber's callback listener for
|
|
145
|
+
- `OPENCHAMBER_HOST_CALLBACK_URL` — only for a service that provides `source-control` or `notifications`: OpenChamber's callback listener for the requests those roles send out (see below)
|
|
136
146
|
|
|
137
147
|
Inbound auth: every request, including ready, must send:
|
|
138
148
|
|
|
@@ -339,7 +349,7 @@ OpenChamber decides, not the service, what a failure means. A mutation counts as
|
|
|
339
349
|
|
|
340
350
|
### Reaching the forge
|
|
341
351
|
|
|
342
|
-
The service never holds a token. `createSourceControlForge({ env: process.env })` returns `{ request(handle, req) }`, which posts to `${OPENCHAMBER_HOST_CALLBACK_URL}/source-control/outbound` with `Authorization: Bearer <handle>`. `OPENCHAMBER_HOST_CALLBACK_URL` is a separate loopback listener, set only for services with this role.
|
|
352
|
+
The service never holds a token. `createSourceControlForge({ env: process.env })` returns `{ request(handle, req) }`, which posts to `${OPENCHAMBER_HOST_CALLBACK_URL}/source-control/outbound` with `Authorization: Bearer <handle>`. `OPENCHAMBER_HOST_CALLBACK_URL` is a separate loopback listener, set only for services with this role or the `notifications` role.
|
|
343
353
|
|
|
344
354
|
- Request (`SourceControlOutboundRequest`): `{ method: GET|POST|PUT|PATCH|DELETE, path, query?, headers?, body? }`. `path` starts with one `/` and stays on the call's instance origin. `headers` (lower-case names) may be `accept`, `content-type`, `if-none-match` and `range`; OpenChamber sets the rest, the token included, per `token.header` and `token.scheme` (with `basic`, the account's login too). `body` is text up to `SOURCE_CONTROL_OUTBOUND_BODY_MAX` (1 MB).
|
|
345
355
|
- Answer: HTTP 200 with `{ status, headers, body }`, the forge's status (any, 401 included), the headers in `SOURCE_CONTROL_OUTBOUND_RESPONSE_HEADERS` plus `x-ratelimit-*`, and the body as text up to `SOURCE_CONTROL_OUTBOUND_RESPONSE_MAX` (8 MB). Redirects are not followed. `request` resolves with it.
|
|
@@ -353,6 +363,179 @@ VS Code lists no Git hosts from extensions: the guest catalog is unsupported the
|
|
|
353
363
|
|
|
354
364
|
`examples/source-control-provider` is a checked-in provider for a subset of Gitea's REST v1, with a fake forge its tests run against.
|
|
355
365
|
|
|
366
|
+
## Notification channel (`provides: ["notifications"]`)
|
|
367
|
+
|
|
368
|
+
A service with this role delivers the user's notifications, next to the desktop and the phone: Slack, Matrix, a phone automation, a receiver of your own. The role needs `contributes.notifications` in the same manifest, and the manifest needs it only with the role; either alone fails as `invalid-service`. OpenChamber names the channel `ext-<panel.id>` (so `panel.id` is at most `NOTIFICATIONS_GUEST_ID_MAX`, 28), draws its account card in Settings → Integrations from the contribution, and keeps the account: the user connects it there, the token stays in OpenChamber, and the service never sees it.
|
|
369
|
+
|
|
370
|
+
```json
|
|
371
|
+
"service": { "entry": "service/main.js", "runtime": "host", "provides": ["notifications"] },
|
|
372
|
+
"notifications": {
|
|
373
|
+
"label": "Slack",
|
|
374
|
+
"instances": { "kind": "fixed", "origin": "https://slack.com" },
|
|
375
|
+
"token": { "header": "authorization", "scheme": "bearer", "scopes": ["chat:write"], "createPath": "/apps" }
|
|
376
|
+
}
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
| Field | Rules |
|
|
380
|
+
|---|---|
|
|
381
|
+
| `label` | The channel's name on its card, untranslated, 1 to 64 characters |
|
|
382
|
+
| `instances` | `{ kind: "fixed", origin }` when every account lives on one https origin (Slack); `{ kind: "user", label, placeholder? }` when the user types the server at connect (a Matrix homeserver). A typed server is an https origin, or http on this machine for a development receiver; paths, queries and credentials are refused, the service builds the paths |
|
|
383
|
+
| `token` | `{ header: "authorization" \| "private-token", scheme: "bearer" \| "token" \| "basic" \| "none", username?, scopes?, createPath? }`, the Git host's token block: `private-token` takes the bare token (`none`); `basic` needs `username: true`, which makes the card ask for a username beside the token |
|
|
384
|
+
|
|
385
|
+
Approving the extension and connecting an account is the whole switch: there is no setting for the channel on the Notifications page. A channel receives exactly the kinds the user's notification toggles let through (`GUEST_NOTIFICATION_KINDS`: `ready`, `error`, `question`, `permission`, `review`, `goal_complete`, `goal_blocked`, `goal_budget`), after the same debounces and suppressions, and only when no client is visible, unless the user chose to be notified always. Pausing the extension stops it; removing it forgets the account. In enterprise mode the channel gets the kind's generic title and no body, session name or directory, as the phone does, and the role installs only from an allowed repository like every service.
|
|
386
|
+
|
|
387
|
+
### Calls
|
|
388
|
+
|
|
389
|
+
OpenChamber posts `POST /notifications` (`NOTIFICATIONS_PROVIDER_PATH`) with the service bearer, one call at a time:
|
|
390
|
+
|
|
391
|
+
```json
|
|
392
|
+
{ "v": 1, "requestId": "…", "method": "notify", "provider": "ext-slack-bridge",
|
|
393
|
+
"session": { "instance": "https://slack.com", "handle": "…", "account": { "id": "U123", "login": "mona" } },
|
|
394
|
+
"input": { "notification": { "kind": "ready", "title": "…", "body": "…", "tag": "ready-ses_…", "at": 1730000000000,
|
|
395
|
+
"sessionId": "ses_…", "sessionName": "Fix the parser", "directory": "/Users/me/repo", "url": "/?session=ses_…" } } }
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
| Method | Deadline | Answer `data` |
|
|
399
|
+
|---|---|---|
|
|
400
|
+
| `auth.probe` | 15 s | `{ supported, message? }`: whether `session.instance` is a server this channel delivers to. The handle carries no token; `account` is null |
|
|
401
|
+
| `auth.verify` | 15 s | `{ account: { id, login, name? } }`: who the candidate token belongs to; OpenChamber stores the account under `id`. The handle carries the token; `account` is null |
|
|
402
|
+
| `notify` | 10 s | `{ delivered: true }` once the notification is on its way. The handle carries the account's token |
|
|
403
|
+
|
|
404
|
+
Parse the body with `readNotificationsProviderRequest` (`NotificationsProviderRequest`; `null` → HTTP 400) and answer HTTP 200 `{ ok: true, data }` or `{ ok: false, error: { message, status?, code? } }` (`NotificationsProviderResult`). OpenChamber checks answers with `notificationsProviderSchemasV1`; one that does not parse is a failed delivery, never a delivered one. A `status: 401` writes the account off (the card says it needs attention until the user connects again) only when OpenChamber's proxy saw the server answer 401 through the call's handle; a 401 the service reports on its own is a malformed answer.
|
|
405
|
+
|
|
406
|
+
### Reaching the server
|
|
407
|
+
|
|
408
|
+
The service never holds a token. `createNotificationsOutbound({ env: process.env })` returns `{ request(handle, req) }`, which posts to `${OPENCHAMBER_HOST_CALLBACK_URL}/notifications/outbound` with `Authorization: Bearer <handle>`; `req` is `{ method, path, query?, headers?, body? }`, `path` on the account's instance origin, `headers` limited to `accept`, `content-type`, `if-none-match` and `range`, the body up to 1 MB. OpenChamber adds the token per `token.header` and `token.scheme` (with `basic`, the account's login too), keeps the request on the origin, and relays `{ status, headers, body }`. A refusal (an expired handle, a path off the origin) is a `NotificationsOutboundError`. The handle ends with the call; never store it.
|
|
409
|
+
|
|
410
|
+
Delivery is at most once: a failed `notify` (a timeout, a non-200, `ok: false`, a body that does not parse) is logged once a minute per channel and never retried. OpenChamber never logs a notification's text. The service runs on demand and stops after `NOTIFICATIONS_PROVIDER_IDLE_MS` (ten minutes) without a call.
|
|
411
|
+
|
|
412
|
+
Authentication and notification calls share one ordered queue per channel, capped at 200 outstanding calls. New calls beyond the cap are dropped with a rate-limited warning for notifications or a busy error for Connect. Pausing the extension cancels its in-flight call and drops queued calls. Disconnect supersedes an earlier pending Connect; a delayed 401 can invalidate only the account used by that call. Enterprise mode is checked again immediately before sending, including for notifications that were queued before it was enabled.
|
|
413
|
+
|
|
414
|
+
`examples/notification-webhook` is the reference: a channel that posts every notification to a receiver the user names, with the receiver to try it against.
|
|
415
|
+
|
|
416
|
+
## Tracker (`provides: ["tracker"]`)
|
|
417
|
+
|
|
418
|
+
A service with this role is a work item tracker next to Linear: YouTrack, Jira, Plane, a tracker of your own. Its items appear wherever Linear's do: the picker and the composer chips, New Worktree, the linked items of a session, the live states of tracked items, and the account card in Settings → Integrations under Extension accounts. The role needs `contributes.tracker` in the same manifest, and the manifest needs it only with the role; either alone fails as `invalid-service`. OpenChamber names the tracker `ext-<panel.id>` (so `panel.id` is at most `TRACKER_GUEST_ID_MAX`, 28), lists it from the contribution without starting the service, and keeps the accounts: the user connects them on the card, tokens stay in OpenChamber, and the service never sees one.
|
|
419
|
+
|
|
420
|
+
```json
|
|
421
|
+
"service": { "entry": "service/main.js", "runtime": "host", "provides": ["tracker"] },
|
|
422
|
+
"tracker": {
|
|
423
|
+
"wire": 1,
|
|
424
|
+
"label": "YouTrack",
|
|
425
|
+
"instances": { "kind": "user", "label": "Server address", "placeholder": "https://youtrack.example.com" },
|
|
426
|
+
"auth": {
|
|
427
|
+
"token": { "header": "authorization", "scheme": "bearer", "createPath": "/users/me?tab=account-security" },
|
|
428
|
+
"oauth": { "authorizeUrl": "/hub/api/rest/oauth2/auth", "tokenUrl": "/hub/api/rest/oauth2/token", "scopes": ["YouTrack"] }
|
|
429
|
+
},
|
|
430
|
+
"capabilities": { "stateUpdate": true, "labels": true, "priority": true, "filters": { "people": true }, "projectMapping": true, "sessionComments": true },
|
|
431
|
+
"stateFilters": ["open", "all", "todo", "in-progress", "done", "inReview"],
|
|
432
|
+
"identifier": "key-number",
|
|
433
|
+
"urls": { "item": ["/issue/{id}", "/{segments+}/issue/{id}", "/agiles/{segment}/current?issue={id}"] }
|
|
434
|
+
}
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
| Field | Rules |
|
|
438
|
+
|---|---|
|
|
439
|
+
| `wire` | `1` |
|
|
440
|
+
| `label` | The tracker's name on its card and in the picker, untranslated, 1 to 64 characters |
|
|
441
|
+
| `instances` | `{ kind: "fixed", origin }` when every account lives on one https origin; `{ kind: "user", label, placeholder? }` when the user types the server at connect (a self-hosted server, a per-company site). A typed server is an https origin, or http on this machine for a development server; paths, queries and credentials are refused, the service builds the paths. The accounts of one tracker live on one server; the API and the item pages share that origin |
|
|
442
|
+
| `auth` | `token`, `oauth`, or both. `token` is the Git host's token block (`header`, `scheme`, `username?` with `basic`, `scopes?`, `createPath?`). `oauth` is `{ authorizeUrl, tokenUrl, scopes? }`: https URLs, or paths on the instance for a `user` tracker; OpenChamber runs PKCE S256 with a client id (and secret) the user enters on the card, and keeps the tokens |
|
|
443
|
+
| `capabilities` | Flags, each off when left out: `multipleAccounts`, `stateUpdate` (also `states.list`), `children`, `labels`, `priority`, `filters.people`, `filters.priority`, `projectMapping` (also `containers.list`), `sessionComments` (also `comments.create`). Host access for SDK guests is not declared: the host keeps it off, because the service itself never sees a token |
|
|
444
|
+
| `stateFilters` | In menu order, 2 to 16 unique ids: `open`, `all`, then the neutral categories you answer (`triage`, `backlog`, `todo`, `in-progress`, `done`, `canceled`) and your own ids (`inReview`, `^[a-z][a-zA-Z0-9-]{0,31}$`). What `items.list` is asked for |
|
|
445
|
+
| `identifier` | The shape of an item's key: `key-number` (`PROJ-42`), `number` (`123`), `short-code` (letters and digits). OpenChamber compares identifiers case-insensitively everywhere and sends them upper-cased |
|
|
446
|
+
| `urls.item` | 1 to 8 link templates: `/`, segments that are each a literal (`[A-Za-z0-9._~-]`) or a placeholder of `TRACKER_URL_TOKENS` (`{id}` the identifier, `{segment}` any one segment, `{segments+}` one or more, at most once), and optionally `?name={id}` when the identifier travels in the query. `{id}` once per template, in the path or the query. OpenChamber compiles them against the instance into the pattern that recognises a pasted link, on the server and in every client; an extension never supplies a regular expression |
|
|
447
|
+
|
|
448
|
+
What the user sees: the card under Extension accounts asks for the server address first when `instances.kind` is `user`, then offers the ways in you declared: a token field (and a username for `basic`) with a link to `createPath`, and, for `oauth`, the client id and secret fields with the redirect URL to register, then a browser sign-in. Connected accounts, switching between them (`multipleAccounts`), project mapping (`projectMapping`) and the session comments switch (`sessionComments`) follow the capabilities. Pausing the extension takes the tracker out of every surface and keeps the accounts; removing it forgets them. In enterprise mode the role installs only from an allowed repository like every service; its reads carry no conversation content and its comments carry a fixed label and a session link, so nothing else is stripped.
|
|
449
|
+
|
|
450
|
+
### Calls
|
|
451
|
+
|
|
452
|
+
OpenChamber posts `POST /tracker` (`TRACKER_PROVIDER_PATH`) with the service bearer. Reads may overlap, since the picker, the preview and the board read at once; each call carries its own handle.
|
|
453
|
+
|
|
454
|
+
```json
|
|
455
|
+
{ "v": 1, "requestId": "…", "method": "items.list", "provider": "ext-youtrack",
|
|
456
|
+
"session": { "instance": "https://youtrack.example.com", "handle": "…", "account": { "id": "u-1", "login": "mona" } },
|
|
457
|
+
"input": { "query": "login", "state": "todo", "people": "me", "priority": "all", "containerId": null } }
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
| Method | Deadline | `input` | Answer `data` |
|
|
461
|
+
|---|---|---|---|
|
|
462
|
+
| `auth.probe` | 15 s | `{}` | `{ supported, message? }`: whether `session.instance` is a server this tracker works with. The handle carries no credential; `account` is null |
|
|
463
|
+
| `auth.verify` | 15 s | `{}` | `{ account: { id, login, name?, avatarUrl? } }`: who the credential belongs to (a pasted token, or the OAuth bearer OpenChamber minted). OpenChamber stores the account under `id`. `account` is null |
|
|
464
|
+
| `items.list` | 20 s | `{ query?, cursor?, state, people, priority, containerId }` | `{ items, cursor, hasMore }`: up to `TRACKER_PROVIDER_LIST_MAX` (100) `TrackerProviderItemSummary`, an opaque cursor for the next page or null. A query that is a link your templates claim, or an identifier of your declared shape, is read through `items.get` first and answered as a page of one; a bare word of your identifier shape that `items.get` answers null for reaches `items.list` as a search after all |
|
|
465
|
+
| `items.get` | 20 s | `{ id }` | `{ item }`: a `TrackerProviderItem` with description, comments, children, or null for an unknown id. `id` is an item's `id` as you answered it, or its identifier (`PROJ-1`, upper-cased) when OpenChamber resolves a pasted link or key; answer both |
|
|
466
|
+
| `items.updateState` | 30 s | `{ id, stateId }` | `{ item }` after the change. A write: the handle may send `POST`, `PUT`, `PATCH`, `DELETE` |
|
|
467
|
+
| `items.liveSummaries` | 20 s | `{ identifiers }` (up to 50) | `{ items: [{ identifier, title, state: { name, category } }] }`; identifiers you do not know are left out |
|
|
468
|
+
| `states.list` | 20 s | `{ containerId }` | `{ states: [{ id, name, category, position }] }` |
|
|
469
|
+
| `containers.list` | 20 s | `{}` | `{ containers: [{ id, key, name }] }`: the projects, teams or spaces items belong to, for project mapping |
|
|
470
|
+
| `comments.create` | 30 s | `{ identifier, body }` | `{ comment: { id } }`, or `{ comment: null }` for an unknown identifier. A write. `body` is Markdown OpenChamber wrote: `[OpenChamber session completed](https://…/?session=…)` |
|
|
471
|
+
|
|
472
|
+
Items answer in the neutral shapes without `provider`; OpenChamber adds it. An item's `url` is an absolute http(s) URL. `state.category` is one of the six categories or null; `priority` one of `none`, `urgent`, `high`, `medium`, `low`. Parse the body with `readTrackerProviderRequest` (`TrackerProviderRequest`; `null` → HTTP 400) and answer HTTP 200 `{ ok: true, data }` or `{ ok: false, error: { message, status?, code? } }` (`TrackerProviderResult`). OpenChamber checks answers with `trackerProviderSchemasV1`, bounded in count and length; one that does not parse is `502 MALFORMED_PROVIDER_RESPONSE`, never an empty list. A `status: 401` writes the account off (the card says it needs attention until the user connects again) only when OpenChamber's proxy saw the server answer 401 through the call's handle; a 401 the service reports on its own is a malformed answer.
|
|
473
|
+
|
|
474
|
+
### Reaching the server
|
|
475
|
+
|
|
476
|
+
The service never holds a credential. `createTrackerOutbound({ env: process.env })` returns `{ request(handle, req) }`, which posts to `${OPENCHAMBER_HOST_CALLBACK_URL}/tracker/outbound` with `Authorization: Bearer <handle>`; `req` is `{ method, path, query?, headers?, body? }`, `path` on the account's instance origin, `headers` limited to `accept`, `content-type`, `if-none-match` and `range`, the body up to 1 MB. OpenChamber adds the credential (the token per `auth.token`, or `Bearer <access token>` for OAuth, renewed before it lapses), keeps the request on the origin, and relays `{ status, headers, body }`. A read's handle sends `GET` only; a write through it is refused with 403 `METHOD_NOT_ALLOWED`. A refusal is a `TrackerOutboundError`. The handle ends with the call; never store it.
|
|
477
|
+
|
|
478
|
+
Session comments: when the user turns them on, OpenChamber posts `started` when a session starts from one of your items, then `completed` or `failed` once each when that session's turn ends or fails, each through `comments.create`, with a link only to an origin other people can open. The service runs on demand and stops after `TRACKER_PROVIDER_IDLE_MS` (ten minutes) without a call.
|
|
479
|
+
|
|
480
|
+
`examples/tracker-provider` is the reference: a tracker over a fake server you run beside it, run by `scripts/tracker-provider-example.test.ts`.
|
|
481
|
+
|
|
482
|
+
## Session events (`contributes.sessionEvents`)
|
|
483
|
+
|
|
484
|
+
A service can be told what happens in the user's sessions without a window being open. Declare which events it wants next to the service:
|
|
485
|
+
|
|
486
|
+
```json
|
|
487
|
+
"service": { "entry": "service/main.js", "runtime": "host" },
|
|
488
|
+
"sessionEvents": { "events": ["turn-finished", "question-asked"], "content": true }
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
`events` lists 1 to 4 distinct types:
|
|
492
|
+
|
|
493
|
+
| Type | When |
|
|
494
|
+
|---|---|
|
|
495
|
+
| `turn-finished` | The session went idle with nothing waiting: no running subagent, no open permission, form or page, no goal or auto-review step still driving it. A turn the user stopped emits nothing |
|
|
496
|
+
| `turn-failed` | The turn ended in an error |
|
|
497
|
+
| `permission-asked` | The agent asked a permission the user has to answer: sent 500 ms after the ask, only when it is still open and not answered by the session's own policy |
|
|
498
|
+
| `question-asked` | The agent asked the user a question, through a form or a page it shows and waits on. An MCP elicitation, which belongs to no session, is not sent |
|
|
499
|
+
|
|
500
|
+
`sessionEvents` needs `contributes.service` (`invalid-session-events` otherwise) and, like a role, lets the service go without a panel or background entry. Hosts older than this feature drop the key at parse and send nothing, so declare an `engines.openchamber` floor.
|
|
501
|
+
|
|
502
|
+
Approving the package is the subscription: the dialog lists the events under the service line. With `content: true` the manifest also asks for the `transcript` capability, shown as a line of its own, and the service receives what the agent said or asked; without it, identifiers only. The grant scope records the event list and the content flag, so a version that adds a type or starts asking for content shows Needs approval and receives nothing until the user approves again. In enterprise mode `transcript` is gated with `service`.
|
|
503
|
+
|
|
504
|
+
### Wire
|
|
505
|
+
|
|
506
|
+
OpenChamber posts `POST /session-events` (`SESSION_EVENTS_PATH`) with the service bearer, one request per event per subscribed service. The body is a `GuestSessionEventRequest`:
|
|
507
|
+
|
|
508
|
+
```json
|
|
509
|
+
{
|
|
510
|
+
"v": 1,
|
|
511
|
+
"requestId": "…",
|
|
512
|
+
"event": {
|
|
513
|
+
"id": "evt_…", "type": "turn-finished", "at": 1730000000000,
|
|
514
|
+
"session": { "id": "ses_…", "parentId": null },
|
|
515
|
+
"project": { "directory": "/Users/me/repo" },
|
|
516
|
+
"linked": [{ "id": "linear:OPE-367", "kind": "linear", "url": "https://…", "provider": "linear", "identifier": "OPE-367" }],
|
|
517
|
+
"permission": { "requestId": "perm_…" }
|
|
518
|
+
},
|
|
519
|
+
"content": { "type": "turn-finished", "sessionTitle": "Fix the parser", "reply": "Done. …", "replyTruncated": false }
|
|
520
|
+
}
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
- `event.id` is OpenCode's event id (or one OpenChamber made for an event it synthesized), `at` epoch milliseconds, `session.parentId` set for a subagent's session, `project.directory` the session's directory or `null` when OpenChamber does not know it. `linked` is what the user attached to the session: ids, kinds, urls, the tracker's `provider` and `identifier`, a Git host's `number`, never a title. `permission` comes with `permission-asked` only.
|
|
524
|
+
- `content` comes only with the `transcript` grant, typed by `type`: `turn-finished` carries `sessionTitle`, `reply` (cut at `SESSION_EVENT_REPLY_MAX`, 64 000 characters, with `replyTruncated`); `turn-failed` carries `error { type, message }` and no reply; `permission-asked` carries `action`, `resources`, `message`; `question-asked` carries `ask`, a form (`id`, `title`, `fields[{ title, description }]`) or a page (`id`, `title`).
|
|
525
|
+
- Parse the body with `readSessionEventRequest` (zod-free; `null` is a body that is not a session event of this wire version, answer HTTP 400) and answer HTTP 200 `{ "ok": true }` once the event is taken, or `{ "ok": false, "error": "…" }` (`GuestSessionEventResult`, `error` up to `SESSION_EVENT_ERROR_MAX`). OpenChamber parses the answer with `sessionEventResultSchema`; anything else is a failed delivery.
|
|
526
|
+
|
|
527
|
+
### Delivery
|
|
528
|
+
|
|
529
|
+
- Host sessions in every project are sent, each with its directory. Sessions inside isolated spaces are not delivered to extensions yet. Which projects an extension acts on is its own choice: compare `event.project.directory` with your own settings and answer `{ ok: true }` for the rest.
|
|
530
|
+
- The first event starts the service; `SESSION_EVENTS_IDLE_MS` (ten minutes) without one stops it, and the next event starts it again. Events that arrive while it starts wait, at most `SESSION_EVENT_PENDING_MAX` (200), newer ones dropped with one warning.
|
|
531
|
+
- Event preparation also admits at most one oldest event and `SESSION_EVENT_PENDING_MAX` later events before starting reads. Excess announcements are dropped, while open asks and their answers remain tracked. Shutdown discards prepared events and prevents further reads.
|
|
532
|
+
- One request in flight per service, in order. A delivery has `SESSION_EVENT_TIMEOUT_MS` (10 s). A failed delivery (a timeout, a non-200 status, `ok: false`, a body that does not parse) drops that service's events for the next 30 s and logs the `error` text; OpenChamber never logs an event's content.
|
|
533
|
+
- At most once, with no persistence: an event that happened while OpenChamber was down, while the extension was paused, or during that backoff is gone. A paused extension, a withdrawn approval and a removal drop what waited for them.
|
|
534
|
+
- Two idle events for one turn end (OpenCode announces it twice) are one `turn-finished`.
|
|
535
|
+
- The service never receives a token, a callback URL or a way back into the session through this path.
|
|
536
|
+
|
|
537
|
+
`examples/session-events-log` is the reference: a service-only extension that journals every event to a file.
|
|
538
|
+
|
|
356
539
|
## Shared surface (`surface: true`)
|
|
357
540
|
|
|
358
541
|
A surface lets a person watch what an agent is working in and step in for a manual step. It is not about browsers: a service that drives a browser, a simulator, or a desktop app can show one. The host draws it in the extension's rail panel (a canvas; the extension runs no code there), sends the user's pointer, keyboard, and paste back, and decides who is in control.
|
|
@@ -366,7 +549,7 @@ Plain HTTP on the service loopback, same bearer as everything else:
|
|
|
366
549
|
| Call | Body / answer |
|
|
367
550
|
|---|---|
|
|
368
551
|
| `GET /surface/frame?after=<seq>&wait=<ms>` | 200 with `image/jpeg` or `image/png` bytes and headers `x-surface-seq`, `x-surface-width`, `x-surface-height`, optional `x-surface-title`, optional `x-surface-agent-active: 1` while your own automation is driving; 204 when nothing newer than `seq` arrived within `wait` ms. Sequence numbers start at 1, so `after=0` is "the current picture, now". The host asks one frame at a time per viewer; a viewer that draws slowly skips frames rather than queueing them. Frames up to `SURFACE_FRAME_MAX_BYTES` (8 MB). |
|
|
369
|
-
| `POST /surface/input` | `{ events: SurfaceInputEvent[] }`: `pointer` (`down`/`up`/`move`, `x`, `y` in frame pixels, `button`, `buttons`, `modifiers`), `wheel` (`x`, `y`, `deltaX`, `deltaY`, `modifiers`), `key` (`down`/`up`, `key`, `code`, `modifiers`), `text` (pasted or composed text). Parse with `readSurfaceInputBatch`; `null` → 400. Sent only while the user holds control. Headers `x-surface-viewer` (the viewer's id) and `x-surface-frame-seq` (your own sequence number of the frame that viewer last drew, `0` before its first) say where the input came from; answer 409 to refuse input made on a picture you no longer show, and the host tells the viewer it was not applied. |
|
|
552
|
+
| `POST /surface/input` | `{ events: SurfaceInputEvent[] }`: `pointer` (`down`/`up`/`move`, `x`, `y` in frame pixels, `button`, `buttons`, `modifiers`), `wheel` (`x`, `y`, `deltaX`, `deltaY`, `modifiers`), `key` (`down`/`up`, `key`, `code`, `modifiers`, and `keyCode`, the legacy code the viewer's browser computed for the user's keyboard layout; older hosts omit it, so derive one from `key` and `code` then), `text` (pasted or composed text). Parse with `readSurfaceInputBatch`; `null` → 400. Sent only while the user holds control. Headers `x-surface-viewer` (the viewer's id) and `x-surface-frame-seq` (your own sequence number of the frame that viewer last drew, `0` before its first) say where the input came from; answer 409 to refuse input made on a picture you no longer show, and the host tells the viewer it was not applied. |
|
|
370
553
|
| `POST /surface/control` | `{ controller: "none" \| "agent" \| "user", viewer? }` whenever control changes; `viewer` is the controlling viewer's id when `controller` is `user`. Parse with `readSurfaceControlNotice`. Advisory. |
|
|
371
554
|
| `POST /surface/resize` | `{ width, height }` the panel can show, in device pixels. Answer `{ width, height }` you settled on, or 400 to keep your size. Parse with `readSurfaceResizeRequest`. |
|
|
372
555
|
| `GET /surface/clipboard` | `{ text }`: what the user copied inside the surface. The host asks after a copy chord and puts it on the user's clipboard. |
|
package/README.md
CHANGED
|
@@ -96,9 +96,9 @@ Git installs also accept SSH addresses such as `git@github.com:owner/extension.g
|
|
|
96
96
|
- `actions` is optional: menu entries on messages (`where: "message"`, optionally only `roles: ["assistant"]`) and on sessions (`where: "session"`). By default, picking one opens your page with that message or session in `ctx.item` (`kind: "message"` with the text, or `kind: "session"`; add `payload: ["messages"]` to get the conversation too). Set `mode: "background"` to call `onAction` without opening UI, as shown below. Up to 8.
|
|
97
97
|
- `commands` is optional: slash commands for the chat box, up to 8. `/task DEMO-2` calls your `host.onResolve` handler instead of the model; return a chip to attach it, or `null` for nothing. A name the app already has is ignored.
|
|
98
98
|
- `tools` is optional: how your tool calls look in the chat, up to 16, no code. `match` is the tool name OpenCode reports (`mcp.tasks.*` matches every tool under that prefix); `name` and `icon` (a Remixicon name or an SVG inside the folder, like `panel.icon`) set the header, `title` and `subtitle` are templates like `{input.id}` or `{output.total} open`, and `output` picks the body: `text`, `json`, `markdown`, `code` (with `language`), or `table` (with `columns`, rows from the output array or `output.items`). Leave `output` out to keep the app's own detection.
|
|
99
|
-
- `capabilities` lists what needs the user's approval: `prompt` to send messages, `sessions` to create sessions and worktrees, `files` to read and write inside the open project, `model` for one-off text generation with the user's Small Model (`host.generate`, no session involved). An `integration` adds `network`, a `service` adds `service`, `filesystem` patterns (like `["~/.config/opencode/opencode.json"]`) add `filesystem`, which lets `readFile`, `writeFile`, `listDir`, and `stat` reach those paths outside the project,
|
|
99
|
+
- `capabilities` lists what needs the user's approval: `prompt` to send messages, `sessions` to create sessions and worktrees, `files` to read and write inside the open project, `model` for one-off text generation with the user's Small Model (`host.generate`, no session involved). An `integration` adds `network`, a `service` adds `service`, `filesystem` patterns (like `["~/.config/opencode/opencode.json"]`) add `filesystem`, which lets `readFile`, `writeFile`, `listDir`, and `stat` reach those paths outside the project, a session action with `payload: ["messages"]` adds `conversation`, and `sessionEvents` with `content: true` adds `transcript` (what the agent said or asked, delivered to the service). The user approves the whole list once at install. Calls outside it fail with `NOT_GRANTED`.
|
|
100
100
|
- `integration` is optional. It adds a card at Settings → Integrations. `token` takes a pasted API token (`scheme: "bearer"` for `Authorization: Bearer`, `"basic"` for a username and token pair as Jira Cloud wants), `oauth` runs an authorize flow with a pasted client id, and `host: { "provider": "linear" }` reuses the Linear account already connected in OpenChamber. The page never sees the token; OpenChamber makes the calls through `host.request`.
|
|
101
|
-
- `service` is optional. It declares a local process OpenChamber starts next to the extension. It runs with the user's full access and no sandbox, so declare one only when the page cannot do the job. See [GUEST_SERVICES.md](./GUEST_SERVICES.md). With `provides: ["browser"]` the service can stand in for the agent's browser, so agents browse on the server with no desktop app open; the user picks it in Settings → OpenChamber Tools. With `surface: true` it shows a live picture in the rail that the user can watch and take over, and hand back to the agent; add `panel.entry` (with `panel.dock` and `panel.size`) for your own controls docked beside it.
|
|
101
|
+
- `service` is optional. It declares a local process OpenChamber starts next to the extension. It runs with the user's full access and no sandbox, so declare one only when the page cannot do the job. See [GUEST_SERVICES.md](./GUEST_SERVICES.md). With `provides: ["browser"]` the service can stand in for the agent's browser, so agents browse on the server with no desktop app open; the user picks it in Settings → OpenChamber Tools. With `surface: true` it shows a live picture in the rail that the user can watch and take over, and hand back to the agent; add `panel.entry` (with `panel.dock` and `panel.size`) for your own controls docked beside it. With `contributes.sessionEvents` beside it, OpenChamber tells the service when a turn finishes or fails and when the agent asks permission or a question, in every project and with no window open; `content: true` adds the text of each (the `transcript` capability). With `provides: ["notifications"]` and `contributes.notifications`, the service delivers the user's notifications next to the desktop and the phone; the user connects the account in Settings → Integrations and the token stays in OpenChamber. With `provides: ["tracker"]` and `contributes.tracker`, the service is a work item tracker next to Linear: its items appear in the picker, the composer, New Worktree and linked items, the user connects accounts on its card under Extension accounts, and tokens stay in OpenChamber. An extension is a Git host or a tracker, never both.
|
|
102
102
|
|
|
103
103
|
## Actions without opening a panel
|
|
104
104
|
|
package/dist/host.d.ts
CHANGED
|
@@ -139,7 +139,7 @@ export type HostClient = {
|
|
|
139
139
|
* for the next listener.
|
|
140
140
|
*/
|
|
141
141
|
onLateGenerateResult: (listener: (result: LateGenerateResult) => void) => () => void;
|
|
142
|
-
/**
|
|
142
|
+
/** Count behind this guest's rail icon (0 to `GUEST_BADGE_MAX`): a dot on the icon, the number in its tooltip; `null` clears it. Opening the panel clears it too. */
|
|
143
143
|
setBadge: (count: number | null) => Promise<void>;
|
|
144
144
|
/**
|
|
145
145
|
* The height the guest's content needs, in CSS px. On the Work Status
|
package/dist/index.d.ts
CHANGED
|
@@ -11,8 +11,10 @@ export { GUEST_STATUS_SOURCE_INTERVAL_DEFAULT, GUEST_STATUS_SOURCE_INTERVAL_MAX,
|
|
|
11
11
|
export type { GuestStatusAction, GuestStatusActionEvent, GuestStatusChartUnit, GuestStatusChip, GuestStatusContent, GuestStatusRow, GuestStatusTone, GuestStatusView, StatusSectionSource, } from './status-view.ts';
|
|
12
12
|
export type { OpenChamberManifestApiVersion } from './api-version.ts';
|
|
13
13
|
export { compareOpenChamberVersions, hostMeetsOpenChamberEngine, openChamberEngineMinimum, OPENCHAMBER_ENGINE_PATTERN, parseOpenChamberVersion, } from './host-version.ts';
|
|
14
|
-
export { DECLARED_GUEST_CAPABILITIES, GUEST_ACTIONS_MAX, GUEST_ACTION_LABEL_MAX, GUEST_CAPABILITIES, GUEST_COMMANDS_MAX, GUEST_COMMAND_DESCRIPTION_MAX, GUEST_COMMAND_NAME, GUEST_TOOLS_MAX, GUEST_TOOL_COLUMNS_MAX, GUEST_TOOL_COLUMN_MAX, GUEST_TOOL_LANGUAGE_MAX, GUEST_TOOL_MATCH, GUEST_TOOL_MATCH_MAX, GUEST_TOOL_NAME_MAX, GUEST_TOOL_OUTPUTS, GUEST_TOOL_TEMPLATE_MAX, GUEST_TOOL_TEMPLATE_VALUE_MAX, guestActionsNeedConversation, GUEST_FILESYSTEM_PATTERN_MAX, GUEST_FILESYSTEM_PATTERNS_MAX, GUEST_ORIGINS_MAX, GUEST_SERVICE_PROVIDES, GUEST_SURFACE_DOCKS, GUEST_SURFACE_DOCK_DEFAULT, GUEST_SURFACE_DOCK_SIZE_DEFAULT, GUEST_SURFACE_DOCK_SIZE_MAX, GUEST_SURFACE_DOCK_SIZE_MIN, GUEST_STATUS_SECTION_HEIGHT_DEFAULT, GUEST_STATUS_SECTION_HEIGHT_MAX, GUEST_STATUS_SECTION_HEIGHT_MIN, GUEST_STATUS_SECTION_TITLE_MAX, clampStatusSectionHeight, HOST_LINEAR_API_ORIGIN, isGuestFilesystemPattern, hasGuestCapability, hasGuestPage, isGuestApproved, isGuestPackageSvgIcon, requestedGuestCapabilities, resolveAttachEntry, resolvePageEntry, resolveStatusSectionEntry, resolveStatusSectionSource, resolveAttachMode, resolveIntegrationApi, resolveIntegrationAuth, serviceProvides, SOURCE_CONTROL_DEFAULT_INSTANCES_MAX, SOURCE_CONTROL_GUEST_ID_MAX, SOURCE_CONTROL_URL_TEMPLATES_MAX, SOURCE_CONTROL_URL_TEMPLATE_MAX, SOURCE_CONTROL_URL_TOKENS, sourceControlProviderIdOf, toPublicService, toPublicSourceControl, toPublicIntegration, } from './manifest.ts';
|
|
15
|
-
export
|
|
14
|
+
export { DECLARED_GUEST_CAPABILITIES, GUEST_ACTIONS_MAX, GUEST_ACTION_LABEL_MAX, GUEST_CAPABILITIES, GUEST_COMMANDS_MAX, GUEST_COMMAND_DESCRIPTION_MAX, GUEST_COMMAND_NAME, GUEST_TOOLS_MAX, GUEST_TOOL_COLUMNS_MAX, GUEST_TOOL_COLUMN_MAX, GUEST_TOOL_LANGUAGE_MAX, GUEST_TOOL_MATCH, GUEST_TOOL_MATCH_MAX, GUEST_TOOL_NAME_MAX, GUEST_TOOL_OUTPUTS, GUEST_TOOL_TEMPLATE_MAX, GUEST_TOOL_TEMPLATE_VALUE_MAX, guestActionsNeedConversation, GUEST_FILESYSTEM_PATTERN_MAX, GUEST_FILESYSTEM_PATTERNS_MAX, GUEST_ORIGINS_MAX, GUEST_SERVICE_PROVIDES, GUEST_SURFACE_DOCKS, GUEST_SURFACE_DOCK_DEFAULT, GUEST_SURFACE_DOCK_SIZE_DEFAULT, GUEST_SURFACE_DOCK_SIZE_MAX, GUEST_SURFACE_DOCK_SIZE_MIN, GUEST_STATUS_SECTION_HEIGHT_DEFAULT, GUEST_STATUS_SECTION_HEIGHT_MAX, GUEST_STATUS_SECTION_HEIGHT_MIN, GUEST_STATUS_SECTION_TITLE_MAX, clampStatusSectionHeight, HOST_LINEAR_API_ORIGIN, isGuestFilesystemPattern, hasGuestCapability, hasGuestPage, isGuestApproved, isGuestPackageSvgIcon, NOTIFICATIONS_GUEST_ID_MAX, notificationProviderIdOf, requestedGuestCapabilities, resolveAttachEntry, resolvePageEntry, resolveStatusSectionEntry, resolveStatusSectionSource, resolveAttachMode, resolveIntegrationApi, resolveIntegrationAuth, serviceProvides, SOURCE_CONTROL_DEFAULT_INSTANCES_MAX, SOURCE_CONTROL_GUEST_ID_MAX, SOURCE_CONTROL_URL_TEMPLATES_MAX, SOURCE_CONTROL_URL_TEMPLATE_MAX, SOURCE_CONTROL_URL_TOKENS, sourceControlProviderIdOf, toPublicNotifications, toPublicService, toPublicSessionEvents, toPublicSourceControl, toPublicIntegration, toPublicTracker, TRACKER_BASE_STATE_FILTERS, TRACKER_GUEST_ID_MAX, TRACKER_IDENTIFIER_KINDS, TRACKER_STATE_FILTERS_MAX, TRACKER_URL_TEMPLATES_MAX, TRACKER_URL_TEMPLATE_MAX, TRACKER_URL_TOKENS, isTrackerUrlTemplate, trackerProviderIdOf, } from './manifest.ts';
|
|
15
|
+
export { GUEST_SESSION_EVENT_TYPES, SESSION_EVENT_ERROR_MAX, SESSION_EVENT_PENDING_MAX, SESSION_EVENT_REPLY_MAX, SESSION_EVENT_TIMEOUT_MS, SESSION_EVENTS_IDLE_MS, SESSION_EVENTS_PATH, SESSION_EVENTS_WIRE_VERSION, isGuestSessionEventType, readSessionEventRequest, } from './session-events.ts';
|
|
16
|
+
export type { GuestPermissionAskedContent, GuestQuestionAskedContent, GuestSessionEventContent, GuestSessionEventRequest, GuestSessionEventResult, GuestSessionEventType, GuestSessionLinkedItem, GuestTurnFailedContent, GuestTurnFinishedContent, PublicSessionEvents, SessionEventsContribution, } from './session-events.ts';
|
|
17
|
+
export type { GuestServiceProvides, GuestSurfaceDock, ServiceContribution, ServicePermissions, GuestActionContribution, GuestActionPayload, GuestActionRole, GuestActionWhere, GuestCommandContribution, GuestToolContribution, GuestToolOutput, AttachContribution, AttachContributionObject, AttachMode, DeclaredGuestCapability, GuestAuthorization, GuestCapability, IntegrationAuth, IntegrationContribution, IntegrationHost, IntegrationHostProvider, IntegrationOAuth, IntegrationOAuthAccount, IntegrationSettingField, IntegrationToken, IntegrationTokenScheme, NotificationsContribution, PublicNotifications, PublicTracker, TrackerCapabilities, TrackerContribution, TrackerIdentifierKind, TrackerOAuth, TrackerTokenAuth, OpenChamberContributes, OpenChamberEngines, OpenChamberManifest, PanelContribution, BackgroundContribution, PageContribution, StatusSectionContribution, ParseManifestErrorCode, ParseManifestFailure, ParseManifestResult, ParseManifestSuccess, PublicService, PublicServicePermissions, PublicSourceControl, SourceControlCapabilities, SourceControlContribution, SourceControlMergeMethod, PublicGuestCapabilities, PublicIntegration, PublicIntegrationToken, PublicSocketBinding, ResolvedGuestApi, SocketBinding, SocketPlatform, } from './manifest.ts';
|
|
16
18
|
export { GUEST_FILE_EDITORS_MAX, GUEST_FILE_EDITOR_CONTENT_MAX, GUEST_FILE_EDITOR_PATTERNS_MAX, GUEST_FILE_EDITOR_PATTERN_MAX, GUEST_FILE_EDITOR_TITLE_MAX, GUEST_FILE_EDITOR_VERSION_MAX, createFileSaveTracker, fileEditorPayloadSize, isFileEditorPattern, matchesFileEditorPattern, } from './file-editor.ts';
|
|
17
19
|
export type { FileEditorChange, FileEditorContentKind, FileEditorContribution, FileEditorDocument, FileEditorSnapshot, FileSnapshotPurpose, FileSnapshotRequest, FileSnapshotResultPayload, } from './file-editor.ts';
|
|
18
20
|
export { connectHost, HostRequestError } from './host.ts';
|
|
@@ -22,7 +24,7 @@ export type { GuestPopoverAnchor, GuestPopoverClosedEvent, GuestPopoverContext,
|
|
|
22
24
|
export { clampAttachRequest, clampBadgeCount, clampFrameHeight, isGuestCommitSha, GUEST_COMMIT_SHA, clampPromptRequest, clampStartSessionRequest, guestFileScope, isAttachData, isJsonValue, isFileListResult, isFileReadResult, isFileStatResult, isGenerateResult, isFileWriteResult, isGuestFilePath, isGuestAttachItem, isGuestMessageItem, isGuestSessionItem, isServiceStatusResult, isGuestRequestPath, isGuestRequestResult, isHostRequestErrorCode, isPromptResult, isStartSessionResult, readHostMessage, resolveHostRequestErrorCode, } from './contract.ts';
|
|
23
25
|
export type { BadgeRequest, ActionResultPayload, GuestActionItem, GuestActionResultMessage, HostActionMessage, HostFileOpenMessage, HostFileSnapshotMessage, HostFileSavedMessage, HostGenerateLateMessage, GuestFileSnapshotResultMessage, GuestFileChangeMessage, GuestFileSaveMessage, GuestFileUnsupportedMessage, GuestGenerateLateListenMessage, GuestBadgeMessage, GuestResizeMessage, GuestOpenCommitMessage, GuestStatusControlsMessage, GuestPopoverAnchorMessage, GuestPopoverCloseMessage, GuestPopoverOpenMessage, OpenCommitRequest, ResizeRequest, GuestItem, GuestItemRole, GuestMessageItem, GuestResolveResultMessage, GuestSessionItem, GuestSessionItemMessage, HostResolveMessage, ResolveRequest, ResolveResultPayload, FileListEntry, FileListRequest, FileListResult, FileReadRequest, FileReadResult, FileStatRequest, FileStatResult, FileWriteRequest, FileWriteResult, GenerateRequest, GenerateResult, LateGenerateResult, GuestFileEntryKind, GuestFileListMessage, GuestFileReadMessage, GuestFileScope, GuestFileStatKind, GuestFileStatMessage, GuestGenerateMessage, GuestFileWriteMessage, ServiceStatus, ServiceStatusResult, HostRequestErrorCode, AttachBranches, AttachIssueRequest, AttachThreadKind, JsonValue, HostResultPayload, PromptRequest, PromptResult, SessionLifecycleEvent, SessionLifecyclePhase, StartSessionRequest, StartSessionResult, StartSessionSent, ComposeRequest, GuestServiceRequestMessage, GuestServiceStatusMessage, GuestShellOutputMessage, GuestShellsSubscribeMessage, GuestShellsUnsubscribeMessage, GuestShellStopMessage, GuestAttachMessage, GuestClipboardWriteMessage, GuestCloseMessage, GuestComposeMessage, GuestConnection, GuestHelloMessage, GuestMessage, GuestOauthDisconnectMessage, GuestOauthStartMessage, GuestOpenSurfaceMessage, GuestOpenUrlMessage, GuestRequest, GuestRequestMessage, GuestRequestMethod, GuestPromptMessage, GuestSessionLinkMessage, GuestStartSessionMessage, GuestRequestResult, GuestSettings, GuestToastMessage, GuestHostSurface, HostConnectionMessage, HostDirectoryMessage, HostItemMessage, HostStatusControlEventMessage, HostPopoverClosedMessage, HostMessage, HostReadyContext, HostReadyMessage, HostResultMessage, HostSessionLifecycleMessage, HostSessionMessage, HostSettingsMessage, HostShellsMessage, HostTheme, HostThemeMode, HostThemeTokens, SessionSnapshot, ToastKind, ToastRequest, } from './contract.ts';
|
|
24
26
|
export { EMPTY_GUEST_CONNECTION, GUEST_BADGE_MAX, GUEST_FRAME_HEIGHT_MAX, GUEST_ITEM_MESSAGE_TEXT_MAX, GUEST_ITEM_SESSION_MAX, GUEST_RESOLVE_ERROR_MAX, GUEST_FILE_CONTENT_MAX, GUEST_FILE_ENTRY_KINDS, GUEST_FILE_LIST_MAX, GUEST_FILE_PATH_MAX, GUEST_GENERATE_OUTPUT_TOKENS_MAX, GUEST_GENERATE_PROMPT_MAX, GUEST_GENERATE_SYSTEM_MAX, GUEST_GENERATE_TEXT_MAX, GUEST_GENERATE_TIMEOUT_MS, GUEST_GENERATE_LATE_KEEP_MS, GUEST_GENERATE_LATE_MAX, GUEST_FILE_STAT_KINDS, SERVICE_STATUS_VALUES, HOST_REQUEST_ERROR_CODES, SESSION_LIFECYCLE_PHASES, START_SESSION_SENT, GUEST_ACCOUNT_MAX, GUEST_SESSION_AGENT_MAX, GUEST_SESSION_MODEL_MAX, GUEST_ATTACH_AUTHOR_MAX, GUEST_ATTACH_BRANCH_MAX, GUEST_ATTACH_DATA_MAX, GUEST_ATTACH_ID_MAX, GUEST_ATTACH_TEXT_MAX, GUEST_ATTACH_TITLE_MAX, GUEST_ATTACH_URL_MAX, GUEST_CLIPBOARD_TEXT_MAX, GUEST_TOAST_MAX, GUEST_COMPOSE_TEXT_MAX, GUEST_REQUEST_BODY_MAX, GUEST_REQUEST_PATH_MAX, GUEST_REQUEST_RESPONSE_MAX, GUEST_REQUEST_TIMEOUT_MS, GUEST_SETTING_VALUE_MAX, } from './contract.ts';
|
|
25
|
-
export { BROWSER_CONTROL_ACTIONS, BROWSER_PROVIDER_ACTION_TIMEOUT_MS, BROWSER_PROVIDER_IDLE_MS, BROWSER_PROVIDER_OPEN_TIMEOUT_MS, BROWSER_PROVIDER_PATH, BROWSER_PROVIDER_RESPONSE_MAX, BROWSER_SCROLL_DIRECTIONS, BROWSER_VIEWPORT_MODES, isBrowserControlAction, readBrowserProviderRequest, OPENCHAMBER_HOST_CALLBACK_URL, SOURCE_CONTROL_OUTBOUND_BODY_MAX, SOURCE_CONTROL_OUTBOUND_METHODS, SOURCE_CONTROL_OUTBOUND_PATH, SOURCE_CONTROL_OUTBOUND_REQUEST_HEADERS, SOURCE_CONTROL_OUTBOUND_RESPONSE_HEADERS, SOURCE_CONTROL_OUTBOUND_RESPONSE_HEADER_PREFIX, SOURCE_CONTROL_OUTBOUND_RESPONSE_MAX, SOURCE_CONTROL_OUTBOUND_TIMEOUT_MS, SOURCE_CONTROL_PROVIDER_AUTH_TIMEOUT_MS, SOURCE_CONTROL_PROVIDER_ERROR_MESSAGE_MAX, SOURCE_CONTROL_PROVIDER_IDLE_MS, SOURCE_CONTROL_PROVIDER_METHODS, SOURCE_CONTROL_PROVIDER_MUTATION_TIMEOUT_MS, SOURCE_CONTROL_PROVIDER_PATH, SOURCE_CONTROL_PROVIDER_READ_TIMEOUT_MS, SOURCE_CONTROL_PROVIDER_RESPONSE_MAX, SOURCE_CONTROL_PROVIDER_STATUS_TIMEOUT_MS, SOURCE_CONTROL_PROVIDER_TARGET_MAX, SOURCE_CONTROL_PROVIDER_WIRE_VERSION, SourceControlForgeError, createSourceControlForge, isSourceControlProviderMethod, readSourceControlProviderRequest, sourceControlProviderTimeoutMs, } from './service-providers.ts';
|
|
26
|
-
export type { BrowserCaptureData, BrowserCaptureParameters, BrowserClickData, BrowserClickParameters, BrowserCloseData, BrowserCloseParameters, BrowserConsoleProblem, BrowserControlAction, BrowserElementBounds, BrowserInspectData, BrowserInspectParameters, BrowserNavigationData, BrowserOpenData, BrowserOpenParameters, BrowserProviderContext, BrowserProviderData, BrowserProviderRequest, BrowserProviderResult, BrowserResizeData, BrowserResizeParameters, BrowserScrollData, BrowserScrollDirection, BrowserScrollParameters, BrowserSnapshotData, BrowserSnapshotElement, BrowserSnapshotOption, BrowserSnapshotParameters, BrowserSelectData, BrowserSelectParameters, BrowserTab, BrowserTabTarget, BrowserTypeData, BrowserTypeParameters, BrowserViewportMode, BrowserViewportSummary, SourceControlChangeRequestHead, SourceControlChangeRequestSource, SourceControlForge, SourceControlForgeUser, SourceControlJson, SourceControlJsonObject, SourceControlMutationMethod, SourceControlMutationResolution, SourceControlMutationResult, SourceControlMutationTarget, SourceControlOutboundMethod, SourceControlOutboundRequest, SourceControlOutboundResponse, SourceControlProbeData, SourceControlProjectRef, SourceControlProjectSelector, SourceControlProviderError, SourceControlProviderInputs, SourceControlProviderMethod, SourceControlProviderRequest, SourceControlProviderResult, SourceControlProviderSession, SourceControlProviderUser, SourceControlReconcileData, SourceControlResourceMethod, SourceControlVerifyData, } from './service-providers.ts';
|
|
27
|
+
export { BROWSER_CONTROL_ACTIONS, BROWSER_PROVIDER_ACTION_TIMEOUT_MS, BROWSER_PROVIDER_IDLE_MS, BROWSER_PROVIDER_OPEN_TIMEOUT_MS, BROWSER_PROVIDER_PATH, BROWSER_PROVIDER_RESPONSE_MAX, BROWSER_SCROLL_DIRECTIONS, BROWSER_VIEWPORT_MODES, isBrowserControlAction, readBrowserProviderRequest, OPENCHAMBER_HOST_CALLBACK_URL, SOURCE_CONTROL_OUTBOUND_BODY_MAX, SOURCE_CONTROL_OUTBOUND_METHODS, SOURCE_CONTROL_OUTBOUND_PATH, SOURCE_CONTROL_OUTBOUND_REQUEST_HEADERS, SOURCE_CONTROL_OUTBOUND_RESPONSE_HEADERS, SOURCE_CONTROL_OUTBOUND_RESPONSE_HEADER_PREFIX, SOURCE_CONTROL_OUTBOUND_RESPONSE_MAX, SOURCE_CONTROL_OUTBOUND_TIMEOUT_MS, SOURCE_CONTROL_PROVIDER_AUTH_TIMEOUT_MS, SOURCE_CONTROL_PROVIDER_ERROR_MESSAGE_MAX, SOURCE_CONTROL_PROVIDER_IDLE_MS, SOURCE_CONTROL_PROVIDER_METHODS, SOURCE_CONTROL_PROVIDER_MUTATION_TIMEOUT_MS, SOURCE_CONTROL_PROVIDER_PATH, SOURCE_CONTROL_PROVIDER_READ_TIMEOUT_MS, SOURCE_CONTROL_PROVIDER_RESPONSE_MAX, SOURCE_CONTROL_PROVIDER_STATUS_TIMEOUT_MS, SOURCE_CONTROL_PROVIDER_TARGET_MAX, SOURCE_CONTROL_PROVIDER_WIRE_VERSION, SourceControlForgeError, createSourceControlForge, isSourceControlProviderMethod, readSourceControlProviderRequest, sourceControlProviderTimeoutMs, GUEST_NOTIFICATION_KINDS, NOTIFICATIONS_OUTBOUND_PATH, NOTIFICATIONS_OUTBOUND_TIMEOUT_MS, NOTIFICATIONS_PROVIDER_AUTH_TIMEOUT_MS, NOTIFICATIONS_PROVIDER_ERROR_MESSAGE_MAX, NOTIFICATIONS_PROVIDER_IDLE_MS, NOTIFICATIONS_PROVIDER_METHODS, NOTIFICATIONS_PROVIDER_NOTIFY_TIMEOUT_MS, NOTIFICATIONS_PROVIDER_PATH, NOTIFICATIONS_PROVIDER_RESPONSE_MAX, NOTIFICATIONS_PROVIDER_WIRE_VERSION, NotificationsOutboundError, createNotificationsOutbound, isNotificationsProviderMethod, notificationsProviderTimeoutMs, readNotificationsProviderRequest, TRACKER_OUTBOUND_PATH, TRACKER_OUTBOUND_TIMEOUT_MS, TRACKER_PROVIDER_AUTH_TIMEOUT_MS, TRACKER_PROVIDER_CONTAINERS_MAX, TRACKER_PROVIDER_ERROR_MESSAGE_MAX, TRACKER_PROVIDER_IDLE_MS, TRACKER_PROVIDER_LIST_MAX, TRACKER_PROVIDER_LIVE_SUMMARIES_MAX, TRACKER_PROVIDER_METHODS, TRACKER_PROVIDER_PATH, TRACKER_PROVIDER_READ_TIMEOUT_MS, TRACKER_PROVIDER_RESPONSE_MAX, TRACKER_PROVIDER_WIRE_VERSION, TRACKER_PROVIDER_WRITE_METHODS, TRACKER_PROVIDER_WRITE_TIMEOUT_MS, TrackerOutboundError, createTrackerOutbound, isTrackerProviderMethod, isTrackerProviderWriteMethod, readTrackerProviderRequest, trackerProviderTimeoutMs, } from './service-providers.ts';
|
|
28
|
+
export type { BrowserCaptureData, BrowserCaptureParameters, BrowserClickData, BrowserClickParameters, BrowserCloseData, BrowserCloseParameters, BrowserConsoleProblem, BrowserControlAction, BrowserElementBounds, BrowserInspectData, BrowserInspectParameters, BrowserNavigationData, BrowserOpenData, BrowserOpenParameters, BrowserProviderContext, BrowserProviderData, BrowserProviderRequest, BrowserProviderResult, BrowserResizeData, BrowserResizeParameters, BrowserScrollData, BrowserScrollDirection, BrowserScrollParameters, BrowserSnapshotData, BrowserSnapshotElement, BrowserSnapshotOption, BrowserSnapshotParameters, BrowserSelectData, BrowserSelectParameters, BrowserTab, BrowserTabTarget, BrowserTypeData, BrowserTypeParameters, BrowserViewportMode, BrowserViewportSummary, SourceControlChangeRequestHead, SourceControlChangeRequestSource, SourceControlForge, SourceControlForgeUser, SourceControlJson, SourceControlJsonObject, SourceControlMutationMethod, SourceControlMutationResolution, SourceControlMutationResult, SourceControlMutationTarget, SourceControlOutboundMethod, SourceControlOutboundRequest, SourceControlOutboundResponse, SourceControlProbeData, SourceControlProjectRef, SourceControlProjectSelector, SourceControlProviderError, SourceControlProviderInputs, SourceControlProviderMethod, SourceControlProviderRequest, SourceControlProviderResult, SourceControlProviderSession, SourceControlProviderUser, SourceControlReconcileData, SourceControlResourceMethod, SourceControlVerifyData, GuestNotification, GuestNotificationKind, NotificationsAccountData, NotificationsNotifyData, NotificationsOutbound, NotificationsProbeData, NotificationsProviderAccount, NotificationsProviderError, NotificationsProviderInputs, NotificationsProviderMethod, NotificationsProviderRequest, NotificationsProviderResult, NotificationsProviderSession, TrackerOutbound, TrackerProviderAccount, TrackerProviderAccountData, TrackerProviderComment, TrackerProviderCommentData, TrackerProviderContainer, TrackerProviderContainersData, TrackerProviderDataByMethod, TrackerProviderError, TrackerProviderInputs, TrackerProviderItem, TrackerProviderItemData, TrackerProviderItemState, TrackerProviderItemSummary, TrackerProviderLabel, TrackerProviderListData, TrackerProviderLiveSummariesData, TrackerProviderLiveSummary, TrackerProviderMethod, TrackerProviderPerson, TrackerProviderPriority, TrackerProviderProbeData, TrackerProviderProgress, TrackerProviderRequest, TrackerProviderResult, TrackerProviderSession, TrackerProviderStateCategory, TrackerProviderStatesData, TrackerProviderWorkflowState, } from './service-providers.ts';
|
|
27
29
|
export { SURFACE_AGENT_ACTIVE_HEADER, SURFACE_AGENT_HOLD_MS, SURFACE_CLIPBOARD_PATH, SURFACE_CONTROL_PATH, SURFACE_CONTROLLERS, SURFACE_DIMENSION_MAX, SURFACE_FRAME_MAX_BYTES, SURFACE_FRAME_MIMES, SURFACE_FRAME_PATH, SURFACE_FRAME_SEQ_HEADER, SURFACE_FRAME_WAIT_MS, SURFACE_HEIGHT_HEADER, SURFACE_INPUT_BATCH_MAX, SURFACE_INPUT_PATH, SURFACE_RESIZE_PATH, SURFACE_SEQ_HEADER, SURFACE_TEXT_MAX, SURFACE_TITLE_HEADER, SURFACE_TITLE_MAX, SURFACE_VIEWER_CONTROLS_HEADER, SURFACE_VIEWER_HEADER, SURFACE_WIDTH_HEADER, readSurfaceControlNotice, readSurfaceInputBatch, readSurfaceResizeRequest, } from './service-surface.ts';
|
|
28
30
|
export type { SurfaceClipboardAnswer, SurfaceControlNotice, SurfaceController, SurfaceFrameMime, SurfaceInputBatch, SurfaceInputEvent, SurfaceKeyEvent, SurfaceModifiers, SurfacePointerEvent, SurfaceResizeAnswer, SurfaceResizeRequest, SurfaceTextEvent, SurfaceWheelEvent, } from './service-surface.ts';
|