mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
Merge remote-tracking branch 'origin/master' into worktree/windows-acl-hardening-followup
# Conflicts: # packages/sandbox/sandbox-windows-acl/package.json
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/subsystems/README.md
|
||||
README.md: fddbf460c8e9e7c6f9ed1d3375bdabe65947661f
|
||||
README.zh.md: febc5a97426fef5ec4b2b80d9677957369994a3b
|
||||
README.md: 560851eeda607b762456fe20c874b4497704709f
|
||||
README.zh.md: 4acba4372e995b54a3bb326bec0cf9606f997773
|
||||
|
||||
@@ -18,6 +18,7 @@ One page per subsystem of the DeepSeek Harness: what it is, the data structures
|
||||
| [settings.md](settings.md) | the user-settings seam: `SettingsNamespace` registration, layered resolution (defaults → composition `base` → user document), owner scopes, hot commits |
|
||||
| [credentials.md](credentials.md) | the credential seam: `CredentialRef` references (never values) in configuration, per-operation resolution, UI-safe `CredentialInfo`, provider source layers |
|
||||
| [session-query.md](session-query.md) | logical records, bounded exact-event reads, relationship traces, semantic filters/documents, and full-text result pages |
|
||||
| [feedback.md](feedback.md) | lifecycle-bound per-message feedback records, optimistic versions, sidecar persistence, and the Host Remote contract |
|
||||
| [session-title.md](session-title.md) | durable title snapshots, cited source-message seqs, and the asynchronous provider contract |
|
||||
| [session-reference.md](session-reference.md) | structured cross-session references: `SessionReferenceInput`/`Candidate`, prepared message contexts, the stable error taxonomy |
|
||||
| [system-prompt.md](system-prompt.md) | per-assembly context, tool-provider results, prompt sections, and cooperative assembly |
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
| [settings.md](settings.md) | 用户设置 seam:`SettingsNamespace` 注册、分层解析(默认值 → 组合 `base` → 用户文档)、owner scope、热提交 |
|
||||
| [credentials.md](credentials.md) | 凭据 seam:配置中的 `CredentialRef` 引用(绝不含值)、按操作解析、对 UI 安全的 `CredentialInfo`、provider 来源层 |
|
||||
| [session-query.md](session-query.md) | 逻辑记录、有界精确事件读取、关系追踪、语义筛选器/文档与全文检索结果页 |
|
||||
| [feedback.md](feedback.md) | 绑定生命周期的逐消息反馈记录、乐观版本、伴随记录持久化与 Host Remote 契约 |
|
||||
| [session-title.md](session-title.md) | 持久标题快照、被引用的来源消息 seq 与异步提供方约定 |
|
||||
| [session-reference.md](session-reference.md) | 结构化跨会话引用:`SessionReferenceInput`/`Candidate`、prepared 消息上下文、稳定错误分类 |
|
||||
| [system-prompt.md](system-prompt.md) | 逐次组装的上下文、工具提供方结果、提示词段落与协作式组装 |
|
||||
|
||||
6
docs/subsystems/feedback.i18n.yaml
Normal file
6
docs/subsystems/feedback.i18n.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/subsystems/feedback.md
|
||||
feedback.md: 76a29f7d6ba604fa07ed56429c9b066e22639671
|
||||
feedback.zh.md: 5a409832de68b6d0bc9688a907c0f22edd3b0a43
|
||||
256
docs/subsystems/feedback.md
Normal file
256
docs/subsystems/feedback.md
Normal file
@@ -0,0 +1,256 @@
|
||||
# Message Feedback
|
||||
|
||||
English | [中文](feedback.zh.md)
|
||||
|
||||
[`@deepseek-ai/dsh-message-feedback`](../../packages/feedback/message-feedback) owns editable feedback for individual assistant messages. It is deliberately separate from the immutable Session-level `feedback/record` event: message feedback is a local storage-domain sidecar, not Session-log content or a projection, and it performs no telemetry handoff.
|
||||
|
||||
Source: [`packages/feedback/message-feedback/src/types.ts`](../../packages/feedback/message-feedback/src/types.ts)
|
||||
|
||||
## Public types
|
||||
|
||||
```ts type-equiv
|
||||
/** Opaque compare-and-set token for one exact feedback item revision. */
|
||||
type MessageFeedbackVersion = Branded<'MessageFeedbackVersion'>
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** The human's overall judgment of one assistant message. */
|
||||
type MessageFeedbackRating = 'positive' | 'negative'
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** One current feedback value and its opaque mutation token. */
|
||||
interface MessageFeedbackItem {
|
||||
/** Stable identity of the assistant message inside the owning Session. */
|
||||
readonly messageId: MessageId
|
||||
/** Overall positive or negative judgment. */
|
||||
readonly rating: MessageFeedbackRating
|
||||
/** Optional explanation, preserved verbatim after validation. */
|
||||
readonly note?: string
|
||||
/** Equality-only token replaced by every material create or update. */
|
||||
readonly version: MessageFeedbackVersion
|
||||
/** Host-assigned creation time in Unix epoch milliseconds. */
|
||||
readonly createdAt: number
|
||||
/** Host-assigned time of the most recent material update. */
|
||||
readonly updatedAt: number
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Read all message feedback belonging to one persisted Session lifecycle. */
|
||||
interface MessageFeedbackListRequest {
|
||||
/** Persisted Session whose sidecar should be read. */
|
||||
readonly sessionId: SessionId
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Current feedback values for one Session, in first-creation order. */
|
||||
interface MessageFeedbackListValue {
|
||||
/** Fresh immutable item snapshots. */
|
||||
readonly items: readonly MessageFeedbackItem[]
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Create or replace feedback for one assistant message. */
|
||||
interface MessageFeedbackPutRequest {
|
||||
/** Persisted Session that owns the target message. */
|
||||
readonly sessionId: SessionId
|
||||
/** Target assistant-message identity. */
|
||||
readonly messageId: MessageId
|
||||
/** Desired overall judgment. */
|
||||
readonly rating: MessageFeedbackRating
|
||||
/** Optional non-blank explanation. */
|
||||
readonly note?: string
|
||||
/** Observed item version, or `null` to require that no item exists. */
|
||||
readonly ifVersion: MessageFeedbackVersion | null
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Delete feedback for one message after observing its current version. */
|
||||
interface MessageFeedbackDeleteRequest {
|
||||
/** Persisted Session that owns the sidecar. */
|
||||
readonly sessionId: SessionId
|
||||
/** Message whose feedback should be absent after this operation. */
|
||||
readonly messageId: MessageId
|
||||
/** Observed item version; ignored when the item is already absent. */
|
||||
readonly ifVersion: MessageFeedbackVersion
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Idempotent deletion acknowledgement. */
|
||||
interface MessageFeedbackDeleteValue {
|
||||
/** Stable postcondition shared by the first deletion and every retry. */
|
||||
readonly absent: true
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** No persisted Session header exists for the requested id. */
|
||||
interface MessageFeedbackSessionNotFound {
|
||||
readonly code: 'session-not-found'
|
||||
readonly sessionId: SessionId
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** The id does not name a derived, append-origin assistant message. */
|
||||
interface MessageFeedbackTargetNotFound {
|
||||
readonly code: 'target-not-found'
|
||||
readonly sessionId: SessionId
|
||||
readonly messageId: MessageId
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** A material mutation did not match the addressed item's current version. */
|
||||
interface MessageFeedbackVersionConflict {
|
||||
readonly code: 'version-conflict'
|
||||
/** Authoritative current item, or `null` when it does not exist. */
|
||||
readonly current: MessageFeedbackItem | null
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** A supplied note contains no non-whitespace character. */
|
||||
interface MessageFeedbackNoteBlank {
|
||||
readonly code: 'note-blank'
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** A supplied note exceeds the configured UTF-8 byte limit. */
|
||||
interface MessageFeedbackNoteTooLarge {
|
||||
readonly code: 'note-too-large'
|
||||
readonly maxBytes: number
|
||||
readonly actualBytes: number
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Failures shared by the public message-feedback operations. */
|
||||
type MessageFeedbackFailure =
|
||||
| MessageFeedbackSessionNotFound
|
||||
| MessageFeedbackTargetNotFound
|
||||
| MessageFeedbackVersionConflict
|
||||
| MessageFeedbackNoteBlank
|
||||
| MessageFeedbackNoteTooLarge
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Successful public operation result. */
|
||||
interface MessageFeedbackSuccess<T> {
|
||||
readonly ok: true
|
||||
readonly value: T
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Rejected public operation result with a stable business failure. */
|
||||
interface MessageFeedbackRejected<E extends MessageFeedbackFailure> {
|
||||
readonly ok: false
|
||||
readonly error: E
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Result returned by the message-feedback `list` operation. */
|
||||
type MessageFeedbackListResult =
|
||||
| MessageFeedbackSuccess<MessageFeedbackListValue>
|
||||
| MessageFeedbackRejected<MessageFeedbackSessionNotFound>
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Result returned by the message-feedback `put` operation. */
|
||||
type MessageFeedbackPutResult =
|
||||
| MessageFeedbackSuccess<MessageFeedbackItem>
|
||||
| MessageFeedbackRejected<
|
||||
| MessageFeedbackSessionNotFound
|
||||
| MessageFeedbackTargetNotFound
|
||||
| MessageFeedbackVersionConflict
|
||||
| MessageFeedbackNoteBlank
|
||||
| MessageFeedbackNoteTooLarge
|
||||
>
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Result returned by the message-feedback `delete` operation. */
|
||||
type MessageFeedbackDeleteResult =
|
||||
| MessageFeedbackSuccess<MessageFeedbackDeleteValue>
|
||||
| MessageFeedbackRejected<MessageFeedbackSessionNotFound | MessageFeedbackVersionConflict>
|
||||
```
|
||||
|
||||
## Data and concurrency
|
||||
|
||||
One Session sidecar row contains its header identity `{createdAt, cwd}` and feedback items keyed by `MessageId`. Each item carries a positive or negative rating, an optional note, Host-assigned `createdAt`/`updatedAt` timestamps, and its own opaque version. Versions are compared only for equality and only against the addressed message; callers do not order or synthesize them.
|
||||
|
||||
`put` uses strict optimistic concurrency: every request for an existing item must match its current `ifVersion`, including a no-op. A conflict returns the authoritative current item (or `null`), so a caller can reconcile a lost response or a concurrent edit without another read. Deleting an already absent item succeeds. A per-Session queue encloses inspection, read, conflict evaluation, and whole-row write, so these guarantees cover concurrent calls in one Host process.
|
||||
|
||||
## Target and lifecycle authority
|
||||
|
||||
`SessionPersistence.inspect()` supplies the target Session observation without publishing or resuming an Agent and without committing cold repair. A cold `listSnapshots()` preflight classifies definite absence; inspection failure for a catalogued Session propagates as infrastructure failure. `put` accepts only a non-empty, append-origin `assistant/message` with the requested `MessageId`; replacement-origin, usage-only empty, and non-assistant records are not feedback targets.
|
||||
|
||||
The stored `{createdAt, cwd}` identity must match the inspected header. A mismatch is treated as absence: `list` returns no items, while `put` may replace the stale row with one bound to the current header identity. Forks use a new Session identity and receive no sidecar copy even when their seed contains the same messages.
|
||||
|
||||
## Persistence and Remote contract
|
||||
|
||||
The service stores whole Session rows in the `message_feedback` storage domain through `ctx.storageDomain`. Before `put` commits a row that references a target message, a matching live target passes through the canonical `ctx.sessions.flush` checkpoint; both live and cold paths are then physically read from sequence zero through `SessionPersistence.readFrom`. The resulting observation is revalidated before the sidecar write, so the durable target log always precedes its sidecar commit. `maxNoteBytes` is required and bounds note text by UTF-8 bytes; the Web Host composition sets `8192`. The package publishes the Host `messageFeedback.list`, `messageFeedback.put`, and `messageFeedback.delete` unary Remote contract through `GatewayService` and `@Remote`; the generated Cordis surface below is the method-level authority.
|
||||
|
||||
Plugin disposal closes mutation admission, drains accepted per-Session queue work, and then closes the storage domain.
|
||||
|
||||
## Boundaries and limitations
|
||||
|
||||
- The client Remote aggregate mount and UI consumer are separately owned and deferred.
|
||||
- The mutation queue is process-local. Storage-domain has no cross-process conditional write, so multiple Host writers to one storage root have no compare-and-swap or lost-update guarantee.
|
||||
- Session persistence has no durable deletion surface. The service does not treat `session/disposed` or `host/session-removed` as deletion and therefore performs no fake cascade; orphan sidecar rows may remain after out-of-band log removal.
|
||||
- A request in the narrow interval after live detach but before the persistence catalog materializes the header can receive `session-not-found`; callers retry after retirement materialization.
|
||||
- Cold requests scan the complete Session snapshot catalog because persistence has no lookup-by-id metadata operation. One Session row also has no item-count or aggregate-byte cap; `maxNoteBytes` bounds only each note until a concrete consumer owns a row policy.
|
||||
- Header identity detects a reused id only when `{createdAt, cwd}` differs; a cloned log retaining the same header identity is indistinguishable by this contract.
|
||||
- The Host contract records no authenticated actor or audit identity and therefore assumes a trusted caller boundary.
|
||||
|
||||
<!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
|
||||
|
||||
<a id="cordis-surface"></a>
|
||||
|
||||
## Cordis surface
|
||||
|
||||
Generated from source by `scripts/gen-cordis-catalog.ts` (verified fresh by `pnpm run verify-cordis-catalog` in doc-sync; regenerate with `pnpm run gen-cordis-catalog`) — this section is byte-identical in both language sides of the page. Signature blocks use a `ts cordis-catalog` fence and keep the original source JSDoc; dispatch modes are defined in the [primer](../cordis-primer.md#dispatch-modes), and the framework-inherited `ctx` surface lives in [cordis-api/inherited.md](../cordis-api/inherited.md).
|
||||
|
||||
<a id="ctxmessagefeedback--messagefeedbackservice"></a>
|
||||
|
||||
### `ctx.messageFeedback` — `MessageFeedbackService`
|
||||
|
||||
Storage-domain sidecar service. It inspects persisted Session history and never creates or resumes an Agent or Session.
|
||||
|
||||
```ts cordis-catalog
|
||||
/**
|
||||
* Read feedback belonging to the current persisted Session lifecycle.
|
||||
* A stale row from a reused Session id is invisible.
|
||||
* @param request - Session identity to inspect and list.
|
||||
* @returns current immutable items or `session-not-found`.
|
||||
*/
|
||||
@Remote('list') async list(request: MessageFeedbackListRequest): Promise<MessageFeedbackListResult>
|
||||
|
||||
/**
|
||||
* Create or replace feedback for one derived append-origin assistant
|
||||
* message. Every request must match the addressed item's current version;
|
||||
* a matching no-op returns the stored item without changing its revision.
|
||||
* @param request - target, desired value, and observed item version.
|
||||
* @returns the committed item or an explicit business failure.
|
||||
*/
|
||||
@Remote('put') put(request: MessageFeedbackPutRequest): Promise<MessageFeedbackPutResult>
|
||||
|
||||
/**
|
||||
* Delete one feedback item. Absence is successful regardless of the
|
||||
* supplied version; an existing item requires an exact version match.
|
||||
* @param request - Session, message, and observed item version.
|
||||
* @returns the stable absent postcondition, or an explicit failure.
|
||||
*/
|
||||
@Remote('delete') delete(request: MessageFeedbackDeleteRequest): Promise<MessageFeedbackDeleteResult>
|
||||
```
|
||||
|
||||
Source: [`packages/feedback/message-feedback/src/index.ts:150`](../../packages/feedback/message-feedback/src/index.ts)
|
||||
<!-- END GENERATED cordis-surface -->
|
||||
256
docs/subsystems/feedback.zh.md
Normal file
256
docs/subsystems/feedback.zh.md
Normal file
@@ -0,0 +1,256 @@
|
||||
# 消息反馈
|
||||
|
||||
[English](feedback.md) | 中文
|
||||
|
||||
[`@deepseek-ai/dsh-message-feedback`](../../packages/feedback/message-feedback)拥有针对单条 assistant 消息的可编辑反馈。它刻意与不可变的 Session 级 `feedback/record` 事件分离:message feedback 是本地 storage-domain 伴随记录(sidecar),不是 Session 日志内容或投影,也不执行遥测交接。
|
||||
|
||||
来源:[`packages/feedback/message-feedback/src/types.ts`](../../packages/feedback/message-feedback/src/types.ts)
|
||||
|
||||
## 公开类型
|
||||
|
||||
```ts type-equiv
|
||||
/** Opaque compare-and-set token for one exact feedback item revision. */
|
||||
type MessageFeedbackVersion = Branded<'MessageFeedbackVersion'>
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** The human's overall judgment of one assistant message. */
|
||||
type MessageFeedbackRating = 'positive' | 'negative'
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** One current feedback value and its opaque mutation token. */
|
||||
interface MessageFeedbackItem {
|
||||
/** Stable identity of the assistant message inside the owning Session. */
|
||||
readonly messageId: MessageId
|
||||
/** Overall positive or negative judgment. */
|
||||
readonly rating: MessageFeedbackRating
|
||||
/** Optional explanation, preserved verbatim after validation. */
|
||||
readonly note?: string
|
||||
/** Equality-only token replaced by every material create or update. */
|
||||
readonly version: MessageFeedbackVersion
|
||||
/** Host-assigned creation time in Unix epoch milliseconds. */
|
||||
readonly createdAt: number
|
||||
/** Host-assigned time of the most recent material update. */
|
||||
readonly updatedAt: number
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Read all message feedback belonging to one persisted Session lifecycle. */
|
||||
interface MessageFeedbackListRequest {
|
||||
/** Persisted Session whose sidecar should be read. */
|
||||
readonly sessionId: SessionId
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Current feedback values for one Session, in first-creation order. */
|
||||
interface MessageFeedbackListValue {
|
||||
/** Fresh immutable item snapshots. */
|
||||
readonly items: readonly MessageFeedbackItem[]
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Create or replace feedback for one assistant message. */
|
||||
interface MessageFeedbackPutRequest {
|
||||
/** Persisted Session that owns the target message. */
|
||||
readonly sessionId: SessionId
|
||||
/** Target assistant-message identity. */
|
||||
readonly messageId: MessageId
|
||||
/** Desired overall judgment. */
|
||||
readonly rating: MessageFeedbackRating
|
||||
/** Optional non-blank explanation. */
|
||||
readonly note?: string
|
||||
/** Observed item version, or `null` to require that no item exists. */
|
||||
readonly ifVersion: MessageFeedbackVersion | null
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Delete feedback for one message after observing its current version. */
|
||||
interface MessageFeedbackDeleteRequest {
|
||||
/** Persisted Session that owns the sidecar. */
|
||||
readonly sessionId: SessionId
|
||||
/** Message whose feedback should be absent after this operation. */
|
||||
readonly messageId: MessageId
|
||||
/** Observed item version; ignored when the item is already absent. */
|
||||
readonly ifVersion: MessageFeedbackVersion
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Idempotent deletion acknowledgement. */
|
||||
interface MessageFeedbackDeleteValue {
|
||||
/** Stable postcondition shared by the first deletion and every retry. */
|
||||
readonly absent: true
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** No persisted Session header exists for the requested id. */
|
||||
interface MessageFeedbackSessionNotFound {
|
||||
readonly code: 'session-not-found'
|
||||
readonly sessionId: SessionId
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** The id does not name a derived, append-origin assistant message. */
|
||||
interface MessageFeedbackTargetNotFound {
|
||||
readonly code: 'target-not-found'
|
||||
readonly sessionId: SessionId
|
||||
readonly messageId: MessageId
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** A material mutation did not match the addressed item's current version. */
|
||||
interface MessageFeedbackVersionConflict {
|
||||
readonly code: 'version-conflict'
|
||||
/** Authoritative current item, or `null` when it does not exist. */
|
||||
readonly current: MessageFeedbackItem | null
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** A supplied note contains no non-whitespace character. */
|
||||
interface MessageFeedbackNoteBlank {
|
||||
readonly code: 'note-blank'
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** A supplied note exceeds the configured UTF-8 byte limit. */
|
||||
interface MessageFeedbackNoteTooLarge {
|
||||
readonly code: 'note-too-large'
|
||||
readonly maxBytes: number
|
||||
readonly actualBytes: number
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Failures shared by the public message-feedback operations. */
|
||||
type MessageFeedbackFailure =
|
||||
| MessageFeedbackSessionNotFound
|
||||
| MessageFeedbackTargetNotFound
|
||||
| MessageFeedbackVersionConflict
|
||||
| MessageFeedbackNoteBlank
|
||||
| MessageFeedbackNoteTooLarge
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Successful public operation result. */
|
||||
interface MessageFeedbackSuccess<T> {
|
||||
readonly ok: true
|
||||
readonly value: T
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Rejected public operation result with a stable business failure. */
|
||||
interface MessageFeedbackRejected<E extends MessageFeedbackFailure> {
|
||||
readonly ok: false
|
||||
readonly error: E
|
||||
}
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Result returned by the message-feedback `list` operation. */
|
||||
type MessageFeedbackListResult =
|
||||
| MessageFeedbackSuccess<MessageFeedbackListValue>
|
||||
| MessageFeedbackRejected<MessageFeedbackSessionNotFound>
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Result returned by the message-feedback `put` operation. */
|
||||
type MessageFeedbackPutResult =
|
||||
| MessageFeedbackSuccess<MessageFeedbackItem>
|
||||
| MessageFeedbackRejected<
|
||||
| MessageFeedbackSessionNotFound
|
||||
| MessageFeedbackTargetNotFound
|
||||
| MessageFeedbackVersionConflict
|
||||
| MessageFeedbackNoteBlank
|
||||
| MessageFeedbackNoteTooLarge
|
||||
>
|
||||
```
|
||||
|
||||
```ts type-equiv
|
||||
/** Result returned by the message-feedback `delete` operation. */
|
||||
type MessageFeedbackDeleteResult =
|
||||
| MessageFeedbackSuccess<MessageFeedbackDeleteValue>
|
||||
| MessageFeedbackRejected<MessageFeedbackSessionNotFound | MessageFeedbackVersionConflict>
|
||||
```
|
||||
|
||||
## 数据与并发
|
||||
|
||||
每个 Session 的一条伴随记录包含 header 身份 `{createdAt, cwd}` 和以 `MessageId` 为键的反馈条目。每个条目携带好评或差评、可选备注、Host 分配的 `createdAt`/`updatedAt` 时间戳及自己的 opaque version。version 只能用于相等比较,且只与目标消息比较;调用方不能排序或自行合成它。
|
||||
|
||||
`put` 采用严格乐观并发:已有条目的每次请求都必须匹配当前 `ifVersion`,即使请求不会改变目标值。冲突会返回权威当前条目(不存在时为 `null`),因此调用方无需额外读取,即可协调丢失响应或并发编辑。删除已经不存在的条目同样成功。按 Session 划分的队列覆盖检查、读取、冲突判断与整行写入,因此这些保证适用于单个 Host 进程中的并发调用。
|
||||
|
||||
## 目标与生命周期权威
|
||||
|
||||
`SessionPersistence.inspect()` 提供目标 Session 的观测,且不会发布或恢复 Agent,也不会提交 cold repair。cold 路径先由 `listSnapshots()` 预检明确不存在;已进入目录的 Session 若检查失败,会按基础设施故障原样传播。`put` 只接受具有指定 `MessageId` 的非空、append-origin `assistant/message`;replacement-origin、仅承载 usage 的空记录和非 assistant 记录都不是反馈目标。
|
||||
|
||||
存储的 `{createdAt, cwd}` 身份必须与检查所得 header 匹配。不匹配按不存在处理:`list` 返回空条目,`put` 则可用绑定当前 header 身份的新记录替换陈旧行。fork 使用新的 Session 身份,即使种子包含相同消息,也不获得伴随记录副本。
|
||||
|
||||
## 持久化与 Remote 契约
|
||||
|
||||
服务通过 `ctx.storageDomain` 在 `message_feedback` 存储域中保存完整 Session 行。`put` 提交引用目标消息的伴随记录前,身份匹配的 live 目标先经过权威 `ctx.sessions.flush` checkpoint;随后 live 与 cold 路径都会通过 `SessionPersistence.readFrom` 从序列零做物理复读。写入伴随记录前会再次校验所得观测,因此目标日志的持久提交始终先于其伴随记录。`maxNoteBytes` 为必填项,按 UTF-8 字节限制备注文本;Web Host 组合将其设为 `8192`。该包通过 `GatewayService` 与 `@Remote` 发布 Host `messageFeedback.list`、`messageFeedback.put` 和 `messageFeedback.delete` 一元 Remote 契约;下方生成的 Cordis surface 是方法级权威。
|
||||
|
||||
Plugin disposal 会先关闭变更接纳,排空已进入各 Session 队列的工作,然后才关闭 storage domain。
|
||||
|
||||
## 边界与限制
|
||||
|
||||
- 客户端 Remote 聚合挂载与 UI 消费方由各自边界负责并保持延后。
|
||||
- 变更队列仅在进程内生效。storage-domain 没有跨进程条件写,因此多个 Host 写入同一存储根目录时,不提供 compare-and-swap 或防止丢失更新的保证。
|
||||
- Session persistence 没有持久删除接口。服务不把 `session/disposed` 或 `host/session-removed` 当作删除,因此不伪造级联;在带外移除日志后,孤儿伴随记录可能继续存在。
|
||||
- 请求若恰好落在 live detach 之后、persistence catalog 物化 header 之前的极短窗口,可能收到 `session-not-found`;调用方应在 retirement materialization 后重试。
|
||||
- 由于 persistence 没有按 id 读取元数据的操作,cold 请求会扫描完整的 Session snapshot 目录。单个 Session 行也没有条目数或聚合字节上限;在具体消费方拥有行策略之前,`maxNoteBytes` 只限制每条备注。
|
||||
- 只有 `{createdAt, cwd}` 不同时,header 身份才能识别复用的 id;本契约无法区分保留相同 header 身份的克隆日志。
|
||||
- Host 契约不记录已认证的 actor 或审计身份,因此假设调用方边界可信。
|
||||
|
||||
<!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
|
||||
|
||||
<a id="cordis-surface"></a>
|
||||
|
||||
## Cordis surface
|
||||
|
||||
Generated from source by `scripts/gen-cordis-catalog.ts` (verified fresh by `pnpm run verify-cordis-catalog` in doc-sync; regenerate with `pnpm run gen-cordis-catalog`) — this section is byte-identical in both language sides of the page. Signature blocks use a `ts cordis-catalog` fence and keep the original source JSDoc; dispatch modes are defined in the [primer](../cordis-primer.md#dispatch-modes), and the framework-inherited `ctx` surface lives in [cordis-api/inherited.md](../cordis-api/inherited.md).
|
||||
|
||||
<a id="ctxmessagefeedback--messagefeedbackservice"></a>
|
||||
|
||||
### `ctx.messageFeedback` — `MessageFeedbackService`
|
||||
|
||||
Storage-domain sidecar service. It inspects persisted Session history and never creates or resumes an Agent or Session.
|
||||
|
||||
```ts cordis-catalog
|
||||
/**
|
||||
* Read feedback belonging to the current persisted Session lifecycle.
|
||||
* A stale row from a reused Session id is invisible.
|
||||
* @param request - Session identity to inspect and list.
|
||||
* @returns current immutable items or `session-not-found`.
|
||||
*/
|
||||
@Remote('list') async list(request: MessageFeedbackListRequest): Promise<MessageFeedbackListResult>
|
||||
|
||||
/**
|
||||
* Create or replace feedback for one derived append-origin assistant
|
||||
* message. Every request must match the addressed item's current version;
|
||||
* a matching no-op returns the stored item without changing its revision.
|
||||
* @param request - target, desired value, and observed item version.
|
||||
* @returns the committed item or an explicit business failure.
|
||||
*/
|
||||
@Remote('put') put(request: MessageFeedbackPutRequest): Promise<MessageFeedbackPutResult>
|
||||
|
||||
/**
|
||||
* Delete one feedback item. Absence is successful regardless of the
|
||||
* supplied version; an existing item requires an exact version match.
|
||||
* @param request - Session, message, and observed item version.
|
||||
* @returns the stable absent postcondition, or an explicit failure.
|
||||
*/
|
||||
@Remote('delete') delete(request: MessageFeedbackDeleteRequest): Promise<MessageFeedbackDeleteResult>
|
||||
```
|
||||
|
||||
Source: [`packages/feedback/message-feedback/src/index.ts:150`](../../packages/feedback/message-feedback/src/index.ts)
|
||||
<!-- END GENERATED cordis-surface -->
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/subsystems/filesystem.md
|
||||
filesystem.md: 00e28130db9f60e6ad5f8c582ca5531482cdeb32
|
||||
filesystem.zh.md: 5cf6be12f41b36c8149a941a4d251c4497ed4738
|
||||
filesystem.md: 01fe2d07f5497374019855ca46ced7e173d21445
|
||||
filesystem.zh.md: e68246d7a44b8812e132e02979a2c0cda6adb792
|
||||
|
||||
@@ -52,7 +52,7 @@ type FsTargetKey = Branded<'FsTargetKey'>
|
||||
type FsVersion = Branded<'FsVersion'>
|
||||
```
|
||||
|
||||
`stat` returns metadata (never content), or `undefined` when the target is absent. `type` lets the tool reject directories/special files before reading, and `size` lets it choose `readText` vs `streamText` without probing by failure. A protocol consumer that needs a byte ceiling applies it while consuming `streamText`, so the filesystem seam needs no consumer-specific bounded-read primitive.
|
||||
`stat` returns metadata (never content), or `undefined` when the target is absent. `type` lets consumers reject directories and special files before reading, and `size` lets text consumers choose `readText` vs `streamText` without probing by failure. A text consumer applies its own retention ceiling while consuming `streamText`. Raw-byte consumers use `readBytes(target, signal, maxBytes)`; its required complete-content cap makes a known or discovered overflow fail with `FS_TOO_LARGE` instead of truncating or buffering without a bound.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
@@ -256,6 +256,7 @@ type FsErrorCode =
|
||||
| 'FS_NOT_DIRECTORY'
|
||||
| 'FS_NOT_TEXT'
|
||||
| 'FS_NOT_REGULAR_FILE'
|
||||
| 'FS_TOO_LARGE'
|
||||
| 'FS_PERMISSION_DENIED'
|
||||
| 'FS_SANDBOX_DENIED'
|
||||
| 'FS_IO_ERROR'
|
||||
@@ -274,7 +275,7 @@ type FsErrorCode =
|
||||
|
||||
## The service and the plugin
|
||||
|
||||
`FileSystem` (`ctx.fs`, abstract) owns the provider primitives: `resolve`, `processPath`, `fileUrl`, `contains`, `stat`, `lstat`, `readText`, `streamText`, `listDir`, `writeText`, and `editText`. `dsh-fs-policy` registers **no service** — it is a plugin that adds policy through the `fs/*` event gate: it decides the write/edit intent waterfalls from unseen/absent/present state and records `FsObservation` values. The executor is `dsh-tool-fs`: it reads/writes/edits through `ctx.fs`, dispatches the waterfalls, and emits the recording event. The generated [`ctx.fs` section](#ctxfs--filesystem-abstract-seam) below shows the exact signatures.
|
||||
`FileSystem` (`ctx.fs`, abstract) owns the provider primitives: `resolve`, `processPath`, `fileUrl`, `contains`, `stat`, `lstat`, `readText`, `streamText`, `readBytes`, `listDir`, `writeText`, and `editText`. `dsh-fs-policy` registers **no service** — it is a plugin that adds policy through the `fs/*` event gate: it decides the write/edit intent waterfalls from unseen/absent/present state and records `FsObservation` values. The executor is `dsh-tool-fs`: it reads/writes/edits through `ctx.fs`, dispatches the waterfalls, and emits the recording event. The generated [`ctx.fs` section](#ctxfs--filesystem-abstract-seam) below shows the exact signatures.
|
||||
|
||||
<!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
|
||||
|
||||
@@ -373,6 +374,18 @@ abstract readText(target: FsTarget, signal?: AbortSignal): Promise<string>
|
||||
*/
|
||||
abstract streamText(target: FsTarget, signal?: AbortSignal): Promise<AsyncIterable<string>>
|
||||
|
||||
/**
|
||||
* Read the whole regular file as raw bytes with no decoding or binary
|
||||
* rejection. The bound lives at this seam so a backend can never buffer an
|
||||
* unbounded file: a target known or discovered to exceed `maxBytes` fails
|
||||
* with `FS_TOO_LARGE` instead of returning a truncated result.
|
||||
* @param target - the resolved target to read.
|
||||
* @param signal - aborts the read.
|
||||
* @param maxBytes - inclusive byte cap on the complete content.
|
||||
* @returns the full raw content, at most `maxBytes` long.
|
||||
*/
|
||||
abstract readBytes(target: FsTarget, signal: AbortSignal | undefined, maxBytes: number): Promise<Uint8Array>
|
||||
|
||||
/**
|
||||
* List direct children of a directory in stable name order. Returns resolved
|
||||
* child targets plus cheap metadata only; never reads file contents.
|
||||
|
||||
@@ -52,7 +52,7 @@ type FsTargetKey = Branded<'FsTargetKey'>
|
||||
type FsVersion = Branded<'FsVersion'>
|
||||
```
|
||||
|
||||
`stat` 返回元数据(从不返回内容),目标不存在时返回 `undefined`。`type` 让工具在读取前拒绝目录或特殊文件;`size` 让工具无需通过失败探测即可选择 `readText` 还是 `streamText`。需要字节上限的协议消费方在消费 `streamText` 时执行该上限,因此文件系统 seam 无需消费方专用的有界读取原语。
|
||||
`stat` 返回元数据(从不返回内容),目标不存在时返回 `undefined`。`type` 让消费方在读取前拒绝目录和特殊文件;`size` 让文本消费方无需通过失败探测即可选择 `readText` 还是 `streamText`。文本消费方在消费 `streamText` 时执行自己的保留量上限。原始字节消费方调用 `readBytes(target, signal, maxBytes)`;其必填的完整内容上限会使已知或读取中发现的超限以 `FS_TOO_LARGE` 失败,不会截断结果或无界缓冲。
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
@@ -256,6 +256,7 @@ type FsErrorCode =
|
||||
| 'FS_NOT_DIRECTORY'
|
||||
| 'FS_NOT_TEXT'
|
||||
| 'FS_NOT_REGULAR_FILE'
|
||||
| 'FS_TOO_LARGE'
|
||||
| 'FS_PERMISSION_DENIED'
|
||||
| 'FS_SANDBOX_DENIED'
|
||||
| 'FS_IO_ERROR'
|
||||
@@ -274,7 +275,7 @@ type FsErrorCode =
|
||||
|
||||
## 服务与插件
|
||||
|
||||
`FileSystem`(`ctx.fs`,abstract)拥有提供方原语:`resolve`、`processPath`、`fileUrl`、`contains`、`stat`、`lstat`、`readText`、`streamText`、`listDir`、`writeText` 与 `editText`。`dsh-fs-policy` **不注册服务**——它是一个通过 `fs/*` 事件门禁添加策略的插件:根据未见/缺失/存在状态对写入与编辑意图 waterfall 作出决策,并记录 `FsObservation` 值。执行器是 `dsh-tool-fs`:它通过 `ctx.fs` 读取/写入/编辑,分发 waterfall,并 emit 记录事件。下方生成的 [`ctx.fs` 小节](#ctxfs--filesystem-abstract-seam) 展示确切的 `ctx.fs` 签名。
|
||||
`FileSystem`(`ctx.fs`,abstract)拥有提供方原语:`resolve`、`processPath`、`fileUrl`、`contains`、`stat`、`lstat`、`readText`、`streamText`、`readBytes`、`listDir`、`writeText` 与 `editText`。`dsh-fs-policy` **不注册服务**——它是一个通过 `fs/*` 事件门禁添加策略的插件:根据未见/缺失/存在状态对写入与编辑意图 waterfall 作出决策,并记录 `FsObservation` 值。执行器是 `dsh-tool-fs`:它通过 `ctx.fs` 读取/写入/编辑,分发 waterfall,并 emit 记录事件。下方生成的 [`ctx.fs` 小节](#ctxfs--filesystem-abstract-seam) 展示确切的 `ctx.fs` 签名。
|
||||
|
||||
<!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
|
||||
|
||||
@@ -373,6 +374,18 @@ abstract readText(target: FsTarget, signal?: AbortSignal): Promise<string>
|
||||
*/
|
||||
abstract streamText(target: FsTarget, signal?: AbortSignal): Promise<AsyncIterable<string>>
|
||||
|
||||
/**
|
||||
* Read the whole regular file as raw bytes with no decoding or binary
|
||||
* rejection. The bound lives at this seam so a backend can never buffer an
|
||||
* unbounded file: a target known or discovered to exceed `maxBytes` fails
|
||||
* with `FS_TOO_LARGE` instead of returning a truncated result.
|
||||
* @param target - the resolved target to read.
|
||||
* @param signal - aborts the read.
|
||||
* @param maxBytes - inclusive byte cap on the complete content.
|
||||
* @returns the full raw content, at most `maxBytes` long.
|
||||
*/
|
||||
abstract readBytes(target: FsTarget, signal: AbortSignal | undefined, maxBytes: number): Promise<Uint8Array>
|
||||
|
||||
/**
|
||||
* List direct children of a directory in stable name order. Returns resolved
|
||||
* child targets plus cheap metadata only; never reads file contents.
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/subsystems/llm-streaming.md
|
||||
llm-streaming.md: 8ae4e8b376b4c4221e6179eb719fcf162f3031e4
|
||||
llm-streaming.zh.md: 7d244ab882521a90217873fc3cdee12cd5232db8
|
||||
llm-streaming.md: 17f984166906914c49b2c330bdf57b3cecdbd013
|
||||
llm-streaming.zh.md: 6519710dad8a174418bcc97f1bc4b36296ab969e
|
||||
|
||||
@@ -234,7 +234,7 @@ interface AppIdentity {
|
||||
product: string
|
||||
/** Product version; sourced from package metadata, never hand-copied. */
|
||||
version: string
|
||||
/** Public home URL of the app, used as the `User-Agent` comment. */
|
||||
/** Repository home URL of the app, used as the `User-Agent` comment. */
|
||||
url: string
|
||||
}
|
||||
```
|
||||
|
||||
@@ -238,7 +238,7 @@ interface AppIdentity {
|
||||
product: string
|
||||
/** Product version; sourced from package metadata, never hand-copied. */
|
||||
version: string
|
||||
/** Public home URL of the app, used as the `User-Agent` comment. */
|
||||
/** Repository home URL of the app, used as the `User-Agent` comment. */
|
||||
url: string
|
||||
}
|
||||
```
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/subsystems/persistence.md
|
||||
persistence.md: 0266d17393d07c258036f7054a02c4ab9d3c74a2
|
||||
persistence.zh.md: ced83440160ae91ae37025d8024068fb8148b0c6
|
||||
persistence.md: 7deaa9b30b5a6b1e3cbdcc38255b3974b5abf477
|
||||
persistence.zh.md: c5afcf67319da408b739d41b2b7ad3eb434ffbad
|
||||
|
||||
@@ -87,6 +87,10 @@ interface SessionHeader {
|
||||
}
|
||||
```
|
||||
|
||||
## Format refusal — logs a build cannot faithfully read
|
||||
|
||||
A backend refuses a log it cannot faithfully interpret with `SessionFormatUnsupportedError`, distinct from `SessionPersistenceCorruptionError` because nothing is damaged. A header `version` ahead of `SESSION_FORMAT_VERSION` names the direction ("written by a newer harness — upgrade the harness to open it"); one behind it states that this build ships no upgrade path. After legacy-shape normalization, an event type outside this build's generated vocabulary (`KNOWN_SESSION_EVENT_TYPES`, emitted by `gen-persistence-catalog`) refuses the same way unless the event's envelope carries `ignorable: true` — silently skipping an unrecognized required event could change how the rest of the log must be read. The message appends the raw log path when the backend keeps one artifact per session, so the refused text stays reachable. The JSONL backend refuses a foreign version straight from the raw header line, before validating today's header shape or decoding any event row — a structurally different future format still reports the upgrade direction, never "corrupt"; SQLite gates whole-file structure through its own `SCHEMA_VERSION` pragma first. Design rationale and the deferred upgrader chain live in the [session-log-version-mechanism note](../../.agents/notes/implemented/architecture/2026-08-10-session-log-version-mechanism.md).
|
||||
|
||||
## `CreateSessionOptions` — seeding and metadata
|
||||
|
||||
Creating a `Session` through the store takes a `seed` (initial replay or fork history) and `meta` (the storage-level fields the store folds into a `SessionHeader`). The store fills in `version`/`id` and defaults `createdAt`; the caller may supply the validated absolute `cwd`, the `parentSession` lineage, the `seedLength` seed boundary, the optional coarse `origin`, the `delegationDepth`, the `agentPreset` the agent was composed from, and an existing `createdAt`. `origin: 'subagent'` lets product navigation hide duplicate child rows; it does not prove that a descriptor is valid or that the child can resume.
|
||||
@@ -342,5 +346,5 @@ abstract listSnapshots(signal?: AbortSignal): Promise<SessionPersistenceSnapshot
|
||||
|
||||
Types: [SessionEvent](session.md) · [SessionId](core.md)
|
||||
|
||||
Source: [`packages/session/session-persistence/src/index.ts:72`](../../packages/session/session-persistence/src/index.ts)
|
||||
Source: [`packages/session/session-persistence/src/index.ts:74`](../../packages/session/session-persistence/src/index.ts)
|
||||
<!-- END GENERATED cordis-surface -->
|
||||
|
||||
@@ -87,6 +87,10 @@ interface SessionHeader {
|
||||
}
|
||||
```
|
||||
|
||||
## 格式拒绝:本构建无法可靠读取的日志
|
||||
|
||||
后端用 `SessionFormatUnsupportedError` 拒绝无法可靠解读的日志,它与 `SessionPersistenceCorruptionError` 区分,因为数据没有损坏。header 的 `version` 比 `SESSION_FORMAT_VERSION` 新时,消息说明方向("由更新的 harness 写入,请升级 harness 后打开");比它旧时说明本构建没有升级路径。经过 legacy 形状归一化后,本构建生成词汇表(`KNOWN_SESSION_EVENT_TYPES`,由 `gen-persistence-catalog` 生成)之外的事件类型同样被拒绝,除非该事件的信封带 `ignorable: true`:静默跳过一个不认识的必需事件可能改变日志其余部分的解读方式。后端为每个会话保留独立文件时,消息附上原始日志路径,被拒绝的文本仍然可读。JSONL 后端直接从原始 header 行拒绝外来版本,先于当前 header 形状校验和任何事件行解码,因此结构完全不同的未来格式仍会报告升级方向,绝不会报"损坏";SQLite 则先由自己的 `SCHEMA_VERSION` pragma 把关整个文件的结构。设计理由与推迟建设的升级器链见 [session-log 版本机制 Agent Note](../../.agents/notes/implemented/architecture/2026-08-10-session-log-version-mechanism.md)。
|
||||
|
||||
## `CreateSessionOptions`:seed 与元数据
|
||||
|
||||
通过 store 创建 `Session` 时会接收 `seed`(初始回放或 fork 历史)与 `meta`(store 折叠进 `SessionHeader` 的存储层字段)。store 填充 `version`/`id` 并为 `createdAt` 提供默认值;调用方可以提供已校验的绝对 `cwd`、`parentSession` 谱系、`seedLength` 种子边界、可选的粗粒度 `origin`、`delegationDepth`、该 agent 所依据组装的 `agentPreset` 以及已有的 `createdAt`。`origin: 'subagent'` 让产品导航能够隐藏重复的 child 行;它不证明描述符有效,也不证明 child 可以恢复。
|
||||
@@ -342,5 +346,5 @@ abstract listSnapshots(signal?: AbortSignal): Promise<SessionPersistenceSnapshot
|
||||
|
||||
Types: [SessionEvent](session.md) · [SessionId](core.md)
|
||||
|
||||
Source: [`packages/session/session-persistence/src/index.ts:72`](../../packages/session/session-persistence/src/index.ts)
|
||||
Source: [`packages/session/session-persistence/src/index.ts:74`](../../packages/session/session-persistence/src/index.ts)
|
||||
<!-- END GENERATED cordis-surface -->
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/subsystems/session.md
|
||||
session.md: 0b78e51ebf6e2ad5c312268ad4bfb4392b0486df
|
||||
session.zh.md: d1e91f684a835e08406f524efe876baa1a6a72cb
|
||||
session.md: 990b249cde9f02343f2c668aee5d7c000837df56
|
||||
session.zh.md: 39e8ff1e8831fd75c8929c93e263622bb5aa6ea4
|
||||
|
||||
@@ -215,6 +215,17 @@ type SessionEvent<T extends SessionEventType = SessionEventType> = {
|
||||
/** Unix epoch milliseconds. */
|
||||
time: number
|
||||
data: SessionEventMap[K]
|
||||
/**
|
||||
* Marks an event a reader may safely skip when it does not recognize
|
||||
* `type`. Absent means required: a reader meeting an unrecognized type
|
||||
* without this marker MUST refuse to reconstruct the session instead of
|
||||
* silently dropping the event, because an unrecognized required event may
|
||||
* change how the rest of the log is interpreted. A writer sets `true` only
|
||||
* on purely informational records whose loss cannot affect reconstruction;
|
||||
* defaulting to required means a forgotten marker over-refuses (an
|
||||
* inconvenience) rather than silently resuming a gutted session.
|
||||
*/
|
||||
ignorable?: true
|
||||
} & (K extends SurfaceEventType ? {
|
||||
/**
|
||||
* Seq numbers of earlier events that this event cites as sources
|
||||
@@ -733,7 +744,7 @@ fork(source: SessionForkSource, boundary?: number, childSessionId?: SessionId):
|
||||
|
||||
Types: [CreateSessionOptions](persistence.md) · [PrepareSessionOptions](persistence.md) · [SessionId](core.md)
|
||||
|
||||
Source: [`packages/core/session/src/index.ts:810`](../../packages/core/session/src/index.ts)
|
||||
Source: [`packages/core/session/src/index.ts:813`](../../packages/core/session/src/index.ts)
|
||||
|
||||
<a id="session-events"></a>
|
||||
|
||||
@@ -762,7 +773,7 @@ Creation announcement during session publication. A synchronous throw vetoes and
|
||||
|
||||
Types: [Scoped](scope.md)
|
||||
|
||||
Source: [`packages/core/session/src/index.ts:74`](../../packages/core/session/src/index.ts)
|
||||
Source: [`packages/core/session/src/index.ts:75`](../../packages/core/session/src/index.ts)
|
||||
|
||||
<a id="sessiondisposed--emit"></a>
|
||||
|
||||
@@ -785,7 +796,7 @@ Emitted once when an announced session leaves the store, including publication r
|
||||
|
||||
Types: [Scoped](scope.md)
|
||||
|
||||
Source: [`packages/core/session/src/index.ts:84`](../../packages/core/session/src/index.ts)
|
||||
Source: [`packages/core/session/src/index.ts:85`](../../packages/core/session/src/index.ts)
|
||||
|
||||
<a id="sessionevent--emit"></a>
|
||||
|
||||
@@ -810,7 +821,7 @@ Post-commit, fire-and-forget append feed. The listener snapshot resolves before
|
||||
|
||||
Types: [Scoped](scope.md)
|
||||
|
||||
Source: [`packages/core/session/src/index.ts:96`](../../packages/core/session/src/index.ts)
|
||||
Source: [`packages/core/session/src/index.ts:97`](../../packages/core/session/src/index.ts)
|
||||
|
||||
<a id="sessionflush--parallel"></a>
|
||||
|
||||
@@ -832,5 +843,5 @@ Awaited parallel durability checkpoint: every listener runs and the caller await
|
||||
|
||||
Types: [Scoped](scope.md)
|
||||
|
||||
Source: [`packages/core/session/src/index.ts:105`](../../packages/core/session/src/index.ts)
|
||||
Source: [`packages/core/session/src/index.ts:106`](../../packages/core/session/src/index.ts)
|
||||
<!-- END GENERATED cordis-surface -->
|
||||
|
||||
@@ -217,6 +217,17 @@ type SessionEvent<T extends SessionEventType = SessionEventType> = {
|
||||
/** Unix epoch milliseconds. */
|
||||
time: number
|
||||
data: SessionEventMap[K]
|
||||
/**
|
||||
* Marks an event a reader may safely skip when it does not recognize
|
||||
* `type`. Absent means required: a reader meeting an unrecognized type
|
||||
* without this marker MUST refuse to reconstruct the session instead of
|
||||
* silently dropping the event, because an unrecognized required event may
|
||||
* change how the rest of the log is interpreted. A writer sets `true` only
|
||||
* on purely informational records whose loss cannot affect reconstruction;
|
||||
* defaulting to required means a forgotten marker over-refuses (an
|
||||
* inconvenience) rather than silently resuming a gutted session.
|
||||
*/
|
||||
ignorable?: true
|
||||
} & (K extends SurfaceEventType ? {
|
||||
/**
|
||||
* Seq numbers of earlier events that this event cites as sources
|
||||
@@ -737,7 +748,7 @@ fork(source: SessionForkSource, boundary?: number, childSessionId?: SessionId):
|
||||
|
||||
Types: [CreateSessionOptions](persistence.md) · [PrepareSessionOptions](persistence.md) · [SessionId](core.md)
|
||||
|
||||
Source: [`packages/core/session/src/index.ts:810`](../../packages/core/session/src/index.ts)
|
||||
Source: [`packages/core/session/src/index.ts:813`](../../packages/core/session/src/index.ts)
|
||||
|
||||
<a id="session-events"></a>
|
||||
|
||||
@@ -766,7 +777,7 @@ Creation announcement during session publication. A synchronous throw vetoes and
|
||||
|
||||
Types: [Scoped](scope.md)
|
||||
|
||||
Source: [`packages/core/session/src/index.ts:74`](../../packages/core/session/src/index.ts)
|
||||
Source: [`packages/core/session/src/index.ts:75`](../../packages/core/session/src/index.ts)
|
||||
|
||||
<a id="sessiondisposed--emit"></a>
|
||||
|
||||
@@ -789,7 +800,7 @@ Emitted once when an announced session leaves the store, including publication r
|
||||
|
||||
Types: [Scoped](scope.md)
|
||||
|
||||
Source: [`packages/core/session/src/index.ts:84`](../../packages/core/session/src/index.ts)
|
||||
Source: [`packages/core/session/src/index.ts:85`](../../packages/core/session/src/index.ts)
|
||||
|
||||
<a id="sessionevent--emit"></a>
|
||||
|
||||
@@ -814,7 +825,7 @@ Post-commit, fire-and-forget append feed. The listener snapshot resolves before
|
||||
|
||||
Types: [Scoped](scope.md)
|
||||
|
||||
Source: [`packages/core/session/src/index.ts:96`](../../packages/core/session/src/index.ts)
|
||||
Source: [`packages/core/session/src/index.ts:97`](../../packages/core/session/src/index.ts)
|
||||
|
||||
<a id="sessionflush--parallel"></a>
|
||||
|
||||
@@ -836,5 +847,5 @@ Awaited parallel durability checkpoint: every listener runs and the caller await
|
||||
|
||||
Types: [Scoped](scope.md)
|
||||
|
||||
Source: [`packages/core/session/src/index.ts:105`](../../packages/core/session/src/index.ts)
|
||||
Source: [`packages/core/session/src/index.ts:106`](../../packages/core/session/src/index.ts)
|
||||
<!-- END GENERATED cordis-surface -->
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/subsystems/subagent.md
|
||||
subagent.md: 220ecf493b2d3a84cede34c1cf72dbf0dcd80353
|
||||
subagent.zh.md: b0a0df5a778ed758421225e524c87cf8ef213c0b
|
||||
subagent.md: cf728d9f15fdaaf8199954e91b564167e8efe439
|
||||
subagent.zh.md: 1a8e2e5837b8ac88f7d7b7ca767fb7aa21391a9f
|
||||
|
||||
@@ -293,7 +293,12 @@ The outcome of a one-shot run, resolved by `SubagentRun.result`. `structured` is
|
||||
* The terminal outcome of a subagent run, resolved by {@link SubagentRun.result}.
|
||||
*/
|
||||
interface SubagentResult {
|
||||
/** The child's final assistant output (the last assistant message's content). */
|
||||
/**
|
||||
* The child's final assistant output is the content of its last non-empty
|
||||
* assistant message. Empty-content messages, including usage-only messages,
|
||||
* are skipped. Without a non-empty message, the output is its accumulated
|
||||
* assistant text stream, or `[]` when the child produced neither.
|
||||
*/
|
||||
readonly output: ContentBlock[]
|
||||
/**
|
||||
* The structured result after a requested `outputSchema` was successfully
|
||||
@@ -385,7 +390,10 @@ Each provider is a named child-agent transport, and multiple providers may coexi
|
||||
/**
|
||||
* One registered transport for running child agents. Providers are trusted
|
||||
* same-process implementations; callers treat descriptors and returned values
|
||||
* as borrowed immutable data.
|
||||
* as borrowed immutable data. The service may call one provider concurrently
|
||||
* for distinct children. Providers isolate operation-local mutable state; a
|
||||
* shared capacity controller may delay an operation but must not couple its
|
||||
* settlement or cleanup to a sibling.
|
||||
*/
|
||||
interface SubagentProvider {
|
||||
/** Unique registry name (e.g. `spawn`, `fork`, `acp`). */
|
||||
@@ -406,7 +414,8 @@ interface SubagentProvider {
|
||||
* initial turn. Before fulfillment, the provider owns setup and cleans any
|
||||
* unpublished partial resources before rejecting. Ownership transfers on
|
||||
* fulfillment; subsequent turn or infrastructure failure settles through
|
||||
* the returned run.
|
||||
* the returned run. Distinct starts may overlap; cancellation, failure,
|
||||
* result settlement, and disposal remain independent for each run.
|
||||
*/
|
||||
start(request: ResolvedSubagentStartRequest): Promise<SubagentRun>
|
||||
/**
|
||||
@@ -421,6 +430,8 @@ interface SubagentProvider {
|
||||
* continuation manager owns identity reservation, composition, Agent
|
||||
* creation, prompt delivery, cold resume, ownership, and disposal, so a
|
||||
* provider never sees the child's Agent, handle, turns, or teardown.
|
||||
* Distinct preparations may overlap; each follows its own signal and returns
|
||||
* data belonging only to `request.sessionId`.
|
||||
*/
|
||||
prepareContinuable?(request: ContinuableCreateRequest): Promise<ContinuableCreateSpec>
|
||||
}
|
||||
@@ -614,7 +625,7 @@ async start(name: string, request: SubagentStartRequest): Promise<SubagentRun>
|
||||
|
||||
Types: [Agent](core.md) · [ContentBlock](llm-streaming.md) · [MessageId](llm-streaming.md) · [SessionId](core.md)
|
||||
|
||||
Source: [`packages/subagent/subagent/src/index.ts:169`](../../packages/subagent/subagent/src/index.ts)
|
||||
Source: [`packages/subagent/subagent/src/index.ts:170`](../../packages/subagent/subagent/src/index.ts)
|
||||
|
||||
<a id="subagent-events"></a>
|
||||
|
||||
@@ -640,7 +651,7 @@ A published child settled. Scope-filtered dispatch uses the same delegating pare
|
||||
|
||||
Types: [Scoped](scope.md)
|
||||
|
||||
Source: [`packages/subagent/subagent/src/index.ts:164`](../../packages/subagent/subagent/src/index.ts)
|
||||
Source: [`packages/subagent/subagent/src/index.ts:165`](../../packages/subagent/subagent/src/index.ts)
|
||||
|
||||
<a id="subagentprovider-added--emit"></a>
|
||||
|
||||
@@ -657,7 +668,7 @@ A provider became resolvable in the registry.
|
||||
'subagent/provider-added'(provider: SubagentProvider): void
|
||||
```
|
||||
|
||||
Source: [`packages/subagent/subagent/src/index.ts:138`](../../packages/subagent/subagent/src/index.ts)
|
||||
Source: [`packages/subagent/subagent/src/index.ts:139`](../../packages/subagent/subagent/src/index.ts)
|
||||
|
||||
<a id="subagentprovider-removed--emit"></a>
|
||||
|
||||
@@ -674,7 +685,7 @@ A provider left the registry. Accepted runs remain holder-owned.
|
||||
'subagent/provider-removed'(name: string): void
|
||||
```
|
||||
|
||||
Source: [`packages/subagent/subagent/src/index.ts:144`](../../packages/subagent/subagent/src/index.ts)
|
||||
Source: [`packages/subagent/subagent/src/index.ts:145`](../../packages/subagent/subagent/src/index.ts)
|
||||
|
||||
<a id="subagentstart--emit"></a>
|
||||
|
||||
@@ -698,5 +709,5 @@ A provider established a published child. For in-process providers, `ctx.agents.
|
||||
|
||||
Types: [Scoped](scope.md)
|
||||
|
||||
Source: [`packages/subagent/subagent/src/index.ts:155`](../../packages/subagent/subagent/src/index.ts)
|
||||
Source: [`packages/subagent/subagent/src/index.ts:156`](../../packages/subagent/subagent/src/index.ts)
|
||||
<!-- END GENERATED cordis-surface -->
|
||||
|
||||
@@ -293,7 +293,12 @@ type SubagentDescendantListEntry = SubagentListEntry & {
|
||||
* The terminal outcome of a subagent run, resolved by {@link SubagentRun.result}.
|
||||
*/
|
||||
interface SubagentResult {
|
||||
/** The child's final assistant output (the last assistant message's content). */
|
||||
/**
|
||||
* The child's final assistant output is the content of its last non-empty
|
||||
* assistant message. Empty-content messages, including usage-only messages,
|
||||
* are skipped. Without a non-empty message, the output is its accumulated
|
||||
* assistant text stream, or `[]` when the child produced neither.
|
||||
*/
|
||||
readonly output: ContentBlock[]
|
||||
/**
|
||||
* The structured result after a requested `outputSchema` was successfully
|
||||
@@ -387,7 +392,10 @@ interface SubagentRun {
|
||||
/**
|
||||
* One registered transport for running child agents. Providers are trusted
|
||||
* same-process implementations; callers treat descriptors and returned values
|
||||
* as borrowed immutable data.
|
||||
* as borrowed immutable data. The service may call one provider concurrently
|
||||
* for distinct children. Providers isolate operation-local mutable state; a
|
||||
* shared capacity controller may delay an operation but must not couple its
|
||||
* settlement or cleanup to a sibling.
|
||||
*/
|
||||
interface SubagentProvider {
|
||||
/** Unique registry name (e.g. `spawn`, `fork`, `acp`). */
|
||||
@@ -408,7 +416,8 @@ interface SubagentProvider {
|
||||
* initial turn. Before fulfillment, the provider owns setup and cleans any
|
||||
* unpublished partial resources before rejecting. Ownership transfers on
|
||||
* fulfillment; subsequent turn or infrastructure failure settles through
|
||||
* the returned run.
|
||||
* the returned run. Distinct starts may overlap; cancellation, failure,
|
||||
* result settlement, and disposal remain independent for each run.
|
||||
*/
|
||||
start(request: ResolvedSubagentStartRequest): Promise<SubagentRun>
|
||||
/**
|
||||
@@ -423,6 +432,8 @@ interface SubagentProvider {
|
||||
* continuation manager owns identity reservation, composition, Agent
|
||||
* creation, prompt delivery, cold resume, ownership, and disposal, so a
|
||||
* provider never sees the child's Agent, handle, turns, or teardown.
|
||||
* Distinct preparations may overlap; each follows its own signal and returns
|
||||
* data belonging only to `request.sessionId`.
|
||||
*/
|
||||
prepareContinuable?(request: ContinuableCreateRequest): Promise<ContinuableCreateSpec>
|
||||
}
|
||||
@@ -616,7 +627,7 @@ async start(name: string, request: SubagentStartRequest): Promise<SubagentRun>
|
||||
|
||||
Types: [Agent](core.md) · [ContentBlock](llm-streaming.md) · [MessageId](llm-streaming.md) · [SessionId](core.md)
|
||||
|
||||
Source: [`packages/subagent/subagent/src/index.ts:169`](../../packages/subagent/subagent/src/index.ts)
|
||||
Source: [`packages/subagent/subagent/src/index.ts:170`](../../packages/subagent/subagent/src/index.ts)
|
||||
|
||||
<a id="subagent-events"></a>
|
||||
|
||||
@@ -642,7 +653,7 @@ A published child settled. Scope-filtered dispatch uses the same delegating pare
|
||||
|
||||
Types: [Scoped](scope.md)
|
||||
|
||||
Source: [`packages/subagent/subagent/src/index.ts:164`](../../packages/subagent/subagent/src/index.ts)
|
||||
Source: [`packages/subagent/subagent/src/index.ts:165`](../../packages/subagent/subagent/src/index.ts)
|
||||
|
||||
<a id="subagentprovider-added--emit"></a>
|
||||
|
||||
@@ -659,7 +670,7 @@ A provider became resolvable in the registry.
|
||||
'subagent/provider-added'(provider: SubagentProvider): void
|
||||
```
|
||||
|
||||
Source: [`packages/subagent/subagent/src/index.ts:138`](../../packages/subagent/subagent/src/index.ts)
|
||||
Source: [`packages/subagent/subagent/src/index.ts:139`](../../packages/subagent/subagent/src/index.ts)
|
||||
|
||||
<a id="subagentprovider-removed--emit"></a>
|
||||
|
||||
@@ -676,7 +687,7 @@ A provider left the registry. Accepted runs remain holder-owned.
|
||||
'subagent/provider-removed'(name: string): void
|
||||
```
|
||||
|
||||
Source: [`packages/subagent/subagent/src/index.ts:144`](../../packages/subagent/subagent/src/index.ts)
|
||||
Source: [`packages/subagent/subagent/src/index.ts:145`](../../packages/subagent/subagent/src/index.ts)
|
||||
|
||||
<a id="subagentstart--emit"></a>
|
||||
|
||||
@@ -700,5 +711,5 @@ A provider established a published child. For in-process providers, `ctx.agents.
|
||||
|
||||
Types: [Scoped](scope.md)
|
||||
|
||||
Source: [`packages/subagent/subagent/src/index.ts:155`](../../packages/subagent/subagent/src/index.ts)
|
||||
Source: [`packages/subagent/subagent/src/index.ts:156`](../../packages/subagent/subagent/src/index.ts)
|
||||
<!-- END GENERATED cordis-surface -->
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/subsystems/tasks.md
|
||||
tasks.md: 2da8212b3edf7111180b60162108af0aebb249c1
|
||||
tasks.zh.md: 825a69029ae931f9668cca863c6ebb2324634a9a
|
||||
tasks.md: 6d205d9a7840aef1c115c97836886f51bed829b9
|
||||
tasks.zh.md: 59b7c03d240c45e633f55583b3e08776ec470d52
|
||||
|
||||
@@ -246,6 +246,30 @@ abstract wait(id: TaskId, timeoutMs: number, caller?: Agent, signal?: AbortSigna
|
||||
*/
|
||||
abstract onTaskDone(listener: TaskDoneListener): () => void
|
||||
|
||||
/**
|
||||
/**
|
||||
* Register an effect-scoped observer of visible-set changes. It fires after
|
||||
* every commit that changes what {@link list} returns for that owner —
|
||||
* registration, every stopping transition (including the one teardown
|
||||
* performs before it awaits a slow producer), settlement, owner-disposal
|
||||
* removal, and the emptying that service disposal commits — so an observer
|
||||
* re-reads rather than accumulating deltas.
|
||||
*
|
||||
* Delivery is owner-relative on the same terms as {@link onTaskDone}: an
|
||||
* observer registered from an unscoped context — a host composition's own
|
||||
* carrier — sees every owner, while one registered under an agent
|
||||
* composition's scope sees exactly the agents composed under it.
|
||||
*
|
||||
* This is not a superset of {@link onTaskDone}: that one delivers the terminal
|
||||
* record under first-wins semantics a control surface couples to notice
|
||||
* delivery, while this one carries no delivery meaning and marks nothing
|
||||
* reported. Listeners are contained and never awaited.
|
||||
* @param listener - receives the owner whose visible set changed, or
|
||||
* `undefined` when an unowned task changed and every caller's set did.
|
||||
* @returns disposer that unregisters the listener.
|
||||
*/
|
||||
abstract onTasksChanged(listener: TasksChangedListener): () => void
|
||||
|
||||
/**
|
||||
* Attach an effect-scoped surface that can read and stop tasks. It serves the
|
||||
* owners its registering context's scope covers, and {@link start} refuses an
|
||||
@@ -258,5 +282,5 @@ abstract attachSurface(name: string): () => void
|
||||
|
||||
Types: [Agent](core.md)
|
||||
|
||||
Source: [`packages/tasks/tasks/src/index.ts:55`](../../packages/tasks/tasks/src/index.ts)
|
||||
Source: [`packages/tasks/tasks/src/index.ts:58`](../../packages/tasks/tasks/src/index.ts)
|
||||
<!-- END GENERATED cordis-surface -->
|
||||
|
||||
@@ -151,7 +151,7 @@ interface TaskRead {
|
||||
|
||||
## 服务行为
|
||||
|
||||
抽象的 [`TaskService`](../../packages/tasks/tasks/src/index.ts) Service Definition 规定原子 `start`、限定调用方作用域的 `get` 和 `list`、`read`、`kill`、有界 `wait`、故障隔离的 `onTaskDone` 监听器,以及 `attachSurface` 何时可用;[`LocalTaskService`](../../packages/tasks/tasks-local/src/index.ts) 是其进程局部 Service provider。授权会比较拥有者会话;拥有者清理会选择确切的已注册 `Agent` 实例。Service Definition 约定见 [`dsh-tasks`](../../packages/tasks/tasks/README.md),注册表生命周期见 [`dsh-tasks-local`](../../packages/tasks/tasks-local/README.md),面向模型的 Consumer 见 [`dsh-tool-tasks`](../../packages/tasks/tool-tasks/README.md)。
|
||||
抽象的 [`TaskService`](../../packages/tasks/tasks/src/index.ts) Service Definition 规定原子 `start`、限定调用方作用域的 `get` 和 `list`、`read`、`kill`、有界 `wait`、故障隔离的 `onTaskDone` 与 `onTasksChanged` 监听器,以及 `attachSurface` 何时可用;[`LocalTaskService`](../../packages/tasks/tasks-local/src/index.ts) 是其进程局部 Service provider。授权会比较拥有者会话;拥有者清理会选择确切的已注册 `Agent` 实例。Service Definition 约定见 [`dsh-tasks`](../../packages/tasks/tasks/README.md),注册表生命周期见 [`dsh-tasks-local`](../../packages/tasks/tasks-local/README.md),面向模型的 Consumer 见 [`dsh-tool-tasks`](../../packages/tasks/tool-tasks/README.md)。
|
||||
|
||||
<!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
|
||||
|
||||
@@ -246,6 +246,30 @@ abstract wait(id: TaskId, timeoutMs: number, caller?: Agent, signal?: AbortSigna
|
||||
*/
|
||||
abstract onTaskDone(listener: TaskDoneListener): () => void
|
||||
|
||||
/**
|
||||
/**
|
||||
* Register an effect-scoped observer of visible-set changes. It fires after
|
||||
* every commit that changes what {@link list} returns for that owner —
|
||||
* registration, every stopping transition (including the one teardown
|
||||
* performs before it awaits a slow producer), settlement, owner-disposal
|
||||
* removal, and the emptying that service disposal commits — so an observer
|
||||
* re-reads rather than accumulating deltas.
|
||||
*
|
||||
* Delivery is owner-relative on the same terms as {@link onTaskDone}: an
|
||||
* observer registered from an unscoped context — a host composition's own
|
||||
* carrier — sees every owner, while one registered under an agent
|
||||
* composition's scope sees exactly the agents composed under it.
|
||||
*
|
||||
* This is not a superset of {@link onTaskDone}: that one delivers the terminal
|
||||
* record under first-wins semantics a control surface couples to notice
|
||||
* delivery, while this one carries no delivery meaning and marks nothing
|
||||
* reported. Listeners are contained and never awaited.
|
||||
* @param listener - receives the owner whose visible set changed, or
|
||||
* `undefined` when an unowned task changed and every caller's set did.
|
||||
* @returns disposer that unregisters the listener.
|
||||
*/
|
||||
abstract onTasksChanged(listener: TasksChangedListener): () => void
|
||||
|
||||
/**
|
||||
* Attach an effect-scoped surface that can read and stop tasks. It serves the
|
||||
* owners its registering context's scope covers, and {@link start} refuses an
|
||||
@@ -258,5 +282,5 @@ abstract attachSurface(name: string): () => void
|
||||
|
||||
Types: [Agent](core.md)
|
||||
|
||||
Source: [`packages/tasks/tasks/src/index.ts:55`](../../packages/tasks/tasks/src/index.ts)
|
||||
Source: [`packages/tasks/tasks/src/index.ts:58`](../../packages/tasks/tasks/src/index.ts)
|
||||
<!-- END GENERATED cordis-surface -->
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/subsystems/telemetry.md
|
||||
telemetry.md: 5ea5c67210ce1387cbd886935e914baf7f904fbb
|
||||
telemetry.zh.md: bd8fc8acc4c8522d8b1e4bc543431c0abf224411
|
||||
telemetry.md: 97694a9a5a209224087d0d8454d83e29ce568ea4
|
||||
telemetry.zh.md: 9e8b17f4bddb3debdf4dff9d3c3fed1296ebf3d7
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
English | [中文](telemetry.zh.md)
|
||||
|
||||
Outbound session reporting is one [capability seam](../capability-seams.md): its Service Definition ([dsh-session-telemetry](../../packages/session/session-telemetry), `ctx.telemetry`) declares the minimal backend contract, and its capture coordinator owns the capture points, fixed chunk projection, `telemetry/record` redaction waterfall, and handoff cursor; the Service provider a deployment loads ([dsh-session-telemetry-otel](../../packages/session/session-telemetry-otel)) uses the OpenTelemetry JS SDK's log pipeline with its configuration unchanged. This optional capability is not part of the agent loop, and nothing here reaches a model request. The harness stops after it calls `emit()`; the reporting SDK owns batching, retry, queueing, and loss policy. The [revival Agent Note](../../.agents/notes/implemented/feature/2026-07-23-session-telemetry-otel-revival.md) records that rule and the rejected alternatives. The [Service Definition README](../../packages/session/session-telemetry/README.md) defines the capture-point, cursor, and projection contracts.
|
||||
Outbound session reporting is split as a [capability seam](../capability-seams.md): the Service Definition and capture coordinator ([dsh-session-telemetry](../../packages/session/session-telemetry), `ctx.telemetry`) own the capture points, fixed chunk projection, `telemetry/record` redaction waterfall, handoff cursor, and minimal backend contract; the Service provider a deployment loads ([dsh-session-telemetry-otel](../../packages/session/session-telemetry-otel)) is the OpenTelemetry JS SDK's log pipeline configured verbatim. It is one optional capability, not part of the agent-loop spine, and nothing here reaches a model request. The boundary axiom — the harness's aspect ends at `emit()`; batching, retry, queueing, and loss policy belong to the reporting SDK — and the rejected alternatives are pinned in the [revival Agent Note](../../.agents/notes/implemented/feature/2026-07-23-session-telemetry-otel-revival.md); the capture points, cursor, and projection contracts live in the [Service Definition README](../../packages/session/session-telemetry/README.md).
|
||||
|
||||
Source: [`packages/session/session-telemetry/src/index.ts`](../../packages/session/session-telemetry/src/index.ts)
|
||||
|
||||
@@ -56,6 +56,21 @@ interface TelemetryRecord {
|
||||
|
||||
Only the first `assistant/chunk` of each `(turn, step)` ships — the stream-started signal; the rest drop at capture, so `seq` gaps are routine on the wire and never a loss signal. Every other [session event](session.md) type, including plugin-merged ones the seam never heard of, passes through whole. Delivery is best-effort: the cursor marks handed-off, not delivered, records can be lost (crash, reload window) and duplicated (cursor-less re-adoption, SDK retries), so receivers dedupe ledger records on `(session.id, event.seq)`; ops records deliberately omit that identity — they are signals to alert on, not entries to sum, and tolerate duplicates instead.
|
||||
|
||||
## The sharing disclosure
|
||||
|
||||
The seam's acknowledgement contract (owned by the [Service Definition README's sharing-disclosure section](../../packages/session/session-telemetry/README.md#the-sharing-disclosure)): every backend discloses its deployment-selected sharing policy through the required abstract `sharing` member on `ctx.telemetry`, and consumers render "not configured" only when no telemetry service is mounted. The disclosure states the current policy, never delivery or retention — handoff is the non-blocking enqueue, and batching, retry, and loss policy stay the reporting SDK's.
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Deployment-selected session-sharing policy disclosed by a mounted
|
||||
* {@link Telemetry} backend to human-facing acknowledgement surfaces (the
|
||||
* `/feedback` command's confirmation text). The seam owns the vocabulary so
|
||||
* any backend can disclose a policy without depending on the OTel package;
|
||||
* the values mirror the OTel backend's serialized `TelemetryMode` choices.
|
||||
*/
|
||||
type TelemetrySharingStatus = 'full' | 'feedback-only' | 'disabled'
|
||||
```
|
||||
|
||||
## The backend contract
|
||||
|
||||
```ts type-equiv
|
||||
@@ -104,7 +119,7 @@ interface TelemetryBackend {
|
||||
}
|
||||
```
|
||||
|
||||
`Telemetry` (`ctx.telemetry`, [signatures](#ctxtelemetry--telemetry-abstract-seam)) is the loadable form of this contract: each context accepts one implementation and throws on a duplicate. A backend constructs `TelemetryCoordinator` in its constructor to install capture.
|
||||
`Telemetry` (`ctx.telemetry`, [signatures](#ctxtelemetry--telemetry-abstract-seam)) is the contract's loadable form — one implementation per context, duplicate load throws — and a backend composes the seam's `TelemetryCoordinator` in its constructor to install the capture side.
|
||||
|
||||
## The redact waterfall: `telemetry/record`
|
||||
|
||||
@@ -141,7 +156,7 @@ flush?(): void
|
||||
abstract shutdown(): Promise<void>
|
||||
```
|
||||
|
||||
Source: [`packages/session/session-telemetry/src/index.ts:139`](../../packages/session/session-telemetry/src/index.ts)
|
||||
Source: [`packages/session/session-telemetry/src/index.ts:148`](../../packages/session/session-telemetry/src/index.ts)
|
||||
|
||||
<a id="telemetry-events"></a>
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
[English](telemetry.md) | 中文
|
||||
|
||||
对外会话上报是一项[能力 seam](../capability-seams.md):其 Service Definition([dsh-session-telemetry](../../packages/session/session-telemetry),`ctx.telemetry`)声明最小后端约定,其捕获协调器负责捕获点、固定分片投影、`telemetry/record` 脱敏 waterfall(瀑布式事件)和 handoff 游标;部署方加载的 Service provider([dsh-session-telemetry-otel](../../packages/session/session-telemetry-otel))按原配置使用 OpenTelemetry JS SDK 日志流水线。这项能力可选,不属于 agent loop(智能体循环),这里也没有任何内容会进入模型请求。Harness 调用 `emit()` 后停止处理;上报 SDK 负责批处理、重试、排队和丢失策略。[复活 Agent Note](../../.agents/notes/implemented/feature/2026-07-23-session-telemetry-otel-revival.md)记录了这条规则和被否决的替代方案。[Service Definition README](../../packages/session/session-telemetry/README.md) 定义捕获点、游标和投影约定。
|
||||
对外的会话上报拆分为一项[能力 seam](../capability-seams.md):Service Definition 与捕获协调器([dsh-session-telemetry](../../packages/session/session-telemetry),`ctx.telemetry`)拥有捕获点、固定分片投影、`telemetry/record` 脱敏 waterfall(瀑布式事件)、handoff 游标与最小后端约定;部署方加载的 Service provider([dsh-session-telemetry-otel](../../packages/session/session-telemetry-otel))则是原样配置的 OpenTelemetry JS SDK 日志流水线。它是一项可选能力,不属于 agent loop(智能体循环)主干,这里也没有任何内容会进入模型请求。边界公理(harness 的职责止于 `emit()`;批处理、重试、排队与丢失策略都属于上报 SDK)连同被否决的替代方案,均已在[复活 Agent Note(agent 决策记录)](../../.agents/notes/implemented/feature/2026-07-23-session-telemetry-otel-revival.md)中定案;捕获点、游标与投影的约定见 [Service Definition README](../../packages/session/session-telemetry/README.md)。
|
||||
|
||||
源码:[`packages/session/session-telemetry/src/index.ts`](../../packages/session/session-telemetry/src/index.ts)
|
||||
|
||||
@@ -56,6 +56,21 @@ interface TelemetryRecord {
|
||||
|
||||
每个 `(turn, step)` 只发出第一条 `assistant/chunk`,即「流已开始」的信号;其余分片在捕获时丢弃,因此导出流中的 `seq` 缺口是常态,绝不是丢失信号。其他所有[会话事件](session.md)类型都会完整透传,包括该 seam 从未听说过、由插件合并进来的事件类型。投递是尽力而为的:游标标记的是「已交接」而非「已送达」,记录可能丢失(崩溃、重载窗口)也可能重复(无游标的重新接管、SDK 重试),因此接收端对 ledger 记录基于 `(session.id, event.seq)` 去重;ops 记录刻意省略这类标识——它们是用于告警的信号,而非用于累加的条目,重复被容忍而非被去重。
|
||||
|
||||
## 共享披露
|
||||
|
||||
该 seam 的确认契约(归属 [Service Definition README 的共享披露段](../../packages/session/session-telemetry/README.md#the-sharing-disclosure)):每个后端都通过 `ctx.telemetry` 上必需的抽象 `sharing` 成员披露其部署级共享策略,消费方只有在未挂载任何遥测服务时才渲染「未配置」。披露只陈述当前策略,绝不承诺投递或留存——交接是非阻塞入队,批处理、重试与丢失策略仍归上报 SDK。
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Deployment-selected session-sharing policy disclosed by a mounted
|
||||
* {@link Telemetry} backend to human-facing acknowledgement surfaces (the
|
||||
* `/feedback` command's confirmation text). The seam owns the vocabulary so
|
||||
* any backend can disclose a policy without depending on the OTel package;
|
||||
* the values mirror the OTel backend's serialized `TelemetryMode` choices.
|
||||
*/
|
||||
type TelemetrySharingStatus = 'full' | 'feedback-only' | 'disabled'
|
||||
```
|
||||
|
||||
## 后端约定
|
||||
|
||||
```ts type-equiv
|
||||
@@ -104,7 +119,7 @@ interface TelemetryBackend {
|
||||
}
|
||||
```
|
||||
|
||||
`Telemetry`(`ctx.telemetry`,[签名](#ctxtelemetry--telemetry-abstract-seam))是该约定的可加载类型:每个上下文只允许一个实现,重复加载会抛出异常。后端在构造函数中创建 `TelemetryCoordinator`,以安装捕获处理。
|
||||
`Telemetry`(`ctx.telemetry`,[签名](#ctxtelemetry--telemetry-abstract-seam))是该约定的可加载形态:每个上下文只允许一个实现,重复加载会抛出异常;后端在其构造函数中组合 seam 的 `TelemetryCoordinator`,以此装配捕获侧。
|
||||
|
||||
## 脱敏 waterfall:`telemetry/record`
|
||||
|
||||
@@ -141,7 +156,7 @@ flush?(): void
|
||||
abstract shutdown(): Promise<void>
|
||||
```
|
||||
|
||||
Source: [`packages/session/session-telemetry/src/index.ts:139`](../../packages/session/session-telemetry/src/index.ts)
|
||||
Source: [`packages/session/session-telemetry/src/index.ts:148`](../../packages/session/session-telemetry/src/index.ts)
|
||||
|
||||
<a id="telemetry-events"></a>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user