Repository navigation
v0.77.0
A live voice call opens from a seeded session: a duplex voice session can speak G.711 at 8 kHz, asks the curator for its seed at the ring and at every renewal, streams the caller's words as throttled transcript deltas, and reports per call what it used and how long it has run. The route stamp's early verdict is fired in parallel with the fast model, which holds its answer until the stamp decides; the stamp reads the conversation, may ask the person one question about how a turn is meant, whose answer comes back on later turns, and names the screen form. The screen takes that verdict from the stamp and builds its window in place behind a building mark. A release cut is checked against the commit the battery measured: anything beyond the version and record places is refused. Before the cut, the battery's findings were fixed: a tool name returned with a rest of its arguments is repaired, refused voice upgrades and failed duplex sessions are logged, t_first_audio_ms is the first audible frame, a delegation is answered once, a failed duplex session on a phone call can play a failure medium and hang up instead of leaving silence, web_fetch writes no URL's credentials anywhere, a colony under host disk load no longer exits by watchdog, a session the provider ends mid-call can be replaced at once by a fresh one of the same call, the greeting of a call is no longer answered as if the person had spoken, and a call's usage and call marks survive the replay of the cell's birth params.
Added
- A duplex call whose provider ends the session mid-call can be recovered (#1377). A live model may end a session in the middle of a call (seen as
reason=contenta few seconds after a sentence its moderation flagged), and the caller talked on into a dead line. The newduplex.gpt_live.recover_max(default0= as before) lets the cell replace such a session at once with a fresh one of the same call, which asks for its seed withseed_askreason: recoverand reports its start onrenewedlike a renewal; at most that many times per call. When the session in charge ends after that,duplex_failedgoes out once on theerrorlane, which thefreeswitchhive answers withfailure_mediaand a hang-up. - The quick voice can start beside the route stamp (#1275). With
quick_speculative"1"oncollector/assembleand oncurator/policy, a turn that waits for its stamp sends the same curate earlier, and the curator holds the quick answer until the stamp decides: kept onsimple, dropped on every other way.curator/policy.spec_tools(empty = no tool) names the tool families such an early call may use; a dropped early call is booked like any other call.collector/assemble.delegation_answer_mode("fact","context") says how a duplex delegation is answered,collector/assemble.delegation_rule(text, empty = the built-in rule) replaces the rule of thecontextmode with a set's own, andcollector/assemble.delegation_inherits_stamplets a delegation round take the stamp of the turn it belongs to. All ship off. - The route stamp reads the conversation and judges the screen (#1279). With
talky/route.judge_context"1"the curator answers the stamp's read with a judge context (the last turns of both roles, the topic, the screen's topics,nowandplace), and the stamp asks which screen topic and blocks help (screen {topic, confidence, lead, further}) in the same decide call.talky/route.clarify"1"adds the classclarify: the curator answers such a turn with one question about how it is meant (its note iscurator/policy.clarify_note) and reports the question and the answer to affinity on the new talky exitpropose. The stamp carriesscreenandscreen_form; the new talky exitverdictis the presenter'sin_verdict-- the rim copy of a stamp that carries the screen, withjudge_contexton every rim stamp (so a turn without a topic closes asno_topic), and the second stamp oftalky/screen_judge.enabled"1", which leaves only there, judged by the newtalky/screen_model. Bind the presenter toverdict, not tostamp. All ship off. - A how-question the person answered comes back on their next turns (#1279).
affinity/briefserves the new slothints(asked for, never by default;hint_rows20): the acceptedmx.hints.clarifyproposals about the subject -- the clarify pairs the curator proposes -- newest first, each only in a round that is a subset of the one it was made in, on the tool lane also as the body listhints. With the newcollector/assembleswitchbrief_hints"1"every turn asks for it (about the counterpart, else about the round's one person) and carries it oncurateas the slothints;curator/intakekeeps it per person andcurator/historyadds it to the judge context asjudge.hints, whichtalky/routerenders in the decide state. Off as shipped, and the default bytes do not move. The quick voice's fixed prompt now also says to ask one short question instead of guessing when a request stays unclear despite the conversation. - Two optional
curator/historyparams shape a voice call's seed (#1279):seed_entries_maxkeeps at most that many session and until notes in the seed, the best ranked (priority, then the youngest), and says the rest on stderr;seed_anchoradds the persona's anchor as the seed's lastdeveloperline, after the turns and a held hint and right before a held wind-down. Both are off as shipped (0,""), and the seed's bytes do not move. - A duplex call is seeded and fed by the curator (#1279). New talky lanes
in_seed_ask(answered on the new exitseedwith the judge context, the session's notes and the inner turns as messages),in_seed_line(one line held for the next seed of a call),in_screen_topics(the presenter's topic table) andin_delta(the voice cell's partial transcript); theassistantdeclares all eight new talky lanes and carries them through its talkies: the topics to both, the partial, the seed ask and the seed line only to the surface of the round's channel (guarded oncontext.channel_node, likein_delegation), so one ask is answered with exactly oneseed.curator/policy.live_feed"1"asks the call for a renewal at its next turn boundary (new exitlive_feed,hop.sectionfromlive_feed_section,renewwithhop.at_turn_end"1") after a duplex turn that moved what the next seed carries -- the notes, the inner turns, the named topic, the screen topics;curator/policy.delta_speculation"1"reuses the stamp of a partial transcript when the final text is the same.curator/policy.quick_instructions_slot"1"gives the quick voice the slotinstructions.quick; the brain never sees it.curator/historyandcurator/policytakeplaceandtimezone(set both pairs alike);curator/history.now_utcfixes the clock of the judge context for tests (empty = the clock). All ship off. - The curator tells what a phone call carried (#1369). New lane
in_call_endoncurator,talkyandassistant(the set draws the voice cell's finalusagethere) answered by exactly one newcall_facts:{call_id, session_id, seed_used, facts: [{source, id, text}], delegations: [{turn_id, text}]}-- whether a seed or live feed gave the call facts, which ones (notes, topic, screen topics), and the call's delegations, read out of the curator's ledger, which stays as it was. Without the edge nothing changes. - A failed duplex session on a phone call is no longer silence (#1374). The
freeswitchhive hands the media half'serrorwithhop.error_codeduplex_failedto./signalas well as to its rim, and the new./signalparamfailure_media(empty = nothing happens) plays a recorded announcement into the call the error names and then hangs up, as oneuuid_transfer <uuid> playback:<failure_media>,hangup:NORMAL_CLEARING inlineat the switch. A value with a space or a comma, or alocal_stream://that never ends, is refused with one stderr line naming the reason, as is a call id outside[0-9A-Za-z-]; a-ERRfrom the switch on the transfer is said on stderr, not dropped. Ships empty, so the line behaves as before. - The screen answers from the turn's stamp and builds the window in place (#1357). With the new
presenter/stageparamverdict_source: stampthe presenter makes no call of its own: it takes topic, candidates and the screen form from the talky's stamp on the new lanein_verdict, and a sure verdict opens the window under construction at once -- a construction pictogram (display-statuswithkind: building, no text) and onedisplay-skeletonper chosen block, drawn from the topic set and never from data. Blocks take their skeleton's place as their data come and may be rebuilt until the window is done; done takes the pictogram away and lets the frame settle once over the new paramsettle_ms(600), and from then on the window is frozen. The screen form maps to a rung through the window'srelevance(form_rungs:active->focus,accompanying/referring->relevant,none-> no window, journalform_none); a later form lifts or withdraws the window before it is done. The journal addsverdict_source,t_building_ms,t_form_msandt_done_ms.display-stacktakesbuildandsettle_ms; the window under construction is drawn in full contrast and stands still under reduced motion. The defaultselectoremits byte for byte what it emitted before. - The presenter can say which topics a screen can show (#1357). With the new
presenter/stageswitchscreen_topics(off by default) the table the decider is asked over -- the presenter's own topics and the apps' accepted ones -- leaves on the new lanescreen_topicsas{topics: [{name, purpose}]},purposebeing the manifest'sdescribe, once on boot and on every change of the table, never per turn. Its taker is the talky's curator; off, the lane is silent. - Template versions (#1357): second digit,
display@2.12.0,presenter@1.4.0. - A duplex
voicecall reports what it used and how long it has run (#1362). Withemit_usageset, theusagelane carries the call's totalsecondsover every provider session, renewals included, at each meter reading and once withfinaltrue at the end of the call; a session whose close never arrived counts with its last meter reading, and thatfinalsayscomplete: false. Withcall_marks_s(and optionallycall_mark_every_s) set, thecall_marklane carrieselapsed_sat each mark; one timer per call, which ends with the call. All default off, so an existing colony emits neither lane. - A live
voicesession can speak G.711 and open with a seed (#1359).duplex.gpt_live.audio_format(pcm16by default,g711_ulaw,g711_alaw) runs the model at 8 kHz G.711 on the wire (audio/pcmu,audio/pcma) while the client keeps L16 at 8000; the adapter compands each frame and never resamples, and a G.711 block that namessample_rateis refused asaudio_format_rate_conflict. Withduplex.gpt_live.seed_wait_msset, a session that opens (at the ring, at the media connection, at a renewal) emitsseed_askwithreasonstartorrenewandrenewal_n(0for the first session) and waits that long forin_seed, whose messages become the model'ssession.input(oldest trimmed over 128 messages or 8,192 estimated tokens).in_seedgivesrenewal_nback and reaches only the session that asked; a late answer is dropped and logged, one without the number ismissing_renewal_n, one for a call nobody holdsunknown_session. A call that ends while its session waits for the seed starts no session. Both default off, so an existing colony sends the opening frame it always sent. Thefreeswitchhive passesin_seedandseed_askthrough its media half; thememberdeclares both as ports at./channels. - Template versions (#1359): second digit,
freeswitch@3.1.0,member@2.8.0,voice@2.7.0. - A live
voicesession streams the caller's words as a throttled speculative stream and takes two more kinds of advice (#1361).duplex.gpt_live.delta_min_chars/delta_min_msthrottle thepartiallane per incoming transcript delta (both0by default, one emission per delta as before); the last state always leaves right before theturn, which carries the sameturn_idand nowt_person_end_ms/t_first_audio_mson the model's clock.duplex.gpt_live.emit_spoken: falsestops thespokenlane alone (unset followsemit_partials).in_advisetakesinterrupt(an instruction, then a commentary with the same words and delegation) andrenew(the call's next provider session opens at once and asks for its seed again; a renewal whose seed is still to come and which has not started absorbs it, one asked after that seed or that start opens the next renewal right after the takeover; the old session keeps the call and its open delegations until the handover), andhop.at_turn_end: "1"on any section holds it until the caller's next turn closes; several heldrenewat one boundary are one renewal, a held answer keeps its delegation open (the fallback still falls), and what is still held when the call ends, or is dropped past 16 held sections, goes to the colony as asession_endeddead letter.t_person_end_msis the end of the caller's last words before the answer (backchannels and a barge-in's run excluded),t_first_audio_msthe first audible frame of the answer sent to the client (#1373), never before it. - Template versions (#1279): second digit,
assistant@3.11.0. - Template versions (#1279): second digit,
affinity@3.12.0. - A release cut can be checked against the commit a battery of real model tests measured (#1358).
scripts/release_matches_battery.py <receipt.md>reads the lastcore_commitof a battery receipt and refuses a release commit that changes anything beyond the version places (Cargo.toml,Cargo.lock,template.json) and the release records (CHANGELOG.md,ROADMAP.md,docs/status.en.md): oneforeign:line per file, exit 1.
Fixed
- A duplex call no longer answers its own greeting (#1378).
collector/assembleread a turn with an emptyuserhalf -- the voice cell's turn for the model's greeting -- as a question of the person, so the greeting was answered, spoken into the call and came back as the next turn without the caller. Such a turn (no text of the person, no peer turn, no consult, a text of the agent) is now held as the agent's line, a deferredassistantrow that reaches the curator with the next round, and opens no round. Turns with the person's words and event turns are unchanged. - A
voicecell born withemit_usage,call_marks_sorcall_mark_every_snow runs with them (#1379). Every life of the cell runs on its birth params replayed through thecell.dbparams overlay, and the overlay carried none of the three keys, so the replay dropped them: a call never left ausageor acall_markemission, although the birthconfig.jsonasked for both. The overlay now carries all three; likecredential_backoff_max_msthey are settled at birth, and a runtime update naming one is refused as immutable. - A colony under host disk load no longer exits by watchdog with
starved=colony_loopbecause a log line waited (#1372). The stderr log sink wrote each line on the runtime worker that logged it, under the process-wide stderr lock; to a file on a disk under write-back load one line could take half a second, and the colony task woken onto that worker could not run on another. The stderr sink now writes from its own thread, likelog.jsonl, and drops no line. A failed boot template scan now ends through an error instead ofstd::process::exit, so the buffered lines and the root lease are released. - A quick turn whose spec curate went through
./handovergets its quick round again (#1371). The turn's normal curate came while the spec curate was there and stored its call as the fallback of the spec answer; back from the handover, the spec curate took that stored call for a decided turn and parked, so neither the spec call nor the stored call went. A spec curate whose own row isawaitnow makes its call, and its answer settles the turn. - The route's screen question no longer grows with the topics (#1371). With the presenter's candidates it asked
screen.lead.<topic>andscreen.further.<topic>per topic -- 70 questions for 23 topics, which the decisions wire refuses above 64, so every turn was stamped on the fallback without a word. Onescreen.leadand onescreen.furthernow cover the candidates of all topics as<topic>/<key>; a lead of another topic than the chosen one is none, and a refused decide call says so on stderr. - The presenter's
screen_topicsnow carries each topic's candidates as{key, describe}(#1368), the shape the talky'sin_screen_topicsexpects; without them the talky never asked which block should lead. - A switch of the live line set as JSON
trueis on (#1370).talky/route(clarify,judge_context,stamp_out),talky/screen_judge(enabled),curator/policy(quick_speculative,quick_instructions_slot,live_feed,delta_speculation) andcollector/assemble(quick_speculative,delegation_inherits_stamp) read only"1", so a set that wrotetrueran with the live feed and the delta speculation off and nothing said so. Every one of them now reads JSONtrue,"1","true"or"on"as on, the presenter's rule;"1"stays the bytes of before. curator/policy.live_feed_sectionnames the one way that is built (#1370). It offeredcontext, whose section carries no text, so the voice cell answered itbad_section; the contract now namesrenewalone, and any other value is sent asrenewwith a line on stderr.- The talky README describes the
seedandverdictexits as they leave (#1370):seedcarrieshop.call_id,hop.session_idandhop.degradedstoreon a refused read, and the screen judge's second stamp leaves onverdictwithstamp_out"0"as well. Two chain tests run history'sseedinto the voice cell and the collector'scuratewith the member's context into the live feed. - A duplex call's
usageandcall_markleave thefreeswitchhive (#1369). The rim edge out of./voicelet neither through, so both died inside the hive althoughmemberdeclares them as ports at./channels; they now cross on the edge that carriesdelegation, and the hive's contract declares them. A delta of a voice turn (context.delta"1") askingcurator/historyfor its turns reads the round's rows whatever their session, like the seed, because it stands under the call's id and the conversation under the generation's.talky/routenow asks the wall for a delta as it does for a turn (context_turns,judge_context); before, a delta was decided without the conversation and the final turn could take that stamp as it was. - The presenter's stamp way finds the turn its stamp belongs to (#1367). With
verdict_source: stampthe turn reachespresenter/stagethrough the apps fan-out without aturn_id, while the stamp onin_verdictcarries the session keeper's; the two never met, so an app-owned topic never got its data request (in_showop: data) and its window was withdrawn atdata_wait_ms. A stamp now takes the newest open turn of the same round that came without aturn_idand is younger thanbudget_ms, and a turn without aturn_idgives its words to the newest open stamp of the same round still waiting for them; both orders, never across rounds, a matchingturn_idfirst. The defaultselectordoes not move. - The curator drops an early quick answer when the stamp needs a tool family it never called (#1366).
curator/policykept a speculative answer as soon as it called any tool, so a turn stampedsimplewithneedstooland the familyorgakept an answer that had only runweather, and the orga tool never ran. Now every family the stamp names must lie insidespec_toolsand be called by the early answer; otherwise it is dropped (mark reasonfamily:<name>) and the normal quick round runs. A stamp without families decides as before. - The how-question hints in the route stamp's decide window count against its token budget (#1363).
talky/routerendered every hint of the judge context in full, outsidecontext_budget_tokens. Now they share that budget with the turns the wall already put in the window: the newest hints that fit stay whole, older ones are left out whole, and a cut ends the block on a mark with the hints and tokens shown and the total. Without hints, or with all of them inside the budget, the decide state does not move. - A tool that failed is never spoken by the quick voice (#1279). A tool result whose header carried an
error_codebut whose text was plain prose reached the curator as a speakable result. The collector now names the codes of an iteration's tool results in the newcurateslottool_errors({tool_call_id: error_code}, the turn itself unchanged), and the curator's policy hands such a round to the brain with the note "tool not reachable"; a call that never reached a tool (malformed_tool_call,unknown_tool) is left out, its answer stays the repair aid for the model. - Tool arguments encoded one level too deep are unpacked once (#1360). A small model on the quick voice called a folded family with
argsas a JSON string holding the object; the call left folded, came back an error three times, and the model told the person the connection kept failing. Thedispatchernow unpacks suchargsonce before it unfolds the call, and thellmcell does the same forfunction.argumentsthat hold the JSON string of an object on the chat wire; both name the repair (args_double_decoded). A string inside that string again stays the error it was. - A tool name with a rest of its arguments is repaired to the declared tool (#1373). A fast model on the chat wire returned
web_search"}forweb_search; the call went out under that name, came backunknown_tool, and the model searched a second time. Thellmcell now checks every returned tool name against the menu it sent, on every wire: a name that becomes exactly one declared tool once trailing JSON punctuation and whitespace are cut off is repaired once and named on the log linetool_name_repairedwith the call id. Every menu entry with anopenum -- a folded family (web {op, args}), a hint or a flat op tool alike -- declares the names of its ops too (web_search), and theopof such a call is checked against its enum by the same rule, read where the dispatcher reads it; a lab run showed the first version never fired on the folded menu. A recovered harmony call to an op name staysmalformed_tool_call. Other names and a request without a menu stay as before. - A refused
voiceupgrade is logged (#1373). The cell answered a badsample_rate,session,modeorencodingwith a400before the upgrade and wrote nothing itself; on a telephone bridge seventeen refused calls stood only as "connection error" in the bridge's own log. Both doors, socket and link, now also name the refusal on aWARNlinevoice: upgrade refusedwith the door, the status and the detail. - A failed duplex session is logged (#1373). A duplex start the provider refused (eight
401s in a battery run) reached only thevoice_errorbody; the colony log had no line. The cell now also writesvoice: the duplex session failedwith the session id and the error. - A duplex turn's
t_first_audio_msis the first audible frame sent to the client (#1373). It came from the answer's transcript, which the provider stamps on its own 200 ms raster at the caller's end of speech, and an overlapping answer was clamped tot_person_end_ms; in a battery run 12 of 15 telephone turns showed 0 ms from end of speech to first word. The cell now stamps the first audible frame of the answer it sends (above the barge-in level, so the silence a live model streams before it does not count), to the millisecond; a turn whose answer is only silence has not_first_audio_ms. - One answer per delegation (#1373). A delegated utterance could be answered twice -- by the delegation round and by the turn round beside it -- and both reached the duplex session. The voice cell now speaks the first answer per
delegation_idand drops every later one on aWARNline, aninterrupt(which carries a spokenfact) included; the deadline's fallback does not count as an answer. web_fetchno longer writes a URL's credentials anywhere (#1375). The success log line named the requested URL as given, so a password in its userinfo reached the daemon log in clear. Every URL the cell writes out -- the log line, thefinal_urlheader, error texts -- now has its userinfo and the values of secret-named parameters in query and fragment replaced byREDACTED, the rest byte-identical. A name is secret by its structure: percent-decoded and split into words at camelCase boundaries and at_,-,., it holds a strong word such astoken,secret,signatureorassertionanywhere, or a weak one such askey,sigorsessionas a whole word (accessToken,sessionId,X-Amz-Signatureyes;monkey,signalno;codedeliberately not). A hash route (#/cb?token=…) is redacted like a query. The redacted URL is parsed again, and one that still shows a secret, or does not parse, is redacted whole. The request itself is unchanged.- Template versions (#1360): third digit,
builder-librarian@2.2.98,dispatcher@1.6.2; third digit for the pins that follow,assistant@3.10.9,builder@1.30.11,cogny@5.13.8,figure@2.0.10,meclaw-os@2.3.12,member@2.7.11,memory-hive@3.19.8,org@2.2.11,simple-agent@2.2.8,talky@6.12.2. - Template versions (#1275): second digit,
collector@5.7.0,curator@1.17.0,talky@6.13.0.