docs: rebalance prose cleanup and add trimming skill

This commit is contained in:
Tianyi Cui
2026-07-13 23:27:00 +08:00
parent fcdc318dda
commit 148046b9c8
392 changed files with 2801 additions and 1754 deletions

View File

@@ -1,7 +1,8 @@
/**
* The out-of-process ACP subagent backend: registers a {@link SubagentProvider} on
* `ctx.subagents` that runs each child agent in a spawned SUBPROCESS, driven over the Agent
* Client Protocol (ACP) as the client.
* Out-of-process ACP subagent backend. Each child has its own process, session, model, and
* tools, so it shares no Cordis context, ignores `request.parent`, and advertises no parent-
* enforced start capabilities. This plugin uses named exports only; a default would hide its
* loader metadata (see `docs/postmortem/0001-acp-default-export-drops-inject.md`).
* @module @deepseek-ai/dsh-subagent-acp
*/

View File

@@ -1,6 +1,10 @@
/**
* Fresh-process ACP subagent client. Drives one child session and owns process
* cancellation and quiescent disposal.
* Fresh-process ACP subagent client. Drives one child session and owns cancellation and
* quiescent disposal.
*
* TODO(acp-subagent-replay): add snapshot-tier coverage with a separate replay fixture and
* sessions root inside each child process. Current keyless coverage uses a scripted ACP child;
* with-key coverage drives the real ACP example.
* @module @deepseek-ai/dsh-subagent-acp/run
*/
@@ -24,7 +28,7 @@ import type { ContentBlock } from '@deepseek-ai/dsh-llm'
import type { SubagentResult, SubagentRun, SubagentStartRequest, SubagentStopReason } from '@deepseek-ai/dsh-subagent'
import { buildChildEnv, disposeChildProcess, spawnFailure } from '@deepseek-ai/dsh-subagent-subprocess'
/** Fixed response to child permission requests: reject, or first allow option. */
/** Fixed response to child permission requests: reject by default, or select the first allow option. */
export type PermissionPolicy = 'allow' | 'reject'
/** Resolved spawn spec for an ACP child process (no defaults — see Config). */
@@ -68,7 +72,7 @@ export interface AcpRunSpec {
onError?: (error: Error, stopReason: SubagentStopReason) => void
}
/** Default EOF grace for child flush and nested-process teardown before signaling. */
/** EOF grace for child flush and nested-process teardown; wider than the signal grace below. */
export const DEFAULT_DISPOSE_EOF_GRACE_MS = 6_000
/** Default grace between SIGTERM and SIGKILL on dispose (the `disposeGraceMs` config; mirrors the bash executor). */

View File

@@ -1,7 +1,9 @@
/**
* A minimal mock ACP AGENT, run as a subprocess, for the keyless `dsh-subagent-acp` tests. It
* speaks the agent side of ACP over stdio and is fully scripted by environment variables — no
* model, no network.
* Minimal no-network ACP child process for keyless backend tests. Environment variables script its
* text and stop reason, a cancel-cooperative or cancel-ignoring hang, permission requests, and a
* readiness marker. Disposal fixtures can delay an EOF flush, ignore EOF but exit and mark
* SIGTERM, or trap SIGTERM to require SIGKILL. The specs spawn this non-test module under tsx with
* an explicit tsconfig, mirroring real example boot.
* @module @deepseek-ai/dsh-subagent-acp/tests/mock-acp-server
*/
@@ -122,8 +124,8 @@ function makeAgent(conn: AgentSideConnection): Agent {
process.exit(1)
}
if (IGNORE_CANCEL) {
// A NON-COOPERATIVE child: receive session/cancel but never resolve the pending prompt
// and never exit.
// A non-cooperative child receives cancellation but neither resolves nor exits. The
// backend must still settle `aborted`, and disposal must kill the process.
return Promise.resolve()
}
resolveCancel?.('cancelled')
@@ -142,7 +144,7 @@ new AgentSideConnection(
// Under MOCK_TRAP_SIGTERM, ignore SIGTERM and keep stdin open so the process neither quiesces
// on EOF nor dies on the graceful signal — exercising the backend dispose path's SIGKILL
// escalation.
// escalation. READY_FILE proves the trap was armed before the test disposes the run.
if (process.env.MOCK_TRAP_SIGTERM === '1') {
process.on('SIGTERM', () => { /* trapped: refuse to exit on the graceful signal */ })
// Keep the event loop alive (a bare timer) so nothing else lets it exit.
@@ -152,7 +154,8 @@ if (process.env.MOCK_TRAP_SIGTERM === '1') {
// Under MOCK_FLUSH_ON_EOF, model the real acp-agent's EOF-driven quiesce: on stdin 'end' (the
// dispose path's `child.stdin.end()`), take an ASYNC beat to "flush", then touch the marker and
// exit ON OUR own — no signal involved.
// exit on its own. A signal sent before MOCK_FLUSH_DELAY_MS would suppress the marker, so it proves
// the EOF grace window was long enough for durable flush.
if (FLUSH_ON_EOF !== undefined) {
const flushDelayMs = Number(process.env.MOCK_FLUSH_DELAY_MS ?? '150')
process.stdin.on('end', () => {
@@ -163,7 +166,9 @@ if (FLUSH_ON_EOF !== undefined) {
})
}
// Ignore EOF but exit on SIGTERM to exercise the middle disposal tier.
// Ignore EOF but exit on SIGTERM to exercise the middle disposal tier before SIGKILL. The signal
// marker distinguishes that catchable rung from an immediate, uncatchable SIGKILL; READY_FILE
// proves the handler was armed before disposal.
if (process.env.MOCK_IGNORE_EOF === '1') {
const sigtermFile = process.env.MOCK_SIGTERM_FILE
process.on('SIGTERM', () => {

View File

@@ -9,7 +9,9 @@ import SubagentService from '@deepseek-ai/dsh-subagent'
import * as acp from '../src/index.ts'
/**
* With-key e2e for the ACP subagent backend: the harness drives ITSELF as an ACP server.
* With-key cross-process seam proof: the backend spawns the real acp-agent example, speaks ACP over
* stdio, and returns its real model answer. This is the out-of-process counterpart to in-process
* spawn coverage and self-skips without `DEEPSEEK_API_KEY`.
*/
// The real acp-agent example: its bin + cordis.yml (the live DeepSeek config).