Repository navigation
v0.65.0
Cotal 0.65.0 is out!
Grab it: npm i cotal-ai@0.65.0
Changes in this release
- A platform composition that hosts the auth context can now get the host incarnation that a delegated user intent pins as its executor (SPEC 13.16).
PlatformControlInputtakes an optionalhost: { endpoint, clusterDigest, artifacts }: the reverse-DNS endpoint of the host process that runs the intent flights, the closure digest of its §13.7 cluster and every contract artifact that closure needs. The auth plane self-authorizes that one name, and a single-label name is refused at start. With it,AuthServiceHandlegainsregisterHostIncarnation(instanceId), which publishes the artifacts, registers that instance through the same ceremony the auth plane runs for its ownauthendpoint, and returns{ instanceId, processEpoch }with the epoch that registration committed,observeHostGate(instanceId), the sweeper's point-in-time read of that endpoint's issuance gate over the context's own connection, andawaitHostFence(instanceId, processEpoch), which resolves with the gate once it is no longer open at that epoch, or with null once it is absent. A host that registers its persisted instance id at every start fences its predecessor and advances the process epoch, so a restarted host reads its earlier incarnation as gone. A start whose confirming read of the gate finds that a later start of the same instance registered is refused withconflict. The returned epoch stays current only until the next start registers, which can happen before the call returns, so the host armsawaitHostFencewith it before it admits or recovers any flight and stops serving when the hook resolves.docs/embedding.mdanddocs/design/delegated-user-launch-intent.mddescribe all three members. Core's KV issuance gate adapters (serveIssuanceGateKv,provisionEndpointGateOpen,endpointRegistrationBarrierandreadEndpointGateGeneration) tokenized the endpoint name twice, and the registration repair cursor rebuilt its key from the token, so every reverse-DNS endpoint threw before it reached its gate or partway through its registration. They now carry the name and leave the key builders to tokenize it. Single-label endpoints are unchanged. Core'sregisterServiceInstancealso returns theprocessEpochits completing reopen committed, so a caller no longer reads the epoch back from a gate that a later registration may already have advanced; the auth plane's own registration takes it from there. - A presence observer now drops a bucket record whose
card.nameorstatusis missing or has the wrong type, or whosetsis not a finite number, counts it inpresenceBindingDropCountand reports it on thewarningevent. Before, a record whosetswas missing or text such as"nope"stayed live in the observer's roster after its key expired, because the staleness checks comparedNaNwith the liveness window. A record with a non-stringcard.nameor anullvalue threw inside the presence watch loop, which ended the watch and surfaced as anerrorevent. - A manager start now serves at the process epoch its own registration committed. Both the boot registration and
registerRemoteManagerAuthorityread the issuance gate again afterregisterServiceInstancereturned and took that read's epoch, so a start that a second start of the same instance superseded in between authorized its serve grant at the successor's epoch, and the remote path returned the successor's epoch and registration revision as its own.registerServiceInstancenow returns theprocessEpochits completing reopen committed, both registration paths use it, and the serve grant's epoch check refuses a superseded start withexpired. - A caller now validates a request's args in the form they are sent, so a key whose value is undefined, which JSON drops, no longer fails a responder's closed input contract. A
cotal spawn --detachfrom a current CLI was refused by every manager released beforedefaultAgentexisted, even withCOTAL_DEFAULT_AGENTunset. A caller-side args refusal is now markednot-executed, since nothing was published. Args that JSON cannot carry, such as a BigInt, get the samebad-requestrefusal. A refusal that names a key the responder's contract does not declare carries anai.cotal.ep.undeclared-argdetail. The CLI reads that detail on manager commands and reports version skew with its own version, where it used to print a bare schema error followed by a warning that the request may have run. - Add
once, an at-most-once scope for cotal-lang steps that write to a far side. A resume that finds a step insideoncebegun and never settled does not dispatch it again: it opens a hold, a checkpoint minted underholdRequestIdof the step's recorded request id, and the answer becomes the step's result, while an expired hold fails the step with the catchable L4027. A hold the host refuses leaves the step pending rather than refused, so no later host writes again, and its L5025 says so. A hold whose checkpoint answers an outcome other thanresolvedorexpiredfails the step as a handler fault. Onlyaskruns insideonce; every other effect is refused before it begins (L4028), and a write from the body to a binding outside it is refused (L2032). The journal entry gains aholdfield for the hold's own binding. The hosted runtime ends the heldask's open attempt pause before the hold binds, andcotal run answer,cotal run amendandcotal run journalread a held step at its hold. A fork may cut insideonce, and a migration ignores an orphanedonce.oncebecomes a reserved name, so a program that declares its ownoncebinding is refused (L2002). The design record isdocs/design/at-most-once-external-effect.md. - AG-UI frame metadata (
CotalMeta) now declares ausagecarrier for a run's model usage: input and output token counts, the cached and reasoning parts of those totals, and the cost in US dollars. It rides the event that closes the run (RUN_FINISHEDorRUN_ERROR), and a count the harness does not report is left out rather than written as zero. Before, a connector that read these numbers from its harness had no declared key to publish them under, so it dropped them, and a connector that invented its own key would have disagreed with the next one about the name of the same quantity. No connector fills the carrier yet; each adopts it in its own change. cotal_yieldon a turn the run already settled now refuses with the turn id and its deadline. Before, a seat that yielded after its deadline was told "no turn is active" or that it held no such turn. One that yielded between the manager settling the turn and the seat's next poll was told the yield landed, although the run had recorded L4003.- A mesh registry record this build cannot use no longer refuses a command in the catalog preparation that runs before the command's own checks. While such a record is present the preparation neither refreshes nor applies a catalog, so
cotal spawnreports its own usage errors and a managed handoff refuses with its handoff-phase sentence instead of the record's path. A snapshot an interrupted command left unapplied is applied once the record is restored or removed. Every command that resolves its target through the registry still refuses the record by name. cotal web --detachon the default host and port now comes up on hosts whose system resolver has no answer forcotal.localhost, such as WSL2. The detached parent probed the brandedhttp://cotal.localhost:7799/for readiness through Node's resolver, so every probe failed while the child was already serving on 127.0.0.1:7799; after 30 seconds it reportedweb dashboard did not become HTTP-ready, stopped the healthy child, and left only the banner inweb.log. The readiness probe now asks the bound host and port. The printed address is unchanged.
What's Changed
- fix(manager): serve at the epoch the manager's own registration committed by @davidfarah2003 in #2639
- fix(connector-core): declare a usage carrier on AG-UI frame metadata by @davidfarah2003 in #2650
- fix(auth): register and observe the host incarnation a delegated intent pins by @davidfarah2003 in #2652
- fix(connector-core): name the settlement when a yield targets a turn the run already settled by @davidfarah2003 in #2655
- fix(core): drop a presence record whose card.name, status or ts has the wrong type by @davidfarah2003 in #2660
- fix(cli): skip the catalog refresh while a registry record is unusable by @davidfarah2003 in #2661
- feat(lang)!: add once, an at-most-once scope for steps that write to a far side by @davidfarah2003 in #2659
- fix(core,cli): validate caller args as sent and name the version skew a closed contract refuses by @davidfarah2003 in #2666
- fix(web): probe the bound address when a detached dashboard starts by @davidfarah2003 in #2669
Full Changelog: v0.64.0...v0.65.0