Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
3237 commits
Select commit Hold shift + click to select a range
80e9851
tui3, config: the gate row lands on the conversation you are in, and …
ZeroPoint95 Sep 16, 2026
96eccc7
attribution: the footer names CodeAF, and still links to agentfield.a…
santoshkumarradha Sep 16, 2026
40427b2
session, prompt, tui3: a wrong completion check is answered with [no …
santoshkumarradha Sep 16, 2026
23f1725
Clean internal experiment records and revive test/ux for codeaf (#1068)
santoshkumarradha Sep 16, 2026
0929901
session: task commits are authored by the bot account, not codeaf@loc…
santoshkumarradha Sep 17, 2026
da6e7a0
README: show the pinned-version install line the release test require…
santoshkumarradha Sep 17, 2026
cc8bea7
telemetry: anonymous usage counts, off with CODEAF_TELEMETRY=off (#1095)
santoshkumarradha Sep 17, 2026
615c8ba
session: the carry-ladder fixtures wait for the mark's drawing, not r…
santoshkumarradha Sep 17, 2026
36f1692
docs: the install proxy is live, and the manual says so (#1076)
AbirAbbas Sep 17, 2026
fa252d6
A launch says a newer codeaf is out, and /update installs it and rest…
AbirAbbas Sep 17, 2026
a5af991
a run's own scratch note no longer fails the delivery gate (#1096)
santoshkumarradha Sep 17, 2026
bdc28b0
chat: the wordmark is drawn CodeAF, four rows, capitals on the d's ca…
ZeroPoint95 Sep 17, 2026
98aed82
A real release download finishes: the 3-second check clock no longer …
AbirAbbas Sep 17, 2026
cfdc29d
docs/changes: v0.2.1 rolled up into CHANGELOG.md (#1113)
AbirAbbas Sep 17, 2026
a0cc221
README: telemetry folds under Docs (#1115)
santoshkumarradha Sep 17, 2026
fde49a5
santos/dev lands on dev: crew family, Pareto crewing, the Model Pool,…
AbirAbbas Sep 18, 2026
62cae85
ci: Model Pool mirror workflow (signed index onto the model-pool bran…
santoshkumarradha Sep 18, 2026
4add9c9
chat: the unconnected custom row on /connect keeps its vendored name …
AbirAbbas Sep 18, 2026
9258f70
docs/changes: v0.3.0 rolled up into CHANGELOG.md (#1196)
AbirAbbas Sep 18, 2026
86a4019
ci: a changed directory in a nested module is not a package of the ro…
AbirAbbas Sep 21, 2026
9de07a7
santos/dev lands on dev: the worker harness, stopping and limits, the…
santoshkumarradha Sep 21, 2026
744ff16
a dev build installs beside codeaf as devaf and follows the dev chann…
AbirAbbas Sep 21, 2026
7160b05
chat: the model picker is a table with headed columns (#1107)
ZeroPoint95 Sep 21, 2026
49b4a58
installer: the PATH line is the last thing printed, bare and bold gre…
ZeroPoint95 Sep 21, 2026
17ae56d
UX changes: home navigation and consistent message-box controls (#1071)
ZeroPoint95 Sep 21, 2026
8917dfc
chat: a hosted conversation's stop it ends the live run it can see (#…
AbirAbbas Sep 22, 2026
419741f
e2e: the live standing check returns to the conversation that receive…
AbirAbbas Sep 22, 2026
c084b60
changelog: v0.4.0 rolled up (#1381)
AbirAbbas Sep 22, 2026
ea9e1d9
chat: a waiting message stays with its conversation when you leave, a…
AbirAbbas Sep 22, 2026
5ba9894
changelog: v0.4.1 rolled up (#1386)
AbirAbbas Sep 22, 2026
3a14b55
feat: add total token usage to anonymous session telemetry (#1387)
santoshkumarradha Sep 23, 2026
b11b94f
feat(tui): share randomized working-logo animations across chat and t…
santoshkumarradha Sep 23, 2026
b378e92
chat: sign in to Codex (a ChatGPT plan) in the browser, and codeaf co…
AbirAbbas Sep 23, 2026
a040f1c
chat: /cost keeps its columns under the gutter (#1390)
ZeroPoint95 Sep 23, 2026
d6bb367
chat: a Codex model carries its 272k window, and a Codex call row nam…
AbirAbbas Sep 23, 2026
c26ea9c
tests: the tui3 suite runs in 11s instead of 341s, and a large paste …
AbirAbbas Sep 23, 2026
c9e8a27
chat: rename the Home scheduled heading to standing (#1415)
ZeroPoint95 Sep 24, 2026
ec67e37
Remove closed tasks from current Sessions results (#1425)
ZeroPoint95 Sep 24, 2026
138cda9
Fix home tab alignment across views (#1424)
ZeroPoint95 Sep 24, 2026
e3f94b6
Message-box parity and visual design rules (#1334)
ZeroPoint95 Sep 24, 2026
7b50751
chat: select the next message project by clicking its Home name (#1422)
ZeroPoint95 Sep 24, 2026
9ad949a
Restore double-space Home navigation and ask-here choices (#1388)
ZeroPoint95 Sep 24, 2026
6d9ed58
chat: one list of twenty-two tips, said two ways — home rotates, a co…
ZeroPoint95 Sep 24, 2026
debffab
Show Home conversations as working while their tasks run (#1426)
ZeroPoint95 Sep 24, 2026
f101ad2
chat: know the OpenRouter balance, start a near-zero account on free …
AbirAbbas Sep 24, 2026
5256d70
santos/dev2 lands on dev: the worker harness as default, runs as work…
santoshkumarradha Sep 25, 2026
6005ae2
tui3: hovered tab's full title sits on the blank row and ends under i…
ZeroPoint95 Sep 25, 2026
76148d5
build: staging moves itself every Friday after the full check, and st…
AbirAbbas Sep 25, 2026
3829312
chat: space space home tip; a conversation's tip covers the project a…
ZeroPoint95 Sep 25, 2026
7f136b3
Sign the commits a run's own workers make, and obey CONTRIBUTING when…
AbirAbbas Sep 25, 2026
5d8324a
tui3: conversation overview (#1429)
santoshkumarradha Sep 25, 2026
619af48
One task page for every task: a run's task opens the real task room (…
santoshkumarradha Sep 25, 2026
e94bb4f
tui3: team delegation (#1494)
santoshkumarradha Sep 25, 2026
49496ec
README: DeepSWE benchmark results (#1511)
santoshkumarradha Sep 25, 2026
1194d4b
crew: route the worker, planner and checker per task (Pareto crew) (#…
santoshkumarradha Sep 25, 2026
e7b9512
teams: one wake per reply, stops reach background members, the belt f…
AbirAbbas Sep 25, 2026
8690b18
session: a worker can stop its own job, and a run's checker is told t…
AbirAbbas Sep 25, 2026
b32d34b
teams: tile spend, refused writes said, no wake prompts on tiles, sub…
AbirAbbas Sep 25, 2026
691ade5
task room, landing and --one-model: real file names, one note sentenc…
AbirAbbas Sep 25, 2026
3e0bb5b
first run: a worker's codeaf is the running one, the usage notice is …
AbirAbbas Sep 25, 2026
478eece
crew: the Pareto crew's follow-ups — seat ceilings, unpriced calls at…
AbirAbbas Sep 25, 2026
dd0fcc6
senior-dev: a coding agent built into codeaf takes a whole task, in t…
ZeroPoint95 Sep 26, 2026
c34a3a7
senior-dev: plain folders start again, and shell model names resolve …
AbirAbbas Sep 26, 2026
fa9807f
happy paths after the senior-dev wave: telemetry --help exits 0, --he…
AbirAbbas Sep 26, 2026
6e61631
senior-dev: the side row hides its clock while its page is open, and …
AbirAbbas Sep 26, 2026
e238acb
e2e: the tmux suite follows the places bar, digits and Home that the …
AbirAbbas Sep 26, 2026
baa1dd7
home: a search's pointer previews the match under it, and leaving giv…
AbirAbbas Sep 26, 2026
b58a750
spend: one floor for a sub-cent figure, so the Spending tab says a ti…
AbirAbbas Sep 26, 2026
837b2b0
e2e: the firing's second reminder is found even when the model drops …
AbirAbbas Sep 26, 2026
a7b39d6
crew: count a pinned checker in the panel's coverage warning (#1501)
santoshkumarradha Sep 27, 2026
0415825
tasks: a check naming issue_.go is sent back, and a file nothing make…
AbirAbbas Sep 28, 2026
ac84606
Consolidate verified CodeAF task, chat and runtime fixes (#1627)
santoshkumarradha Sep 28, 2026
b267d39
e2e: the tmux suite never touches the developer's own background time…
AbirAbbas Sep 28, 2026
abdf470
tui3: a task's landing is said after the answer it lands in, and Done…
AbirAbbas Sep 28, 2026
e7097d1
manual: the pages #1545 flagged now say what dev does (#1639)
AbirAbbas Sep 28, 2026
847a22d
session: a stopped job reads as ended at once; the log-folder sweep m…
AbirAbbas Sep 28, 2026
e446507
install: a guided, coloured install that leaves codeaf ready to use (…
santoshkumarradha Sep 28, 2026
80c23bc
Merge the #1635 installer hotfix into staging
AbirAbbas Sep 28, 2026
c037a93
Let Tab select slash command menu entries (#1642)
ZeroPoint95 Sep 28, 2026
88d7e71
install: a guided, coloured install that leaves codeaf ready to use (…
santoshkumarradha Sep 28, 2026
b59781f
Merge the #1635 installer hotfix line into dev
AbirAbbas Sep 28, 2026
795c27d
remote: a closed loopback has no engine left running, so its home can…
AbirAbbas Sep 28, 2026
e797620
Consolidate reviewed CodeAF fixes — batch 2 (#1632)
santoshkumarradha Sep 28, 2026
8470cdc
Remove the retired Search page and command (#1650)
ZeroPoint95 Sep 28, 2026
f8fbbbc
chat: preserve conversation drafts and attachments when returning Hom…
ZeroPoint95 Sep 28, 2026
73ade03
Add ! bash mode with live shell output (#1654)
ZeroPoint95 Sep 28, 2026
c4f8ad2
compaction: summaries as the last step, recovery that fits, and a rec…
ZeroPoint95 Sep 29, 2026
7e00c95
session: fix flaky 'directory not empty' test cleanup — Close now wai…
ZeroPoint95 Sep 29, 2026
e7a2bef
Clarify which budget limit blocked a conversation (#1671)
ZeroPoint95 Sep 29, 2026
7b8b69a
Fix conversation titles after opening shell commands (#1667)
ZeroPoint95 Sep 29, 2026
461a43f
senior-dev works in a private worktree copy that starts from where yo…
ZeroPoint95 Sep 29, 2026
24e018d
tui3: a stopped answer can still be opened after reopening its conver…
AbirAbbas Sep 30, 2026
dbc134c
happy paths before release: hosted drive keys, do --json refusals, no…
AbirAbbas Sep 30, 2026
f1161a3
e2e: follow the provider words, folded landings and folded skills (#1…
AbirAbbas Sep 30, 2026
4949397
changelog: v0.5.0 rolled up (#1683)
AbirAbbas Sep 30, 2026
f769be4
session: wait for both parts to start before landing them in the park…
AbirAbbas Sep 30, 2026
d2255b4
teams: the cap, header, keyboard and wall show the right thing (#1668)
santoshkumarradha Sep 30, 2026
a86af99
session: a job's log path keeps the session folder's own spelling (#1…
ZeroPoint95 Sep 30, 2026
1d7b1ea
go.mod: github.com/google/uuid is a direct requirement (#1688)
ZeroPoint95 Sep 30, 2026
db40bf9
chat: shorter developer-friendly replies (#1428)
ZeroPoint95 Sep 30, 2026
a3fbbf8
Home: copy the current conversation name from the second menu action …
ZeroPoint95 Sep 30, 2026
be3dbb9
Sessions: filter whole conversations by name, project, or task (#1434)
ZeroPoint95 Sep 30, 2026
b8dbfc5
Strip provider API keys from a model's shell environment (#1486)
biggdawg320 Sep 30, 2026
9ca3295
Discard incomplete streaming replies (#1497)
mikemikimike Sep 30, 2026
3e0ab8d
docs(benchmarks): fix measure-cli script path (#1498)
hy3560 Sep 30, 2026
fc734b3
chat: a late task start does not claim failure (#1539)
mikemikimike Sep 30, 2026
4e9710a
docs(manual): correct home standing-order due-time guidance (#1544)
hy3560 Sep 30, 2026
49ad17f
fix(bench): isolate cells from upstream branches (#1621)
erSpell Sep 30, 2026
2f33722
skills: OpenCode's and Goose's folders are read too (#1691)
AbirAbbas Sep 30, 2026
20b0eba
build: the size budget is set on darwin/amd64 and raised to 68,850,00…
AbirAbbas Sep 30, 2026
ca14bf8
senior-dev: the typed start names its ceiling, and ending cards wear …
AbirAbbas Sep 30, 2026
3c203d2
chat: no zero rates, no raw ids for names, and the manual says what t…
AbirAbbas Sep 30, 2026
c00b4f5
cli and provider audit on current dev: flags after the brief, complet…
AbirAbbas Oct 1, 2026
feac16a
chat and media audit on current dev, and saved memory that works on a…
AbirAbbas Oct 1, 2026
4e1d41f
chat: a terminal resize leaves no text from the previous frame (#1709)
AbirAbbas Oct 1, 2026
9f69057
tasks: a stopped run names the work its worker already committed (#1710)
AbirAbbas Oct 1, 2026
df7022d
chat: settings says checker, no zero durations or byte counts, and he…
AbirAbbas Oct 1, 2026
4ad77ed
cli: flags typed after a shell run's brief reach its child as flags (…
AbirAbbas Oct 1, 2026
41ffc27
chat: the /model menu's current model wears no band and the picker ow…
ZeroPoint95 Oct 1, 2026
333026a
provider: dev and staging builds report to OpenRouter as their own ap…
AbirAbbas Oct 1, 2026
7f385da
provider: dev and staging builds report to OpenRouter as their own ap…
AbirAbbas Oct 1, 2026
dbcba60
Merge staging hotfix #1723 into dev (history only)
AbirAbbas Oct 1, 2026
654cf26
chat: chip recognised slash-commands anywhere in the message buffer (…
ZeroPoint95 Oct 1, 2026
db94955
catalog: a day-old cache answers at once and refreshes behind the answer
AbirAbbas Oct 1, 2026
4e45c52
changelog: the first launch of the day no longer waits on the model l…
AbirAbbas Oct 1, 2026
1a15562
changelog: v0.5.1 rolled up
AbirAbbas Oct 1, 2026
30460c0
Merge hotfix v0.5.1 (#1724) into staging
AbirAbbas Oct 1, 2026
9bf7d25
catalog: a day-old cache answers at once and refreshes behind the ans…
AbirAbbas Oct 1, 2026
2650837
Merge staging hotfix v0.5.1 (#1724) into dev (history only)
AbirAbbas Oct 1, 2026
d8c7503
session: stop drawing the skills block as part of a message (#1713)
ZeroPoint95 Oct 1, 2026
9379dbc
senior-dev: a run that carries on a branch counts only its own files,…
ZeroPoint95 Oct 1, 2026
084a33c
senior-dev can be told something while it works, from its page or the…
ZeroPoint95 Oct 1, 2026
fb6c189
Snappy startup and home: no launch waits on the model list, home open…
santoshkumarradha Oct 1, 2026
75f1a13
changelog: v0.6.0 rolled up (#1726)
AbirAbbas Oct 1, 2026
61da161
config: register CODEAF_INBOX as operator plumbing (#1730)
AbirAbbas Oct 1, 2026
d369a3e
senior-dev: no wip commits, its own commit message, and hand-backs ov…
ZeroPoint95 Oct 1, 2026
9d00d1c
chat: ctrl+enter queues a follow-up, and a click takes a queued messa…
ZeroPoint95 Oct 1, 2026
e728296
fix(telemetry): report token usage during sessions (#1731)
santoshkumarradha Oct 1, 2026
22d72d5
docs(telemetry): refresh package contract (#1732)
santoshkumarradha Oct 1, 2026
1493487
ci: touched packages runs in parallel legs and re-checks a failure be…
AbirAbbas Oct 2, 2026
794b49a
session: control the clocks in the slowest regression tests (#1733)
AbirAbbas Oct 2, 2026
3a00ee6
tests: the suite is green on macOS, and the allocation law prices its…
ZeroPoint95 Oct 2, 2026
f86e4ae
chat: onboarding answers the pointer and a second enter, no telemetry…
ZeroPoint95 Oct 2, 2026
06a3283
chat: the @ list finds every open conversation, on every box, with sp…
ZeroPoint95 Oct 2, 2026
ee63b59
keys: a forgotten OPENAI_API_KEY or a stale folder engine no longer h…
AbirAbbas Oct 4, 2026
19bac4e
changelog: roll the unreleased entries up as v0.7.0 (#1754)
AbirAbbas Oct 4, 2026
f816387
Telemetry updates
santoshkumarradha Oct 5, 2026
7e30335
changelog: roll the unreleased entries up as v0.7.1 (#1775)
ZeroPoint95 Oct 5, 2026
4d54756
fix: enable native web tools in codeaf PlanDB tasks (#1783)
santoshkumarradha Oct 6, 2026
3588adf
Contextual memory across chats, teams, and tasks (#1777)
santoshkumarradha Oct 6, 2026
0dbeccf
fix: preserve memory when upgrading an existing database (#1787)
santoshkumarradha Oct 7, 2026
362174a
scripts: the hosted drive reads the task column's key as opt+l on a M…
ZeroPoint95 Oct 7, 2026
baa2d7f
Refine Teams overviews, shared conversations and scoped deletion (#1750)
ZeroPoint95 Oct 7, 2026
4fd9cfa
Keep codeaf current with quiet background updates that preserve runni…
santoshkumarradha Oct 7, 2026
741f1b9
sec: sec-af's security audit, built into codeaf as its second program…
ZeroPoint95 Oct 7, 2026
6246d71
review: pr-af's code review, built into codeaf as its third program (…
ZeroPoint95 Oct 7, 2026
d0a6f49
Make settings clearer, searchable and safe for existing profiles (#1792)
santoshkumarradha Oct 7, 2026
b3d9d88
Keep AI team hover responsive with large previews (#1794)
santoshkumarradha Oct 8, 2026
c240e36
Fix model availability and simplify provider setup (#1789)
ZeroPoint95 Oct 8, 2026
15edceb
chat: a bare number-and-period reply draws instead of vanishing (#107…
7vignesh Oct 8, 2026
d8055f5
Make built-in provider lists extensible (#1795)
ZeroPoint95 Oct 8, 2026
8761488
connect: ai& is a provider of its own, and one key reaches four labs …
fenilmodi00 Oct 8, 2026
4df8ee1
senior-dev: a CMake default, and --verify-build/--verify-test for a c…
ZeroPoint95 Oct 9, 2026
973b7d2
senior-dev: read its window from codeaf offline, refuse tiny models, …
ZeroPoint95 Oct 9, 2026
d2f30c0
senior-dev: a pass that ran no command says so, instead of that the b…
ZeroPoint95 Oct 13, 2026
524843e
senior-dev: keep the store in the run's record folder, write back a r…
ZeroPoint95 Oct 13, 2026
56c1a8b
fix: keep senior-dev help within eighty columns (#1810)
ZeroPoint95 Oct 13, 2026
4ca9642
fix: check senior-dev capacity across selected services (#1811)
ZeroPoint95 Oct 13, 2026
5c26003
fix: refuse task check declarations the completion gate cannot approv…
ZeroPoint95 Oct 13, 2026
3cc50a0
fix: keep the belt worker page within its budget (#1813)
ZeroPoint95 Oct 13, 2026
0818fe9
fix: keep security affinity ASCII and report incomplete analysis (#1815)
ZeroPoint95 Oct 13, 2026
a65a9ee
fix: align updater acceptance with install paths and settings (#1814)
ZeroPoint95 Oct 13, 2026
dbf2bb7
fix: verify hosted task endings and retain diagnostic evidence (#1816)
ZeroPoint95 Oct 13, 2026
3950174
fix(review): reserve answer time and retain failure evidence (#1817)
ZeroPoint95 Oct 14, 2026
8177776
fix(review): resolve model words and linked roots and format final an…
ZeroPoint95 Oct 14, 2026
953d412
fix: price qualified shell models on their selected service (#1819)
ZeroPoint95 Oct 14, 2026
51335ab
fix: validate consistency findings and retain partial review coverage…
ZeroPoint95 Oct 14, 2026
a6d33d7
fix: connect providers with complete bounded catalogs (#1822)
ZeroPoint95 Oct 14, 2026
fe1cc0b
changelog: roll up v0.8.0 (32 entries) (#1821)
ZeroPoint95 Oct 14, 2026
401a1e0
test: drain learned-quirk writers before simulating restart (#1823)
ZeroPoint95 Oct 14, 2026
c68873a
fix: preserve learned provider facts across concurrent saves (#1824)
ZeroPoint95 Oct 14, 2026
8ebf000
changelog: reconcile v0.8.0 with the merged restart repairs (#1825)
ZeroPoint95 Oct 14, 2026
664d5f4
chat: one reader for a report's first prose line so every surface ski…
ursasi Oct 14, 2026
1acf751
tasks: make the tasks-page test fixtures $TZ-stable (#1808) (#1809)
ursasi Oct 14, 2026
1197791
fix: ask-here pane approvals and card guards (#1833)
ZeroPoint95 Oct 14, 2026
ac961f9
fix: home's ask-here hints and the manual's keys match what the keys …
ZeroPoint95 Oct 14, 2026
966e2a7
automations: replace standing orders with reminders, scheduled work a…
ZeroPoint95 Oct 14, 2026
da8d601
automations: the conversation can change one in place (#1829)
ZeroPoint95 Oct 15, 2026
01adb90
update: releases come from one repository; retire the former-name fal…
ZeroPoint95 Oct 15, 2026
d0cff6b
update: a released codeaf installs the newest stable and restarts bef…
ZeroPoint95 Oct 15, 2026
6c65989
memory: one strength — every kept line rides every conversation, neve…
ZeroPoint95 Oct 15, 2026
85d2184
automations: a finished reminder moved later wakes again; a late save…
ZeroPoint95 Oct 15, 2026
6e73ac7
tui3: make the switch and plan test fakes safe for off-loop readers (…
ZeroPoint95 Oct 15, 2026
443a2ed
census: the call row carries the demanded set, so served != lane mean…
7vignesh Oct 15, 2026
922bd9e
docs: refresh the built-in specialist README (#1840)
ZeroPoint95 Oct 15, 2026
470ab4a
session: the daily spend ceiling binds task and errand turns too (#15…
7vignesh Oct 19, 2026
26662f1
docs: document DeepSWE harness versions and release timing (#1877)
ZeroPoint95 Oct 19, 2026
6f5c1ca
fix: refresh Codex client version to expose newer models (#1870)
0xMrMasry Oct 20, 2026
a4dff11
fix: correct SQLite file URIs on Windows (#1868)
0xMrMasry Oct 20, 2026
1a5c885
Require explicit work orders before auto-starting tasks (#1845)
mikemikimike Oct 20, 2026
fbbc563
docs(agents): say CI is real, Spark is private and no key is normal (…
AbirAbbas Oct 20, 2026
428baa6
tui3: make a team from this chat — + New from this session, then /hel…
ZeroPoint95 Oct 20, 2026
88709c5
Improve model usage accounting across providers (#1887)
santoshkumarradha Oct 21, 2026
92df156
changelog: roll up v0.8.1
AbirAbbas Oct 21, 2026
663dfbf
Improve model usage accounting across providers (#1887)
santoshkumarradha Oct 21, 2026
63aa4bb
Merge the v0.8.1 hotfix (#1887) into staging
AbirAbbas Oct 21, 2026
4eb6cad
Merge the v0.8.1 hotfix back into dev
AbirAbbas Oct 21, 2026
d0af920
selector: fitted crew selection behind a snapshot config (#1891)
santoshkumarradha Oct 22, 2026
371d7f1
installer: name why GitHub's API refused, and retry a refused token o…
AbirAbbas Oct 22, 2026
6a384ec
Merge the #1890 installer hotfix into staging
AbirAbbas Oct 22, 2026
9d3d7e8
installer: name why GitHub's API refused, and retry a refused token o…
AbirAbbas Oct 22, 2026
0a344cd
Merge the #1890 installer hotfix back into dev
AbirAbbas Oct 22, 2026
bc37d44
fix(provider): keep direct-service keys off receipt lookups (#1894)
AbirAbbas Oct 22, 2026
d4cef55
Group task and process completions under collapsed work (#1893)
santoshkumarradha Oct 22, 2026
a6b4f97
feat: automatic contextual memory across conversations and workers
santoshkumarradha Oct 22, 2026
2a5a77b
fix(tui): close task tabs without trapping or replacing their chat (#…
santoshkumarradha Oct 22, 2026
c740a54
Show saved task models consistently across run tabs (#1898)
santoshkumarradha Oct 22, 2026
7cdcba0
Add one-click chat model switching and saved rotations (#1900)
santoshkumarradha Oct 22, 2026
ed7fe43
Recover background memory updates after transient timeouts (#1901)
santoshkumarradha Oct 22, 2026
47a3f80
Show first messages immediately and refresh Home in the background (#…
santoshkumarradha Oct 22, 2026
e8cacad
Recover unavailable memory helpers and clarify engine upgrades (#1903)
santoshkumarradha Oct 23, 2026
7f173fd
fix: confine provider credentials to their configured origin (#1904)
santoshkumarradha Oct 23, 2026
0aa5339
fix: honor free-route opt-out throughout credit recovery (#1905)
santoshkumarradha Oct 23, 2026
4f8cb57
test: verify scoped project instructions reach plan workers (#1906)
santoshkumarradha Oct 23, 2026
39fdbea
fix: stop delegated work and helpers at conversation limits (#1907)
santoshkumarradha Oct 23, 2026
a18155a
fix: open plans beside writers and preserve literal database paths (#…
santoshkumarradha Oct 23, 2026
b14ddcd
fix: recover completed plan work through explicit acceptance (#1911)
santoshkumarradha Oct 23, 2026
e3f7822
changelog: roll up v0.8.2
AbirAbbas Oct 23, 2026
2c9e7b6
Merge the v0.8.2 roll-up into staging
AbirAbbas Oct 23, 2026
360ee86
Merge the v0.8.2 roll-up back into dev
AbirAbbas Oct 23, 2026
d049b13
Restate the DeepSWE comparison around codeaf (#1913)
santoshkumarradha Oct 23, 2026
414276f
Wormhole: your codeaf follows you between computers, on by default (#…
AbirAbbas Oct 24, 2026
38b4660
notices: say Windows builds embed the furrow engine too (#1917)
AbirAbbas Oct 24, 2026
502ed96
cellsync: let a chat go on quit only after its last save uploaded (#1…
AbirAbbas Oct 24, 2026
a43c63b
fix: keep large team and Activity overviews responsive (#1919)
santoshkumarradha Oct 24, 2026
69b14a7
Tidy relay folder (#1920)
santoshkumarradha Oct 24, 2026
d7587fe
Tidy documentation and benchmark folders (#1921)
santoshkumarradha Oct 24, 2026
574c804
Trim contributor docs (#1922)
santoshkumarradha Oct 24, 2026
233e231
Tidy source comments (#1923)
santoshkumarradha Oct 24, 2026
127eb40
Tidy repository examples and documentation (#1924)
santoshkumarradha Oct 24, 2026
29e3f74
perf: reduce Activity memory and repeated team and store work (#1925)
santoshkumarradha Oct 24, 2026
a2f0486
Fix multiline prompt history and discoverable keyboard copy (#1926)
santoshkumarradha Oct 24, 2026
b8f71f3
Run the matching program when connecting to another machine
santoshkumarradha Oct 25, 2026
bb6e1c2
Keep cross-channel update guidance tied to the calling program
santoshkumarradha Oct 25, 2026
e54c33c
Read the handoff guard through the shared environment interface
santoshkumarradha Oct 25, 2026
6bb78c1
Preserve runnable update commands for older remote installations
santoshkumarradha Oct 25, 2026
5fd7484
Keep hosted stop validation independent of machine snapshot helpers
santoshkumarradha Oct 25, 2026
7ce27cf
Register the executable handoff guard as internal plumbing
santoshkumarradha Oct 25, 2026
78af041
Prevent repeated executable handoffs when the marker is empty
santoshkumarradha Oct 25, 2026
35380b8
Merge current dev into host program fix
santoshkumarradha Oct 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 24 additions & 8 deletions cmd/codeaf/chatv3_host.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"sync/atomic"
"time"

"github.com/Agent-Field/codeaf/internal/buildinfo"
"github.com/Agent-Field/codeaf/internal/catalog"
"github.com/Agent-Field/codeaf/internal/config"
"github.com/Agent-Field/codeaf/internal/enginehost"
Expand All @@ -31,7 +32,7 @@ import (
// ── `codeaf chat --host devbox` ─────────────────────────────────────────────
//
// The surface runs here and the session runs there. This file is the door: it
// parses the destination, starts `ssh <dest> codeaf engine`, completes the
// parses the destination, starts the matching program over ssh, completes the
// handshake, and hands the connection to the same surface a local launch opens.
//
// THE PROMPT LAW, and it is the reason this whole path is shaped the way it is:
Expand Down Expand Up @@ -177,10 +178,8 @@ func (l *engineLink) spawn() (io.ReadWriteCloser, error) {
// The remote command, as the far machine's login shell will read it. The
// workspace is quoted because a path with a space in it is a path, and an
// unquoted one would arrive at `codeaf engine` as two arguments.
remoteCommand := "codeaf engine"
if l.workspace != "" {
remoteCommand += " --workspace " + shellQuote(l.workspace)
}
remoteCommand := hostEngineCommand(programName(), l.dest, l.workspace)

// -T because there is nothing interactive on the far end: the engine reads
// frames on stdin and writes them on stdout, and a pseudo-terminal in the
// middle would turn a newline into a carriage return and a frame into
Expand Down Expand Up @@ -210,6 +209,25 @@ func (l *engineLink) spawn() (io.ReadWriteCloser, error) {
return pipePair{r: stdout, w: stdin}, nil
}

// programName follows the executable file, including custom installation names.
func programName() string { return codeupdate.InstallName(enginehost.ThisBinary()) }

// hostEngineCommand selects on the far machine in the same ssh hop. An engine
// failure never triggers a fallback: only absence of the requested file does.
func hostEngineCommand(name, dest, workspace string) string {
args := " engine"
if workspace != "" {
args += " --workspace " + shellQuote(workspace)
}
primary := "exec " + shellQuote(name) + args
if name == "codeaf" {
return primary
}
notice := "printf '%s has no %s; using its codeaf (%s)\\n' " + shellQuote(dest) + " " + shellQuote(name) + " \"$(codeaf version)\" >&2"
return "if command -v " + shellQuote(name) + " >/dev/null 2>&1; then " + primary +
"; elif command -v codeaf >/dev/null 2>&1; then " + notice + "; exec codeaf" + args + "; else " + primary + "; fi"
}

// stderrWriter keeps the launch-time prompts visible but keeps redial output
// inside the session's diagnostic tail. A reconnect happens while Bubble Tea
// owns the terminal's alternate screen, so writing ssh's transient errors to
Expand Down Expand Up @@ -389,10 +407,8 @@ func (l *engineLink) diagnose(dest string, cause error) error {
}
}

const missingHostCommandText = "the program is called codeaf now (it was aforge before 2026-09-14) and must be installed on %s under that name — put it on the PATH that a non-login ssh command sees" // legacy-name

func missingHostCommand(dest string) error {
return fmt.Errorf(missingHostCommandText, dest)
return fmt.Errorf("%s is not installed on %s — run %s on %s; put it on the PATH that a non-login ssh command sees", programName(), dest, codeupdate.CurlLine(enginehost.ThisBinary(), codeupdate.FollowedChannel(buildinfo.String())), dest)
}

// mentionsMissingCommand reads the far shell's own wording. The three spellings
Expand Down
2 changes: 1 addition & 1 deletion cmd/codeaf/chatv3_host_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ func TestH9HostMissingCommandNamesTheCurrentInstallation(t *testing.T) {
}
}
got := missingHostCommand("devbox").Error()
for _, want := range []string{"called codeaf now", "before 2026-09-14", "installed on devbox under that name"} {
for _, want := range []string{"not installed on devbox", "https://agentfield.ai/get/", "non-login ssh"} {
if !strings.Contains(got, want) {
t.Fatalf("host remedy %q does not contain %q", got, want)
}
Expand Down
46 changes: 26 additions & 20 deletions cmd/codeaf/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,14 @@ import (
"path/filepath"
"strings"
"sync"

"time"

"github.com/Agent-Field/codeaf/internal/buildinfo"
"github.com/Agent-Field/codeaf/internal/cell"
"github.com/Agent-Field/codeaf/internal/config"
"github.com/Agent-Field/codeaf/internal/enginehost"
"github.com/Agent-Field/codeaf/internal/env"
"github.com/Agent-Field/codeaf/internal/home"
"github.com/Agent-Field/codeaf/internal/remote"
"github.com/Agent-Field/codeaf/internal/session"
Expand Down Expand Up @@ -299,8 +301,8 @@ func clearStaleEngineHost(workspace string) (string, error) {
// WHICH IS OLDER IS THE BUILD MOMENT AND NOTHING ELSE, and a tie is never a
// replacement, so two windows on two builds agree and the slot cannot go back
// and forth. A NEWER ENGINE IS JOINED when it speaks this build's wire and
// refused in words when it does not — the older half of that pair is this
// binary, and the sentence names it.
// handed to its executable when it does not. If that file cannot be used,
// the older half of the pair is this binary, and the sentence names its update.
func clearStaleEngineHostAs(workspace string, me engineBuild) (string, error) {
held, err := enginehost.Inspect(workspace)
if err != nil {
Expand Down Expand Up @@ -337,7 +339,15 @@ func clearStaleEngineHostAs(workspace string, me engineBuild) (string, error) {
if host.Version == remote.Version {
return settleCellsMode(workspace, host, me)
}
return "", &staleHost{reason: newerEngineHostSentence(hostWorkspace(host, workspace))}
if _, handedOff := env.Lookup(engineHandoffEnv); !handedOff && host.BuiltAt.After(me.BuiltAt) {
if info, err := os.Stat(host.Binary); err == nil && info.Mode().IsRegular() && info.Mode().Perm()&0111 != 0 {
environment := append(os.Environ(), engineHandoffEnv+"=1")
if err := execEngineHolder(host.Binary, os.Args, environment); err != nil {
return "", &staleHost{reason: newerEngineHolderSentence(host, workspace) + " (" + err.Error() + ")"}
}
}
}
return "", &staleHost{reason: newerEngineHolderSentence(host, workspace)}
}
// Too old to answer at all: it cannot say whether it is holding work, so
// nothing here may decide for it. Words, and the explicit stop.
Expand All @@ -350,6 +360,12 @@ func clearStaleEngineHostAs(workspace string, me engineBuild) (string, error) {
return olderEngineOnThisWire(workspace, held)
}

const engineHandoffEnv = "CODEAF_ENGINE_HANDOFF"

func newerEngineHolderSentence(host remote.HostSelf, workspace string) string {
return newerEngineHostSentence(hostWorkspace(host, workspace)) + "; the holder's executable " + shellQuote(host.Binary) + " is unavailable or cannot be used"
}

// olderEngineOnThisWire is an older engine that speaks this build's protocol:
// the ordinary road. Busy means joined, idle means asked to go.
func olderEngineOnThisWire(workspace string, held enginehost.Holder) (string, error) {
Expand Down Expand Up @@ -544,9 +560,9 @@ func staleEngineHostSentence(busy bool, workspace string) string {
}
stop := engineStopWords(workspace)
if busy {
return fmt.Sprintf("engine: %s is still running an older codeaf and something is still going in it — let that finish, or run %s on %s (interrupts active turns and tasks)", name, stop, name)
return fmt.Sprintf("engine: %s is still running an older %s — after work finishes, close every window for --workspace %s and reopen", name, programName(), shellQuote(workspace))
}
return fmt.Sprintf("engine: %s is still holding this conversation on an older codeaf — run %s on %s (interrupts active turns and tasks)", name, stop, name)
return fmt.Sprintf("engine: %s is still holding this conversation on an older %s — run %s on %s", name, programName(), stop, name)
}

// unknownOlderEngineHostSentence is what a person reads when the process holding
Expand All @@ -562,8 +578,7 @@ func unknownOlderEngineHostSentence(workspace string) string {
if strings.TrimSpace(name) == "" {
name = "that machine"
}
stop := engineStopWords(workspace)
return fmt.Sprintf("engine: %s is holding this conversation on an older codeaf that cannot say what it is or what it is doing, and this build will not end work it cannot see — run %s on %s if you want it stopped (interrupts active turns and tasks)", name, stop, name)
return fmt.Sprintf("engine: %s is holding this conversation on an older %s that cannot say what it is or what it is doing — after work finishes, close every window for --workspace %s and reopen", name, programName(), shellQuote(workspace))
}

// otherProtocolEngineHostSentence is the refusal for an older engine on a wire
Expand All @@ -576,8 +591,7 @@ func otherProtocolEngineHostSentence(workspace string) string {
if strings.TrimSpace(name) == "" {
name = "that machine"
}
stop := engineStopWords(workspace)
return fmt.Sprintf("engine: %s is holding this conversation on a codeaf that speaks a different protocol, and something is still going in it — let that finish, or run %s on %s (interrupts active turns and tasks)", name, stop, name)
return fmt.Sprintf("engine: %s is holding this conversation on a %s that speaks a different protocol — after work finishes, close every window for --workspace %s and reopen", name, programName(), shellQuote(workspace))
}

// engineStopWords is the command a refusal tells a person to run: the explicit
Expand Down Expand Up @@ -607,15 +621,7 @@ func newerEngineHostSentence(workspace string) string {
if strings.TrimSpace(name) == "" {
name = "that machine"
}
self := enginehost.ThisBinary()
if strings.TrimSpace(self) == "" {
self = "this codeaf"
}
stop := "codeaf engine --status"
if workspace = strings.TrimSpace(workspace); workspace != "" {
stop += " --workspace " + workspace
}
return fmt.Sprintf("engine: %s is holding this conversation on a newer codeaf than %s — use the newer binary (%s on %s shows which one it is)", name, self, stop, name)
return fmt.Sprintf("engine: %s is holding this conversation on a newer %s — run %s update on this machine", name, programName(), programName())
}

// hostWorkspace is what the host says it is holding, and what this process asked
Expand Down Expand Up @@ -645,11 +651,11 @@ func busyEngineHostSentence(workspace string, waits bool) string {
if strings.TrimSpace(name) == "" {
name = "this machine"
}
note := fmt.Sprintf("this window is using an older codeaf background engine on %s; restarting one window does not update it. Attached windows count as in use. After work finishes, close every window for this workspace and reopen", name)
note := fmt.Sprintf("this window is using an older %s background engine on %s; restarting one window does not update it. Attached windows count as in use. After work finishes, close every window for this workspace and reopen", programName(), name)
if waits {
note += " — the engine will step aside when all windows and work are gone"
}
return note + ". Or run " + engineStopWords(workspace) + " on " + name + " (interrupts active turns and tasks); reconnect with this build"
return note
}

// replacedEngineHostSentence is the ONE LINE a takeover owes: which engine was
Expand Down
10 changes: 10 additions & 0 deletions cmd/codeaf/engine_handoff_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//go:build !windows

package main

import "syscall"

// Exec retains the bridge's arguments and all three standard streams.
func execEngineHolder(binary string, args, environment []string) error {
return syscall.Exec(binary, args, environment)
}
9 changes: 9 additions & 0 deletions cmd/codeaf/engine_handoff_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//go:build windows

package main

import "fmt"

func execEngineHolder(binary string, args, environment []string) error {
return fmt.Errorf("automatic executable handoff is unavailable on this platform")
}
90 changes: 82 additions & 8 deletions cmd/codeaf/engine_stale_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ func TestAnOlderEngineHoldingWorkKeepsItAndIsJoined(t *testing.T) {
if err != nil {
t.Fatalf("an older engine holding work was refused rather than joined: %v", err)
}
for _, want := range []string{"older codeaf background engine", "Attached windows count as in use", "all windows and work are gone", "interrupts active turns and tasks", engineStopWords(workspace)} {
for _, want := range []string{"older " + programName() + " background engine", "Attached windows count as in use", "all windows and work are gone"} {
if !strings.Contains(note, want) {
t.Fatalf("the busy line %q does not say %q", note, want)
}
Expand Down Expand Up @@ -405,7 +405,7 @@ func TestALaunchRefusesAnIncompatibleBusyEngineRatherThanFallingBack(t *testing.
if !errors.As(err, &refusal) {
t.Fatalf("the launch answered %v, want the host's own refusal", err)
}
if !strings.Contains(refusal.sentence, "different protocol") || !strings.Contains(refusal.sentence, "engine --stop") {
if !strings.Contains(refusal.sentence, "different protocol") || !strings.Contains(refusal.sentence, "close every window") {
t.Fatalf("the refusal %q does not say what is holding it and the way out", refusal.sentence)
}
// AND NOTHING WAS OPENED IN THIS PROCESS: no project tree, no journal.
Expand All @@ -431,8 +431,8 @@ func TestAnOlderBuildWithNoStampHoldingWorkIsJoined(t *testing.T) {
if !strings.Contains(note, "older codeaf") {
t.Fatalf("the notice did not say which engine this is: %q", note)
}
if !strings.Contains(note, "After work finishes, close every window") || !strings.Contains(note, "interrupts active turns and tasks") {
t.Fatalf("the notice does not offer a safe update and warn about explicit stop: %q", note)
if !strings.Contains(note, "After work finishes, close every window") || strings.Contains(note, "--stop") {
t.Fatalf("the notice does not offer a safe update: %q", note)
}
if !hostAnswers(workspace) || !host.waitingSaid() || host.forced() {
t.Fatal("a stampless older build was ended rather than joined")
Expand Down Expand Up @@ -509,14 +509,14 @@ func TestANewerEngineIsJoinedAndNeverReplacedByAnOlderWindow(t *testing.T) {
func TestANewerEngineOnAnotherWireIsRefusedAndLeftAlone(t *testing.T) {
shortEngineHome(t)
workspace := "/home/somebody/api"
standIn(t, workspace, remote.HostSelf{Version: remote.Version + 1, Build: "tomorrow", BuiltAt: later, Workspace: workspace}, false)
standIn(t, workspace, remote.HostSelf{Version: remote.Version + 1, Build: "tomorrow", BuiltAt: later, Workspace: workspace, Binary: "/missing/newer-program"}, false)

_, err := clearStaleEngineHostAs(workspace, window("today", earlier))
var stale *staleHost
if !errors.As(err, &stale) {
t.Fatalf("a newer engine on another wire answered %v, want a refusal", err)
}
if !strings.Contains(stale.reason, "newer codeaf") || !strings.Contains(stale.reason, "codeaf engine --status --workspace "+workspace) {
if !strings.Contains(stale.reason, "newer ") || !strings.Contains(stale.reason, " update") {
t.Fatalf("the refusal does not say this binary is the older one and how to see which is newer: %q", stale.reason)
}
if !hostAnswers(workspace) {
Expand All @@ -542,7 +542,7 @@ func TestAnOlderEngineOnAnotherWireHoldingWorkIsRefusedNotKilled(t *testing.T) {
}
// AND THE COMMAND IS COPY-PASTEABLE: the folder is quoted, because a path
// with a space in it typed out of a sentence has to be one argument.
for _, want := range []string{"different protocol", "engine --stop", "--workspace " + shellQuote(workspace)} {
for _, want := range []string{"different protocol", "close every window", "--workspace " + shellQuote(workspace)} {
if !strings.Contains(stale.reason, want) {
t.Fatalf("the refusal %q does not say %q", stale.reason, want)
}
Expand Down Expand Up @@ -741,7 +741,7 @@ func TestAnEngineTooOldToBeAskedIsRefusedAndTheExplicitStopStillEndsIt(t *testin
if !errors.As(err, &stale) {
t.Fatalf("a too-old engine answered %v, want a refusal in words", err)
}
for _, want := range []string{"cannot say what it is", "engine --stop", "--workspace " + shellQuote(workspace)} {
for _, want := range []string{"cannot say what it is", "close every window", "--workspace " + shellQuote(workspace)} {
if !strings.Contains(stale.reason, want) {
t.Fatalf("the refusal %q does not say %q", stale.reason, want)
}
Expand Down Expand Up @@ -848,3 +848,77 @@ func TestEngineStatusUpgradeGuidanceMatchesAdmission(t *testing.T) {
})
}
}

// Re-execute a helper process so the production exec preserves real argv and
// stdio rather than replacing a function with a mock.
func TestNewerHolderHandsOffToItsExecutable(t *testing.T) {
if os.Getenv("CODEAF_TEST_HANDOFF_CHILD") == "1" {
shortEngineHome(t)
workspace := t.TempDir()
target := filepath.Join(t.TempDir(), "newer-program")
body := "#!/bin/sh\nprintf 'holder-exec:%s\\n' \"$CODEAF_ENGINE_HANDOFF\"\nprintf '%s\\n' \"$@\"\n"
if err := os.WriteFile(target, []byte(body), 0700); err != nil {
t.Fatal(err)
}
standIn(t, workspace, remote.HostSelf{Version: remote.Version + 1, Build: "newer", BuiltAt: later, Binary: target}, false)
_, err := clearStaleEngineHostAs(workspace, window("older", earlier))
t.Fatalf("handoff returned: %v", err)
}
cmd := exec.Command(os.Args[0], "-test.run=^TestNewerHolderHandsOffToItsExecutable$")
cmd.Env = append(os.Environ(), "CODEAF_TEST_HANDOFF_CHILD=1")
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("handoff: %v: %s", err, out)
}
if !strings.Contains(string(out), "holder-exec:1\n-test.run=^TestNewerHolderHandsOffToItsExecutable$\n") {
t.Fatalf("argv or guard lost: %s", out)
}
}

func TestNewerHolderHandoffCannotLoopOrUseAMissingExecutable(t *testing.T) {
for _, tc := range []struct {
name string
mode os.FileMode
guard bool
value string
}{
{"missing", 0, false, ""}, {"not-executable", 0600, false, ""}, {"guarded", 0700, true, "1"}, {"empty-guard", 0700, true, ""},
} {
t.Run(tc.name, func(t *testing.T) {
shortEngineHome(t)
workspace := t.TempDir()
target := filepath.Join(t.TempDir(), "devaf")
if tc.mode != 0 {
if err := os.WriteFile(target, []byte("#!/bin/sh\nexit 9\n"), tc.mode); err != nil {
t.Fatal(err)
}
}
if tc.guard {
t.Setenv(engineHandoffEnv, tc.value)
}
standIn(t, workspace, remote.HostSelf{Version: remote.Version + 1, Build: "newer", BuiltAt: later, Binary: target, Busy: true}, false)
_, err := clearStaleEngineHostAs(workspace, window("older", earlier))
if err == nil || !strings.Contains(err.Error(), target) || !strings.Contains(err.Error(), " update") || strings.Contains(err.Error(), "--stop") {
t.Fatalf("unsafe refusal: %v", err)
}
if !hostAnswers(workspace) {
t.Fatal("holder was stopped")
}
})
}
}

func TestChannelsShareOneWorkspaceAndTheNewerBuildWins(t *testing.T) {
shortEngineHome(t)
workspace := t.TempDir()
host := standIn(t, workspace, remote.HostSelf{Version: remote.Version, Build: "newer", BuiltAt: later, Binary: "/bin/devaf", Busy: true}, false)
for _, name := range []string{"/bin/codeaf", "/bin/devaf", "/bin/custom-af"} {
note, err := clearStaleEngineHostAs(workspace, engineBuild{Build: "older", BuiltAt: earlier, Binary: name})
if err != nil || note != "" {
t.Fatalf("%s could not join: %q %v", name, note, err)
}
if !hostAnswers(workspace) || host.forced() {
t.Fatalf("%s replaced newer holder", name)
}
}
}
Loading
Loading