mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-08-29 04:26:38 +00:00
Rename the tool-presentation transport from code-mode to ptc everywhere that is not written into session logs: the mode config value becomes 'ptc', the preset directory/id becomes ptc, the demo becomes demo:ptc, the dispatch waterfall becomes tools/ptc-dispatch-log (types PtcDispatch*), the prompt rule becomes tools:ptc-only, source/test files become ptc.ts etc., and prose says PTC mode / PTC 模式. The session-persistent vocabulary (durable events tool/code-dispatch*, logged plugin name tools-code-mode, sub-call id segment :code:) intentionally stays and moves in the stacked persistence PR, which is blocked until the SESSION_FORMAT_VERSION v0→v1 migration lands with it. run_code, its code parameter, CodeSdkLanguage, CodeRunFailedError, the dsh-code-runtime family, third-party codex names, and frozen archived notes keep their names.
156 lines
16 KiB
Markdown
156 lines
16 KiB
Markdown
# AGENTS.md
|
|
|
|
DeepSeek Harness is an all-plugin Cordis agent harness. Read [docs/architecture.md](docs/architecture.md) before changing `packages/`; follow [docs/AGENTS.md](docs/AGENTS.md) for documentation.
|
|
|
|
## Pre-release stance: foundation over blast radius
|
|
|
|
**Remove at the first tagged release.** Until then, prefer correct foundations to compatibility shims: rename or repackage freely and update every reference. Backends reject old on-disk formats. SQLite uses monotonic `SCHEMA_VERSION`; `dsh-session` keeps `SESSION_FORMAT_VERSION` at `0` with no compatibility promise.
|
|
|
|
**Application launch.** Only `dsh` profiles launch supported Node apps; package bins, demos, and public SDK argv escapes are forbidden ([rule](docs/architecture.md#application-launch)).
|
|
|
|
## Repository layout
|
|
|
|
```
|
|
vendor/ Vendored Cordis source — manifest + sync procedure in vendor/README.md
|
|
packages/ @deepseek-ai/dsh-<pkg> workspaces at packages/<group>/<pkg>/
|
|
core/ product API spine: session, system-prompt, tools, agent, agent-loop
|
|
api/ Remote BFF assembly and Typert RPC gateway
|
|
typert/ type graph generator, loader, and runtime registry
|
|
llm/ LLM capability: Service Definition/Consumer + DeepSeek providers
|
|
e2b/ E2B POC: sandbox + FS/subprocess adapters
|
|
shell/ bash capability: Service Definition + local/pwsh providers + shell Consumers
|
|
subprocess/ subprocess capability + local process-tree provider + shared Win32 library
|
|
terminal/ persistent sessions
|
|
fs/ filesystem capability + policy
|
|
lsp/ language-server capability
|
|
skill/ skill provider registry + local impl + catalog/loader tool
|
|
web/ web capability: Service Definition + search/fetch providers + tool Consumer
|
|
compaction/ compaction capability + basic provider
|
|
context/ request-context plugins
|
|
subagent/ subagent capability: Service Definition + providers + delegation Consumers
|
|
bundle/ installable dsh --profile patch-layer bundles
|
|
workflow/ workflow capability + worker-thread provider + tool Consumer
|
|
webhook/ webhook ingress
|
|
todo/ todo_write tool
|
|
plan/ plan mode as logged state
|
|
preset/ per-session agent composition from preset cordis.yml files
|
|
guard/ loop-hygiene + tool-timeout plugins
|
|
self-modification/ the agent inspects/mounts its own plugins
|
|
hooks/ Claude Code/Codex hook bridges + wire-protocol library
|
|
session/ durable session data: persistence, projection, titles, telemetry
|
|
identity/ anonymous identity
|
|
settings/ user-settings capability + file provider
|
|
credentials/ credential/authorization capabilities + env/.env provider
|
|
acp/ automation-only Agent Client Protocol server
|
|
interaction/ approval/interaction capabilities, permission, commands, ask-user
|
|
boot/ shared profile/application boot glue
|
|
sdk/ JSON-RPC protocol + TypeScript client/server
|
|
examples/ reusable composition bundles (agent-spine)
|
|
experimental/ private prototypes excluded from official releases
|
|
support/ dev/test infrastructure
|
|
util/ zero-dependency utilities
|
|
python/ Python SDK and bundled runtime (see python/README.md)
|
|
native/ @deepseek-ai/node-addon-landlock-run source of record (see native/README.md)
|
|
.agents/ Agent workflows and Agent Notes (`notes/`)
|
|
docs/ architecture, generated catalogs, postmortems, cookbook (see docs/AGENTS.md)
|
|
scripts/ repo gates and generators
|
|
website/ VitePress projection of selected bilingual docs/ sources
|
|
```
|
|
|
|
Package groups: [packages/README.md](packages/README.md).
|
|
|
|
## Commands
|
|
|
|
```sh
|
|
pnpm install # pnpm workspaces, node ^22.19 || >=24
|
|
pnpm run clean # remove build outputs and safe residue from deleted packages
|
|
pnpm run test # unit tests
|
|
pnpm run test:coverage # CI coverage gate: per-file 100% on packages/*/*/src
|
|
pnpm run test:e2e # real-API tests; self-skip without DEEPSEEK_API_KEY
|
|
pnpm run test:expected # owner-local process expectations
|
|
pnpm run test:snapshot # keyless recorded-session replay through shipped profiles; filter: -t <name>
|
|
pnpm run test:snapshot:record # re-record expected outputs (needs key)
|
|
pnpm run typecheck
|
|
pnpm run lint
|
|
pnpm run duplication # cross-file TypeScript clone detection
|
|
pnpm run build # tsc emits lib/types, tsdown bundles runtime
|
|
pnpm run hygiene # knip + publint + workspace constraints + NodeNext consumer check
|
|
pnpm run check:windows-wine # ONLY when diagnosing a known Windows failure (needs wine); CI owns this signal
|
|
pnpm run doc-sync # all documentation gates; leaf list in scripts/run-gates.ts
|
|
pnpm run test:docs # quick documentation checks (no build; doc-quick aggregate)
|
|
pnpm run website:build # VitePress build (doubles as dead-link check)
|
|
pnpm dsh --profile headless "task" # run one task from source (needs DEEPSEEK_API_KEY)
|
|
pnpm run demo:ptc -- "task" # headless PTC mode run (needs key)
|
|
```
|
|
|
|
### Host sandbox failures
|
|
|
|
If a required `gh`, `pnpm`, build, test, or generator command fails because the sandbox blocks credentials, network, IPC, watching, or nested `sandbox-exec`, retry unchanged with the narrowest host escalation. Require sandbox evidence; never bypass test failures or the product sandbox.
|
|
|
|
### Run relevant checks locally
|
|
|
|
Run checks before pushes via [dsh-pre-push-checks](.agents/skills/dsh-pre-push-checks/SKILL.md); report only commands run. After `gh stack sync`, validate immediately; do not merge before checks pass.
|
|
|
|
- Match evidence to the surface: focused behavior tests, model/user-output snapshots, `doc-sync` for docs, built smokes for published paths, and real-API e2e for providers.
|
|
- Never default to the full suite or repeat a passing check for commit or push. CI owns exhaustive coverage and the platform matrix; rehearse all locally only by explicit request, for CI diagnosis, or for an irreducibly repository-wide change.
|
|
- `test:coverage`, not `test`, is the CI coverage gate ([why](docs/testing.md)).
|
|
|
|
## Secrets / .env
|
|
|
|
Real-API tests and demos read `DEEPSEEK_API_KEY`, optional `DEEPSEEK_BASE_URL`, and root `.env`. cordis.yml allows `!!js` (never `!js`) under plugin `config` and entry `disabled`; other metadata stays literal, so conditional composition also uses overlays ([primer](docs/cordis-primer.md#loader-configuration)). Never commit credentials. CI e2e skips without a key; [testing.md](docs/testing.md) owns key policy.
|
|
|
|
## Conventions
|
|
|
|
- Every npm package is `@deepseek-ai/dsh-<name>`; vendored packages are rescoped ([mapping](docs/rescope.md)) and `private: true`. `@deepseek-ai/cordis` is a peerDependency (+ dev) of every harness package.
|
|
- ESM everywhere (`"type": "module"`). Use package names across packages and `.ts` in local relative imports. Config subprocesses run built `lib/` under plain Node; source regressions use their declared launcher ([testing policy](docs/testing.md#test-subprocess-launch-modes)). The `dsh` CLI source launch runs through tsx's ESM-only hook (`node --import tsx/esm`); modules it reaches must stay ESM (no CJS-only exports) — Node's native TypeScript modes are unavailable across the engines range ([source-launch contract](.agents/notes/implemented/architecture/2026-07-29-dsh-source-launch-tsx-esm.md)). Raw/Web `cordis.yml` bare plugins must appear in their resolver manifest's `dependencies`; `verify-cordis-config` enforces it.
|
|
- **Registrations are effects**: every contribution goes through `ctx.effect()` / `ctx.on()`; a registry's `register()` returns the disposer.
|
|
- **Runtime invariants assert owned relationships.** Check authoritative event streams or mutable data, not service or method presence, plugin metadata or effects, or fixed pure examples. Without a plausible relationship, an explained empty companion is correct ([package invariant rules](packages/AGENTS.md)).
|
|
- **Typed events use declaration merging** and merge-extensible maps. Event JSDoc needs `@mode` and payload `@param`; scoped keys absent from payloads need `@dshScopeScan unsupported`. Public service methods document parameters and non-void returns. Every `SessionEventMap` member is required-on-read: builds that do not know its type refuse the log; only structural format changes bump `SESSION_FORMAT_VERSION` ([mechanism](.agents/notes/implemented/simplification/2026-08-25-fail-closed-session-event-vocabulary.md)).
|
|
- **Switch on discriminant tags.** Closed unions end in `assertNever`; merge-extensible unions fall through a documented default.
|
|
- **Waterfall listeners MUST call `next()`** to delegate; returning without it short-circuits the chain ([semantics](docs/cordis-primer.md#cordis-waterfall-semantics)).
|
|
- **Model-visible ⟺ logged**: anything that reaches a model request must be reconstructable from the session log; a new model-visible input requires a session event.
|
|
- **Plugins, not loop changes**: new behavior goes on documented extension points; changing `agent-loop` requires updating docs/architecture.md.
|
|
- **A capability seam comprises Service Definition / Service Provider / Consumer roles.** It is complete, never one role; split only when roles evolve independently ([glossary](docs/glossary.md#capability-seam)).
|
|
- **Prefer maintained dependencies over hand-rolling** when they genuinely delete owned code and tests ([policy](.agents/notes/implemented/process/2026-07-26-dependencies-over-hand-rolling.md)).
|
|
- **Explicit > implicit at package boundaries**: defaulting is an explicit `resolve(request): Spec` step in the owning implementation, never a hidden `?? default` inside `run()` (the `dsh-shell` request/spec split is the template).
|
|
- **No hardcoded tunables in plugins**: deployment-varying choices are validated `Config` fields changeable from cordis.yml; a `DEFAULT_*` constant or test hook is not configurability. Protocol constants, external specs, and security invariants stay fixed.
|
|
- **Misconfiguration fails loud** at load when self-contained, otherwise at the earliest resolvable point; never silently skip a missing referent.
|
|
- **Opaque cross-boundary ids are branded** (`Branded<B>` from `dsh-brand`), never bare `string`.
|
|
- **Trust TypeScript at typed same-process boundaries.** Do not add runtime validation, fallback behavior, or hostile-input tests solely for values the static interface requires; validate at parser/config, queued, model/tool JSON, durable/file, worker, process, and wire boundaries.
|
|
- **Source plane vs artifact plane, never mixed.** Static gates and tests resolve workspace imports through tsconfig `paths` to `src` and pass on a clean tree; gates consuming built `lib/` declare that dependency ([layout](docs/development.md#typescript-project-layout)).
|
|
- **Keep compiler faces explicit.** A package with both Host and Client programs exposes face-specific leaf configs and a solution-only root; repo-wide programs seed a face config, never the root solution ([layout](docs/development.md#typescript-project-layout)).
|
|
- **An empty `catch` names what it swallows** and why nothing else can reach it; keep the `try` to one statement.
|
|
- **Keep comments local.** Do not restate code, explain distant behavior unless locally required, or expand unrelated comments ([rationale](.agents/notes/implemented/process/2026-08-09-concrete-prose-names-actors-and-recorded-facts.md)).
|
|
- **Prefer symmetry for parallel values**; unexplained asymmetry usually signals a missed extraction.
|
|
- **Tests describe behavior, not correctness.** Change obsolete behavior with its tests; explain why in the PR.
|
|
- **Non-trivial changes MUST include an Agent Note in the same PR;** only mechanical/local edits are exempt ([scope](.agents/notes/README.md#when-to-write-one)). Archived notes are frozen: never edit or treat them as current authority ([archive policy](.agents/notes/README.md#archiving-and-deletion)).
|
|
- **Client UI copy is locale-owned.** Route product text through typed dictionaries and `t` or localized primitive props; `verify-client-ui-i18n` rejects hardcoded copy ([decision](.agents/notes/implemented/architecture/2026-08-23-locale-owned-client-ui-copy.md)).
|
|
- **Testing policy** — [docs/testing.md](docs/testing.md). Every non-trivial model- or product-user-visible change updates a keyless recorded-session snapshot; [snapshot ownership](snapshots/AGENTS.md) reserves the top-level tree for session-driven cases and keeps other expected output owner-local. Fixtures replay on macOS/Linux; fix fixtures, not normalizers.
|
|
- **Design each tool's UI presentation up front.** Host presenters stay pure; Web cards derive from raw events and persisted result metadata ([cookbook](docs/cookbook/adding-a-tool.md)).
|
|
- **Plan unit, e2e, and snapshot coverage** for capability seams, lifecycle paths, and transcript output; include missing snapshot-harness support in the same change.
|
|
- **Both SDKs project the loop.** Agent-loop, session-lifecycle, and `SessionEventMap` changes update the TypeScript and Python SDK expected outputs in the same PR; `pnpm run test` covers neither ([surfaces](docs/testing.md#when-a-snapshot-test-is-required)).
|
|
- **Choose PR history deliberately.** Split independent changes and fix the introducing PR before propagation. Standalone/stack branches may merge-forward or rebase. Rewrites use `--force-with-lease`, abort on remote movement, never raw `--force`; preserve an in-progress merge-forward checkpoint before taking a newer base ([rationale](.agents/notes/implemented/process/2026-08-02-native-github-stacks-and-optional-rebases.md)).
|
|
- **Labels:** one PR `kind/*`, all material `area/*`, and native Issue Type ([taxonomy](.agents/notes/implemented/process/2026-08-08-unified-github-label-taxonomy.md)).
|
|
- TODO markers: `FIXME`/`TODO`/`XXX` by urgency ([semantics](docs/development.md)).
|
|
- Files end with exactly one trailing newline; `git diff --cached --check` (pre-commit) gates it.
|
|
|
|
## Defensive patterns
|
|
|
|
Read [docs/defensive-patterns.md](docs/defensive-patterns.md) before lifecycle, concurrency, subprocess, or teardown work.
|
|
|
|
## Type safety and documentation
|
|
|
|
Everything compiles under `strict: true` with `noImplicitAny`; every remaining `any` explains why narrowing is infeasible. Every module and export has concise JSDoc for its non-obvious contract; function-like exports include `@param`/`@returns`, as enforced by `verify-export-jsdoc`. Heritage-declared members, plugin-protocol slots, and constructors keep their docs at the declaring Service Definition, protocol, or class.
|
|
|
|
Comments and docs state complete contracts and context, not reasoning transcripts. Use direct, concrete terms. Do not use metaphors. Before writing `contract`, `boundary`, or `shape`, ask whether a more exact term names the subject: write `response fields`, `JSON validation`, or `ESM exports` instead of `response shape`, `validation boundary`, or `module shape`. Keep `contract` for preconditions, postconditions, invariants, compatibility promises, and other obligations that callers, callees, implementers, providers, producers, or consumers rely on. Keep a literal process, wire, security, transaction, or lifecycle boundary. Do not narrate control flow or tests, preserve review history, or restate code. Keep behavior, failure, timing, ownership, and safe-use facts; link the rationale. Use [dsh-prose-standard](.agents/skills/dsh-prose-standard/SKILL.md) for decisions. Wire mechanically checkable invariants into an executed top-level gate and prove each changed acceptance path rejects an invalid case. Use narrow, justified exceptions instead of disabling a rule globally.
|
|
|
|
Docs accompany every code change: update affected README and JSDoc contracts together. Routine bilingual work follows [docs/AGENTS.md](docs/AGENTS.md); only explicit user invocation may run `dsh-translate-docs`. Current-state prose, one physical line per paragraph, one home per fact, and word budgets live there.
|
|
|
|
## Editing these instructions
|
|
|
|
`CLAUDE.md` symlinks `AGENTS.md` at root and `packages/`; edit the real file. Keep each rule self-contained while linking high-level docs. Condense when clarity survives; raise a `verify-doc-budgets` ceiling when the required content genuinely needs more space.
|
|
|
|
## Vendoring policy
|
|
|
|
`vendor/` packages are pinned source copies (manifest with upstream SHAs in [vendor/README.md](vendor/README.md)). Update via the sync procedure there; re-apply or retire the logged local modifications; rerun `pnpm run test && pnpm run build`.
|