Repository navigation
v0.87.0
Cotal 0.87.0 is out!
Grab it: npm i cotal-ai@0.87.0
Changes in this release
-
@cotal-ai/core/browser-controlnow opens a Manager caller in a strict-CSP page.openBrowserManagerCalleropens one connection for one explicitly selected Manager instance with the person's ownmanager-callerbearer: it checks the bearer's space, principal, lifecycle and instance before dialling, reads the issued caller, pins the describe to the instance and uses the host's generated contracts. It never reconnects on its own;reopenre-checks every pin.followGoalsubmits a goal once under the host's persisted envelope id and runs the stock goal follow;reconcileGoalreads an accepted goal back throughgoal-result;verifyGoalResultFactchecks the fact with Web Crypto. Nothing retries, and an accepted goal is never submitted again. To share the follow, it moved into a module without transport-node and takes the host's fact verifier (submitAndFollowGoalWith), and the goal-result shape checks moved out ofendpoint-action. The goal follow, at the package root and in the browser, now settles a goal from live progress only on a terminal whose subject names the instance and epoch that accepted it and the same goal as its body. Any other terminal, such as one another Manager publishes under its own instance, settles nothing and prompts one read of the goal's record, and a read fact committed by another instance is refused. The package root'ssubmitAndFollowGoal,parseGoalResultFact,GOAL_TERMINAL_STATESandepGoalProgressGrantRowkeep their names and signatures. -
History reads on observer and admin credentials no longer leave consumers behind.
channelHistory,multiChannelHistoryanddmHistoryused to read through nats.js ordered consumers that these profiles could not delete, so each read left two or three consumers for five minutes. A dashboard polling history filled the stream's consumer limit, and every later read failed with "maximum consumers limit reached". The reads now use the connection's own reader names,hist_<readerId>_<n>withnin0..7, and delete each reader when its step ends. A static credential'sreaderIdis its nkey. Under the auth callout it is the server's id and the client id the server assigned the connection, so a client cannot claim another connection's readers by choosing its inbox nonce. A connection runs at most eight history reads at once per stream, and a read past that waits for a free name. A consumer found under one of the names is never deleted, and a create that returns an existing consumer is never read through; the read moves to a free name.The observer profile gains
$JS.API.CONSUMER.DELETE.<CHAT>.hist_<readerId>_<n>for those eight names, and admin also gains them on<DM>. Each row names one consumer of this connection and no other. Breaking: a static observer or admin credential minted before this release has no such row, and a current client's history read on it fails and says to remint it. Remint those files, and upgrade the auth service before or with the hosts that read history on a user-auth mesh. The auth callout'spermissionsForhook now receives the connection's reader id as a third argument, and the functioncalloutPermissionsreturns requires it. -
A person who may spawn on their own Manager can now read what a launch there needs. A
manager-callerview whose actor holdsspawncarries the Manager'sstatus,modelsandresolve-cwdcommands on its one pinned instance. Before, those reads neededadmin, and no view carriedresolve-cwd, so a launch dialog that reads the Manager's connectors, model catalogs and a working directory was refused at the broker. Arun-only or no-scope view gains nothing,psandslotsstay withadmin, and the standingspawnset that reaches every Manager on the class rail is unchanged.A Manager whose runtime delegates seats outside its host refuses
resolve-cwdbefore filesystem lookup. Every path gets the same execution-mode refusal, since delegated seats cannot use a Manager-host working directory.A user mesh's co-located Manager answers every owner's view, so its
statusnow counts only the caller's own seats, aspswould list them, unless the caller holdsadmin. A remote Manager's seats all belong to its owner, and its count is unchanged. The auth service mints the view, so the new reads arrive with its upgrade. On a self-hosted user mesh, restart the Manager on this release with or before the auth service, so the count is bounded and delegated host cwd resolution is refused before the reads are minted. -
Declare frozen private tool-result consent, revocation, serving-material and handle-mode command contracts, with closed parsers and a managed-agent issue/renew envelope. Add the consents anchor role and its tool-results scope check. These exports do not sign, issue, register, serve or enable the private path; public tool arguments and results remain withheld.
-
Accept the additive private tool-result contract in SPEC and the version-exact documentation bundle. Define consent, capture frontiers, fixed audiences, bounded revocation and exact-instance reads without implementing or enabling the path. Public tool arguments and results remain withheld; activation requires qualified owner implementations and a stock release.
-
cotal send,cotal endpoints,cotal personas list --running,cotal channels listand the name check incotal spawnno longer leave consumers on the broker. Each one opened a transient endpoint that watched the presence bucket and the channel registry, and the operator credential cannot delete those consumers, so every command left one consumer on each bucket for five minutes. Five shells sending in a loop reached the broker's limit of 1000 consumers per stream, and further sends failed withmaximum consumers limit reached. With a static or open-mesh credential the transient endpoint now reads both buckets once with a batched Direct Get and binds no consumer.cotal channels listreads the registry the same way through the newdirectGetoption ofreadChannelRegistry.@cotal-ai/coreadds thereadOnceendpoint option andlastKvEntries, and theoperatorprofile gains the Direct Get row on both buckets. On a user-auth mesh these commands still run on the user's bearer and keep their consumers. -
A seat's launch-material directory (
cotal-launch-*in the OS temp dir, holding the broker URL, the creds path and the control token) is now removed when the seat's process exits. Before this change a Claude seat left it behind aftercotal stop, while the persona file and control socket of the same launch were removed.materialEnvin@cotal-ai/connector-corenow takes the launch's artifact array as a second, required argument and appends the material's private directory to it, and every connector returns that array onLaunchSpec.artifacts, so the manager, the pty runtime and the foregroundcotal spawnremove it the way they remove a persona file. Each connector now writes the material after its last refusal, so a refused launch leaves no material file behind. The second material file the Hermes launcher writes for its gateway child is not covered by this change. -
Clarify private tool-result audience wording, distinguish the expired answer state from refusal codes, and spell out the consent replay row's existing signing and validity claims. No contract rule, runtime behavior or activation gate changes.
-
A repair
cotal upafter a crash now restores the manager and the delivery daemon in one run. A
manager or delivery daemon that is killed, or a daemon that exits because its broker went away,
cannot release its lease, and a replacement started inside the lease TTL used to lose the create and
makeupexit 1. The new manager now waits out a lease row that names its workspace root and a pid
that is not running on the host, andupwaits out a delivery lease held by the credential the
folder gave its previous daemon while no daemon is recorded running. The wait never writes the
lease: a row that is written again during it has a live holder and is refused, and a row held by
anyone else is refused at once as before. -
Add a consent-only verifier for pinned signed password authentication within five minutes and a host-only proof decision door. Retain validated authentication provenance without changing ordinary bearer exchange. Private tool-result consent, issuance and serving remain unavailable.
-
A tmux seat that outlives its manager is now adopted by the next manager instead of closed. The manager records each adoptable seat, and on startup the successor checks the recorded tmux server, window and pane, then manages the seat again under its existing credential so a run's pending turn continues on it. A seat whose pane has exited is retired as before.
-
The auth service serves
POST /manager-instanceson both faces: the signed-in human's own Manager instances, read-only. It verifies the same IdP proof the human exchange verifies, derives the owner the same way, and answers{ v: 1, owner, complete: true, instances }, each instance with its placement and the verdict selection reads (candidate,not open,not registered,not this owner's,unknown). Selection is now built on the same list, and the planeopenAuthAuthorityPlanereturns gainslistManagerInstances. A listed candidate is registration evidence, not proof that the instance answers. Browser-origin requests are refused as on every exchange route, and a failed gate read answers 503, never an empty list. -
A
cotalcommand started with a few hundred KB of argv, such ascotal send dm <name> "<480 KB text>", no longer exits 13 before it runs. The CLI loaded Ink and its wasm layout engine at startup for every command, and on macOS that engine's init never finished with an argv that large. Ink and the console UI now load only whencotal consoleruns. -
cotal joinunder a provisioned credential now prints each channel post once. Such a join holds a durable delivery backstop for its channel, so every post reached it twice under the same message id, once on the live channel subscription and once from the backstop, and the join printed both. It now deduplicates by message id as the protocol requires of a receiver, acks the second copy, and never treats an empty id as a key. -
A Linear token or OAuth state file on Windows is no longer refused as "readable by other users (mode 666)". Windows reports every file's POSIX mode as 666, so the check refused every token-mode account there, including token files Cotal wrote itself. The mode check now applies only where POSIX modes exist, as for the broker TLS key and launch material; on Windows the file's privacy is the ACL Cotal sets when it writes the file.
-
A seat spawned with
supervisenow survives its manager's death. When a manager ends without stopping the seat (a crash, or a pty seat that ended with it), the next manager of the same instance restarts the seat under the same name, lifecycle uid, credential, persona and cwd and counts it as one restart of the policy, so a workflow run resumed by that manager finishes its interruptedturninstead of failing it with L4002. The manager keeps what that restart needs in.cotal/seat-records/<space>/<name>.jsonwhile the seat runs. A seat whose budget is spent, or whose persona file or credential changed or expired, is retired as before. A seat resumed from a preserved inventory now also keeps the nkey seed its credential renewal needs: the manager read it with a pattern that matched no real creds file.
What's Changed
- feat(spec): accept the private tool-result contract by @davidfarah2003 in #4121
- fix(connectors): remove the launch-material directory when the seat exits by @davidfarah2003 in #4096
- fix(core): one-shot CLI reads leave no consumers on the presence and channel-registry streams by @davidfarah2003 in #4099
- fix(cli): print each channel post once in a provisioned join by @davidfarah2003 in #4093
- fix(cli): load Ink only when cotal console runs by @davidfarah2003 in #4097
- fix(cli,manager): wait out a crashed holder's lease on a repair up by @davidfarah2003 in #4100
- fix(manager): restart a supervised seat its manager's crash took down by @davidfarah2003 in #4104
- fix(spec): clarify private tool-result wording by @davidfarah2003 in #4140
- fix(tmux): adopt a seat that outlived its manager by @davidfarah2003 in #4105
- feat(core): declare private tool-result contracts by @davidfarah2003 in #4143
- fix(core)!: delete observer and admin history readers after each read by @davidfarah2003 in #4142
- test(manager): answer a failed proxy forward in the remote EF smoke by @davidfarah2003 in #4149
- test(manager): serve the batch eviction in the smokes that answer the delivery-admin rail themselves by @davidfarah2003 in #4139
- test(auth): pin the native-key issuance row in the D32 writer fixtures by @davidfarah2003 in #4136
- fix(linear): check token file modes only where POSIX modes exist by @davidfarah2003 in #4137
- test(manager): give the seat-exit-cause fixture seat its launch spec by @davidfarah2003 in #4147
- test(auth): keep the persona's channel in the over-envelope spawn's read list by @davidfarah2003 in #4148
- test(cli): add app-identity to the flag inventory golden by @davidfarah2003 in #4141
- feat(auth): verify fresh password proof for tool-result consent by @davidfarah2003 in #4145
- test(cli): count partial-tear checks in the seed total by @davidfarah2003 in #4151
- feat: follow a Manager goal from a strict-CSP browser and list a person's Manager instances by @davidfarah2003 in #4146
- test(cli): isolate crash-repair smoke child environment by @davidfarah2003 in #4153
- test(cli): expect degraded boot before system credential rotation by @davidfarah2003 in #4154
- feat(core): let a spawn-scoped manager-caller view read launch options by @davidfarah2003 in #4150
- test: re-anchor the goal-follow mutation fixtures on the moved follow by @davidfarah2003 in #4152
- test: retarget fixture anchors after core source extraction by @davidfarah2003 in #4156
Full Changelog: v0.86.0...v0.87.0