A deny or ask rule such as Bash(rm:*) compared only the words the model wrote,
so `env X=1 rm`, `sudo rm`, `command rm`, `nohup rm`, `nice rm`, `time rm` and
`/bin/rm` were judged by fallback mode instead of the rule.
shellparse.PeelWrappers reads one simple command from the shell AST and
follows a fixed set of wrappers (env, sudo, doas, command, builtin, exec,
nohup, time, nice) down to the program they run, naming a path-spelled program
by its base name. Deny and ask matching test every layer; allow and session
rules are unchanged. A wrapper whose target cannot be named statically needs a
human instead of falling through to the mode.
The model list collapsed entries by endpoint + model, so two providers
sharing a base URL with different API keys listed each model once and
the second account's refs vanished from the picker.
TestInstancesDifferBetweenRuns instantiated every task twice and failed
whenever two independent draws collided. Three tasks answer with a bounded
number (i70 quota 1200..9800, i47 lease 15..300, i112 lease 20..420), so a
run fails with probability 1/8601 + 1/286 + 1/401, about 0.61%.
liveRand now delegates to liveRandFrom(io.Reader). The test asserts the
invariant at the seed: two live generators do not share a stream, the same
entropy yields identical instances and different entropy yields different
values.
The walk tolerated only not-exist, but a directory being removed on Windows
fails to open with a sharing violation or access denied. Those errors now
count as vanished only when a re-stat shows the path gone.
Cause: the per-model check classified any 4xx it could not name as
unknown/rejected and the row printed only "request refused, not confirmed".
The kernel already carried httpStatus; the settings UI dropped it, and the
endpoint's own error text was never sent, so a local Ollama 404 (base URL
without /v1, model id without its tag), a 400 for a model that cannot take
tools, and a missing key on a System One entry all read identically.
Blast radius: POST /providers/check/model gains an optional `detail` field:
the endpoint's error text as one printable line. It is built in this order:
control characters and ANSI sequences replaced, the probe key masked in raw,
URL-escaped and JSON-escaped form (skipped for keys under 8 characters),
secrets.RedactCredentials, then cut to 300 runes. Folding and the key
replacement see the whole text; only the pattern scrub is bounded (8 KiB cut
on a word boundary, after folding). The model row shows an HTTP badge and the text inline. Status and
reason are unchanged and never derived from the text.
Neighbouring behaviours tested: Ollama-shaped upstream (correct, base without
/v1, tagless id, System One kind); 400/404/405/422 classification; detail
equals the upstream text, masks raw/escaped/plain keys, Bearer, ?token=,
Cookie, neutralises newlines/ANSI/NUL, cuts after redaction, cuts at the scan cap with the key split at every offset, with and
without leading whitespace, bounds a 4 MB body, leaves short keys alone (each mutation-checked); add and edit flows
share one fact helper; full ui vitest suite and the serve package.
Why this layer: the serve handler owns the typed result and the row is the only
renderer of it; classification stays where the failure is first known.
POST /browser/open answers 400 when the navigation fails even though the
window's tab already exists, and the link handler read every refusal as
"nothing opened" and sent the same address to the machine's browser.
The refusal now names the tab it left behind and the handler only falls
back when there is none.
A pane adopted at launch is not a folder anyone picked, so nothing listed it
once a later launch opened another one. Its conversations stayed on disk with
no sidebar row to open them from. The folder is now listed behind the existing
ones when the pane's first turn mints its session path.
A queued line taken back could fail with a spurious error on a repeated click,
leave a pending transcript row when its receipt landed after the withdrawal,
and reappear in the panel when an older queue pull answered last.
A truncated, timed-out or failed summary request blocked automatic
compaction for the whole generation, so context kept growing past the
fold trigger with no further attempt. Block release now reads the typed
failure code: transient request failures are released after the input
grows by one retry step; every other class stays blocked.
The digest request inherited the session's reasoning effort, so reasoning
tokens spent the same 16384-token cap the digest had to fit in and a
finish_reason=length dropped the whole summary. It now carries
EffortOverride=low; adapters apply it only when the endpoint's own effort
vocabulary admits it, so endpoints without a low tier keep their configured
depth.
Refs #11970
/compact notices were raw English sentences built from err.Error(), and an
aborted card with no code read "compaction done / folded nothing". Add
compacted/compact_declined/compact_failed notice codes (detail = the
compaction code), give cancellation, unclassified failures and a busy
session their own codes, and word them in the desktop and TUI catalogues.
Refs #11970
The terminal UI asked the kernel to refuse any slash line the registry does
not answer, so a typed path or half-remembered command dead-ended with
"unknown command". 1.x sends it as an ordinary message and says so (#5756).
The TUI client no longer sets refuseUnknownSlash; the controller's existing
default path runs the line through the normal turn pipeline. That notice is
now localised (en/zh/zh-TW) and carries the unknown_command code.
A built-in named first is that built-in whatever follows it, as in 1.x: the
kernel matches /context, /new and /clear by first word, and the TUI does the
same for /help, /clear, /setup, /version, /mouse, /resume and its display
commands, so none of them is sent as prose when given arguments.
Refs #11493, #5756
With no API key the connection panel opened silently and a typed message went
to the provider, which answered 401 with a pointer to a shell command. The
panel now names the model that lacks a key, and /submit refuses a turn for it
with provider.key_missing before anything is sent. The TUI returns the line to
the composer and points at /setup.
The usage range test assigned the process-global time.Local while goroutines from earlier tests still read it, so the race detector failed the package. usageWindow now delegates to usageWindowIn, which takes the zone trailing windows count days in; the test passes a fixed zone and never touches the global.
A provider whose natural key slot is taken gets REASONIX_CONNECTION_<id>_KEY. Collect wrote that provider and its key, but restore validation denied every REASONIX_ name, so the backup sealed fine and then failed to open as malformed.
Settings and feedback are answered by a pane's kernel. With no pane open, feedback silently did nothing and the settings refusal never cleared. Opening either now starts a session in the current folder, a refusal is shown for both, and it is withdrawn once a folder or pane exists. The cloud-backup button now states what it is waiting for.
Preserve ACP frame-order assertions while skipping only MCP status updates,
and use t.Context in the serve regression test.
Report servers requiring launch approval as pending rather than failed in
controller, ACP and /mcp snapshots. Keep pending rows disabled without a retry
action while retaining their recorded transport and sanitized error.
Document connecting and awaiting-approval notifications, style the pending
indicator in both themes, and derive frontend state coverage from Go constants.
Add controller, endpoint, ACP and row-rendering regression coverage.
- Map the new `pending` state in the desktop panel with an i18n label. A
repository-declared server nobody has answered for is off, but not because
the user switched it off, and the row rendered the raw state string.
- Announce what actually happened. RecordFailure and
RecordLaunchApprovalRequired now share one store path and name their own
state, so a project MCP awaiting authorization stops reporting a failed
connection.
- Correct the MCPSurfaceReady contract: it fires on every change to what /mcp
answers, not once per server. Audited every consumer before changing it —
the ACP sink republishes a full snapshot, the serve broadcaster numbers the
frame and the SPA refetches /mcp, the headless stream passes it through — so
repeats are harmless and the documentation was the stale part.
- Keep the spawn claim beside the rest of the deferred-spawn bookkeeping
instead of a file of its own.
- Add the /mcp endpoint regression test the refactor was missing (live
transport and source, recorded status code, in-flight spawn, cached
description and tools), and a panel test tying every state label to the
English catalogue.
SetCredential and SetCredentialIfRevision built a KEY=value line and parsed it
with the dotenv reader, so ${...} was expanded and wrapping quotes stripped
before the key was ever written. The writer also left a value containing $
bare, which the reader then expanded. Store the assignment directly and pick
the first encoding the reader returns unchanged; a value no encoding can hold
(for example a trailing backslash next to a quote or $) is refused before
anything is written, so one entry can never make the whole file unreadable.
Adds GET/POST /display-currency over the controller's new
DisplayCurrencySettings/SaveDisplayCurrency (shared with /currency), a
Settings > Usage control, and makes the quote sink follow the
display_currency notice so rounds after a change are priced in it.
Picking a 1.x data folder only looked at <root>/sessions, so per-project
sessions, sessions-v4 and desktop-sessions-v5/by-id imported nothing and the
UI reported no old sessions.
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).
cli-channels and cli-pointer both waited only for cli-tag, so the cask could be
pushed while the v<version> release holding its archives was still being
created, and stayed pointing at nothing if cli-pointer then failed. The cask
step also overwrote the tap whatever its version, so a workflow_dispatch of an
older tag moved Homebrew backwards; it now skips a tap that is ahead and fails
closed when the tap's version cannot be read.
Leaf package internal/contract/chartspec: spec types for bar/line/pie, a
strict Parse (size cap first, unknown fields refused, scalar cells only,
unsafe integers refused), typed refusals with dotted codes and caps, and a
deterministic Summary that quotes cell data. No consumer yet (#11564).
EditConn titled every outcome "not saved" at warn level, so a write that
landed but was not applied to the live session read as a failure, and a real
failure read as a soft notice. Split by outcome: error alert for a refused
save, status note for saved-not-applied, own title for a catalog refresh.
The archives that reasonix upgrade, the Homebrew cask and the GitHub release
install were linked with main.version only, so they reported no git_commit or
build_time_utc and the embedded product docs identified themselves as dev. The
npm packages of the same release were linked with all of them.
CostQuote carries a rate band for a rate confirmed as the vendor's own
schedule, aggregates fold it to peak, off-peak or mixed (none when any call
has no schedule), and the TUI prints it beside the cost as 1.x does.
A headless run in a folder with no trust decision refused every edit and
command and exited 0, so scripts that relied on 1.x defaults looked
successful. It now exits 4 (3 under --fail-on-unverified); the gate and
what it allows are unchanged. docs/MIGRATING.md gains the 1.x differences:
trust prompt, untrusted-folder runs, compact-ratio range, env vars.
Refs #11493
The panel dropped a list as soon as every item was completed, so the final
N/N never showed. It now stays through the ask panel and the idle prompt and
goes when the next turn begins; a rebuilt screen (resume, rewind) still does
not draw a list that finished before it was opened.
Adds a typed Commits port on the controller: ProposeCommit reads the staged
index through gitcmd, frozen as a tree object, and asks the session's model
for a conventional-commit message in a request of its own (prefix untouched,
billed as commit-message). CommitStaged writes the reviewed tree with
commit-tree and moves HEAD only if it has not moved, so an index that changes
after confirmation cannot enter the commit. Files named as secret holders or
whose added lines carry a private key are withheld from the model and need an
acknowledgement. Hooks and signing never run. Nothing pushes.
Refs #11518
- Add the pre-5.3 GLM catalogs to shippedPresets so installs holding the
old list are moved forward, plus upgrade tests (old shape upgrades, a
curated catalog stays untouched).
- Declare the per-model ladder once in provider.ZhipuEffortContract and
read it from the /effort menu, the normalizer and the request builder,
instead of repeating the model lists across three switches.
- Cite Zhipu's Core Parameters reference for the documented levels
(5.2's none/minimal/low/medium/xhigh folds, 5.3's forced thinking).
- Add a boot.Build effect test at the provider-request boundary covering
the stored enabled/disabled -> EffortDisplay -> request path.
- Surface the disabled->low landing for GLM-5.3 in the /effort menu and
document the 1M context window the added models reuse.
CodeQL's go/command-injection models regexp matches as barriers only when the
match is the branch condition dominating each use. screen() folded the match
into an || condition and the -C directory skipped it, so alert 1014 kept its
flows through screen() and Args.
- keep typed field details for days and explicit ranges
- reject from/to combined with days
- treat explicit dates as zone-less calendar days
- relocate and extend usage range tests
rebuildFailed collapsed the busy refusal into runtime.rebuild_failed with
the English sentence as detail, so the compaction, shell, sandbox,
permission, browser and MCP-load saves told a zh reader the write had
failed. It now answers runtime.saved_while_running, which the UI renders.
A bare https://host passed the add-provider probe because {base}/v1/models
answered, while chat posts to the saved base + /chat/completions and failed.
The probe now reports the base that was proven (typed address completed with
/v1 only when the listing answered there alone, openai-style kinds only), and
add, onboarding and edit-refresh adopt it into the address field before save.
F5 and Ctrl/Cmd+R reloaded the whole Studio UI from any focus, so a page
or browser-pane user pressing them lost the window state. The app window
now reloads only on Ctrl/Cmd+Shift+R (white-screen recovery, crash reload
unchanged); the browser pane reloads its own page on F5 and Ctrl/Cmd+R and
ignores the cache on the shifted chord.
POST /model persisted every switch as the user's default model, so changing
the model inside one conversation silently changed what every new session and
the next launch started on. The typed /model command, the terminal UI and ACP
never did this; only the HTTP picker path did.
The request now carries an explicit `default` flag. The Settings default-model
row and the first-run model choice send it; the composer picker does not, so
it changes only that session. A pane resolving through the hub's resolver
still never writes this machine's config.
One structural rule (secrets.RedactEndpoint / CredentialKey / RedactArgs)
replaces mcpsetup.RedactURL's query-only masking. Userinfo and fragments are
dropped or masked, query and fragment keys are decoded before classification,
unparseable endpoints render as <redacted>, and transport diagnostics keep
error identity while omitting peer-supplied prose.
Operational values (install, approval fingerprints, persistence) are untouched;
preview and plan responses add separate display fields that the desktop mirror
and confirmation UI render.
Rebased onto the launcher-aware NameFromArgv: names derive from the original
argv but never from a value the display projection masked, so build-flag values
such as `go run -exec` still resolve. Install classification reads the typed
HTTP status instead of sniffing the server name out of the message.
Fixes#11785
The primary button is focused from a passive effect that runs after the
element is committed, so asserting document.activeElement right after a
findBy* resolves races that effect. Use waitFor, as settings-layers does.
testenv.TempDir retries RemoveAll for two seconds when a file stays held.
On Windows the usage and history SQLite catalogs, which the shared helpers
left open, held a file under nearly every test's directory, so each of those
tests paid the full retry budget at teardown. Closing the catalogs from the
helper's own cleanup, which runs before the removal, makes it converge on
the first attempt.
A successful Windows bash probe is remembered per executable identity (path, size, mtime) for the life of the process; failures are never remembered. Every boot.Build used to spawn Git bash for it.
agent.Options.WorkspaceScanLimit (zero keeps the 50,000 production bound) is threaded from boot.Options. The incomplete-prose-scan effect test now sets it to 64 and writes 65 files instead of 50,001.
The deadline starts at launch, so with a real interpreter the test measured startup time. Under load Git bash needed more than 500 ms and the pre-deadline write never landed.
TestRememberPermissionRuleSerializesConcurrentWriters queued 32 writers on a
lock that waits 5s, so a slow Windows filesystem ran the last waiters out of
time (lock timeout at 5.08s). The cross-process variant had the same shape:
each helper waited a fixed 5s for the others to spawn. Eight writers still
interleave, and the readiness waits now read the shared hang budget.
The deadline is armed at launch, so a 500ms budget races interpreter startup:
on a loaded Windows runner PowerShell had not printed anything when it
fired, and the test reported the pre-deadline write as lost. PowerShell now
gets a 10s deadline named for what it has to outlast; POSIX shells keep 500ms.
Hang-catching waits in the control tests were hard-coded to 2, 3, 5 or 10
seconds. A whole turn through the real agent loop can exceed that on a
loaded Windows runner, so TestTurnOutcomeClassifiesPerseverationPause failed
with 'timed out waiting for TurnDone' on a run that passed on rerun. They now
read testenv.Budget(t), which is the single named bound for a wait that only
exists to report a hang.
cloudPresence kept a monotonic counter, so every reconnect of the same phone
took the next number. Allocate the lowest ordinal not held by a live
connection instead; it stays fixed for the connection's lifetime.
The wide-workspace effect test compared total goroutine growth against a
magic 64, which host background work (sqlite watchers in the history
catalog) pushed past under load. Count only goroutines inside the walkers
and bound them by pool size times concurrent walk kinds; assert none
survive the turn.
A fold that was refused or failed ended with an aborted card reading
"folded nothing" and a blocked receipt whose code was empty; the cause lived
only in a sentence. Checkpoint rejections now carry a code per rule, summary
failures are classified by sentinel (truncated, timeout, input too large,
context changed, provider failure), and the code rides both the receipt and
the aborted compaction frame. The Studio card shows a localized cause, and
keeps its before/after token pair on one line when the row wraps.
The Windows runner's temp dir is an 8.3 short name. The grant target for an
ambiguous path is resolved to the long form, so the expectation built from the
unresolved root never matched.
The regression held row and button references across the refetch that
PackageList.submit now triggers after a successful review submission, and
its deferred submitMarket fixtures never updated the inventory myMarket
returns. Query live rows after each reload and delegate to the mock's real
submitMarket so the reloaded inventory reflects the new status.
A tilde inside a Windows 8.3 short-name segment (RUNNER~1) of a drive-letter
path is an ordinary character; any other tilde can expand to a home
directory and stays refused as non-literal. Unescaped braces are refused
too, since a brace word expands to several targets.
Also give the PowerShell delete-analysis subprocess test a hang guard
instead of a 15 s latency assertion that fails on loaded runners.
The reference-solution guard fired only for the train corpus. For every other corpus a missing solution/ was a silent skip, so a grader that can never pass stayed indistinguishable from a task with no solution - the case the rule above it exists to prevent. #11380 supplied the ten vs-pos solutions; this extends the guard to the whole verification-stress corpus and supplies the ten tasks it was still skipping.
The e2e suite keeps the skip, and the skip message now says why: its handwritten tasks do not commit a reference solution.
Verified: go test ./cmd/e2ebench -run TestCorpusGradersPassTheReferenceSolution is green across e2e, verification-stress and train. Removing any single solution/ fails the guard at corpus_test.go:254 (checked by hand for vs-neu-cfgkey), so the added solutions are what the guard grades.
A relay that rejected the continuation field with a message the client did not recognise surfaced the HTTP 400 to the user, and the stored response id was kept so the next turn sent the same rejected request. Any HTTP 400 on a request that carried previous_response_id now retries once with the full history; a completed recovery stops sending the field for the client's lifetime, a failed one surfaces its own error naming the attempt.
Revision after the security review: ordinary commands with dynamic executable names, non-recursive deletes and non-delete wrappers launch again; each refusal carries its own cause and remedy; bounded compound sequences with literal targets inside the granted roots are allowed; default-parameter, pipeline, alias, .NET, find, xargs, cmd and static interpreter payload deletes are judged; symlink components are resolved before dot-dot cleaning; the session temp root is a granted scope; the PowerShell parser is reused across calls; bilingual copy for the new codes.
Doctor now validates declared agent roots fatally, which the warning test added by the root-diagnostics change did not expect, so the two merged changes failed together. The warnings are printed first and the root rejection keeps its exit status.
Approval alone did not stop a recursive delete: in unattended Auto and YOLO a PowerShell sequence whose first assignment failed non-terminally went on to delete the real home directory. A pre-launch gate now parses the command (bash AST, PowerShell parser run as data) and refuses recursive deletes whose target is the home directory, a drive or filesystem root, a workspace ancestor, an expansion, variable or glob, or outside the workspace and granted roots, with a typed shell.destructive_target code; parser failure is shell.analysis_unknown. The gate runs after approval and before every launch route.
A write to a markdown or text file in a workspace with no declared or captured checks still added a stale_verification debt and, in Balanced, a readiness floor demanding a verification command. When every proven mutation is a supporting-role path and nothing declares or captured a check, neither applies. Mixed, opaque, unproven or partially observed effects and declared checks keep their behaviour.
The sidebar row and the account panel disagreed when a token was present but the identity lookup failed: the row showed a signed-in state with a question-mark avatar while the panel drew the sign-in form and hid the error. Both now render the signed-in state, the identity error, a retry and confirmed sign-out.
The record panel was rendered after the whole active-turns list, so with a few
hundred rows it sat thousands of pixels below the click. It now renders inside
the clicked row's list item (or under the signal track for a signal), the same
item stays mounted, and clicking again closes it.
The session filter row gave four equal cells to labels with counts, so at the
old 176px floor the labels collided; the new-session and search rows wrapped
around their shortcut hints. Raise the column floor to 232px, let the filter
cells size to their labels, ellipsize labels, and drop counts and shortcut
hints below a container width.
The session and project menus wrote clientX/clientY into left/top, but the
interface scale is CSS zoom, so a fixed element's left/top is multiplied by the
zoom on its way in. Both now go through pinToViewport, which divides by the
zoom and clamps the measured box inside the viewport. The session menu's
max-height is divided by the zoom as well.
The official DeepSeek entry's model list migrates the retired
deepseek-v4-flash name to deepseek-flash, and qualified refs move with it,
but a bare default_model = "deepseek-v4-flash" (the shape a shared 1.x/Studio
config carries) was left behind. It then resolved to nothing, and because an
unnamed boot preserves an unknown default for its error, the Studio host's
only pane failed to build and the host exited 1 before its handshake.
- config: migrate a bare retired flash ref (default_model and every other
model-ref field) to the official entry's successor, unless another
provider still serves that name.
- boot: a new OpenOnFallbackModel option, set only by the Studio host and
hub panes, lets an unnamed build pass over an unknown non-plugin default
like a keyless one, open on the first configured chat model, and report
default_model_unavailable. The config file is not written. run, the TUI
and every other build keep the actionable unknown-model error and never
move to another provider on their own.
- serve: removing the provider in use moves the conversation to the startup
model when the saved default names nothing, instead of answering 500
after the removal; session titles follow the same startup model.
A background sub-agent keeps emitting reasonix.subagent.* progress after the
dispatching call has returned its job id. Folding those frames into the call's
card re-opened it, renamed it and its execution, and nothing closed it again.
Progress now lands in per-call phase state beside the cards; the Subagents
panel reads a child as running while its call is open or its last phase is not
terminal.
Take back cancelled the entry and dropped its text, so the label promised a
retrieval nothing performed. The body is now read before the entry is given
up and appended to the composer, under whatever is already typed. A line whose
body cannot be read stays queued rather than being lost.
The header chip counted client-side pending rows, which a delivery reported
before its HTTP receipt left pending forever, so it named a line the queue
panel (the only place to act on one) no longer showed. It now counts the
kernel's waiting guidance through the same predicate the panel filters by,
and a receipt that arrives after its delivery no longer re-opens the line.
The terminal UI opens a Configure connection panel when the session owes a
key, and /setup or /auth opens it at any time. The key is tested without
being stored, saved to the credential store only, and applied to the running
session when it belongs to the active connection; config.toml is untouched.
The Studio terminal UI answers the 1.x commands that read or steer the
kernel's own state, with 1.x argument shapes and output. Queue verbs and
steer go through the existing inbox routes, so the controller still owns
admission and ordering. /export is a controller capability: the file is
created exclusively and never through a symlink. /takeover resumes the
target without taking a session from another process.
foldTool looked for a call's parent only among top-level cards, so a
worker's own calls, whose parent is the per-worker task card nested in the
fleet group, fell through to the main list.
Pastes of five lines or a thousand characters become [Pasted text #N · M lines],
counted whatever row ending the terminal uses, and expand on send into the
labelled Begin/End block 1.x sends. Numbering continues past labels a resumed
session already holds, and panels receive pastes unfolded.
The footer's data row now leads with workspace@branch and the +added -removed ?untracked
counts from a new GET /workspace/git, read at start and after each turn. The compaction
meter label is back to the bare COMPACT word.
The one-time confirmation gate is gone: YOLO is always a stop on the cycle and a
single Ctrl+Y enters it, as in the 1.x terminal UI. --yolo on the command line
keeps its one-time notice, and the sandbox still applies in every mode.
On a fresh HTTP/2 connection, api.deepseek.com's edge acknowledges an eagerly
sent request body (about 8 KB or more) with WINDOW_UPDATE before its own
SETTINGS frame. RFC 9113 §3.4 requires SETTINGS first, so Go's client aborts
with "received WindowUpdateFrame before a SETTINGS frame" and the turn fails
with connection error PROTOCOL_ERROR. Studio sends a ~30 KB body on every
turn, so every request fails on affected networks.
Send Expect: 100-continue on chat requests to the official DeepSeek host.
The transport then holds the body until the server answers, by which point
its preface has arrived. HTTP/2 stays negotiated and request bytes are
unchanged; other providers are unaffected.
Moving the header setup into chat_headers.go keeps openai.go within its
file-size budget.
Refs #11679
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Next used the first still-open question, so after going back it skipped answered questions, and with two questions it vanished in favour of Confirm. Stepping is now positional; only the last question looks for an open one.
The workspace label in the sidebar footer, the composer's model name and the
workbench file name paired overflow clipping with line-height 1, so a UI font
whose ascent and descent exceed 1em lost the tops and bottoms of its glyphs.
They now use line-height 1.5; the rows centre their children, so nothing moves.
A style contract fails any new rule that clips a line tighter than 1.2.
The rail items re-declare the font shorthand, which resets
font-variant-numeric, so the digits were proportional and each item changed
width with its value. Restate tabular-nums in those rules and reserve the
width of a three-digit speed.
A submit that gets an answer without a recognised service code (an uncoded
5xx or an undecodable success body) is retried once with the existing
backoff, keeping the idempotency key. Coded refusals, refused redirects and
replies are never retried. The failure message now says the typed text is
kept, to try again shortly, and points at the GitHub alternative.
The source-name field intentionally hides validity errors while an IME candidate is still composing. The Add button did not share that state, so a completed form could submit a partial non-ASCII candidate before composition ended. Hold the button while composing and cover the name boundaries at the UI and serve boundary.
Probe host capabilities before opening a native picker. Headless and remote browser clients now enter a server-side path and use the existing /tree/workspaces API, while the desktop shell keeps its native dialog.
Archive a folded legacy recovery row together with its siblings, keep siblings open in a pane active, and restore sidecars if a multi-file update fails. Refs #11581.
The context-budget notice reported "room remaining" and, at the second
rung, told the model to land what it knows now. It never said that the
fold is automatic and the same task goes on, so a model near the trigger
read the figure as the end of its window, announced it would be
compacted, and handed the work back to the user (in-app feedback
FB-J2QR-HQ89).
Both rungs now state that the figure counts to the automatic compaction,
not to the end of the window, and carry one shared continuation clause:
keep working, do not stop, hand the work back or suggest a new session.
The notice still rides the turn tail and the typed ContextBudgetFigures
event is unchanged.
Two fixtures sat within a few tokens of the notice size and are widened.
Every runtime build ran ReconcileCleanupPending inline: a handful of stat and
read calls per transcript in the session directory. On a Windows box with 8000
sessions the sweep took 11 s of an 11.8 s build and 29 s at 20000 sessions
(about 1.4 ms per session, warm), and it sits before the host handshake, so a
cold start with a large history waits on it. The shell gave up at 20 s.
The Studio host, hub.Open, workspace switch and rebuild paths now hand boot a
reconciler that runs the sweep in a goroutine, once per directory at a time.
8000 sessions: build 11.8 s -> 1.0 s.
Refs #11434
Skipping an ask ends the turn through the same cancel as the stop button, so
guidance the user had queued into it was held uncertain and the queue paused.
The cancel now carries a typed cause: only the user declining a question
returns the undelivered item to the queue as an ordinary follow-up and lets
dispatch continue. Stop, errors and crashes keep the pause, and a stop that
lands after a skip still wins. The requeue only moves an accepted steer, so a
repeat delivers nothing twice.
Refs #11524
The add-source form only learned that a name such as a Chinese one is not
allowed after the kernel refused the save, and then showed the refusal under
the title "cannot connect". The name rule now shows inline as the name is
typed (with the allowed characters stated), the add button waits for a usable
name, coded name refusals from the kernel land on the field, and any other
failed save is titled as a save failure. The identifier rule is unchanged.
Reported through in-app feedback FB-WJ8B-C1XH.
An optional user-level [serve] share_port makes the direct link listen on a
chosen port instead of a random one. Unset keeps today's behaviour. The range is
1024-65535, a project reasonix.toml cannot set it, and the bind range is
unchanged. A taken port is a typed refusal (share.port_in_use). The settings
page saves it to the user config; changing it moves an open door and
phones re-pair.
Refs #11540
The onboarding stage only had a min-height, so it grew with its content and
never overflowed itself while the body clipped it. Once the connection result
was showing, the Start button sat below the window with nothing to scroll.
The stage is now the window's height and scrolls; the shell no longer shrinks
or acts as a scroll container. Adds a browser guard (perf/onboarding.mjs).
A model told a user that Reasonix has no hooks. The guide's hooks section only
covered SessionStart, and the bundled reasonix-guide skill had no settings
schema, exit-code convention or worked example.
A ```diff fence rendered through the colourised built-in path on every
frame, and a colourised row costs ~1.4 ms to lay out (chroma via
regexp2) against ~1.3 µs for a plain one. While a fence streams every
delta re-renders the whole growing body, so a multifile diff pegged a
core and the display crawled. Changes:
- Each "@@ " hunk header settles the rows before it, so render that
prefix and leave only the in-progress hunk on the plain rail: the diff
colours in as it streams instead of only at the closing fence. A
closed fence settles the whole body. The formatter runs on each
settled prefix too, so its output appears hunk by hunk.
- Keep the already-formatted rows on screen while a settled body's run
is pending: growing the body by one hunk changed its key and redrew
the whole section on the plain rail until the new run landed, so the
delta output flashed back to plain at every hunk. The renderer now
draws the largest settled prefix whose result has landed and leaves
only the newly settled remainder (and the in-progress hunk) on the
rail.
- Memoize the colourised rows per hunk (hunk, path, width, rail, theme).
A completed hunk never changes, so it is highlighted once however many
frames redraw it; caching the whole body instead would re-highlight
every earlier hunk at each "@@ " and is quadratic in the hunk count
(measured: 60 hunks, 79 KB took 40 s with a body memo, 2 s per-hunk).
The theme is in the key so a runtime theme switch cannot serve the old
palette's colours.
- A fence still with fewer than two hunks keeps the plain rail, and a
closed fence whose configured [cli].diff_formatter result is still
pending also draws the rail, since that output supersedes the built-in
rows anyway; a landed-but-failed run falls back to the built-in.
- Hold the transcript viewport still when the content shrinks: a settled
diff collapsing its raw preamble to one formatted line frees rows at
the bottom, and following them back up made the screen jump. The
position now holds, the freed rows stay blank, and later rows fill
them, so scrolling resumes only when the output actually grows past
what is shown. The hold has only a lower bound, so a shrink larger
than the viewport would hold the position past the content and blank
the whole transcript — a collapsing multifile fence, or /clear
restarting over a shorter one — so the view re-anchors to its tail
instead. A manual scroll or resize releases the hold.
- Coalesce the stream frames the TUI hands the model: waitUpdate drains
everything already queued into one updateMsg, so a burst of deltas
costs one render instead of one per delta. Order is kept and a Gap
still triggers a history reload.
- Fix the file-header misdetection: a removed SQL "-- x" line renders
"--- x", which trimDiffPreamble/diffSectionStart mistook for a
"--- "/"+++ " file header, dropping the "@@" hunk header (line numbers
reset to 0) or the rows themselves when a removed "--- x" sat before
an added "+++ y". A pair is a file header only when a "@@ " hunk
follows it, and countDiff no longer skips "--- "/"+++ " rows.
- Keep a streaming multi-file diff's next file header on the plain rail:
splitAtLastHunk settled everything before the last "@@ " header, which
included the next file's "--- "/"+++ " pair (and its git preamble) once
that file's first hunk header had arrived but no hunk of it had
completed. isFileHeaderPair then saw no "@@ " after the pair, so the
settled renderer read it as a removed/added row of the previous file's
last hunk and counted it in that file's stat. A trailing dangling file
header now moves from the settled part into the tail until its "@@ "
arrives.
Tests: the multi-file cases TestSplitAtLastHunk was missing and an
end-to-end streamed test; a theme-switch test pinning the per-hunk memo's
theme key; and a benchmark replaying a 4-file x 15-hunk stream, redrawn
per streamed line.
fix(cli): highlight a streaming diff fence's hunk once
splitDiffSections appends a newline to every section, so splitHunks
returns a section's last hunk with a trailing newline and every earlier
hunk without. hunkRowsCached keyed the memo on the raw hunk, so one hunk
carried two keys over a stream: it was highlighted once while it was its
section's last hunk and again once a following hunk arrived. Every hunk
but a file's last was therefore highlighted twice, at ~1.4 ms a row.
Trim the trailing newline before keying. diffBody already trims, so the
rendered rows are unchanged; the streaming benchmark over a 4-file,
30-hunk, 1816-line diff drops from 7.8 s to 5.6 s.
TestStreamingDiffHunkMemoisedOnce pins one memo entry per hunk: it sees
three entries for two hunks before this change, two after.
Fold the writer-tool preview before highlighting it too. diffBody laid
out every row and then dropped all but maxLines, so a write_file card's
~24-row preview colourised the whole diff: ~772 ms per repaint for a
199-line file, paid on every message while the call runs. Counting the
fold from the source and laying out only the kept rows prices a
2000-line diff like a 24-line one.
BenchmarkDiffBlockPreview prices both shapes through a diffPreviewFold
seam, which TestDiffBodyFoldSeamSameRows proves draws the same rows: a
2000-line preview is 6.3 s and 86 MB with the whole body laid out, 71 ms
and 1.0 MB folded, and flat from 24 lines up.
docs(cli): correct diff-fence comment coherence
Four comments described behaviour the code does not have:
- diffRowCount's doc counted one row per non-blank line, but diffBody draws no
row for a "@@ " header, so it over-counted by the hunk count.
- renderCodeRail's doc claimed the in-progress hunk and the pending-formatter
placeholder, both drawn by renderRailText, not here.
- splitDiffSections' doc named only the fence path, omitting DiffText, and
described the plain-rail fallback where DiffText drops a headerless section.
- the file header claimed md.go still sits at the 800-line ceiling.
Reword each to the constraint that holds now.
A held queue item showed the kernel's English diagnostic (for example
"steer accepted but unapplied before turn exit") in a Chinese UI. Items now
also carry a stable BlockCode and the desktop words it in the UI language;
unknown codes fall back to the diagnostic.
A real failure that raced the stop button was reported as a cancellation and
hidden. TurnDone.Cancelled now requires Err to be nil or context.Canceled. The
desktop also reads "Cancelled" instead of "Interrupted" after the user's own
cancel.
Refs #11524
Settings > Storage moved sessions and projects but left other top-level state
behind while the code reads only the new root: the remembered project list
(serve-workspaces.json), user-level REASONIX/AGENTS/CLAUDE instructions,
memory/global, schedules, trusted seals, machine-id.key and the MCP migration
marker. All are now owned entries, so a move carries them.
Installs already moved get them back at boot: each entry is merged once without
overwriting and the old copy is renamed *.adopted. The project list is merged
under its lock and written atomically by a new leaf package shared with serve,
and file copies publish whole files. A guard test scans the tree for paths
joined under the state root so a future entry cannot be forgotten.
Both host notices reached every frontend as kernel English, so a Chinese UI
showed an English card. The context-budget notice now carries a code and a JSON
figures payload, the unapplied-guidance notice carries the user's text as
Detail, and the desktop card and the TUI word them from those in the UI
language. Unknown codes and undecodable payloads still fall back to the
kernel's text.
Refs #11524
Skipping an ask (or stopping a turn) ends it with the kernel's Cancelled flag
set and context.Canceled as Err. The transcript appended Err as an error card
regardless of the flag, so the user's own choice read as "context canceled".
The flag is the identity; Err is not drawn when it is set. An effect test pins
the kernel side through boot.Build.
Refs #11524
resolveWorkspace only knew a leading slash as rooted, so a drive-letter path such as C:\Users\u\x (what the remote tree reports as a project root) was appended to home and the serve never started. Rootedness is now asked of the target machine.
Headless `run` must leave stderr empty unless --debug is passed, so the
boot record of the resolved shell interpreter cannot be an Info log. It is a
diagnostic; the always-on counterpart is the Warn shellrun emits when a
launch actually fails.
Trim repolint comment blocks and move launch-failure logging out of RunForeground so the function stays within the 120-line limit. No behavior change.
Signed-off-by: Harbor404 <[email protected]>
A pinned [tools.shell] path was honoured on existence alone, so a pwsh.exe
that stats fine but will not start was held for the whole session while every
command failed at launch — the failure #10633 reported, still reachable
through the pinned-path branch after #10617 reworked auto-detection.
- Prove a pinned PowerShell the way the bash arm already proves its own, and
fall back to detection with a warning when it will not start.
- VerifyShell proves a pinned PowerShell too, so the settings screen refuses
one where it was typed rather than on every later command.
- powerShellLaunches answers false for a path that is not there: a missing
executable is not launchable, so callers that gate on it alone now agree.
- resolveShell takes the launch probe as an argument, which puts the decision
table ("pwsh will not start, 5.1 will") under test on any host.
- Detection that finds nothing says so instead of silently returning bare bash.
- Boot records the interpreter the session resolved to, and a launch-phase
spawn failure records the shell, its path, the caller and the error. The
desktop relays the sidecar's stderr into desktop-shell/logs/, which the
report found empty.
Windows end-to-end is not verified locally; the fallback is covered by the
injected probe on Linux and macOS.
The hand-written Edit menu dropped Paste and Match Style, Delete,
Substitutions and Speech; restore them with the same roles (structure checked
against the role-based menu in a real Electron). The menu factory is now
Electron-free and unit-tested, the language follows the first preferred system
language like the page, and the redundant second menu install is gone.
Refs #11524
The project menu offered only sort and remove-from-list. Add a reveal item that
asks the hub where a listed project lives (GET /host/workspaces/locate, window
only, refuses any folder the window does not list) and has the shell select it,
so it needs no open pane. Remote hosts draw in their own rail and never reach
this menu; a project whose folder is gone shows the item disabled with the
reason.
Refs #11524
Role-only menu items take their text from Electron's built-in English
catalogue even when the app locale is zh-CN (verified on a real Electron:
getLocale() == zh-CN, labels Undo/Cut/Copy/Paste/Select All). Keep the roles
for the clipboard work and give each item an explicit zh/en label chosen by the
page's saved language (rx-lang), falling back to the machine locale exactly as
the page does. The macOS Edit menu is rebuilt when the language preference
changes.
Refs #11524
Registers tool.show-all, agent.open, agent.close and agent.focus-trap, moves
focus into the overlay and back to the deck chip, caps the overlay's rendered
output at 200000 characters with a note, keeps the sub-agent count stable, wraps
nested output, and drops a stray .agents rule that changed the sidebar spacing.
Headless `run` must leave stderr empty unless --debug is passed, so the
boot record of the resolved shell interpreter cannot be an Info log. It is a
diagnostic; the always-on counterpart is the Warn shellrun emits when a
launch actually fails.
The sub-agent list stays after delegates finish and each row opens that
sub-agent's whole transcript. A nested call whose output exceeds the 400
character preview now says so with a show-all control instead of cutting.
Trim repolint comment blocks and move launch-failure logging out of RunForeground so the function stays within the 120-line limit. No behavior change.
Signed-off-by: Harbor404 <[email protected]>
Project configuration could replace a user server entry and drop disabled_tools, while a stale call to a filtered tool was reported as unknown. Merge user and project disables, reject empty names, and carry a typed refusal through boot and hot-added MCP registration.
Move an existing contextual-tool helper unchanged so execute_one.go does not grow past the file-size ratchet.
A pinned [tools.shell] path was honoured on existence alone, so a pwsh.exe
that stats fine but will not start was held for the whole session while every
command failed at launch — the failure #10633 reported, still reachable
through the pinned-path branch after #10617 reworked auto-detection.
- Prove a pinned PowerShell the way the bash arm already proves its own, and
fall back to detection with a warning when it will not start.
- VerifyShell proves a pinned PowerShell too, so the settings screen refuses
one where it was typed rather than on every later command.
- powerShellLaunches answers false for a path that is not there: a missing
executable is not launchable, so callers that gate on it alone now agree.
- resolveShell takes the launch probe as an argument, which puts the decision
table ("pwsh will not start, 5.1 will") under test on any host.
- Detection that finds nothing says so instead of silently returning bare bash.
- Boot records the interpreter the session resolved to, and a launch-phase
spawn failure records the shell, its path, the caller and the error. The
desktop relays the sidecar's stderr into desktop-shell/logs/, which the
report found empty.
Windows end-to-end is not verified locally; the fallback is covered by the
injected probe on Linux and macOS.
A run in a folder with no trust decision refused every edit and command
under the asking default and exited 0 with an empty stderr (plain text
run) and only the generic 'no interactive approver' reason for the model.
The refusal now carries permission.untrusted_folder. The model reads the
cause and the remedy, the text run prints refused tools, code and remedy on
stderr, the structured result's denial carries a remedy, and
--fail-on-unverified exits 3 for it. Exit 0 and the trust gate itself are
unchanged.
Refs #11493
The shell gave the kernel 20 s to handshake and reported every failure as a
sentence. The wait is now 60 s (REASONIX_STUDIO_HANDSHAKE_TIMEOUT_MS), a small
starting window appears after 3 s, an exit or spawn failure within 10 s is
retried once, and launch failures carry a typed code (timeout, exited, spawn,
overflow) with a matching explanation in the error box.
Refs #11434
Answering the last question of a multi-question ask with nothing left
unanswered commits the batch immediately, skipping the extra confirmation
Enter; an incomplete batch jumps to the first unanswered question instead. The
behaviour is gated on a new top-level `auto_submit` option (default false):
with it on the ask drops its Submit tab, and without it the Submit tab is shown
and still needs its explicit Enter.
`auto_submit` is a user preference: LoadForRoot holds it across the project
merge so a cloned repository cannot turn it on, and neither the annotated
project render nor the project delta carries it. Answering moves on to the next
unanswered question, so answering one the panel was thrown back to skips ahead
to the next gap rather than stopping on the already-answered question that
follows it; only the last question, with nothing left unanswered, commits. With
auto-submit on there is no Submit tab to page onto, so right stops at the last
question rather than landing on a hidden tab that commits.
- config: new top-level auto_submit, user/global only; rendered in the user's annotated TOML and omitted from a project render and the project delta.
- tui: Options.AutoSubmit; nextQuestion advances to the next unanswered question and commits on the last; right stops at the last question instead of a hidden Submit tab.
- program_test.go: the commit, next-unanswered-after-skip-back, skipped-question, hidden-tab, no-option and arrow-stops-at-last cases.
- docs: auto_submit in CONFIG_PATHS.md, reasonix.example.toml, and the Guide shortcut tables (en and zh-CN).
Move the providerSourceScope type out of config.go and the [ui] table render
out of render.go into their own files, so both sit under their recorded
repolint budgets with room for the options that follow.
My feedback shows the thread under each report as plain-text bubbles, a
needs-your-input chip with an inline reply box (byte counter, errors per code),
status chips for answered and closed, and a New mark that stays for the visit
in which the replies were read. The sidebar entry and the command palette item
carry an unread count that is read when the app opens and when the page opens,
never on a timer. The form copy says a report becomes public only if it is
filed as an issue, and keeps the warning about secrets and screenshots.
Refs #11424
The kernel reads the new statuses (needs_info, answered, closed), each report's
thread and needsInput, keeps the last-seen reply id per report locally and
exposes unread/hasNew for an entry badge. A reply goes through the controller
port, serve (POST /feedback/{receipt}/reply and /seen) and the TUI
(/feedback show, /feedback reply with --yes as the first token). The new
worker codes become sentinels: feedback.reply_limit, feedback.not_replyable
and feedback.challenge_required. A reply is never retried. Thread text is
cleaned of escape sequences before any frontend sees it. Submit copy no longer
says every report becomes public.
Refs #11424
The worker now holds new submissions by default. The converter still
creates issues only for received items, links only images the worker marks
released, and posts one idempotent bot comment per reporter reply on items
that have an issue, then acks it. Contact is never read by the converter,
which a test pins.
Refs #11424
A browser cannot read the status of a refused WebSocket upgrade, so a
rate limit, a refused origin, an expired ticket, a network loss and an
unreachable gateway all read as "relay temporarily unavailable". When the
socket errors before opening, one probe round (offline check, the gateway
health route, and an attachment preflight that only an allowed origin passes)
now classifies the failure by typed code; a 429 from the account service
carries its Retry-After. The notice names the cause, offers reconnect
(counting down a rate limit), and a reconnect that runs out of retries ends
with the last attempt's cause, or idle when the relay had reclaimed the
lease. The generic sentence remains only when the probe itself fails.
Adds internal/platform/feedback: env collection, local redaction (the
crashreport implementation, now exported), image re-encode with a metadata
self-check, idempotent submit with retry, persisted install identity and
receipts, and GET /v1/feedback/mine with typed sentinel errors.
The controller exposes it as the Feedback port (SessionAPI), serve answers
GET /feedback/env, GET /feedback/mine, POST /feedback and POST /feedback/name
with dotted refusal codes, and /feedback [list|name|<category> --yes <text>]
is a controller slash command so the TUI inherits it. Nothing reaches a model
request or the cache-stable prefix.
Refs #11424
Adds a scheduled converter (scripts/feedback-sync.mjs) that turns pending
Studio feedback into issues, idempotently via a hidden marker, and reports
in_progress/fixed/wontfix/duplicate back to the worker. A fix recorded as
"next" is upgraded to its studio-v tag once the tag exists.
exec.LookPath returns only the first bash.exe on PATH, which is normally the
WSL launcher in System32, and the fallback list covered only the default
Program Files roots. A Git for Windows install elsewhere (with just its cmd
directory on PATH) was therefore never detected, so Studio offered PowerShell
only. Candidates now also come from every PATH directory and from the Git
tree the git.exe on PATH belongs to, and the WSL launcher is rejected in the
candidate loops too.
Fixes#11421
Screened 87 candidates, staged: one pass over the pool, then two more only for
the candidates that were clean on both boundary arms. The six that held on both
arms in all three passes stay, the rest are dropped, so the efficiency substrate
only contains tasks that qualify:
quarter i62-epochlatch, i37-dispatchlag
half i106-budgetseal, i70-ingestfloor
default i47-tenantlease, i51-routedelay
All six are SnippetStop, SnippetThenRead or DefensiveRead in each of their
eighteen judged cells - nothing post-sufficient.
What the screening found: the answer kind decides whether a candidate holds.
Half-tier candidates whose answer was a bare number never survived (0 of 8);
the same tier with a codename answer survived 11 of 20. The dirty class in both
cases was PostSufficientRetrieval - the model re-checking a value it already had
- so the shape that needs no checking is the one that holds.
Also: i07 and i10 were re-measured after the review asked for it (their old
numbers came from the grep shape they no longer use), NeverSufficient is
reported separately rather than counted as clean, and the i11/i12 rows are gone
because those tasks are not in the corpus.
The host took its working directory as the first pane's project. A Start menu
launch inherits the install directory and a Finder launch the filesystem root,
so the app's folder was listed in the sidebar again after every removal. The
directory now counts only when it is outside the app's own directory and not a
filesystem root; otherwise the first remembered folder that still exists is
opened, then the home directory.
Fixes#11418
The provider form only edited one window and one output cap for every model
on a connection. model_overrides already carried both per model and the
kernel resolves them for requests and compaction, so the edit endpoint and
the form now read and write them.
A model with no entry inherits the connection's value, shown as a grey
placeholder; a blank field is never sent as an override, and clearing one
removes the entry.
A cli-pointer job publishes the Studio release's CLI archives as a GitHub
release vX.Y.Z (the only shape the 1.x updater accepts), records it
immutably in R2, then advances cli/{stable,preview}/latest.json. It reuses
the 1.x manifest validators and pointer ordering, only moves forward, and
is gated by STUDIO_PUBLISHES_CLI.
cli-gate requires CLI_PUBLISH_FROZEN so two lines never write the CLI
channels. cli-tag creates the vX.Y.Z tag with the release tag identity in
one inline step of a job that runs no repository code. An existing release
is trusted on its own checksums, never compared with a rebuild.
- The host creates the schedule store directory (0700) before a command's
sandbox is built, so the forbid-read mask exists for it on Linux and on macOS
paths that go through a symlink.
- Document in schedule/doc.go that the confirmed digest defends only against
sandboxed writers (Windows, sandbox off, yolo and MCP/plugin processes can
still write the store).
- Only the model's own usage report for an executor request counts toward
metering; estimates and side calls no longer let unmetered requests slip past.
- The job handle is released on the ready-refused, ready-timeout and cancelled
paths.
- A stored session path must be a .jsonl file in a sessions directory under the
state root.
- Tests: the report's untrusted mark, the stderr sanitising, the supervisor
watch that cancels and forces the process out, and bubblewrap tests for the
forbidden-directory mask. The child's own state check is dropped; Start
refuses a run that is not running.
Refs #11406
- A run whose provider reports no usage is stopped after two unreported
requests and charged the whole per-run cap instead of 0.
- The start of a run is one manifest transition guarded by a token that only
travels over the supervisor's pipe (its hash is what the manifest holds); the
child announces it holds the run's lease before it is released, and the
supervisor holds a supervision lock from before the spawn until the run is
settled, so the reaper never sees a gap.
- The read-only posture runs no config migration, so it rewrites no config file.
- Seatbelt denies writes as well as reads under forbid-read directories, as the
bubblewrap mask already did, so a blind overwrite of the schedule store fails.
- Windows refuses to run a child that a Job Object cannot hold; the job handle is
released; a child whose supervisor left is forced out after a grace period.
- The stored report is marked untrusted, the session path must lie under the
state root, the stderr tail is sanitized, the supervisor's backstops use the
policy minimum, the child reads the same user config file the loader does.
- Tests for the mutants the review found surviving; supervised runs are bounded
by a context so a missing kill fails instead of hanging.
Refs #11406
Adds the hidden `reasonix schedule exec <trigger-id>` child and a supervisor
that starts it, counts the tokens it reports and ends it, process tree and
all, when the wall clock or 1.25x the per-run token cap passes. A run that is
killed, crashes or loses its usage stream is charged the whole per-run cap.
The child holds an operating-system lease for the run, waits for the
supervisor's release line (so a run the store already settled spends nothing;
MarkRunning returning ErrRunSettled kills the child), reads its policy from the
user's configuration only, builds the read-only posture, and keeps reading its
stdin: the end of that stream is the supervisor going away, and the child then
stops and settles itself. A run that parks one kind of request too often
(max_repeat_parks, default 3, user level) is ended.
The final report and parked records are sanitized and bounded by the store on
the way in (PutResult). The schedule store directory joins the protected read
set of every run and the file tools' write guard; Grant.Tools narrows the read
tools of the posture.
Refs #11406
realPath no longer cleans a POSIX path lexically before resolving it, so a
'..' after a symlink is checked against the file the system opens. On Windows
it asks for the final path through a handle, which follows junctions and
settles short names, case and the extended and UNC spellings. read_file
re-checks the scope against the open file. LockSurface freezes SetTools and
SetExtensions once assembly has installed them; Sanitize now removes
zero-width, tag and line-separator characters and runs before a record reaches
any store; a workspace root is refused when the home directory is unknown.
Refs #11406
Read tools take readRoots, checked after symlinks resolve, with the typed
refusal workspace.read_outside_scope; walkers, glob matches and ripgrep
(--no-follow) honour it. The posture forces both secret-protection switches
on and refuses a workspace containing the home directory or filesystem root.
No hook loads under the posture (an empty non-nil scope list). Rebuilding an
observed controller without the posture is refused (ErrObserveRebuild) and
the executor's gate and asker are locked. Approval records keep the host's
text in Summary and the model's arguments in Detail, marked untrusted and
stripped of control and bidi characters. A run may park at most 32 records,
then it is ended (ErrParkLimit). A tool outside the ceiling is refused with
posture.tool_not_allowed instead of unknown tool.
Refs #11406
Adds a host-only Options.Observe that builds a run which can only read
files. The tool ceiling is decided by what each tool declares about its
reach (tool.Reach), enforced by a registry that refuses every other Add.
Approvals and ask questions are parked as Pending records and never
answered; the gate and asker cannot be replaced through the controller.
The workspace's reasonix.toml, .mcp.json, project hooks and folder grants
are not read, no MCP server or extension sidecar starts, and the run
context rides the turn tail as <scheduled-run>. The posture records
remote_content=false and whether an OS sandbox backs the tool filter.
Refs #11406
Slice 1 of the scheduled-runs P0 (#11406): a machine-wide manifest with atomic
writes and a schema version, a Policy that only the user layer can loosen,
pessimistic budget accounting, and claim-before-start with deterministic
trigger ids. No process is started and nothing is wired to a frontend.
The review settled the bar: a task qualifies only if it is clean on both of its
boundary arms, and none of the first six candidates is. This adds five more,
authored to the same shape but with a single value each and a call whose
arguments name the topic (./scripts/x {{cue}}), which is the shape the review
pointed at for i07 and i10.
- i15-ingest-lag and i16-commit-fence (quarter), i17-route-budget and
i18-salvage-epoch (half), i19-tenant-quota (default).
- All five preflight clean at their own tier's generation.
- The corpus stays unqualified until six candidates hold on both arms across a
three-pass batch; the ones that do not are dropped rather than promoted.
The title button in a memory row was flex:none, so a long title (or an
unbroken path/identifier) pushed the scope, date and action past the
container and made the Forget label break vertically. Let it shrink and
wrap within 60% of the row, and keep the trailing action on one line.
Withdraws the studio-cli-channels environment from #11395 at the owner's
decision: no human approval. The job reads the shared NPM_TOKEN and
HOMEBREW_TAP_TOKEN. The contract test and STUDIO_RELEASE.md follow.
2026-09-30 01:45:41 -07:00
1469 changed files with 95566 additions and 5464 deletions
echo "::error::environment studio-cli-channels has no required reviewers (or its protection rules were unreadable: '${reviewers}'); refusing to publish"
<a href="https://qm.qq.com/q/i59b0z2R8s"><img src="https://img.shields.io/badge/QQ%20group-1093562660-12B7F5.svg?style=flat-square&labelColor=161b22&logo=tencentqq&logoColor=white" alt="QQ group 1093562660"/></a>
</p>
<p align="center">
@@ -41,12 +44,16 @@
<br/>
<p align="center"><strong>Open source · MIT · a single Go binary</strong></p>
<h3 align="center">A coding agent you can leave running.</h3>
<h3 align="center">A reliable coding agent for complex software engineering tasks.</h3>
<p align="center">One local engine, four ways in — terminal, desktop app, browser, or your editor over ACP. Plan mode, permissions, a workspace sandbox and per-turn checkpoints keep a long autonomous run something you can still read and undo.</p>
<p align="center">Maintained by <strong>Huahui Yu</strong>.</p>
> [!IMPORTANT]
> **Community · 加入社区** — bilingual Discord for setup help (`#help` / `#求助`), workflow showcases, and feature ideas. → **<https://discord.gg/XF78rEME2D>**
>
> QQ group **DeepSeek-Reasonix官方群** (`1093562660`) → **<https://qm.qq.com/q/i59b0z2R8s>** ([QR code](./docs/assets/qq-group.svg))
> Douyin: **做游戏的小鱼** (Douyin ID `22703872788`) · [QR code](./docs/assets/douyin.png)
<br/>
@@ -265,8 +272,9 @@ coffee, not a contract — donations don't buy feature priority or change how
"The kernel started but did not finish starting in time. Security software scanning a new or updated install is a common cause; starting Studio again usually works.",
check("the edit menu carries every editing command",["undo","redo","cut","copy","paste","selectall"].every((r)=>editRoles.includes(r)),editRoles);
check("the edit menu carries every editing command",["undo","redo","cut","copy","paste","pasteandmatchstyle","delete","selectall"].every((r)=>editRoles.includes(r)),editRoles);
check(`${tag}${label}: after wheeling to the bottom the button is in the viewport`,!!box&&box.y>=0&&box.y+box.height<=viewport.height+0.5,JSON.stringify(box));
"同一段内容被逐字重复":"The same block is being repeated verbatim",
"任务已按设置暂停:模型卡在重复输出同一段文字":"Paused by your setting: the model is stuck repeating the same text",
"等待你的输入":"Waiting for you",
"引导没有生效:这一轮在处理它之前就结束了。如果仍然需要,请再发送一次:":"Guidance was not applied because the turn ended before it could be processed. Send it again if it is still needed:",
"费用显示币种已设为 {mode}":"Cost display currency set to {mode}",
"上下文已用到压缩阈值的 {percent}%,已告知模型约剩 {remaining} 个词元的空间。":"Context at {percent}% of the compaction threshold — the model was told it has about {remaining} tokens of room left.",
"模型卡在重复输出同一段文字,这一轮已停止;可以重试、补充引导,或换一个供应商/模型":"The model is stuck repeating the same text; this turn was stopped. Try again, add guidance, or switch provider/model.",
"遇到问题、想交流用法或想参与进来,可以加入社区;下面是为 Reasonix 提交过代码的人。":"Join the community for help, tips and ideas. Below are the people who have contributed code to Reasonix.",
"QQ 群二维码":"QQ group QR code",
"群号":"Group number",
"复制群号":"Copy group number",
"用 QQ 扫描二维码,或点击下方按钮加入。":"Scan the QR code with QQ, or use the button below to join.",
"加入 QQ 群":"Join the QQ group",
"贡献者":"Contributors",
"按提交数排序,名单随版本更新":"Sorted by commits; the list is refreshed with each release",
"Keep the receipt number: it identifies this report. We read it first, and only what we file as a GitHub issue becomes public. Progress, the issue link and any reply or question from us appear under My feedback.",
"你的文字里有看起来像密钥的内容,已在发出前打码。":"Text in your report that looked like a secret was masked before it was sent.",
"查看我的反馈":"View my feedback",
"再写一条":"Write another",
"只有被我们登记为议题的反馈才会公开":
"It becomes public only if we file it as an issue",
"If we file it as a GitHub issue, your text, nickname and screenshots are posted in the esengine/DeepSeek-Reasonix repository, where anyone can read them; if we do not, it stays private. You will see a receipt here and, once filed, the issue link, and we may reply or ask you questions here.",
"请不要在文字或截图里写入密钥、账号密码、聊天记录或私有代码:一旦公开就收不回来。":
"Do not put secrets, passwords, chats or private code in the text or in screenshots: once public it cannot be taken back.",
"发现的是安全漏洞?请不要在这里提交,{link}。":"Found a security vulnerability? Do not post it here; {link}.",
"按安全策略私下报告":"report it privately under the security policy",
"反馈内容":"Feedback details",
"反馈类别":"Feedback category",
"发生了什么?":"What happened?",
"描述你遇到的情况,或你希望它怎样。":"Describe what you ran into, or what you would like instead.",
"{n} / {max} 字节":"{n} / {max} bytes",
"昵称(登记为议题时公开)":"Nickname (public if filed as an issue)",
"如果登记为议题,会显示为「由 {name} 提交」。用昵称即可,不必真名。":
"If it is filed as an issue it shows as \"Reported by {name}\". A nickname is fine; no real name needed.",
"联系方式(可选,私密)":"Contact (optional, private)",
"邮箱或 QQ。只有维护者看得到,绝不会出现在 GitHub 上。":"Email or QQ. Only the maintainers see it; it is never posted on GitHub.",
"截图(可选,登记为议题时公开)":
"Screenshots (optional, public if filed as an issue)",
"If this report is filed as a public issue, your screenshots are published with your text. Before sending, check they show no keys, chats or private code.",
"{name}:只支持 PNG 或 JPEG":"{name}: only PNG or JPEG is supported",
"{name}:超过 {size} MB,没有添加":"{name}: larger than {size} MB, not added",
"{name}:最多 {n} 张,没有添加":"{name}: the limit is {n} images, not added",
"随反馈一起发送的环境信息":"Environment sent with your report",
"入口":"Surface",
"渠道":"Channel",
"只读。不包含密钥、地址或任何文件内容。":"Read-only. It contains no keys, addresses or file contents.",
"去 GitHub":"Go to GitHub",
"重试发送":"Retry sending",
"正文不能为空,请写下你遇到的情况。":"The text is empty. Please describe what happened.",
"正文太长了,请缩短后再发。":"The text is too long. Please shorten it and send again.",
"请填写昵称。":"Please enter a nickname.",
"昵称太长了,请缩短后再发。":"The nickname is too long. Please shorten it.",
"联系方式太长了,请缩短后再发。":"The contact is too long. Please shorten it.",
"请重新选择反馈类别。":"Please pick a category again.",
"截图数量超出上限,请去掉几张后再发。":"Too many screenshots. Remove a few and send again.",
"截图只支持 PNG 或 JPEG。":"Screenshots must be PNG or JPEG.",
"有截图太大了,请换小一点的图。":"A screenshot is too large. Use a smaller one.",
"有截图无法读取,请换一张图。":"A screenshot could not be read. Try another image.",
"内容不符合要求,请检查后重试。":"Something in the form was not accepted. Please check it and try again.",
"没能连上反馈服务。已填的内容都还在,网络恢复后可以直接重试,不会重复提交。":
"Could not reach the feedback service. Everything you entered is kept; retry once the network is back and it will not be filed twice.",
"这次的内容没能被解析,请检查后重试。":"The request could not be read. Please check the form and try again.",
"内容太大,服务端没有接收。请缩短文字,或去掉一张截图后再发。":"The report was too large for the service. Shorten the text or remove a screenshot, then send again.",
"提交得太频繁了,请等 {secs} 秒后再试。":"You are sending too fast. Try again in {secs} seconds.",
"提交得太频繁了,请稍等一会儿再试。":"You are sending too fast. Please wait a moment and try again.",
"相同内容刚刚已经提交过,不需要再发一次。可以在「我的反馈」里查看它。":"The same text was just sent, so there is no need to send it again. You can find it under My feedback.",
"反馈通道今天的接收量已满,不是你发得太多。内容都还在,请明天再试。":"The feedback channel has reached its capacity for today; you did not send too much. Your text is kept, so please try again tomorrow.",
"有一张截图没能清除其中的隐藏信息。请换一张,或重新导出后再添加。":"A screenshot could not be cleaned of its hidden metadata. Pick another one, or re-export it and add it again.",
"反馈通道暂时关闭。你可以直接到 GitHub 提交问题。":"The feedback channel is switched off for now. You can file an issue on GitHub directly.",
"反馈服务没有认出这台电脑,请稍后重试。":"The feedback service did not recognise this computer. Please try again later.",
"反馈服务暂时出了问题。已填的内容都还在,请稍等片刻再试,也可以直接到 GitHub 提交问题。":"The feedback service is having trouble. What you typed is kept. Try again in a moment, or file an issue on GitHub directly.",
"反馈服务暂时出了问题。你写的回复还在,请稍等片刻;重发前先刷新列表确认是否送达,也可以直接到 GitHub 提交问题。":"The feedback service is having trouble. Your reply is kept. Wait a moment, then refresh the list to check whether it arrived before resending, or file an issue on GitHub directly.",
"本机保存反馈记录时出错,请重试。":"Saving the feedback record on this machine failed. Please try again.",
"反馈没有发出去,请稍后重试。":"The feedback was not sent. Please try again later.",
"需要补充信息":"Needs more info",
"维护者已回复":"Answered by us",
"已关闭此反馈":"Closed",
"需要你回复":"Needs your reply",
"等你补充信息":"Waiting for your information",
"维护者":"Maintainers",
"新":"New",
"显示更早的 {n} 条":"Show {n} earlier messages",
"回复":"Reply",
"回复维护者":"Reply to the maintainers",
"你的回复":"Your reply",
"发送回复":"Send reply",
"离线时暂时不能回复。":"You cannot reply while offline.",
"这份反馈已有公开议题 #{n}:你的回复会被公开转发到那个议题里。请不要写入密钥、密码或私有内容。":"This report already has a public issue, #{n}: your reply is copied there publicly. Do not include secrets, passwords or private content.",
"只发给维护者,不会公开。请不要写入密钥、密码或私有代码。":"Sent to the maintainers only; it is not made public. Do not include secrets, passwords or private code.",
"找不到这条反馈,请刷新列表后再试。":"That report could not be found. Refresh the list and try again.",
"回复不能为空。":"The reply is empty.",
"回复太长了,请缩短后再发。":"The reply is too long. Please shorten it and send again.",
"回复不符合要求,请检查后重试。":"The reply was not accepted. Please check it and try again.",
"这份反馈的回复次数已到上限,或你回复得太频繁了。请稍后再试,必要时另外提交一条新反馈。":"This report has reached its reply limit, or you replied too often. Try again later, or send a new report.",
"这份反馈现在不接收回复,它可能已经处理完毕或被关闭。请刷新列表查看最新状态。":"This report takes no reply right now; it may be finished or closed. Refresh the list to see its latest status.",
"这台电脑的反馈身份已经变了,这份反馈不能再从这里回复。可以另外提交一条新反馈。":"This computer's feedback identity has changed, so this report can no longer be answered from here. You can send a new report.",
"反馈通道暂时关闭,暂时不能回复。":"The feedback channel is switched off for now, so replies are not possible.",
"没能连上反馈服务。你写的回复还在;如果不确定它有没有送达,请先刷新列表看看,再决定要不要重发。":"Could not reach the feedback service. Your reply is kept; if you are not sure it arrived, refresh the list first, then decide whether to send it again.",
"回复没有发出去,请稍后重试。":"The reply was not sent. Please try again later.",
"服务暂时要求额外验证,当前版本还不能显示验证步骤。请稍后再试,或直接到 GitHub 提交问题。":"The service temporarily asks for an extra verification step that this version cannot show. Try again later, or file an issue on GitHub directly.",
"{n} 项待查看":"{n} to review",
"有 {n} 份反馈收到了新回复。":"{n} of your reports have new replies.",
// code adds one here. i18n.test.ts is what stops one from being forgotten.
exportconstEN_KERNEL: Record<string,string>={
"递归删除目标受保护或超出授权范围;请使用工作区或授权目录内的字面路径":"The recursive delete target is protected or outside the granted roots; use a literal path inside the workspace or a granted directory",
"无法确定递归删除范围;请使用字面命令名、路径和解释器内容":"The recursive delete extent is unknown; use a literal command name, path and interpreter payload",
"请拆分命令;递归删除前只能使用字面路径切换目录,不能依赖变量赋值":"Split the call; only literal directory changes may precede a recursive delete, without relying on variable assignments",
"请使用授权目录内的字面删除路径,不要使用变量、展开、通配符或未知管道输入":"Use a literal delete path inside a granted directory, without variables, expansions, globs or unknown pipeline input",
"删除选项无法识别;请使用已知选项并提供公共参数的值":"The delete option is unknown; use known options and supply common parameter values",
"命令语法有误;请修正后重试":"The command has a syntax error; correct it and retry",
"主机命令解析器不可用;请在主机恢复后重试":"The host shell parser is unavailable; retry after the host parser is restored",
"主机命令解析超时或被取消;请重试":"The host shell parser timed out or was canceled; retry the call",
"命令超过主机长度限制;请拆分命令或从文件读取长文本":"The command exceeds the host length limit; split it or read long text from a file",
"另一个会话持有重叠的写入范围,取得所需范围后会自动继续":"Another session holds an overlapping write claim; this session will continue when its claim is available",
"这个会话已取得所需的写入范围,现已继续":"This session has acquired its requested write claim and continued",
"尚未取得所需的写入范围,这次等待已结束":"The wait ended before the requested write claim was granted",
"另一个会话持有所需的写入范围。本次操作未执行;请结束当前轮次,待该范围释放后再重试":"Another session holds the requested write claim. This operation did not run; end the current turn and retry after the claim is released",
"反馈通道今日已满,请明天再试":"Feedback is at capacity for today - try again tomorrow",
"这份反馈的回复次数已到上限,或回复太频繁了,请稍后再试":"This report has reached its reply limit, or you replied too often - try again later",
"这份反馈现在不接收回复":"This report takes no reply right now",
"反馈服务要求额外验证,请稍后再试":"The feedback service asks for extra verification - try again later",
"截图无法清除其中的元数据,请换一张或先用截图工具重新导出":"An image could not be cleaned of its metadata - pick another or re-export it",
"反馈内容不符合要求,请检查后重试":"That feedback could not be accepted - check the fields and try again",
"反馈内容过大,请缩短文字或减少截图":"That feedback is too large - shorten the text or attach fewer screenshots",
"提交太频繁了,请稍后再试":"Too many submissions - try again a little later",
"反馈通道暂时关闭,请稍后再试,或直接到 GitHub 提交问题":"Feedback is switched off for now - try again later, or open an issue on GitHub",
"相同内容的反馈刚刚提交过了":"The same feedback was just sent",
"反馈服务没有接受本机的反馈身份,请重试":"The feedback service did not accept this install's identity - try again",
"无法连接反馈服务,请检查网络后重试,已填内容会保留":"The feedback service cannot be reached - check the network and retry; what you wrote is kept",
"反馈服务暂时无法处理请求,请稍后再试":"The feedback service could not handle that - try again later",
"本机保存反馈记录失败,请重试":"Feedback could not be recorded on this machine - try again",
"该决定已不符合当前状态:计划在你回答前已发生变更":"That decision is no longer current - the plan changed before you answered",
"该条已发送给模型,无法撤回":"That line already reached the model - it cannot be taken back",
"任务正在运行,请先停止再重载扩展":"A task is running — stop it before reloading extensions",
"该来源正在使用中,请先切换模型再删除":"This source is in use — switch models before deleting it",
"请为该来源填写名称":"Give this source a name",
"名称只能包含字母、数字、点、连字符和下划线":"A name may use letters, digits, dot, dash and underscore only",
"名称只能用字母、数字、点、连字符和下划线,以字母或数字开头,最长 64 个字符":"Use only letters, digits, dots, hyphens and underscores, starting with a letter or digit, up to 64 characters",
"已经有名为「{name}」的连接了,换一个名称":"A connection named “{name}” already exists; pick another name",
"读不到配置文件,没法安全地选择密钥存放位置,请检查配置后重试":"The configuration could not be read, so there is no safe place to store the key; check the config and try again",
"互联网连接暂时不可用,请确认电脑端 Studio 已登录并保持在线。":"Internet access is temporarily unavailable. Make sure desktop Studio is signed in and stays online.",
"地址 {ip} 不是本机的局域网地址。":"{ip} is not a private address on this computer.",
"无法在 {ip} 上开启监听:{error}":"Could not listen on {ip}: {error}",
"端口 {port} 已被其他程序占用,请换一个端口,或清空后让系统自动选择。":"Port {port} is already in use by another program. Choose a different port, or clear it to let the system pick one.",
"端口需要在 {min}–{max} 之间,或清空后让系统自动选择。":"The port must be between {min} and {max}, or cleared to let the system pick one.",
"无法保存端口设置:{error}":"Could not save the port setting: {error}",
"没有这台已配对的设备,可能已被移除。":"No paired device has that id; it may already have been removed.",
"这个内核没有窗口,打不开本机的编辑器。":"This kernel has no window, so it cannot open an editor on this machine.",
"这个文件夹不在当前窗口的项目列表里。":"That folder is not a project in this window.",
"项目文件夹已不在磁盘上。":"The project folder is no longer on disk.",
"这个内核不在本机,没法在系统文件管理器中显示它的文件。":"This kernel is not on this machine, so its files cannot be shown in the system file manager.",
"这个系统没有可用的文件夹选择框,请直接填写路径。":"This system has no folder picker available; type the path instead.",
"打不开文件夹选择框:{error}":"The folder picker could not be opened: {error}",
"名称「{name}」不能用来存放密钥:密钥槽位由名称推导,而它不能以数字开头。改一个以字母开头的名称即可,密钥本身没有问题。":"The name “{name}” cannot hold a key: the credential slot is derived from it, and a slot may not start with a digit. Use a name starting with a letter — the key itself is fine.",
"这个内核没有带界面,只提供接口":"This kernel serves no interface, only its API",
"当前模型还没有 API key,请先在设置里添加":"The current model has no API key yet. Add one in Settings first.",
"请填写 API key":"An API key is required",
"该 key 长度异常,可能粘贴了错误内容":"That key is too long — something else was probably pasted",
"已连接,无需重复配置":"Already connected — no need to set it up again",
"远端配置未完成,请稍后重试":"The remote setup did not complete; try again shortly",
"没有这个模型连接":"There is no such model connection",
"该连接没有可存放密钥的变量,请先在配置中为它指定 api_key_env":"This connection has no variable to store a key in — set its api_key_env in the configuration first",
"该 key 含有无法安全保存的字符组合,请重新复制":"That key contains a character combination that cannot be stored safely — copy it again",
"该 key 超长或含换行,请重新复制":"That key is too long or spans several lines — copy it again",
"已保存的密钥在此期间被改动,请重新打开配置再试":"The stored keys changed in the meantime — reopen the setup and try again",
"密钥已保存,但连接尚未生效,请重试":"The key was saved but the connection is not active yet — try again",
"服务商拒绝了该 key,请检查是否复制完整":"The provider rejected that key — check it was copied in full",
"服务商没有及时应答,请稍后重试":"The provider did not answer in time — try again shortly",
"连不上服务商,请检查网络或服务地址":"Cannot reach the provider — check the network or the service address",
"服务商返回了错误,请稍后重试":"The provider returned an error — try again shortly",
"连接测试未通过":"The connection test did not pass",
"这台服务器已关闭会话切换":"This server has session switching turned off",
"该会话正在清理,请稍后再打开":"This session is being cleaned up; open it again in a moment",
"该会话正被其他位置占用,请先关闭该处":"This session is held elsewhere — close it there first",
"写入目标不在工作区可写范围内。这是文件工具的写入范围,不是操作系统沙箱;要允许写入,请在 设置 → 沙箱 → 额外可写目录 中添加目标文件夹":"The target is outside the workspace write scope. This is the file tools' write scope, not an OS sandbox; to allow the write, add the target folder under Settings → Sandbox → Also writable",
"无法读取工作区文件列表":"The workspace file list could not be read",
"该文件不是可编辑文本或超过大小限制":"The file is not editable text or exceeds the size limit",
"不支持这个币种,只能选自动、CNY 或 USD:{detail}":"That currency is not supported; choose Auto, CNY or USD: {detail}",
"费用显示币种未能保存:{detail}":"The display currency was not saved: {detail}",
"无进展设置未能保存:{detail}":"The progress settings were not saved: {detail}",
"主目录或磁盘根目录不能整体信任,请打开具体的项目文件夹":"A home directory or drive root cannot be trusted as a whole - open a specific project folder",
"未能记下对此文件夹的信任决定:{detail}":"The trust decision for this folder was not saved: {detail}",
"This network can't reach the Reasonix remote service. It may be restricted, or the service may be briefly down. Switch networks and reconnect.",
"此网址不能使用远程中转":"This address can't use the relay",
"打开 reasonix.io":"Open reasonix.io",
"远程中转拒绝了当前网页的来源。请从 reasonix.io 打开 Web Studio 后再连接。":
"The relay rejected this page's origin. Open Web Studio from reasonix.io, then connect.",
"尝试次数过多":"Too many attempts",
"请求过于频繁。请等待 {n} 秒后重新连接。":"Too many requests. Wait {n} seconds, then reconnect.",
"请求过于频繁。请稍等片刻后重新连接。":"Too many requests. Wait a moment, then reconnect.",
"中转拒绝了这次连接":"The relay refused this connection",
"连接凭证可能已过期,或这台电脑上同时连接的设备已满。请重新连接;仍然失败请等一分钟再试。":
"The connection ticket may have expired, or this computer already has its maximum number of connected devices. Reconnect; if it still fails, wait a minute.",
"长时间没有操作":"Inactive for a while",
"为节省资源,中转已回收这条空闲连接。点击重新连接即可继续。":"The relay released this idle connection. Reconnect to continue.",
"远程连接失败":"Remote connection failed",
"{action}({n})":"{action} ({n})",
"远程中转服务暂时不可用,请稍后重试。":"The remote relay is temporarily unavailable. Try again shortly.",
"远程 Studio 身份校验失败,请停止连接并检查设备。":"Remote Studio identity verification failed. Stop and check the device.",
"手机和这台电脑在同一个网络里,扫码后就能看会话、发消息、批准操作。关闭或退出 Studio 会断开所有手机。":"A phone on the same network as this computer can scan a code to follow sessions, send messages and approve actions. Turning this off or quitting Studio disconnects every phone.",
"虚拟网卡":"virtual adapter",
"换网络会断开已连接的手机,它们需要扫新的二维码。":"Switching networks disconnects the connected phones; they will need to scan the new code.",
"留空则每次开启时由系统随机选择,范围 1024–65535。更改端口后,手机需要重新扫码配对。":"Leave empty to let the system pick a port each time, or choose one from 1024 to 65535. After changing the port, phones need to scan a new code to pair again.",
"改端口会断开已连接的手机,它们需要扫新的二维码。":"Changing the port disconnects the connected phones; they will need to scan the new code.",
"Use this after editing extension code, or installing, removing, enabling or disabling a package. The current turn is unaffected; the next one uses the new configuration.",
"正在重启常驻进程,重新扫描技能、命令和钩子…":"Restarting resident processes and rescanning skills, commands and hooks…",
"已生效,下一轮开始用新的扩展":"Live — the next turn runs the new extensions",
"更改已保存,运行时未重载:{reason}。请用「重载运行时」重试。":"Changes saved, but the runtime did not reload: {reason}. Use Reload runtime to retry.",
"One package can bring skills, commands, automation hooks and external services at once. Installing and importing are the same action: give it a repository address or a folder on this machine.",
"上次填写未通过:{note}":"The last answer was not accepted: {note}",
"填写这一项":"Fill this in",
"已提交给 {name}":"Sent to {name}",
"MCP 服务器 {name} 请求外部操作":"MCP server {name} is asking for an external interaction",
"请在自己的浏览器中完成操作,完成后再继续。打开页面不会自动确认,也不会把浏览器内容交给 agent。":"Complete the interaction in your own browser, then continue. Opening the page does not confirm completion or share its contents with the agent.",
"此链接指向本机或私有网络。打开前请确认你信任该服务器和目标地址。":"This link targets this device or a private network. Open it only if you trust the server and the destination.",
"请核对完整地址:此链接使用 HTTP 或国际化域名。":"Check the full address: this link uses HTTP or an internationalized domain.",
"无法打开浏览器。请重试,或手动打开上面的地址。":"Could not open the browser. Retry, or open the address above manually.",
"已确认完成":"Completion confirmed",
"打开浏览器":"Open browser",
"已完成,继续":"Completed, continue",
"已拒绝提供":"Declined",
"拒绝提供":"Decline",
"(补写过一次)":" (repaired once)",
"(缺失部分由主机补全)":" (gaps kept by the host)",
"正在折叠为简报":"Folding into a digest",
"本次未折叠任何内容":"This pass folded nothing",
"扩展拒绝了这次折叠":"An extension refused this fold",
"折叠结果没能保存":"The folded result could not be saved",
"压缩未完成":"Compaction did not complete",
"生成摘要的请求失败了":"The request that writes the summary failed",
"生成摘要的请求超时了":"The request that writes the summary timed out",
"摘要在输出上限处被截断,没有采用":"The summary hit the output limit and was discarded",
"待折叠的内容缩减后仍超过单次摘要请求的容量":"The content to fold still exceeds one summary request after shortening",
"摘要生成期间对话发生了变化,已放弃这次折叠":"The conversation changed while the summary was written, so this fold was dropped",
"摘要没有记下这段内容里的任何一处改动,已放弃这次折叠":"The summary recorded none of this span's changes, so this fold was dropped",
"折叠后的上下文不比原来小,没有采用":"The folded context was no smaller, so it was not used",
"折叠后仍超过检查点上限(受保护的内容太多),没有采用":"The folded context still exceeds the checkpoint limit (too much protected content), so it was not used",
"折叠后仍不低于压缩阈值,没有采用":"The folded context was still at or above the compaction threshold, so it was not used",
"折叠后仍超过窗口的物理上限,没有采用":"The folded context still exceeds the window's physical limit, so it was not used",
"固定前缀已占满检查点上限,这次折叠省下的空间太少,没有采用":"The fixed prefix fills the checkpoint limit and this fold saved too little, so it was not used",
"无法折叠的固定部分本身已超过压缩阈值":"The part that cannot be folded is already over the compaction threshold",
"扩展钩子把待折叠的内容清空了":"An extension hook emptied the content to fold",
"压缩被取消了":"The compaction was cancelled",
"压缩失败,原因未归类;详情见日志":"The compaction failed for a reason the host did not classify; see the log",
"有一轮对话或会话切换正在进行,请稍后再试":"A turn or a session change is in progress; try again shortly",
"上下文自上次整理后没有变化":"The context has not changed since it was last tidied",
"自上个检查点以来没有已结束的内容":"Nothing has closed since the last checkpoint",
"新增内容太少,不值得再生成一次摘要":"Too little has been added to pay for another summary",
"正在进行的这一轮需要先结束":"The turn in flight has to finish first",
"没有可折叠的内容":"No foldable region remains",
"没有值得折叠的内容":"Nothing left worth folding",
"已压缩":"Compacted",
"无需压缩:{why}":"Nothing to compact: {why}",
"压缩失败:{why}":"Compaction failed: {why}",
"本段的 {n} 处改动均已写入简报":"All {n} changes made here are in the digest",
"{kept}/{required} 处改动写进了简报":"{kept}/{required} changes are in the digest",
"另有 {n} 处仅保留索引地址,需通过 recall 取回原文":"{n} more are index entries only; recall fetches the original",
"连接可用 · 找到 {n} 个模型":"Connection works · {n} models found",
"接口地址已补全为 {url}":"Address completed to {url}",
"刷新模型目录时,模型列表只在 {url} 下响应。聊天若能正常使用就不用改;可选:把接口地址改成它。":"The model list only answers at {url}. If chat already works, no change is needed; optionally set the address to it.",
"启用的模型":"Enabled models",
"目录只用于发现,不是白名单。直接输入服务商给出的原始模型 ID 即可。":"The catalogue is for discovery, not a whitelist. You can enter any exact model ID supplied by the service.",
"阈值需至少为 1,000,并小于模型上下文窗口。":"The threshold must be at least 1,000 and below the model context window.",
"以 @{handle} 的名义提交,审核通过后公开。只收来源地址,不上传文件。":"Submitted as @{handle} and made public once approved. Only the source address is sent; no files are uploaded.",
"以 @{handle} 的名义保存,仅自己可见,不提交审核。只收来源地址,不上传文件。":"Saved as @{handle}, visible only to you and not submitted for review. Only the source address is sent; no files are uploaded.",
"主题以插件包发布,包里只能有主题;带技能、钩子或 MCP 服务的包请按插件发布。":"A theme is published as a plugin package that holds only themes; a package with skills, hooks or MCP servers is published as a plugin.",
"小写字母、数字、点、下划线、连字符,最多 64 个字符。":"Lowercase letters, digits, dots, underscores and hyphens, up to 64 characters.",
"要发布这个包的新版本,请保持名称不变;改名会发布为另一个包。":"Keep this name to publish a new version of this package. Changing it publishes a different package.",
"留空时新包为 0.1.0,更新自动加一个补丁号。":"Left empty, a new package starts at 0.1.0 and an update bumps the patch number.",
"These are still in {dir}: {names}. A move left them behind, so a wallpaper, a theme pack or the backups an update rolls back to can look gone on this machine. Copying those folders into the current locationrestores them.",
"These are still in {dir}: {names}. A move left them behind, so the project list, global instructions and memory, scheduled runs, a wallpaper, a theme pack or the backups an update rolls back to can look gone on this machine. The next launch merges them into the current location; if they arestill listed, quit and copy them there without overwriting files that already exist.",
"Only decides which currency costs are shown in; each source's price list is untouched. Auto follows the account balance's currency, and falls back to the price list's currency when there is no balance.",
"显示币种":"Display currency",
"当前会话的费用、余额与用量页立即按所选币种显示,无需重启。":"The session's cost, the balance and the usage page switch to the chosen currency at once, no restart.",
"人民币 CNY":"Chinese yuan (CNY)",
"美元 USD":"US dollar (USD)",
"无法读取币种设置。":"Could not read the currency setting.",
"Token usage and cost recorded on this machine only; nothing is uploaded. Input that hits the cache is billed at the cache rate, so the hit rate drives the bill.",
"另有 {n} 天有用量但没有成本记录 —— 不是那几天没花费,是成本字段后来才开始持久化。":"{n} more days have usage but no cost recorded — not free days; the cost field started being persisted later.",
"按模型":"By model",
"按来源":"By source",
"未知来源":"Unknown source",
"尚无 key?前往获取":"No key yet? Get one",
"打开 {name} 控制台":"Open the {name} console",
"无法打开浏览器,请手动访问 {at}":"Could not open a browser — visit {at} yourself",
Loaded 100 of 1469 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.