Commit Graph
188 Commits
Author SHA1 Message Date
Yichen Jiang 3ad561862f Merge remote-tracking branch 'origin/master' into codex/rfc-subagent-background-tasks
Adopts #201 (session-prefix composition before pre-step + the pressure
gate). Only the generated RFC index conflicted; regenerated.
2026-07-09 23:17:16 +08:00
Tianyi Cui c39fe5cd30 Merge remote-tracking branch 'origin/master' into agent-request-messages
# Conflicts:
#	docs/cordis-catalog/services.md
2026-07-09 22:43:26 +08:00
Yichen Jiang 032510a85a Merge remote-tracking branch 'origin/master' into codex/rfc-subagent-background-tasks
Adopts #220 (tool-cordis + the gen-cordis-api gate: the runtime API
catalog regenerated with ctx.tasks/onCleanup on it) and #225 (shared
transcript renderer). Tool-catalog expectations take the union
(cordis_* + task_*); packages/README budget adopts master's 660
ceiling, which absorbs both new group rows.
2026-07-09 22:42:19 +08:00
imccyu fda5784e16 Merge remote-tracking branch 'origin/master' into tool-cordis
# Conflicts:
#	docs/capability-seams.md
#	packages/core/tools/tests/gen-tool-catalog.spec.ts
#	scripts/gen-doc-graphs.ts
2026-07-09 22:02:06 +08:00
Yichen Jiang beaace5eb3 Merge remote-tracking branch 'origin/master' into codex/rfc-subagent-background-tasks
Adopts #185 (dsh-timeout: clampTimeout/deadline/timeoutOf drive bash
run() timeout classification; runBash loses its own timer) and #108
(ask_user_question) across the task-runtime rework: bash-local keeps
the BashProcess handle shape with master's deadline mechanics, tool
catalogs/expectations carry both the task_* and ask-user tools, and
generated docs are regenerated on the union.
2026-07-09 21:32:07 +08:00
Yichen Jiang 184e164091 feat(tasks): background task runtime, generic task_* control tools, bash/subagent producers
One shared ctx.tasks registry (branded <kind>-N ids, owner-fenced
read/kill/wait/list, attachSurface misconfiguration fence, reported-flag
notice dedup, atomic register) + dsh-tool-tasks (task_output/task_list/
task_kill, completion-notice injection, background prompt habit).
Producers opt in via their own enableRunInBackground config: bash
(stream kind; seam slimmed to resolve/run/start returning a BashProcess
handle, bash_output/bash_kill deleted) and subagent (final-output kind;
done settles after run.dispose()). Owner disposal drains tasks through
the new awaited ctx.agents.onCleanup seam in the loop's disposal chain.
Both RFCs moved to implemented/; docs, catalogs, snapshots re-pinned.
2026-07-09 21:22:54 +08:00
Yichen Jiang 0df497133c Merge branch 'master' into agent-request-messages 2026-07-09 21:12:31 +08:00
Yichen Jiang 1e06fdbb86 refactor: colocate user interaction with ui packages 2026-07-09 17:59:02 +08:00
imccyu ee1da1ce5b feat(cordis): @deepseek-ai/dsh-tool-cordis — inspect/mount/unmount over the live runtime
New top-level packages/cordis/ group with the self-referential toolset:
cordis_inspect (services / plugin tree / tools / dynamic mounts / api / events,
the api section intersecting the generated catalog with the live service store),
cordis_mount (model-written code evaluated in a node:vm sandbox, mounted under
one cordis-dynamic group fiber as dyn-<n>), cordis_unmount (awaited disposal to
quiescence). Boundary mechanisms: dual-realm instanceof, JSON realm
normalization of dynamic tool results, marker-guarded registration, SchemaSpec
teaching errors, parse failures surfaced with the offending line + caret and a
line-scoped TypeScript hint, and the unmount-first recipe on tool-name
collisions. Config: vmTimeoutMs (schemastery, default 5000). Design record:
docs/rfc/implemented/feature/2026-07-08-self-referential-cordis-toolset.md.

The tool-catalog boot manifest, its regenerated output, and the pinned
tool-name list land here rather than with the other repo registration: the
completeness guard globs packages/*/tool-* and fails the generator (and the
core/tools spec) the moment the package directory exists.
2026-07-09 13:57:03 +08:00
Yichen Jiang bdab8c6a61 Merge remote-tracking branch 'origin/master' into codex/ask-user-question
# Conflicts:
#	docs/config-catalog.md
2026-07-09 13:34:13 +08:00
Yichen Jiang e4331ca5e7 Merge branch 'master' into agent-request-messages 2026-07-09 13:30:11 +08:00
Yichen Jiang 959a3c3a8b fix(agent-loop): discard an interrupted prefix composition instead of caching it
A cancel/dispose landing inside the first agent/session-prefix waterfall
used to commit the listener chain's return value to the instance cache
before the interruption check dropped the turn; an abort-aware listener's
degraded fallback would then ship on every later request of the instance.
The commit now happens only after the composition survives the
interruption check — the cache only ever holds a fully composed prefix,
and the next turn recomposes under a live signal.
2026-07-08 21:46:03 +08:00
Yichen Jiang 765052a7d1 fix(agent-loop): compose the session prefix before pre-step; hand it to the pressure gate
ds-review-bot critical (follow-up): on the first step of a resumed or
seeded/forked instance, auto-compaction ran before runStep composed
this instance's prefix, so the gate read the PREVIOUS instance's logged
prefix from the header fold — a contributor that grew across
resume/fork (skills added, AGENTS.md grown: exactly the
environment-dependent case) could under-gate and ship an over-window
first request.

The loop now composes agent/session-prefix before the instance's first
agent/pre-step (still once per instance; runStep just reads the cache),
and agent/pre-step carries the composed prefix to its listeners.
CompactService.compactIfNeeded gains the sessionPrefix parameter;
BasicCompactService.estimatePressure gates on the handed value — the
header-fold read is gone, so the estimate is exact at every step
including a resumed/forked instance's first. Composition moving before
the boundary snapshot also means a composing listener's session append
now joins the CURRENT request (documented on the seam).

New coverage: composition precedes pre-step and the seam receives the
composed prefix; cancel and disposal landing inside the composition
window drop the step cleanly; the compact gate test hands the prefix
directly.
2026-07-08 20:30:10 +08:00
Yichen Jiang 77333c0a19 fix(agent): correct session-prefix composition-order docs; scrub messagePrefix in snapshots
Two ds-review-bot findings:

The seam JSDoc claimed extending 'await next()' composes in
registration order — false for the append form: the waterfall unwinds
innermost-first, so appending places later-registered contributions
first. The canonical contribution is now documented as the PREPEND
'[mine, ...await next()]' (registration order on the wire), with the
append form's reverse-order behavior stated explicitly; the ordering
test now uses the canonical pattern in both listeners.

scrubRequestHeaders tokenized only system/tools, so a fixture recording
a composed session prefix would carry its raw text (workspace-specific
churn/leak). The scrubber now maps each header/delta messagePrefix
entry to a {{messagePrefix}} token — count stays a structural fact,
absence stays absent, the empty-array transition stays visible — with
normalize.spec coverage for the header, delta, absence, and odd-shape
paths.
2026-07-08 16:16:08 +08:00
Yichen Jiang ea4c10d753 refactor(agent): replace the per-step advice seam with agent/session-prefix
Review discussion converged on the industry shape (Claude Code caches
user context per conversation; Codex separates initial context from
diffs; Kimi appends at continuation boundaries to protect prompt
caching): stable openers belong in a compose-once prefix, mid-session
changes belong in append-only history — not in a per-request slot.

agent/session-prefix fires ONCE per loop instance, lazily on its first
request-building step: the composed Message[] is deep-frozen, cached on
the transmission bookkeeping, recorded as EpochHeader.messagePrefix on
the anchoring 'initial'/'resume' snapshot, and reused verbatim for
every request the instance sends — prefix stability is structural, not
a producer discipline, and a resume recomposes with attributable drift.
The request is messagePrefix + boundary snapshot.

The per-step RequestAdvice/RequestAdviceContext surface and the
messageSuffix header field are dropped: the tail slot had no consumer,
and every current update pattern (new AGENTS.md discovered, memory
update, skills change) routes through the existing append-only history
channels — inject(), tools/post-execute additionalContext,
prompt-submit additionalContext — each paid once and prefix-cached
thereafter. The messagePrefix delta arm stays for codec totality; the
loop never produces one in practice.
2026-07-08 15:44:30 +08:00
Dudu-0223 5d451bb2a0 feat(tools): add ToolDefinition.timeoutMs declared+validated via defineTool
A tool declares its cooperative timeout budget on its own definition
rather than a deployment naming it in a central config map. The field
never reaches the model (schemas() whitelists name/description/parameters)
and defineTool rejects a non-positive-finite value at authorship.
2026-07-08 14:06:24 +08:00
Dudu-0223 a76285c4e6 Merge origin/master into timeout-design
Resolve conflicts from master's catalog/doc refactors landing alongside the
tool-call timeout work:
- knip.json: keep both new workspace entries (util/timeout + support/acp-snapshot).
- tool-web/src/fetch.ts: keep the timeout_ms removal, adopt master's richer
  JSDoc @param/@returns style on parseFetchArgs/presentFetchCall.
- tools/README.md: keep the tools/execute pipeline wording, adopt master's
  flattened docs/tool-catalog.md path.
- Regenerate every generated doc (cordis-catalog, tool-catalog, config-catalog,
  doc-graphs, module-graph) so they carry both master's changes and the
  tools/execute event + timeout-policy package.
- Add @param/@returns to toolTimeoutResult for master's new verify-export-jsdoc gate.
2026-07-08 11:18:27 +08:00
Yichen Jiang d3f86996f7 Merge remote-tracking branch 'origin/master' into codex/ask-user-question
# Conflicts:
#	docs/config-catalog.md
#	examples/acp-agent/tests/snapshots/text-turn/session.jsonl
#	pnpm-lock.yaml
2026-07-08 11:09:20 +08:00
Yichen Jiang 2cbdeb0872 Merge remote-tracking branch 'origin/master' into agent-request-messages
docs/core-data-structures/core.md: combined master's canonical tool-order
wording (#196) with this branch's request-advice envelope + wire-order
paragraphs.
2026-07-08 10:24:35 +08:00
Dudu-0223 8190016e2b feat(timeout): add tools/execute seam + tool-timeout policy plugin
Model-facing tool-call budgets were tangled into each capability's schema
(bash timeoutMs, web_fetch timeout_ms) with no shared home. Add a
tools/execute around-dispatch waterfall to dsh-tools whose base next() is
the dispatch-with-normalization thunk, and a new @deepseek-ai/dsh-timeout-policy
plugin (packages/timeout/) that arms a per-tool deadline on exec.signal and
returns a structured TOOL_TIMEOUT when it wins. Migrate web_fetch (drop the
model-facing timeout_ms) and web_search onto it; the fetch provider keeps its
timeout only as a resource backstop for direct callers. bash and hook command
execution keep BASH_TIMEOUT unchanged.

Named the plugin timeout-policy (not the RFC's tool-timeout) so it does not
trip the gen-tool-catalog packages/*/tool-* completeness guard, and replace
exec.signal by in-place mutation before next() since cordis waterfall next()
ignores passed arguments. RFC moved to implemented/ recording both deviations.
2026-07-08 10:10:37 +08:00
Yichen Jiang e97fffeab7 refactor(agent): rename agent/request-messages to agent/request-advice
tianyicui's review: the seam name did not say what the event or its
types do. 'advice' reads both ways — advisory content for the model,
and AOP before/after advice woven around a join point (here the
derived history) without modifying it — so RequestAdvice.before/after
are self-describing. Types follow: RequestAdvice / RequestAdviceContext;
the logged EpochHeader fields keep their positional names
(messagePrefix/messageSuffix).

Also sharpens the core.md wording the review flagged as ambiguous:
before-advice sits in front of the ENTIRE derived history, directly
after the system slot (the conventional home for session-stable openers
— an AGENTS.md digest, a skills catalog), after-advice follows the
history's last message. Catalogs and doc graphs regenerated.
2026-07-08 10:05:34 +08:00
Tianyi Cui 0172e3b67d Merge remote-tracking branch 'origin/master' into structured-output-subagent-seam 2026-07-07 22:59:35 +08:00
Tianyi Cui aeaccf6d36 docs: satisfy the new export-JSDoc gate on the assertion signature
Master's verify-export-jsdoc (landed mid-stack) wants @returns on every
exported function including asserts-returning ones; document the narrowing.
2026-07-07 22:13:01 +08:00
imccyu 161275e287 feat: add assembly-time validation rejects placeholder 2026-07-07 22:03:12 +08:00
Tianyi Cui a1c01cda52 Merge remote-tracking branch 'origin/master' into structured-output-subagent-seam
# Conflicts:
#	docs/config-catalog.md
2026-07-07 21:40:07 +08:00
imccyu eeeadd7bef Merge remote-tracking branch 'origin/master' into worktree-llm-tool-order 2026-07-07 21:35:28 +08:00
Tianyi Cui d1b52a063b fix review findings: own-property and plain-JSON discipline in the schema subset
Three Codex findings on json-schema.ts, one discipline:

- required-declared and every value check now use Object.hasOwn — 'in' let
  inherited names (toString) satisfy required, dodge additionalProperties:
  false, and validate a declared property against the value's prototype
  member instead of a carried one
- isObjectLike now means PLAIN JSON object (proto chain of at most one link,
  realm-agnostic): a Date annotation or a Map-as-properties no longer passes
  structurally and serializes lossily — they fail loud as subset violations
- startInProcessRun asserts BEFORE the defensive structuredClone, so a
  hostile schema fails as OutputSchemaError, never a raw DataCloneError

Also the type-equiv catalog gap: tools.md gains the structured-output subset
vocabulary (4 blocks) with matching manifest entries. The driver index also
drops the runtime internals from its public re-export (runs acquire it
internally; no external consumer remains — see the following commit).
2026-07-07 21:07:54 +08:00
Yichen Jiang 731ae2443c Merge remote-tracking branch 'origin/master' into agent-request-messages
# Conflicts:
#	docs/cordis-catalog/events.md
#	docs/event-producer-consumer.md
#	docs/persistence-catalog.md
2026-07-07 21:03:07 +08:00
Yichen Jiang f3d26ed049 fix request-messages boundary immutability 2026-07-07 20:55:13 +08:00
imccyu adbba0deb2 fix(system-prompt): reject a toolOrder that names an unregistered tool
Review follow-up (#196): a listed name with no registered tool was silently
ignored; misconfiguration must block work instead. The check lives in the
assembly — the earliest moment the registered tool set exists (tool plugins
register after the service constructs) and the only universal one (cordis has
no "all plugins loaded" event; registrations change at any time). assemble()
is now async so the throw surfaces as a rejection rather than a synchronous
escape from a Promise-returning method.

Blast radius, pinned by a loop-level test: the rejection reaches the turn's
outer catch — the turn closes balanced with an `error` reason, agent/error
mirrors it, no step opens, no request/header is logged, no request reaches
the adapter, and the agent returns to idle; every turn fails identically
until the config is fixed. A boot-time validation pass was considered and
rejected (recorded in the RFC). The general principle — misconfiguration
fails loud, never a silent skip — is added to AGENTS.md.
2026-07-07 20:49:42 +08:00
imccyu 72933ec558 refactor(system-prompt): rename TOOL_ORDER_REST from '...' to '<unlisted-tools>'
A three-dot rest entry reads as elision in a cordis.yml; the spelled-out
sentinel says what lands there. The literal now appears once in code (the
constant) and once in the value-pinning test; every other reference — the
forwarding tests included — imports TOOL_ORDER_REST, which adds the
dsh-system-prompt devDependency to the two app packages.

Review follow-up on #196.
2026-07-07 20:49:35 +08:00
Tianyi Cui 5e4ac5e472 fix review findings: CI leaf-gate wiring, heritage return surface, AGENTS.md self-containedness
- run-gates.ts docSyncLeafGates() gains verify-export-jsdoc — CI lanes
  and the pre-push hook execute this leaf list, not the doc-sync npm
  script, so the gate was previously unenforced there (proven by
  SessionForkErrorCode landing undocumented via a master merge while
  checks stayed green; now documented). Same wiring gap fixed for
  master's verify-config-catalog, which was also missing from the list.
- The heritage exemption now recovers the base's return surface: a void
  base return carried no @returns duty, so an override returning a
  concrete result documents it itself (annotated overrides run the
  standard check; unannotated ones are classified by the checker so
  faithful void overrides need no boilerplate annotation). Three new
  negative-path tests pin it; RFC and module doc updated.
- AGENTS.md states each principle inline instead of citing RFCs (eight
  citations removed; high-level doc links kept) and the editing section
  now carries the self-containedness rule.
- Generated catalogs/graphs regenerated for the shifted line pointers.
2026-07-07 20:30:34 +08:00
Yichen Jiang 17bd71e530 feat(agent): add the agent/request-messages request-only message seam
A new waterfall near request construction lets plugins contribute
request-ONLY messages framing the derived history: RequestMessages
{ before, after } with a frozen empty seed, fired inside the open step
after the agent/request config waterfall, so the step/start boundary
snapshot and its same-sync-frame invariant are untouched. The request
becomes messagePrefix + boundary snapshot + messageSuffix.

Contributions never enter session history — deriveMessages() is
unchanged — so the request header is their durable record:
EpochHeader gains messagePrefix/messageSuffix (canonical absence for
empty arrays), request/header-delta replaces either array whole with
an empty array encoding the transition back to absence, and the
dev-mode reconstruction cross-check now expects the folded header's
framing around the boundary derivation.

This is the seam for per-request advisory context that must be
model-visible now without becoming durable history (a skills catalog,
an environment reminder), keeping the base system prompt
workspace-independent and provider prefix caches stable. The docs
carry the channel cost model: session-frozen content belongs in
before, low-frequency change notices belong in durable history via
inject() (paid once, prefix-cached thereafter), and after is reserved
for small frequently-refreshed state snapshots re-paid on every
request they ride. No shipped producer yet, so ACP snapshot fixtures
are byte-identical.
2026-07-07 19:42:30 +08:00
Yichen Jiang 7065b32e0b Merge branch 'master' into codex/ask-user-question 2026-07-07 19:42:25 +08:00
Tianyi Cui 9296411ea1 Merge remote-tracking branch 'origin/master' into worktree-export-jsdoc-gate
# Conflicts:
#	docs/config-catalog.md
#	docs/cordis-catalog/services.md
2026-07-07 17:17:03 +08:00
Tianyi Cui 51b715433d fix review finding: an export list surfaces only the declarators it names
A name resolved through an export list (or a default-export identifier)
mapped back to its whole VariableStatement, and checkDecl walked every
declarator — so a private sibling sharing the statement with an exported
const was wrongly required to carry JSDoc.

The scope dispatch is now two-phase: requests accumulate per statement
(null = whole statement for a direct export modifier or ambient scope;
name sets union across lists, so two lists naming different declarators
of one statement both count), then each surfaced statement is checked
once with the declarator filter. Regressions pin the private-sibling
skip, the cross-list union, and the default-export sibling.
2026-07-07 17:05:32 +08:00
Tianyi Cui e930dd328a Merge branch 'master' into worktree-llm-tool-order 2026-07-07 17:01:32 +08:00
Yichen Jiang b8327bc0e8 Merge remote-tracking branch 'origin/master' into codex/ask-user-question
# Conflicts:
#	examples/acp-agent/tests/snapshots/cancel/session.jsonl
#	examples/acp-agent/tests/snapshots/error-finish/session.jsonl
#	examples/acp-agent/tests/snapshots/fs-edit/session.jsonl
#	examples/acp-agent/tests/snapshots/fs-policy-reject/session.jsonl
#	examples/acp-agent/tests/snapshots/fs-read-window/session.jsonl
#	examples/acp-agent/tests/snapshots/fs-read/session.jsonl
#	examples/acp-agent/tests/snapshots/fs-terminal-card/session.jsonl
#	examples/acp-agent/tests/snapshots/fs-write-overwrite/session.jsonl
#	examples/acp-agent/tests/snapshots/fs-write/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-cc-posttool-block/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-cc-posttool-context/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-cc-pretool-ask/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-cc-pretool-deny/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-cc-promptsubmit-context/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-cc-stop-continue/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-codex-posttool-block/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-codex-posttool-context/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-codex-pretool-block/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-codex-promptsubmit-context/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-codex-stop-continue/session.jsonl
#	examples/acp-agent/tests/snapshots/multi-turn/session.jsonl
#	examples/acp-agent/tests/snapshots/subagent-fork/session.1.jsonl
#	examples/acp-agent/tests/snapshots/subagent-fork/session.jsonl
#	examples/acp-agent/tests/snapshots/subagent-mixed/session.1.jsonl
#	examples/acp-agent/tests/snapshots/subagent-mixed/session.2.jsonl
#	examples/acp-agent/tests/snapshots/subagent-mixed/session.jsonl
#	examples/acp-agent/tests/snapshots/subagent-multi/session.1.jsonl
#	examples/acp-agent/tests/snapshots/subagent-multi/session.2.jsonl
#	examples/acp-agent/tests/snapshots/subagent-multi/session.jsonl
#	examples/acp-agent/tests/snapshots/subagent-spawn/session.1.jsonl
#	examples/acp-agent/tests/snapshots/subagent-spawn/session.jsonl
#	examples/acp-agent/tests/snapshots/todo-plan/session.jsonl
#	examples/acp-agent/tests/snapshots/tool-call-turn/session.jsonl
#	examples/acp-agent/tests/snapshots/workspace-edit/session.jsonl
2026-07-07 15:22:07 +08:00
Yichen Jiang cebf781d69 fix review findings: polish ask-user question 2026-07-07 14:57:06 +08:00
imccyu 42e7a2f691 fix: add tools reorder to system prompt 2026-07-07 11:03:11 +08:00
Tianyi Cui 08ff3e404f Merge remote-tracking branch 'origin/master' into structured-output-subagent-seam
# Conflicts:
#	packages/hooks/hooks-claude/tests/bridge.spec.ts
2026-07-07 09:41:14 +08:00
Tianyi Cui 6d6b554fdf Merge remote-tracking branch 'origin/master' into worktree-export-jsdoc-gate
# Conflicts:
#	docs/cordis-catalog/services.md
#	docs/persistence-catalog.md
#	docs/rfc/INDEX.md
#	package.json
#	scripts/gen-cordis-catalog.ts
2026-07-07 09:34:12 +08:00
Hypatia May 4f55c15cf8 Merge remote-tracking branch 'origin/master' into session-fork 2026-07-07 09:06:00 +08:00
Hypatia May c7fba41ba9 Inline session fork parameters 2026-07-07 09:04:49 +08:00
Tianyi Cui f7bd7e82d1 fix review findings: restrict export-import aliases to prose-only targets
Codex round-3: alias prose matches the gate's strength only when the
target's own contract is prose-only. An export-import alias to a
function, class, or namespace target (or an unresolvable one) is now
refused — those carry signature/member contracts the alias cannot
hold; export the declaration directly instead. Const/enum/interface/
type-alias targets keep the self-documentation contract.

Tests pin the refusal for function, class, and namespace targets;
module doc and RFC updated.
2026-07-07 00:20:55 +08:00
Tianyi Cui 4e2da27201 Merge branch 'config-catalog' into catalog-flatten 2026-07-07 00:06:19 +08:00
Tianyi Cui f0ef280848 fix: identity-check name resolutions across the pasted closure; exclude the catalog from i18n pairing
Review findings on the config catalog:

The transitive-paste closure deduplicated by bare name, so two DIFFERENT
package-local declarations sharing a name would silently collapse to the
first one pasted — and a reference satisfied by the wrong declaration is a
wrong catalog, not just a thin one. Every resolution is now identity-checked
by source pointer: reaching the same declaration again is benign, while a
name that resolves to two distinct declarations, to conflicting imports, or
to a declaration in one file and an import in another is a violation — a
verbatim fence has a single flat namespace, so the fix is a rename at the
source. A spec case pins the two-declarations collision.

docs/config-catalog.md also joins the translation-pairing exclusions: it is
a generated English-only doc, and without the entry the gate would accept a
hand-added .zh.md twin it must reject.
2026-07-07 00:00:34 +08:00
Tianyi Cui eaac3b58a4 fix review findings: close wrapped-expression, alias, and binding-pattern gaps
Codex round-2 review found three adjacent fail-open shapes:

- Wrapped function expressions escaped classification: parentheses,
  as/satisfies casts, and non-null assertions are now peeled before the
  arrow/function test (initializers and default exports), and a
  single-call-signature type literal counts as the surface signature.
  A literal mixing call/construct signatures with anything else is
  refused outright — no single signature to hold the tags against.
- The blanket 'export import X = N.member' skip was unsound (the target
  can be a non-exported namespace member no walk visits): an alias now
  documents itself.
- The heritage extra-parameter duty missed binding-pattern extras,
  which no base declaration can name: they now trigger the standard
  binding-pattern violation.

Five new negative-path tests pin the closed shapes; module doc and RFC
updated.
2026-07-06 23:55:16 +08:00
Tianyi Cui 74502fa8c2 Structured output on the subagent seam: schema subset, capture runtime, spawn/fork support
Carved out of #170 per review feedback — the foundation the workflow tool
builds on, now standing alone on master:

- dsh-tools: the structured-output JSON Schema subset (StructuredOutputSchema,
  assertSupportedOutputSchema, validateStructuredValue) — rejects loud outside
  the enforced subset, listing every violation
- dsh-subagent: SubagentStartRequest.outputSchema / SubagentResult.structured
  become a real capability; the service rejects a schema'd request whose
  provider lacks it
- dsh-subagent-inprocess: the shared structured runtime — one global
  structured_output capture tool, a prepend final-assembly listener that
  strips the placeholder for plain agents and swaps in the run's own schema
  (plus the calling instruction as a trailing section) for structured
  children, an agent/turn-continuation veto once captured, and the
  capture/nudge loop in the run driver (structuredNudgeRetries, cancellation
  honored mid-nudge); lifetime refcounted by backends and live runs
- subagent-spawn / subagent-fork flip outputSchema: true

One deliberate divergence from the #170 revision: the backends do NOT add
'tools' to their plugin inject. Doing so deferred their apply past the todo
plugin, and the delegation tool mirrors provider lifecycle — so the
model-visible tool order of every existing prompt changed, invalidating every
recorded snapshot fixture. The runtime now gates its capture-tool registration
on tools availability itself (sync when live, a scoped inject fiber when the
Loader starts the backend first), keeping this PR byte-invisible to existing
transcripts: all 35 snapshot scenarios pass against master's fixtures
unchanged.
2026-07-06 23:29:08 +08:00
Tianyi Cui 9ff010cbef fix review findings: close export-form and heritage-exemption gaps
Codex round-1 review found three fail-open paths in the new gate:

- Unhandled export forms passed silently. checkDecl now fails CLOSED on
  unrecognized exported statement kinds, 'export =' is refused outright,
  'export import X = N.member' is an explicit documented skip (alias;
  definition site owns the doc), and ambient 'declare namespace' bodies
  recurse with implicit export semantics.
- Function-like exports escaped the function contract: non-identifier
  default exports and consts with INLINE function-type annotations now
  get full @param/@returns checks (the named-type waiver stays for
  reference annotations only).
- The heritage exemption was name-only: it no longer exempts a public
  override of a protected-only base member, and parameters the base
  never names keep their @param duty (underscore-prefixed renames of a
  base parameter count as the same parameter).

Eight new negative-path tests pin the closed gaps; RFC and module doc
updated to the refined contract.
2026-07-06 23:25:33 +08:00