Commit Graph

625 Commits

Author SHA1 Message Date
kingwl 3f663c9154 feat(modes): per-session sandbox/approval switching — the session log as the store, ACP config options
effective(session) = findLast(the session own knob events)?.value ?? the
composition-config default. One log-only event per knob, owned by its
domain (bash/sandbox-mode in dsh-bash, approval/policy in dsh-approval),
each exporting the same three-piece kit: the event declaration, a pure
fold, and THE write path — a switch IS its event; no owner service, no
facts map. Restart immunity and multi-session isolation fall out of the
log replay by construction.

Execution follows the fold on both sides: the bash tool stamps
escalation grant > session override > executor default, and the approval
seam prepends the never-gate that auto-rejects before any interactive
answerer. Visibility is two layers per knob: a per-agent prompt section
states the effective value on every request (logged through
request/header*, so what-the-model-was-told replays from the log), and an
agent/pre-step narrator injects at most one coalesced delta notice with
positional attribution (user switch vs operator/config drift). The ACP
bridge advertises one capability-gated select per composable knob with
currentValue folded per session, validates set_config_option against the
closed vocabularies, and anchors idle switches at the next turn
prompt-submit under the turn-enclosure contract.
2026-07-10 15:44:38 +08:00
kingwl 80d8726601 feat(acp): the bridge approval answerer + scripted permission answers
The ACP bridge registers the first real approval answerer: an ask for an
agent it owns becomes session/request_permission attached to the already-
streamed tool call (one-shot allow_once/reject_once only), outcomes map
conservatively (unknown optionId never grants, client cancel → cancelled),
and foreign or call-less requests delegate down the waterfall. The snapshot
harness accepts scripted permissionAnswers (FIFO; an unscripted prompt
answers cancelled, fail closed) so recorded scenarios can drive the wire
keylessly.
2026-07-10 15:43:02 +08:00
kingwl ef35007d75 feat(approval): the approval seam — one-shot permission decisions over a waterfall of answerers
ctx.approval (dsh-approval): request() dispatches the approval/request
waterfall and always resolves a closed outcome — allowed-once / rejected /
cancelled / unavailable — never rejects; zero listeners fall through to
fail-closed unavailable; abort settles cancelled and discards late answers;
throwing or rogue answerers are contained as unavailable; every ask lands
the log-only approval/asked / approval/decided audit pair. dsh-tools routes
a pre-execute ask through the seam opportunistically (ctx.get) with three
distinct deny reasons, keeping the historical ask→deny degrade when the
seam is absent.

The per-session policy tier, the ACP bridge answerer, and the sandbox
escalation asker are staged follow-ups of the approval-seam RFC.
2026-07-10 15:43:02 +08:00
Yichen Jiang 680ae53bb6 Merge remote-tracking branch 'origin/master' into codex/skill-system
# Conflicts:
#	docs/architecture.md
#	docs/config-catalog.md
#	docs/module-graph.md
#	docs/rfc/INDEX.md
#	examples/acp-agent/tests/snapshots/text-turn/session.jsonl
#	packages/core/agent-core/src/index.ts
#	packages/core/tools/tests/gen-tool-catalog.spec.ts
#	packages/support/acp-snapshot/src/suite.ts
#	packages/ui/acp-agent/src/index.ts
2026-07-10 14:43:33 +08:00
Yichen Jiang eee4f02108 Merge remote-tracking branch 'origin/master' into codex/project-instruction-files
# Conflicts:
#	docs/architecture.md
#	docs/config-catalog.md
#	docs/event-producer-consumer.md
#	docs/module-graph.md
#	packages/core/agent-core/src/index.ts
#	packages/ui/acp-agent/src/index.ts
2026-07-10 14:40:33 +08:00
Yichen Jiang 3fe196a751 Adapt workspace context to session prefixes 2026-07-10 14:32:44 +08:00
Yichen Jiang 6292d52236 feat(skill): move catalogs into session prefixes 2026-07-10 14:19:06 +08:00
Yichen Jiang f42cd49337 Merge remote-tracking branch 'origin/master' into codex/rfc-subagent-background-tasks
# Conflicts:
#	docs/architecture.md
#	docs/module-graph.md
#	examples/acp-agent/tests/snapshots/both-mode-turn/session.jsonl
#	examples/acp-agent/tests/snapshots/code-mode-turn/session.jsonl
#	examples/acp-agent/tests/snapshots/text-turn/session.jsonl
#	packages/README.md
#	packages/core/tools/tests/gen-tool-catalog.spec.ts
#	scripts/doc-budgets.manifest.json
#	tsconfig.base.json
#	tsconfig.build.json
#	tsconfig.json
2026-07-10 10:13:13 +08:00
kingwl 63ced3e0e2 feat(mode): the session-mode core — logged per-agent policy state (@deepseek-ai/dsh-mode)
Plan mode's stage 1 (RFC 2026-07-07-plan-mode): a new packages/mode/ group
with one product package owning the mode/set SessionEventMap vocabulary
(log-only, non-surface, whole-value replace), the pure foldMode, and the
ctx.modes service (list/get/set). User flips are pending intents flushed
at turn/start / step/end — turn enclosure makes an idle append illegal —
with one coalesced context/message notice when the flushed mode differs
from what the last logged request header told the model; a folded mode
the config no longer defines reads as default plus one boundary notice.

Enforcement is two covering layers: a system-prompt/assemble wrapper
filters the RETURNED assembly's tools to the mode's allowlist (and shows
exit_plan_mode IFF the folded mode is plan) beside the mode:policy
section at order 50, and a tools/pre-execute gate denies deny-by-default
against the same allowlist, judging by the logged mode only. The default
mode is the absence of policy — assemblies stay byte-identical to a
no-dsh-mode deployment.

AgentOptions.mode (declaration-merged) seeds a child's initial mode
through the same flush on agent/created; the stdio app gains /mode
(print/switch, never sent to the model) over an opportunistic
ctx.get('modes'). Config is an explicit resolve step: the built-in plan
definition (read-only allowlist; bash/subagent excluded until the
sandbox family lands) merges unless overridden, 'default' as a key
throws at load, unknown names throw at set() time.
2026-07-10 01:38:39 +08:00
Yichen Jiang fda827a751 Merge remote-tracking branch 'origin/master' into codex/rfc-subagent-background-tasks
Adopts #211 (Code Mode tools: run_code + the code/both-mode snapshot
scenarios). Tool-catalog expectations take the union (run_code +
task_*); the two new pinsHeader fixtures (code-mode-turn,
both-mode-turn) were recorded on master without the task runtime, so
they are re-pinned KEYLESSLY by replaying their recorded chunks against
the merged tree (same procedure as text-turn) — the fixture diff is
exactly the header delta: task tool schemas, the tool:tasks prompt
section, and the bash background wording.
2026-07-10 00:08:03 +08:00
Tianyi Cui 4751a7be0b Merge origin/master: Code Mode composed with agent scopes
Master advanced 11 commits mid-merge (the Code Mode registry integration:
mode config, run_code, the tools:sdk section, the ACP run_code cards and
unified demos). The fusion makes Code Mode scope-aware end to end:

- wireSchemas(scope): the mode-aware wire contribution is computed from the
  CALLING SCOPE's visible set (scoped tools join, shadowing and restrictions
  apply) and feeds the {schemas, knownNames} provider protocol.
- knownNames under the mode collapse: a per-scope RESTRICTION is runtime
  state, so the universe stays pre-restriction (a restricted-away tool in
  toolOrder is a normal absence) — but the MODE collapse is deployment
  config, so under mode 'code' the universe is [run_code] and a toolOrder
  naming a native tool fails every assembly loud (master's tested decision,
  kept).
- The tools:sdk section renders per assembly CONTEXT: the SDK declares
  exactly the calling agent's callable set, using the section-text provider
  signature this branch already had.
- run_code bindings enumerate schemas(exec.agent) — a program can bind
  exactly what its prompt promised; sub-dispatches already threaded
  exec.agent through registry.execute, so scoped resolution and carriers
  flow unchanged.
- dsh-tools declares both sides' new deps (dsh-scope + dsh-session);
  lockfile and all generated catalogs/graphs/api-catalog regenerated.

Gates green on the merged tree: typecheck, lint, per-file 100% coverage
(2710 tests), snapshots (41), doc-sync, module graph, build, hygiene, demo
smoke.
2026-07-09 23:57:50 +08:00
Tianyi Cui 58a5088feb Merge remote-tracking branch 'origin/master' into workflow-vm-to-workerthread
# Conflicts:
#	docs/config-catalog.md
#	docs/event-producer-consumer.md
#	packages/core/tools/tests/gen-tool-catalog.spec.ts
2026-07-09 23:48:17 +08:00
Tianyi Cui 2489402610 Merge origin/master: scope-aware fusion of the tools/execute seam, session-prefix, and tool-cordis
Master brought 50 commits (the tool-cordis group, dsh-code-runtime + worker,
the tools/execute around-dispatch seam + timeout-policy, repeat-tool-guard,
agent/session-prefix, the ui reorganization). Beyond the ten textual
conflicts, the merge reconciles master's new seams with this branch's
scoped-registration world:

- tools/execute (new waterfall around core dispatch): dispatched with the
  SAME exec.agent carrier as the pre/post waterfalls — an agent.ctx wrapper
  times/retries only its own agent's calls — and its base thunk resolves the
  tool through the caller's visible view (get(exec.name, exec.agent)), so a
  scoped/shadowed tool dispatches and a restricted-away global stays
  UNKNOWN_TOOL. Declared this: Scoped<ToolRegistry> with the scope-filtered
  doc sentence; invariants table + verify-scoped-dispatch pin it (21 events).
- agent/session-prefix (new waterfall, once per loop instance): composed via
  the fused agentEvents dispatcher (scope-filtered like every agent-subject
  event), declared this: Scoped<Agent>, table-pinned. agent/pre-step keeps
  master's new sessionPrefix parameter with this branch's Scoped this.
- timeout-policy reads the budget through the caller's visible view
  (get(exec.name, exec.agent)): a scoped tool's own timeoutMs governs its
  calls; a global name-twin's budget is never misapplied to a shadowing
  per-agent variant.
- tool-cordis: cordis_inspect's tools section lists the CALLING agent's view
  (its description promises "what you can call"); the sandbox tool façade's
  reads resolve through the mount's own scope, mirroring where its register
  lands writes; sandboxRegisterTool's return type carries the exact-disposer
  union honestly. dsh-scope declared as peer+dev with the project reference.
- doc-sync chain unions master's verify-cordis-api with this branch's
  verify-scoped-dispatch; the generated catalogs, event matrix (the
  zero-dispatcher guard passes over master's new events), module graph, and
  the cordis api-catalog are regenerated on the merged surface.

Full gate sequence green on the merged tree: typecheck, lint, per-file 100%
coverage (2668 tests), snapshots (38), doc-sync, module graph, build,
hygiene, demo smoke.
2026-07-09 23:24:42 +08:00
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
Yichen Jiang b9bf67d0a7 Merge remote-tracking branch 'origin/master' into codex/skill-system 2026-07-09 23:11:10 +08:00
imccyu 7d3b16c184 docs: update budget / catalog 2026-07-09 23:10:28 +08:00
Yichen Jiang 7b54768ee7 Merge remote-tracking branch 'origin/master' into codex/project-instruction-files
# Conflicts:
#	AGENTS.md
#	docs/architecture.md
#	docs/event-producer-consumer.md
#	docs/module-graph.md
#	docs/rfc/INDEX.md
#	packages/bash/bash-local/tests/executor.spec.ts
#	packages/bash/bash-local/tests/run.spec.ts
#	packages/core/agent-core/src/index.ts
#	packages/core/agent-core/tests/agent-core.spec.ts
#	packages/fs/fs-local/src/fsio.ts
#	packages/ui/acp-agent/package.json
#	packages/ui/acp-agent/src/index.ts
#	packages/ui/acp-agent/tsconfig.json
#	packages/ui/stdio-agent/package.json
#	packages/ui/stdio-agent/tsconfig.json
#	packages/util/README.md
#	pnpm-lock.yaml
#	tsconfig.build.json
#	tsconfig.json
2026-07-09 23:04:41 +08:00
Tianyi Cui 2da28e89eb Merge remote-tracking branch 'origin/master' into code-mode-tools
# Conflicts:
#	examples/AGENTS.md
#	examples/README.md
#	package.json
#	packages/core/tools/tests/gen-tool-catalog.spec.ts
2026-07-09 22:58:58 +08:00
imccyu 053a2b9403 Merge remote-tracking branch 'origin/master' into workflow-vm-to-workerthread 2026-07-09 22:54:19 +08:00
imccyu 7f5b990aa1 Merge remote-tracking branch 'origin/master' into workflow-vm-to-workerthread 2026-07-09 21:34:52 +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
imccyu d5c65e2b4c docs: describe the workflow engine as worker-thread first
The outer ring catches up with the engine swap (the package's own
README/JSDoc rode the port commit):

- Seam module doc and README name the worker-thread engine as THE
  implementation, with isolated-vm/separate-process sandboxing as the
  deferred hardening; the seam service doc states the holder-owned-runs
  contract (engine-fiber disposal deliberately leaves live runs to
  their holders).
- Seam contract precision: agentsStarted documents the termination-path
  degradation to the host-observed count; the events section scopes the
  agent-start/agent-end pair to calls that STARTED a child run;
  WorkflowRun wording drops the vm-era abandonment language.
- The dynamic-workflows RFC is rewritten in place to the shipped
  mechanism (implemented-RFC rule): why worker threads, the thread's
  concrete buys, the in-process node:vm first cut recorded under
  alternatives considered; the tool section describes the usage policy
  as the tool's own prompt section.
- gen-doc-graphs: six workflow/* DYNAMIC_EVENT_DISPATCHERS entries (the
  catalog no longer claims nothing dispatches them) and the seam-note
  wording; core-data-structures gains its workflow.md index row;
  packages/README + AGENTS.md layout line + example cordis.yml comments
  say worker-thread; catalogs regenerated.
2026-07-09 19:31:56 +08:00
Tianyi Cui a4553cc10d Merge remote-tracking branch 'origin/master' into code-mode-tools
# Conflicts:
#	docs/cordis-catalog/events.md
#	docs/cordis-catalog/services.md
#	docs/event-producer-consumer.md
#	docs/tool-execution-pipeline.md
#	packages/core/tools/README.md
#	scripts/gen-doc-graphs.ts
2026-07-09 13:30:26 +08:00
Yichen Jiang e4331ca5e7 Merge branch 'master' into agent-request-messages 2026-07-09 13:30:11 +08:00
Dudu-0223 78318f5d12 Merge remote-tracking branch 'origin/master' into codex/truncated-design
# Conflicts:
#	docs/event-producer-consumer.md
#	docs/module-graph.md
#	docs/rfc/INDEX.md
#	knip.json
#	packages/README.md
#	scripts/gen-module-graph.ts
#	tsconfig.base.json
#	tsconfig.build.json
#	tsconfig.json
2026-07-09 12:54:30 +08:00
Tianyi Cui 4e8dc8e8f5 docs: fail the event matrix on a zero-dispatcher row; attribute provider-removed
The chained-fused-dispatch spelling fix (0dc2fe90) was whack-a-mole: each
unrecognized dispatch spelling silently drops a producer edge from the
generated matrix, and only a human reading the table catches it. Convert the
class to a build failure: the generator now hard-errors when any DECLARED
event ends with zero dispatchers — dead vocabulary or a missed spelling,
both actionable ('teach the scan or add a DYNAMIC_EVENT_DISPATCHERS
override'). Zero LISTENERS stays legal: seven current rows (agent/request,
system-prompt/assemble, tools/change, ...) are ordinary extension points
dispatched for out-of-repo plugins.

The guard caught a real one on its first run: subagent/provider-removed
routes through the same contained events.dispatch as subagent/start|end
(it fires inside the provider registration's disposer), but the
DYNAMIC_EVENT_DISPATCHERS override list never got an entry when that
containment routing was introduced — the committed matrix (on master too)
claimed the event has NO dispatcher while tool-subagent listens for it.
Override added; matrix regenerated with the producer edge restored.
2026-07-09 12:36:18 +08:00
Dudu-0223 0a3ffe1b04 Merge remote-tracking branch 'origin/master' into timeout-design
# Conflicts:
#	docs/event-producer-consumer.md
#	packages/README.md
2026-07-09 11:52:18 +08:00
Tianyi Cui 673f20c990 Merge remote-tracking branch 'origin/master' into code-mode-tools
# Conflicts:
#	docs/event-producer-consumer.md
#	packages/support/acp-snapshot/tests/suite.spec.ts
2026-07-09 11:06:50 +08:00
Tianyi Cui 8819c71b81 docs: teach the graph generator the CHAINED fused-dispatch spelling
ds-review-bot round-3 finding: agentEvents(ctx, agent).emit(...) has a
call-expression receiver the generator's identifier check missed, silently
dropping agent-loop as agent/session-start's producer. The generator now
recognizes a call receiver whose callee is agentEvents; graph regenerated
with the producer edge restored.
2026-07-09 06:06:17 +08:00
Tianyi Cui e7bcbb8bc6 feat(dx): scopeHost, agent-aware ACP presentation, and the scoped-dispatch drift gate
scopeHost(ctx, services) is the sanctioned way to mint scopes in tests: it
names absent services loudly instead of the cryptic cordis without-inject
dead end, and catches the silent-no-op host (cordis resolves a
dependency-pending fiber's await without running the inject callback).

The ACP ToolPresenter resolves presentations through the session agent's
view (tools.get(name, agent)) so a scoped/shadowed tool renders with the
same definition that executed.

verify-scoped-dispatch (doc-sync + pre-push) pins the dev-invariants
carrier table against the declaration JSDoc set: an event enforced but
undocumented, documented but unenforced, or a registry-subject notification
leaking into the table fails the build. subagent/start|end docs gain their
scoped-dispatch sentence (a real gap the gate caught on first run).
2026-07-09 02:38:54 +08:00
Tianyi Cui f91eb39538 docs: regenerate catalogs and sync subagent type-equiv blocks for persona/toolFilter 2026-07-09 02:18:39 +08:00
Tianyi Cui 67cb9a591d docs: teach graph generator the scoped-dispatch spellings; sync Agent type-equiv block
The producer/consumer matrix reads dispatch sites statically; the fused
agentEvents dispatcher, the agent's loopCtx handle, the session store's
captured emitCtx, and carrier-first argument lists were invisible to it,
silently dropping agent-loop/session as producers of every scoped event.
The generator now recognizes those spellings; the Agent type-equiv doc
block gains readonly ctx.
2026-07-09 01:36:14 +08:00
Yichen Jiang 0d023b9aaa Merge remote-tracking branch 'origin/master' into agent-request-messages 2026-07-08 22:26:25 +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
Dudu-0223 463b72ce96 feat(spill): add tool-output spill seam, local backend, and policy
Oversized plain-text tool results now spill to a session-scoped file and
return a bounded preview plus the spill path, so a verbose result stays
readable via `read` without consuming the next model request in full.

- dsh-spill: minimal SpillFiles seam (saveText → session-scoped SpillPath)
- dsh-spill-local: private 0700 session dirs, traversal-safe names, exclusive
  owner-only writes
- dsh-spill-policy: tools/post-execute transformer; no-op unless maxInlineBytes
  is set; skips read; best-effort on save failure (never turns a success into
  an isError)

web_fetch is the showcase — no tool-specific spill code. The coding-agent
example loads the stack so its keyless Loader smoke guards the namespace-plugin
export shape. Snapshot gap for a transcript-visible web_fetch spill is recorded
in the RFC's Consequences (ACP replay is keyless and cannot hit the web).
2026-07-08 20:41:55 +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 f453ba77a2 refactor: split skill providers 2026-07-08 15:50:38 +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 395a0b8336 docs(timeout): update RFC + generated catalogs for the declaration split
The RFC's deployment-policy decision is unchanged; state the current
mechanism in place — the per-tool budget is declared on ToolDefinition
(timeoutMs, set by the owning tool plugin from its config) and the
enforcer is zero-config, so a mistyped tool name is impossible.
Regenerate config-catalog (timeout-policy -> no-config; tool-web gains
fetch/searchTimeoutMs), the event graph (tools/change loses its
timeout-policy consumer), the ToolDefinition type-equiv block, and a
source-line drift in the cordis services catalog.
2026-07-08 15:06:02 +08:00
kingwl db26ef479d feat(guard): add the repeat-tool-guard plugin 2026-07-08 14:24:20 +08:00
Tianyi Cui b59d245c7c feat: Code Mode — the registry's mode config, the SDK codegen, and the run_code bridge
The dsh-tools half of the Code Mode RFC (its fourth, final change): the
registry gains its first config — mode: native | code | both — and OWNS how
its tools reach the model. 'code' contributes exactly one wire tool,
run_code, plus a lazy tools:sdk prompt section declaring every other tool
as a generated TypeScript API (jsonSchemaToTs: total over the defineTool
subset, unknown degradation, lexicographic byte-identical rendering);
'both' ships both representations; 'native' is byte-for-byte the old
behavior. Non-native modes fail every assembly loudly without a
typescript-language ctx.codeRuntime.

run_code's dispatch bridge: JSON-normalizes each binding argument before
dispatch (what dispatches is what the tool/code-dispatch event logs — the
append can never fail on payload shape; BigInt/circulars reject that one
call), serializes all program tool calls through a per-run queue (even
Promise.all — no concurrency-safety metadata yet), routes every sub-call
through tools/pre-execute → tools/post-execute (a deny rejects the
program-side promise), drops sub-call additionalContext (no safe outlet
mid-run; pinned), owns a run-scoped abort that follows the outer signal in
and fires on settlement (in-flight sub-dispatch aborted, queued abandoned,
queue drained before returning), and converts a failed run into
CodeRunFailedError → a structured isError carrying kind + captured logs.
tool/code-dispatch joins SessionEventMap by declaration merging (log-only;
deriveMessages ignores it).

The composed surface: the tools config forwards through agent-core and
both app packages; examples/code-agent + demo:code run the worker runtime
under mode code (keyless boot smoke + a with-key e2e proving the collapsed
[run_code] header, the dispatch events, and the file the program wrote);
two new snapshot scenarios (code-mode-turn, both-mode-turn) record the SDK
section, collapsed header, dispatch events, and result card — each its own
header-pinning class (the harness gains per-scenario config overlays and
per-class pins). Catalogs, graphs, cookbook, hooks-bridge notes, and the
RFC (moved to implemented/, restructured to decision-era headings) updated
in the same change.
2026-07-08 12:58:23 +08:00
Dudu-0223 3265bdbf70 fix(timeout-policy): warn on configured-but-unregistered tool names
ds-review-bot flagged that a typo'd or stale config key (e.g. web_fech for
web_fetch) silently applies the timeout to nothing — the tools/execute lookup
just never matches. Mirror dsh-tool-subagent's lifecycle-driven handling of a
configured-but-unregistered provider: on every tools/change (and once at load),
logger.warn each configured name still absent from ctx.tools, warning each name
at most once so a late registration silences it. Not a load-time throw — the
tool set is dynamic (cordis.yml load order, HMR), so a real tool may register
later.

Declare inject = ['tools'] since the plugin now reads ctx.tools synchronously
in apply (previously only inside event callbacks). Regenerate config-catalog
(Requires: tools) and event-producer-consumer graph.
2026-07-08 11:43:29 +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 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 8db99d36e8 Merge branch 'structured-output-subagent-seam' into worktree-dynamic-workflows
# Conflicts:
#	examples/acp-agent/tests/snapshots/fs-policy-reject/session.jsonl
#	examples/acp-agent/tests/snapshots/fs-policy-reject/stdout.golden.jsonl
2026-07-07 22:21:44 +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