Commit Graph
67 Commits
Author SHA1 Message Date
Tianyi Cui b023791481 fix(headless): retain live failures without a later terminal
A child completion can wake the parent after its first completed turn. If
that summary cannot append turn/start or turn/end, AgentLoop reports an
agent/error and contains the rejected driver. Whole-agent idleness then
resolves, but the headless runner previously reused the older completed
terminal and exited successfully, sometimes printing the older answer.

Retain failures from the exact owned Agent with the current Session log
offset. Only a later terminal committed by the same Session clears that
failure. After work and persistence drain, fail the run before emitting a
final answer when no such terminal exists. Dispose both observers with
the run. Ordinary durable error outcomes and later successful recovery
continue to use the log's final terminal.

Add real AgentLoop regressions for missing summary start/end commits in
plain and JSON output, ordinary hook failures with and without recovery,
recovery after persistence failures, and unrelated Agent errors. Update
the bilingual exit contract and generated event consumer matrix.

Validation: 106 headless package tests; 100% runner statements, branches,
functions, and lines; 9 focused terminal-failure tests after the typed
append wrapper correction; package TypeScript build; full lint including
host build; 21 quick documentation checks; 42 doc-sync checks; diff check.
2026-10-06 02:53:04 +08:00
Dudu-0223 307c75011b Merge master and reconcile declarative presets with activations 2026-09-21 16:41:53 +08:00
Turtle d1e22a7e24 feat(preset): declare Agent compositions in profile YAML (#4569)
* feat(preset): declare Agent compositions in profile YAML

* fix(preset): share scope binding cleanup and refresh module graph

* fix(preset): use host syntax colors in the YAML editor

* fix(preset): write new profile declarations as block YAML

* fix(preset): align eager activation with browser and worker surfaces

* fix(ui-agent-preset): retain the existing preset card layout

* fix(preset): keep rows waiting for a Host service mounted and re-audit after settlement

A preset row whose Host provider was still activating at declaration time
was reported as waiting for a missing service, and the registry recorded the
definition as broken for the rest of the process. Split the mount audit into
failed and pending rows: failures and root-realm leaks still reject the
mount, while pending rows stay mounted and every read and binding re-audits
them after the Host Loader tree settles.

Remove the accidental self-dependency of @deepseek-ai/dsh-web-app and its
lockfile self-link. Archive the copy-only preset authoring note, which the
declarative-preset note fully supersedes, and update the ralph note's
restore recipe to the profile-patch mechanism.

* chore(lint): re-key and prune the unknown-cast baseline after the master merge

* refactor(web-app): ship each Agent preset as its own bundle patch file

`dsh.bundle.patch` accepts an ordered file list, so the four shipped preset
declarations move out of the 1000-line web patch into
`presets/<id>.patch.yml`, one insert per file, applied after the host patch.
The host patch keeps the preset registry row. Gates and test scaffolds that
read the web layer resolve every patch file through `bundlePatchPaths`
instead of one hardcoded path.

* refactor(preset): drop the save notice injected into live Agents

The notice duplicated the save result the Web editor already shows, and
after the producer-owned message sources it would have needed its own
persisted source kind. Saving now only persists and reports the activation
diagnostic; the persistence catalog is unchanged from master.

* docs: regenerate the module graph after dropping the registry's llm dependency

* test(ui-agent-preset): cover the guide dialog's keyboard handling on the declarative section

* fix(preset): address the remaining review comments

Apply the lowercase id rule only when creating a declaration, so presets
declared with other ids stay editable. Export `isBuiltInPreset` from the
display fold and key the client card groups on it. Remove the dead `error`
and `retry` locale keys, the JSDoc orphaned by the `trust` removal, the
`trust` fields in the plugin-inventory test fixtures, and the stale
`dsh-agent-presets` package name in comments and READMEs.

* refactor(preset): make the Web preset page view-and-select only

Remove the child-plugin YAML editor, the `agent_preset` tool and the
registry's read/save Remote methods. Presets are declared and changed only by
bundle patches; the settings page keeps the roster, the new-task default and
the mode help, and its question icon explains that and offers to start a
Creator-mode task. The `editing-cordis-compositions` skill and the Creator
persona describe the bundle-based path, and the authored skill-load Session
and Web snapshots are refreshed for it.

* docs(skill): make the composition skill self-sufficient for preset authoring

State where the shipped declarations live, list every declaration field,
inline a complete minimal bundle and an override example, and name the
verification calls, so Creator mode no longer reads DSH package sources to
infer the format. Refresh the authored skill-load Session for the new text.

* test(tools): drop the removed agent_preset tool from the catalog expectation
2026-09-21 08:24:18 +00:00
Dudu-0223 a1da7bcfb9 fix(subagent): close settlement delivery gaps 2026-09-21 16:19:07 +08:00
Dudu-0223 6419c0eb21 chore(subagent): integrate latest master and typed test fixtures 2026-09-19 20:44:43 +08:00
Dudu-0223 7e8591de1c refactor(subagent): rebuild activations on master with shared catalog 2026-09-19 20:39:43 +08:00
Tianyi Cui 580bdc7258 refactor: remove redundant unknown casts 2026-09-19 20:13:58 +08:00
_KermanandTianyi Cui f4a32dbd0a refactor(llm): flatten tool results and validate native V4 sessions
Represent tool results as tool-role messages and migrate released wrappers
without losing outer own JSON fields, call identity, error state, or writer
field order. Refuse nested or wrapper-only information that has no lossless
current representation before successor publication.

Validate current event relationships directly instead of projecting V4 data
back into V3. Keep lifecycle, PTC, title/command, compaction, system-field and
retired-syntax checks on catalog restoration and ordinary JSONL opens. The
Worker JSON equality helper must inspect own keys so it cannot admit PTC
arguments that Node rejects.

Keep the source role change, required native admission and consumer fixture
updates in one complete review layer. Explain shared-type changes in the
persistence record and the native reader rationale in its existing note.
Use checked branded fixture identities and discriminant narrowing instead
of unknown double casts; fixture values and assertions remain intact.

This recomposes the original tool-role work by _Kerman with the independently
validated migration and admission repairs by Tianyi Cui. Frozen generations
and snapshot input version selection are retained.

Co-authored-by: Tianyi Cui <[email protected]>
2026-09-18 19:05:32 +08:00
Dudu-0223 3d852a1cea refactor(subagent): unify local and external activation lifecycles 2026-09-17 14:54:10 +08:00
Tianyi Cui caa69608fb refactor(sandbox): await cancellable preparation in process consumers 2026-09-12 15:03:02 +08:00
lsdsjy 71b2cc80aa fix(headless): waive the deferred Session history read
Master's `deprecate-synchronous-session-event-reads` marks `Session.eventAt`
deprecated and rejects new production calls. `liveEvents` predates that decision
and still needs the ordered log to reconstruct the preset a session runs under,
so carry the repository's standard line-scoped waiver for deferred migration.
2026-09-10 19:32:54 +08:00
lsdsjy 79bd986fa7 Merge remote-tracking branch 'origin/master' into HEAD 2026-09-10 19:16:53 +08:00
lsdsjy 3be1ea10b9 refactor(headless): move the runner process hooks out of the package entry
Address the Code Entropy follow-up on d9c4f53758: the runner entry still
expanded its process-substitution object with a `readStdin` hook used only by
tests. Move that seam to `src/runner-internals.ts`, mirroring
`src/startup-internals.ts`, so neither entry grows package API for tests.
2026-09-10 19:13:05 +08:00
lsdsjy 6d05765405 refactor(headless): keep the startup process seam out of the public entry
Address the Code Entropy finding on f67d722243: the `./startup` entry exported
`internals` only so tests could substitute process facts, which added avoidable
package API. Move the seam to `src/startup-internals.ts`, which no package
export names, and have the provider and its tests import it from there.
2026-09-10 17:29:15 +08:00
lsdsjy 9b330b653f docs(headless): correct the adopt-only wording and lockfile attribution
Address the ds-review-bot suggestions on 4099b84ee8:

- The post-resume comment scopes the write lease to other processes, so it no
  longer contradicts the post-idle re-check that defends against a same-process
  overlay writer.
- The Agent Note attributes `pnpm-lock.yaml` to the new
  `@deepseek-ai/dsh-session-query` workspace link instead of the shared chunk,
  which `scripts/check-workspace-constraints.ts` and the manifest publish.
- The package summary keeps "leaves nothing running behind", so it does not
  imply the runner leaves no persisted Session behind.
2026-09-10 16:52:43 +08:00
lsdsjy 31be030ccc fix(headless): keep --json stderr free of commander's error print
Address the ds-review-bot findings on 8724223621:

- The JSON error override throws commander's control-flow error instead of
  delegating to `program.error`, whose own printer wrote an unprefixed
  `error: …` line to stderr and broke the documented `--json` contract.
- Cover the post-idle adoptability re-check with a focused case: an overlay that
  appends a preset while the runner awaits idle is still rejected, and the stale
  "live Session" comment now names that residual writer.
- Scope the Agent Note change sentence to the product change so it no longer
  claims the whole patch is confined to one package.
2026-09-10 16:25:19 +08:00
lsdsjy 9bcb1e214c fix(headless): refuse a live Agent identity and finish the adopt-only docs
Address the ds-review-bot findings on 472554a29e:

- resolveAgent refuses an Agent already live under the requested id: its owner
  may still drive it, and `whenIdle` is not a single-message signal, so the
  one-shot runner cannot own an exclusive run interval over it. The adoptability
  rules still run first so the refusal stays specific.
- Refresh the README intro, identity section, and limitations, and the Agent
  Note live-identity rule and change scope, for adopt-only.
- Clarify the loader-smoke `cwd` JSDoc (the prefix/parent inputs are ignored)
  and pin the resumed wake's final text in the two-wake e2e.
2026-09-10 16:00:53 +08:00
lsdsjy 127d2c8eb5 fix(headless): align the adopt-only docs and prove resume end to end
Address the ds-review-bot findings on 9aea4b0804:

- Drop the stale "freshly created" wording from the resolveAgent `@returns`
  and rewrite its leading comment for the two remaining identity paths.
- Rewrite the README run-flow paragraph (EN and ZH), the intro sentence, and
  the cordis.patch.yml header comment for adopt-only.
- Add a real-composition two-wake e2e: capture the generated id from the first
  `--json` wake, resume it by `--session-id`, and assert one persisted log holds
  both tasks. `runLoaderSmoke` gains an optional caller-owned `cwd` so both
  wakes share one world; the harness leaves that directory in place.
2026-09-10 15:19:17 +08:00
lsdsjy ea84ad31b6 feat(headless): require --session-id to name an existing Session
`--session-id` now adopts only: an id with no stored Session fails before the
task runs instead of creating an empty history the caller believes it is
resuming. A first round omits the flag, and the runtime reports the minted
`session-<uuid>` identity in the opening `session` event, so a supervisor
persists it and names it on every later wake.

Updates the help text, the README pair, the Agent Note pair, the generated
config catalog, the unit suite, and the product-profile e2e.
2026-09-10 14:44:44 +08:00
lsdsjy a69933d3e5 fix(headless): omit step usage when any attempt lacks a sample
Address the ds-review-bot v12 critical on PR #3849: `addUsage` published the
sum of the attempts that reported usage while silently dropping the ones that
did not, so `step_end.usage` looked exact but under-counted billed tokens.
Track completeness per step and omit usage entirely when any attempt in the
step reports no sample, matching `deriveTurnTokenUsage`'s fail-closed rule.
2026-09-09 21:05:19 +08:00
lsdsjy 980b4b77af fix(headless): close the v11 review gaps in the json run surface
Address the ds-review-bot v11 review on PR #3849:

- Accumulate every attempt's usage into `step_end.usage`, including a retried
  attempt whose only sample sits in its discarded `assistant/attempt` stream.
- Re-check the adopted Session after the idle wait, so a preset selected in
  that window is still rejected.
- Reject a whitespace-only `sessionId` supplied through a Cordis overlay, which
  bypasses the CLI trim check.
- Narrow the documented error contract: a profile whose own plugins fail to
  load exits before the runner mounts and keeps only stderr diagnostics.
2026-09-09 20:46:16 +08:00
lsdsjy b3f6c5e991 fix(headless): count the line terminator inside the 32 KiB event cap
Address the ds-review-bot v10 review on PR #3849: `boundJsonLine` bounded the
serialized JSON but not the newline the writer appends, so an event that
serialized to exactly 32 KiB produced a 32 KiB + 1 byte record. Reserve the
terminator byte inside the cap, document the cap as newline-inclusive, and
assert the written record length in the boundary tests.
2026-09-09 20:16:58 +08:00
lsdsjy 755b44bbcd fix(headless): require the query service for every --session-id run
Address the ds-review-bot v9 review on PR #3849:

- Require `sessionQuery` before the live fast path too: a later process has no
  live Agent and must find the id through the query service, so a live identity
  no longer bypasses the check.
- Soften the live `stat` comment to what it proves: a stored record rules out an
  in-memory registration, while write-handle ownership is not queryable.
- Complete the `parseArguments` JSDoc and fix the broken English sentence in
  README.md and the Agent Note.
2026-09-09 19:53:03 +08:00
lsdsjy 7a4a55f30b fix(headless): fail closed on malformed preset records and unroundtrippable args
Address the ds-review-bot v6 review on PR #3849:

- A persisted `agent-preset/selected` record whose `data.agentPreset` is not a
  non-empty string used to read as "no preset", letting the run continue under
  this bundle's composition. It now fails closed.
- A live identity now has to be known to `sessionPersistence` (`stat`), since
  an Agent registered only in memory has no write handle and would flush
  nothing.
- Tool arguments that JSON cannot round-trip (an overflowing number such as
  `1e400`) keep their raw text instead of the `null` JSON.stringify reports.
2026-09-09 19:39:31 +08:00
lsdsjy a5c21a69b8 fix(headless): require persistence for every --session-id path
Address the ds-review-bot v6 review on PR #3849: the guard sat before the
create branch only, so adopting an already-live identity could still run a
session whose history dies at exit. Move it to the top of resolveAgent, where
it covers both the live and the create path, and add a rejection test for the
live path alongside the existing create-path one.
2026-09-09 19:17:01 +08:00
lsdsjy b3756a89d1 fix(headless): fail loud when --session-id would create a non-durable session
Address the ds-review-bot v6 review on PR #3849:

- Require the composed sessionPersistence service before creating an exact
  --session-id identity, so a profile that keeps sessionQuery but drops
  persistence exits 1 instead of printing an id whose history dies at exit.
- Stop exporting the now-internal boundJsonEvent and describe it in the
  present tense; its test now exercises boundJsonLine.
- Say "64 levels or deeper" in the README and Agent Note, matching the
  depth budget (an input keeps 63 nested containers, the 64th is cut).

Tests: 77 headless unit tests with per-file 100% coverage on src, 14 profile
e2e tests, constraints/typecheck/lint/doc/pairing gates.
2026-09-09 19:00:06 +08:00
lsdsjy 3488db77bf fix(headless): cap the bounding recursion depth and route error events through the line cap
Address the ds-review-bot v6 review on PR #3849:

- Cut a projected payload nested deeper than 64 levels instead of recursing
  until the stack overflows; the isolated session listener would otherwise
  drop the `tool_call` silently.
- Write both `error` events (startup usage errors and runner failures) through
  `boundJsonLine`, so the documented 32 KiB line cap covers JSON-escaping
  expansion as well as the 8 KiB message cap.

Tests: 76 headless unit tests with per-file 100% coverage on src, 14 profile
e2e tests, constraints/typecheck/lint/doc/pairing gates.
2026-09-09 18:06:29 +08:00
lsdsjy 0ca42f3b78 fix(headless): bound whole events, re-check adoption after resume, tighten --json scan
Address the ds-review-bot v5/v6 review on PR #3849:

- Register the shared `lib/json-stream-*.js` chunk in `packageFileExtras` and
  order `files` as the workspace-constraints gate requires, so
  `pnpm run constraints` passes.
- Re-validate the preset on the Session `agents.resume` actually attached,
  closing the TOCTOU window between `observeSession` and the write lease.
- Bound one serialized event line at 32 KiB: an over-long event keeps its
  scalar fields, drops structured ones, and at the extreme reduces to `type`.
- Normalize an empty tool-argument string to `{}`, matching the executor.
- Scan only real `--json` flags (stop at `--`, skip a `--session-id` value)
  before installing the JSON error override.

Tests: 74 headless unit tests with per-file 100% coverage on src, 14 profile
e2e tests, constraints/typecheck/lint/doc/pairing gates, tarball import smoke.
2026-09-09 17:40:47 +08:00
lsdsjy 3797127eb2 fix(headless): publish the shared chunk and read the session's current preset
Address the ds-review-bot v6 review on PR #3849:

- Publish the `lib/json-stream-*.js` chunk that both entry points import.
  Without it the installed tarball failed to load with ERR_MODULE_NOT_FOUND,
  because `files` listed only `lib/index.js` and `lib/startup.js`.
- Resolve a Session's preset from its log (`agent-preset/selected`) instead of
  only the immutable creation header, so a Session that switched preset while
  blank is rejected too. Both the persisted and the live path scan the events.
- Write the `--json` error event for Commander grammar rejections (an unknown
  option, a missing option value) as well as the action's own usage errors, and
  drop Commander's `error: ` prefix from the event message so one event type
  carries one message shape.
- Reject a whitespace-only positional instead of falling back to stdin, so an
  accidental blank argument cannot consume a pipe.
- Document the stdin task as verbatim and the widened usage-error contract.

Tests: 68 headless unit tests with per-file 100% coverage on src, 14 profile
e2e tests, plus a packed-tarball import smoke check.
2026-09-09 17:16:09 +08:00
lsdsjy 45032ea1bd fix(headless): address re-review findings on the machine-readable run surface
- Reject adopting a Session recorded under an agent preset, and one that
  recorded no cwd; align the subagent/forked diagnostic wording.
- Write the bounded JSON `error` event for a --json usage error before exit,
  since the runner never mounts to write it.
- Keep the caller's exact --session-id instead of the trimmed value.
- Bound object keys and copy a literal `__proto__` key as data rather than
  through the inherited setter.
- Reject a lone `-` mixed with other task words.
- Document the in-turn failure contract and step-commit text/thinking timing.
2026-09-09 16:58:24 +08:00
lsdsjy ba6e19809d fix(headless): project committed content and scope live adoption 2026-09-09 15:51:23 +08:00
lsdsjy 0e7361a489 test(headless): close per-file coverage on the run projection 2026-09-09 15:19:52 +08:00
lsdsjy d5fe1fb887 docs(headless): regenerate event producer graph and restore model-selection note 2026-09-09 15:17:01 +08:00
lsdsjy ba6a90d2f2 feat(headless): add stdin task, --session-id, and --json run events 2026-09-09 15:12:32 +08:00
_Kerman 37abcb74c5 chore(session): permit existing deprecated history reads 2026-09-09 12:33:53 +08:00
Tianyi Cui f99b06eaed feat(session)!: embed assistant streams in format v2 2026-09-02 04:00:01 +08:00
Tianyi Cui 27bf1039db refactor(session)!: distinguish event seqs from log offsets 2026-09-01 20:36:00 +08:00
Turtle d68ff7e66f Merge remote-tracking branch 'origin/master' into turtle/omit-unneeded-invariants
# Conflicts:
#	docs/module-graph.i18n.yaml
#	docs/module-graph.md
#	docs/module-graph.zh.md
#	packages/session/session-projection/src/invariant.ts
#	tsconfig.base.json
2026-09-01 11:18:11 +08:00
Turtle b8e3b32fcf Merge master into codex/omit-unneeded-invariants 2026-08-31 14:38:55 +08:00
_Kerman 6c63e708b8 Merge remote-tracking branch 'origin/master' into dshw/pr-deepseek-harness-deepseek-harness-2907
# Conflicts:
#	docs/config-catalog.i18n.yaml
#	docs/persistence-catalog.i18n.yaml
#	docs/subsystems/session.i18n.yaml
#	packages/api/session-controller/tests/transport.host.spec.ts
#	packages/bundle/headless/src/index.ts
#	packages/core/session/README.i18n.yaml
#	packages/schedule/schedule/README.i18n.yaml
#	packages/schedule/schedule/README.md
#	packages/schedule/schedule/README.zh.md
#	packages/session-query/tool-session-query/src/workspace-access.ts
#	packages/session/session-log-deepseek/src/index.ts
2026-08-31 10:56:53 +08:00
imccyu 9135a13a8b refactor(consumers): remove cross-package runtime relays 2026-08-30 02:29:52 +08:00
_Kerman 5660f44d29 perf(session): separate indexed and snapshot log reads 2026-08-28 13:25:58 +08:00
Turtle 15f2997bcb cleanup: omit unneeded invariant companions 2026-08-28 13:12:52 +08:00
lsdsjy 3a9820c8cb fix(headless): make stream chunk handling exhaustive 2026-08-25 18:51:37 +08:00
lsdsjy 2813ef2a95 fix(headless): preserve reasoning block continuity 2026-08-25 18:51:37 +08:00
lsdsjy 937d2b3513 feat(headless): stream reasoning progress to stderr 2026-08-25 18:51:37 +08:00
Turtle 54dd75a969 refactor(cmdline): run the program's own commander action instead of a plan callback
parseCmdline(ctx, program): void only adapts commander control flow to
the launcher: it parses the immutable cmdlineArgs snapshot and turns
help, version, parse errors, and action rejections into a ctx.appExit
request. App validation and the ctx.provide of the app-owned service
live in the program's own synchronous .action(), which commander runs
inside parse — program.error(...) there shares the exit path with a
grammar rejection. Deletes the CmdlinePlan export, its unread ctx
parameter, the type-unsound (() => ({}) as T) default, and the
T | undefined return with its per-caller publish guard.
2026-08-12 13:07:41 +08:00
Turtle c598989d08 refactor(cmdline): trim the command-line seams to existing interfaces
The web runtime creates its dev-mode client-hmr row in the root tree
after Loader settlement with plain loader.create, deleting the vendored
Entry.enableRuntime state machine and dsh-cmdline's enableRow export.
Include declares the existing EntryGroup.key tree-carrier marker instead
of the EntryConfigResolver protocol (its own path stays literal; nothing
used a dynamic path). The launcher recognizes no app row: SIGTERM exits
0 on every surface, every boot watches its user patch layers, and the
headless runner exits through ctx.appExit, deleting ctx.headlessIo. Also
restores the vendor README rescope entry to the position the
rescope-vendor exact-edit anchor requires, fixing the master hygiene
regression.
2026-08-11 18:00:15 +08:00
Yichen Jiang 61d5cb9e41 fix(apiproxy): serve history events and projections from one log position
Review found two defects in the previous commit's ordering fix.

@pku-xht: `historyStateFor` copied the attached session's events, the handler
then awaited `presenterScopeFor`, and only then read the projection baseline
off the still-live Session. An append during that await served events cut at N
beside a baseline folded to N+1 — one response describing two moments. The
same restructure had also moved the baseline read outside the `try`, so a
failing snapshot escaped the structured `internal` error.

Both awaits now happen before the cut: `historySourceFor` resolves which
session serves the read, `presenterScopeFor` ensures the recorded composition,
and `historyCutOf` then reads events and baseline adjacently with nothing
between them. The whole sequence is back inside the try.

The invariant judged any scoped assembly with a chain of one as an unjoined
agent, which rejects a legitimate assembly in a standing preset key (that key
has no parent of its own). It now gates on `context.agent` — a scope-only read
is not an agent and is out of range by construction rather than by a premise
about who else calls `assemble` — and asks the roster's own
`composedPreset()` instead of introspecting chain length. The advisory warning
uses the same relation.

Also from review: the `2026-08-05-per-agent-tool-presentation` note still
described `presentAs` as per-agent, which standing mounts made false and this
branch's own rewording contradicts; the duplicated "process-wide unit table"
argument collapses to the Agent Note with pointers from the five copies; a
dead `.sort()` before `arrayContaining`; and change-history narration in the
tool-cordis README.
2026-08-11 10:45:15 +08:00
Yichen Jiang 19ea7ae573 Merge remote-tracking branch 'origin/master' into worktree/preset-plane-fallout-p1
The pre-commit staged-pairing hook was bypassed for this merge: master's
`docs(notes): archive superseded dsh run decision` (7ee9e16001) arrives as a
rename into `.agents/notes/archived/`, and the hook hands that path to
`verify-translation-pairing`, which correctly refuses an archived note as
out-of-corpus. The full-corpus gate passes (851 pairs).
2026-08-11 00:32:25 +08:00