Commit Graph

787 Commits

Author SHA1 Message Date
Yichen Jiang
740106a51a Merge remote-tracking branch 'origin/master' into codex/rfc-subagent-background-tasks
# Conflicts:
#	docs/config-catalog.md
#	docs/cordis-catalog/services.md
#	docs/core-data-structures/bash.md
#	docs/core-data-structures/core.md
#	docs/event-producer-consumer.md
#	docs/module-graph.md
#	docs/tool-catalog.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/bash/README.md
#	packages/bash/bash-local/src/index.ts
#	packages/bash/bash/README.md
#	packages/bash/bash/package.json
#	packages/bash/bash/src/index.ts
#	packages/bash/bash/src/types.ts
#	packages/bash/bash/tests/service.spec.ts
#	packages/bash/bash/tsconfig.json
#	packages/bash/tool-bash/README.md
#	packages/bash/tool-bash/src/index.ts
#	packages/bash/tool-bash/tests/tools.spec.ts
#	packages/bash/tool-bash/tsconfig.json
#	packages/cordis/tool-cordis/src/api-catalog.ts
#	packages/core/agent-core/README.md
#	packages/core/agent-core/package.json
#	packages/core/agent-core/src/index.ts
#	packages/core/agent-core/tests/agent-core.spec.ts
#	packages/core/tools/tests/gen-tool-catalog.spec.ts
#	packages/hooks/hook-protocol/tests/runner.spec.ts
#	packages/ui/acp-agent/tests/acp-agent.spec.ts
#	packages/ui/stdio-agent/tests/stdio-agent.spec.ts
#	pnpm-lock.yaml
#	scripts/doc-budgets.manifest.json
#	scripts/gen-tool-catalog.ts
2026-07-11 23:04:27 +08:00
Tianyi Cui
3baaecc078 Merge remote-tracking branch 'origin/master' into codex/skill-system
# Conflicts:
#	docs/rfc/INDEX.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
2026-07-11 22:31:28 +08:00
Tianyi Cui
28f7fa0969 docs(rfc): record deferred skill extensions 2026-07-11 22:18:58 +08:00
Tianyi Cui
0d23d95dde docs(sandbox): record follow-up boundaries 2026-07-11 22:07:56 +08:00
Tianyi Cui
1e2d8014a5 Merge remote-tracking branch 'origin/master' into feat/sandbox-support
# Conflicts:
#	packages/workflow/workflow-workerthread/tests/workflow-workerthread.spec.ts
2026-07-11 21:43:10 +08:00
Tianyi Cui
b29a8eca71 fix(review): reconcile sandbox and approval contracts 2026-07-11 21:37:38 +08:00
Yichen Jiang
386d4d3ec4 Merge branch 'master' into codex/skill-system 2026-07-10 22:58:15 +08:00
Tianyi Cui
65587c6091 Merge remote-tracking branch 'origin/master' into codex/mock-snapshot-rerecord
# Conflicts:
#	packages/workflow/workflow-workerthread/tests/workflow-workerthread.spec.ts
2026-07-10 22:19:18 +08:00
kingwl
ca39fd89b0 feat(example): sandbox-acp-agent — the live composition; RFCs to implemented
The three-entry cordis.yml (dsh-sandbox-local + dsh-bash-sandbox at a
read-only default + dsh-approval) served over ACP: the first live approval
composition. Recorded snapshot scenarios pin the wire end to end —
config-options advertisement, the mode-switching arc as the suite pinned
header (both switches, the prompt-section delta, one changed-by-the-user
notice per knob, a confined write landing under the switched mode), and
both escalation branches over scripted permissionAnswers (a grant runs
confined under workspace-write; a rejection executes nothing and pins the
fail-closed text). The with-key escalation e2e drives a real model +
real runner + the real bridge answerer, world-verified; ci.yml snapshot
lane and e2e.yml install bubblewrap so the confined replays actually
execute.

Both RFCs move to implemented/ (Decision/Consequences form, deferred
phases tracked in their own sections), with every cross-reference flipped.
2026-07-10 15:44:38 +08:00
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
0e49615a3d feat(tool-bash): sandbox escalation — one approved wider retry after a denial
The tool gate advertises sandbox_permissions (an enum of exactly the modes
STRICTLY WIDER than the mounted executor default — the schema makes a
non-widening request inexpressible) plus a required justification, exactly
when ctx.bash.sandboxMode reports a confining mode at registration:
composition truth, never a dead lever. An escalating call resolves
ctx.approval BEFORE anything executes with the audit-self-contained reason
"escalate sandbox to <mode>: <justification>"; allowed-once stamps the
granted mode onto that one bash request (the seam-level per-call override),
while rejected / cancelled / unavailable and the no-service / no-agent
paths each fail closed with their own error text and execute nothing. The
description teaches the flow only when the fields exist: retry the SAME
command once after a real denial, never preemptively; a rejected
escalation is final. No new session events: the attempt is an ordinary
tool/call, the decision is the approval audit pair, the outcome an
ordinary tool/result whose facts name the mode it ran under.
2026-07-10 15:43:28 +08:00
kingwl
2eed448acf feat(bash): the sandboxed executor — per-call policy carrier, denial facts, runner-failure classification
dsh-bash grows the per-call policy carrier: BashExecRequest.sandboxMode
(request-optional, spec required-but-nullable — the owner pattern; resolve()
is the one explicit defaulting step) and the BashExecutor.sandboxMode
capability fact (undefined in the base class — composition truth the tool
layer can read). dsh-bash-local carries the field verbatim and confines
nothing.

dsh-bash-sandbox extends LocalBashExecutor and hands ctx.sandbox the exact
argv it is about to spawn. A denial is a RESULT FACT (the command RAN;
result.sandbox.denied is orthogonal to exitCode/signal), classified
conservatively against the wrap own dialect; a RUNNER failure outranks
denial — foreground re-throws the structured SANDBOX_UNAVAILABLE, a settled
background task stamps sandbox.runnerFailed — so a broken sandbox never
reads as a failing command and the command never runs unconfined.
dsh-tool-bash renders the markers and teaches the model not to retry around
a policy denial; escalation and per-session switching are staged follow-ups.
2026-07-10 15:43:02 +08:00
kingwl
7b8c3a9b40 feat(sandbox): the confinement seam and the per-platform native runner chains
ctx.sandbox (dsh-sandbox): confine(argv, policy) returns the argv to spawn
instead — wrapped so the process and its children run confined — plus the
enforcement completeness and the backend denial/runner-failure dialects;
no usable backend throws the fail-closed SANDBOX_UNAVAILABLE. Policy rides
per call. dsh-sandbox-local selects by platform and caches the verdict:
multi-candidate chains probe FUNCTIONALLY in preference order (Linux:
bwrap → the registry-installed node-addon-landlock-run launcher), a sole
candidate is selected unprobed (darwin: sandbox-exec/Seatbelt) and fails
closed at execution via runnerFailureSignatures; win32 is a reserved empty
chain. Profile parity is honest per backend (documented temp-area and ABI
differences; enforcement full|partial is a structured result fact).

CI: the sandbox-e2e matrix proves real-kernel confinement per rung (bwrap,
Landlock per architecture through the registry-installed launcher,
Seatbelt), failing on a silent all-skip; the packed-install rehearsal
installs the launcher family from the registry and asserts the binary
executable apart from kernel enforcement.
2026-07-10 15:43:02 +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
6292d52236 feat(skill): move catalogs into session prefixes 2026-07-10 14:19:06 +08:00
Yichen Jiang
b4d09d3a2d Merge remote-tracking branch 'origin/master' into codex/rfc-subagent-background-tasks
# Conflicts:
#	docs/module-graph.md
2026-07-10 13:43:09 +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
pku-xht
3eea868a1b Merge branch 'master' into feat/subagent-process 2026-07-10 10:05:01 +08:00
kingwl
7afdc6e3b9 docs(rfc): escalation targets are a closed static vocabulary; strict widening moves to an execution-time check (review blocker)
The default-relative ladder conflicts with per-session overrides: schemas
are registry-global while the effective mode is switchable, so a session
overridden NARROWER than the default loses exactly the lever it needs — a
workspace-write default with a read-only override could only request
danger-full-access (forced over-escalation), and a danger-full-access
default with a read-only override advertised no fields at all (confined,
no escalation path). The enum now pins the closed target vocabulary
(workspace-write / danger-full-access) whenever the executor confines;
strict widening is enforced per call against the session effective mode
(override ?? default), failing closed without prompting anyone. The
default-relative ladder and per-session dynamic schemas move to
Alternatives; the harmless Risks claim is corrected to name the runtime
check as the safety boundary.
2026-07-10 02:34:11 +08:00
kingwl
d3a833fc4a docs(rfc): speak of the staged example and recordings at plan-time tense (review)
The proposed forms claimed examples/sandbox-acp-agent, its recorded
scenarios, and the approval servicing of ask as already existing; a reader
of this docs-only change would look for a composition and goldens that are
not in the tree. Usage walkthroughs now name the arc the staged scenarios
are to record, both Testing sections open with the plan-time banner, and
the grounding bullet states todays degrade honestly.
2026-07-10 02:34:11 +08:00
kingwl
3191cb9404 docs(rfc): propose the approval seam and the subprocess sandbox 2026-07-10 02:33:22 +08:00
Tianyi Cui
9d2cf8ce82 Add keyless snapshot refresh mode 2026-07-10 00:48:27 +08:00
imccyu
3d141eea57 Merge remote-tracking branch 'origin/master' into workflow-vm-to-workerthread 2026-07-10 00:35:16 +08:00
Turtle
c60eb87860 make wordcount budget guidance clearer and dedup docs 2026-07-10 00:20:35 +08:00
Turtle
f77174f13a make it clearer 2026-07-10 00:13:06 +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
Turtle
bf1ab14b78 a human touch
get some headroom
2026-07-09 23:48:29 +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
Yichen Jiang
35acd34fd9 fix(tools): enforce a disabled run_in_background at execution time (review findings)
enableRunInBackground: false removed the parameter from the advertised
schema only — the arg validator deliberately allows undeclared keys, so
a caller (or a model that has seen the parameter elsewhere) could still
force run_in_background: true and start background work past the
deployment's opt-out, in both tool-bash and tool-subagent. Both
producers now refuse the forced key loud in execute(); tests pin the
refusal (and that nothing spawns) alongside the untouched foreground
path; the schema-omission-is-advertising rule is recorded in the
runtime RFC and both READMEs.
2026-07-09 23:37:51 +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
43d60286e3 fix(tasks): an aborted wait must not leave a settled task notice-suppressed (review finding)
Two windows where settle() could read a live waiter count, mark the
task reported (suppressing the completion notice), and then watch that
waiter reject with 'wait aborted' delivering nothing — leaving the
owning session with no terminal notification at all:

- abort and settlement in the same tick, settle continuation ordered
  first: the waiter now un-counts itself SYNCHRONOUSLY inside onAbort
  (the finally decrement alone lands a microtask too late), so the
  settle path sees no live waiter and the notice fires;
- abort landing after settlement but before the wait's resolve
  microtask: the wait now resolves and DELIVERS the terminal snapshot
  it owes instead of rejecting (settlement suppressed the notice on
  this waiter's behalf).

Both windows pinned by deterministic tests that fail on the previous
implementation; wait()'s abort contract updated in JSDoc/README/RFC.
2026-07-09 23:08:26 +08:00
imccyu
64d0703c40 workflow: host-guarantee the agent-start/agent-end pairing on every stop path
agent-end was worker-authored only, so a start already forwarded to
observers lost its paired end whenever the worker could no longer speak
- the grace force-settle terminating a wedged script, or an unexpected
worker death - stranding progress consumers with agents that never
finish (ds-review-bot finding on #233).

The host now keeps a ledger of forwarded starts and funnels every
agent-end through one gate: worker-reported ends pair (and clear) their
entry, and both termination paths drain the remainder as synthesized
'cancelled' ends BEFORE the run settles, so ends always precede
workflow/end. A real settlement racing the force-settle loses to the
synthesized cancellation - the same first-wins override onResult applies
to the run's own result.
2026-07-09 23:05:43 +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
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
2c812fb84a Merge remote-tracking branch 'origin/master' into workflow-vm-to-workerthread 2026-07-09 22:36:52 +08:00
Tianyi Cui
d976767fa6 Merge branch 'master' into recall-renderer-shared 2026-07-09 22:34:56 +08:00
Tianyi Cui
88ab5ce0bc Merge branch 'master' into feat/subagent-process 2026-07-09 22:19:30 +08:00
imccyu
fe4da9244f fix(tool-cordis): validate a dynamic tool's execute return shape after the realm round-trip
The sandbox execute wrapper JSON round-tripped the return and blindly cast it
to ToolExecuteReturn. A JSON-valid but wrong-shape return — a bare string,
{ content: 'ok' }, blocks without a type tag — sailed through: the registry
spreads result.content, so { content: 'ok' } became ['o','k'], passed the
session log's isJsonValue gate, and the DeepSeek serializer then flattened it
to '(no output)' — silent corruption of the next model request and every
replay, instead of a contained tool error.

The round-tripped value is now shape-checked against the two ToolExecuteReturn
forms (array of content blocks, or { content: blocks, meta? }); block checks
are structural only (plain object + string type tag) because the ContentBlock
union is merge-extensible. A wrong shape — and the formerly cryptic
forgot-return/bare-string cases — fails that one call with a teaching error
echoing a truncated preview of what was returned and the two valid forms.
New specs pin the object-form pass-through (meta included), six rejection
shapes, and the preview truncation; per-file 100% coverage holds.
2026-07-09 22:12:30 +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
bd59fddacd refactor(tasks): declare-then-execute — ctx.tasks.start() replaces register()
start({ kind, label, owner, run }) preflights everything that can fail
(the attachSurface fence, validation, the owner-cleanup attach) BEFORE
invoking the producer's run() starter, then commits atomically —
'work started but never got a collectable id' is now structurally
impossible instead of a producer try/catch rollback obligation (the
P1 review fix, rebuilt on #185's declare/execute split). Producers
lose their catch-wraps; the leak tests now pin the stronger property
that a failed preflight never spawns anything. TaskRegistration splits
into TaskStart (identity + run) and TaskHooks (cancel/done/readOutput);
docs, type-equiv manifest, catalogs, and both RFCs move with it.
2026-07-09 21:55:07 +08:00
Yichen Jiang
f858c647a6 refactor(tasks): drive wait() timing through dsh-timeout
ctx.tasks.wait arms a deadline() fusing the caller's abort with the
wait timeout and classifies the outcome with timeoutOf scoped to the
new TASK_WAIT_TIMEOUT code: a wait timeout resolves to the live
snapshot (the task keeps running), a caller abort rejects the wait —
same contract, no hand-rolled timer/listener plumbing, and a nested
foreign deadline can no longer misread as a wait timeout. task_output
deliberately declares NO ToolDefinition.timeoutMs: timeout-policy
turns a timed-out call into a structured TOOL_TIMEOUT failure, but a
timed-out wait is a SUCCESS that must still report [status: running]
(decision recorded in the runtime RFC alternatives).
2026-07-09 21:36:56 +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
Tianyi Cui
387f19c7f6 docs: regenerate the module graph for the ask-user merge 2026-07-09 21:23:45 +08:00