Merge latest origin/master into parallel-tool-call

# Conflicts:
#	docs/architecture.md
#	examples/acp-agent/tests/snapshots/bash-spill/session.jsonl
#	examples/acp-agent/tests/snapshots/escalation-approved/session.jsonl
#	examples/acp-agent/tests/snapshots/escalation-rejected/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-cc-pretool-ask/session.jsonl
#	packages/core/agent-loop/src/loop.ts
This commit is contained in:
Tianyi Cui
2026-07-18 15:09:39 +08:00
198 changed files with 6086 additions and 4036 deletions

View File

@@ -98,6 +98,10 @@
"text": "ctx.tasks",
"link": "/zh-CN/api/harness/tasks"
},
{
"text": "ctx.tokenMeter",
"link": "/zh-CN/api/harness/token-meter"
},
{
"text": "ctx.tools",
"link": "/zh-CN/api/harness/tools"

View File

@@ -4,9 +4,9 @@
`CompactService` (abstract seam) — provided by `@deepseek-ai/dsh-compact`.
Abstract compaction service. Implementations own token estimation, retention, and summarization, but a successful run must replace the selected surface span with one summary node and prevent concurrent compaction of the same session. Load one implementation per context as `ctx.compact`.
Abstract compaction service. Implementations own trigger policy, retention, and summarization, and may consume a separate measurement service. A successful run replaces the selected surface span with one summary node and prevents concurrent compaction of the same session. Load one implementation per context as `ctx.compact`.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/compact/compact/src/index.ts#L37)
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/compact/compact/src/index.ts#L38)
### ctx.compact.compactIfNeeded(agent, fullSystemPrompt, sessionPrefix, signal)
@@ -23,7 +23,7 @@ Check token pressure and compact if the conversation is too large. Estimate the
**Returns** the compaction result, or `null` if no compaction was needed.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/compact/compact/src/index.ts#L57)
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/compact/compact/src/index.ts#L58)
### ctx.compact.compactRegion(session, start, end, agent, signal?)
@@ -31,14 +31,14 @@ Check token pressure and compact if the conversation is too large. Estimate the
abstract compactRegion( session: Session, start: number, end: number, agent: CompactAgentContext, signal?: AbortSignal, ): Promise<CompactionResult>
```
Forcibly compact a range of surface nodes into a single summary node. `start` and `end` name an inclusive span by surface position, not numeric seq order; replacements can make visible seqs non-monotonic. Both edges must be balanced so assistant tool calls remain paired with their results. A model- backed implementation forwards cancellation and rejects active, missing, reversed, or unbalanced ranges. Use toolPairingBalancedBefore and toolPairingBalancedAfter for the edge checks.
Forcibly compact a range of surface nodes into a single summary node. `start` and `end` name an inclusive span by surface position, not numeric seq order; replacements can make visible seqs non-monotonic. Both edges must be balanced so assistant tool calls remain paired with their results. A model- backed implementation forwards cancellation. The agent must own the exact target session object; implementations reject an ownership mismatch before model resolution, lock acquisition, summarization, or log mutation, and reject active, missing, reversed, or unbalanced ranges. Use toolPairingBalancedBefore and toolPairingBalancedAfter for the edge checks.
- `session` — session to mutate.
- `session` — session to mutate; must be identical to `agent.session`.
- `start` — first surface seq, inclusive.
- `end` — last surface seq, inclusive.
- `agent` — summarizer context.
- `agent` — owner of the target session and summarizer context.
- `signal` — optional cancellation; model-backed implementations must forward it.
**Returns** the replaced range and summary.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/compact/compact/src/index.ts#L82)
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/compact/compact/src/index.ts#L85)

View File

@@ -18,7 +18,7 @@ A fully configured agent and live session were published. Setup is composition-o
- `agent` — the newly registered agent with its live session and completed setup. Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/types.ts#L153)
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/types.ts#L154)
### agent/disposed
@@ -32,7 +32,7 @@ An agent left the registry; AgentLoop emits this after driver quiescence but bef
- `agent` — the exact agent removed from the registry. Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/types.ts#L162)
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/types.ts#L163)
### agent/error
@@ -49,7 +49,7 @@ A step or turn errored. The loop reports a failure here (plus the logger) even w
- `step` — the step at which the failure surfaced.
- `error` — the failure, verbatim. Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/types.ts#L297)
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/types.ts#L298)
### agent/pre-step
@@ -68,7 +68,7 @@ Awaited serial checkpoint for session-surface mutation after prompt assembly and
- `sessionPrefix` — the frozen request prefix.
- `signal` — the turn abort signal.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/types.ts#L216)
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/types.ts#L217)
### agent/prompt-submit
@@ -84,7 +84,7 @@ Allow, rewrite, or block one drained prompt before it becomes a user message. Ca
- `content` — the drained message's blocks, as queued.
- `source` — the message's resolved source. Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/types.ts#L226)
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/types.ts#L227)
### agent/queued
@@ -100,7 +100,7 @@ Detached, frozen content entered the agent's inbox. Source defaults have already
- `content` — the accepted content blocks retained by the inbox.
- `info` — the accepted source plus whether it entered as steering. Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/types.ts#L181)
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/types.ts#L182)
### agent/request
@@ -117,7 +117,7 @@ Replace the frozen call configuration. Model-visible content must use logged cha
- `step` — the step whose request this is.
- `config` — the config the loop would use (frozen); return a replacement to switch. Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/types.ts#L238)
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/types.ts#L239)
### agent/session-prefix
@@ -133,7 +133,7 @@ Compose request-only messages placed before derived history. The frozen result i
- `prefix` — the frozen seed; return an extended replacement.
- `signal` — aborts composition when the step is torn down.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/types.ts#L253)
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/types.ts#L254)
### agent/session-start
@@ -148,7 +148,7 @@ The session lifecycle began, once before the first turn. Use `agent.inject()` to
- `agent` — the agent whose session lifecycle began.
- `source` — why the session started (fresh startup, resume, …). Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/types.ts#L194)
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/types.ts#L195)
### agent/status
@@ -163,7 +163,7 @@ Agent status changed (`idle` ⇄ `running`, or → `disposed`). `send()` does no
- `agent` — the agent whose status flipped.
- `status` — the status just entered (the transition's destination). Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/types.ts#L171)
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/types.ts#L172)
### agent/step-result
@@ -180,7 +180,7 @@ Waterfall: post-process the assembled assistant Message before tool dispatch (va
- `step` — the step that produced the message.
- `message` — the assistant message as assembled from the stream. Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/types.ts#L264)
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/types.ts#L265)
### agent/turn-continuation
@@ -196,7 +196,7 @@ Override whether the turn continues. The default continues after tool calls or s
- `turn` — the turn being continued or stopped.
- `defaultDecision` — what the loop would do absent an override. Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/types.ts#L274)
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/types.ts#L275)
### agent/turn-stop
@@ -211,7 +211,7 @@ Monotonic terminal-stop checkpoint after continuation and steering are folded; a
- `agent` — the agent whose composed continuation outcome may be stopped.
- `turn` — the turn at its terminal-stop checkpoint. Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/types.ts#L284)
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/agent/src/types.ts#L285)
## approval/*

View File

@@ -7,7 +7,7 @@
In-memory session store (`ctx.sessions`).
Persistence is intentionally not implemented here — persistence plugins subscribe to `session/event` and flush on `session/flush` / dispose.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/session/src/index.ts#L566)
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/session/src/index.ts#L580)
### ctx.sessions.create(id?, options?)
@@ -23,7 +23,7 @@ For an agent whose session must be torn down IN ORDER with its loop (so the loop
**Returns** the live session, already entered and announced.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/session/src/index.ts#L595)
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/session/src/index.ts#L609)
### ctx.sessions.prepare(id?, options?)
@@ -38,7 +38,7 @@ Build a session WITHOUT entering it into the store — validate the id/cwd and c
**Returns** the constructed session, NOT yet in the store.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/session/src/index.ts#L624)
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/session/src/index.ts#L638)
### ctx.sessions.enter(session)
@@ -53,7 +53,7 @@ Re-checks the id for a duplicate: `prepare` and `enter` are public cross-package
**Returns** the detach disposer (publication hooks + store removal). When called from a synchronous `session/created` listener, removal and disposal wait until that creation dispatch unwinds.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/session/src/index.ts#L668)
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/session/src/index.ts#L682)
### ctx.sessions.announce(session)
@@ -65,7 +65,7 @@ Emit `session/created` exactly once for an entered session (with the carrier ent
- `session` — the entered session to announce to listeners.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/session/src/index.ts#L723)
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/session/src/index.ts#L737)
### ctx.sessions.flush(session)
@@ -79,7 +79,7 @@ Dispatch the awaited `session/flush` durability checkpoint for `session`, with t
**Returns** resolves when every flush listener has settled; after all settle, rejects with the first registered listener failure if any listener failed.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/session/src/index.ts#L775)
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/session/src/index.ts#L789)
### ctx.sessions.get(id)
@@ -93,7 +93,7 @@ Look up a live session.
**Returns** the session, or undefined when no live session has that id.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/session/src/index.ts#L807)
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/session/src/index.ts#L821)
### ctx.sessions.list()
@@ -105,7 +105,7 @@ All live sessions, in creation order.
**Returns** a fresh array; mutating it does not affect the store.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/session/src/index.ts#L815)
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/session/src/index.ts#L829)
### ctx.sessions.fork(source, boundary?, childSessionId?)
@@ -121,4 +121,4 @@ Create a live child session from a turn-enclosed prefix of a live source. `bound
**Returns** The created live child session.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/session/src/index.ts#L832)
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/core/session/src/index.ts#L846)

View File

@@ -0,0 +1,50 @@
<!-- Generated by scripts/gen-website-api.ts — do not edit by hand. Run `pnpm run gen-website-api` to regenerate. -->
# ctx.tokenMeter
`TokenMeterService` — provided by `@deepseek-ai/dsh-token-meter`.
Replay owner for one service-wide estimator and isolated per-session folds.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/llm/token-meter/src/index.ts#L106)
### ctx.tokenMeter.contextWindow
```ts website-api
readonly contextWindow: number
```
Provider context-window capacity used by pressure consumers.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/llm/token-meter/src/index.ts#L112)
### ctx.tokenMeter.measure(session, requestHeader?)
```ts website-api
measure(session: Session, requestHeader?: EpochHeader): TokenMeasurement
```
Measure current request pressure and surface through the durable tail.
Provider usage is reused only when the latest successful call's canonical request envelope matches `requestHeader` and its total is no lower than that call's full heuristic anchor; otherwise the complete envelope and surface are heuristically repriced.
`requestHeader` affects request pressure only; surface fields always describe the current session surface. Every call clones those positional nodes, so measurement is O(surface).
- `session` — session to replay through its current durable tail.
- `requestHeader` — optional effective request envelope replacing the latest logged header.
**Returns** a detached deeply immutable pressure and surface measurement.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/llm/token-meter/src/index.ts#L143)
### ctx.tokenMeter.estimateMessage(message)
```ts website-api
estimateMessage(message: Message): number
```
Heuristically price one model-visible message.
- `message` — message to price without mutation.
**Returns** content and role-framing tokens under the fixed service heuristic.
[Source](https://github.com/deepseek-harness/deepseek-harness/blob/master/packages/llm/token-meter/src/index.ts#L181)

View File

@@ -84,14 +84,18 @@ Harness 使用 `cordis.yml` 描述一个 Agent 加载哪些插件、以什么参
You are coding-agent, a coding assistant powered by the {{model}} model.
Verify your work by running the code or tests. Keep answers brief and factual.
# Token 计量:统一定义模型能看到的 token 上限
- id: token-meter
name: '@deepseek-ai/dsh-token-meter'
config:
contextWindow: 128000
# 自动压缩:对话太长时自动总结旧内容,腾出上下文空间
# contextWindow 是模型能看到的 token 上限
# thresholdRatio 超过这个比例就触发压缩
# compactionRetries 是压缩后仍超标时的额外重试次数
- id: compact-basic
name: '@deepseek-ai/dsh-compact-basic'
config:
contextWindow: 128000
thresholdRatio: 0.8
retainTokens: 20480
maxTokens: 8192
@@ -214,8 +218,6 @@ config:
- id: compact-basic
name: '@deepseek-ai/dsh-compact-basic'
disabled: true
config:
contextWindow: 128000
```
## 各插件配置参考