50 Commits
Author SHA1 Message Date
esengine 5766a8ebeb refactor(tools,plan): complete_step takes no evidence; drop conclude_blocked and plan criteria
complete_step now only says a step is done: it names the step and what is now
true, and the host advances the task list as before. It no longer asks for
commands, files or criteria to cite and no longer checks them against the
session's receipts.

conclude_blocked existed to let the readiness gate through and goes with it;
the prompts that pointed at it now ask for a sentence in the reply. Plan steps
lose their acceptance and verification fields: the submit_plan schema, the
planner and executor prompts, the rendering and the re-approval rule all
stop mentioning them, and plans saved with them still load.

The executor's no-action nudge now keys on a plan naming files only.
2026-11-02 06:33:28 -08:00
esengine 181e697147 fix(tools): tell the model browser_open is visible and web_fetch reads static pages 2026-10-28 22:36:18 -07:00
SivanCola 9f08f957f3 feat(tools): propose enabling file delivery cards by default
Problem: File delivery requires explicit opt-in even when cards are useful.
Root cause: The user-only configuration key defaults to false.
Fix: Default it on while preserving explicit off choices, read boundaries
and tool exclusions. Keep this policy separate from the settings UI PR.
Verification: Config round trips, complete config/settings/builtin/boot
suites, exact request goldens, schema stability, vet and lint.

The default request fixture gains 143 schema tokens. A newly opened session
may have one cold prefix; this does not mutate the live session's schemas.
2026-10-21 12:27:21 +08:00
SivanCola 85bd880d68 Make file delivery an explicit user opt-in
Problem: the presenter was registered for every install, and review requested
default-off model behavior, repository screenshot removal and PDF hardening.

Root cause: present_files used the unconditional builtin path rather than the
existing user-scoped conditional registration and provider-surface mechanism.

Fix: gate registration with tools.present_files, protect user scope across
project merges, preserve the setting on save, and expose the tool only after
opt-in. Restore the unconditional schema budget and remove PR screenshots.
Document PDF.js 6's removal of eval/isEvalSupported, keep scripting disconnected
and explicitly disable XFA rather than passing an unsupported no-op option.

Verification: boot.Build direct/proxy off/on/forbidden/disabled/excluded effect
matrix; user-scope and save/reload tests; document tests and typecheck; real
browser PDF navigation; go vet, make lint, make check and release-note tests.
2026-10-21 03:48:14 +08:00
SivanCola e1159a4685 Add structured file deliveries and workbench document previews
Problem: AI-created documents could not be handed off as validated, durable
file cards, and binary files could not be read through the text editor route.

Root cause: delivery identity lived only in ordinary tool output, while file
reading, view selection and auxiliary actions lacked a shared preview path.

Fix: add a bounded root-confined reader, Controller and raw-byte preview APIs,
and host-confirmed present_files results projected through live and stored
history. Add lazy PDF, worker CSV and sandboxed HTML viewers, responsive file
cards, type icons, current-file state and accessible shared tooltips. Preserve
dirty drafts while surfacing deletion/revocation and avoid metadata reads per
keystroke. Keep the current workbench request-consumption contract intact.

Verification: root and desktop Go tests, go vet, make lint/check, frontend
typecheck, 498 related tests and production build. Browser checks cover PDF
navigation, CSV literals, menus, request consumption and responsive themes.
The builtin schema inventory grows by 143 measured tokens; boot effect tests
verify stable schemas within a turn for direct and capability-proxy calls.
2026-10-21 03:32:25 +08:00
esengine c7e6ef9a52 feat(settings): offer the setting tool, name the switch on a disabled dispatch, and document both 2026-10-17 18:57:27 -07:00
esengine f3a5a6bcce feat(settings): the setting tool, the host that confirms a change, and the structured remedy on refusals 2026-10-17 18:57:27 -07:00
esengine 1502533094 feat(tools): read_video hands an MP4 to a model that watches video natively 2026-10-17 04:35:17 -07:00
esengine 92d084515c feat(tools): read_video samples frames from a video with ffmpeg
A read-only tool that turns a video into a few time-labelled pictures for a
model that reads images. The container is named from the file's own bytes and
ffmpeg is forced to it with file-only protocols; failures carry typed codes.
It is reached through use_capability and stays out of the schema prefix (#13399).
2026-10-16 07:31:37 -07:00
xxoingr c02541014b fix(cli): stop offering the retired light execution setting
agentpreset.Normalize maps "light" and "economy" to balanced, and All()
lists balanced and delivery; light's own policy no longer exists. The CLI
still put light forward as a choice, in seven hand-written strings: the
--preset help of run, tui and serve, the --profile deprecation warning of
each, and parseRuntimeProfile's refusal ("want light, balanced, or
delivery"). The guides went further and described a Light policy (direct-
first planning, targeted verification) that nothing enforces, so a user
who picked it got balanced without being told.

The seven strings now come from agentpreset.All() through livePresets in
preset_flags.go, and the docs (GUIDE, GUIDE.zh-CN, CLI.zh-CN, TOOL_CONTRACT)
offer balanced and delivery, drop the Light policy bullet, and say the
retired names select balanced. light and economy still parse.
2026-10-15 18:27:31 +08:00
yhh b0e85b06cf fix(computer): restore installed applications through CUA 2026-10-13 22:00:14 -07:00
esengine 1ef2f9168d Merge studio into codex/intelligent-ui-12584-workspaces
Declares import_web_image's snip stance (a small JSON receipt takes the default) and regenerates
docs/TOOL_CONTRACT.md, the two contract tests that landed on studio after the branch point.
2026-10-13 13:56:03 -07:00
yhh 88129d0f6a feat(studio): replace native computer helper with CUA Driver 2026-10-13 08:43:14 -07:00
yhh 8e483dde72 feat(tools): bash_output without a job id lists the running background jobs
The orchestrating agent had no way to ask which background jobs are still
running and for how long, so it kept a count of its own and miscounted. The
listing comes from the job manager that owns the jobs (id, kind, label,
elapsed), through the existing read-only tool so no tool schema is added.
list_subagents rows now report when a child started and, while it runs, for
how long.
2026-10-11 01:59:57 -07:00
yhh 362c8381b5 refactor(agent): stop pushing context pressure to the model and the user
The host appended a <context-budget> block to the turn tail at 75% and 92% of
the compaction trigger and raised a matching warning card. The model can still
ask: the read-only context_budget tool and its figures are unchanged, and its
description now says when to check, since nothing reports it unprompted.
2026-10-06 19:56:33 -07:00
yhh dc809e1150 feat(tools): add the render_chart built-in, hidden from the provider schema
render_chart validates a chartspec v1 spec with chartspec.Parse and answers
with a chart_id and the deterministic text summary, never the rows. It is a
registered built-in left out of the provider allow list, so it is found and
called through use_capability and the cache-stable prefix is unchanged. The
spec persists in the call arguments the session already stores (#11564).
2026-10-04 04:46:12 -07:00
esengine 0e03a900b5 docs: bring the contract documents up to the docs standard
Add owner headers to twelve contract and design documents, split their
over-wide paragraphs into rules, route their ownership to the primary
named in CODEOWNERS, and treat the generated protocol index as a
machine-read file. Baseline: doc-owner 40 -> 27, doc-prose 2276 -> 2147.
2026-09-29 09:40:04 -07:00
yhh 11edcd175b refactor(config): configuration stops reaching into the subsystems
contract/config imported eight packages from the layer above it. This takes
five of them out, each by moving the piece to where it belongs rather than
hiding the import:

- The installed plugin packages merge into configuration through data config
  declares (InstalledPackage, PackageMCPServer); pluginpkg converts a parsed
  package into it and names itself the source at init. How a package merges
  with what the person wrote, conflicts included, stays in config; how a
  package is parsed never enters it. That also breaks the loop that kept
  command dirs typed by the command package: config names CommandDir and
  command.Root is an alias of it.
- A permission rule's syntax is the form config stores it in, so Rule and its
  parser move to contract/permrule; permission keeps both names.
- mcpdiag has no dependencies and moves to contract. The directory move itself
  landed in 03e0cb894, swept in from a shared index; this completes it.
- The workspace path fingerprint is a pure function of a path and moves to
  base/workspaceid, with the same result.
- The system-one migration writes the hosted address it was written for.

computer_act and computer_read now have one description on every platform,
naming the difference (Meta+s or control+s; paste needs the front on macOS, on
Windows typing brings it forward), so the generated tool contract, the schema
budget and the cached prefix no longer depend on the host OS.

layer-order debt: 47 -> 40.
2026-09-23 18:49:10 -07:00
yhh 394da0a313 refactor: the kernel's packages live in the layer they belong to
internal/ was 141 packages side by side, so nothing but a reader's memory
said which were foundations and which assembled the rest. Each now sits in a
layer directory, and the directory is the declaration:

  base      L0  foundation with no domain in it
  contract  L1  types and interfaces the layers above agree on
  model, tools, safety, ext, state, platform
            L2  subsystems, peers of one another
  runtime   L3  the turn engine
  session   L4  the controller
  assembly  L5  the composition root
  frontend  L6  cli, serve, acp and the host adapters

Only paths change: package names, code and behaviour do not. Imports,
repolint and coverage baselines, sensitive paths, CI package lists and docs
follow the new paths; release notes keep the paths they were written with,
and sample paths inside tests and fixtures keep theirs.

repolint gains layer-order: a package may import its own layer and those
below it, read from where it lives. The 47 imports that point upward today
are recorded as that rule's opening debt (-allow-widen, a new rule's first
baseline) and can only go down. Most of it is config, which still reaches
into eight subsystems and is the next thing to take apart; the rest is
event, history and a few tools reaching into runtime for types that belong
in contract.
2026-09-23 18:28:21 -07:00
yhh 5af3cb3800 fix: restore CI, and two Windows defects it was hiding
The browser gate read a file URL's path and ignored its host, so
file://elsewhere/C:/<workspace>/x passed as a workspace file while Chrome on
Windows opens it as a UNC share on another machine. A host other than
localhost or a drive letter now names no local file, and a bare drive (C:),
which resolves against the working directory rather than the drive root, is
refused.

Escape during a computer-use run compared the stop time against the run's
start. Windows advances time.Now by timer tick, so a stop inside the tick the
run started in compared equal and the run carried on. The stop is now counted.

The rest is CI debt:
- golden prompts and TOOL_CONTRACT.md regenerated after the browser_act
  description change
- desktop go.sum tidied after the terminal removal
- the three OAuth concurrency tests the terminal removal deleted are restored
  against ReconcileHTTPMCPOAuthAfterRemoval, the production writer that still
  bumps the generation; their loss put internal/plugin under its floor
- the broken-config test reads the resolved user config path instead of
  assuming ~/.reasonix, which is not where Windows keeps it
- frontend tests run without vi.mock or fake timers: the launch probation
  waits in real time, a missing settings chunk is a real failed import, and
  format switches language through boot()
2026-09-22 18:51:05 -07:00
esengine 3c31656424 fix(tools): a screenshot says that the person sees it too
Watched a real run: asked to operate the Calculator and show the result, the
model took the screenshot the tool gives it, then wrote sixty-three lines of
ctypes against CoreGraphics to find the window id, ran screencapture into a
file, and opened that file in the browser pane — six or seven calls to put a
picture somewhere it believed a person could look. It had no way to know the
card already draws the one it was given.

Sixteen tokens of schema (6751 -> 6767) against that.
2026-09-18 03:25:30 -07:00
esengine 87817a5faa fix(docs): the tool contract is generated, as it says it is
The document opens by saying it is generated from the same canonical schema
path as the runtime registry. It was not: the table was maintained by hand, and
the check only asked whether a row existed with the right read-only flag. So a
description could drift from the one the model is actually sent, and sixteen of
them had — including bash, whose row still told a reader to prefer shell
redirects the tool now tells the model never to use.

The table is written from the registry, and the check compares it. Everything a
person wrote around it stays theirs. A round that changes a description on
purpose regenerates with -update-contract.
2026-09-18 02:29:55 -07:00
esengine 591326b98b feat(computer): the pointer the person is holding, borrowed and given back
Accessibility can only touch what an application published as an element. A
canvas, a drawn view, a map, a game — the other implementations reach those by
moving the pointer, and this one answered computer.no_action. The live test
pins the difference on one view: AXPress finds nothing there, and the same
point under pointer_click reaches the application as a real mouseDown.

So the pointer becomes an explicit class rather than another step in the list:

- pointer_move, pointer_click, pointer_drag and pointer_position aim in the
  pixels of the screenshot the model was given, the same frame every other
  point-taking step reads, and the helper posts real events to the HID tap.
- The helper refuses a point whose window belongs to another application, so
  an approval for one application cannot walk into the next one's window.
- It is the person's pointer: taking it parks where they left it and the run
  gives it back, whether the steps finished, failed part way, or were stopped.
- The permission subject carries a pointer prefix, and that subject is only
  ever answered by an exact grant — no posture, no application-wide allow, can
  hand over the pointer on its own. The approval says so in as many words.

Alongside it, the steps the other implementations have and an accessibility
tree already exposes: right_click opens the element's own menu, scroll says
whether it turned the wheel or brought a named element into view, hold_key
holds a chord for a duration, key repeats, and wait is bounded.

internal/computer's floor rises 85.8% -> 89.8%.
2026-09-17 20:45:57 -07:00
esengine 2c6f74d1ec feat(browser,computer): the actions the other implementations have and these lacked
Read against Anthropic's computer tool (17 members), OpenAI's computer-use
action list and Playwright's MCP server, then filled the gaps that a real run
had already hit.

The browser gains reload — a page whose file changed is served again, which a
run worked around by appending ?v=2 to the address — forward beside back, drag
from a ref or point to another, upload, which hands a file input files from
inside the workspace and refuses anything outside it, and browser_read find,
which answers where text is on a page too long to read rather than paging 250
lines at a time. Verified against a real Chromium: the reload shows the changed
file, back and forward walk this tab, the upload arrives and /etc/hosts is
refused, and the drag travels so the page sees it.

Computer use gains right_click, which opens the element's own context menu
rather than borrowing a pointer, scroll, which brings a ref into view or turns
the wheel at the application, and wait. The keys and the pointer stay where the
person left them, as before.

And the host now names what a locked screen does: measured here,
CGSSessionScreenIsLocked makes every application answer with its own element in
place of its windows, so a snapshot holds a menu bar and a click by point finds
nothing to press. The snapshot, the missing element and the dead point all say
so; typing, keys and screenshots keep working, which is what the failing run
ended up using. The built-in schema budget grows 6346 -> 6512 for the two
vocabularies.
2026-09-17 19:50:52 -07:00
esengine 049ee1a75d feat(computer): the agent can read and operate this Mac's other applications
Two tools over a native helper. computer_read lists the applications a person
can see (what=apps), reads one's accessibility tree with host-issued [aN] refs
(snapshot), or captures its front window (screenshot). computer_act runs steps
in order on one application — click by ref or at x/y in the latest
screenshot's pixels, focus, set_value, type, key — stops at the first failure
with its code, and returns the application's tree afterwards. An application
is named by its bundle id.

Nothing takes the person's pointer or the foreground. A click is an
accessibility action on the element under the point, and only an element of
the application the call names takes it; keys go to that application's
process. An element with no accessibility action answers computer.no_action
instead of being clicked some other way. The helper draws its own cursor where
it acts, and Escape pressed while that cursor shows stops the run between
steps (computer.stopped). Terminals, the keychain, password managers, System
Settings, authorization prompts and Studio itself are refused before the
helper is asked. A missing Accessibility or Screen Recording grant is requested
from the system once per helper, and the failure tells the model what the
person has to switch on and where.

desktop/computer-helper is the helper, in Swift: JSON lines on stdin/stdout,
built for macOS 12 with ScreenCaptureKit checked at run time. internal/computer
owns everything it is not: which applications, what a ref means, and the
screenshot geometry that turns image pixels into screen points. It is a leaf
package and declared sensitive, with a coverage floor of 85.8 from tests that
run the test binary as a fake helper; live_test.go drives a real application
through the real helper when REASONIX_LIVE_COMPUTER names one.

Permission: both tools form the Computer rule group, granted per application
as Computer=<bundle id>. A call on another application needs a person the way
typing a secret into a site does — auto, a glob and a bare rule do not answer
it; a person, the session grant they gave for that application, an exact rule,
or YOLO does. Headless auto refuses and says why. computer_act is an unknown
mutation for evidence, since an application can write into the workspace and
names no paths; computer_read is none.

The tools reach a provider only in a host that has a helper:
reasonix-studio-host takes -computer-helper, and no other host passes one, so
the default prefix and its goldens do not move. The built-in schema budget
is widened 6049 -> 6346 for these two schemas, which only a Studio session on
macOS carries.
2026-09-17 09:42:38 -07:00
esengine d1ec7b727e feat(tool): browser_open, browser_read and browser_act, granted per site
Three tools over internal/browser. browser_open loads a page and returns its
snapshot, so the first look costs no extra round; browser_read pages a
snapshot, scopes it to a ref, takes a screenshot or drains console errors and
failed requests; browser_act runs a list of steps in order, stops at the first
failure with its code, and answers with what the page reported and a line diff
against the snapshot the model last saw — a whole snapshot only when the
document changed.

[browser] enabled (default true) decides the schema; which browser runs is
found at first use, so the prefix answers to the config and not to the
machine, and a machine without one hears browser.engine_missing. executable
and headless are the other two keys. The tools are provider-visible rather
than behind use_capability: a browsing session is dozens of calls and nesting
each one's steps inside a proxy's arguments is where calls go wrong. This
moves the default prefix — tool schema tokens 4876 → 5335, goldens
regenerated — so every install pays one cold start on upgrade, and the
built-in schema budget is widened 5590 → 6049 for the same 459 tokens.

Permission reads the site, which is host state for a read or an act: a tool
may now project what the gate reads (tool.PermissionProjector) while the call,
its hooks and its evidence keep the arguments as sent. The browser tools write
the origin the host knows over any origin the model sent. The three share one
rule group, so a session grant or an always-allow names Browser=<origin> and
covers loading, reading and operating that site and no other; a bare grant
covers none. Ask prompts once per site, auto and yolo do not, and a read
follows the read-only fallback. Rule groups move into rule_group.go with the
file-writer group they generalise, whose predicate lost its last caller.

LSPConfig and its renderer move to config/lsp.go beside the new browser.go,
and the [tools] enabled list renders through renderStringArray.
2026-09-17 07:47:37 -07:00
esengine 99c5360fd0 docs: the references and code the deletion commit left behind
68b031ae5 was pushed with only its deletions and renames staged. Its
`git add` named CHANGELOG.md, which was already removed, and the failed
pathspec skipped every modification after it. That commit alone does
not build:
- productdocs still imports reasonix/release-notes;
- repolint still calls checkSpecParity;
- the extension golden test reads the old fixture path.

This commit carries the changes 68b031ae5 describes: productdocs and
docs-manifest without the release catalog, repolint without spec-parity,
the golden test on testdata, the /docs tests, the link fixes, ci.yml,
CODEOWNERS, the standard, the theme generator and the lowered baseline.
Verified locally on this exact tree before 68b031ae5 was pushed:
go test ./... passes and make lint is clean.
2026-09-16 21:50:24 -07:00
esengine 94d8982444 feat(agent): a turn can hand its list back to the user instead of being driven
A turn that stopped with items still open was read as work left undone, and
continueUntilReady pushed it on for up to eight rounds with a message the host
wrote. That is right for a step the model can advance alone. It is wrong for a
step whose next input is the user's — presenting one item of a list and waiting
for a reply was indistinguishable from quitting early, so a list worked one item
per turn could not be worked at all: the turn stopped, the host restarted it,
and the whole list came out in one round.

Nothing about the shape of a turn tells those two apart, so a call does.
await_user joins ask and conclude_blocked as the third way a turn ends on the
model's terms: it names the waiting item and what the user must supply, the list
is kept as it stands, readiness waives the open list, and the continuation loop
does not run. Under a Goal the gate is a pause cause, so the FSM stops rather
than driving through it and the evaluator does not spend a call judging a turn
that already said why it stopped; the next user turn lifts that pause and no
other.

The guards are structural: there must be an open list, the step_id must name an
item on it that is not completed, and there must be an interactive user — a
headless run refuses and is pointed at conclude_blocked. The continuation prompt
names the call too, so the round being told to keep going can take it.

Prefix cost is 292 tool-schema tokens for the new schema, and every existing
session cold-starts its prompt cache once.

Left undone and worth its own change: conclude_blocked is registered but absent
from HostControlToolNames, so the host names it as a way out of an unfinished
turn while the schema does not carry it and only use_capability reaches it.
Adding it grows the prefix enough to move where the compaction fixture in
TestEffectFoldDropsSupersededStandingState lands its first fold, which that
test reads as a fold that dropped nothing.
2026-09-14 01:05:43 -07:00
yhh 01ee3830b1 feat(agent): a fleet dependency edge carries the answer, not only the order
depends_on ordered its endpoints and delivered nothing, so a research →
implement pair paid for the same research twice. Three changes that compose:

- The edge carries the dependency's answer. It travels as
  ContextRequest.Upstream, never inside TaskSpec.Objective, because the
  objective must stay the pristine text delivery classification judges —
  ClassifierTaskText is captured before any host framing. One 8 KiB budget is
  shared across a node's dependencies, so a wide fan-in cannot flood the
  opening turn and no dependency is starved out of it.

- list_subagents enumerates the children a conversation owns. A reference is
  the only address a child answer has, and a background fleet that a restart
  repaired to interrupted never delivered the aggregate carrying those
  references, leaving its completed children unreachable.

- adopt_ref stands a node down to an already-completed child's answer instead
  of running it. It is proven in preflight through ReadFinalAnswer, so what may
  be adopted cannot drift from what read_subagent_result may read. Nothing
  runs, so the node claims no write_paths and cannot collide with a live
  writer; prompt and every execution field are refused on it rather than
  silently ignored.

Whether an adopted answer is still valid is not readable from structure, so the
host does not decide it: adoption is addressed by reference, and the caller that
listed the reference judges freshness. That is also why a plan fingerprint does
not quietly reuse matching children — reuse the caller cannot see is reuse it
cannot correct.

A child now inherits something, which is exactly what InheritedContext exists to
keep a decision rather than an accident, so the capsule records it and SPEC 3.13
says so.

--ablate upstream leaves the edge ordering its endpoints while delivering
nothing, which is what lets the payload be measured apart from the order.
Against the real provider on benchmarks/upstream-edge/fleet-edge-wide, n=6 per
arm, removing the payload cost +27.6% tokens (exact permutation p=0.011) and
+59% sub-agent tool calls for the same answer, 6/6 solved either way. On a fact
one grep away it buys nothing: the edge pays in proportion to what the
upstream's work cost against how small its result is.

boot.go's delegation registrations moved to delegation_tools.go and Execute's
spec loop to buildItemSpecs; that, plus folding two defaulting branches into
firstNonEmpty, is what paid for the new lines without widening the repolint
baseline.
2026-08-24 01:03:50 -07:00
yhh 70928f045a feat(agent): the model can see how much room it has left
We had a lot of compaction machinery and no way for the model to know it
was about to be compacted. Grepping the tree for any figure ever shown to
it returned nothing: the model was always the folded party, never an
informed one. A model that knows it is at 15% writes down the exact path
and the next step before the summary takes them; one that does not gets
folded mid-thought.

Two halves, because they answer different moments.

The push is a tagged fragment appended before the next sampling call, at
75% and again at 92% of the distance to the compaction trigger — the fold
is the event it can still act ahead of, so the figure counts down to that
rather than to the physical window. It rides the turn tail, so the prefix
stays byte-stable; it is edge-triggered, because a notice that re-fired
every step would spend the very window it reports on. The rungs re-arm
when the history is rebuilt.

It hangs in the run loop rather than in control.Compose. A goal turn runs
dozens of tool calls between user turns, and a notice that only fires on a
user turn would never reach the turn that needs it.

The pull is context_budget, for the decision that arrives between the two
rungs: a broad search or a large file the model cannot afford to read.
Measured at the boundary rather than estimated — ToolSchemaTokens in the
golden moves 4362 -> 4509, so the schema costs 147 tokens of prefix, and
SystemHash is unchanged, which is the evidence the base prompt was not
touched.

ContextGeneration lands here because the latch needs it. Compaction
installs a projection and never calls Session.Rewrite, so a latch keyed on
the rewrite counter alone never notices a fold.

Extracting toolCallContext is not drive-by: the one binding line pushed
prepareToolExecution past the 120-line limit, and the repo does not widen
a baseline to land a change. The extraction reclaims budget instead.
2026-08-21 05:54:56 -07:00
esengine 31fecad954 feat(compaction): give the fold index a reader
Every fold has left an index of #n addresses since the last change, and
nothing could read one. This is the tool that does: recall names canonical
positions and returns them, with the tool results that answered a call, so
an address is a way back rather than a headstone.

Two things keep it from undoing the fold that made room for it. A recall
draws on a budget worth a tenth of the window, and a request past what is
left is refused whole rather than trimmed — half a span reads as the whole
of what was there. The ledger carries the generation it belongs to, so each
compaction grants a fresh budget with nothing to reset: a stale generation
already reads as unspent.

What is still visible is refused too. The model re-reads that itself, and
the description sends it back to read_file for anything a current copy
answers better. Recall is for what cannot be re-derived: the user's own
words, and the output of work that should not run twice.

The tool schema costs 199 tokens on the provider-visible prefix.
2026-08-14 21:16:31 -07:00
SivanCola 46923b9895 Rename role settings to execution settings
Problem: User-facing role terminology suggests a persona switch, while these presets actually control planning, verification, and delivery behavior.

Root cause: Desktop, CLI, ACP, and documentation retained the earlier role-setting product name.

Fix: Use Execution setting / 执行设定 / 執行設定 consistently across product surfaces while preserving agent_preset, work_mode, tokenMode, and compatibility values.

Verification: go test ./internal/i18n ./internal/cli ./internal/acp; cd desktop && go test ./...; cd desktop/frontend && pnpm test:all && pnpm build; local browser menu click and overflow check.
2026-08-11 17:04:07 +08:00
SivanCola 5e03e03eaf feat(agent): light/balanced/delivery role settings with unified tools / 角色设定与统一工具面
Problem:
Work modes (economy/full/delivery) mixed token surface trimming, delivery
gates, and UI labels. Economy forced connect_tool_source schema growth;
switching modes rebuilt controllers and busted prompt-cache prefixes.
Skill profiles could hide tools from the model surface.

Fix:
- Introduce AgentPreset (light|balanced|delivery) + TaskPolicy host floors
  for plan route, verification, and independent review.
- Share one provider-visible core surface across presets; optional tools
  dispatch only through use_capability (retire connect_tool_source boot).
- Switch role settings in place without controller rebuild (CLI /preset,
  desktop, ACP agent_preset; legacy work_mode/tokenMode dual-write one version).
- Emit turn_phase and completion_summary events; wire desktop/CLI UI.
- Keep skill profiles diagnostic-only; dual-write agent_preset on tabs/branch meta.
- Docs, i18n, golden surfaces, and regression matrix updated.

Verification:
- go test ./internal/boot ./internal/agent ./internal/cli ./internal/acp
  ./internal/control ./internal/skill ./internal/agentpreset ./internal/taskpolicy
- desktop role-setting smoke tests
2026-08-11 09:25:13 +08:00
esengine 4caf5d7f2f feat(completion): the claim becomes checkable — reconcile it against the receipts
update_goal could say "complete" but never say what it ran, what it skipped, or
what it is worried about. The first is checkable and the other two are not, and
that asymmetry is the design: `verified` commands are matched against this
session's real receipts, while `unverified` and `risks` are declarations the
host has no way to infer and therefore keeps verbatim.

A claim can only add. Every host-found gap survives reconciliation untouched, so
accepting the model's account costs nothing in strictness — and a verification
that never ran, exited non-zero, or last ran before the newest mutation becomes
an unbacked_claim gap, leading the report because it is the worst kind.

The claim rides the ledger: update_goal receipts already carry their arguments,
so the report reads the latest successful call and needs no new plumbing. A
rejected call claims nothing.

e2ebench prices it as unbacked claims — asserted verifications the ledger
denied. Behavior is unchanged: the goal FSM still gates exactly as before, and
the report still only observes.
2026-08-09 03:49:25 -07:00
SivanCola cb829eab2c feat(agent): add explicit context compression tool 2026-08-09 03:17:57 +08:00
SivanCola 6aa634db60 Merge remote-tracking branch 'origin/main-v2' into fix/goal-runtime-fail-closed 2026-08-05 09:26:45 +08:00
SivanCola 2c54501b5e fix(agent): preserve parallel subagent results (#7375)
Problem: Long parallel_tasks and fleet answers could exceed the single-tool output limit, combining and truncating independent child reports.

Root cause: Both aggregators concatenated every full child answer before the global output guard applied head/tail truncation.

Fix: Persist each child transcript, return bounded fair previews with stable references, and add a conversation-scoped read_subagent_result pager with lineage and workspace checks.

Verification: go test ./... -count=1; go test -race ./internal/agent -count=1; ./scripts/cache-guard.sh; git diff --check.
2026-08-05 02:43:45 +08:00
SivanCola 5e4f0eb708 fix(goal): make autonomous completion fail closed
Problem:
Goal mode relied on assistant footer markers and treated missing or malformed markers as a reason to keep opening turns. Delivery readiness also owned a separate retry loop, allowing simple answers or stalled work to consume tokens until a hard turn ceiling.

Root cause:
Completion state was encoded in free-form text, continuation ownership was split between Goal and Delivery, and the host lacked a unified evaluator, progress signal, and token budget.

Fix:
Add the stable update_goal tool, epoch-scoped turn reports, a bounded independent evaluator, centralized Goal readiness decisions, progress-aware budgets, usage accounting, and compatible CLI/Desktop/remote runtime state. Delivery now reports structured readiness without scheduling hidden retries.

Verification:
- go test ./...
- cd desktop && go test ./...
- cd desktop/frontend && pnpm typecheck && pnpm test:all && pnpm build
- go test -race ./internal/control ./internal/agent ./internal/recovery ./internal/goaleval ./internal/tool/... ./internal/evidence
- ./scripts/cache-guard.sh
- go run ./cmd/remote-protocol-gen -check -root .
- git diff --check
2026-08-05 02:29:35 +08:00
SivanCola 0c542ceb7c feat(docs): embed version-matched product documentation
Problem:
Reasonix installations could not reliably search the exact documentation and release notes shipped with their build, and dynamic slash-menu filtering could leave stale virtual row gaps.

Root cause:
Documentation lived only as source files without a compiled retrieval identity or deterministic slash-command path. The shared virtual menu also let TanStack Virtual cache rows by numeric index while React used different keys.

Fix:
Embed docs and reviewed release notes, add the read-only docs tool and /docs command across CLI and Desktop, stamp and verify corpus identity in release builds, enforce documentation-impact declarations, and align virtual-list semantic keys across slash and file menus.

Verification:
- go test ./...
- go test -race ./internal/productdocs ./internal/boot ./internal/control ./internal/cli
- cd desktop && go test ./...
- pnpm --dir desktop/frontend build
- ./scripts/cache-guard.sh
- bash scripts/release-workflows.test.sh
- bash scripts/verify-embedded-docs.sh v0.0.0 e67b2aa824
2026-08-04 13:20:08 +08:00
SivanCola 2c1df5b4f9 Trust configured MCP and Hooks by default / 默认信任已配置的 MCP 与 Hooks (#6889)
* fix(mcp): trust and surface project servers

Problem:
Project-provided MCP servers were blocked behind a separate launch authorization flow and were not clearly distinguished from global servers in Desktop or CLI management views.

Root cause:
Project scope was coupled to launch approval, while configured-server lookup and presentation did not preserve project provenance consistently.

Fix:
Treat project MCP configuration as user-authorized by default, preserve workspace-scoped activation and relative working directories, load project config for reconnects, and group project, global, and managed servers consistently across Desktop and CLI.

Verification:
- ./scripts/cache-guard.sh
- go test ./...
- cd desktop && go test ./...
- pnpm build
- pnpm exec tsx src/__tests__/capabilities-panel-actions.test.ts

* fix(mcp): preserve config scopes and precedence

Problem:
Global MCP installs and project declarations shared persistence paths, so Desktop and CLI actions could write the wrong config, store activation against the wrong workspace, or remove same-name declarations across scopes.

Root cause:
Runtime config merging preserved the effective entry but persistence call sites did not consistently carry its owning source or the controller workspace.

Fix:
Write explicit installs to the user-global config, keep project edits in reasonix.toml or .mcp.json, apply deterministic project-over-global precedence, and restore the next same-name fallback after removing an override. Align Desktop, CLI, TUI, imports, Planner-facing runtime state, and documentation with the same rules.

Verification:
- go test ./...
- go vet ./...
- cd desktop && go test ./...
- cd desktop && go vet ./...
- cd desktop/frontend && pnpm exec tsx src/__tests__/capabilities-panel-actions.test.ts
- cd desktop/frontend && pnpm run typecheck
- cd desktop/frontend && pnpm build
- ./scripts/cache-guard.sh
- git diff --check

* test(mcp): satisfy staticcheck in handler

Problem:
The PR lint job rejected the new MCP test handler with staticcheck QF1003.

Root cause:
The handler used an if/else-if chain on the same request method value.

Fix:
Use a tagged switch for initialize and tools/list responses without changing test behavior.

Verification:
- go test ./internal/control
- git diff --check

* feat: trust configured project hooks by default

Problem:
Project hooks required a separate workspace trust action even though configured MCP servers and installed plugin hooks already use low-friction trusted defaults. This added a second confirmation model and left Desktop, CLI, diagnostics, and documentation describing a trust state users should not need to manage.

Root cause:
Hook loading and inspection were gated by a persistent trust.json store, with that state propagated through boot, slash commands, Wails settings, diagnostics, and compatibility payloads.

Fix:
Load project hooks automatically whenever a project root is present, while preserving Safe Mode as the emergency disable path. Remove the trust store and user-facing trust controls, and keep legacy CLI commands, Wails methods, LoadOptions.Trusted, and trusted_project JSON as backward-compatible no-ops or enabled values. Update tests, diagnostics, localization, and documentation to describe automatic loading.

Verification:
- env -u DEEPSEEK_API_KEY go test ./...
- cd desktop && go test ./...
- pnpm typecheck
- pnpm check:css
- pnpm exec tsx src/__tests__/diagnostics-settings.test.tsx
- ./scripts/cache-guard.sh
- Desktop browser validation for project Hooks
- git diff --check

* fix: keep MCP auth cleanup in the active workspace

Problem: The TUI clear-auth action followed the process working directory instead of the active controller workspace, and Desktop CI was blocked by an orphaned helper.

Root cause: Authentication cleanup bypassed the workspace selection used by the MCP manager, while the removed project-trust flow left an unused Desktop path helper behind.

Fix: Centralize TUI MCP workspace selection, add a cross-workspace regression test, remove the unused helper, and document the executable repository-config boundary with Safe Mode recovery guidance in English and Chinese.

Verification: go test ./internal/cli; go vet ./...; go test ./...; cd desktop and run go vet ./..., go build ./..., and go test ./...; ./scripts/cache-guard.sh; git diff --check.

* fix(mcp): preserve advertised cold-cache connect calls

Problem:
A provider could call a cold-cache MCP connect placeholder that had been advertised at request serialization time, but background schema discovery could replace it before dispatch and produce an unknown-tool failure.

Root cause:
Tool calls are resolved against the current registry after the provider returns, while cache-miss discovery intentionally swaps the synthetic connect placeholder for the server's real tools.

Fix:
Recognize a stale synthetic connect call as completed when the same MCP namespace now exposes real tools, and let already-resolved lazy placeholders report the same controlled result. Keep genuine unknown calls unchanged and add regressions at both dispatch and lazy-lifecycle boundaries.

Verification:
- go test ./...
- go test -race ./internal/plugin ./internal/agent
- go vet ./internal/plugin ./internal/agent
- ./scripts/cache-guard.sh
- delayed cold-cache LongCat MCP integration test
2026-07-24 16:00:36 +08:00
SivanCola 0f42b4b650 feat(agent): Planner and sub-agent trusted MCP via stable use_capability / Planner 与子 Agent 安装即信任 MCP (#6865)
* feat(agent): trust installed MCP in Planner and task sub-agents via stable use_capability

Problem:
The dual-model Planner and task/fleet children could not reliably query installed
MCP servers (e.g. databases) without readOnlyHint or dynamic mcp__ schemas, so
plans and delegated research were often ungrounded. Issue #6854.

Root cause:
Planner and strict/ordinary child registries either excluded MCP or required
readOnlyHint, and MCP tools entered provider-visible schemas so connect churn
broke prompt cache. Delivery alone had use_capability.

Fix:
- NewPlannerAgent with PlannerMCPExecution: authorized non-destructive MCP via
  use_capability without readOnlyHint; destructive left for Executor.
- Session-shared MCPCapabilityRuntime (Host/specs/connections) with per-agent
  use_capability frontends (isolated ledger/audit).
- Ordinary task/fleet children get full proxy (list/inspect/call); destructive
  follows normal writer permissions.
- Strict read-only children share proxy schema/reuse but keep authorized +
  readOnlyHint + non-destructive execution.
- Strip direct mcp__* from Planner and sub-agent registries; convert profile
  MCP allowlists to capability IDs; action=list for discovery.
- NonDestructiveMCPExecutionIntent re-check before tools/call; Desktop MCP
  tool card + docs.

Verification:
- go test ./internal/agent ./internal/boot ./internal/control ./internal/plugin ./internal/capability
- ./scripts/cache-guard.sh
- pnpm --dir desktop/frontend typecheck
- git diff --check

Fixes #6854

* fix(agent): close trusted MCP auth, connect gate, and review proxy gaps

Problem:
PR #6865 left three P1 boundaries incomplete: deferred MCP targets treated
!RequireLaunchApproval as authorized (bypassing Spec.Authorized), authorized
mcp_connect__* still entered the ordinary permission Gate, and review/read_only_skill
children never received the shared capability runtime or use_capability allowlist.

Root cause:
Connect helpers invented trust instead of using ServerAuthorized(); lifecycle
connect targets were excluded from the installed-MCP fast path; boot skill
runners still called registry builders without capRuntime and builtin review
AllowedTools omitted use_capability.

Fix:
- Deferred MCP targets and ensureServerTools use only Spec.ServerAuthorized().
- Authorized MCP tools and lifecycle connects share the explicit-deny-only gate.
- Wire capRuntime into read_only_skill and subagent skill runners; add
  use_capability to builtin review/security-review allowlists.
- Regression tests for zero-start unauthorized non-project MCP and trusted
  first connect under deny-all ordinary gates.

Verification:
- go test ./internal/agent ./internal/skill ./internal/plugin ./internal/boot

* fix(agent): stabilize restricted proxy prefix and isolate planner ledger

Problem:
Restricted use_capability proxies embedded dynamic capability IDs in their
Description, so MCP install/tool-list changes altered provider-visible tool
bytes (cache churn). Delivery dual-model still shared Executor use_capability
and ledger with the Planner. Restricted action=list leaked every configured
server. An empty if in subagent_registry_test triggered staticcheck SA4006.

Root cause:
Allowlist text was baked into tool descriptions; PlannerToolRegistry copied
parent use_capability before the clone branch; attachSubagentCapabilityProxy
returned early when a parent proxy was already present; list delegated to the
unfiltered frontend.

Fix:
- Keep restricted proxy description/schema identical to the base proxy; enforce
  allowlists only in host checks and filtered list results.
- Always strip and reinstall an isolated use_capability frontend for sub-agents
  and the Planner; give the Planner its own ledger and audit in boot.
- Filter list output to allowlisted servers only.
- Assert FilterReadOnlyRegistry keeps authorized RO MCP; add stability and
  isolation regression tests.

Verification:
- go test ./internal/agent ./internal/boot ./internal/skill
- git diff --check

* fix(agent): fail-closed restricted MCP list filtering

Problem:
Malformed allowed-tools entries such as "mcp-server:" produced a non-empty
capability allowlist but an empty server set, so filterCapabilityListResult
returned the unfiltered inventory. Unreadable list payloads also passed through.

Root cause:
Incomplete capability IDs were accepted literally, and empty-server / parse
errors used fail-open returns.

Fix:
- Accept only complete mcp-server:<name> and mcp-tool:<server>/<tool> IDs.
- Skip installing a restricted proxy when no servers resolve from the allowlist.
- Empty server sets and JSON parse/encode failures return an empty servers list.
- Regression tests for malformed IDs, empty servers, and bad list payloads.

Verification:
- go test ./internal/agent -run 'TestRestricted|TestMalformed|TestFilterCapability|TestPlannerTool|TestCustomProfile'

* fix(ui): show resolved MCP capability effects

Problem:
The stable use_capability proxy made Desktop and CLI present MCP calls only by the proxy surface. Writer MCP calls could be grouped as quiet research, and terminal users saw the implementation-level proxy name instead of the selected capability.

Root cause:
Reasonix resolved the real MCP target only inside execution. That target name, capability ID, and read-only classification were not carried through durable session history, frontend events, or Remote history projections.

Fix:
- Persist optional resolved target metadata while keeping the provider-visible tool call unchanged.
- Emit deterministic same-ID dispatch refreshes after MCP resolution and carry the same classification on tool results.
- Classify Desktop MCP cards by the resolved effect, fail closed for legacy history, and preserve the metadata across Local and Remote reloads.
- Render use_capability as MCP(capability_id) in the CLI TUI and headless output.
- Add concurrency, serialization, history, protocol, Desktop, and CLI regression coverage.

Verification:
- env -u DEEPSEEK_API_KEY go test ./...
- cd desktop && go test ./...
- pnpm --dir desktop/frontend typecheck
- pnpm --dir desktop/frontend test
- go test -race ./internal/agent -run 'TestResolvedCapabilityDispatchRefreshesWriterClassification|TestResolvedCapabilityRefreshesParallelCallsInProviderOrder|TestUpdateToolCallResolutionPersistsAfterMidTurnSnapshot'
- ./scripts/cache-guard.sh
- go run ./cmd/remote-protocol-gen -check -root .
- git diff --check

* fix(agent): close trusted MCP proxy safety gaps

Problem:
Restricted sub-agent MCP allowlists could reveal sibling tool schemas, proxy-resolved writers could execute concurrently, and Balanced Executors could not directly call capability IDs discovered by their Planner.

Root cause:
Tool grants were widened to server IDs, batching trusted the proxy's read-only surface before resolving the real target, and dual-model Balanced installed use_capability only on the Planner registry.

Fix:
Keep tool grants exact and filter concrete tool inspection, serialize use_capability for every agent, and install isolated stable proxy frontends on both Balanced Planner and Executor while sharing the MCP runtime. Update bilingual contracts and add focused regressions.

Verification:
- env -u DEEPSEEK_API_KEY go test ./...
- go test -race ./internal/agent
- go vet ./...
- ./scripts/cache-guard.sh
- git diff --check

* fix: enforce trusted MCP runtime boundaries

Problem:
Path-scoped sub-agents could resolve use_capability into unscoped MCP writers, and the stable MCP proxy kept boot-time specs after add, update, disable, or removal.

Root cause:
use_capability was classified as statically read-only before dynamic resolution, while MCPCapabilityRuntime and capability routing copied immutable boot snapshots that were not synchronized with controller lifecycle changes.

Fix:
Keep the provider-visible proxy contract stable while restricting explicit write_paths tasks to proven read-only MCP targets. Make each controller runtime an authoritative, race-safe MCP inventory, synchronize CLI/Desktop lifecycle changes and shared-host tabs, clear stale live tools, and route from live runtime snapshots.

Verification:
go test ./internal/agent ./internal/boot ./internal/control ./internal/plugin
go test -race ./internal/agent ./internal/control
env -u DEEPSEEK_API_KEY go test ./...
cd desktop && go test ./...
go vet ./...
./scripts/cache-guard.sh
git diff --check

* fix(agent): bind trusted MCP dispatch to live runtime identity

Problem:
Planner and stable capability frontends could reuse a same-name MCP client from another controller or stale configuration. Plan mode also blocked authorized opaque non-destructive MCP, lifecycle snapshots could mix generations, and legacy Desktop history lost built-in reader classification.

Root cause:
Shared Host reuse checked only the server name, resolved targets were not bound to the controller's current enabled/authorized spec, lifecycle mutations were not linearized with tools/call, and routing read configuration and live tools in separate snapshots. Documentation also described ordinary sub-agent MCP as using normal writer permission prompts even though installation is the trust boundary.

Fix:
- Compare complete host-local MCP runtime identity and require exact identity for Host/tool reuse.
- Bind resolved MCP targets to the live runtime and re-check enablement, authorization, identity, and destructive safety before dispatch.
- Linearize connect/call against update, disable, and removal; return atomic catalog/live-tool snapshots.
- Preserve Planner Plan-mode access to authorized non-destructive MCP without readOnlyHint and safely complete connected server-directory calls.
- Restore legacy Desktop reader classification and align bilingual contracts with explicit-deny-only installed MCP trust and direct-Executor cache limits.
- Add identity, shared-Host, real opaque Planner, revocation, concurrency, and frontend regressions.

Verification:
- go test ./internal/agent ./internal/plugin ./internal/control ./internal/boot -count=1
- go test ./internal/agent -run 'TestPlannerPlanModeExecutesAuthorizedOpaqueMCPThroughRuntime|TestRuntimeDisableLinearizesWithInFlightMCPDispatch|TestMCPCapabilityRuntimeConcurrentUpdatesAndSnapshots' -count=1
- go test ./internal/plugin -run TestMCPRuntimeSpecMatchesExactHostIdentity -count=1
- ./scripts/cache-guard.sh
- pnpm --dir desktop/frontend exec tsx src/__tests__/tool-subject.test.ts
- pnpm --dir desktop/frontend typecheck
- git diff --check

* fix(agent): remove obsolete MCP runtime wrapper

Problem:
The latest PR head failed golangci-lint because ensureServerTools remained as an unused private wrapper after every dispatch path moved to the identity-bound ensureServerToolsForSpec helper.

Root cause:
The runtime-identity refactor replaced all callers but left the compatibility wrapper behind. Go compilation and vet allow unused methods, while CI's unused analyzer correctly rejected it.

Fix:
Delete the dead wrapper so all connect paths continue to require the already-authorized current runtime spec explicitly.

Verification:
- go test ./internal/agent -count=1
- go vet ./...
- go run github.com/golangci/golangci-lint/v2/cmd/[email protected].2 run --timeout=5m
- git diff --check

* fix(agent): preserve MCP images and route IDs

Problem:
The first use_capability call to an unconnected MCP server dropped structured image results, and Planner transient routing rewrote concrete MCP capability IDs to source targets that use_capability cannot call.

Root cause:
onDemandMCPTool exposed only the text Execute interface, so Agent dispatch could not select ExecuteWithImages. RenderTransientBlock applied legacy source rewriting before accounting for Planner MCP proxy routing.

Fix:
Give the deferred MCP target a shared image-aware execution path that retains the runtime authorization lock through tools/call. Keep concrete MCP IDs for capability-proxy routes while preserving ordinary source routing for non-MCP capabilities. Add full Agent-path image coverage and focused route rendering regressions.

Verification:
- go test ./internal/agent ./internal/capability ./internal/control ./internal/boot
- env -u DEEPSEEK_API_KEY go test ./...
- go test -race ./internal/agent -count=1
- go vet ./...
- ./scripts/cache-guard.sh
- git diff --check
2026-07-24 08:21:22 +08:00
SivanCola 2335d0df9e Add profile-aware parallel subagent fleets / 新增可配置并行子智能体 Fleet (#6636)
* feat(subagents): add profile-aware parallel fleets

Problem:
Custom subagent profiles could only be run serially through skill execution, making multi-document workloads unnecessarily slow and leaving concurrent writers without one session-wide safety boundary.

Root cause:
Task execution had no runtime profile selector or shared fleet scheduler, and write coordination was split across entry points without atomic parent reservations or enforceable path claims.

Fix:
- add profile-aware task and fleet execution with session-wide total/writer concurrency limits
- normalize and enforce write path claims, including sandboxed Bash roots and parent write reservations
- make background tasks queue asynchronously and keep parallel_tasks on the shared scheduler
- expose concurrency and read-only profile controls in Desktop and document the contracts

Verification:
- go test ./...
- cd desktop && go test ./...
- go test -race ./internal/agent ./internal/boot ./internal/config ./internal/skill
- cd desktop/frontend && pnpm typecheck && pnpm test:typecheck && pnpm test
- ./scripts/cache-guard.sh

* Tighten subagent write and tool boundaries

Problem:
Path-bound Bash retained sandbox cache and temporary-directory write lanes, wildcard tool allowlists could reject valid narrowed calls, and stale scheduler code blocked lint.

Root cause:
Bash root rebinding did not enable minimal-write confinement, tool lists were intersected before live-registry pattern expansion, and obsolete control flow remained after scheduler unification.

Fix:
Restrict rebound Bash to claim roots, expand both allowlists before intersection, remove stale scheduler parameters and helpers, and add focused execution regressions.

Verification:
- golangci-lint v2.12.2: 0 issues
- go test ./...
- go test -race ./internal/agent ./internal/tool/builtin ./internal/boot ./internal/config ./internal/skill
- cd desktop && go test ./...
- ./scripts/cache-guard.sh

* Preserve fleet cancellation results

Problem:
Cancelled fleet writers could be reported as skipped after they had started, and preflight conflicts could cite the wrong task numbers when read-only items were interleaved.

Root cause:
The cancellation path stopped draining terminal worker results after waiting, while write claims were compacted into a writer-only slice before indexed validation.

Fix:
Drain every started worker result after cancellation, reserve skipped for tasks that never started, and retain one claim slot per original fleet item. Add deterministic cancellation and mixed-task indexing regressions.

Verification:
- go test -race ./internal/agent -run '^TestFleet' -count=1
- go test ./internal/agent
- go test ./...
- golangci-lint v2.12.2: 0 issues
- ./scripts/cache-guard.sh
- git diff --check
2026-07-18 22:18:44 +08:00
88be1a23ce fix(plan): separate workflow from permissions
Align Plan, Standard, and Goal with the shared Permissions and Sandbox path while preserving explicit phase opt-outs, fresh MCP reviews, and cache-stable provider surfaces.

Co-authored-by: HaoyueQin <[email protected]>

Co-authored-by: myipanta <[email protected]>
2026-07-15 11:18:45 +08:00
SivanCola 4285eb0e3c feat(agent): let plan mode connect the workflow planning subset
planmode.Marker promises todo_write for maintaining planning state, but
Economy kept the whole workflow source fail-closed in plan mode because
it bundles complete_step. Admit the workflow source in plan mode and
narrow its installer to todo_write while planning; complete_step still
requires a fresh workflow connect outside plan mode. Document the subset
in the tool contract and cover the full plan/execute loop.
2026-07-14 19:03:38 +08:00
SivanCola 6c0cac8311 feat(agent): streamline prompts and Economy tool surface 2026-07-14 16:53:41 +08:00
SivanCola 3cfc30d421 fix delivery capability enforcement gaps 2026-07-11 22:26:05 +08:00
SivanCola 7d12d4c1b4 fix: gated first MCP discovery, shared-host snapshots, output-backed review evidence
Address the third review round:

- first discovery (P1): action=call on an mcp-server id resolves to a
  deferred connect target — permission-named with the server's bare
  mcp__<server>__ prefix so server-scoped rules match, ReadOnly=false so
  plan mode blocks it and Ask gates prompt before any process spawns —
  whose Execute connects and returns the live tool directory; connected
  servers list side-effect-free, and the Delivery route block now
  instructs exactly that flow for server candidates instead of an
  uncallable capability id
- shared host (P1): the resolve and inspect already-connected branches
  go through the snapshot-refreshing serverTools helper, so a server
  connected by a sibling tab still yields routable mcp-tool entries in
  this tab's catalog
- review evidence (P1): receipts record host-observed output length;
  bash review evidence now requires non-empty output plus an AST-parsed
  content command (cat/head/tail/diff/cmp, git diff/show) whose static
  argv names the claimed path exactly — pipelines, redirects, summary
  and quiet flags (--stat, --name-only, -q), dynamic expansions, and
  substring path hits (path.bak) are all rejected
- stale cache (P2): cached tools of a fingerprint-mismatched server
  inherit StatusStale so routing prompts expose the outdated schema

End-to-end coverage runs against a real stdio MCP helper: no-cache
connect-list-call, cross-tab shared-host snapshots via resolve and
inspect, plan-mode blocking of the connect target, and seven
content-evidence bypass shapes.

Provider-visible schemas and tool ordering remain unchanged; on-demand
tools stay off the main registry (cache-guard 8/8, tail 92-95%).
2026-07-11 13:51:23 +00:00
SivanCola 1f7a4efd87 fix: harden use_capability gating, lifecycle, and review evidence
Address the six review findings on the Delivery capability runtime:

- plan mode (P0): re-run the plan-mode policy after proxy resolution
  against the real target's name/read-only/trust/safety, so
  use_capability(action=call) can no longer reach a write-capable MCP
  tool in plan mode; trusted read-only targets still pass
- permission-before-start (P0): ResolveCall is now side-effect free —
  unconnected servers resolve to a deferred target that connects in
  Execute, after the permission gate and PreToolUse hooks; inspect lists
  live tools or cached schemas and never starts a process
- lifecycle (P0): on-demand connects use AddWithLifecycle(sessionCtx,
  handshakeCtx) so the stdio child survives the 5s handshake budget and
  exits with the session instead of dying before the tool call runs
- capability gate (P1): finalReadinessCheck now checks the require/
  prefer gate before the no-writer early return, so read-only answers
  cannot skip a user-required capability
- review_report (P1): reviewed_paths must be backed by host-observed
  read/diff receipts in the subagent's own ledger; Delivery review/
  security subagents fail their parent call when they finish without a
  kind-matching report instead of retrying silently
- unified specs (P1): the proxy consumes boot-converted []plugin.Spec
  (env expansion, workspace overrides, timeouts, trusted read-only
  tools) instead of hand-copying config fields
- cached tools (P1): Boot and Controller catalog builds now pass
  CachedTools/CacheHashOK loaded from the shared spec fingerprints, so
  auto_start=false servers surface concrete mcp-tool candidates and
  stale caches are marked
- minor: SemanticRouter prices its usage events and records router
  token/cost/latency into the capability audit; the doctor illegal
  profiles warning is reachable again via Skill.InvalidProfiles

The provider-visible tool schema and ordering are unchanged; the proxy
keeps its stable schema and on-demand tools stay off the main registry
(cache-guard tail hit rate 92-95%, unchanged).
2026-07-11 10:49:54 +00:00
Sivan e7e5bc2c0d feat: add runtime profiles and delivery capability routing
Add Economy/Balanced/Delivery runtime profiles with host-enforced delivery
gates, hybrid capability routing, a Delivery-only use_capability MCP proxy,
risk-adaptive structured review, skill requires/profiles metadata, and
capability metrics for e2ebench.
2026-07-11 16:56:03 +08:00
SivanCola 5401c3538b Add context maintenance and tool contract guards 2026-06-29 16:41:36 +08:00