Skip to content

Releases: kuun993/dsh-loop-engine

dsh-loop-engine 0.2.1-rc1

Choose a tag to compare

@kuun993 kuun993 released this 18 Oct 23:36

dsh-loop-engine 0.2.1-rc1

Serves the 0.2.1 harness line (>=0.2.1-rc.1 <0.2.2-0) on the same build that still covers the 0.1.5, 0.1.7 and 0.2.0 lines.

Why this release exists

The 0.2.1 line deleted @deepseek-ai/dsh-invariants outright and republished the JSONL durability backend as a namespace plugin with no default export. Neither renames a method, but the first one is fatal: dsh's boot gate skips a whole bundle whose @deepseek-ai/dsh-* peers do not satisfy the running version. Keeping that peer would have unmounted the entire plugin rather than degrading one feature — the symptom is a missing "Loop engine" settings section, not an error.

Changes

  • src/invariant.ts now declares the invariant registry's shapes locally (the same technique driver-core/host-servers.ts already used), so one build mounts on every line. inject = ['invariants'] is unchanged, which keeps the companion pending where the service no longer exists.
  • @deepseek-ai/dsh-invariants is removed from peerDependencies and devDependencies.
  • tests/helpers/harness-generation.ts gains jsonlSessionPersistence, handing ctx.plugin whichever form the running generation publishes. That single adapter turned 108 red specs green.
  • .compat/021/ + vitest.config.compat021.ts add the fourth line's isolated dependency set.
  • Peer ranges extend to >=0.2.1-rc.1 <0.2.2-0; devDependencies follow.
  • Docs: compatibility.md gains §4.5 (the two structural changes) and an upgraded §6 checklist; source-checkout.md drops the dsh-invariants shim; both READMEs and architecture.md §5 are updated.

Verification

  • pnpm run typecheck and pnpm run build clean
  • pnpm run test:coverage: 68 files / 1298 specs, per-file 100%
  • All four compat lines green: 0.1.5 (1296 + 2 skipped), 0.1.7, 0.2.0, 0.2.1 (1298 each)
  • dsh web boots clean from both the source checkout and the published dsh@0.2.1-rc.2 runtime: the managed block disables agent-loop and command-goal, all four hosted presets are authored from the new standard, and the browser half serves dsh-loop-engine/client.js
  • The desktop build (Electron shell) verified end to end, with data-loop-engine reflected onto <html>

Known limitation

dsh-usage-overlay still declares 0.1.7-only peer ranges and is skipped by dsh on 0.2.1; that is a separate package.

0.2.0-rc1 — harness 0.2.0 line, and images for Claude Code

Choose a tag to compare

@kuun993 kuun993 released this 29 Sep 11:59

Serves the harness 0.2.0 line on top of the 0.1.5 and 0.1.7 lines, and hands the step's images to Claude Code natively.

0.2.0 line (>=0.2.0-rc.1 <0.2.1-0)

  • Only the browser half needed code: 0.2.0 turned TurnProcessNodeView into a settled-turn summary and moved the live "深度求索中…" indicator into RunningStatus, a [data-chat-running] node that exists exactly while the session runs. The plugin paints that node instead, recolours the two aliases it resolves its colours from, and hides the harness's own whale there (a hosted engine under a DeepSeek mark is the one thing that row must not say). The per-row mark is deliberately not stamped on this line — the remaining button[data-turn-process] rows are all settled turns.
  • The node half needed no code: the 0.2.0 diffs to the surfaces this plugin consumes are additive.
  • Each line gets its own isolated dependency set and compat run (.compat-015/, .compat-017/, .compat-020/).

Claude Code images

  • The driver hands the SDK a one-message async iterable — the transcript plus one base64 image block per image the step carries — and only for steps that carry images; text-only and slash-command steps keep the plain string prompt. The placeholder line in the transcript (image identity + read-only host path) stays the invariant for earlier images.
  • Verified on a real 0.2.0 host: a kimi session and a claude-code session each answered ATTACHED:1 to an image prompt with zero tool calls, so the picture reached the model rather than a path.

Gates: typecheck, per-file 100% coverage, the three compat suites (1263 tests each) and the build all pass.

dsh-loop-engine 0.1.7-rc5

Pre-release

Choose a tag to compare

@kuun993 kuun993 released this 27 Sep 01:30

For harness 0.1.7-rc.2 (the 0.1.5 line stays supported by the same artifact).

Added

  • childIdleMs (composition entry, default 0 = off): shuts down an IDLE session's persistent child process (Kimi's kimi acp, Codex's app-server) and lets the next step spawn a fresh one. One child per session is what saves a spawn per step, but nothing releases an agent at turn end, so a session left alone kept paying for it at one process per SESSION. Only the child is closed -- the agent, the session, and the page are untouched, so no session/disposed and no reload. Pi and Claude Code spawn per step and are not affected.

Verified

  • End to end against the real kimi acp CLI: with childIdleMs: 5000 the child is gone ~5s after each turn and is spawned again by the next one, both turns answering normally.
  • pnpm run typecheck, test:coverage (per-file 100%), build, and the 0.1.5 compat job all green.

v0.1.7-rc4

v0.1.7-rc4 Pre-release
Pre-release

Choose a tag to compare

@kuun993 kuun993 released this 26 Sep 11:16

Hosted engines can finally be selected on the 0.1.7 line.

The presets were being written where nothing reads them

The plugin authors one stripped copy of the deployment's standard preset per hosted engine as a directory under $DSH_HOME/.agent-presets/. That is the 0.1.5 mechanism, and 0.1.7 deleted it: the dsh-agent-presets package is gone, a preset is now a composed @deepseek-ai/dsh-agent-preset row, and no harness code reads that directory any more.

Measured on a live 0.1.7-rc.2 web host with the plugin installed and authoring reporting success, the roster answered ["standard","ptc","minimal","cordis"] -- none of the four loop-engine-*. So session/create {agentPreset:'loop-engine-kimi'} failed with Unknown agent preset: loop-engine-kimi, and every session fell back to the harness loop. That single cause accounts for the whole family of preset errors: hosted engines could not be selected at all.

agentPresets.register() is not the fix -- activate() requires entry OBJECTS and every public read exposes only ids, text, or an inventory without config. So the modern line writes the presets where the harness looks: four - insert: rows of @deepseek-ai/dsh-agent-preset in the profile patch the plugin already manages, each config.plugins being the source composition minus the stripped rows. That reuses the existing textual strip (no YAML parser, no new dependency), is rewritten atomically and idempotently like the managed block, and falls back to plugins: [] rather than a valueless key that would fail the whole profile boot. The 0.1.5 line keeps the directory authoring and gains no rows.

Verified end to end on 0.1.7-rc.2

Boot a web host, session/create {agentPreset:'loop-engine-kimi'} succeeds, the session records that preset, its request/header provider is external (the plugin's hosted route -- the Kimi driver drove it), the model answers, a Bash tool call is attempted and recorded, and the durable log passes the harness's own V4 relationship validation with zero projection mismatches and zero duplicated results. The YAML shape was separately proven loadable with the harness's own loader (entryListSchema + applyEntryPatches + entryListProblem).

Both suites green: 62 files / 1181 cases on 0.1.7 (per-file coverage still 100%), 1179 passed plus the 2 modern-only tests skipped on the 0.1.5 line. No peer-range change, so nothing moves for users on either line.

v0.1.7-rc3

v0.1.7-rc3 Pre-release
Pre-release

Choose a tag to compare

@kuun993 kuun993 released this 26 Sep 10:19

One fix on top of 0.1.7-rc2, for harness 0.1.7-rc.2 boots.

Engine preset authoring gave up on a loader race

The plugin authors one stripped copy of the standard preset per hosted engine at boot. It read that source preset from the roster as soon as the agentPresets service existed, and retried only while the service was absent. But a preset row is itself a plugin injected with agentPresets: standard enters the registry when that row's own callback runs, which may land after this plugin's. A read landing in that gap throws Unknown agent preset: standard, and the boot's authoring was abandoned -- one error in the startup log and no loop-engine-<engine> presets on disk, so every session on a hosted engine would fail to mount its preset.

Harness 0.1.7-rc.2 loses that race where rc.1 won it (its loader re-derives skipped bundles and its web patch gained rows, which moves the callback order), which is why this arrived as an rc.2 regression.

The bounded window now covers the read as well as the service (20 attempts x 100 ms). Failures stay advisory: the error is logged once after the last attempt, and a failed pass stays un-memoized so a later engine switch starts a fresh one.

Verified on a real 0.1.7-rc.2 boot with the managed preset directories removed first: all four presets are re-authored and the startup log carries no authoring failure. Two new tests pin the shape -- a roster whose first read rejects and then answers authors without reporting a failure, and a roster that never answers reports exactly once.

No API or peer-range change, so nothing moves for users on either harness line.

v0.1.7-rc2

v0.1.7-rc2 Pre-release
Pre-release

Choose a tag to compare

@kuun993 kuun993 released this 26 Sep 09:55

Fixes for the durable session logs the hosted drivers write. No API change and no peer-range change: the union stays >=0.1.5-rc.1 <0.1.6-0 || >=0.1.7-rc.1 <0.1.8-0, so users on either line are unaffected.

Session V4 log correctness

A hosted engine's session log is authored by this plugin, and Session V4 validates its relationships on every load -- so one malformed event makes a whole session unopenable rather than merely untidy. Three writers got that wrong:

  • Projected tool-call mismatch (all four drivers). The assistant/message tool-call block kept the engine's spelling (Bash, Read, command_execution) while the tool/call event carried the projected dsh spelling (bash, read); V4 requires them byte-identical. Measured on a real log: 87 of 102 calls disagreed, all on name. Each driver now normalizes once and feeds the same values to both writers.
  • Duplicate tool/result (claude and pi). The Claude Agent SDK delivers the same result in two user messages, and Pi reports every execution twice (tool_execution_end, then again in turn_end.toolResults); neither driver de-duplicated, so a second result arrived for an already-settled call and V4 rejected it as has no advertised tool lifecycle. One real log had 269 doubled calls. Both drivers now keep a per-step settled-call set; kimi and codex were audited and need nothing.

Chat turn-status row

  • The 0.1.7 row is a button[data-turn-process] that stays on screen after its turn ends, one per turn, so a session-level mid-turn gate alone re-painted every finished row whenever a later turn ran. The row selector is now button[data-turn-process]:disabled [class$="_label"] -- :disabled is ui-chat's "this turn cannot be collapsed", true only for the live row.

Repair for logs already written

scripts/repair-session-logs.mjs (repo-only, not shipped) repairs the artifacts older builds produced, on v3 and v4 logs, and verifies itself against the harness's real migration and V4 validation. On the machine it was written for it repaired 30 v3 + 3 v4 logs, including a 2 221-event session whose 269 duplicated results are gone.

v0.1.7-rc1

Choose a tag to compare

@kuun993 kuun993 released this 25 Sep 07:20

One artifact that runs on both harness lines: 0.1.5-rc.1 .. 0.1.5-rc.3 and 0.1.7-rc.1.

Peer ranges are now >=0.1.5-rc.1 <0.1.6-0 || >=0.1.7-rc.1 <0.1.8-0, and a runtime probe (src/compat.ts) picks the right shape for the generation it booted on.

Compatibility fixes

  • Remote contribution codec shape. The strict validator moved from { typeSymbol, schema } to { typeSymbol, create: () => schema }; the descriptor now carries both, so either registry accepts it. Without this the session-scoped Remote never mounted and the composer's engine picker sat on 「读取中」 forever, while the settings page (a different path) looked fine.
  • Agent-presets roster read. Prefer readDocument (0.1.7) and fall back to read (0.1.5).
  • Protected system/message head. A session that started on a hosted engine and later ran the harness loop used to fail the V3→V4 migration (system/message requires a protected first surface head). Each driver now writes one empty-content head on the session's first step, only while no surface event exists yet. Gated off on 0.1.5, which cannot drive that event.

Chat turn-status row

  • One row selector per harness generation: [class$="_turnStatus"] (0.1.5) and button[data-turn-process] [class$="_label"] (0.1.7, whose row is a button and whose CSS-module classes are [hash]_[local]).
  • The gradient sweep is re-supplied by the plugin: 0.1.7's row is plain tertiary text.
  • The whole sheet is now gated on a mid-turn attribute (data-loop-engine-running, from session.running), so the 0.1.7 row stops painting once the turn it belongs to has ended — that row stays on screen afterwards as the collapsed 「用时 X 秒」 summary.

Verification

The same suite is run against both generations: 62 files / 1152 cases, each green. 0.1.7 runs through pnpm run test:coverage (per-file 100%); the 0.1.5 line runs through vitest.config.compat015.ts against the isolated .compat-015/ dependency set. Both pnpm run typecheck and the build are green, and the published bundle was verified as served by running dsh web on both generations.

0.1.5-rc5

0.1.5-rc5 Pre-release
Pre-release

Choose a tag to compare

@kuun993 kuun993 released this 24 Sep 10:21

Hand a dsh model whose provider declares no wire protocol over instead of refusing it, so a hosted engine reaches dsh's endpoint rather than quietly using its own.

  • Admission for the model handover now needs only a non-empty baseURL and a resolvable credential; api is a hint, not a gate. A declared protocol always wins, a shipped route is filled in by a one-entry table (deepseek-official -> openai-completions), and anything still unknown hands over api: undefined for each engine to degrade on its own.
  • codex: the provider override now carries name (without it app-server died at config load), and openai-completions no longer maps to the chat wire upstream removed -- writing it is now a fatal config error. Both failures reached the driver only as "codex app-server process exited unexpectedly".
  • pi alone cannot express a protocol-less provider, so its driver drops such a handover rather than writing a models.json pi rejects.
  • Verified against a live endpoint: the request lands on <baseURL>/responses and the gateway answers. See docs/optimization-backlog.md BL-01 and BL-19 for what remains unproven.

Harness peers stay at 0.1.5-rc.2.

0.1.5-rc4 (harness 0.1.5-rc.2)

Pre-release

Choose a tag to compare

@kuun993 kuun993 released this 24 Sep 02:56

Targets harness 0.1.5-rc.2 — every harness peer is pinned to exactly that release.

What changed since 0.1.5-rc3

  • The loop engine is a per-session choice. One router factory owns the process's single agent-factory slot and dispatches each session to the engine its agent preset names: the built-in in-process loop, or one of claude-code, codex, pi, kimi. Sessions on different engines run concurrently — one chat can be on Codex while another is on Kimi.
  • Any open, idle session can move to another engine. Between two hosted engines the move is an in-place handover: the same Session object, its store entry and its write handle pass to the successor, so the session never leaves the registry and the page never disconnects. A move involving in-process cannot be handed over in place (the harness loop neither yields a live session nor adopts one it did not create): the host releases that session's agent and the page reloads, after asking first. The dsh web process is never restarted. A move is refused while a turn is running, and subagent sessions cannot move.
  • One shared model route. Every hosted engine logs the same provider label, external, which advertises exactly one entry, default. The model menu no longer shows one identical group per engine (the browser catalog is built without a session and shared across the whole host generation, so it cannot be filtered per session).
  • The model selection is handed to the engine — endpoint and credential included. A session that selects a real dsh model has that model's endpoint, protocol and credential resolved from dsh's own configuration (provider directory → settings namespace → the credentials service; never process.env) and passed through the engine's own surface: Claude Code's ANTHROPIC_BASE_URL/ANTHROPIC_AUTH_TOKEN, Kimi's KIMI_MODEL_* environment, a plugin-owned Pi agent directory plus --provider/--api-key, and Codex's -c model_providers… plus its credential environment. Whether an engine can speak a given protocol is the engine's business — a refusal is reported, not swallowed. default, no selection, or an unresolvable endpoint injects nothing.
  • A session's model seat now follows its engine, written where the host reads it, and picking the deployment default is contained: session.selectModel also saves the pick as the deployment default, which the router now rewrites for sessions built on the harness loop. The Codex driver's parsed-but-unused config.env is consumed as of this release.
  • Both READMEs were slimmed (English 471 → 75 lines) with their runnable procedure moved into docs/source-checkout.md.

Known open items

docs/optimization-backlog.md carries them, numbered for incremental work. The four P0 items all need a real machine: Codex's -c parsing and wire_api compatibility, Kimi's environment-defined model appearing in session/new's configOptions, Claude Code's acceptance of the endpoint's request path, and Pi's tolerance of a minimal models.json.

Install

dsh plugin --profile web add dsh-loop-engine

See the README for requirements, usage, and the known limitations, and docs/ for the details.

v0.1.5-rc3

v0.1.5-rc3 Pre-release
Pre-release

Choose a tag to compare

@kuun993 kuun993 released this 20 Sep 07:30

dsh-loop-engine 0.1.5-rc3

Retargeted to harness 0.1.5-rc.2 (dsh-v0.1.5-rc.2).

  • peerDependencies / devDependencies: harness pins 0.1.5-rc.1 → 0.1.5-rc.2
  • pnpm-lock.yaml and the pnpm-workspace.yaml release-age excludes refreshed
  • README (en/zh) compatibility table updated

Harness 0.1.5-rc.2 is a client-UI/docs backport that leaves every seam the drivers consume untouched, so no driver code changes were required. Verified with pnpm run typecheck, pnpm run build, pnpm run test (44 files / 861 tests), and pnpm run test:coverage (100%).

Requires harness 0.1.5-rc.2.

dsh plugin --profile web add dsh-loop-engine@0.1.5-rc3