ompclaw 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/guide.md ADDED
@@ -0,0 +1,360 @@
1
+ # Operator guide
2
+
3
+ [Back to the README](../README.md)
4
+
5
+ `ompclaw` runs one persistent OMP RPC session and exposes it through enabled, authenticated transport adapters. It is a gateway process, not an OMP extension and not a per-chat session launcher. Telegram and WebSocket clients feed the same OMP session.
6
+
7
+ ## Before you start
8
+
9
+ Use the [user quickstart](../README.md#user-quickstart) for package installation, token-free configuration, private environment file creation, `telegram-allow`, `doctor`, and foreground or service startup.
10
+
11
+ The following operating assumptions are intentional:
12
+
13
+ - OMP is version 17.0.0 or newer and is already authenticated for the provider you intend to use.
14
+ - One process owns one gateway state directory and one OMP session.
15
+ - Every incoming transport identity must be bound to a local principal before it is admitted.
16
+ - A token authorizes a WebSocket credential only after that credential's identity resolves to a principal.
17
+ - Telegram uses Bot API long polling. Do not configure a webhook for the same bot token.
18
+ - WebSocket is intended to bind to loopback by default. The only HTTP route is the unauthenticated health response.
19
+
20
+ ## Architecture and persistence
21
+
22
+ At startup the gateway acquires `ompclaw.lock` in `stateDir`, opens `ompclaw.sqlite`, resolves transport secrets, constructs the OMP runtime, starts OMP, then starts the transport adapters. If another process holds the gateway lock, startup stops before opening the database.
23
+
24
+ ```text
25
+ Telegram long poller ─┐
26
+ ├─ authenticated transport adapter ─┐
27
+ WebSocket client ─┘ │
28
+ v
29
+ one OMP RPC session
30
+
31
+ v
32
+ SQLite state and session checkpoint
33
+ ```
34
+
35
+ The start order prevents an adapter or scheduler from accepting work before the OMP session is available. On shutdown, the scheduler stops first, then adapters stop before OMP, and the state store closes before the lock is released.
36
+
37
+ The SQLite database is `~/.omp/agent/ompclaw/ompclaw.sqlite` by default. A newly created database is private to its owner. It records:
38
+
39
+ | Data | Purpose |
40
+ | --- | --- |
41
+ | principals and transport identities | resolve an inbound identity to authorized local roles |
42
+ | conversation bindings | record the exact OMP session path associated with a transport address |
43
+ | adapter checkpoints | store the active OMP session file and Telegram update progress |
44
+ | inbound messages | deduplicate accepted transport messages |
45
+ | pending UI interactions | keep transport UI metadata until completion or expiry |
46
+ | migration markers | make legacy Telegram import idempotent |
47
+ | scheduled jobs | persist one-shot and cron automation, ownership, next run, retry, and outcome state |
48
+
49
+ SQLite uses foreign keys, full synchronous mode, and immediate transactions for the legacy import. Do not open the database for concurrent writes or share `stateDir` between gateway instances.
50
+
51
+ ## Configuration reference
52
+
53
+ The configuration is a bounded JSON document. It must be a regular file, not a symlink, and must be no larger than 256 KiB. Unknown keys are rejected. Paths beginning with `~/` expand to the current user's home; relative paths resolve from the directory where the command is run.
54
+
55
+ The JSON document never contains a token value. It names environment variables that are read from the process environment or an `--env-file`.
56
+
57
+ ### Top-level fields
58
+
59
+ | Field | Type and default | Meaning |
60
+ | --- | --- | --- |
61
+ | `workspace` | path, current directory | OMP working directory |
62
+ | `stateDir` | path, `~/.omp/agent/ompclaw` | gateway lock, SQLite database, inbox, exports, and checkpoints |
63
+ | `profile` | identifier, `ompclaw` | OMP profile passed to the RPC child |
64
+ | `omp` | object | OMP runtime settings |
65
+ | `transports` | object | enabled transport settings |
66
+ | `automation` | object | optional durable unattended job runner |
67
+ | `learning` | object | optional experimental gateway-scoped memory and managed-skill capture |
68
+
69
+ ### `omp`
70
+
71
+ | Field | Type and default | Meaning |
72
+ | --- | --- | --- |
73
+ | `command` | string, `omp` | OMP executable |
74
+ | `model` | optional string | initial OMP model selection |
75
+ | `resume` | optional path | exact OMP session path used only when no persisted session checkpoint exists |
76
+ | `sessionDir` | optional path | OMP session directory |
77
+ | `configFiles` | string array, `[]` | OMP configuration files, passed in order |
78
+ | `args` | string array, `[]` | additional OMP arguments, passed in order |
79
+ | `authBrokerTokenFile` | optional path | private auth-broker token file passed to the OMP child |
80
+ | `allowRpcBash` | boolean, `false` | enables `/shell` and `/abortbash` in the gateway command surface |
81
+ | `inheritHarness` | boolean, `false` | requests inherited harness preparation in the RPC runtime configuration |
82
+ | `autoRestart` | boolean, `true` | restart an unexpectedly exited OMP child with bounded backoff |
83
+
84
+ `authBrokerTokenFile`, when used, must be private in the same way as the environment file. `allowRpcBash` changes the authority available through the gateway. Leave it disabled unless it is a deliberate operational choice.
85
+
86
+ ### Durable automation
87
+
88
+ Automation is off by default. Enable it explicitly:
89
+
90
+ ```json
91
+ {
92
+ "automation": {
93
+ "enabled": true,
94
+ "pollIntervalMs": 1000,
95
+ "retryDelayMs": 15000,
96
+ "maxAttempts": 3
97
+ }
98
+ }
99
+ ```
100
+
101
+ | Field | Type and default | Meaning |
102
+ | --- | --- | --- |
103
+ | `enabled` | boolean, `false` | register scheduling host tools and start the durable dispatcher |
104
+ | `pollIntervalMs` | integer, `1000` | scan interval from 250 to 60000 milliseconds |
105
+ | `retryDelayMs` | integer, `15000` | base retry delay from 1000 to 3600000 milliseconds |
106
+ | `maxAttempts` | integer, `3` | maximum failed attempts from 1 to 10 before a one-shot job is disabled |
107
+
108
+ An authenticated operator can ask OMP to schedule a one-shot job at an ISO 8601 time with an explicit UTC offset, or a recurring cron job with an optional IANA timezone. OMP receives `ompclaw_schedule_job`, `ompclaw_update_job`, `ompclaw_list_jobs`, `ompclaw_set_job_enabled`, `ompclaw_delete_job`, and `ompclaw_run_job` host tools. The model never supplies a principal or delivery route. The gateway binds each job to the active server-derived principal, identity, and conversation.
109
+
110
+ Use `/jobs` to inspect your jobs, `/job_pause <id>` or `/job_resume <id>` to change dispatch, `/job_run <id>` to make a job due immediately, and `/job_delete <id>` to remove it. Job IDs are intentionally exact and principal-scoped.
111
+
112
+ The scheduler persists the next occurrence before dispatch. A restart recovers every due job from SQLite. Only one OMP turn runs at a time, so a scheduled job that finds the runtime busy is deferred without consuming an attempt. Other failures use bounded linear backoff. A one-shot job is disabled after success or after exhausting retries. A recurring job advances to its next cron occurrence after success or final failure. Job execution is at least once across process crashes, so prompts that mutate external systems should be idempotent.
113
+
114
+ Scheduled output and OMP interaction requests return to the conversation that created the job. Telegram can receive output while no inbound request is active. WebSocket jobs require the exact authenticated origin to be connected when delivery occurs.
115
+
116
+ ### Experimental learning and profile isolation
117
+
118
+ Learning is off by default because OMP marks auto-learn as experimental and automatic capture consumes an extra model turn. A private single-operator setup can enable the full loop:
119
+
120
+ ```json
121
+ {
122
+ "omp": {
123
+ "inheritHarness": true
124
+ },
125
+ "learning": {
126
+ "enabled": true,
127
+ "autoCapture": true,
128
+ "minToolCalls": 5,
129
+ "memoryModel": "online"
130
+ }
131
+ }
132
+ ```
133
+
134
+ | Field | Type and default | Meaning |
135
+ | --- | --- | --- |
136
+ | `enabled` | boolean, `false` | enable isolated Mnemopi memory, `learn`, and `manage_skill` |
137
+ | `autoCapture` | boolean, `false` | run OMP's private capture turn after an eligible stop |
138
+ | `minToolCalls` | integer, `5` | minimum tool calls before automatic capture, from 1 to 100 |
139
+ | `memoryModel` | string, `online` | memory extraction model: `online`, `qwen3-1.7b`, `llama3.2:3b`, `gemma-3-1b`, `qwen2.5-1.5b`, or `lfm2-1.2b` |
140
+
141
+ When enabled, the gateway writes a private generated OMP overlay at `stateDir/omp-learning.json` and stores Mnemopi data under `stateDir/memory`. `online` uses OMP's configured TINY role, then its small online fallback. The other values select local on-device memory models. The active conversation model remains independently selectable through `omp.model` or `/model`.
142
+
143
+ With `omp.inheritHarness: true`, each gateway start refreshes desktop `skills`, `rules`, `commands`, `agents`, `docs`, and `bin` into read-only snapshots inside the named gateway profile. Root policy and harness configuration files refresh atomically. Symlinked skill directories are materialized so the named profile never points back into the desktop profile. Secret and volatile paths such as `.env*`, dependency directories, virtual environments, logs, caches, and browser profiles are excluded.
144
+
145
+ OMP-managed skill creation and refinement write to the gateway profile's separate `managed-skills` directory. Gateway memory, managed skills, sessions, credentials, and databases stay isolated, while updated desktop harness content flows into the gateway on restart.
146
+
147
+ ### Telegram transport
148
+
149
+ A Telegram object is required only when Telegram is configured:
150
+
151
+ ```json
152
+ {
153
+ "enabled": true,
154
+ "account": "default",
155
+ "tokenEnv": "TELEGRAM_BOT_TOKEN"
156
+ }
157
+ ```
158
+
159
+ | Field | Meaning |
160
+ | --- | --- |
161
+ | `enabled` | whether to start this adapter |
162
+ | `account` | stable local account identifier used in identities and checkpoints |
163
+ | `tokenEnv` | environment variable containing the bot token; it must be `TELEGRAM_BOT_TOKEN` |
164
+
165
+ Authorizing a user is an explicit local database operation:
166
+
167
+ ```bash
168
+ ompclaw telegram-allow <your-numeric-telegram-user-id> \
169
+ --config ~/.config/ompclaw/config.json
170
+ ```
171
+
172
+ It creates or updates an `operator` principal and binds the exact Telegram identity for the configured account. To use a custom principal or roles, create it with `principal-add` and bind the Telegram identity with `identity-bind`.
173
+
174
+ ### WebSocket transport
175
+
176
+ The following loopback configuration is the recommended starting point:
177
+
178
+ ```json
179
+ {
180
+ "enabled": true,
181
+ "hostname": "127.0.0.1",
182
+ "port": 8787,
183
+ "account": "local",
184
+ "credentials": [
185
+ {
186
+ "tokenEnv": "OMPCLAW_WS_TOKEN",
187
+ "subject": "local-operator",
188
+ "channel": "local"
189
+ }
190
+ ]
191
+ }
192
+ ```
193
+
194
+ | Field | Meaning |
195
+ | --- | --- |
196
+ | `enabled` | whether to start the WebSocket server |
197
+ | `hostname` | server bind hostname; use `127.0.0.1` by default |
198
+ | `port` | integer from 0 to 65535; `0` lets Bun choose a port |
199
+ | `account` | stable local account identifier |
200
+ | `credentials` | non-empty list of credential metadata |
201
+ | `credentials[].tokenEnv` | environment variable holding the token; use an `OMPCLAW_...` name |
202
+ | `credentials[].subject` | stable identity subject derived after authentication |
203
+ | `credentials[].channel` | stable delivery channel derived after authentication |
204
+ | `credentials[].thread` | optional stable delivery thread derived after authentication |
205
+
206
+ Credential metadata is configuration, not client input. Tokens are compared by hash. Each configured token and conversation origin must be unique, and one live connection is allowed for an origin. Bind each credential identity before clients can use it:
207
+
208
+ ```bash
209
+ ompclaw principal-add local-operator \
210
+ --config ~/.config/ompclaw/config.json
211
+ ompclaw identity-bind websocket local local-operator local-operator \
212
+ --config ~/.config/ompclaw/config.json
213
+ ```
214
+
215
+ ## Secret environment file
216
+
217
+ Create the environment file outside the repository and make it mode `0600`:
218
+
219
+ ```bash
220
+ cat > ~/.config/ompclaw/ompclaw.env <<'ENV'
221
+ TELEGRAM_BOT_TOKEN=replace-with-telegram-bot-token
222
+ OMPCLAW_WS_TOKEN=replace-with-a-long-random-websocket-token
223
+ ENV
224
+ chmod 600 ~/.config/ompclaw/ompclaw.env
225
+ ```
226
+
227
+ The loader accepts literal `KEY=VALUE` lines, optional `export ` prefixes, comments, and quoted values. Existing process variables take precedence over values in the file. On Unix-like systems it refuses an environment file that is a symlink, not a regular file, not owned by the current user, or readable by group or others.
228
+
229
+ Before launching OMP, the gateway removes Telegram and gateway transport secret variables from the OMP child environment. Do not rely on that filtering as a reason to store credentials in project files or OMP configuration.
230
+
231
+ ## Start, validate, and service operation
232
+
233
+ Use `doctor` before the first start and whenever credentials or OMP configuration change:
234
+
235
+ ```bash
236
+ ompclaw doctor \
237
+ --config ~/.config/ompclaw/config.json \
238
+ --env-file ~/.config/ompclaw/ompclaw.env
239
+ ```
240
+
241
+ `doctor` resolves all enabled transport secrets, opens the SQLite store, verifies Telegram's bot identity and the absence of a webhook when Telegram is enabled, starts a short OMP RPC child, requests session state, then stops that child. A successful run ends with `Doctor: ready`.
242
+
243
+ Run in the foreground during setup:
244
+
245
+ ```bash
246
+ ompclaw run \
247
+ --config ~/.config/ompclaw/config.json \
248
+ --env-file ~/.config/ompclaw/ompclaw.env
249
+ ```
250
+
251
+ The process stops cleanly on `SIGINT` or `SIGTERM`. It starts the OMP session first, then the adapters.
252
+
253
+ For a persistent user service:
254
+
255
+ ```bash
256
+ ompclaw service-install \
257
+ --config ~/.config/ompclaw/config.json \
258
+ --env-file ~/.config/ompclaw/ompclaw.env
259
+ ```
260
+
261
+ The installer requires both `--config` and `--env-file` as absolute regular-file paths, requires the environment file to be exactly mode `0600`, verifies that configured secrets resolve, and reports its manager and installed path. It uses launchd label `com.ompclaw` on macOS and user systemd unit `ompclaw.service` on Linux. The environment file is referenced by the service command rather than copied into the service definition.
262
+
263
+ Remove a user service with:
264
+
265
+ ```bash
266
+ ompclaw service-uninstall \
267
+ --config ~/.config/ompclaw/config.json
268
+ ```
269
+
270
+ The command reports the stopped and removed service path. Stop the service before making manual database backups or state-directory copies.
271
+
272
+ ### Health and logs
273
+
274
+ The WebSocket adapter exposes exactly one HTTP response:
275
+
276
+ ```text
277
+ GET /healthz -> 200 {"status":"ok"}
278
+ ```
279
+
280
+ It is a liveness endpoint, not an authenticated API, metrics endpoint, command endpoint, or reverse-proxy control plane. Unknown HTTP paths return `404`. The WebSocket upgrade is `GET /` with the WebSocket upgrade header.
281
+
282
+ Use service-manager logs and foreground stderr for gateway and OMP failures. Do not include environment file content, session exports, inbox attachment paths, or database rows in a public report.
283
+
284
+ ## Crash recovery and exact-session resume
285
+
286
+ After an OMP state update and after each successful inbound turn, the gateway records OMP's current `sessionFile` in the `omp/session_file` checkpoint. On the next gateway start it resumes that exact path first. If no checkpoint exists, it uses `omp.resume` when configured. `/switch <exact session path>` asks OMP to switch to the exact path supplied by the operator.
287
+
288
+ If the OMP child exits unexpectedly and `omp.autoRestart` is true, the gateway notifies the active delivery context and retries startup with delays of 1, 2, 5, 10, then 30 seconds. `autoRestart: false` leaves the gateway offline after the exit.
289
+
290
+ The checkpoint allows a restarted gateway to resume a completed OMP session. It does not resume a partly executed prompt, an in-flight tool call, a transport connection, or a pending user interaction. Send a new message after recovery when the interrupted result matters.
291
+
292
+ ## Migration from omp-gateway 0.2.x
293
+
294
+ The OmpClaw rename changes the package, CLI, service identifiers, default profile and state paths, environment prefix, database filenames, and OMP host-tool names. There are no legacy aliases.
295
+
296
+ 1. Stop the old service while the `omp-gateway` command is still installed:
297
+
298
+ ```bash
299
+ omp-gateway service-uninstall \
300
+ --config ~/.config/omp-gateway/config.json
301
+ ```
302
+
303
+ 2. Move `~/.config/omp-gateway` to `~/.config/ompclaw` and `~/.omp/agent/gateway` to `~/.omp/agent/ompclaw`.
304
+ 3. In the stopped state directory, rename `gateway.sqlite` to `ompclaw.sqlite` and any matching `gateway.sqlite-wal` or `gateway.sqlite-shm` sidecars to the same `ompclaw.sqlite-*` suffixes. Rename `gateway.lock` to `ompclaw.lock` if it remains after the clean stop.
305
+ 4. Update the JSON config to use the new state directory. Change `profile` from `gateway` to `ompclaw` only when the old config used the default; keep an explicitly configured custom profile unchanged. Rename `OMP_GATEWAY_*` environment variable names to `OMPCLAW_*`, and rename the environment file if desired.
306
+ 5. Install `ompclaw`, run `doctor`, then install the new `com.ompclaw` or `ompclaw.service` user service.
307
+
308
+ The SQLite schema and stored conversation, principal, scheduler, inbox, and checkpoint records are unchanged. OMP sees the renamed host tools only after the new process starts.
309
+
310
+ ## Migration from standalone omp-telegram RPC state
311
+
312
+ Migration is for state created by the old standalone `omp-telegram` RPC service. It is not an extension activation step and it does not reuse legacy pairing commands.
313
+
314
+ 1. Stop the old standalone service so only one Telegram poller owns the bot token.
315
+ 2. Create the new token-free gateway JSON and private environment file.
316
+ 3. Locate the old standalone access-state JSON and RPC-state JSON. Keep backups outside source control.
317
+ 4. Run the idempotent importer:
318
+
319
+ ```bash
320
+ ompclaw migrate-telegram <legacy-access-state.json> <legacy-rpc-state.json> \
321
+ --config ~/.config/ompclaw/config.json
322
+ ```
323
+
324
+ 5. Run `telegram-allow` if you want to establish or replace the Telegram operator binding explicitly, then run `doctor` and start the gateway.
325
+
326
+ The importer reads only the two named JSON state files. It does not read or copy an old token environment file. In one SQLite transaction, it imports the legacy Telegram operator as an `operator` principal, binds its default Telegram identity, records the exact OMP session path for its conversation, and imports the Telegram update checkpoint when present. A migration marker makes subsequent invocations report that the state was already migrated.
327
+
328
+ ## Transport behavior
329
+
330
+ ### Telegram
331
+
332
+ Telegram uses long polling, per-account update checkpointing, and a per-account polling lock. It ignores replayed or already completed updates and advances the durable checkpoint only across the completed update prefix. A failed earlier update is not skipped by a later successful one.
333
+
334
+ Private chats and forum topics map to stable gateway conversation addresses. Incoming Telegram identity comes from the sender ID. Normal inbound content can include text, captions, reply metadata, supported media as private inbox files, and optional voice transcription when configured by the adapter. Images are passed to OMP as image input when readable; other attachment references are retained in the transport-message prompt.
335
+
336
+ Telegram supports assistant message creation and editing, reactions, attachments, threads, confirmation buttons, single and multi-select buttons, reply-based text/editor input, notifications, URL buttons, and rendered status surfaces. Interaction responses are bound to the original address and authorized principal. An expired, moved, or cross-user control is rejected.
337
+
338
+ ### WebSocket
339
+
340
+ WebSocket is a versioned, authenticated transport. Connect to the configured endpoint, send an `authenticate` frame first, wait for `ready`, then send messages or UI responses. The server derives identity, account, channel, and optional thread from the credential configuration. A client cannot set those fields.
341
+
342
+ The server accepts a connection at `GET /` only after HTTP WebSocket upgrade. It requires authentication within 10 seconds by default. Invalid, missing, duplicate, or unbound credentials are rejected and the socket closes with a policy error. See the full frame contract in the [RPC and transport reference](rpc-service.md#websocket-protocol-v1).
343
+
344
+ ## Operational limitations
345
+
346
+ - One gateway process owns one OMP session, one scheduler, and one SQLite writer. This is not a worker pool or a distributed multi-writer system.
347
+ - Interactive and scheduled work serialize through the same OMP runtime. A due job waits while another turn is active. A different interactive conversation receives a busy response while another turn is active.
348
+ - Job dispatch is at least once across gateway or host crashes. External side effects require idempotent prompts or downstream deduplication.
349
+ - In-flight OMP work is not resumable after a process or child crash. Completed session and scheduled-job state remain durable.
350
+ - One Telegram bot token has one long poller. Do not run multiple pollers or mix long polling with a Telegram webhook.
351
+ - WebSocket delivery requires the authenticated client for the exact configured origin to remain connected. The gateway does not queue delivery for a disconnected WebSocket client.
352
+ - HTTP is health-only. There is no HTTP prompt, scheduler, database, or unauthenticated transport API.
353
+ - Experimental auto-capture uses extra provider tokens and can create or refine gateway-profile managed skills. Review that profile before relying on learned behavior for consequential automation.
354
+
355
+ ## Further reference
356
+
357
+ - [README quickstart](../README.md)
358
+ - [RPC and transport reference](rpc-service.md)
359
+ - [Security policy](../SECURITY.md)
360
+ - [Upstream attribution](../NOTICE)
@@ -0,0 +1,240 @@
1
+ # RPC and transport reference
2
+
3
+ [Back to the operator guide](guide.md) · [Back to the README](../README.md)
4
+
5
+ `ompclaw` presents one persistent OMP RPC session through authenticated transport adapters. It is not an HTTP RPC server. HTTP is limited to the WebSocket adapter's health endpoint. The OMP child uses its RPC UI mode; Telegram and WebSocket clients receive the gateway-level behavior described here.
6
+
7
+ ## Session and delivery model
8
+
9
+ The runtime starts one OMP child with `--mode rpc-ui`, the configured workspace and profile, and a persisted session path when one exists. It subscribes to OMP subagent progress, registers the gateway host tools, and asks OMP for session state.
10
+
11
+ An inbound message or scheduled job establishes an active delivery context containing the authenticated principal and the exact transport address. Streamed updates, final assistant text, OMP UI, command output, host-tool delivery, and unexpected-exit notifications go only to that context. A message from a different authenticated conversation during an active turn receives a busy response. It does not join the active turn or receive its output. Due scheduled work defers while the runtime is busy.
12
+
13
+ Assistant updates are lossless and ordered at the gateway boundary. A first visible assistant update creates an outbound message; later visible updates edit that same receipt when the transport supports it. A terminal OMP response delivers the final visible assistant text and clears the active delivery context. Thinking and tool-call blocks are not forwarded as assistant text.
14
+
15
+ ## Gateway command matrix
16
+
17
+ Send these commands as a slash command in an authenticated conversation. Any other available OMP slash command is forwarded to the OMP session. Commands that take an argument show their usage when the argument is absent or invalid.
18
+
19
+ | Command | OMP action and gateway result |
20
+ | --- | --- |
21
+ | `/status` | refresh and report OMP streaming or compaction state, exact session name or ID, model, thinking, fast mode, message and queue counts, context usage, current tool, tracked subagents, UI display state, and last error |
22
+ | `/stop` | send OMP `abort` for the current run |
23
+ | `/new` | request an OMP `new_session` and clear the active delivery context when complete |
24
+ | `/steer <message>` | send OMP `steer` with a correction |
25
+ | `/followup <message>` | send OMP `follow_up` to queue work after the current turn |
26
+ | `/compact [instructions]` | compact OMP context, optionally with custom instructions, then refresh state |
27
+ | `/model` | list current and available `provider/model-id` values |
28
+ | `/model <provider>/<model-id>` | send OMP `set_model` and refresh state |
29
+ | `/thinking` | show the current reasoning level and accepted levels |
30
+ | `/thinking <inherit|off|minimal|low|medium|high|xhigh|max|auto>` | send OMP `set_thinking_level` and refresh state |
31
+ | `/fast [on|off]` | show or set OMP fast mode |
32
+ | `/queue` | show OMP steering, follow-up, and interrupt policy |
33
+ | `/queue steering <all|one-at-a-time>` | set OMP steering policy |
34
+ | `/queue follow <all|one-at-a-time>` | set OMP follow-up policy |
35
+ | `/queue interrupt <immediate|wait>` | set OMP interrupt policy |
36
+ | `/stats` | request OMP session statistics |
37
+ | `/todos` | show the current OMP todo phases from session state |
38
+ | `/subagents` | request and show active or recent OMP subagents |
39
+ | `/commands` | request and show available OMP slash commands |
40
+ | `/history [1-50]` | request messages and show the requested recent visible summaries; default is 12 |
41
+ | `/branch` | list recent OMP branch points |
42
+ | `/branch <entry-id>` | ask OMP to branch from the exact entry ID |
43
+ | `/name <session name>` | set the OMP session name and refresh state |
44
+ | `/handoff [instructions]` | hand context to a fresh OMP session and refresh state |
45
+ | `/switch <exact session path>` | ask OMP to switch to the exact session path and refresh state |
46
+ | `/export` | request an OMP HTML export, store it under `stateDir/exports`, and attach it to the active conversation |
47
+ | `/retry <on|off|stop>` | enable or disable automatic retry, or send OMP `abort_retry` |
48
+ | `/autocompact [on|off]` | show or set automatic compaction |
49
+ | `/login` | show provider login availability and authentication state |
50
+ | `/login <provider-id>` | start OMP provider login and route its secure URL prompt to the active delivery context |
51
+ | `/jobs` | list durable scheduled jobs owned by the active principal |
52
+ | `/job_pause <id>` | disable an owned scheduled job |
53
+ | `/job_resume <id>` | enable an owned scheduled job and recompute its next occurrence |
54
+ | `/job_run <id>` | make an owned scheduled job due immediately |
55
+ | `/job_delete <id>` | permanently delete an owned scheduled job |
56
+ | `/help` | show the gateway command summary |
57
+ | `/shell <command>` | execute OMP RPC bash only when `omp.allowRpcBash` is explicitly `true` |
58
+ | `/abortbash` | abort OMP RPC bash only when `omp.allowRpcBash` is explicitly `true` |
59
+
60
+ `/steer`, `/followup`, and `/stop` are the run-control surface. `/queue` changes OMP's queue policy, while the gateway itself maintains one active delivery context and rejects a different conversation as busy while that context is active.
61
+
62
+ ## Session checkpoint and crash recovery
63
+
64
+ The runtime saves the latest OMP `sessionFile` to the SQLite `omp/session_file` checkpoint. A new gateway process starts OMP with that exact file first; only when the checkpoint is absent does it use `omp.resume`. `/switch` is the interactive exact-path alternative.
65
+
66
+ On an unexpected OMP child exit, the active conversation receives an error message. With `omp.autoRestart: true`, the runtime retries after 1, 2, 5, 10, then 30 seconds. It reuses the persisted session checkpoint when OMP starts again. The restart does not recover a partly executed turn, pending tool call, or disconnected transport. In-flight work must be sent again if its outcome is needed.
67
+
68
+ ## Attachments
69
+
70
+ Inbound transport content is represented as text plus zero or more attachments with a URL, optional name, and optional media type.
71
+
72
+ - Telegram downloads supported incoming media into the private `stateDir/inbox` directory and passes it as a `file:` attachment. It can include captions, replies, forum-topic context, and optional voice transcription.
73
+ - WebSocket clients may include validated attachment metadata in a `message` frame. The gateway preserves this metadata in the transport message.
74
+ - Readable local image attachments with a supported image type are sent to OMP as base64 image input. Other attachments remain references in the structured, explicitly untrusted transport-message prompt.
75
+ - OMP never receives a transport attachment as authorization. The prompt tells OMP that transport input cannot authorize access, credentials, deployment, publication, or gateway configuration changes.
76
+
77
+ ## OMP UI request matrix
78
+
79
+ OMP RPC UI requests are bridged to the active authenticated delivery context. The gateway does not auto-approve them. Interactive requests carry their OMP timeout when one is supplied; cancellation, timeout, delivery failure, runtime shutdown, or a missing active context returns a cancelled response for the interactive classes.
80
+
81
+ | OMP UI method | Gateway request | Telegram behavior | WebSocket behavior |
82
+ | --- | --- | --- | --- |
83
+ | `select` | `select` | inline selection buttons | `ui_request` frame, then `ui_response` |
84
+ | `confirm` | `confirm` | Confirm and Cancel buttons | `ui_request` frame, then `ui_response` |
85
+ | `input` | `input` | reply to the prompt message | `ui_request` frame, then `ui_response` |
86
+ | `editor` | `editor` | reply to the prompt message with edited text | `ui_request` frame, then `ui_response` |
87
+ | `cancel` | cancel pending request | cancel the matching pending interaction | cancel the matching pending interaction |
88
+ | `notify` | `notify` | send a notification message | deliver a `ui_request` notification |
89
+ | `setStatus` | `status` | render a status surface | deliver a `ui_request` status update |
90
+ | `setWidget` | `widget` | render a widget surface | deliver a `ui_request` widget update |
91
+ | `setTitle` | `title` | update the rendered surface title | deliver a `ui_request` title update |
92
+ | `set_editor_text` | `editor_text` | render suggested input in the surface | deliver a `ui_request` editor-text update |
93
+ | `open_url` | `open_url` | send a labeled URL button | deliver a `ui_request` URL request |
94
+
95
+ Only `select`, `confirm`, `input`, and `editor` produce a response back to OMP. Telegram immediately acknowledges presentation of status, widget, title, editor-text, notification, and URL requests. A WebSocket client must return the matching acknowledgement response for every `ui_request`, including display-only requests, so the transport can settle that request. Telegram verifies both the exact conversation address and principal before accepting a button or reply response. WebSocket verifies that the current credential still resolves to the same principal and that its response type matches the pending request.
96
+
97
+ ## Gateway host tools
98
+
99
+ The OMP child always receives three delivery host tools. When `automation.enabled` is true, it also receives six durable job-control tools. The gateway derives the principal, transport identity, and conversation address from the active server-owned context. A model cannot choose or override them.
100
+
101
+ | Tool | Parameters | Behavior |
102
+ | --- | --- | --- |
103
+ | `ompclaw_send` | `text` and/or `files` | send text and optional absolute local file paths to the active conversation; at least one is required |
104
+ | `ompclaw_ask` | `question`, optional `title`, `options`, and `multi` | ask the operator a free-text, single-select, or multi-select question in the active conversation |
105
+ | `ompclaw_react` | `message_id`, `emoji` | react to a message in the active conversation |
106
+ | `ompclaw_schedule_job` | `name`, `prompt`, exactly one of `at` or `cron`, optional `timezone` | create an owned one-shot or recurring job bound to the active conversation |
107
+ | `ompclaw_update_job` | `id`, optional mutable job fields | update an owned job and recompute its next occurrence |
108
+ | `ompclaw_list_jobs` | none | list the active principal's jobs |
109
+ | `ompclaw_set_job_enabled` | `id`, `enabled` | pause or resume an owned job |
110
+ | `ompclaw_delete_job` | `id` | permanently remove an owned job |
111
+ | `ompclaw_run_job` | `id` | make an owned job due now |
112
+
113
+ `ompclaw_send` accepts only absolute local paths for `files`. Transport adapters enforce their own attachment and message rules. `ompclaw_ask` without `options` uses text input; with options it uses selection, and `multi: true` requests multi-select. Host-tool cancellation aborts the in-progress gateway delivery operation.
114
+
115
+ One-shot `at` values must be ISO 8601 date-times with an explicit UTC offset. Cron timezones must be valid IANA names. Names, prompts, expressions, and retry state are bounded and validated before SQLite mutation. Job lookup and mutation always include the server-derived principal ID.
116
+
117
+ ## WebSocket protocol v1
118
+
119
+ The WebSocket protocol version is `1`. It is separate from the OMP child RPC protocol. After authentication the server sends `ready` with `protocolVersion: 1`; clients should treat that value as the protocol they are speaking. The current client frames do not include a version field or negotiate versions.
120
+
121
+ The OMP child protocol starts at v1 and negotiates v2 when OMP advertises v2 support. This supports lossless chunk reassembly for OMP RPC frames. `doctor` reports the protocol selected for the OMP child; this does not change the WebSocket protocol number.
122
+
123
+ ### Connection sequence
124
+
125
+ 1. Connect to `ws://127.0.0.1:8787/` or the configured local endpoint.
126
+ 2. Send exactly one `authenticate` frame within 10 seconds by default.
127
+ 3. Wait for `ready` with protocol version `1`.
128
+ 4. Send `message` frames or answer `ui_request` frames with `ui_response`.
129
+ 5. Handle `message`, `update`, `reaction`, `ui_request`, and `error` frames from the server.
130
+
131
+ Use a TLS and access-controlled boundary before making a WebSocket endpoint reachable beyond loopback. The token is a bearer credential. Replace every value shown below with an actual client-specific value outside source control.
132
+
133
+ ### Client frames
134
+
135
+ ```json
136
+ { "type": "authenticate", "token": "replace-with-websocket-token" }
137
+ ```
138
+
139
+ ```json
140
+ {
141
+ "type": "message",
142
+ "id": "client-message-id",
143
+ "text": "Inspect the current session",
144
+ "attachments": [
145
+ {
146
+ "url": "https://example.invalid/attachment.txt",
147
+ "name": "attachment.txt",
148
+ "mediaType": "text/plain"
149
+ }
150
+ ]
151
+ }
152
+ ```
153
+
154
+ ```json
155
+ {
156
+ "type": "ui_response",
157
+ "requestId": "server-request-id",
158
+ "response": {
159
+ "type": "confirm",
160
+ "confirmed": true
161
+ }
162
+ }
163
+ ```
164
+
165
+ The exact `response` shape must match the request type. `select` returns selected values, `input` and `editor` return either cancellation or a string value, and display-only UI request types return an acknowledgement response. Send a matching `ui_response` for every WebSocket `ui_request`.
166
+
167
+ Client frames are exact schemas. A message cannot supply a principal, account, channel, thread, timestamp, or delivery address. The server derives all of those values from the authenticated configured credential. Message IDs are scoped by the derived origin, and timestamps use server time.
168
+
169
+ ### Server frames
170
+
171
+ ```json
172
+ { "type": "ready", "protocolVersion": 1 }
173
+ ```
174
+
175
+ ```json
176
+ {
177
+ "type": "message",
178
+ "messageId": "gateway-message-id",
179
+ "content": { "text": "Initial assistant text", "format": "text" }
180
+ }
181
+ ```
182
+
183
+ ```json
184
+ {
185
+ "type": "update",
186
+ "messageId": "gateway-message-id",
187
+ "content": { "text": "Updated assistant text", "format": "text" }
188
+ }
189
+ ```
190
+
191
+ ```json
192
+ { "type": "reaction", "messageId": "gateway-message-id", "emoji": "replace-with-an-emoji" }
193
+ ```
194
+
195
+ ```json
196
+ {
197
+ "type": "ui_request",
198
+ "requestId": "server-request-id",
199
+ "request": { "type": "confirm", "title": "Continue", "message": "Proceed with the requested action?" }
200
+ }
201
+ ```
202
+
203
+ ```json
204
+ { "type": "error", "code": "unauthorized", "message": "authentication failed" }
205
+ ```
206
+
207
+ The server accepts only the health endpoint over ordinary HTTP:
208
+
209
+ ```text
210
+ GET /healthz -> 200 {"status":"ok"}
211
+ ```
212
+
213
+ All other ordinary HTTP routes return `404`. The WebSocket upgrade route is `GET /` with the upgrade header.
214
+
215
+ ### Authentication and connection boundaries
216
+
217
+ The server hashes configured credential tokens and uses a timing-safe comparison. A valid token is necessary but not sufficient: its configured identity must resolve to a principal in SQLite. A second connection for the same configured origin is rejected. Credentials with duplicate tokens or duplicate configured origins are rejected at startup.
218
+
219
+ Before authentication, malformed frames, non-authenticate frames, a timeout, or bad credentials produce an error when possible and close the connection with a policy error. After authentication, unknown UI request IDs, principal mismatches, response-type mismatches, and rejected inbound messages produce error frames. Delivery fails when the exact authenticated origin disconnects; the gateway does not retain a WebSocket outbound queue.
220
+
221
+ ## Telegram transport reference
222
+
223
+ Telegram is long-poll only. `doctor` calls `getWebhookInfo` and rejects a non-empty webhook URL because Telegram cannot use the same bot for both webhook delivery and long polling.
224
+
225
+ The adapter persists each completed update ID under an account-specific checkpoint. It processes duplicate or replayed updates at most once and only moves the checkpoint across a contiguous completed prefix. The adapter lock prevents a second poller for the same account in the same state directory.
226
+
227
+ Telegram outbound delivery supports text messages and edits, reactions, uploads, forum threads, and native message segmentation. UI presentations use inline buttons for confirmation and selection, reply-to-message for free-text input and editor input, URL buttons for URL requests, and rendered surfaces for status-like requests. Pending interaction ownership is checked again when the user responds.
228
+
229
+ ## Security boundary summary
230
+
231
+ - Principal identity is resolved locally from transport identity before OMP receives a message.
232
+ - The active delivery context scopes every outbound reply, UI presentation, host tool, and reaction.
233
+ - JSON configuration names secret variables but does not contain their values. Private environment files are required for token loading.
234
+ - Gateway transport secrets are removed from the OMP child environment.
235
+ - One state directory has one gateway writer. One Telegram bot token has one poller.
236
+ - An authorized transport principal has the authority of the configured OMP workspace and profile. See the [security policy](../SECURITY.md) before binding an identity or exposing a WebSocket endpoint.
237
+
238
+ ## Limits
239
+
240
+ The gateway does not distribute turns across writers, create one OMP session per client, resume an in-flight OMP turn, or offer a general HTTP API. Durable jobs run through the same single OMP session with at-least-once dispatch and bounded retry. WebSocket state is live only, scheduled WebSocket delivery requires the originating client to be connected, and Telegram is limited to one long-polling owner for a bot token.