Clients
Desktop interface
The operator decisions, ownership boundaries, state rules, and packaging contract behind Oko Desktop.
Oko Desktop interface design#
Oko Desktop is the decision surface over Oko's merged transcript catalogue, rebuildable search projection, and the PTY sessions Oko started itself. Raw vendor transcripts remain owned by their runtimes, while Transcript Lake owns the masked canonical store. The interface names that boundary wherever an operator could otherwise delete or trust the wrong thing.
Native service connection#
OkoKit sends typed requests directly to Oko's owner-only broker socket. It does not find or launch an oko executable, capture CLI output, or create a terminal for a task action. Rust owns register writes, task settlement, provider calls and routine receipts. Session controls address the broker that accepted the request, rather than another process selected by a default path.
oko api status [--socket PATH] reports the running service's executable, process and protocol versions. oko api call --file PATH [--socket PATH] sends a structured request; see the API command reference. An unavailable socket, incompatible version or failed operation remains a failure on the screen. Installing a newer CLI alone does not prove the running service has the required API.
The Tasks screen sends identities, statements, contract amendments, relations and source handoffs as fields, not command arguments. Close requires a reason and the task identity typed by the operator. The native operation checks that identity before saving the same bound verdict as the CLI's terminal-confirmed close. No model is invoked to approve that operator action. An agent must not use either interface to substitute its own completion claim for the operator's decision.
Native journey reports under .build/api-*/ distinguish the candidate's declared source_revision from harness_source_revision, the checkout running the test. An undeclared candidate revision stays null; the harness revision is not substituted as proof of the service executable's origin. The report retains its binary checksum, startup and request failures, exchanges and observed exit state. A passing operation with unknown candidate provenance is not release qualification, and a native API exchange is not a visual review of the screen.
Historical operator messages#
Rulings → Operator messages and their records reads the same native linkage report as oko rulings unlinked. Enter a session to inspect complete operator words, their reading, carrying task, related defect and explicit work attachments. Attach and Remove work link persist an association to current work without inventing a new task or changing the message's reading.
Audit Transcript Lake derives its genuine operator turns read-only; Store Transcript Lake ledgers persists those derivations. Audit all Lake message links names every uncovered turn and failed session. Its runtime rows distinguish audited, linked and unlinked counts; derivation rows show derived, unread and unattached counts. A zero unlinked count alongside a failed session does not establish complete coverage. The native journey in oko-desktop/tests/rulings/MessageLinks.swift exercises attachment, removal and the whole-Lake runtime reports against the actual service and store.
Asks reach the window as they are made#
While it runs, Oko Desktop stays attached to Oko's service on this Mac as an ask watcher (watch-asks on the broker socket, OkoAskWatch in OkoKit). When a product runs oko asks ask on this Mac, the service hands the ask to every attached Oko Desktop; the app puts it in front of whatever screen is open as a sheet — the product and subject, the question and its detail, one button per choice or a field, and Later, which leaves it waiting on the Requests screen — posts a macOS notification once the operator has let Oko notify, and writes back that the ask is shown. The asking command records that word as the ask's oko-desktop delivery; with no Oko Desktop attached the delivery fails with `no Oko Desktop is attached to Oko's service at is refused to the asker. The watch ends when the service ends and is joined again when the app becomes active, so an installed service is picked up the next time the operator comes back to the window.
The same service writes each ask into every terminal of its own that somebody is watching (oko sessions attach, the terminal views of this app): a framed block with the product, subject, question, detail, choices and the oko asks answer <id> --text <answer> --on terminal that answers it. It is the one channel that needs no phone, no Oko Desktop, no Stado and no vault, so an ask about their outage still reaches the person at a terminal. An ask no channel took stands undelivered; the service delivers it again by itself the moment an Oko Desktop attaches or a terminal is watched, and every control pass delivers it again for the channels that recover unseen.
Destinations#
| Group | Destination | Decision |
|---|---|---|
| Work | Posture | Decide whether the catalogue, projection, broker, or router needs attention. |
| Work | Catalogue | Find a session in the merged catalogue by content or recency. |
| Work | Sessions | Inspect, steer, end, or stop sharing a PTY session Oko started. Agent sessions reads every agent session on this Mac the way oko sessions agents does — live ones, or with hours every one written in that window — with who holds it, whether it works, what it stands on and its role in supervised work (a chip: supervisor, supervisor of N, or executor under a session); the Role menu on a row marks it as supervisor, as executor of a recorded supervisor, or clears its role, the way oko sessions role does, and shows the product's refusal word for word; Resume with message continues one in a broker terminal by its own runtime the way oko sessions resume does and shows the terminal and transcript the message landed in, or the product's refusal. Each row shows the provider and model the session actually runs on, read from its transcript, beside the Brama subscription it leases; the Model menu offers the models the operator declared with how many live sessions run on each and moves the session to one the way oko sessions model does — it waits for the turn to end, ends the terminal and continues the session on the chosen model — and the section says how many live sessions run on each declared model. Run model policy runs one pass the way oko sessions policy run does and prints each decision and each stalled command it reported in the CLI's own line. Start session starts a new agent session the way oko sessions start --text does: Jeden unless the harness field names omp, in the directory given, with the first prompt, on the model picked or the declared mix's choice. Outside the broker lists the Claude Code, Codex and Jeden processes on this Mac that Oko did not start, each with the session it records, its directory and command line, the way oko sessions list --external does; Adopt with an optional text resumes one in a broker terminal the way oko sessions adopt does and shows the terminal, or the refusal word for word. |
| Work | Tasks | Read each request's status and derived standing. The summary counts open (including claimed), claimed, verified, withdrawn and refused. A done verdict or operator Close without a commit on main, a source-bound real behavioral test receipt and observed acceptance remains an open claim; its row names the missing check and attempted revision. Hand over open tasks retains those claims and uses the same OkoKit control pass as CLI. Later operator corrections take precedence. |
| Work | Requests | Answer interaction and approval requests blocking a run. Asked of you lists every ask a Wisent product put to the signed-in person (oko asks list) with its deliveries, answers one with its choices or a field (oko asks answer … --on oko-desktop), and chooses the channels to be asked on after Oko Desktop (oko contact). An ask every tried channel refused stands undelivered, as oko asks list prints it: the row says nobody was reached, lists each refused channel with its cause, still takes an answer, and Deliver again tries every channel that has not taken it (oko asks deliver ID); Oko's service tries again by itself as a channel comes back. Request history shows what oko asks history [--session ID] prints: every ask with its standing and the person's answer word for word, and for a named session its chat requests newest first — the answer the turn ended on, the commands it put on the clipboard, each grant it asked the person to confirm in their window on one line with what the window answered, and the person's reply. A history Oko could not read shows oko's own sentence. |
| Work | Automation | Inspect context sources, read Slack readiness, change the local autonomy policy and manage Stado-owned recurring work with verified job results. |
| Work | Innovation | Read every proposal of the innovation register the way oko innovation list prints it: its problem, owning product, measure, baseline read at the proposal, promised figure, cost, the operator's words it answers, its defect, verdict and, once measured after its release, its effect and the release (<product>@<commit>) it was read after. Propose records one as oko innovation propose does (title, problem, product, measure, window for a resources measure, promised figure, cost, quotes as SESSION#ORDINAL, sources with ; between them), refusing a figure that is not a number by name and showing the product's refusal word for word; Edit on a pending proposal changes its title, problem, promised figure or cost and adds sources, as oko innovation edit does, keeping each field left empty; Derive runs oko innovation derive and lists each proposal recorded, each one refused with the moments it cited, each moment left open with its outcome, or each moment waiting and why nothing was asked. Measure what is due runs oko innovation measure; Withdraw takes a pending proposal back with a reason, as oko innovation withdraw does. |
| Evidence | Projection | Verify transcript-root coverage and rebuild the search projection. |
| Evidence | Activity | Read prompts, tokens, and measured frustration for the stated scan window, and under One-off repairs the commands agents ran by hand against live state as the index recognised them: the catalogue counts by category with how many sessions the detector has read, each category beside the product command that owns its outcome, and the newest findings with their outcome — the same rows and the same routes oko oneoff list --all prints. Under Models, the table oko stats models prints: per model the active agent time, tasks done, defects repaired and closed, time, tokens and cost per item, the median time from assignment to repair, repairs taken back, records withdrawn and pushes refused against commits, with the work no transcript traces to a model in its own unattributed row; the hours field names the window (empty reads all history) and Rate models re-reads it, and a refused read shows oko's own sentence. |
| Evidence | Goals | Inspect transcript-derived goals together with provenance. |
| System | Sync | Decide whether LAN mirrors and object storage are current. |
| System | Routing | Confirm the loopback proxy is the only model route. |
| System | Settings | See every path Oko reads and writes and the boundary it never crosses. |
On Sessions → Start session, an empty model lets Oko choose from the declared mix using OMP's measured free account usage when OMP is selected. The chosen provider/model is an identity OMP recorded in model_usage, not a shortened alias that could select a different model. The result shows the model that actually started. If Brama refuses a lease but OMP starts on its selected account, the same result names Brama's refusal, model and account; a failed account measurement or a harness requiring Brama remains a refusal. Agent sessions then shows the provider and model recorded by the harness. An OMP session stopped on its provider also shows still served by: the accounts OMP holds that still serve its model now (an enabled account with a current usable reading of every limit window that binds the model and no block OMP holds against it), least used first with each one's used fraction, as oko sessions agents prints it and as servingAccounts in its JSON; no account OMP holds still serves this session's model when none does, and serving accounts not read with the cause (servingAccountsFailure) when OMP's store could not be read. The supervisor moves the session to one of them instead of reporting that the account limit is spent.
A session with the supervisor role supervises and does not code: its row in Agent sessions says it reads, records, assigns and sends, and that Tama refuses its edits and commits in product repositories, which go to an executor. Tama's block-off-task-work refuses such a change naming the executors recorded under it, and the supervision pass Oko submits to it says the same: record the change as a defect, assign it to an executor and send it.
When a recorded supervisor's turn has ended and Oko still holds open work, or its last answers repeat one another (the row's gives the same answer N times in a row), the model policy submits a supervision pass into its broker terminal. Agent sessions shows supervision pass: continued with the open count, whether the turn ended or the answers repeated, and the terminal, or the actual refusal; Run model policy shows the same decision through the native sessions API. The pass carries the items nobody owns and one proposed assignment per executor, the oko defects assign line from oko work backlog --supervisor <session> (or why the backlog could not be read), and asks the supervisor to dispatch before it reads the standing oko sessions supervision receipt, agents and work registers again. The standing routine continues to settle executors even when the supervisor has not taken another turn. A task-map read failure or a terminal that cannot take Return is shown as not continued, not as an empty successful pass.
These continuations do not wait for the next pass. The service that owns the broker terminals follows each terminal's transcript and continues a supervised session the moment its turn ends, with the same decision the pass would make; Agent sessions shows it on the session's row like the pass's own, seconds after the turn ended.
Sessions → Agent sessions → Intervention names an open defect before a supervisor redirects, approves or interrupts an executor. The message is submitted through Oko's broker; Assign this defect writes the executor as owner through the defect register. A missing, closed or repaired defect is refused before the keystroke; a failed delivery shows the actual cause. The Defects screen lists each linked action with its message, result and time and shows a repeated intervention shape at high priority. The same contract is available in oko sessions send and oko sessions interrupt. A session outside Oko's broker cannot be typed into, so its row offers only Interrupt the running call, which ends the processes the call started with the request to terminate; every row shows its last interruption: how the call was ended, each process signalled, and each checkout it left mid-operation (a rebase, a merge, an index lock) with its branch, unpushed commits and the defect recorded for it.
Sessions → Scorecard shows what oko sessions scorecard prints, through the native API's scorecard action: one card per session (every live one, every one with a recorded role, and with hours every one written in that window) with its model and role chips, a stalled now chip when its running call stands past its norm, the commits it recorded as evidence and their repositories, the defect repairs those commits carry, the consent dialogs it stood on, the calls interrupted in it and the seconds they stood, and the supervisor directives the reflection pass read as ignored; then the same figures summed per model. A register that could not be read is a warning panel naming it (Register commits not read: …) and its figures are absent from every card, never shown as zero.
The same Scorecard shows verified repair credits and charged standing per session and model. A credit needs a current repair whose measure and required verdicts hold on main, with its author and model from the Transcript Lake commit call. Factor rows cite priority, blocked work, class, products, changed files, reopenings and peer speed; pending or withdrawn credits name the cause. Standing is credits minus distinct compiler-charged commits, never an assumed zero for unreadable builds or authors. Read oko sessions scorecard credits --json or native sessions/scorecard_credits for the same projection.
The card declares neutral, concerned or frustrated supervisor wording and close review of every tool call versus structural sampling. A missing measure names wording unread, not neutral; oko scorecard standing --json exposes it. Defects → Supervisor review shows watched calls with arguments and transcript beside questions and refusals; answers send defect-bound redirects. A scan does not pause the next tool call. Read all it said on an entry shows its executor's whole transcript, as oko sessions messages <executor> --json prints it: every record in order with its time and kind, every call with its arguments, every result marked when it failed, and the lines that are no record named as unread.
Fleet report, inside that same scorecard, groups delivery and usage by executor, model, provider, product and organization member. Select a dimension, expand a group, then expand Daily trend or a metric's Source records. The screen displays the shared report's counts, median and p90 durations, release-qualified repair outcomes, hangs, usage per closed position and repeated question/defect classes without recomputing them. Refused metrics show the missing source and cause rather than zero. Cost is explicitly an API list-price estimate, not a subscription invoice. oko report and the native sessions/report action return that same projection.
Defects → Evidence reflection reads the same retained entries as oko reflection list. An infrastructure observation keeps its machine, service, failed operation, observed cause and state visible while judgment is pending. When Brama assesses it, the separate assessed cause, proposed repair and completion criterion appear beside the original evidence; a pending failure shows its cause instead of a fabricated verdict. The defect link remains visible when ordinary admission records it.
Blockers → Forecast shows what oko work forecast answers through the native API's blockers/work_forecast: the end from the holders' estimates laid on the live executors (a floor while an item is overdue), each queue, every take without an estimate with Ask <holder> to estimate, which leaves the holder a message naming oko defects estimate the way oko sessions send <holder> --defect <id> does (the screen writes no estimate under the holder's name) and shows the send's refusal word for word, every overdue and unforecast item, and when each release of the waiting graph unblocks. Blockers → Resources shows what oko work resources measures over the typed window, and with an added count the end with that many more sessions, hosts or subscriptions and the bottleneck; Declare keeps the hours sooner worth asking for (oko work resources declare) and Ask the operator sends the asks oko work resources ask would.
Every destination states the operator journey it serves. Surfaces that cannot name a real source or decision do not ship; there is no engaged-time or cost screen, because OkoKit exposes no such measurement.
Start with an existing transcript source#
First run opens a source-setup screen before the narrative walkthrough. Settings > Transcript source exposes the same reusable screen, and Show it again replays source setup before the walkthrough. Choose an exact runtime/root pair discovered by Transcript Lake, or skip to keep an empty usable Oko.
Adopt and initialize catalogue calls OkoKit's product-owned TranscriptSourceAdoption operation. It never shells out to oko and never parses a vendor file in the GUI: Transcript Lake validates, masks, ingests, and persists the source, then the existing CatalogueClient indexes only the Lake's Oko projection through its one long-lived TranscriptIndexer. Completion requires the persisted Lake receipt and finished catalogue pass.
The result names the stable source and imported, unchanged, conflicting, rejected, incomplete-final-line, and catalogue counts. No credential or transcript contents are rendered. Missing Lake CLI, no discovered root, malformed or unsupported input, a writer lease conflict, or a failed destination write remains a failure and does not dismiss first-use source setup.
Slack agent settings#
Settings > Slack agent writes the preferences oko suggestions tick reads for a Slack request: Harness (oko.agent.kind), chosen from the runnable harnesses oko agent harnesses lists, with nothing chosen for you; Execution root and Projects root (oko.agent.executionRoot, oko.agent.projectsRoot, ~/ read against HOME); Execution host and its root (oko.agent.executionHost, oko.agent.remoteExecutionRoot), where the chosen harness runs over ssh with oko agent run --harness ID; and whether the agent may run any shell command on this Mac (oko.agent.fullHostAccess).
Until a harness is chosen a Slack request is refused with no Slack agent is chosen: set the Oko Desktop setting oko.agent.kind to one of <ids>, and the screen shows the same sentence. A setting oko cannot read is refused by name rather than read as unset: cannot run /usr/bin/defaults read ai.wisent.Oko setting <key> must be a positive whole number; it is "<value>".
Automation#
Automation calls OkoKit, not the Oko CLI. Context shows source states, timestamps, errors and retrieved documents. Inspect Slack performs read-only authentication and conversation checks; missing grants show their exact consumer and failed read without displaying tokens.
The autonomy panel shows this Mac's policy and recent attempts. Applying Disabled stops new ticks, not an active run. Experimental and Full describe their scope before selection; neither enables payments.
Create a paused routine with a name, operation, existing execution host, five-field cron and IANA timezone. Deliver learned rule proposals runs Tama's digest on the operator's Mac; creation itself sends nothing. The editor retains its UUID before submission. Inspect creation recovers a lost response using that identity. Run now / retry retains its token; an uncertain response reuses it, while a confirmed click requests a new run.
Pause, resume and confirmed removal affect future occurrences. Inspect result reads Stado's job and verified routine-result.json: source documents, accepted Pursuit receipt, delivered and pending proposals, successful channels, failed channels, unreadable evidence or the actual refusal. A completed process without its Oko result is not labelled successful.
Rules the client may not break#
- Every screen uses the shared Wisent design system; the repository carries no second local card, badge, panel, or button language.
- A failed read retains the last known value and adds the backend's exact error and reproduction command. It never turns a broken SQLite read into an empty catalogue.
- Not configured is neutral. A missing measurement is a dash, not a fabricated zero.
- Every number states its scope. Activity scans a bounded set of files; Projection counts indexed rows. Neither may call its value a total when it is not one.
- Secrets are presence booleans. Bearers, agent secrets, object API tokens, and capabilities never reach a view, log, or pasteboard.
- Destructive writes are confirmed and scoped. Projection pruning computes against every indexed session id and never rewrites the merged catalogue.
Refresh and cost#
Independent reads execute concurrently. Transcript statistics are the slow path because they open source files, so Activity shows a named loading state while the rest of the window remains usable. A stale response never clears already-rendered content.
The Desktop task ledger documents message ownership, task relations, batch qualification, completion checks and their concrete refusals.
Window and packaging contract#
The app creates a usable window when macOS restoration points at view types from an older release and returns no window. The Swift package product is OkoWorkspace even though the installed bundle and executable remain Oko.app and Oko; this prevents SwiftPM from resolving the dependency's older executable product with the same name.
From the Oko Desktop checkout, stado product swift run oko-desktop-bundle builds and verifies .build/Oko.app. Add --install to replace ~/Applications/Oko.app, or --into APP to choose a destination. --identity NAME accepts a complete certificate name or SHA-1 fingerprint. Certificate purpose is read from Apple's certificate extensions, not inferred from its display name. Ad-hoc signing and unrelated destination bundles are refused. The bundle includes SwiftPM resources, Sparkle and the shared identity helper. Installation asks a running destination app to quit, replaces only a verified bundle and reopens that app in the background. A stopped app stays stopped. If the running app refuses to quit, nothing is replaced. This command does not install or update Oko's CLI.
Build, signing and installation share one private staging bundle. With --install, .build/Oko.app is published only after the installed copy has been verified. Another build therefore cannot replace the installer's input between signing and installation.
For a build host that signs through a separate authorised process, use stado product swift run oko-desktop-bundle --prepare. This assembles .build/Oko.app without reading signing keys or invoking code signing. Its output is explicitly unsigned, not ready for installation. --prepare cannot be combined with --install or --bundle.
The normal combined build and installation command signs with the vault item that plays the fleet signing role (stado:role:macos-development-signing; WISENT_CODESIGN_ROLE names another role); no vault item is named. The shared Stado product signer also accepts certificate and matching private-key PEM or an explicitly selected signing keychain. Keep stado 0.22.0 or later on PATH. Supplied key material uses an owner-only temporary keychain without opening a system dialog. The builder signs the embedded helper as ai.wisent.identity.keychain-helper, then the app as ai.wisent.oko, and checks continuity against the corresponding installed artifacts. An explicitly selected credential that cannot be read remains a failure: the builder neither chooses another identity nor replaces the installed app. Without supplied signing configuration, the local-keychain build remains available; Developer ID keeps its hardened-runtime and timestamp options.
Local keychain signing is not necessarily interactive. If the matching private key already authorizes /usr/bin/codesign without a password prompt, the same Wisent Products signer can use it with --identity set to its complete certificate name or SHA-1 fingerprint. Keep --previous for both the helper and app, then install the verified bundle with --install --bundle. A different certificate in the shared signing item does not establish that the existing local identity is unavailable. A certificate listing alone also does not prove private-key access: check the existing signing authorization, without changing it or exporting the key. The signer verifies the old designated requirement before replacement and records an identifier-and-team requirement for later updates. This path relies on existing authorization; it does not authorize resetting macOS permissions or triggering consent to make another key usable.
Build and installation failures preserve the failed command or filesystem operation. A staging cleanup failure includes the original operation's error. If replacement fails and reopening the preceding app also fails, both errors remain visible. Automatic reopening does not request application-location prompts; this is not a promise to bypass macOS Gatekeeper or privacy consent.
For an existing artifact, add --bundle APP to --install. This skips compilation, not verification: the installer checks the complete signature, Oko identifier and Apple certificate chain before replacing anything. stado product swift test --test-product OkoDesktopPackageTests --filter BundleInstallJourneyTests, after building the bundle, exercises installation, replacement and refusal of unrelated destinations. With a signing role readable, it also builds and installs through that real credential, verifies both code identities, then checks that an explicitly missing credential leaves the installed signature and executable unchanged. Receipts remain under .wisent-output/bundle-journeys/; temporary inputs are confined to .build/.
WisentAuth gates the window and threads the signed-in user id into OkoModel.ownerId. That identity scopes shared-session publications, so a registry Oko could not reach reports not read rather than zero.
Migrated from src/content/docs/desktop-interface.md