6722 Commits
Author SHA1 Message Date
yhh 289033df01 feat(studio): a newer release is announced by default and from the sidebar: update_mode reads as notify until the user picks one, so a fresh install checks the catalog in the background, and the sidebar footer carries a button that names the version and opens the version page; a user who chose manual keeps it because the choice is now written to the file 2026-11-07 07:11:24 -08:00
yhh 2fff350d61 fix(jobs): the view test waits for the live job's first line before it reads the running list: the job goroutine had not written it yet on a loaded runner, so the tail came back empty in one run in about two thousand 2026-11-07 06:56:05 -08:00
yhh 0b455f4c81 style(control): the skip-ordering comment keeps to the three lines the repo allows 2026-11-07 04:50:47 -08:00
yhh 01000d4646 fix(control): a skipped question is marked as a skip before its answer reaches the turn: the turn that got the answer first could run on or end before the gate knew why, so it finished as cancelled, was handed a dismissal, or did not requeue the guidance queued into it, which showed as two intermittent failures under the race detector 2026-11-07 04:49:51 -08:00
yhh 07d2d4bdd0 Show the cache hit rate after the cold start, and the output ratio
The session's first executor request pays for the prefix every later one
reuses, so the session-wide hit rate mixes "did the cache work once warm"
with "how much a cold start could reuse". usagereport already records the
cold start, but nothing a live session reads projected it.

The live figures come from the serve ledger, which restarts on /new,
/resume and /fork; usageTally does not, so its ColdStart can belong to an
earlier conversation in the same process. The broadcaster therefore takes
the cold start from the ledger's own population: the first request the
ledger files under the executor (pricing.LedgerSource, the rule Add
already used), split by the same CacheSplit the ledger applies, and reset
with it. SessionUsage carries it as coldStart.

- /status (TUI): "warm cache" row, ledger hit rate with the cold start
  taken out of hit, miss and unknown; "out/in" row, output ÷ input.
- Desktop session overview behind the cache meter: the same two figures,
  computed in state/overview beside cacheRate.
- wire-parity: ColdStartUsage mirrored in port/session.ts.
2026-11-07 09:06:37 +00:00
yhh 0fac934b3f fix(ci): the Windows shell listing joins paths the way the OS it is computed for does, the save allocation check is skipped under the race detector that defeats regexp's pool, and the context-retrieval benchmark whose premise archived folds ended is removed with the surface helper only it used 2026-11-07 00:39:03 -08:00
yhh 14649962fc refactor(provider): remove the summary request flag and what only it served: the Request.Summary field and ErrSummaryToolsUnsupported, the adapters' no-tools branches, the broker's capability probe, and the test doubles and live test written for a summarizer call that no fold makes; the reported-usage effect test now detects a fold by its archive pointer and has a control arm 2026-11-06 23:47:31 -08:00
yhh 0f81082776 refactor(agent): remove the summarizer's bill and request identity, which nothing writes now that a fold is an archive: the spend accumulator, the unset usage and request id on the fold result, and the zero usage carried into telemetry and the receipt 2026-11-06 23:24:39 -08:00
yhh 31db766ff1 test(boot): through the real assembly a hand-asked compact folds again after a little more was said, the report names the fold an archive, and a declined one does not claim a summary 2026-11-06 23:22:14 -08:00
yhh 05c8743f03 fix(agent): a fold is described as the archive it is: the decline wording no longer says a summary is being paid for, /context and the TUI name an archived fold instead of calling it summarized or printing the raw mode, and the codes, modes and translations nothing produces any more are removed 2026-11-06 23:08:04 -08:00
yhh be92796684 test: the coordinator handoff and guardian compaction tests stop scripting a summarizer call, which compaction no longer makes since it archives to a file 2026-11-06 22:51:42 -08:00
yhh 6264845f53 fix(tool): a refusal's remedy names the setting tool by its capability id, tool:setting, now that the tool sits behind use_capability and a bare "setting" is not a name the model can see 2026-11-06 22:51:42 -08:00
esengine 20c2705623 chore: clear the unused agent symbols and the two lines App.tsx is over its ceiling
These block every push through lint and make check; none of them changes behaviour.
2026-11-06 22:49:20 -08:00
esengine 1923e50a34 fix(storage): a move plan proves the nearest existing parent writable instead of creating the target
Planning ran on every keystroke and its write probe called MkdirAll on the target, leaving a folder per typed prefix. The probe now runs in the nearest existing ancestor, free space is read from it, and Move creates the target once the user starts the move.
2026-11-06 22:46:13 -08:00
yhh 339f443ffb feat(boot): setting, present_files, compress and context_budget move behind use_capability and are indexed in its description, cutting the first-round tool definitions by another 4 KB 2026-11-06 22:34:44 -08:00
yhh c8afd3be60 feat(boot): the browser tools move behind use_capability, whose description now indexes the deferred tools by id with a short purpose, so a model that could see a browser but not web_fetch no longer opens a page to read a JSON endpoint 2026-11-06 21:59:04 -08:00
yhh 756c6159a6 fix(agent): a compact asked for by hand no longer answers "too little added" after a checkpoint; asking waives how much was added, and an unchanged context is still declined 2026-11-06 21:22:30 -08:00
yhh 26f72585ee fix(control): in yolo mode remember no longer asks for approval either, so neither memory tool prompts under the mode that skips every other approval 2026-11-06 21:13:36 -08:00
yhh 5b80976176 fix(control): in yolo mode forget no longer asks for approval; it archives a memory rather than destroying it, and remember still asks 2026-11-06 21:11:56 -08:00
yhh 7c7e3ff99c perf(session): resuming a long session no longer repeats work; a replay observes each touched path once, event records are framed in bulk, and a load whose ledger agrees with its transcript is a verified baseline so switching away from an untouched session reads nothing back; resume of a 15 MB log goes from about 1.0 s to 0.42 s 2026-11-06 18:17:52 -08:00
yhh 868a14cadc perf(session): a save that finds its files as its own last save left them no longer replays the event log; the prefix is verified by digest in the pass that digests the transcript, cutting a 5000-message save from 137 ms to 50 ms 2026-11-06 17:25:44 -08:00
yhh 63da66261c perf(serve): package init no longer loads the OS mime table, taking Windows startup from 143 ms to 91 ms; page files already carry their own content types 2026-11-06 17:06:12 -08:00
yhh 76ed2566ed feat(context): a fold archives the whole span and keeps nothing: no first-turn pin, recent tail, kept user turns, user-words list, work index or summary; the pointer is one short message and keep, recent_keep, user_turn_keep_tokens and first_turn_pin_tokens are retired 2026-11-06 10:02:32 -08:00
yhh 817d78b399 fix(memory): the new save refusals carry wording in the desktop, and the remember freshness effect test states the mode it exercises 2026-11-06 08:28:08 -08:00
yhh 813af4958e feat(memory): the model asks before remembering — remember holds a suggestion the user accepts, edits or dismisses; memory.proactive is ask, auto or off, and YOLO saves directly; the open goal panel joins the composer 2026-11-06 08:17:42 -08:00
yhh 1ff0525a34 fix(serve): opening a project whose folder is gone is refused with workspace.folder_missing and the path, not the system's stat sentence 2026-11-06 06:44:19 -08:00
yhh 933ee31d6c feat(studio): the notes above the composer are one line at a time, as the Compose-Notes board draws them; a remote project's terminal says which machine it is on
The composer showed the connect strip, the trust question and the sandbox note
together, each with a paragraph, and a short stack clipped them over the input.
Now one strip is drawn, by priority (cannot send, trust, no sandbox): a single
line, the explanation in the hover text, calm grey for reminders and warm only
for "cannot send".

A terminal opened from a remote pane runs on that machine through the existing
forwarding, and its header carries the host. A test holds that the host's
requests are forwarded and a paired device's never reach the far kernel.
2026-11-06 05:06:01 -08:00
yhh 83bdeb81f0 feat(studio): the workbench gets a terminal of your own, as the Terminal board draws it
A fourth kind of tab opens a real shell (ConPTY on Windows, a pty elsewhere) in
the project folder, outside the model's permissions and sandbox and with the
host's provider keys left out of its environment. The kernel keeps each shell's
output, so a tab left or a window reloaded comes back as it was; a tab is named
for the command submitted while the shell is busy and carries a blue dot, and
closing a busy one asks first.

Also: the composer's note stack no longer clips to a few pixels when it holds
two notes, which left the second one overlapping the input.
2026-11-06 04:44:41 -08:00
esengine dc768371ab chore(agent): drop the snipping helpers nothing calls since compaction archives to a file
The unused linter reports eight symbols in prune.go that the archive-to-file change left without a caller; the strategy type and its hint mapping, which are still used, stay.
2026-11-05 20:48:17 -08:00
yhh ca80fec2fc fix(studio): a hand-written provider at a declared address takes that preset's vendor mark; the setup cards show vendor marks
The mark for a model came only from the preset a provider was installed from, so an entry pointing at api.deepseek.com by hand drew a letter tile. The address is now read through the curated preset serving it, as vision models already are. Setup service cards draw the mark their vendor declares.
2026-11-05 17:17:45 -08:00
yhh 1b8b56aa65 feat(studio): compaction archives earlier turns to a file instead of summarizing; a skipped ask ends the turn as finished; chat overlays follow the design board
Compaction moves the folded span into <session>.outputs/fold-<hash>.md and leaves a pointer the model reads back with read_file. The summarizer path, its retries, bounds and tests are removed.

Answering an ask with no selection ends the turn normally instead of reporting a failure.

Find bar, overview and context cards, compact dialog, image preview, root-missing banner, toasts, side panel and prompt refine are aligned to the Chat-Tools board; the agent preset picker follows the Agent-Picker board.
2026-11-05 16:25:25 -08:00
esengine 1cdbb9303f test(tui): the wait for the provider after a saved key is the hang-catching budget
A fixed ten seconds reported a hang on a loaded Windows runner where the first-run test had already spent forty seconds. testenv.Budget exists for waits that only catch a hang.
2026-11-05 08:19:41 -08:00
esengine 8beea984d0 test(config): the churn in the state-root walk test finishes every removal it starts
A removal that lost a race with the walker was dropped, so the directory stayed behind and, because names are never reused, a later walk could not tell it from an unreadable one and failed after the five-second re-stat window. The churn now retries the removal until it succeeds, which is what the never-reused-name invariant assumes.
2026-11-05 08:19:27 -08:00
esengine c93eabbe4c feat(studio): starter chips in the empty conversation and a changed-settings card on the common page 2026-11-05 10:41:00 +00:00
esengine 8472468039 refactor(studio): remove the delivery-receipt chain and the completion-standard row; light remember and guardian cards 2026-11-05 08:16:07 +00:00
esengine 200db241af feat: a command sent to the background names its job, and its row follows that job, as the Bg-Jobs board draws it
A background start carried its job's id only in the sentence the model reads,
so a window could not tie the call to the job without parsing it. The shell
execution now carries jobId on every layer to the wire. In the execution group
that call reads "放到后台运行", its row shows the job's live state from the
pane's job list — running and for how long, then how long it took and its exit
status — and while it runs a note says the turn goes on without it, with a way
to its output.

The run tabs hook no longer writes its list when a request adds nothing, which
re-rendered without end for a caller passing a fresh setter each time; the job
tab test that did so and hung its worker now keeps one. Execution's comment in
wire.ts states what the type is for rather than how it came to be.
2026-11-04 11:07:07 +00:00
esengine 7d2e9b9457 feat: finished background jobs stay listed with how they ended, and the rail's job list follows the Bg-Jobs board
The status surface carried running jobs only, so a job that ended left no
trace there: the rail could never read "failed", and a person had no way to
see how long a finished build took or how a test run exited. /status now also
carries endedJobs — this session's finished jobs, kept until the session is
purged — and every job view carries the last line it wrote; an ended one adds
when it ended and, for a command, its exit status. The last line is what the
command wrote itself, kept before the finished job's in-memory tail is
released, not the host's note of how it failed. jobs stays the running list,
since that is what its readers take it to be.

The rail's list follows the board: running jobs above ended ones, each with
how long it ran, what it runs and its last line; an ended one with its exit
status; stop asks again before it acts; ended jobs can be cleared from this
window. The reading is a blue pill while one runs, stays red for a failure
until the list has been opened on it, and is grey once all have ended.
Durations pad seconds as the board writes them (4 分 02 秒).
2026-11-04 10:45:08 +00:00
esengine 00601023cc feat(desktop): the plan is one card in the conversation, with the strip above the composer only while it is out of view
The plan card follows the Plan-Card and Chat-Plan boards. Proposed, running,
waiting on a person and finished are the same card changing in place: numbered
steps before it starts, then the kernel's live list with a done mark, a turning
mark on the step in hand, and a badge for where it stands. The work after a
started plan is filed under its card, and the plan's own todo_write rewrites
are left out of that work because the card already draws the list.

The strip above the composer is the card's stand-in: a tab inset 14px with the
step it is on and one segment per step, shown only while the card is off
screen and no decision is waiting. Opened, it is the same tab grown upward,
titled once, with a way back to the card through the transcript's own landing.

Cause: the pending card was one sentence and three buttons, because the plan
request carried no steps, and the live list lived only in the strip, so the
plan was shown twice once it ran. The kernel now sends the starter list the
approval seeds as the plan request's input, from the same parser, and the live
event names its kind as the replayed one already did.

Blast radius: plan approvals on every frontend (the TUI keys its plan answers
on kind, which only the replay carried), the wire form of a plan approval, the
transcript's work layout around a started plan, and the plan-progress
preference, now on unless a person turned it off.

Neighbouring behaviours tested: replayed plan prompt, seed equals the card's
list, unscoped calls still send no input, revise/exit cards, fold-work folding
a card's work, refused todo_write kept, strip collapse/escape/outside press,
goal-only strip, progress preference.

Why this layer: the controller is the one place that parses a plan into steps,
so the card shows what approving starts rather than a second parse.
2026-11-04 08:25:53 +00:00
YHH bfdc7e2a1d Merge pull request #14355 from kevin9327/fix/gitignore-worktree-info-exclude
fix(gitignore): read info/exclude from the common dir when .git is a file
2026-11-03 17:45:27 -08:00
esengine c6fb0eb95b test: skip symlink fixtures where the platform cannot build symlinks
Cause: ten fixtures in serve, ext/plugin and platform/repair built a symlink
with os.Symlink and failed the test on error. A non-elevated Windows run
without Developer Mode returns ERROR_PRIVILEGE_NOT_HELD there, so ten tests
failed and the whole serve suite went red, though CI's elevated runners never
saw it.

Fix: skip those fixtures with the underlying error as the reason, the pattern
the boot package already uses; the refusals they pin down still run wherever
symlinks can be built.

Blast radius: ten test sites in five files, no production code.

Neighbouring behaviours tested: the same tests run unchanged where symlinks
work (ext/plugin, platform/repair, serve), and the packages vet for windows.

Why this layer: the fixture owns whether it can build its own precondition.

Fixes #14969
2026-11-03 15:26:16 -08:00
esengine 47fb2f3ac2 fix(inbox): read and write the queue schema 1.x leaves behind
Cause: Studio's inbox is schema 2 while 1.x writes schema 3, so a session
that had been used in 1.x opened as "written by a newer version" and its
queued messages could be neither sent nor deleted.

Fix: accept schema 3 and keep its fields (fingerprint, attachment identities,
image inputs and source refs) through every rewrite so nothing 1.x wrote is
dropped. This build cannot read 1.x image inputs, so an entry carrying them is
held with the foreign_images block code on both the run and steer paths
instead of being sent without the images.

Blast radius: state/sessioninbox (envelope, request hash, images flag), the
two dispatch paths in session/control, and the block-reason tables in the
desktop and terminal frontends.

Neighbouring behaviours tested: queue tests of the inbox, control, tui, serve
and i18n packages, plus the desktop queue and translation tests.

Why this layer: the envelope owns what is persisted and the shared
applyInboxReferences owns whether an entry may run.
2026-11-03 06:40:40 -08:00
esengine fd6fa05b19 test(history): assert a complete index only once the projection has settled
CI's Windows full suite saw the index answer State=ready with Indexed=Total=1
while a rescan was still pending, so SearchIndexProgress honestly reported
building and the test, which had only waited for the hit to appear, failed.
The test now retries until the projection reports every source indexed and
nothing pending, fails at once on any unreadable-source signal, and only then
asserts that no incomplete-index signal is raised.

Not reproduced locally: the base passed 20x on macOS, 90x under six concurrent
processes on Windows, and a full package run on Windows.
2026-11-02 22:57:54 -08:00
YHH 40b9b8f211 Merge pull request #14947 from chuiwenwei/fix/history-search-does-not-hide-a-source
fix(history): a search says when the index could not read a source
2026-11-02 21:37:39 -08:00
YHH 055b60bc0c Merge pull request #14948 from chuiwenwei/fix/acp-lease-refusal-carries-a-code
fix(acp): a lease refusal says which refusal it is
2026-11-02 21:16:57 -08:00
esengine d0289a4d0f feat(memory): expose galaxy, strength and review state to the memory page
The memory list now carries each fact's galaxy, importance, retention
state, use count, days left before it turns fuzzy, whether it decays,
and whether it is new (written in the last 14 days and not yet reviewed).
POST /memory/confirm reinforces a fact and marks it reviewed; a save from
the page also marks it reviewed and may move its galaxy, importance and
type. The review mark lives in the usage sidecar, so it never rewrites
the fact file.
2026-11-02 17:41:01 -08:00
esengine 87c91ebd06 refactor(memory): copy usage records with maps.Copy 2026-11-02 17:23:53 -08:00
esengine bfc838a3ce feat(memory): a forgetting curve, use reinforcement and links between facts
Each fact has a retention of e^(-days since last use / strength). Reading a
fact multiplies its strength by 1.8 and an automatic recall by 1.2, capped at
365 days; constraints, pinned and evergreen facts never fade. Recall keeps a
clear fact's score, scales a fuzzy one down and holds a faded one at 0.35 so a
strong match still comes back. Facts recalled in the same turn build a link,
and a link of weight two brings its neighbour along. Usage lives in a
.usage.json beside the facts, so a recorded use never rewrites a fact file or
moves its revision. memory search and read report the state.
2026-11-02 17:21:32 -08:00
esengine eef0e8590b fix(memory): a one-letter galaxy name never counts as named by the turn 2026-11-02 17:05:09 -08:00
esengine c22dd9495d feat(memory): rank recall by importance and bring constraints along
Automatic recall scores the name, title, description, galaxy and body as one
field and the synonyms as a second field at 0.6, then weights the result by
importance (0.85, 1.0, 1.15) and a 1.1 boost for facts used in the last 30
days. A constraint is brought whenever its galaxy is named in the turn or is
the galaxy of one of the three best hits, or when its own score reaches 8% of
the best, and it leads the block; at most three are held and an expired one is
never recalled.
2026-11-02 17:05:09 -08:00
esengine cee09e76ba fix(context): a manual compaction above the trigger folds when it frees space
Compacting by hand accepted any real saving only while the context was still
below the automatic trigger. Above it, the request had to land below the
trigger or was declined, so the moment compaction was most needed the user
was told there was nothing to compact. A manual request now accepts any fold
that is strictly smaller; an unchanged context and too little new history are
still declined as before.
2026-11-02 17:03:39 -08:00