Product
Release notes
Every published Oko release and the current unreleased changes.
Changelog#
All user-visible Oko changes are recorded here, newest release first. Oko is a macOS desktop application; every published release ships a signed application archive plus a Sparkle appcast.xml, and installed copies update through that feed.
Releases up to and including v0.2.11 were published from .github/workflows/release.yml with the body text Automated release <version> and carried no release notes. Every section below was therefore reconstructed after the fact from the Git diff between consecutive published tags; each section says so explicitly. Changes after v0.2.11 are listed under Unreleased until they appear in a published release.
v0.1.0 is a GitHub draft release and never became a published artifact, so it has no section. v0.2.5 was never tagged or published; the jump from v0.2.4 to v0.2.6 is expected.
Unreleased#
- A message Oko delivers into a session is no longer read as the operator's own words. Every
oko sessions send, inbox message, resumed or adopted session's first prompt and model-typed terminal text opens with “[Delivered by Oko, not typed by the operator]”, and the inbox reader hands messages to OMP attributed to an agent.oko tasks messages,oko tasks rules, the task ledger and every reader of his words leave those turns out, and also any earlier turn whose text the session's inbox records as delivered to it; an unreadable inbox message refuses the reading by name.
oko work progressno longer falls silent when nothing moved. Its time left names its source: with anything moved in the window, the unclaimed items at that rate (source: rate); with nothing moved, the endoko work forecastreads from the holders' estimates (source: estimates), and every take without an estimate is named underunestimated.oko defects estimate <id> --minutes N --by SESSIONrecords the holder's estimate of its take once;oko work next --executorprints the command after the item it takes.oko work forecast [--all]lays the corrected estimates on the live executors, each estimate corrected by how far that session's, else its model's, else every estimator's estimates missed on closed items, and says when each release of the waiting graph unblocks. Oko Desktop → Blockers → Forecast shows the same answer and asks the holder of each take that lacks an estimate to record one.oko work resources [--hours N] [--add N]measures what the sessions stood on: the host's load, processors and processes waiting on the disk; calls building, on version control and on serial gates (each call shape judged once by Brama and kept); Stado's announced waits; provider capacity refusals; and the operator. With--add Nit answers the end with N more sessions, hosts or subscriptions, each unchanged with its reason where the resource is not what the work waits on, and the bottleneck.oko work resources declarekeeps the gain worth asking for and each resource's cost;oko work resources askasks the operator throughoko asksonly when a resource passes it, and for the serial gates' repair when they are the bottleneck. Oko Desktop → Blockers → Resources runs all three.oko innovation propose|list|show|edit|withdraw|measure|derive: proposals that change a product or the factory, each with the problem, the owning product, a measure Oko reads (a declared signal, a figure ofoko work resourcesor ofoko work forecast) whose baseline is read at the proposal, the promised lower figure, the cost in executor hours and the operator's words quoted verbatim from his transcript. Each is carried intooko workas a defect of its product; once that defect records its repair and a published release carries it, and the measure's window, when it has one, has passed since, the measure is read again and the proposal is kept as a pattern when it fell and rejected when not. Every model policy pass measures what is due and has Brama propose from the open frustration moments no recorded proposal cites, offering a moment again only when a new one arrives or what it was offered under changed. Oko Desktop → Innovation shows the register, proposes, derives, measures and withdraws a proposal.
- Oko keeps a model of the operator as a person (
oko person, Oko Desktop's Person screen): each of his messages is assessed once by the model on declared dimensions — whose words they are, whether an agent can act on them without guessing, whether they say when the work is done, how much of the method they leave to the agent, and whether they decide something and say why — and every reading pass assesses the stored messages the register does not hold yet.oko person decisionssays which of his rulings stand, which a later decision replaced and after how long, and which nothing enforces;oko person traitkeeps what he said about himself in his words;oko person showgathers it with his frustration causes and what relieves him. A correction of his stands over the model's reading.
- A defect the operator reported (a standing ruling names it, or a moment of his frustration names it as the cause) is repaired only as a class:
oko defects repairedwaits for its class probe listing every instance across the products and for its prevention, declared with the newoko defects prevention <id> --kind gate|record-refusal|commit-review --refuses TEXT --by SESSION. A defect recorded later with that class's shape after its repair is a recurrence: a broken verdict on the repair's claim reopens the class repair and charges its session and model on the scorecard, the class leadsoko work, and the improvement loop'soperator-shape-recurrencessignal counts it at the level the operator declared.oko defects showand Oko Desktop's Shapes section show the reports, the prevention and the recurrences. oko defects repairedon a defect without its mechanism shape or a shape review at the repair revision is refused once with every step it still needs, in the order they are taken (shape, completion measure, shape review), each with its command, instead of one refusal per missing step.oko defects shape-reviewon a defect without a shape names theoko defects shape <id> --shape …command.- Goal status and
oko goals checklist a goal snapshot that holds no goals or prompt-mappings array as unreadable, with its cause, instead of counting it as empty, and status is available only when every snapshot of the member was read. The responder's metric replies say a figure the metrics did not carry is unknown instead of printing 0, write the active span as<h> h <m> min, and a date reply whose message timestamp cannot be read says it read this machine's clock. - The responder's work metrics no longer answer a failed read with a value nobody measured: an unreadable request store gives
open_countnull with its error instead of 0, a terminal context without sessions gives null counts, a catalogue answer that is no array of sessions is refused naming it, a Slack cache row withoutchannel_idis listed underunreadable_lines, a session's missing first prompt is null, and a row naming no cwd is counted under(the catalogue row names no cwd). The running-harness count reads the kernel's process table, notps. oko sessions orphansreads the process table from the kernel too, so it lists wherepsmay not run. Itselapsedis now the seconds since the process started (<n> s) instead ofps's[[dd-]hh:]mm:ss, andterminalthe device name the kernel gives (??without one).- A completion probe that names the claimed repair commit through a ref or revision expression (
git rev-list {revision}..mainwhile main is the repair,HEAD, a tag) is refused like one that names its id: each name a probe word holds between shell separators, range dots and the<revision>:<path>colon is resolved in the repair's checkout. Before, only hexadecimal runs were asked, so a probe spelled with a ref recorded a repair that only the commit's presence answered. - The process table is read from the kernel (
proc_listallpids,proc_pidinfo,KERN_PROCARGS2) instead of by running/bin/ps, for the child processes and waitsoko sessions agentsshows under a running call, the transcript writer's parent the control pass checks and the command line of the process that signals the broker. A harness that may not startpsno longer sees those reads fail withthe process table cannot be read: /bin/ps failed: Operation not permitted.oko sessions orphansand the work suggestions' count of running harnesses still readps. - A repair a register read moves to claimed keeps what it said: the dated note now carries the displaced repair detail, repair evidence, closing detail and completeness beside the revision and the reason, where the migration used to clear them and keep only the revision. Oko Desktop → Defects labels a recorded repair revision "Claimed repair revision" unless its measured standing is verified, instead of "Verified repair on main" for every recorded repair.
- A broker that finds the ownership lock held while nothing answers on the control socket says so:
another process holds the broker ownership lock no control socket a client can reach; …. It used to name "the broker answering on <socket>" when none answered, so a service whose socket file had gone while it still held the lock read as a running broker of another build. - A service whose build an install replaced runs each later model policy pass with the installed build (
<installed> sessions policy run) instead of its own. It keeps serving its own terminals until the last one ends, and while a session it started lived it went on running the replaced build's pass indefinitely; the service log says which build ran each pass. A session's state is read from its last two turns (two answers or two prompts), not its whole transcript: a session that ends its turns on tool calls had its whole transcript, hundreds of megabytes, decoded on every pass, and the service's memory peaked at several gigabytes. The continuation step reads the owed tasks of every stopped executor's lineage once per pass, not once per executor, and a transcript's operator turns are read one line at a time, keeping only what can be an operator turn: one pass measured 969 s of continuation and a 7.9 GB memory peak from reading the same transcripts whole for each executor.
- An OMP session starts only on a model OMP still lists. Oko's mix resolves a declared model to the selector OMP last used for it; one OMP used once and no longer serves (
omp models --jsondoes not list it) is refused by name and the next declared model is chosen, instead of starting OMP on it to stand at its "model not found" screen with the start waiting forever. Which account a limit window binds is OMP's own scope of the limit, read from its cached usage reports, not the limit's display label. Real test: the measured-session-start journey (tests/sessions/model/unleased.rs).
oko sessions resumeno longer strands an OMP session Jeden cannot run. The session still moves to Jeden first; when Jeden cannot start it (it reaches its models only through a Brama lease, and Brama refused or could not be asked), OMP, which wrote the transcript, continues it on its own credentials. Standard error says which harness could not continue it and why, and the answer names the runtime that ran it andstandInFor: the runtime that could not, the ledger the move imported and its cause. Real test:tests/sessions/lifecycle/stand_in.rs.
- Without Brama the asking session's attestation stands in for the decision guard's judgment:
oko defects record,oko defects assignandoko sessions send, refused while Brama could not be asked or gave no answer, run withOKO_ATTESTATION="<what you checked and why the operation holds>", and each attestation is kept inattestations.jsonlwith the operation and the cause. See the overview; real test:tests/no-model/attested.rs.
oko roadmap push --file PATHwrites a product's whole declared roadmap from one JSON file,{"product": P, "items": [...]}, each item carrying the fields the flags take. Every item is judged before any is written and the rows go in one write, so a file with a blank field, an undeclared status, a slug declared twice or an uncatalogued product writes nothing and names the item and the file; pushing the same file again updates the same rows. A product with hundreds of planned features keeps them in its repository and pushes them with one command instead of one call per item. The command reference now publishes one page per roadmap command instead of two that disagreed. See/docs/cli/roadmap/push; journeytests/roadmap/catalog.rs.
- A paged read of the shared database is one wait on standard error: one
czekam: GET <table> page by pageline before its first page and onekoniec czekania(orblad czekaniawith the failed page's address, time and cause) after its last. Every page used to write a start and an end line of its own, so oneoko tasksread that pages through the readings table came back as 118 wait lines, 27,798 of its 29,669 characters, around a few lines of answer. Seeoko database read; journeytests/database/paged_waits.rs.
- Steering is every organization member's, not one operator's. A ruling now names its author — member, email, organization and central role, read from the organization's register — and
oko rulings recordrefuses a member the register does not give steering: owners steer by role, anyone else through Oko's grant ofsteering.decide, which an owner manages with the newoko rulings steering list | grant <member> | revoke <member>and Oko Desktop → Rulings → Who steers.oko rulings supersedeand gate reconciliation accept a later decision only from a member who may steer (an owner's decision only from an owner) and keep the replacement's author and the precedence rule on the superseded ruling.oko tasks mapnames each entry's author and counts tasks by author;oko tasks rules --allnames each rule's author. An answer to an ask, from any surface, is kept as the answering member's message of the sessionask-<id>and read, placed and listed like a transcript's;oko tasks readkeeps answers Oko iOS wrote straight to the database and reports them underanswers. Real tests:tests/rulings/steering.rs,tests/rulings/superseded.rs,tests/asks/journey.rs.
- A shared-database request whose access token expired while it ran (Supabase's
JWT expired) is sent again with a refreshed token as before; when that second request fails too, the refusal names both answers:the access token expired during <method> <address> (after <ms> ms): ...; a refreshed token was exchanged and the request sent again, which failed: .... The time the first request ran until the token expired is the lead, not a bare 401 that reads as a sign-in problem.
oko asks ask --session <id>reads, before anything is sent, the person's replies to what that session already asked him in chat (oko asks pleas): his reply to the session's newest chat request, and to an earlier chat request for a command the ask admits (--admits) or for the grant it names (--grant), refuse the ask with his words, when they came and how the session asked (the command on his clipboard, the grant and what the window answered), until the asker names each reply it read with--answered <answered_at>. Nothing he said is interpreted: his newest reply weighs most because it has to be read before he is asked again, whatever it says.--answeredrepeats and also takes the id of an earlier answered ask. The grant is compared as JSON with the arguments of the grant tool call the session made in chat, so--grantis that arguments object; a--grantthat is not JSON is refused with the parser's sentence, since it could never be matched with a request already made. Real test:tests/asks/pleas.rs.
oko sessions resumeand a control pass continue a Jeden session in place: Jeden is given--resume-session <the session's directory>(ledgerResumein runtimes.json), so it continues with the session's history and chat memory, and the delivery is proved by the message as the newest task of that same ledger. A barejeden runopened a new session that knew nothing of the work. An OMP session handed over to Jeden still opens a ledger of its own.
oko sessions list --externalandoko sessions adopttake a running Jeden. A process's session is read from the live transcript it holds open as Transcript Lake's sources declare it, so Jeden's.jeden/sessions/<id>/transcript.jsonlnames its session as a Claude Code or Codex file name does; the catalogue resumes Jeden with--resume-session <id>, and an adopted Jeden is continued as a task of that transcript on a subscription lease, asoko sessions resumestarts it, instead of being refused for having no proxy overlay. A Jeden that has opened no session is no candidate. Real test:tests/agent/external.rs(agent-external, withJEDEN_BIN).
- Oko Desktop's Sessions screen lists the agent processes outside the broker and adopts one (native API
sessions.externalandsessions.adopt, OkoKitOkoSessions.external()andadopt(_:text:submit:)), and its Run model policy prints each stalled command the pass reported.
- A writing
oko taskslisting writes a session's ledger only when it would differ from the one this machine last wrote: the kept settlement records digests of the messages and of what the register held about them, with the build, and an unchanged session is neither reconciled nor written again. The ledgers that are written are written side by side, each settled from the ledger its write derived, and a read-only reading digests nothing. Rewriting every unchanged ledger one after another kept a writing listing of every live session running for hours. WithOKO_TRACE_REQUESTS=1the settle phase counts the ledgers written. Real test:tests/tasks/ledgers/unchanged.rs.
- A command that runs past every completed call of its shape is a defect of the workshop product it runs. Each
oko sessions policy runreports the stalled sessions understalls(stalled: <session> (supervisor <id>) on shape as a defect of that product and notes later ones on it once. OkoKit answers them asPolicyPass.stalls. Real test:tests/stalls/report.rs`.
oko asks historyreads every ask with its answer, and the native asks domain and OkoKit'sOkoOperatorAsks.history()answer the same;oko asks askprints the new ask'sidat the top level.
oko tasksrefuses--opentogether with--sessionoutside--owed.--openadded every live transcript on the machine, however old, to the sessions named and, without--read-only, wrote each one's ledger, so a caller after one session's open work that typed both ran for hours. The combination is now refused before anything is read, naming both readings and--open --read-only --owed --session ID, the one that takes both. Real test:tests/tasks/scope/listing.rs.
- An ask for a grant carries its whole case.
oko asks ask --grant <capability>also requires--lifetime,--admits(each exact command),--effect,--unblocks(each Oko object),--tried(each cheaper route and how it was refused),--riskand--undo, and is refused before the database is read naming every missing part and why the person needs it. The case is written into the ask's detail under one label per part, so Oko Desktop, Oko iOS andoko asks showshow it. Real test:tests/asks/case.rs. oko asks waitjoins again with a fresh token when the Realtime server refuses the one it was sent. A token with seconds left could expire on its way to the join (Token has expired 0 seconds ago), and that refusal ended a long wait. The refused token is now exchanged as a REST read exchanges one afterJWT expired, stderr says so, and only a second refusal with the fresh token ends the wait, naming both.
- An ask nobody was reached with stands
undelivered, and reaches the terminal.oko asks askalready refused an ask no channel took, but the ask stayedwaitingin the database, sooko asks list,oko asks showand Oko Desktop showed an escalation as made that nobody had received, and every delivery of it needed something the ask might be about: a registered iPhone, an attached Oko Desktop, Stado's pages through the vault. Now a waiting ask every tried channel refused standsundeliveredwherever it is read;oko asks deliver [<id>]delivers it again on the channels that did not take it, refused the same way when still none does; Oko's service delivers every undelivered ask again by itself when an Oko Desktop attaches or a terminal of it is watched, and each control pass (oko tasks control, its newasksstage) delivers them again for the channels that recover unseen. The service also writes every ask into the terminals somebody is watching (announce-askon the broker, theterminalchannel): the one channel that needs no phone, no Desktop, no Stado and no vault, answered withoko asks answer <id> --text … --on terminal.
- A replaced Oko service hands its control socket to the installed build at once. Retirement used to keep the socket until the last terminal ended and refuse every new terminal meanwhile ("Oko is retiring; no new terminal was started"), so after an install no session could be started or resumed while one long terminal lived. The retiring broker now admits no new terminal, answers for the terminals it started on
<socket>.retired.<pid>, releases the control socket and starts the installed build on it; it ends with its last terminal.oko sessions listlists those terminals on the one socket withbroker.heldByand names each retired broker with its terminals underretiringBrokers;send,screen,resize,repaintandendfor them go through the control socket and are answered by the broker holding them.oko installsays the installed build serves new terminals now. A build started this way runs with its input closed and its output appended to<socket>.serve.log, never to the output of the process that started it, sooko installreturns at once and names that log; a log that cannot be opened is refused with its path and cause. Oko Desktop's Sessions screen reads the same inventory through the service's native API: a section "Replaced service still holding terminals" names each retired socket with its terminal ids, such a terminal's row carries "held by replaced service", and its screen, send, end and share reach it through the broker holding it.
- An unset or unreadable
HOMEis refused by name —HOME cannot be read, so nothing under the home directory can be found: <cause>— by every command that keeps something under the home directory (the registers, the suggestions state, the chat cache, the calendar store, the strategy cache, the judge copies, the pricing catalogue, hooks, Ghostty, the agent launcher, credential reads and the workspace checkouts revisions are verified against). It used to be read as the empty path, so those files were read from and written to the directory the command ran in. Live transcript discovery prints the same cause and finds nothing. - A Skarbiec vault named by
SKARBIEC_VAULT_FILEthat cannot be read, or has noskarbiecCLI installed, is refused with the cause; Oko no longer turns quietly to the broker and answers another store's value. - The approval channel and the team DM cache read the local Slack config through one reader: a config that cannot be read is refused instead of being rewritten from an empty map, which lost every other Slack setting. Approval-queue read failures are refused or printed, never read as no approvals.
oko context snapshotreports a cached source it cannot stat asunavailablewith the cause; only a missing file isnotConfigured.- Calendar providers and
oko calendarverbs are read through typed declarations; a resumed handoff that names no working directory is refused instead of starting in the home directory.
- A transcript directory Oko cannot list is no longer passed over in silence. The goals transcript walk adds it to the unreadable list, so a recompute does not prune the goals of the sessions inside it; telemetry, stats, the calendar,
oko tasks sweepand live session discovery print the directory and cause;oko lake surveyanswersunreadablefor each root. A hidden session whose transcript cannot be read joins the recompute's unreadable list too.
oko tasks judgerefuses to write the localjudge-health.jsoncopy when the existing copy cannot be read, instead of writing it back with no defect holding the outage; the shared observation is still written and the refusal says so.
- A goals recompute stage refuses, naming every transcript and cause, when a transcript of the window cannot be read, and no session-scoped goal is pruned while one is unreadable; before, such a session looked as if it had written nothing and its goals and mappings were pruned.
oko telemetry calendarrefuses naming the member and the transcript instead of drawing the day without its prompts.
oko stats snapshotreads each transcript once and answersunreadableTranscripts, every transcript it could not read aspath: cause; before, such a transcript silently dropped out of the prompt, token and frustration counts. The Activity screen shows the list under the volume counters.
oko telemetry work,tokensandforecastread each transcript once and answerunreadable, every transcript in the window they could not read aspath: cause;oko telemetry publishrefuses naming those transcripts instead of publishing a row that undercounts the member's prompts and tokens, and refuses a negative--days.
oko telemetry tokensno longer skips a token price catalogue it cannot use: a catalogueOKO_TOKEN_PRICING_PATHnames that is missing, or a chosen or installed one that cannot be read or is not a USD-per-1M-tokens catalogue, prices nothing and is named with its cause undersummary.catalogRefused; before, the next catalogue priced in its place without a word. Assignment plans state it as their cost basis.
- The subscription router config is refused with its path and cause when it cannot be read, is not JSON or is not a JSON object; only a missing file is no configuration. The local cost catalogue source reports
missingonly for a missing file, andunreadable: <cause>orinvalid: <cause>otherwise.oko subscriptions snapshotrefuses with the path and cause when the company context it reads team members from cannot be read or is not JSON; a missing company context is still no team.
- A company strategy update, the daily advisory and the work report's Slack section call
company-context.jsonor the Slack cache missing only when the file is absent. A file that cannot be read, or a strategy that is not JSON, is reported with its path and cause, and the advisory refuses instead of skipping. Closing a goal from Slack names every candidate transcript it could not read when no readable transcript proves the agent's completion.
- An autonomous goal run refuses when the goal's source transcript cannot be read, naming the transcript and cause, and when a redaction pattern in
goals.jsondoes not compile, naming the pattern; before, the pattern was skipped and the session text reached the agent without that redaction.
oko slack autoconfigureandoko slack statusrefuse with the path and cause whenslack.jsonor Claude's settings cannot be read, are not JSON or are not a JSON object; before, autoconfigure read such a file as empty and rewrote the Slack config from the other sources. When neither the webhook nor the bot token is usable,oko slack postquotes what Skarbiec answered for each, and it refuses an unreadable or invalid local Slack config instead of reading it as no configured channel.
- Installing or removing Oko's hook refuses with the path and cause when an agent settings file exists but cannot be read; before, the file was treated as absent and replaced by one holding only the hook.
- A plan's subscription basis names why no subscription cost was allocated: a local catalogue that cannot be read, is not JSON or has no subscriptions list, or the router snapshot's refusal. An unreadable local catalogue is no longer passed over for the snapshot.
- The controller keeps broker-owned workers occupied until the broker observes actual process completion, including between writes and after answer records while maintenance runs. It no longer terminates workers from transcript content or reports an inferred
reapedcount. Health and verification preserve an observed live writer's ownership regardless of answer records or silence. Process-observation failures retain the owning session, PID and OS cause. A task is diagnosed only when its verdict names an observed obstacle, not after a count of failed rounds.
- Agent sessions run on the models the operator declared, and a session can move to another one between turns. Oko's models.json names the mix (Opus, Astra and Sol, never Fable) and the harness a new session starts in (Jeden; OMP only when named).
oko sessions start --text TEXT [--harness NAME] [--model PROVIDER/MODEL] [--cwd DIR]starts a new agent session;oko sessions resume ID --text TEXT [--model PROVIDER/MODEL]continues one on the model named; without --model both pick the declared model the fewest live sessions on this machine run on, read from their transcripts, instead of whatever an old transcript recorded. OMP receives the model as its --model at start and resume; Jeden gets the newest model Brama's catalog lists under the declared name.oko sessions model ID PROVIDER/MODELwaits for the session's turn to end, ends its broker terminal and continues it on the new model — typing/modelinto a running harness reached the model as a prompt, so there was no way to do this before.oko sessions agentsshows each session's provider and model from its transcript and the live spread over the declared models; Oko Desktop's Agent sessions show the same with a Model menu that switches a session and a form that starts one.
oko tasks kindnow decides what every reader derives. A message can hold a reading from more than one member of the organization; the ledger took whichever row the database listed last, sooko tasks kind … questionanswered "it is no work owed" whileoko tasks show, the map and the control pass kept the task open under an older reading. The newest reading byread_atdecides now.
- A session whose turn the provider ended is a stopped session. An assistant record OMP closed with stopReason error (the provider refused or the request broke; errorStatus and errorMessage say how) or aborted (the turn was interrupted) ends the turn like an answer:
oko sessions agentsreads such a session asstopped: provider error 429 <the provider's sentence>; retry after <time>, withproviderError(status, message, at, retryAfterSeconds, retryAt, retryPassed) besidestoppedOn, Oko Desktop's Agent sessions show the same, andoko sessions resumecontinues it like any stopped session instead of refusing it as one still writing. The control pass does not hand such a session its work before the wait the provider stated (retry-after-ms=<ms>at the end of errorMessage) has passed — the handover would meet the same refusal — and lists it underretryingwith when;oko tasks healthcarriesretryAtin each session'srefused. Before this, the session read as answering for as long as its process lived, resume refused it, and nobody handed it its work until a person typed into its window.
- A task the model working on it will not do has its own status,
refused, and says which model refuses it.oko tasks state <task> --refused REASON --by <session>records the reason; Oko reads the model (and provider) from that session's own transcript rather than taking the session's word for it.oko tasks showprints "refused by MODEL (session S, TIME): REASON",oko tasks map --place refusedlists them, the control pass no longer hands such a task back to a session and Tama's stop gates no longer count it as open work.--unrefuse, or Take the refusal back on Oko Desktop's task contract, makes it open work again; a defect reported after the refusal reopens it too.
oko tasks healthandoko tasks control --statusread only what they show and print each session as soon as it is read. They used to settle every transcript on the machine, build every owing session's handover message and history, read each transcript whole four times and ask Stado for the fleet's build verdicts before printing one document at the end; one--sessionran for hours on Stado's object API without a byte of output. Now only the sessions the shared register holds a task reading for are settled, nothing a handover delivers is built, each transcript is read backwards from its end for its newest turn only, Stado is not asked (the control pass's qualification stage reads its verdicts), and each owing session is printed on its own line — state, open, diagnosing and blocked tasks, silence, last write, the model's refusal — before the totals and the last pass.--jsonprints the one document, as Oko Desktop reads it. The control pass and the judge read the newest turn the same way.
oko asks askreaches somebody or says that it reached nobody. An ask used to be delivered on Oko iOS alone, by the database, and a person with no registered iPhone was never reached while the command answered as if they had been. Every ask is now delivered on every channel the person has: the channels chosen withoko contact— Oko iOS, whose verdict the command now waits for as the database records it (Supabase Realtime on the ask's deliveries), and the ones Stado pages through — and Oko Desktop on the Mac the ask is made on: Oko's service hands the ask to every Oko Desktop attached to it as an ask watcher and waits for its word that the question is on the screen, where it opens as a sheet over whatever screen is open with one button per choice (or a field), and as a macOS notification once the operator lets Oko notify. Each attempt is one delivery row with the channel's own words, and every wait — on Stado, on Oko's service, on the database — writes theczekamlines. An ask no channel took is refused at once:ask <id> reached nobody: oko-ios failed (…); oko-desktop failed (…); …, the ask waits in the database, and the sameoko asks askdelivers it again on the channels that did not take it, so a retry is not a silent duplicate. Choosing no channel no longer refuses the ask before it is written: Oko Desktop is Oko's own surface and is always tried.oko asks ask --socket PATHnames the service to reach, as the sessions commands do. The sheet answers with--on oko-desktop, the same write asoko asks answer.
- A control pass finishes, and is written stage by stage. The pass standing on a model route that never answered — Oko's model reading operator messages, the judge, both through Brama as oko, and both behind
stado service directory connectand the credential reads it goes through — left no row at all, sooko tasks control --statusshowed the pass before it. The pass now runs as named stages (handover,reading,qualification,verification,batch), and its row in the shared database is written as each stage begins, naming what it is about to stand on, and as each ends: the answer and the row carrystage(doneonce every stage ended,failedwhen one ended in the pass's own failure, else the stage it stands on) andstageswith each stage'sstanding,startedAt,endedAtandfailed. A route Stado's resolver already holds unanswered is refused by Stado before it is asked, the stage records that sentence as its refusal (reading.refusal,verification.refusal), and the pass goes on and finishes.oko tasks control --statusandoko tasks healthread the row back aslastPassStage,lastPassStagesandstanding, and Oko Desktop's Control screen shows the stage the last pass stands on, since when and on what. The one-shotoko tasks controlrefuses without--openor--session, as its page promised;--allis gone from the page, since the command never read it.
- Which agent session is the supervisor, and which sessions execute under it, is recorded instead of written into a brief.
oko sessions role set ID supervisor --by WHO,oko sessions role set ID executor --supervisor ID --by WHO,oko sessions role show ID,oko sessions role clear IDandoko sessions role listkeep the roles in~/.oko/session-roles.jsonon the machine whose transcripts they describe; an executor works under a session already recorded as a supervisor, and a supervisor that still has executors cannot become an executor or lose its role.oko sessions agentsshows each session's role, supervisor and executors, and Oko Desktop's Agent sessions show the role with a Role menu to change it. The control pass's handover tells a supervisor, before its tasks, that it does not do them itself — no edits, builds, tests or commits — but hands each to an executor session withoko sessions resumeand follows it withoko sessions agents, naming its executors; an executor's handover names its supervisor.
- Oko no longer waits without saying on what, and a hung session is handed its work again whoever holds it. Every request to the shared database and every child process Oko waits on — Stado, git, Skarbiec, the transcript lake,
ps,lsofand the rest — writesczekam: <what>; gdzie: <where>; rodzaj: baza|proces; od: <UTC>; id: …on stderr before it waits andkoniec czekaniaorblad czekaniaafter — the line Stado writes — and the wait lines a Stado call prints are passed on as they arrive, so a control pass that stands onstado service directory connect bramasays so in its job log with the time it began. The control pass now has Oko's model read unread operator messages, and reads Stado's verdicts on handed-over sources again (qualificationin the pass), after the handovers, so neither a Brama that does not answer nor a fleet object API that holds requests can hold them;oko tasks handover,oko tasks healthand the stop check read the kept verdicts instead of waiting on Stado. A session somebody's window holds is left alone only while its turn is open: once it stopped or hung mid tool call for the hung threshold, the pass hands it its work like any other session and lists it underhung, andoko sessions resumecontinues such a session instead of refusing it as a live writer.oko sessions agentsshows the input the running tool was called with andhung, printed asHUNG on tool ….
- A supervisor reads and writes to agent sessions Oko did not start.
oko sessions agents [--hours N] [--json]lists every session a live process holds, and with--hoursevery session written in that window: title, directory, last write, the processes and broker terminal holding it, whether it is working (a tool call or result within the idle window), and otherwise what it stands on — the first sentence of the answer it stopped with, the tool it has been running since when, or the open Stado wait that tool printed — and whether it gives the same answer again. It reads only the two ends of each transcript and the processes' open files from the kernel, so it answers in seconds.oko sessions resume ID --text TEXT [--json]continues a session in a broker terminal by its own runtime (omp --resumefor OMP, which now has a declared runtime), records the start inoko_session_deliveries, and answers once the text is the newest prompt of the session's transcript, or of the file OMP moves the session to while another process holds it, naming that transcript and the terminal; it refuses a session whose turn a live process is still writing, and a terminal that ended without the text landing, with what that terminal printed. Oko Desktop's Sessions screen has the same list and a Resume with message control under Agent sessions. A control pass proves a delivery a resume left pending by the same landing, and reads whether the harness holds its transcript from the kernel instead of throughlsof. oko calendar addrefuses a timed event without--ends(calendar add requires --ends for a timed event; an --all-day event without one lasts its day) instead of giving it an hour nobody chose, and an add or edit whose end is not after its start is refused; Oko Desktop's add form sends the end the person picked instead of moving it a minute past the start. The OAuth redirect listener reads the redirect's request line to its end rather than its first 16 KiB, and the bundled first-use journey is no longer held to three to five screens.- One model call as Oko is
oko gateway send --prompt TEXT [--model NAME] [--router URL], printing{status, body, route}as JSON; it asks the declared model service or Brama, and after a refused declared route each chat model the gateway lists for Oko (never the decision alias, which Brama refuses for chat).oko proxy-call, which printedHTTP <status>,---and the body as text, is gone, and Tama's model-judged guards ask throughgateway send. The local proxy is one group,oko proxy serve|sign|translate, instead ofproxy-serve,proxy-signandproxy-translate. - The model service Oko asks is
oko model-service show|set|clear, the formeroko tasks judge-provider show|set|unset: it chooses for every model call Oko makes, not only the judge's.oko hooks bundle-checkis gone;publishandpullvalidate the bundle. - The terminal sessions Oko's broker owns are one object,
oko sessions list|screen|start|send|repaint|end|adopt|attach|publish|unpublish, the same operations Oko Desktop's Sessions screen runs through the native API.oko ptyand its seventeen verbs are gone:inventoryandbroker-listaresessions list,broker-external-listissessions list --external,broker-snapshotissessions screen ID,broker-spawnissessions start,broker-send|repaint IDtake the session as their subject,broker-terminateandbroker-pruneare onesessions end(an ID, or--dead,--older-than,--all),broker-adopt-externalissessions adopt TARGET,relay-attachissessions attach ID --relay URL,relay-publishissessions publish ID --relay URLandshared-endissessions unpublish ID. Every verb names the session by its ID and the broker by--socket;attachandpublishread the attach socket from the broker's own row.broker-serveisoko serve --socket PATH, andbroker-versionandbroker-ensureareoko api status, which now refuses a service that does not speak this build's broker protocol. oko sessions publishlists the session for the organization while it is published: it writes the member's shared-session row, renews it before it expires and ends it when the relay or the session closes. No build wrote that row before, so Oko iOS's Terminals list showed no session published since.oko defects recordnames every open defect a title restates in one refusal, and--distinct-fromrepeats: a different defect resembling two open ones was refused for the first, then for the second once the first was named, and could never be recorded.- A session whose first operator message reports a defect can write its ledger again. Oko made that message a task and also a defect under the same identity, and the shared database refused the write (
ON CONFLICT DO UPDATE command cannot affect row a second time), sooko tasks verifyfailed for every such session. The report now opens the task itself and records no second object beside it. oko routines standing [--host HOST]declares and enables the routines Oko needs on every machine:transcript-catalogue(brings the shared catalogue up to Transcript Lake),transcript-ledger(the newsweeproutine action: writes again every session's ledger whose transcript changed, however old) andtask-control(reads the stored operator messages no reading covers and hands open work on), each run one after the other. Running it again changes nothing; a paused one is resumed and a re-timed one is put back. Installing Oko's service runs it for that host, and Oko Desktop has the same operation under Automation → Recurring work. Before, nothing created them, so operator messages of sessions nobody kept open never reached a task.
oko tasks backlogandoko tasks mapread the stored messages of only the sessions a reading or an addition names. Reading every stored message of every session was cancelled by the database, so neither answered.
- A task whose every acceptance check is met with evidence (
oko tasks check --met --evidence …) is proven by those checks when it has nooko tasks evidencerow: the report carriesproof.checks, recorded when the newest check was met. Work with no commit and no report file — a print job, an answer — could not carry proof before, so a judge outage was never written on it and Tama's stop guard held every turn on it as open work. Now a pass the judge cannot answer marks it as waiting for the judge, as it does for a commit or a report.
oko roadmap pushrefuses a--productthat Stado's product catalog does not hold, naming the word and the catalog it was checked against, before anything is written. A roadmap item names a real product, so a test fixture or a typo can no longer become a row of the organization's roadmap.
oko tasksis the one command group for the task ledger. The listing and every verb that used to be spelledoko transcripts tasks …, together withmessagesandrules, now run asoko tasks …; each prints its answer for a person and takes--jsonfor the same answer as JSON.oko transcriptskeeps sources, adopt, reindex, search, occurrences, list and stats, and refusestasks,messages,rulesandone-offswith the command that replaced them (oko tasks …,oko oneoff list). The verbs are documented under/docs/cli/tasks; the older notes below keep the spelling they shipped with.
oko transcripts tasks mapreports every relation end that names a task no stored ledger derives any more (danglingEndsin JSON), and Oko Desktop's map shows the same as “Ends at a vanished task”.tasks relaterefuses a--bythat is neitheroperatornor a session id, in the CLI and in Oko Desktop alike.- The judge uses chat completions for every explicitly selected OpenAI-compatible service, even for a model named
decision-model. Its health probe rejects empty chat messages and absent typed answers instead of recording HTTP 200 alone as a working judge. A real CLI journey checks model access, saved health, credential-destination refusal and independently persisted completed and incomplete task verdicts.
Added
oko transcripts tasks kind <session> <number> <KIND> --by …records what one operator message is, with the kinds Oko's model reads from, when the model has not read it or read it wrong. A new kind,detail, is a fact, correction or decision for work an earlier message asked for: it is written onto that task and reopens it instead of becoming a task of its own, so many messages about one piece of work stay one task. A reading a session or the operator recorded is never read again by the model, and reading a message as anything buttaskwithdraws a mistakentasks add. Tama's stop gate and task-scope gate now name this command instead of turning every unread message into a task. Documented under/docs/cli/transcripts/tasks-kind.oko tasks judge-provider show|set|unsetchooses the model service the judge asks: Brama by default, or any OpenAI-compatible service whose API key is read from standard input and kept owner-only, so tasks are judged on a machine without Brama. Documented under/docs/transcript-intelligence.
oko defects reopen <id> --reason Ralso withdraws a pushed repair that turned out not to repair an open defect: the revision and its sentence move into arepair withdrawn:note and the defect counts as unrepaired again, so the one build no longer treats it as ready. Oko Desktop shows Withdraw the repair beside Repaired in, not built. See/docs/defect-register.
- What each operator message is to the task ledger is read by Oko's model, not by a word list.
oko transcripts tasks read [--limit N]and every control pass ask Brama's decision route one typed choice per unread message (task, do it, question, acknowledgement, rule, order, resume, paste, defect or remark, each defined in prose) and keep the answer in the sharedoko_message_readings; every listing, the Stop gates,tasks rulesand the judge read the kept kind without asking a model. A message nobody has read yet counts as work. An acknowledgement closes the tasks it names only when the model read the message as one. The operator word list and every family in it are gone; the product's own handover is recognised by the first sentence of its template, and a harness's resume is one of the harness prefixes.oko transcripts rulesprints who read each rule instead of the word that matched. Documented at/docs/cli/transcripts/tasks-readand/docs/cli/transcripts/tasks.
- Frustration is read by the same model, not a lexicon. The reading above also answers whether each message shows frustration with the agent, kept as
frustratedbeside its kind.oko stats snapshotcounts prompts read as frustrated per window and answersfrustrationUnread, the prompts not read yet; the word-group table (frustrationWordGroups) is gone and the Activity screen shows a Frustration reading section with the unread count instead.oko frustration scanandoko index syncmark a moment "frustrated: read by <model> (<confidence>)" beside shouting and repetition.oko index coveragecounts a session's signals read when the model has read all its operator messages. The lexicon and its shared term table writes are gone. Documented at/docs/frustration-registerand/docs/cli/transcripts/tasks-read.
- An operator instruction the word list did not read as work can be put on the ledger.
oko transcripts tasks add <session> <number> --by (operator | <session>)records the message as a task under the identity every reader derives for it; from then on the listing,tasks show,state,check,relate, the map, the handover andtama assignment showtake it like any other, withclassifiedBy"added by <who>", and a reindex keeps it. It refuses a number the session does not hold and a message that is already a task. Oko Desktop's Tasks screen does the same with Add a task on a session, so an assignment the list did not read as work no longer stays outside the ledger with no command to put it there. Documented under/docs/cli/transcripts/tasks-addand on the desktop interface page.
- A roadmap item is a task contract kept in Oko.
oko roadmap pushwrites one work item that says what the work promises, what the product already has, what it still lacks, and the acceptance that decides it is done; the row is keyedroadmap:<product>:<slug>, so pushing the same title again moves it instead of adding a second one, and an undeclared status is refused before anything is written.oko roadmap listreads the contracts back, and the MCP server serves the same rows asoko_roadmap_items, so an agent picks up future work from the store rather than from a conversation. Both commands are documented under/docs/cli/roadmap/pushand/docs/cli/roadmap/list.
- Oko catches the one-off repairs an agent ran instead of changing the product. When the index reads a transcript it recognises, in every recorded shell call, the shapes the operator's rules forbid — a service restarted or killed by hand, a grant or token issued from the terminal, an inline script or heredoc, operator configuration rewritten in place, a binary copied over an installed path, ssh past Stado, work under /tmp, rows changed with raw SQL — and keeps each with its tool-use id, whether it ran or a hook refused it, and whether a commit or push followed it in the same session.
transcripts one-offslists them for the sessions in scope or the whole catalogue, with how many sessions the detector has read, so a zero is a measurement and not an absence; the detector version is stored per session, so the whole history is re-read after this update. The independent judge is told which one-off repairs followed the task it judges and how each ended, and its standard says a fix request answered only by such a command is not done. The index is at schema 6; update the CLI and Desktop together. Activity in the desktop shows the same findings under One-off repairs. - Every one-off category names the product command that owns its outcome, and the finding is printed with it: once per category in
transcripts one-offs, under each meaning in--categories, asroutein the JSON, beside each row on the Activity screen, and in the context the judge reads, so a missing item can name a command instead of a wish. transcripts one-offs --since <7d|48h|YYYY-MM-DD> [--until DATE]counts a stretch of time instead of listing it: the total, the sessions, how many a hook refused, how many ran with no commit after, and the same split per category with each category's route, so "how many were there last week" is a reading that can be taken again rather than a count done by hand.transcripts one-offs --rulespublishes the shapes the detector counts by — category, rule, the product command that owns its outcome, and one example command each — and--gate-checkruns every example past the gates this machine has bound to bash, naming the hook that refuses each one and listing the shapes nothing refuses. Oko counts a one-off repair afterwards and Tama refuses one before it runs; comparing the two lists shows a shape nothing gates. A gate that refuses a benign control command too is answering about context rather than shape and is reported apart, so the count is what it says it is. A machine with no bash gate is told so, with the command that installs one, instead of being shown a clean sheet.- The operator ledger is refreshed from the bytes appended since the last refresh, not from the whole transcript: each ledger records how much of the file it covers and a digest of the last kilobyte of it, so a transcript that only grew is read from there, and one rewritten — a turn inserted before the others — fails the digest and is read whole, keeping every ordinal right. The harness stores are also walked once per process instead of once per session.
transcripts rules --open --hours 168went from twenty seconds to two on a machine with a 15 MB session, andtama assignment show, which reads it, from seven to one. This is a gate's correctness, not a comfort: a gate that reads the ledger has to answer inside the hook runtime's wait, or it gates nothing. oko brama checkreads the gateway's own/readyzbeside the bearer check: whether it is serving and degraded, its build and source revision, its own reason, how many models the agentokocan be routed to per provider, and every subscription it could not redeem or account it cannot route to. A bearer the gateway accepts says nothing about whether a request can be served.- A judge refusal Brama marks
retryableis waited out — three attempts twelve seconds apart,OKO_JUDGE_RETRY_DELAY_MSshortens the wait — and only an exhausted retry becomes an outage, whose sentence names the control pass rather than an authorization. A refusal no wait repairs is raised at once. When the judge's evidence budget is spent the tools are withdrawn from the request instead of merely declined, so a provider that ignorestool_choice: nonecan no longer end a question with no verdict. - A
transcripts tasks verifypass refused because the judge's last recorded reading says it does not answer (SEDZIA NIE DZIALA. NIE UZYWAJ TEGO DOPOKI USTERKA NIE BEDZIE NAPRAWIONA) now writes that outage on every open task with recorded evidence in its scope, exactly as a pass the judge fails mid-way does, and its stderr names them:the outage is recorded on the delivered task(s) waiting on the judge: <key> #<n>; they are unjudged, not open, until it answers. A branch that wrote nothing would leave a task delivered while the judge was down carrying proof and no outage, and Tama's stop guard — which stands down only for an outage newer than the proof — would refuse every stop of a session that had finished.transcripts tasks show <task-id> --jsoncarries the record asjudgeOutage. oko transcripts reindex --helpprints the usage and runs nothing; it used to sweep the whole catalogue.- Native context snapshots expose each source's availability, cache age and read failure. Desktop Automation and mobile Team > Oko settings > Automation expose context, Slack readiness, host policy and Stado-managed routines.
- Routines retain creation and retry identities, support paused monthly schedules, and inspect real job artifacts. Only context and autonomy are executable; Oko has no payment executor. See the CLI reference for all routine operations.
oko autonomypersists experimental or full delegated authority, selects eligible active goals, executes them through Jeden Pursuit, and closes a goal only after a successful receipt and independent acceptance verdict.- The installed
run-autonomy-servicelauncher keeps scheduler ticks alive as a Stado-managed service instead of depending on the desktop app. - Task verification can check an active session's saved answer with
transcripts tasks verify --before-stop --session ID. The independent judge receives the recorded tool calls and results, not just the agent's claim. - Tasks offers Verify current result and keeps the result or refusal under Last completion check. The desktop calls the same OkoKit verification as the CLI.
- A task is an object of its own. Every task carries an identity,
task-and sixteen hex digits derived from the session and the harness's id for the operator's message, so it is the same after every reindex and in every session that names it; a number is only its place among one session's messages. Claims, evidence, verdicts, judge outages and defects are keyed by the identity, and rows recorded earlier are given theirs on the first writable open.transcripts tasksprints the identity beside the number,transcripts tasks show <task-id>reads one task with its defects and its parent, andclaim,evidenceanddefectaccept the identity in place of a session and a number. A defect is a task of kinddefectthat names the task it repairs. Tasks in the desktop shows the identity, the kind and the parent on every row and finds a task by identity. - Tasks relate to one another:
transcripts tasks relate <task-id> <kind>unrelatetakes it back andrelations` lists a task's. A duplicate is done when its original is done and waits while it is open; a task that follows or is blocked by an open task waits on it and is not handed over; a task with open parts says how many. A self-relation, a repeat and a cycle are refused. Tasks in the desktop shows each row's relations, what it waits on, and relates tasks from a row. - Every task carries an origin:
operatorfor the operator's own words,delegatedfor a subagent's session (its id names the parent session),automationfor the product's own gate and classifier prompts.transcripts tasks mapreads every stored ledger — the whole history, not a window — and places each task by origin, kind and state (open,waiting,blocked,answered,done,withdrawn), with counts for the whole and filters for the rows; an unknown place, origin or kind is refused by name. The listing marks delegated tasks. Tasks in the desktop shows the origin chip on a row and builds the same map under Map of every task.
Fixed
- A session Jeden spawns for another one — its task intake or completion inspection, which opens with an
agent_statenaming itspurpose— no longer holds operator tasks. Its prompt ("You are Jeden's independent task intake…") had become an open task of its own, which no session could deliver and which the stop gate held against every session.
- Two commands changing one task's contract at the same time no longer erase each other.
tasks check,tasks stateand every other contract change used to write the whole contract back from the copy they read, so of several writes started together only the last one's check results, scope and surfaces survived while every command reported success. A change now applies only over the version it read; if another command changed the contract in between, nothing is written and the command refuses with "the contract of <task> changed while this command ran …; run this command again so it applies to the current contract".
- Questions containing action examples no longer create executable handovers.
- The ledger sweep no longer rebuilds the same sessions forever: a session whose transcript yields no operator message (one opened only by a harness-written subagent prompt) is recorded as swept, so such sessions no longer stay "not swept yet" for good. The control loop builds up to twenty-five missing ledgers on every pass, so the map covers the whole history over time.
- Slack execution results survive delivery failure without repeating the action; failed source reads no longer become zero-activity coaching reports. Scoped responder calls omit unrelated global fanout and reject ambiguous filters.
- Task verification resolves requests in their original conversation and can recover earlier tool evidence instead of judging only the latest answer. Legacy verdicts are checked again without requiring another user message; saved request bindings prevent an old writer from inheriting qualification.
- Controller handovers no longer act as unfinished-work corrections or redirect a later operator acknowledgement, including when another session leads the newer task. The original user-role handover remains in message exports.
- Resumed workers use the handover's own task index for both completion verification and the read-only stop gate. They request the independent verdict before stopping rather than waiting for an idle session to be judged.
- Startup refusals preserve the actual process state and terminal error even after the worker exits. Completed output stays bounded and does not make a finished worker appear to be running.
- Background workers discover installed Bun and Oko tools even with a launch agent's minimal PATH. Standalone CLI releases include their model resources; the broker supervisor restores missing resources even if its executable is already current, without depending on a retained build directory.
- Broker protocol 5 restores child-process signals before executing terminal commands, so inherited executor masks cannot strand completed subprocesses. An idle broker upgrades through its supervisor; active sessions are preserved.
- Task-control documentation distinguishes a missing runtime from a macOS
System Policyfile-read refusal. Neither a running broker nor a passing interactive test proves that the background runtime can read a protected file. - Local CLI builds delete a temporary signing keychain only when that build created it. Builds without Developer ID credentials no longer call macOS keychain deletion during cleanup. The real installation journey is
OKO_TEST_LOCAL_INSTALL=1 python3 -m unittest discover -s Tests/release -vwith the existingWISENT_CODESIGN_CREDENTIAL_ITEM; it retains installation, signature and Brama checks under.build/release-journeys/.
Safety
- Experimental mode is limited to the signed-in local user, one accepted goal per day, repository work under the Wisent workspace, and no purchases, irreversible external actions, release publication, or service mutation.
- Failed attempts are retained and retry after a 15-minute backoff; they do not consume the successful-outcome daily limit.
Minimum supported system is macOS 14.0 (LSMinimumSystemVersion). The goals, daily/period digest and telemetry-writing features depend on Apple Foundation Models and are compiled behind #available(macOS 26.0, *): on macOS 14 and 15 they are inert, not degraded.
0.2.11 - 2026-06-25#
Reconstructed from git diff v0.2.10 v0.2.11 (392 files) because the release was published without notes.
Removed and renamed — product identity
- The product is now Oko. The previous name Swiatowid is gone from the shipped surface: application bundle
Swiatowid.app→Oko.app, bundle identifierai.wisent.swiatowid→ai.wisent.oko, SwiftPM productsSwiatowid/SwiatowidKit/swt-cli→Oko/OkoKit/oko, and the release archiveSwiatowid-<version>.zip→Oko-<version>.zip. - The OAuth callback URL scheme changed from
swiatowid://tooko://(CFBundleURLNameai.wisent.oko.oauth). The old scheme is no longer registered. - The user configuration directory moved from
~/.swiatowidto~/.oko. - The rebuild/relaunch skill was renamed
swiatowid-rebuild-relaunch→oko-rebuild-relaunch. - Release signing environment variables were renamed
SWIATOWID_SIGN_IDENTITY→OKO_SIGN_IDENTITYandSWIATOWID_ALLOW_ADHOC_SIGNING→OKO_ALLOW_ADHOC_SIGNING.
Added — organization membership
- Added organization management: invite by email with a role, cancel an invite, accept an invite, list members and invites, remove a member, and change a member's role. Surfaced by
OrganizationSettingsView,InviteAcceptanceViewandOrganizationStore. - Added first-sign-in bootstrapping, so a new account provisions its own organization membership instead of requiring a manual backend row.
Added — company context and advisor
- Added an on-device company-context extractor and a company advisor pane that reads execution signals from GitHub (
CompanyAdvisor,CompanyContextExtractor,GitHubExecutionSource), plusoko adviseandoko snapshot-contextheadless equivalents. - Added an orchestrator planner, an advisor path and correction write-back, so a human correction feeds back into later suggestions.
- Added team-wide goals views (
TeamGoalsAllDaysSection,GoalsWorkTodaySection) and org-level goal materialization.
Added — terminal and suggestions daemon
- Added a supervised PTY broker and an explicit PTY permission sandbox (
PTYBrokerSupervisor,PTYPermissionSandbox), and a terminal agent provider selected by a stable provider id. - Added a headless suggestions tick (
oko suggestions tick) with strategy, team, velocity, assignment, Slack and model-fallback stages, plus the launch agentdeploy/ai.wisent.oko.suggestions.plistso suggestions can run without the app in the foreground.
Added — subscription cost accounting
- Added a provider-subscription catalog and router. Token telemetry can now be attributed to a fixed subscription charge instead of metered list price:
Resources/subscriptions.example.jsondeclares per-subscriptionperiodCostUSD,coveredModelPatternsand optionalowners, andResources/subscription-router.example.jsonpoints at the central model-router endpoint. - Model list prices moved out of Swift source into the bundled resource
Sources/OkoKit/Resources/token-pricing.json.
Configuration changes
- Optional new files, both read from
~/.oko/:subscriptions.jsonandsubscription-router.json. Both are compatibility fallbacks only — the source of truth isGET /v1/subscription-router/{agentId}on the model-router service. - Optional new environment variables for the router secret:
OKO_ROUTER_AUTH_SECRETorMODEL_ROUTER_AGENT_SECRET. Placing the secret in the JSON file is supported but not preferred.
Data and state migrations
- New Supabase migrations, applied to the backend project rather than to device-local state:
20260618190707_bootstrap_user,20260618191708_org_invites(addspublic.organization_invitesplus the invite/member RPCs and theirauthenticatedgrants),20260620190000_prefer_team_org_membership(a signed-in user resolves to their team organization rather than a personal one) and20260620203000_org_member_auto_goals_upsert. Several follow-up migrations correct return types, ambiguous identifiers and role casts in the same RPCs. - The transcript-object SQL definition was renamed
swiatowid_transcript_objects.sql→oko_transcript_objects.sql.
Compatibility requirements
- The renamed bundle identifier and the new update feed mean this release is not an in-place upgrade of an installed Swiatowid build. The old and new applications are distinct to macOS, to Sparkle, and to every macOS permission grant.
- The Sparkle feed moved from
https://storage.googleapis.com/wisent-swiatowid-updates/appcast.xmltohttps://storage.googleapis.com/wisent-oko-updates/appcast.xml. A copy still pointed at the old feed will not discover this or any later release.
Operator actions required
- Install
Oko.appexplicitly; do not expect the Swiatowid feed to deliver it. - Copy any local state from
~/.swiatowidto~/.okobefore first launch. - Re-grant the macOS permissions the app asks for (Automation/Apple Events and the Messages database read used by Ultrareach); grants are keyed to the old bundle identifier and do not carry over.
- Apply the new Supabase migrations before signing in, otherwise organization bootstrap and invite handling will fail.
- Remove the old Swiatowid application and its login items once Oko is verified.
Known limitations
- Company-advisor GitHub signals require a reachable GitHub token; the pane has no offline mode.
- Subscription cost attribution is only as good as the manually entered
periodCostUSD; the example file ships the literal placeholderREPLACE_WITH_REAL_BILLED_AMOUNT.
0.2.10 - 2026-06-17#
Reconstructed from git diff v0.2.9 v0.2.10 because the release was published without notes.
Added
- Added Google as a second sign-in provider. The GitHub-specific PKCE flow was generalized into one provider flow (
signInWithOAuth(provider:label:),oauthAuthorizeURL(provider:…)), and the sign-in screen now offers "Continue with GitHub" and "Continue with Google" beside the email code. - Added a team working-hours calendar (
TeamWorkCalendarSection) that browses by week or longer period, filters to one member or the whole team, and rolls telemetry up per member (TeamWorkMemberBucket,TeamWorkPeriod,WorkTelemetryRollupGranularity). - Telemetry now records the exact engaged stretches within a day (
DailyWorkSegment,segments) and every individual prompt timestamp (promptTimes), instead of only a first-to-last span, so the timeline shows real gaps rather than one filled bar. - Added file-backed persistence for the agenda, company-context and execution-metrics documents (
FileBackedStores), so those panes work against local files rather than requiring Supabase. - Added a headless telemetry view in the CLI and two inspection scripts (
scripts/inspect-transcript-prompt.swift,scripts/render-working-hours-chart.swift).
Changed
- Agenda items expose
researchRoleas an alias ofrole, with a matching initializer, so the older research-oriented call sites keep compiling against the renamed agenda model. - Model pricing data was reworked in
Pricing.swift; per-day, per-model token rows now feed the digest sums.
Fixed
- Long-term wins text and the daily-wins writer no longer emit the stray epistolary wrapper in some paths; forecast and digest rollups were corrected alongside the new segment data.
Compatibility requirements
- Persisted telemetry documents gain
segmentsandpromptTimes. Days written by an earlier release have neither, so their timelines render from the span only until they are recomputed.
Operator actions required
- Google sign-in must be enabled as a provider in the Supabase project before the new button can succeed.
0.2.9 - 2026-06-17#
Reconstructed from git diff v0.2.8 v0.2.9 because the release was published without notes.
Added
- Added "Continue with GitHub" sign-in through Supabase OAuth using the PKCE authorization-code flow. The consent screen opens in an
ASWebAuthenticationSession; GoTrue redirects back toswiatowid://auth-callback?code=…, which the app exchanges for a session (PKCE,oauthAuthorizeURL-predecessorgithubAuthorizeURL,exchangeCodeForSession).
Security-relevant changes
- The OAuth flow is PKCE-only: a 64-byte random verifier stays on the device and only its SHA-256 challenge is sent to the authorize endpoint, so an intercepted authorization code is not redeemable on its own.
Configuration changes
- The application bundle now registers the custom URL scheme
swiatowid(CFBundleURLTypes,CFBundleURLNameai.wisent.swiatowid.oauth).
Operator actions required
- Enable GitHub as a provider in the Supabase project and register
swiatowid://auth-callbackas an allowed redirect URL, otherwise the button fails at the provider.
Known limitations
- Cancelling the provider sheet is deliberately silent — no error is shown — which is indistinguishable from a provider that never opened.
Fleet report — unreleased#
oko report extends the existing session scorecard with delivery counts, median and p90 repair and closure durations, release-qualified repair outcomes, recorded hangs, token and estimated API-price cost per closed position, repeated questions and defect shapes, and UTC daily trends. Groups identify executors, models, providers, products and organization members from recorded sources rather than inferred names.
Every metric links its source records. Missing history, unreadable registers, ambiguous attribution and incomplete pricing produce named refusals, never replacement zeros. Defect lifecycle transitions now retain before/after state inside the register transaction, so reopening cannot erase which revision and repair time failed. Publication alone is not behavioral qualification. Register failures refuse their dependent metrics. An unreadable defect register does not suppress independently measured transcript tokens or cost; the real qualification journey exercises that refusal through the CLI. A failed Stado register command retains its refusal even if it also printed a parseable JSON document; partial output cannot establish a queue result.
Queue delivery outcomes and repair-to-publication/recording durations cite Stado's delivery register separately from behavioral-test success. Qualified completion uses the platform evidence timestamps, not the last refresh time. Updating evidence for the same repair revision preserves its original attempt. Sent-token measurements use the harness context snapshot; billable usage counters separately determine estimated API cost.
The native sessions/report operation and OkoKit expose the same projection. Oko Desktop's existing Work → Sessions → Scorecard includes Fleet report, with dimension selection, daily trends and expandable source records. Repeated diagnostic rows remain visible rather than colliding on their displayed text. See the report command for definitions, attribution limits, refusals and the real release-qualification journey under tests/report/. The release manifest registers fleet-report as a required test quality gate, beside the session, shared-wait and task-graph journeys. The release queue invokes the real Cargo integrations rather than treating their presence in the repository as qualification. The journey independently reads real cited harness records to verify sent tokens and model/provider attribution. Its host needs the real organization configuration and measured transcripts; absent dependencies fail qualification. The shared native fixture binds a release archive to the worker's WISENT_SOURCE_DIR, WISENT_SOURCE_COMMIT and WISENT_SOURCE_SHA256; a local checkout retains its actual Git revision and patch. The report journey uses that bound commit, not the current HEAD of an exported directory. For real defect revision validation, the qualification account must also have its canonical Oko checkout with the bound commit and its parent available. Missing history refuses by checkout, revision and Git's cause. The retained report includes the parent lookup, repaired and reopened register snapshots, commands, statuses and output; the shared fixture carries it into WISENT_TEST_EVIDENCE_DIR when the release worker declares that directory. The authored journey has not established a runtime or Desktop pass.
0.2.8 - 2026-06-16#
Reconstructed from git diff v0.2.7 v0.2.8 because the release was published without notes.
Changed
- Every proactive Slack post now passes through one shared engagement gate (
EngagementGate) instead of each surface deciding for itself. The daily operating check-in, the daily wins digest, the weekly and monthly recaps and the morning welcome DM all reward their previous post by how it landed and then ask the shared model whether that kind of message is still worth sending; one the team keeps ignoring goes quiet. - Per-member digests on a shared channel are scored only against that member's own messages, matched by the
*<name> —prefix, so a teammate's post no longer rewards someone else's digest. - A suppressed check-in no longer burns its once-per-day marker: the stored signature is cleared so the message can be reconsidered later.
Known limitations
- The gate reads the last 20 channel messages to judge reception, so a busy channel can push the relevant post out of the window and score it as silence.
0.2.7 - 2026-06-16#
Reconstructed from git diff v0.2.6 v0.2.7 because the release was published without notes.
Added
- Added a persisted learner over post topics (
ContentEngagementStore), the content counterpart to the existing tone learner. Each topic keeps an exponential-moving-average score, is rewarded by how the post landed, and is suppressed once it falls below the floor. - Answers posted in a public team channel now use their own prompt (
questionReply,questionAnswerSystemPrompt): sourced, explicit about what it does not know, and required to name the item, owner or status it relies on. Private DMs keep the terser reply. Public channels are recognized by the Slack channel-id prefixesCandG.
Fixed
- Fixed the suggestion spam in which the same unanswered topic was re-raised roughly ten times. Suggestions now pick one still-engaging topic and stay silent when everything worth raising has been ignored into suppression, and exploration is confined to eligible topics so a suppressed topic is never re-raised by chance.
- Fixed approval escalations resetting on unrelated metadata refreshes. The escalation fingerprint is now the stable
source|ididentity rather than the churningdetail/statusfields, so a refreshed brief item no longer restarts the escalation ladder and re-spams the channel. - Fixed the tone learner reading the wrong prior message: it now scores the last message the bot actually posted in the resolved window.
0.2.6 - 2026-06-16#
Reconstructed from git diff v0.2.4 v0.2.6 because the release was published without notes. v0.2.5 was never published.
Added
- Added weekly and monthly long-term wins recaps (
AutoGoalsStore+LongTerm.postLongTermWinsIfNeeded,LongTermWinsWriter). Each posts once per completed period, gated on a key stored in the goals document and persisted only after a successful Slack post, and reuses the existing 180-second extract loop rather than adding a scheduler. - Added token and implied-cost telemetry unified across Claude, Codex and Kimi (
TokenExtractor,TokenTelemetry,DailyModelTokens), with cache writes and cache reads priced separately. - Added published-list-price reference data in one place (
Pricing,TokenRate). A model with no known rate is reported uncosted rather than silently priced at zero; a superseded model version is priced at its family's current rate and flagged as approximate. - Added period work-texture insights (
PeriodInsights) and day-texture insights for the daily digest (DigestInsights), including comparison against the previous period and against the member's usual rhythm for that weekday. - Added per-goal effort attribution (
GoalEffort): prompts and time from the goal's own session, tokens and cost from that session's token stream.
Changed
- The daily-wins note is written in the product voice and then deterministically stripped of the greeting and sign-off the on-device model insists on adding (
stripEpistolary), because the note is already rendered under its own Slack header.
Known limitations
- Kimi sessions are reported without per-goal token or cost attribution. Their token file is keyed separately and their user turns carry no timestamps, so attribution would not be sound; the value is left absent rather than estimated.
- Implied cost is a list-price estimate, not billed spend.
0.2.4 - 2026-06-16#
Reconstructed from git diff v0.2.3 v0.2.4 because the release was published without notes.
Fixed
- Fixed duplicated goals. When the model emitted
startGoalwith the same title on several turns of one session instead ofcontinueCurrent, identical goals stacked up.GoalLifecycle.dedupnow collapses goals sharing a (session, title): it keeps the earliest start, the latest activity, marks the goaldoneif any copy finished, and preserves an already-set announce flag so a transition still posts at most once.
0.2.3 - 2026-06-16#
Reconstructed from git diff v0.2.2 v0.2.3 because the release was published without notes.
Added
- Added Kimi Code as a third coding-agent source alongside Claude Code and Codex. Sessions under
~/.kimi-code/user-historyare indexed, so Kimi work feeds goals, telemetry and search; Kimi appears in the agent dock with its own icon and can be resumed from a transcript row; statistics attribute its work by name. - Provider dispatch was extracted into
TranscriptIndexer.candidates(forRoot:)with one walker per on-disk layout, so a further provider is added there instead of by growing the scan function.
Fixed
- Fixed sessions from providers without per-message timestamps being indexed once and then skipped forever. Undated turns now receive monotonically increasing stamps, so the extractor's
timestamp > cursorfilter advances turn by turn exactly as it does for dated Claude and Codex transcripts.
Compatibility requirements
TranscriptSourcegained akimicase. Persisted transcript data written by this release is not readable by an earlier one.
Known limitations
- The Kimi history file records only the user's own prompts, with no assistant turns and no timestamps, so a Kimi session's last activity falls back to the file modification time.
0.2.2 - 2026-06-16#
Reconstructed from git diff v0.2.1 v0.2.2 because the release was published without notes.
Fixed
- Fixed goal extraction aborting mid-run. Unpaced rapid on-device classify calls tripped the Foundation Models rate limit and killed the whole run; calls are now paced 700 ms apart, keeping them under the cap.
- Fixed the extraction order so today's sessions are processed before the backfill. Entries are now walked most-recent-first, so a long backfill no longer delays current goals, and a failure part-way through leaves the recent work already persisted.
0.2.1 - 2026-06-16#
Reconstructed from git diff v0.2.0 v0.2.1 because the release was published without notes.
Fixed
- Fixed the shipped application aborting immediately at launch with dyld
Library not loaded: @rpath/Sparkle.framework. The bundler now embedsSparkle.frameworkinContents/Frameworks, gives the executable an rpath that resolves it, and code-signs the embedded framework with the same identity as the application.
Operator actions required
- Reinstall from this release.
v0.2.0cannot launch, so it cannot update itself through Sparkle.
0.2.0 - 2026-06-16#
Reconstructed from git diff v0.1.0 v0.2.0 (58 files) because the release was published without notes. v0.1.0 itself is a draft and was never published, so this is the first section with a published artifact.
Added — intent-level goals
- Added a goals layer that answers "what did the user set out to accomplish", extracted on device by Apple Foundation Models rather than inferred from a working-directory name (
AutoGoal,GoalStatus,GoalIntentExtractor,GoalIntentRules). It is persisted per organization and triggered manually; the existing heuristic per-project work tracker is untouched and continues to work independently. - Added the Goals workspace pane with an explicit "Extract goals" action, and a headless
swt-cligoals harness, which is the only way to exercise the extractor without the SwiftUI app.
Added — work telemetry
- Added per-member work telemetry computed on the member's own machine from their own recent transcripts: working span, prompt volume, prompt length, engaged time and session count (
DailyWork,WorkTelemetryStore,WorkTelemetryPane), persisted per user. - Added a work forecast that predicts a member's start and end of day from their own history (
WorkForecast). Too little data yields an explicit "not enough data", never a guess. - Added a headless telemetry harness in
swt-clifor the same reason as the goals harness.
Added — organization-scoped configuration
- Slack and Ultrareach (iMessage) configuration moved into Supabase per organization (
OrgIntegrationStore,IntegrationsSettingsView), replacing device-local dotfiles. Owners and admins can edit; everyone else sees the values read-only. - Message routing contacts became a per-organization list (
OrgContact), replacing two hardcoded named recipients.
Added — evolving voice
- Added an explicit set of voice registers over one constant identity and one constant set of hard rules (
SwiatowidTone,SwiatowidVoice), plus a persisted learner that rewards the last used register by how the post landed and picks the next one (ToneEvolutionStore). The chosen register applies to every outbound surface, not only the channel that produced the feedback. - Added the orchestrator suggestions path and its model responder, and a Kimi agent runner and model router entry.
Changed
- The research-specific agenda was generalized into a product-neutral agenda:
ResearchAgenda*types, stores, seeds, migrations and panes were renamed toAgenda*, and the pane hierarchy moved accordingly.
Compatibility requirements
- Integration settings are read from Supabase from this release on. An organization that has not filled in the Integrations settings screen has no Slack or Ultrareach configuration, even if the previous device-local files are still present.
- Goals and telemetry require macOS 26.0 for the on-device model. On macOS 14 and 15 both layers stay empty rather than falling back to a cloud model.
Operator actions required
- Re-enter the Slack and Ultrareach configuration under Settings → Integrations for each organization, and define its routing contacts.
Known limitations
- Goal extraction is manual in this release; there is no background loop.
- Neither goals nor telemetry has a SwiftUI-testable path, which is why both ship with a headless CLI harness.
Migrated from src/content/docs/releases.md