Commit Graph
106 Commits
Author SHA1 Message Date
WHCandCursor 15c6c4a02a fix: align topbar/sidebar so the top-right is not a dead hole
ci / tcc-check (push) Failing after 39s
ci / linux (aarch64, 0) (push) Skipped
ci / linux (x86, 1) (push) Skipped
ci / linux (x86_64, 1) (push) Skipped
ci / windows (push) Skipped
Topbar spans the full width; seat deck starts under it. Compact badge
and a seat chip on the right of the chrome. Hermit 1.0.2.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-29 22:08:58 +08:00
WHCandCursor f29c1000b6 fix: ship hermit-update (and hermit-service) as 1.0.1
ci / tcc-check (push) Failing after 39s
ci / linux (aarch64, 0) (push) Skipped
ci / linux (x86, 1) (push) Skipped
ci / linux (x86_64, 1) (push) Skipped
ci / windows (push) Skipped
Install hermit-update / hermit-service from make install and bootstrap;
keep cst-agent-* as compat symlinks.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-29 22:00:07 +08:00
WHCandCursor 41f4cf85ee release: Hermit 1.0.0 — home layout, icon font, install
ci / tcc-check (push) Failing after 41s
ci / linux (aarch64, 0) (push) Skipped
ci / linux (x86, 1) (push) Skipped
ci / linux (x86_64, 1) (push) Skipped
ci / windows (push) Skipped
Compact the home parliament chip, animate crab states like the HTML kit,
ship hermit-icons.ttf (sbix) + frames, and install the font from make/
bootstrap/package paths.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-29 21:57:46 +08:00
WHCandCursor 8fcbd886c3 docs: final CST-agent → Hermit migration notice (0.10.1)
ci / tcc-check (push) Failing after 40s
ci / linux (aarch64, 0) (push) Skipped
ci / linux (x86, 1) (push) Skipped
ci / linux (x86_64, 1) (push) Skipped
ci / windows (push) Skipped
Point readers at WHC/Hermit as canonical; add MIGRATION.md and
migrate_remote_to_hermit.sh so existing checkouts can retarget origin.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-29 21:46:23 +08:00
WHCandCursor f30b4ede48 feat: rebrand as Hermit — TUI palette, dual glyphs, binary 0.10.0
ci / tcc-check (push) Failing after 41s
ci / linux (aarch64, 0) (push) Skipped
ci / linux (x86, 1) (push) Skipped
ci / linux (x86_64, 1) (push) Skipped
ci / windows (push) Skipped
Ship hermit binary (cst-agent symlink), ~/.config/hermit with legacy fallback,
Hermit color theme and mini crab frames, plus SVG→PUA font build pipeline.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-29 21:42:45 +08:00
WHCandCursor 2602806aea docs: product-facing README; archive CST theory notes (0.9.34)
ci / tcc-check (push) Failing after 43s
ci / linux (aarch64, 0) (push) Skipped
ci / linux (x86, 1) (push) Skipped
ci / linux (x86_64, 1) (push) Skipped
ci / windows (push) Skipped
Rewrite the homepage for day-to-day use, move paper/eval drafts under
docs/archive, and add a docs index so theory no longer leads the project.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-29 20:00:22 +08:00
WHCandCursor 814a31c9c8 feat: restore CI/release, prompt contracts, faster install (0.9.33)
Bring back Gitea CI + auto-publish; share release helpers; strengthen multi-turn
tool/job prompts with a unit contract; ccache/make help and --release bootstrap.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-29 19:56:42 +08:00
WHCandCursor f00aad9e3e feat: solo default, job completion reports, tools wave-1 (0.9.32)
Password Realm no longer auto-enables parliament (keeps task/worker tools);
finished jobs fan out to agent notes + TUI; raise PAR_MAX/grep caps and tag truncations.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-29 19:48:02 +08:00
WHCandCursor 5917c48bd0 feat: TUI completeness — identity, Seat Deck empty states, lean chrome (0.9.31)
Sanitize session titles, stamp CST on the topbar, fill work/sync/keys so
the sidebar never looks unfinished, drop duplicate ctx from the foot, and
tone down the scroll banner plus composer mode line.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-29 19:35:21 +08:00
WHCandCursor 2ccad623e4 fix: drop stranded topbar chair chip; blend chrome into panel (0.9.30)
Seat role already lives in the Seat Deck. Remove the lonely right-edge
chip and paint the session strip on panel ground so it no longer reads
as a raised bar with a hairline seam.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-29 19:18:55 +08:00
WHCandCursor 67b9d6683f fix: keep stream write-head on tip glyph, not next-line col0 (0.9.29)
Appending the scanning caret after trailing newlines parked a lonely
block at the left margin. Overlay the tip character instead and let the
decode crest sweep through the wake.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-29 19:15:01 +08:00
WHCandCursor 6d2e106e26 feat: decode-wave stream glitch — plasma tip, braille wake, caret (0.9.28)
Replace noisy glyph spam with a layered frontier: bright cyan plasma at
the tip, a traveling braille wake that ghosts the real text, a lock flash
as glyphs settle, and a scanning write-head caret.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-29 19:08:09 +08:00
WHCandCursor 1b0f14533a feat: adaptive drip buffer for stream reveal (0.9.27)
Network tokens fill md_live immediately; the UI reveals md_shown at a
non-linear rate (slow at the tip, accelerates with backlog) so glitch
and text stay readable without lagging far behind a fast model.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-29 19:05:20 +08:00
WHCandCursor 7d183d5bba fix: park cursor in composer; glitch stream frontier (0.9.26)
leaveok on chrome panes and always redraw input after side/log motion
so the caret no longer sticks on the Seat Deck version line. Live
assistant text briefly scrambles at the tip then settles to plaintext.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-29 19:02:05 +08:00
WHCandCursor 6e09f79666 feat: Seat Deck sidebar with parliament telemetry and motion (0.9.25)
Replace the OpenCode-style inspector with a seat/fleet/ctx/work telemetry
rail, default-on for wide sessions, driven by 80ms ASCII micro-motion.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-29 18:50:36 +08:00
WHCandCursor 00288108cd feat: TUI chrome refresh — steel theme, home card, session topbar (0.9.24)
Cooler brand palette, compact CST-agent home with tagline and framed
composer, session topbar (model + chair chip), sidebar off by default.
Screenshot pipeline in tools/tui_shot.py; docs/screenshots for before/after.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-29 18:27:06 +08:00
WHCandCursor c329db504d perf: accept thread + dial backoff; join/peers.json (0.9.23)
Run inbound peer handling on its own thread so outbound dials cannot starve
:9100. Failed peers cool down exponentially. Also land --join (member seat)
and realm/peers.json seed discovery for Tailscale meshes.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-29 18:16:46 +08:00
WHCandCursor de2eaf87f7 fix: stop parliament accept starvation on outbound dial (0.9.22)
Peer dial used per-step read timeouts so a stuck handshake could block the
single-threaded daemon for tens of seconds; inbound hellos piled up unread
(CLOSE-WAIT). Use a wall-clock dial budget, drain accepts before each dial,
skip self, and only one side initiates idle polls.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-29 13:19:03 +08:00
WHCandCursor 2f96b050d6 perf: faster builds, leaner daemon, smoother install/run (0.9.20)
Reuse libcstcore.a for tests with -MMD incremental deps; idle-backoff the
parliament seat loop; defer curl init and cache settings; add systemd helper.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-29 11:41:16 +08:00
WHCandCursor 812e3ee265 feat: parliament seat collaboration loop (0.9.19)
Seats publish via parliament_note, sync pushes on write, member join without
stealing the chair, and worker tools are hard-gated when parliament is active.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-29 11:29:38 +08:00
WHCandCursor 1605d74f17 fix: sealed peer sync smoke + clearer approval denials (0.9.18)
CLI/Docker assert AEAD sync; status shows sealed channel; headless denials no longer pretend the user refused.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-29 11:18:04 +08:00
WHCandCursor 5900b53642 feat: seal peer sync with X25519 + XChaCha20-Poly1305 (0.9.17)
Encrypt parliament context sync after Realm mutual auth without pulling in OpenSSL.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-29 11:16:26 +08:00
WHCandCursor a3e115539e feat: /realm in TUI, daemon instance takeover (0.9.16)
Pidfile records instance+port; login restarts the service when the seat
changes. TUI /realm status|login|stop; prompts discourage filler chatter.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-29 11:00:34 +08:00
WHCandCursor e610d70c30 fix: interactive Realm password, recoverable worker budget (0.9.15)
Prompt for Realm password when CST_REALM_PASSWORD is unset; map worker
request-gate exhaustion to a clear budget error with partial progress;
hard-skip identical tool failures after 3 tries. Add docs/parliament.md.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-29 10:53:36 +08:00
WHCandCursor 9c6257fc96 fix: parliament UX, prompt alignment, execution nudges (0.9.14)
Inject shared parliament context into the agent environment, switch
prompts away from in-process workers when parliament is active, clear
login tickets on chair sync/new-project, and surface realm/chair/service
in /status. Tighten empty-reply handling and prefer-tool principles.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-29 10:22:31 +08:00
WHCandCursor be8d021480 fix: Alpine parliament Docker build (musl clean rebuild)
Ignore/clean host objects, add linux-headers, and include sys/time.h
so discover builds under musl. Docker chair↔member handshake now passes.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-29 10:14:15 +08:00
WHCandCursor e853abac5e feat: parliament daemon, login gate, peer context sync (0.9.13)
Login claims the chair, writes a session ticket, and starts a background
service for discovery plus revision-based context sync. Password Realm
opens require --login (or CST_REALM_PASSWORD); --service/--service-stop
manage the resident agent.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-28 23:11:23 +08:00
WHCandCursor de9ee28773 feat: password Realm, single-chair login, shared parliament context (0.9.12)
Derive Realm keys from a user password (Argon2id, no private key on disk).
Login claims the sole chair with handover; peers share parliament context;
--new-project clears it. CLI peer/discover helpers and Docker alpine recipe
included; headless empty-answer retries raised for unattended runs.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-28 23:06:51 +08:00
WHCandCursor 08ca613a0e feat: mutual Realm peer handshake over TCP (0.9.11)
Exchange enrolled certificates and Ed25519 challenge signatures so
same-Realm instances can authenticate each other after discovery.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-28 22:49:33 +08:00
WHCandCursor 5ee82f126d feat: same-Realm peer discovery via UDP and local registry (0.9.10)
Advertise instances with a multicast beacon and a config-dir registry so
same-machine and LAN peers that share a Realm fingerprint can find each
other without TOFU pairing.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-28 22:48:04 +08:00
WHCandCursor dade4072cf feat: add Realm Ed25519 keys and instance enrollment (0.9.9)
Introduce a user Realm keypair (Monocypher) that signs instance
certificates so same-domain peers can later auto-discover and connect.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-28 22:43:54 +08:00
WHCandCursor acd6f1d998 fix: keep task completion, cancel, pause, and undo honest
Pending earlier-revision jobs no longer skip completion repair; late
cancels preserve real exits; begin clears pause; partial undo keeps
snapshots; permissions and worktree failure paths are hardened.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-28 22:09:47 +08:00
WHC d971cb02b0 feat: add v2 task control and asynchronous isolated workers 2026-10-20 14:46:57 +08:00
WHC 143dba203b feat: add persistent multi-model workers and orchestration budgets 2026-10-19 11:29:26 +08:00
WHC 235652604d feat: add Kimi Code and web APIs v0.9.8 2026-10-19 10:41:00 +08:00
WHC 60047ca5ce the session readers use getline(3), which a Windows CRT does not have
Building the Windows TUI by hand -- the first local build since the CI file
went away -- stopped in src/tools/session_tools.c: `getline` was an implicit
declaration there, which this GCC treats as an error. src/session.c had a
private fgetc() shim for exactly that; the session-list scan is newer and used
the POSIX call directly, and only the Windows build notices.

The shim moves to os_compat as os_getline(): getline(3) itself on Unix, the
fgetc() loop on Windows, one prototype in the header so a caller reads the same
everywhere. src/session.c drops its copy and calls it; the scan does too (its
`ssize_t` becomes `long long`, which Windows has).

make check is green on Linux, the Windows cross build now finishes and
packages (dist/cst-agent-0.9.7-windows-x86_64.zip plus the NSIS setup), and the
static Linux tarball was rebuilt with it.
2026-10-11 21:54:32 +08:00
WHC b7034baec4 build: no CI; the packages are made here
The release files are built on this machine from now on, so the workflow that
did the same steps on a runner -- TinyCC build and unit tests, the static musl
Linux tarballs, the mingw Windows zip, the Gitea release upload -- is gone.
.gitea/ held nothing else.

Everything the workflow called stays and is runnable by hand exactly as it ran
there: scripts/ci_linux_toolchain.sh is what package_linux.sh uses to pick its
musl toolchain, scripts/ci_tcc.sh builds the second compiler the test job used,
scripts/ci_windows_cross.sh and scripts/bootstrap_windows.sh are the mingw
build, and scripts/release.sh is the upload (GITEA_TOKEN=... scripts/release.sh
FILE...).
2026-10-11 21:52:08 +08:00
WHC db8cdd7dd1 the back key: one C++ path, so a dialog closes too
A phone's back key did not close the settings dialog. A window-level QML
Shortcut never sees the key while a modal popup is up, and a desktop run says
so plainly: with the dialog open, Back left backLayer() at "settings", while
invokeMethod(win, "stepBack") closed it on the spot and Escape -- the popup's
own CloseOnEscape -- closed it too. So the key reaches the popup and never
reaches the shortcut map, which is why the drawer (no popup) closed fine and
hid the problem on a phone.

A Keys handler on the window's root item is no help either: nothing reached
it, the focused TextField keeps the key. So the key is read in C++, on the
application object but only for this window's events -- a filter installed on
the window itself was never called once, while the same key closed the drawer
through the shortcut, hence the application object.

With a layer open, the window's own backLayer() names it and stepBack() closes
it, press and release both taken; with nothing open the key is passed on
untouched, which is what leaves the window on a phone and does nothing on a
desktop. Main.qml keeps only those two functions: the Shortcut and
backHasLayer are gone, --uitest installs the same handler main() does, and the
back checks now drive the code the phone runs instead of a shortcut only the
test had.

The last check asks the phone's own question and can end the run without the
closing line -- the activity leaving takes the process with it, and every line
is flushed as it is written.

Desktop: 106 ok, 0 FAIL. Phone: 104 ok, 0 FAIL (two clipboard checks are
skipped on Android), and "after the last back: window visible 0".
2026-10-11 21:44:33 +08:00
WHC 81aeee7d1e uitest: scroll a card into view before clicking it
Open and Share failed on the phone for a reason that was in the harness,
not in the app: the card was where isVisible() says yes, but the transcript
clips it away -- the run's own note says it found contentY at -14 with the
card's row below the visible part -- so the click (and the tap after it)
landed on whatever was at that point instead, and the header stayed empty.

The run now stops the view following its tail (ChatView.stickBottom, which
the replay sets), scrolls the card's row into view with the numbers in a
note, and only then clicks. ChatView gets an objectName for that.

The checks also say what the card hands the engine (the name the store
keeps the file under, from the strip's own model) and what the card itself
answers -- which is what turned "it did nothing" into this answer:

  note the card is out of the viewport (contentY -14, card at 433 of 615):
       scrolling to it
  ok   Open hands the file to the system (opened report.md (test mode);
       cards hold 'img-...png, art-....md', on screen, card says '')
  ok   Share names the file (shared report.md (test mode); card says '')

The whole device run is green now: 97 ok, 0 FAIL (it was 88/17 when this
session's work started, and every one of those was real).
2026-10-11 21:14:54 +08:00
WHC a99f78134a uitest: no clipboard on Android, and say which event got through
The two clipboard-image checks failed on the phone for a reason that is not
a defect: Android's clipboard carries text and URIs, not images -- setImage
does nothing there -- so they are skipped on Android with a note saying so,
instead of reporting a phone that is broken because it is a phone.

Open and Share still fail on the device, and now say how far they got:
neither the synthesized mouse click nor a touch tap leaves a line on the
card's buttons, while the same click reaches the header and the sidebar.
The engine's own path is fine (openArtifact() called directly answers as it
should), and the only way for the header to stay empty is openArtifact()'s
early return for a name it cannot resolve -- so what is left to look at is
the name the card hands over (the model's `rel`) and whether the card is
inside the visible part of the scrolled transcript at that moment, since
QQuickItem::isVisible() does not account for clipping.

Device run after this: 95 ok, 2 FAIL (was 17 before this session's fixes).
2026-10-11 21:05:21 +08:00
WHC 8e5bdd0c9d the header is a Rectangle, and the phone puts it where it is
A Controls ToolBar hands its children to a style-owned contentItem, and on
the NX789J that item sat 35 px below the bar itself: the header was at y 0
h 42 while its own gear was at y 56 -- drawn on top of the sidebar's first
lines, and clicked there too, so the settings dialog could not be opened
from the gear at all (eleven checks that follow it failed with it). A
desktop never showed it: same style (Basic), same numbers, gear at y 21.

A plain Rectangle has no such indirection -- its children are where it is.
Nothing else changed; the background and the border line are the same.

--uitest prints what it sees at that point now (style, window, header,
gear, safe area), which is how this was found:

  note style Basic, window 1280x853, header y 0 h 42, gear centre y 21

Before: gear centre y 56, `FAIL the gear opens the settings dialog`, 17
failures on the device. After: 95 ok, 4 FAIL (Open/Share and the two
clipboard-image checks, which Android has no answer for).
2026-10-11 21:00:11 +08:00
WHC f8030ec201 a device run can be looked at: --shots, and Qt's messages in the log
The settings dialog check failed only on the phone, with no QML error and
nothing to read: this build's Qt messages never reach logcat (verified: a
plain qInfo from the app is not in the buffer). Two ways in, then:

- --uitest installs a message handler that writes Qt's own warnings into
  uitest.log, next to the checks. What the phone did say, it says there.
- --uitest --shots saves window grabs at the interesting points into the
  workspace. A device run cannot be handed CST_APP_SHOT (an app started
  from adb gets no environment), so what a desktop run asks for with an
  environment variable has to be an argument here. main.cpp takes it, so
  `--uitest --workspace DIR --shots` works from adb.

What they said about the phone (NX789J, 1280x853):

  ok ... the gear is reachable, centre 1191,56 of 1280x853
  ok ... and openSettings() opens it when called directly

so the button is there and the handler works: the synthesized click is what
does not open the dialog. The run's own picture of that moment shows why --
the header is drawn over the sidebar, so the click lands on what is drawn
above the gear. On a desktop the same window has the header above the
sidebar and the click lands. Not fixed yet (nor the Open/Share checks on the
device, which are the other two failures that are not about the dialog).

The settings checks now go on either way, so a phone run reports the dialog
instead of eleven lines of "not there".
2026-10-11 20:53:16 +08:00
WHC a34c74e453 the tool card counts, and a device run leaves a log
A tool that takes minutes -- a build, a wide search, a command nobody can
predict -- read as a window that had hung: the card said "running" and
nothing else, forever. The chip now counts from the point the card starts
running (from five seconds on, before that it is noise), which is where a
person looks when a call seems stuck; the status line and the Stop button
were already there. A card that appears already running (a replayed
transcript) starts its clock too.

--uitest: two checks for it (a card four minutes eighteen in says
"running · 4:18"; a finished one stops counting).

And the run now writes uitest.log in its workspace, every line and the
verdict. That is the only way to read a run *on a phone*: stdout reaches
nobody there, an app started from adb cannot be handed CST_APP_SHOT or any
other environment (so the shots a desktop run can take are out), and logcat
does not carry this build's Qt messages at all -- checked: a plain
`qInfo` from the app is not in the buffer.

First device run (NX789J, 105 checks): 88 ok, 17 FAIL, every failure
pre-existing and unrelated to this work -- the settings dialog does not
open from the gear (so nothing in it is reachable: the five dialog checks,
the two provider-form ones and the four clicks that follow), Open/Share
leave no status line on the device, and the clipboard-image checks have no
clipboard. All the md_to_pdf and card checks pass on the phone, including
"black ink on paper (darkest pixel 0)" from 4c69ef3.
2026-10-11 20:41:58 +08:00
WHC 9d7ad9433e android: the arguments arrive as a String[], not an ArrayList
The hook added in da04913 asked for getStringArrayListExtra, but `am
start --esa applicationArguments "--uitest,--workspace,/sdcard/x"`
stores a String[] -- that call answers null for an array, so the window
still started with no arguments at all. Ask for the array first, then
the list (Qt's own loader, putStringArrayListExtra), then the single
comma-separated string.

With this the uitest runs from adb on the NX789J: the fixtures and
selftest.pdf land in --workspace, which is how a device round trip is
possible without touch injection (adb cannot tap this window).
2026-10-11 20:35:41 +08:00
WHC 4c69ef39ab pdf: text that names no colour comes out black, not the palette
A QTextDocument takes its default text colour from the application
palette, and markdown names no colour at all: in a window running a dark
palette -- which is what a phone in dark mode hands Qt -- every plain
paragraph was laid out #eeeeee on a white page. Printed, that is a blank
document; on screen it reads as "the font is oddly faint". The first
PDFs off the phone were exactly that.

- PdfTools: paperDocument() sets the default stylesheet (body colour
  #000000) before setHtml, and writePdf() lays that out. A colour the
  document does name still wins, so an .html handed to the tool keeps its
  own -- checked with a #cc0000 document under both palettes.
- --uitest: draws that document on a white image under a dark palette and
  looks for ink (238 before the fix, 0 after).

Verified on the NX789J: md_to_pdf on the device now writes CID TrueType
NotoSansCJKHK-Regular/-Bold text whose page renders at min=0.
2026-10-11 20:35:41 +08:00
WHC da04913071 android: read the activity's own arguments, since Qt does not deliver them
`am start -n <pkg>/org.qtproject.qt.android.bindings.QtActivity --esa
applicationArguments "--workspace,/sdcard/x"` left the window on its default
workspace on the NX789J (the plain-string extra too), so the extra is read
from the intent directly. That is what makes `cst-agent-app --uitest
--workspace DIR` reachable on a device with no keyboard -- and adb cannot
inject touch into this window either (tap, touchscreen tap, mouse tap and
motionevent all do nothing; key events do work), so it is the only way in.

Everything degrades to "no arguments": an invalid object, a missing method or
a context that is not an activity all come back empty.
2026-10-11 20:22:50 +08:00
WHC 7b2e7a3dbd fix: the window's approval switch never reached the engine
needs_confirm() (src/agent_loop.c) decides whether a tool asks by reading
cfg->confirm_shell / confirm_write / confirm_net; the approval mode only
relaxes those. The CLI fills them in (main.c: parse_confirm), the TUI and the
WebUI do too -- the Qt window never did: AgentCore::open() memset the whole
config and cst_agent_create() keeps what it is given, so every tool ran
without asking while the sidebar said "ask". That is why nothing was ever
asked on the phone (or on a desktop).

- AgentCore: one mapping, applyApproval(), used by open() and setApproval();
  the mode is remembered like the TUI's /approval and read back on start
  (else "ask", the only mode where the window's consent dialog is reached).
- --uitest: two checks that the mode reaches the engine as those flags.

Version 0.9.7 (local).
2026-10-11 20:22:50 +08:00
WHC 4e990c8e35 docs/android.md: what a turn does when nobody answers, and md_to_pdf
The SAF picker is not 'still to come' any more (v0.9.6 did it): the list keeps
the back gesture and the icon glyphs. In its place, what v0.9.7 changed on the
phone -- a consent question refused in the background and bounded in the
foreground, the bounded GUI hop, the phase in the header, and the window's own
md_to_pdf.
2026-10-11 20:04:24 +08:00
WHC dae51e06c7 fix the session-list scan's line count, and pin both new paths with tests
The rewritten cst_agent_store_info() counted a transcript line that ran past a
whole 64 KiB read block a second time, when the newline ending its tail
arrived: a session with one very long line (a tool that read a file back)
reported one message too many -- exactly what the sidebar shows the person.

- src/agent_store.c: the tail's newline ends a line that was already counted.
- tests/test_store.c: test_info_scan() holds the scan down -- a 70 KiB line is
  one line, the first user message comes from the head of the file, and a user
  line past the window is not called the first one.
- tests/test_tools_ext.c: a byte budget stops the session_search walk, says so
  in the result, and never reports a match it did not reach; the same query
  without a budget finds it.

make check and make asan are green.
2026-10-11 20:03:39 +08:00
WHC 042d7f6999 v0.9.7: a turn that cannot hang, a session list that reads nothing twice, and PDFs
- consent: on_permission / on_ask_user wait at most two / five minutes, and in
  the background (a phone off screen, a window that is gone) the answer is
  refused at once with a reason -- a dialog nobody can see blocked the worker
  for good, which is what "the app hangs while the agent works" looked like.
- finishTurn() no longer joins the thread it is called from; the event loop
  reaps the Turn instead.
- AndroidTools: the hop to the GUI thread pings first and gives up after 3 s
  instead of waiting forever on a busy GUI thread.
- the session list stopped reading every transcript: cst_agent_store_info()
  counts newlines in 64 KiB blocks (first_user only within the first 256 KiB),
  and AgentCore::sessions() reuses a row while (mtime, size) is unchanged.
- session_search gets a budget (max_bytes 32 MB, max_seconds 5 s), skips lines
  over 64 KiB, and says when the budget stopped the walk.
- the header shows the phase ("shell: make -j8"), so a long turn no longer
  looks like a stuck one.
- md_to_pdf: the front end's own Qt tool (owner app:qt, every platform):
  markdown or a workspace .md/.html becomes an A4 PDF with 15 mm margins,
  paginated by hand, written into the workspace and delivered as a file card.
  No PrintSupport, so the phone gets it too.
- --uitest runs md_to_pdf through the registry with Chinese text and checks the
  bytes are a PDF; app/CMakeLists.txt no longer drops src/web_md.c from the
  standalone engine build, which the window needs to render markdown.

Version 0.9.7.
2026-10-11 19:26:36 +08:00