- Support for the local non-generative
Phocinae-Largha-150M-v1decision model as the reviewer.reviewerBackend: phocinae— or justreviewerModel: Phocinae-Largha-150M-v1, which infers the backend — routesai-policy reviews to a loopbackphocinae-server(POST /v1/systemone, defaulthttp://127.0.0.1:8155/v1/systemone) instead of a generative subagent: no API key, nosubagents/llmseam and no network. It is a veto/triage layer, not a general-purpose approver: only aharmlessanswer withanswer_confidence >= phocinae.minConfidence(default 0.4) allows,destructivedenies, andrisky/ an unsureharmless/ any transport or protocol failure resolve throughfallbackPolicy(defaultrejected;delegatehands them to the human chain). Reviews are shell commands only — a call without acommandargument escalates without contacting the model. - The request wording is the measured one, not an improvised prompt. State is exactly
The agent is about to run the shell command "<command>" in its workspace., and the harm options carry the descriptions the model was trained on ascriteria(the HTTP contract has nocriteriafield). On 89 labelled commands (2026-10-25, CPU fp32, released weights) 0 of 53 destructive commands were allowed at 0.4 — 26 denied, 27 escalated — while 5 of 36 routine commands were allowed; the same measurement at 0.35 let two destructive Windows commands through. The other measured formulations (bare option names, a boolean question,safe/must not runwording) were rejected because they either collapse to deny-everything or clear destructive commands. - Privacy and egress. Only the redacted shell command is sent to the decision service — the approval reason, transcript and workspace path are not — and the command passes the same key-level and inline-secret redaction as the generative prompt.
phocinae.allowRemoteEndpointmust be set explicitly to send it off-host. - Observability and docs.
/auto-review statusnames the reviewer backend and model, andautoReview/verdictrecordsprovider: phocinae(instead of claiming a subagent ran). Full config documentation, an annotated example infixtures/config/config-full.yaml, and the service setup instructions in all five READMEs.
- Installing the plugin no longer hoists the
0.2.1-alpha.1host implementation line into the DSH profile (issue #74). The package declared 14@deepseek-ai/dsh-*host packages (app-boot, credentials, fs/sandbox, subprocess, session persistence, subagent drivers, tool-fs) as runtimedependencies. In a pnpmhoistedprofile those were installed at the profile root, where they shadowed the host's0.2.1-rc.2providers:credentials,fs,subprocess,sandbox,sandboxPolicy,sessionPersistenceandconnectionnever activated and the app failed to boot. They now live indevDependencies(the line the test suite runs); a consumer install adds zero@deepseek-ai/dsh-*packages. The node faces already leave every@deepseek-ai/specifier external (neverBundle), so they resolve from the host profile;dsh-evalfollows the same rule and a standalone global install no longer carries its own host copy.
reviewerModelProviderroutes the reviewer to a second LLM provider (issue #60).reviewerModelonly setagentOptions.model, so the child/activation stayed on the session provider. A new optionalreviewerModelProvideris passed asagentOptions.provideron both reviewer seams and as the tool-lessllm.streamprovider; unset keeps the previous inherit-the-session-route behavior. Config docs added in all five READMEs; tests cover both the inline route and config resolution.
/auto-review statusno longer calls the audit-disabled in-memory tally “All-time” (issue #50). On a host where session-log audit is fail-closed, the numbers come from the per-turn memory mirror; the line is now labeled “This turn (audit disabled; in-memory)” / “本轮(审计已停用,内存统计)”, so a 0 after a turn with real verdicts is not presented as a lifetime total.
- Move
tsdown/typescript(and@types/reactwhere present) from runtime dependencies to devDependencies. Git installs still build: pnpm installs devDependencies for a git dependency and runsprepare; npm tarballs ship the builtlib/. The runtime tree no longer carries the build chain.
-
Auto-review was silently bypassed on
dsh 0.2.1: the answerer never saw a request.approval/requestis a Cordis waterfall resolved in registration order, and the web app bundle mounts its approval forwarder (@deepseek-ai/dsh-api-remotes) before any profile plugin. The forwarder claims the request and raises the human dialog, so an answerer registered later is never asked — no error, no warning, just an approval that auto-review did not review. The answerer now registers withprepend: true, which puts it at the head of the chain. Every path that does not claim (switch off,human/neverpolicy, budget exhausted, circuit open) still delegates withnext()to the same downstream chain, so what happens after it is unchanged. -
The reviewer failed on every request once
dsh 0.2.1removedctx.subagents.start.0.2.1replaced the one-shot child withstartActivation, whose spawn provider no longer accepts atoolFilter; a child without the read-only allow-list is exactly the reviewer face this plugin refuses to publish. The reviewer therefore falls back to one tool-lessctx.llm.streamcall on the delegating turn's route (the latestrequest/header's provider/model,temperature: 0), with the same prompt and the same verdict validation — no agent, no tools, and no child session to isolate. Both seams are feature-detected at call time instead of being declared ininject, so a host that still offerssubagents.startkeeps the isolated-child reviewer and does not have to mountllm.
- Add a 1280×720 hero and a 960×540, 4-second terminal GIF rendered from the test run;
screenshots.jsonis an object array andcapture.jsonrecords the command.
- Risk-rule examples are valid JavaScript regexes, and an invalid pattern names its index. The READMEs used the PCRE inline flag
(?i), whichnew RegExp(..., "u")rejects; examples now spell the case-insensitive character classes out. Config errors readriskRules[0]: pattern …instead of an unlocated message (issue #59).
- Reviewer activation on 0.2.1+ (
startActivation). The review and eval paths now detectsubagents.startActivationand use it withdelivery: "caller"; on hosts that still exposestart()the old path is unchanged. BecausestartActivationhas notoolFilter, the new path requiresreviewerPreset(a read-only Agent Preset) and fails closed without one, rather than giving the reviewer the parent's full tool surface. This closes the functional failure where every AI approval on 0.2.1 was rejected.
reviewerPresetadded to config/ResolvedConfig and the eval review config.
- Windows default no longer silently does nothing (issue #30). The shipped defaults now cover
pwshandpowershellasai, not onlybash/write. - The context-firewall docs no longer claim the runtime-context snapshot is dropped. The host re-admits
runtimeContext.project(...)afteragent/pre-step;README(en/zh/es/pt),AGENTS.mdandisolation.tsnow state that the reviewer still sees it. Workspace instruction files and third-party injections remain filtered. - Compatibility:
engines.dsh->>=0.2.1-0 <0.3.0; devDependencies to0.2.1-rc.2; add@deepseek-ai/dsh-working-directory; remove the deleted@deepseek-ai/dsh-invariantspeer.
All notable changes to dsh-auto-review are documented here. The repo is pre-release; versions follow the DeepSeek Harness 0.1.0-rc.x target runtime and bump on every behavior change.
- A secret spelled inside a free-form string argument reached the reviewer verbatim.
sanitizeArgumentsredacts by object KEY, so it sees{"apiKey": "..."}but notdeploy --token sk-live-123orcurl -H "Authorization: Bearer …"— which is how a secret usually appears in a shell call. Both rendering paths are covered now: the pending call's own preview (sanitizeArguments) and the compact transcript line (contextLine), the second being the one that reached the reviewer intact. Known limit, pinned by a test: a bare secret with no key adjacent to it (echo sk-live-123) stays visible — this is a heuristic, not a secret scanner. - Truncated evidence read as complete evidence. The pending call's preview is cut at
reasonMaxChars(default 2000) behind a bare…, so a reviewer shown only the benign head of a long call could allow it with the decisive arguments cut off. A truncated preview is now labelledTRUNCATED, states the exact counts (only the first <n> of <m> characters are shown; the remainder is NOT visible to you), and the verdict rules gained the matching- truncated evidence counts as insufficient: when the arguments are marked TRUNCATED, DENY. - The review panel showed all zeros on every host that declines the audit events. The
autoReviewprojection folds the plugin's ownautoReview/*session events, and on a host whoseSession.appendcannot stamp theignorablemarker none of them is ever written — which is every published line — so the panel folded an empty log while the in-memory mirror held the real numbers. The persisted state now carries the owningsessionId(state version 4) and the runtime registers a live wire view that serves the switch state and the session's activity from the mirror; on a marker-aware host the pure fold stays authoritative and is returned untouched.
test/hardening.spec.ts— a regression case per fix above, including the documented bare-secret limit.test/projection.spec.tscovers the state-version-4 contract and the substituted panel view.
-
Publish with pm publish instead of pnpm publish.
pnpm's publish does not send the README (or a gitHead) in the packument, so the registry stored an empty readme and the npm package page showed no documentation at all. Both symptoms are known upstream: pnpm#12966 and pnpm#3373.
-
The README now carries the terminal demo and an animated version of it: the same real run, shown assembling line by line. The static image stays as the primary one, because a crawler or an assistant can read a PNG and cannot read a GIF.
Both frames are that plugin's own output — the card is generated by running its code over its own fixture, not drawn by hand.
- The published package cannot typecheck on the 0.2.1 line.
src/invariant.tsimported theInvariantFailureandInvariantInstallertypes from@deepseek-ai/dsh-invariants, which the host REMOVED in0.2.1-alpha.1(upgrade guideremove-runtime-invariants), while the package's own peer band already admitted>=0.2.1-0 <0.3.0. Both shapes are now declared locally, together with theinvariantsservice augmentation the removed package used to contribute 鈥?the companion's singlectx.invariants.register(...)call depends on it, so dropping the import without that augmentation would have moved the same failure one line down. - The companion's behaviour is unchanged:
inject: ['invariants']still parks its fiber in PENDING on a composition that provides no such service, which is every plain web profile (the row ships commented out in the bundle patch anyway). - The
@deepseek-ai/dsh-invariantsdev pin is dropped (the plugin pins and this pin had drifted apart,0.2.1-alpha.1vs0.2.0-rc.2), andtest/invariant.spec.tsnow loads the registry dynamically and skips its 23 cases when the host no longer ships the package, instead of failing at collection time.
- Correct the release date in the previous section, which was stamped with the literal string
undefinedby the release stamper. No content or behaviour change; the version is bumped only because npm will not republish an existing version.
undefined
Adds the versioned five-language interoperability declaration to the READMEs: the plugin records, against DSH 0.2.0-rc.2, every injection point it owns and why none of them collide with another plugin — no tool-name, service-key, slot, HTTP-route, patch-layer or global-mutation overlap, and, for each ordering-sensitive event it listens on, that the listener delegates through next() so a shared waterfall chain is never short-circuited. Verified by dsh-plugin-doctor K10-K14 (checkset R0-R8+K1-K14+D0-D3,D9+CC1-CC5/3). Documentation only; no behaviour change.
- Host pins move to
0.2.1-alpha.1; re-verified against that host line. Every@deepseek-ai/dsh-*dev/test dependency now pins0.2.1-alpha.1, thedshWorkshop.compatibility.dshVersionstimeline appends0.2.1-alpha.1, and the compatibility baseline in every README records thedsh-v0.2.1-alpha.1host. The declared host ranges (engines.dshand thepeerDependenciesunion) gain the|| >=0.2.0-0 <0.3.0 || >=0.2.1-0 <0.3.0clauses: the previous upper bound was<0.2.0, which under semver rejects every 0.2.x host, so the probe host itself was not installable. Nothing was narrowed — the0.1.xclauses are unchanged, in place and in order.
- Host pins move to
0.1.7-rc.2; re-verified against that host line. Every@deepseek-ai/dsh-*dev/test dependency now pins0.1.7-rc.2, thedshWorkshop.compatibility.dshVersionstimeline appends0.1.7-rc.2, and the compatibility baseline in every README records thedsh-v0.1.7-rc.2host. The declared host ranges (engines.dshand thepeerDependenciesunion) are deliberately unchanged — they already admit0.1.7-rc.2, and a range is what the manifest accepts, not what has been tested.
- Move the
@deepseek-ai/dsh-*host pins to the published0.1.7-rc.1line and re-verify this plugin against that host line. dshWorkshop.compatibility.dshVersionsrecords0.1.7-rc.1; the five-language README compatibility rows namedsh-v0.1.7-rc.1.
- Re-verified against the
0.1.7-alpha.2host; the@deepseek-ai/dsh-*host pins move to0.1.7-alpha.2. Thealpha.2wave removes nothing this plugin consumes: the published type surface of every host package it resolves is either byte-identical to0.1.7-alpha.1(@deepseek-ai/cordis4.0.3≡4.0.4,@deepseek-ai/schemastery3.18.3≡3.18.4) or strictly additive (ToolDefinition.projectContent?,ClientModuleLoader.importError(), six addeddsh-client-localekeys). The wave's only removals are internals no plugin in this family references —dsh-subprocess-localprivates and its non-entrybindManagedProcess,dsh-client-webassertEntriesActive,dsh-app-boot'sunhandledRejectionevent,dsh-client-ui-plugin-managerapply(), a refineddsh-client-ui-primitivesCodeBlocksignature, and thediff.files.one/diff.files.otherlocale key pair. Both rulers therefore stay green on the moved pin:typecheckagainst the checkout,typecheck:ciagainst the published0.1.7-alpha.2faces. - The declared host range is deliberately unchanged. It already admits
0.1.7-alpha.2(0.1.7-alpha.2satisfies the>=0.1.7-0 <0.2.0clause), and the family convention keeps peer ranges wider than the verified line rather than narrowing them to it; a range is what the manifest accepts, not what has been tested. - Repo documentation (
AGENTS.md), the workspace graph pin (pnpm-workspace.yaml) and the published-line workflow pins move with the manifest, so no file still claims the previous line.
- Adapt to host
dsh-v0.1.7-alpha.1. Five confirmed breakages between the0.1.6-alpha.2faces this package was built against and the0.1.7-alpha.1host:- The
MessageSourceMapcatch-allkind: 'plugin'is gone. The host'sMessageSourceMapis a merge-extensible sum type whose every producer declares its OWN kind, and the durable-log admission path (session-format-v3-to-v4/src/message-sources.ts) refuses a row whose source is literallykind: 'plugin'— the refusal is physical, so not even a cast gets such a row past it. Newsrc/message-source.tsdeclares'auto-review', and both notices (/auto-review on|off's switch notice, the circuit-breaker abort warning) now carry{ kind: 'auto-review', form: 'notice', summary }with the summary bounded by the host'sboundContextSummary. Nothing in this package reads messages back BY source (theautoReviewprojection folds only its ownautoReview/*events), so no back-compat branch was needed: the three durable spellings a stored log can hold —{ kind: 'auto-review' }, the V3→V4 migration'splugin:auto-reviewEVENT namespace (not a source), and the retired{ kind: 'plugin', plugin: 'auto-review' }source — only have to stay LOADABLE, which the host's own migration guarantees. - The
'tool-result'content block no longer exists. V4 writes a first-classrole: 'tool'message carryingtoolCallIdand the result blocks at its TOP level, and the V4 admission path refuses content that still contains a releasedtool-resultwrapper. Newsrc/session-message.tsnormalizes both shapes for the three readers (src/review.ts's reviewer transcript,src/invariant.ts's marker↔event link,src/eval/trace.ts's tool-result fold). Read compatibility only — this package never WRITES the retired shape;tool/resultrows are produced by the host, andfixtures/sessions/*.jsonare replayable logs authored on the old line, so a fixture rewritten to V4 would stop exercising the upgrade path a real user's log takes. The invariant tests gained a released-V3 fixture for the same reason. @deepseek-ai/dsh-settings-filewas deleted andctx.settingsScopewith it. This package never registered a settings namespace and never injectedsettings— the only references were one deaddependenciesrow and theeval/cordis.ymlrow that mounted it. Both are removed;eval/cordis.ymlnow boots without it, and every configuration field stays an ordinary cordis.ymlConfigfield, so no config, default, or persisted shape changes. (Itssettingsservice is provided by the composition's own spine row.)@deepseek-ai/dsh-agent-presets→@deepseek-ai/dsh-agent-preset-registry. This package never imported the renamed package; the only reference was one now-deadpnpm-workspace.yamloverride row naming it, and that whole block is gone (see the dependency entry below).turn/end.reasongainedforked.src/eval/trace.ts'scopyTurnEndwas already a non-exhaustiveswitchwith adefault, so nothing fell through silently;forkednow has its ownTraceTurnEndvariant instead of degrading to{ kind: 'unknown' }, because the eval runner forks sessions and a case can legitimately end on a fork-seed cut.turnEnds: completedstill reports it as not completed.
- The
- Whole-graph pin of
@deepseek-ai/dsh-*to the host line inpnpm-workspace.yaml. Only the DIRECT dependencies can be pinned inpackage.json(pnpm 11 no longer reads apnpmfield there), and everything else in the graph is a PEER:autoInstallPeerssatisfies a peer with whatever version its range allows, and the frozendsh-agent-spine-demo@0.1.1-rc.2subgraph still declares^0.1.1-rc.2/^0.1.2-alpha.2peers. pnpm filled those with 0.1.1/0.1.2 previews, which do not carry the exports the 0.1.7 host packages import —assertNeverfromdsh-llm,longEdgeDimensionsfromdsh-attachment,SessionAlreadyExistsErrorfromdsh-session-persistence, thedsh-subprocess/controlsubpath — so four rows of the shippedeval/cordis.ymlfailed to IMPORT anddsh-evalbooted a composition with 7 of 18 entries inactive. The 18 old0.1.2-alpha.2compatibility override rows are replaced by 57 rows pinning every@deepseek-ai/dsh-*peer to0.1.7-alpha.1. Verified by bootingeval/cordis.ymlthrough@deepseek-ai/dsh-app-bootin a scratchDSH_HOME: before,approval/commands/subagents/sessionProjectionsup andsessions/autoReviewRuntimeabsent; after, every service present and no inactive entry. No row is added for a package the host has deleted (dsh-agent-presets,dsh-code-runtime) — pnpm could not resolve it. - Pin the
@deepseek-ai/dsh-*dev/test dependencies and runtimedependenciesto the published0.1.7-alpha.1line (onlydsh-agent-spine-demostays on0.1.1-rc.2, which has no 0.1.7 release and no longer constrains anything: it declares no dependency on the renamed presets package). Raise@deepseek-ai/cordis^4.0.2→^4.0.3,@deepseek-ai/schemastery(dependency + devDependency)^3.18.2→^3.18.3,@deepseek-ai/cordis-plugin-loader^1.0.3→^1.0.4,@deepseek-ai/cordis-plugin-include^1.0.7→^1.0.8, and add@deepseek-ai/cordis-plugin-group^1.0.3: the0.1.7-alpha.1host packages declare exactly those peers, and the lockfile had resolved the older versions. Verified after install:cordis 4.0.3,schemastery 3.18.3,cordis-plugin-loader 1.0.4,cordis-plugin-include 1.0.8,cordis-plugin-group 1.0.3;pnpm peers checkreports no issues. A raised floor is strictly narrower, so no supported host line is dropped. - Add a fourth
engines.dshandpeerDependenciesrange segment,|| >=0.1.7-0 <0.2.0. This is a BUG FIX, not a narrowing: npm's prerelease rule admits a prerelease only into a comparator set whose own tuple carries a prerelease, so the previous>=0.1.6-0 <0.2.0segment did not cover0.1.7-alpha.1itself. The earlier segments are unchanged, so0.1.2-rc.1,0.1.3-alpha.2,0.1.5-rc.x, and0.1.6-alpha.xstay supported. - Explicit deviation from the
0.1.5-rc.3pin: the@deepseek-ai/dsh-*line is0.1.7-alpha.1.0.1.5-rc.3is the old-contract next line — it still carrieskind: 'plugin'message sources, thetool-resultwrapper block, anddsh-settings-file— so adapting to it would produce code that cannot compile or run against the0.1.7-alpha.1host this package targets.0.1.7-alpha.1is the only published line carrying the contracts this package now consumes.
- The three Loader negative regressions were silently passing. Under loader
1.0.4+ cordis4.0.3a row whose config is rejected no longer rejectsloader.await(): the loader reports it throughctx.logger.errorand its tree waits withPromise.allSettled, so the error stays on that row's fiber while the composition returns normally — and a minimal composition has no log sink, so even the report is swallowed. The runner then fell through to its own downstream check and exited withLoader composition: /auto-review command is missing from the commands registry, which the loose assertion regexes accepted.scripts/loader-runner.mjsnow re-throws the firstFiberState.FAILEDrow's recorded error (value mirror3, since cordis declares it as aconst enum). Measured before/after on the same invalid config, with the re-throw disabled by an env flag the runner honours for exactly this re-measurement: silent →command is missing; loud →invalid config: - $.toolsPolicy.default expected "ai" | "human" | "never" but got "bogus" (at toolsPolicy.default)andreviewerTimeoutMs must be a positive safe integer, got 0. A new regression guard intest/composition.spec.tsasserts both sides, so the re-throw cannot be dropped without a red test.
test/session-message.spec.tscovers the V4 and released-V3 tool-result shapes at their own seam (normalization, thesource.callIdfallback, non-result rows, text joining, and the two failure signals);test/eval/trace.spec.tsgained V3-wrapper andforkedcases;fixtures/sessions/valid-deny-verdict-v3.jsonreplays the valid deny chain in the released shape.
pnpm run check:lockfile(scripts/check-lockfile-drift.mjs) fails fast whenpackage.jsonandpnpm-lock.yamldisagree; the documented checks chain runs it alongside the other gates.
- The release workflow now publishes through npm trusted publishing (OIDC) instead of the long-lived
NPM_TOKENsecret:setup-nodeno longer setsregistry-url(its empty_authTokenline made the registry answer 404), and the "NPM_TOKEN is not set -> fail" guard is gone so the OIDC exchange is the only path. pnpm performs the OIDC exchange itself.
- Raise the pinned harness line from
0.1.5-rc.2to0.1.6-alpha.2(31@deepseek-ai/dsh-*pins acrossdependenciesanddevDependencies; onlydsh-agent-spine-demostays on0.1.1-rc.2, as it has no alpha.2 release). The old pin was not merely cosmetic: on an alpha.2 host the hoisteddsh-home-pathscame from the rc.2 line and lacksdshCachePath, which the alpha.2 host packages (dsh-attachment-local) import at module scope, so a scratch profile booted withSyntaxError: The requested module '@deepseek-ai/dsh-home-paths' does not provide an export named 'dshCachePath'. The lockfile now resolves a singledsh-home-paths@0.1.6-alpha.2. Feature detection for older host lines is untouched.
- Land the eval fixtures on the alpha.2 model vocabulary:
eval/cordis.ymlandeval/cases/demo.yaml(plus the five README run examples) now namedeepseek-flashinstead of the removeddeepseek-v4-flashid, and the compatibility row admits the>=0.1.6-0 <0.2.0peer segment. - Declare
dsh.manifestVersion: 1and the canonical three-clauseengines.dsh(G-3).
- The A2 wrapper static assertion (a lint rule proving
src/session-events.tsstays the onlysnapshotEvents()call site) is not yet enforceable here: this repo lints with oxlint (96 rules, notypescript/no-deprecatedrule configured), so the card's precondition does not hold. The single call site is unchanged and documented; the assertion can land once the rule is enabled. - The resume round-trip criterion (
check-session-log.mjsover a real log) still needs a real host log; the audit gate contract plus the existing regression tests pin the no-unmarked-append invariant in the meantime.
- Correct the host-capability documentation that claimed a later harness line could stamp the
ignorableenvelope marker fromSession.append. No published line can, and the code was right all along. Verified three ways: the host source (packages/core/session/src/index.ts:710-738— the optional third parameter isSurfaceIntent, accepted for surface event types only, and the envelope is assembled from{ type, seq, time, data }plussurfaceOp/sourceEventSeqsalone), every relevant published tarball (0.1.0-rc.2/3/6/7/8,0.1.1-rc.1/2,0.1.2-alpha.2–alpha.5,0.1.2-rc.1,0.1.3-alpha.2,0.1.5-rc.1,0.1.5-rc.2— all build the same envelope), and a live append against the host's own builtSessionon the0.1.5-rc.2line, which returns keys["type","seq","time","data"]withignorableundefined. The marker is reachable only through the seed/restore path, i.e. it is written by the harness that owns the log, never by a plugin appending to it.isUnmarkedHostVersionreturningtruefor the whole published 0.1.x range is therefore accurate and not an over-reach; it is now pinned by a test so a future "narrow the bound" change cannot silently become the log pollution the guard exists to prevent. Reported with the opposite diagnosis on issue #38. - Rewrite the
auditDisabledNotice(EN + ZH), thewarnUnmarkedAuditHostwarning, theallowUnmarkedAuditconfig doc, and the AGENTS.md audit bullet so they no longer say the host "predates" the marker or that unmarked events break resume only "on stricter harness builds". The unmarked event is refused byvalidateStoredEventson every validating harness, reached from the ordinary local jsonl load path — andallowUnmarkedAudit: trueis now stated as what it is: a switch that makes those sessions unloadable, not a compatibility workaround. The previous wording led a user to recommend it publicly as safe on0.1.5-rc.2. - Add a regression test that calls the installed peer's real
Session.appendwith{ ignorable: true }on an out-of-tree type and asserts the returned envelope carries no marker, plus a table test over every published 0.1.x version string.
- Rename the four translated READMEs to
README-<lang>.md. npm selects the package-page readme as the first markdown file matching its{README,README.*}glob (@npmcli/package-json, publish path), and that glob order putsREADME.<lang>.mdahead ofREADME.md— so npm was serving the Simplified-Chinese file for this package too (measured on 15/15 sampled packages of the family). The new names sit outside the glob, so the English source is served again. No content changed apart from the language-switcher link each translation holds to its siblings, and the repo readme gate still passes. Takes effect with the next release; an already-published version cannot gain a corrected readme retroactively. - Pin the
@deepseek-ai/dsh-*dev/test dependencies to the published0.1.5-rc.2line and record0.1.5-rc.2indshWorkshop.compatibility.dshVersions; the monthly Compat workflow now runs against0.1.5-rc.2. The peer range>=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0is unchanged, so no supported host line is dropped.
- Pin the
@deepseek-ai/dsh-*dev/test dependencies to the published0.1.5-rc.1line and record0.1.5-rc.1indshWorkshop.compatibility.dshVersions; the monthly Compat workflow now runs against0.1.5-rc.1. The peer range>=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0is unchanged, so no supported host line is dropped.
- Refresh the five-language README compatibility baseline to
dsh-v0.1.5-rc.1(verified 2026-09-10).
- Runtime
dependencies: all 15@deepseek-ai/dsh-*pins moved from0.1.2-rc.1to0.1.5-alpha.1. Installing the0.12.0tarball into adsh@0.1.5-alpha.1web profile hoisted that pinned0.1.2-rc.1tree (dsh-session-persistence-jsonl,dsh-app-boot,dsh-tool-fs, …) over the CLI's own0.1.5-alpha.1tree, and the nextdsh webdied at boot withfailed to import loader entry session-persistence-jsonl (@deepseek-ai/dsh-session-persistence-jsonl): The requested module '@deepseek-ai/dsh-session-persistence' does not provide an export named 'DEFAULT_PREPARED_SESSION_CACHE_SIZE'. The runtime pins now follow the alpha line the published CLI installs, so a profile install no longer shadows the host tree. Reproduced and re-verified withpnpm pack+dsh plugin --profile web add <tgz>+dsh webagainst a scratchDSH_HOME: the pre-fix tarball crashes at boot, the fixed tarball boots and serves. pnpm-lock.yamlrefreshed for the new pins (pnpm install --no-frozen-lockfile).
- Peer ranges stay the dual-line
>=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0anddshWorkshop.compatibility.dshVersionsstill lists0.1.2-rc.1,0.1.3-alpha.2, and0.1.5-alpha.1: the plugin's code keeps feature-detecting both published host lines; only the installed runtime dependency pins follow the alpha line.
- Five-language READMEs: the Harness row now states runtime deps
0.1.5-alpha.1and why the pins follow the alpha line. AGENTS.mdand thepnpm-workspace.yamlpin comment: the stale "runtimedependenciesstay on the published0.1.2-rc.1line" statements replaced with the alpha-line pin.
- dsh-eval session artifacts (
traces/*.session.jsonl) are replayable on the 0.1.3+/V3 line again: the header line now emits the seed vocabulary of the header's own format version —isSeeded(and never the retired V0seedLength) for V2/V3,seedLengthfor V0 — so the harness format catalog reads the artifact as a current header instead of refusing it as malformed. Previously a V3 artifact carriedversion: 3withoutisSeeded(andseedLengthwhenever an inherited count was present), which the persistence reader rejects. src/eval/trace.ts: the traced system prompt is read from the last non-emptysystem/messagesurface node on the 0.1.5 line (the prompt moved into the message history as surface node zero) whilerequest/header.header.systemremains the source on the 0.1.2/0.1.3 lines, so the prompt-baseline assertions keep working on both published lines.scripts/check-host-versions.mjs: the peer-range parser now splits||unions and judges every segment, so the dual-line peers (>=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0) are recognized; the alpha-line check accepts an alpha peer segment in addition to the dev pins. The gate previously exited 1 withno rc-pinned @deepseek-ai/dsh-* peers found, which made thehost-compatCI job permanently red.src/eval/report.ts: the Markdown footer no longer claims unconditional replayability; it states the line-specific header vocabulary.
- Dev pins moved to
@deepseek-ai/dsh@0.1.5-alpha.1(every@deepseek-ai/dsh-*devDependency that publishes the line;@deepseek-ai/dsh-agent-spine-demostays on0.1.1-rc.2), including the new@deepseek-ai/dsh-session-format-catalog@0.1.5-alpha.1used by the artifact-replay tests; peer ranges are the dual-line>=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0; runtimedependenciesstay on the published0.1.2-rc.1line. src/audit.ts:0.1.5-alpha.1joins the known-unmarked host lines (V3 format;Session.appendstill has no marker channel), so session-log audit stays fail-closed there.dshWorkshop.compatibility.dshVersionsnow lists0.1.5-alpha.1;.github/workflows/compat.ymlprofile pins moved from the unsupported0.1.1-rc.2(below the peer floor) to0.1.5-alpha.1.
- New
test/eval/session-artifact.spec.ts(L6/U6): the rendered artifact replays through@deepseek-ai/dsh-session-format-catalog(readHeader+createRestore().finish()), a V0-line header stays readable with its seed semantics, and malformed V3 fixtures (missingisSeeded, retiredseedLength, non-booleanisSeeded), a newer-format header, a V3 body behind a V2 header, and an unmarked unknownautoReview/*event are refused instead of silently misread. test/eval/runner.spec.tsasserts the runner's own artifact header against the installed format catalog;test/audit.spec.tscovers the0.1.5-alpha.1version line.
- Five-language READMEs: the Harness row now names
dsh-v0.1.5-alpha.1(verified 2026-09-09), the dev pin0.1.5-alpha.1, and the dual-line peer range; theallowUnmarkedAuditrow and the session-log bullet list0.1.5-alpha.1among the non-stamping lines. AGENTS.md: corrected the stale test-peer statement and the host-version gate description (compound||peer ranges and the alpha peer segment).
- Migrate the dev pins from
@deepseek-ai/dsh@0.1.2-rc.1to@deepseek-ai/dsh@0.1.3-alpha.2(all 15@deepseek-ai/dsh-*devDependencies;@deepseek-ai/dsh-agent-spine-demostays on0.1.1-rc.2). Dual-line runtime compatibility is preserved: the runtimedependenciesand the>=0.1.2-rc.1 <0.2.0peer range stay on the published rc line, the trace fold reads stream timing fromassistant/chunk(0.1.2) or the message's timedstream(0.1.3, including the newassistant/attemptsettlement records), and the session-artifact renderer feature-detects the removedpackChunkRunsexport. - Test fixtures now stamp headers with the host line's
SESSION_FORMAT_VERSION(0 on 0.1.2, 2 on 0.1.3) instead of the literal0, and the fake-session header carriesisSeeded; the reviewer/isolation suites pass on both published lines (278/278 each). src/audit.ts:0.1.3-alpha.2joins the known-unmarked host lines (itsSession.appendstill assembles the envelope without theignorablemarker, andautoReview/*is still absent fromKNOWN_SESSION_EVENT_TYPES— verified against the published tarball), so session-log audit stays fail-closed on the alpha line; unit tests cover both lines.scripts/check-host-versions.mjs: the0.1.3-alpha.2stay-behind exemption is removed — the migrated dev pins now cover the newest alpha line natively.dshWorkshop.compatibility.dshVersionsand the five-language READMEs now state both supported host lines (0.1.2-rc.1and0.1.3-alpha.2).
- Dual-line session-log compatibility for dsh-eval: the trace folds derive per-step stream timing from
assistant/chunk(0.1.2 line) or the message's timedstreamrecords (0.1.3 line), and the session-artifact renderer feature-detects the 0.1.2-onlypackChunkRunsexport; both published host lines keep their existing behavior. - The host-version gate now carries a documented stay-behind for
@deepseek-ai/dsh@0.1.3-alpha.2(whose session vocabulary droppedassistant/chunk/packChunkRuns) until the dev pins migrate; the shipped pins stay on the published rc line.
- Fix the DSH plugin badge URL: shields.io rejects the four-segment static badge form with "404 badge not found"; the label now uses the documented double-dash form (
dsh--plugin), rendering identically; no behavior change.
- Refresh the five-language README support-version wording: the verified GitHub tag
dsh-v0.1.3-alpha.1now leads the compatibility claim, while npm0.1.2-rc.1stays the published dependency-pin line (peers>=0.1.2-rc.1 <0.2.0); no behavior change.
peerDependencies: all 14@deepseek-ai/dsh-*peer ranges moved from>=0.1.0-rc.8 <0.2.0to>=0.1.2-rc.1 <0.2.0. The old range matched only the0.1.0-rc.8line under bare (registry-driven) resolution while the0.1.2-rc.1production dependency wave demands^0.1.2-rc.1peers, sopnpm installof the packed tarball failed withERR_PNPM_NO_MATCHING_VERSION(npm fell back to an ERESOLVE override). The range now resolves the0.1.2-rc.1line that the package actually ships against.
- All
@deepseek-ai/dsh-*runtimedependenciesand dev pins moved from the published0.1.2-alpha.5line to0.1.2-rc.1;@deepseek-ai/dsh-agent-spine-demostays on0.1.1-rc.2(no newer spine line is published). src/audit.ts:isUnmarkedHostVersionnow classifies the0.1.2-rc.xline as non-stamping (it ships the alpha.5 surface: the third append parameter isSurfaceIntentfor surface event types only). Previously0.1.2-rc.1fell through the rc bound and was probed as marker-aware, which re-enabled session-log audit, broke the per-turn budget fold, and risked appending unmarked verdict events.scripts/check-host-versions.mjs: a dev pin on the rc line now counts as covering the npm alpha line (rc outranks alpha within the same minor), keeping the host-compat job green after the bump.- Five-language READMEs: the harness table row now states
0.1.2-rc.1.
- Runtime
dependenciesmoved from the0.1.1-rc.2line to the published0.1.2-alpha.5line (15 packages);@deepseek-ai/dsh-agent-spine-demo(removed from the alpha.5 line) moved to devDependencies at0.1.1-rc.2— it is used only by the eval profile (eval/cordis.yml). - Dev pins
@deepseek-ai/cordis-plugin-loader ^1.0.3/@deepseek-ai/cordis-plugin-include ^1.0.7aligned with thecordis 4.0.2peer ranges. - Five-language READMEs: the harness table row now states
dependencies pinned to 0.1.2-alpha.5.
- Sync the five-language READMEs to the 0.1.2-alpha.5 facts; no behavior change.
- Align the devDependency pins to the published dsh 0.1.2-alpha.5 line and re-verify the adaptation claims; no behavior change.
- Host
0.1.2-alpha.3compatibility (no behavior change): dev/test pins bumped to0.1.2-alpha.3,dshWorkshop.compatibility.dshVersionslists0.1.2-alpha.3; theisUnmarkedHostVersionclassification (0.1.2.x→ unmarked, stay-write) and the audit docs are calibrated to the alpha.3 facts.
- The reviewer child no longer receives injected context (#21). The reviewer is built through the ordinary agent path, so the harness composed its steps the ordinary way: the workspace instruction files (
AGENTS.md/CLAUDE.md), the loop's runtime-context snapshot, and any context-injecting plugin the user has installed all entered the child ABOVE the reviewer prompt — repository-controlled text inside the component that decides whether a privileged operation is allowed. A new context firewall (src/isolation.ts) filters every reviewer step on the documentedagent/pre-stepseam ("reject a proposed step or replace the messages that enter it") down to an allow-list of message SOURCES: the reviewer's own prompt (user) and its own read-only tool results (tool). Everything else is dropped before the loop appends it, so it never enters the child's log or its model request. The listener is registered withprepend: trueso it wraps the whole waterfall and removes injections whichever listener added them; non-reviewer steps are returned untouched. Reviewer children are recognized by their announced prompt beforectx.subagents.starthas resolved their session id (the in-process driver wakes the child's loop whilestartis still resolving, so an id-only test would miss the first step) and by the latched id afterwards. - The reviewer prompt fences everything outside itself as untrusted transcript that grants no permission and overrides no verdict rule. This is defence in depth behind the firewall, and the only defence against a seeding provider's parent-history copy (
fork's completed-turn prefix), which is already the child's own log rather than a message entering a step. - The injected context is not provider-dependent. The same request measured under
reviewerProvider: forkandreviewerProvider: spawnproduced BYTE-IDENTICAL injected context (workspace instructions 3559 chars, runtime-context snapshot 843 chars, third-party git-status snapshot 691 chars): these producers inject fresh into any new agent session rather than arriving through the fork seed. Theagent/pre-stepsource filter is therefore the only thing that closes them, and it does so under either provider —reviewerProvider: spawnalone does NOT keep workspace instructions out of the reviewer.spawnaddresses the completed-turn seeding specifically; neither substitutes for the other, and the seeding produced no additional messages in either trace, so its practical impact is unquantified.
contextBudget.turnsdefaults to 2 instead of 0 (#21). Withturns: 0the reviewer received no transcript at all, so the only evidence for a user-authorized action was the calling agent's own self-report — and the reviewer's own verdict rule ("deny … when the evidence is insufficient") turned that into a denial of every user-authorized escalation. The new default is the open turn (which carries the pending call and usually the request that authorized it) plus the one before it;fixtures/config/config-full.yamlalready used 2.turns: 0remains a valid opt-out, and combining it with anyaipolicy now logs a warning at mount time (hasAiPolicy) instead of reading as a broken reviewer.- The context budget's character cap is spent newest-line first.
buildContextSectiontruncated the joined transcript from the front, which cut its tail — the open turn holding the evidence the verdict rules ask for. It now collects lines backwards from the newest until the cap is reached, so an over-budget transcript loses its oldest lines instead. A single line wider than the whole cap still yields its head. - The verdict cache is off by default as a consequence of the new
contextBudgetdefault:fingerprintForalready refused to key a transcript-dependent verdict ontool + argumentsalone, and that guard now fires with the shipped configuration. SetcontextBudget: { turns: 0 }to trade the reviewer's transcript back for same-fingerprint verdict reuse. - Host
0.1.2-alpha.2compatibility:Session.append's third parameter is nowSurfaceIntent(surface event types only — noignorableoption), so append can never stamp the audit marker; theignorablefield stays on the event envelope and the persistence read path still fails closed on unmarked unknown event types. TheisUnmarkedHostVersionclassification (0.1.2.x→ unmarked, stay-write) is unchanged — the audit docs are calibrated to the alpha.2 facts. - The call-id brand is derived locally (
src/call-id.ts) fromdsh-tools'ToolExecution['callId']instead of importingCallIdfrom@deepseek-ai/dsh-llm(renamedToolCallIdon host master), keeping the source green on both the published0.1.1-rc.2line and host checkout. - The client half no longer imports
@deepseek-ai/dsh-client-runtime(removed from the host):ClientContextis cordisContext,SessionIdcomes from@deepseek-ai/dsh-session/types, and the slots registry is accessed structurally (ctx.get('slots')). The@deepseek-ai/dsh-client-runtimepeer/dev dependency, clientinjectentry, and bundle external are dropped. - Peer lower bounds relaxed
>=0.1.1-rc.2 <0.2.0→>=0.1.0-rc.8 <0.2.0; dev/test pins bumped to0.1.2-alpha.2;@deepseek-ai/cordis^4.0.2;@deepseek-ai/schemastery^3.18.2. scripts/check-host-versions.mjsnow also watches the npmalpharelease line (the exact dev pins are its coverage), so a newer host alpha fails the gate before a publish.
- The five READMEs now say where the config actually comes from (#21). An
auto-review:block in~/.dsh/settings.yamlhas no effect and produces no warning — this plugin receives itsConfigfrom the row the loader mounts it with, the profile's cordis patch layer — and the failure looks identical to the reviewer simply denying. Some other DSH plugins additionally read the settings service, so the inconsistency is easy to trip over. The new "Where the config actually comes from" subsection states the one real source, warns that an id-targeted override replaces the WHOLE config row (droppingtoolsPolicyreturnsbash/writeto the schema defaulthumanand the reviewer stops running), and carries a complete working example.
- Host
0.1.2-alpha.1compatibility: master removed theignorableenvelope (42dc2a46c2) and fail-closes on unknown session event types, soisUnmarkedHostVersionnow also classifies0.1.2-alpha.1+ lines as unmarked, and an unresolvable peer version fails closed BEFORE the first append (the append probe now only runs for recognized marker-aware future lines) — a probe append would otherwise pollute the log and make the session unloadable.allowUnmarkedAudit: truestill opts back in.
- Test harness derives synthetic tool-call ids from
dsh-tools'ToolExecution['callId']instead of importingCallId(renamedToolCallIdon host HEAD), keepingtypecheck(checkout) and the published0.1.1-rc.2types both green.
- Declared the five client
@deepseek-ai/dsh-client-*services (dsh-client-connection,dsh-client-runtime,dsh-client-locale,dsh-client-ui-slots,dsh-client-ui-conversation) as optional peerDependencies (viapeerDependenciesMeta.optional) to match the clientinjectmanifest. Added an install note to runpnpm approve-buildsfor thekoffi/node-ptybuild scripts when pnpm reportsERR_PNPM_IGNORED_BUILDS.
- stdio MCP server export (
dsh-auto-review/mcp+ thedsh-auto-review-mcpbin): a standalone stdio JSON-RPC 2.0 / MCP server (newline-delimited JSON, one object per line) exposing two tools —review_action(deterministic deny on a matched never-rule or fail-closed, otherwise replay a cached verdict for an identical fingerprint) andcache_stats(cache counters + TTL). The newsrc/mcp/modules own only protocol dispatch; every decision lives in the standalone reviewer. - Same-fingerprint verdict cache with TTL: a repeated, identical tool action reuses its recent reviewer verdict instead of paying another second-model round-trip. The fingerprint is a SHA-256 digest of the tool name plus canonicalized call arguments (volatile keys stripped, keys sorted); only the digest is stored, so plaintext arguments never enter the cache. New config knobs control TTL and cacheability,
cache_statsreports hits/stores/live size, and the web panel shows the cache state.
- Renovate is enabled via the shared
dsh-plugin-kitpreset, and the five-language READMEs credit community contributors. No plugin behavior changed.
- Packed smoke install now resolves the next-tagged rc line: the
@deepseek-ai/dsh-*peer floor is raised to0.1.1-rc.2(the peer ranges already cover it), sopnpm packplus the smoke install no longer fails to resolve peers.
- dsh-eval assertion engine gains three structured assertion families (completing the evaluation-platform direction):
- Prompt regression (
expect.prompt): the rendered system prompt must match a committedbaseline(inline or viabaselineFromfile); any drift is reported as a side-by-side diff in the report, withallowedChangesregexes to whitelist intended edits. The baseline file is resolved before any agent runs, and a missing/unreadable file fails the suite loudly. - Stress metrics (
expect.stress): gates P99 step latency (maxP99Ms), worst time-to-first-token (maxTtftMs), and aggregate token generation speed (minTokensPerSecond), folded from per-step timing records (step/start→ first/lastassistant/chunk→step/end). - Bias radar (
expect.bias): per-category regex counts over the final output (categories), hardforbidpatterns, andmaxHits/maxCategoryHitscaps — the fairness (Bias-Radar) assertion. - Per-step timing is now collected into
trace.steps, and the assertion engine reports optional multi-linedetail(the prompt diff, the bias radar) which the Markdown report renders as a code block.
- Prompt regression (
- Five-language READMEs document the assertion families with a combined
prompt/stress/biasexample, a GitHub Action CI snippet, and the contrast withopenai/codex-research.
- Runtime
dependenciesanddevDependenciespin the exact0.1.1-rc.2line anddshWorkshop.dshVersionslists0.1.1-rc.2; the peer specs stay on the>=0.1.0-rc.8 <0.2.0range, which already covers0.1.1-rc.2. - The session-projection unit migrated to the rc2
ProjectionDefinitioncontract (stateSchema+wire: { viewSchema, view }+ the requiredstateVersion), and theautoReviewkey now declares both the clientSessionProjectionMapand the hostSessionProjectionStateMapentries. - The browser panel calls the rc2
remote.commands.executeshape (empty image list for the text-only/auto-reviewcommand). scripts/check-host-versions.mjsnow recognizes the0.1.1-rc.Nline family;pnpm-workspace.yamlexcludes the whole@deepseek-ai/*scope fromminimumReleaseAge; the compat workflow pins the rc2 CLI anddsh-base/dsh-headlessbundles.
- Peers and dev/test pins bumped
0.1.0-rc.7→0.1.0-rc.8(npmnextmoved; the batch rc8 compatibility release). Peer specs now use the>=0.1.0-rc.8 <0.2.0range form; runtimedependenciesanddevDependenciespin the exact0.1.0-rc.8line, anddshWorkshop.dshVersionslists0.1.0-rc.8. isUnmarkedHostVersionbound extended to0.1.0-rc.8: the rc.8 release still drops theignorableenvelope marker (the stamping fix exists on harness master only), so rc.8 hosts are detected BEFORE the first append and session-log audit degrades to the in-memory mirror instead of polluting the log with an unmarkedautoReview/*event. Docs (five-language READMEs, AGENTS.md) and the README compatibility table updated to the rc.8 line.scripts/check-host-versions.mjsnow accepts both exact (0.1.0-rc.N) and range (>=0.1.0-rc.N <0.2.0) peer pins and fails when they no longer cover the newest rc line published by@deepseek-ai/dsh(onlatestornext).
- The circuit breaker's deferred abort-turn
setTimeout(…, 0)is now tracked by the runtime and cleared through a teardown effect when the plugin fiber unloads — an unload inside the macro-task window can no longer fire a staleagent.cancelagainst a disposed composition.
- rc session-corruption fix (the #918 report). On hosts whose
Session.appendpredates theignorableenvelope-marker surface (every released rc line through0.1.0-rc.7silently drops the options bag — the stamping fix exists on harness master only),autoReview/*audit events used to land in the session log WITHOUT the marker — stricter hosts then refused to resume those sessions (SessionFormatUnsupportedError). The runtime now detects such hosts BEFORE polluting a log (installed-peer pre-check, then a probe of the first appended envelope's return value), disables session-log audit with a one-time warning, and degrades to an in-memory audit mirror: marker-free deny/fallback/circuit feedback, in-memory budgets, circuit breaker,/auto-review on|offoverride andapprovefeed. Marker-aware hosts (a future release that stamps the marker, or any unresolvable version that probes clean) keep the full event-based audit unchanged.allowUnmarkedAudit: trueopts back into the old behavior on unmarked hosts (deliberately dangerous), and already-polluted logs can be repaired withscripts/repair-session-logs.mjsfromdsh-permission-rules(its default target set now coversautoReview/state,autoReview/verdict,autoReview/circuit,autoReview/override,autoReview/rejection). /auto-review statusreports the disabled-audit notice on unmarked hosts.- The browser half pins the api-remotes
commandsRemote namespace shape locally (the ambient namespace merge is fragile across strict package-manager copies on rc.7).
- Peers and dev/test pins bumped
0.1.0-rc.6→0.1.0-rc.7(npmlatestmoved); the full gate passes against the rc.7 peers, anddshWorkshop.dshVersionsnow lists both.
- New
src/audit.tshost-capability module (isMarkedAuditEvent,isUnmarkedHostVersion,peerSessionVersion) shared by every audit path; unit-tested intest/audit.spec.ts, and the full degraded path is covered bytest/audit-degradation.spec.ts(unmarked-host default safety, in-memory breaker/override/approve, the unversioned-host probe).
- dsh-eval agent evaluation engine (
dsh-auto-review/eval+ thedsh-evalCLI): a YAML case DSL (input, structuredexpectblock — tool-call sequence with argument matchers, per-tool results, final-output matchers, turn outcome, token budget — optional second-modelreviewblock, per-casetimeoutMs/model/tier, workspacefiles/seedFrom, batch suites), one isolated headless session per case (fresh scratch workspace, official Minimal persona as the baseline system prompt, approvalnever, workspace-write sandbox), tool-call-trace collection from the session event log, and Markdown/JSON reports with token stats, per-assertion explanations, replayable session-log artifacts, and a CI gate (exit 0 only when every case of every suite passed). - The second-model review is a supplementary assertion layer over the same run, reusing the approval reviewer's subagent seam (the mounted runtime is now published as
ctx.autoReviewRuntime, so the eval engine reads the exact mounted configuration instead of a driftable copy). - Shipped evaluation composition
eval/cordis.yml, the self-regression suiteeval/cases/demo.yaml(output assertion, tool-trace assertion, second-model review — three cases over this repository), thebin/dsh-eval.mjslauncher, and the./eval/./eval-clipackage exports. - CLI flags:
--provider,--model,--tier,--timeout-ms,--concurrency,--out,--workspace-root,--keep-workspaces,--no-markdown,--no-gate,--review-provider,--review-model,--review-timeout-ms. A case whose model or timeout resolves from nothing fails the suite loudly (no hardcoded defaults); runs are cancellable (AbortSignal+ SIGINT/SIGTERM) and the worker pool respects the concurrency cap.
- README (all five languages) documents the evaluation engine with a case example, a CI integration snippet, and the contrast with codex-research.
- The unit suite covers the eval engine end to end (DSL, assertion engine, trace collection, runner, reports, CLI).
package.json#dshWorkshopmanifest (omdsh-workshop-package/v1): transactionalprofile-bundleinstall declaration,harness-profileintegration protocol, restart-profile lifecycle, the/auto-review statusnamed capability, and author-run install/remove evidence underdocs/omdsh-evidence/— the omdsh hub intake surface (author declarations only; verification stays with the hub).
- Hard-disable feedback: a
never-policy rejection (risk rule or tool-policy table entry) now records a log-onlyautoReview/rejectionevent (with the matched rule/entry as the reason and the correlatedapprovalId) and injects a[auto-review-never]marker text plus the deny guidance into the denied tool result — the model learns the action is hard-disabled instead of retrying it. The invariant companion validates the rejection chain (marker ⟺ event, one decision perapproval/asked,decidedagreement); the panel and/auto-review statuscount never rejects. /auto-review statusreports a tripped circuit breaker (kind, count, action) when one is active in the turn.- The web review panel's switch gains explicit on/off buttons (they execute
/auto-review on|off).
- Circuit-breaker defaults are now reachable:
windowDenies10 → 6 andwindowSize50 → 10, so the window mode (6 of the last 10 verdicts) can trip under the defaultmaxReviewsPerTurn: 10even without a 3-deny run (the old 10-in-50 window could never trip first). Explicit configs are unaffected. - The projection unit is now built per mount (
makeAutoReviewProjection(enabledByDefault)), so the panel's initial switch state follows the resolvedenableByDefaultinstead of a hardcodedtrue;stateVersionbumped to 2 (also for the newneverRejectswire field). - tsdown
external/noExternalmigrated to the non-deprecateddeps.neverBundle/deps.alwaysBundle. - Published on npm; the README install section now lists the npm channel first.
- The panel's switch row no longer renders the localized "ON" text as its label (it now reads "State: ON/OFF").
- Web review panel: a session-header action in the Web GUI showing the session's switch, both per-turn budgets, cumulative statistics, the circuit trip, recent verdicts, and one-shot approve buttons for recent denials (they execute
/auto-review approve [n]).- Host: an
autoReviewsession projection (src/projection.ts+src/projection-types.ts) folds the log-onlyautoReview/*events into one wire-JSON value; it registers whenever the host provides the session-projection capability (the web profile does), so non-web mounts keep working unchanged. - Browser: a client module (
dsh.clientdeclaration +./clientexport) registered on theconversation.session.header.actionsseat; it reads only the projection whole value — the raw session event stream never reaches browser plugins. - The browser bundle follows the shell's client-bundle handshake (
window.__ModuleLoader__.load), with platform modules external and everything else inlined.
- Host: an
CHANGELOG.mdis now shipped in the tarball.
fallbackPolicy: allow-readonlyis renamedallow-once(the grant is unconditional, never "readonly"); the old spelling is rejected loudly at load. Migration: rename the key.- The shipped bundle patch no longer AI-reviews
editby default (bash/writeremain); in-place modification now reaches the human chain unless configured explicitly.
- Risk-level policy (
riskPolicy): anallowverdict whose risk exceedsmaxAutoAllownever settles the request —onHighRisk: delegatecontinues the chain,denyrejects. Recorded asescalation: 'risk-policy'on the verdict. - Rejection circuit breaker (
circuitBreaker): trips onconsecutiveDeniesconsecutive denials orwindowDenieswithin the lastwindowSizeverdicts of a turn; later requestsdelegate,reject(auditable[auto-review-circuit]feedback), orabort-turn(warning injected + agent cancelled). Recorded as a log-onlyautoReview/circuitevent, once per turn. - One-shot human override:
/auto-review approve [n]records a single-useautoReview/overridefor the n-th most recent denial; the next same-tool review withinoverrideTtlMscarries the authorization as reviewer context. - Compact reviewer transcript (
contextBudget): the reviewer sees a bounded tail of the session's presented messages and tool results. - Ruling policy text (
reviewerPolicyText): Codex-style Markdown policy injected into the reviewer prompt (template atfixtures/config/policy-template.md). - Anti-circumvention deny guidance (
denyGuidance) appended to every injected deny reason. - Risk-rule fields:
riskRules[].fieldselectsreason(default) |toolName|arguments(redacted presented call arguments). /auto-review statusreports cumulative session statistics (allows/denies/fallbacks, mean duration, recent verdicts).- Provider capability precheck (
outputSchema/toolFilter) fails with a clear message instead of a generic start rejection;reviewerTools: []fails loud at load. - UI language config (
language: 'en' | 'zh') for the/auto-reviewcommand output.
- Fallback-policy switch and docs now use the honest
allow-oncenaming.
- Failure-classification race: a timeout or user cancellation surfaced as a subagent start/run rejection (e.g. the fork driver's pre-publication abort) is now classified
timeout/cancelledinstead of the genericunavailable, keepingautoReview/verdict.outcomeconsistent with the service'sapproval/decided. - Invariant gaps closed: the companion now enforces one verdict per
approval/askedand verdict/decidedoutcome agreement (previously claimed in JSDoc but not implemented), and validates the new fallback marker. - Per-turn budget split:
maxReviewsPerTurnnow counts real AI verdicts only; the newmaxFailuresPerTurnbudgets reviewer failures separately, so a broken reviewer no longer eats the AI-decision budget (and stops being retried after the failure budget is spent). - Request reason hygiene: the request
reasonis truncated toreasonMaxCharsbefore entering the reviewer prompt (it is also labeled as the calling agent's self-report, evidence only).
- Fail-closed feedback: a fallback rejection now injects an auditable failure text (
[auto-review-fallback]marker, reviewId-linked) into the denied tool result, so the agent learns why it was rejected instead of retrying the same escalation blindly. /auto-review statusreports both per-turn budgets (AI verdicts and reviewer failures).- CI workflow (
typecheck/test/build/verify/pack) plus a host-version watch that fails when the exact-pinned@deepseek-ai/dsh-*peers lag the npmlatest.
- Removed stale demo probe scripts and capture artifacts; the tests badge is now a live GitHub Actions badge.
autoReview/stateandautoReview/verdictare appended with the envelope'signorable: truemarker so any harness build can load the log.
- Git-install channel hardening: repo-shipped
pnpm-workspace.yamldeclaresallowBuilds: { esbuild: true }for the isolated prepare environment.
- Initial release:
approval/requestanswerer with second-model read-only reviewer subagent, structured verdict schema, fail-closed fallback, anti-recursion, per-tool/risk-rule policy routing, deny-reason feedback, session-log audit (autoReview/verdict), invariant companion, and the/auto-reviewsession command.