Merge origin/master into refactor/unify-time-vocabulary
Resolutions: - docs/config-catalog.md and .zh.md are regenerated from the merged package descriptions.
This commit is contained in:
commit
dbae54a7c9
374 files changed
+3485
-1745
No files matched your search
+6
-6
@@ -6,14 +6,14 @@
|
||||
en: 41f30d473ff53ce2
|
||||
zh: 6851c4414f5331c1
|
||||
/agent-note-session-projections-as-a-required-reader-seam/problem:
|
||||
en: c0c779c717cad546
|
||||
zh: 5d2916ac86bf5d84
|
||||
en: fca00eeb7a8d9abd
|
||||
zh: d7a9766f5322dff9
|
||||
/agent-note-session-projections-as-a-required-reader-seam/decision:
|
||||
en: a1c50d2f43334a88
|
||||
zh: d7736994733454b4
|
||||
en: 8d38ed9356085c7e
|
||||
zh: e15ede51415192f3
|
||||
/agent-note-session-projections-as-a-required-reader-seam/alternatives-considered:
|
||||
en: aea3da939c90d240
|
||||
zh: 21e91c481cdd23b7
|
||||
/agent-note-session-projections-as-a-required-reader-seam/consequences:
|
||||
en: 73325513ec70fbb2
|
||||
zh: 610bf34b9f9db420
|
||||
en: ab45eb87f7c22cdb
|
||||
zh: e65a97991a45164f
|
||||
+5
-7
@@ -6,19 +6,17 @@ English | [中文](2026-08-19-session-projection-mandatory-seam.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
An optional projection registry lets a plugin whose host behavior reads projected state activate without that state. Unless the reader rejects the missing registry or key, the host behavior or subagent catalog fields can silently disappear. Batch-only reads also materialize every client view when a consumer needs one host value. Some contributor sites intentionally retain optional `ctx.inject` registration, so their readers need an explicit missing-state rule.
|
||||
An optional projection registry lets a plugin whose host behavior reads projected state activate without that state. Unless the reader rejects the missing registry or key, the host behavior or subagent catalog fields can silently disappear. Batch-only reads also materialize every client view when a consumer needs one host value.
|
||||
|
||||
## Decision
|
||||
|
||||
This decision builds on the split between host projection state and client views in [Session projection state and client views](../../archived/architecture/2026-08-19-session-projection-state-and-client-views.md).
|
||||
|
||||
Every host reader treats the projection registry and its required key as mandatory state. A plugin either declares `sessionProjections` as a required injection or resolves the registry and key explicitly and throws on the first dependent access. Official compositions mount the registry before those plugins. `ApiProxyService` follows the required-injection form; the lower-level `createApiProxy` factory remains tolerant for isolated tests and diagnostics.
|
||||
Every host state owner treats the projection registry and its required key as mandatory state. Owners declare `sessionProjections` as a required injection and await acquisition before exposing service methods; other consumers depend on those services. Official compositions mount the registry before those plugins. `ApiProxyService` follows the required-injection form; the lower-level `createApiProxy` factory remains tolerant for isolated tests and diagnostics.
|
||||
|
||||
A domain contributor may register its unit through `ctx.inject(['sessionProjections'], ...)`. Optional registration controls the child lifecycle only; it does not authorize a reader to substitute a default when the registry or key is absent.
|
||||
Owners acquire state through [awaited registration and retained handles](2026-10-12-session-projection-registration-handles.md). That decision supersedes optional first-access acquisition and returning a bare disposer. It preserves the required-state rule here: a missing registry cannot silently produce default host state. Client views still contain only consumed fields; business service methods expose the host data each consumer needs.
|
||||
|
||||
The registry provides `stateOf(session, key)` for one typed host state and keeps `snapshot()` for batch carriers. Client views contain only consumed fields; host readers use `stateOf` for richer state.
|
||||
|
||||
`onChanged` publishes client-visible value changes only. Unit registration and removal remain effect-scoped registry lifecycle; `register()` returns the exact Cordis disposer so a composite domain owner can finish cleanup against projected state before removing its unit. Registration changes do not create a second Host event stream or client tombstone protocol. A later authoritative history or list baseline reflects the active key set.
|
||||
`onChanged` publishes client-visible value changes only. Registration changes do not create a second Host event stream or client tombstone protocol. A later authoritative history or list baseline reflects the active key set.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
@@ -30,6 +28,6 @@ The registry provides `stateOf(session, key)` for one typed host state and keeps
|
||||
|
||||
## Consequences
|
||||
|
||||
- Missing projection composition fails either during activation or at the first dependent host access; it never degrades to a default value.
|
||||
- Missing projection composition fails during activation; it never degrades to a default value.
|
||||
- Host consumers avoid repeated whole-registry snapshots and log scans.
|
||||
- Wire payloads exclude host-only fields and per-key watermark wrappers; ordinary baselines communicate the active key set.
|
||||
+5
-7
@@ -6,19 +6,17 @@ Status: implemented
|
||||
|
||||
## 问题
|
||||
|
||||
可选的投影注册表会让 host 行为读取投影状态的插件在缺少该状态时仍然激活。除非读取方拒绝缺失的注册表或 key,否则 host 行为或 subagent 目录字段可能静默消失。只有批量读取也会在消费方只需要一个 host 值时物化每个客户端 view。部分贡献位置有意保留可选的 `ctx.inject` 注册形式,因此其读取方需要明确的缺失状态规则。
|
||||
可选的投影注册表会让 host 行为读取投影状态的插件在缺少该状态时仍然激活。除非读取方拒绝缺失的注册表或 key,否则 host 行为或 subagent 目录字段可能静默消失。只有批量读取也会在消费方只需要一个 host 值时物化每个客户端 view。
|
||||
|
||||
## 决策
|
||||
|
||||
本决策建立在[会话投影的 host 状态与客户端视图](../../archived/architecture/2026-08-19-session-projection-state-and-client-views.md)所定义的拆分之上。
|
||||
|
||||
每个 host 读取方都把投影注册表及其所需 key 视为必需状态。插件要么把 `sessionProjections` 声明为必需注入,要么显式解析注册表与 key,并在第一次依赖它们的访问时抛错。正式组合在这些插件之前挂载注册表。`ApiProxyService` 采用必需注入形式;较低层的 `createApiProxy` factory 对隔离测试和诊断保持容错。
|
||||
每个 host 状态所有者都把投影注册表及其所需 key 视为必需状态。所有者将 `sessionProjections` 声明为必需注入,并在提供服务方法前等待获取完成;其他消费方依赖这些服务。正式组合在这些插件之前挂载注册表。`ApiProxyService` 采用必需注入形式;较低层的 `createApiProxy` factory 对隔离测试和诊断保持容错。
|
||||
|
||||
领域贡献方可以通过 `ctx.inject(['sessionProjections'], ...)` 注册单元。可选注册只控制子功能生命周期;它并不允许读取方在注册表或 key 缺失时替换为默认值。
|
||||
所有者通过[等待注册与保留句柄](2026-10-12-session-projection-registration-handles.zh.md)获取状态。该决策取代可选首次访问获取和返回裸释放函数的方式,保留这里的必需状态规则:缺少注册表不能静默产生默认 host 状态。客户端视图仍只包含消费字段;业务服务方法提供各消费方所需的 host 数据。
|
||||
|
||||
注册表提供 `stateOf(session, key)` 来读取一个类型化 host 状态,并为批量 carrier 保留 `snapshot()`。客户端 view 只包含消费方使用的字段;host 读取方通过 `stateOf` 取得更丰富的状态。
|
||||
|
||||
`onChanged` 只发布客户端可见值的变化。单元注册和移除仍是绑定 effect 的注册表生命周期;`register()` 返回 Cordis 的原始 disposer,使组合式领域 owner 可以先依据投影状态完成清理,再移除自身单元。注册变化不会创建第二条 Host 事件流或客户端 tombstone 协议。后续权威 history 或 list 基线会反映活跃 key 集。
|
||||
`onChanged` 只发布客户端可见值的变化。注册变化不会创建第二条 Host 事件流或客户端 tombstone 协议。后续权威 history 或 list 基线会反映活跃 key 集。
|
||||
|
||||
## 考虑过的替代方案
|
||||
|
||||
@@ -30,6 +28,6 @@ Status: implemented
|
||||
|
||||
## 后果
|
||||
|
||||
- 缺少投影组合会在激活期间或第一次依赖它的 host 访问时失败,绝不会降级为默认值。
|
||||
- 缺少投影组合会在激活期间失败,绝不会降级为默认值。
|
||||
- host 消费方避免重复的全注册表快照和日志扫描。
|
||||
- 协议负载排除 host 内部字段和逐 key 水位包装;普通基线会传达活跃 key 集。
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
# Bilingual-pair consistency record for 2026-10-12-session-projection-registration-handles.md (docs/i18n/README.md): per heading
|
||||
# section, a hash of its English and Chinese blocks outside code blocks and generated regions.
|
||||
# After editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-10-12-session-projection-registration-handles.md
|
||||
/agent-note-asynchronous-projection-registration-and-retained-readers:
|
||||
en: 693349255afbaec8
|
||||
zh: 13bcbf3f40b93b2b
|
||||
/agent-note-asynchronous-projection-registration-and-retained-readers/problem:
|
||||
en: 47ba548b869be190
|
||||
zh: c2e5038a56fe741c
|
||||
/agent-note-asynchronous-projection-registration-and-retained-readers/decision:
|
||||
en: 9e56d4bcbdc3a291
|
||||
zh: bebaaab338a15e40
|
||||
/agent-note-asynchronous-projection-registration-and-retained-readers/alternatives-considered:
|
||||
en: ee5b0cd715ed1fef
|
||||
zh: 6b1478dcde21916d
|
||||
/agent-note-asynchronous-projection-registration-and-retained-readers/consequences:
|
||||
en: 6f0e04ef7924e2c6
|
||||
zh: e3730273e323440d
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
# Agent Note: Asynchronous projection registration and retained readers
|
||||
|
||||
Status: implemented
|
||||
|
||||
English | [中文](2026-10-12-session-projection-registration-handles.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
Host readers coupled to registry key lookup lose access when a plugin unloads, although an Agent may still need projected state while its asynchronous cleanup drains. Repeated batch snapshots materialize unrelated client views. A synchronous registration API also makes every consumer assume that acquisition can never require asynchronous initialization.
|
||||
|
||||
## Decision
|
||||
|
||||
`SessionProjectionRegistry.register` returns a promise for a typed handle. Domain owners await it during activation before exposing dependent operations. Function plugins retain their namespace exports and use asynchronous `apply`; services use their initialization hook. Each domain service keeps its definitions and handles internal and exposes methods returning business data. Consumers depend on that service rather than registering its definitions or receiving projection readers. This supersedes the optional first-access acquisition and disposer-return details in [the mandatory-reader decision](2026-08-19-session-projection-mandatory-seam.md), while retaining its mandatory-state and cropped-client-view rules.
|
||||
|
||||
A handle owns one reference to a shared definition and its per-Session cells. Its disposer releases global participation exactly once; the final release removes the unit from live drives, snapshots, and checkpoints. Its reader remains usable after release, folding unseen events with the original definition. A later registration at a different state version does not replace an older handle's definition. Registration checks owner liveness after its asynchronous handoff and rejects if the owner unloaded before receiving the handle.
|
||||
|
||||
AgentRegistry owns turn and step state and exposes `turnBoundaryOf(session)`. AgentLoop acquires its inbox handle once before publishing its factory, reads the last turn before creating an Agent scope, and passes an inbox callback plus that turn number into synchronous Agent construction. The callback retains its handle through cleanup after the loop fiber removes global registrations. This preserves the existing creation/setup sequence without per-Agent registration or an extra initialization state.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
**Keep registry-level lookup and delay unregistering.** This couples unrelated plugin disposal order to every reader's final access. Retained readers make cleanup independent of that order.
|
||||
|
||||
**Return a synchronous handle.** This preserves callers' immediate reads but commits the acquisition API to synchronous initialization. Awaited acquisition leaves that timing explicit while state reads remain synchronous after readiness.
|
||||
|
||||
**Register the same units for each Agent.** The definitions already contain per-Session cells. Additional references and asynchronous Agent initialization add a creation race without providing independent data ownership.
|
||||
|
||||
**Convert function plugins to classes.** Cordis already awaits promises returned by `apply`. Preserving namespace exports avoids a published entry-point and diagnostic-name break.
|
||||
|
||||
## Consequences
|
||||
|
||||
Projection owners must migrate acquisition and retain handles; other consumers use the owning services. Released handles retain their definitions and weakly keyed cells for as long as the caller keeps them; they do not restore global wire participation. Registration-reader tests cover post-release folding, version replacement, unrelated-unit isolation, repeated disposal, concurrent owner disposal, and owner unload during acquisition. Agent lifecycle tests cover cleanup failures, scope-minting races, and background child shutdown.
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
# Agent Note: 异步投影注册与保留读取句柄
|
||||
|
||||
Status: implemented
|
||||
|
||||
[English](2026-10-12-session-projection-registration-handles.md) | 中文
|
||||
|
||||
## Problem
|
||||
|
||||
依赖注册表键查询的 Host 读取方会在插件卸载时失去访问能力,但 Agent 可能仍需在异步清理排空期间读取投影状态。重复的批量快照会具象化无关 Client 视图。同步注册 API 也使每个消费者假定获取过程永远不需要异步初始化。
|
||||
|
||||
## Decision
|
||||
|
||||
`SessionProjectionRegistry.register` 返回类型化句柄的 Promise。领域所有者在激活期间等待它,再提供依赖操作。函数插件保留命名空间导出并使用异步 `apply`;服务使用初始化钩子。领域服务在内部持有定义和句柄,通过方法返回业务数据。消费方依赖该服务,无需注册其定义或接收投影读取句柄。这取代了[必需读取方决策](2026-08-19-session-projection-mandatory-seam.zh.md)中的可选首次访问获取方式与返回释放函数的细节,同时保留必需状态和裁剪 Client 视图的规则。
|
||||
|
||||
句柄拥有共享定义及其逐 Session 单元的一次引用。其释放函数恰好一次地解除全局参与;最后一次释放会从实时推进、快照和检查点中移除单元。读取句柄在释放后仍可使用原定义折叠未读取事件。后续注册的其他状态版本不会替换旧句柄的定义。注册在异步交接后检查 owner 是否存活;如果 owner 在收到句柄前卸载,注册拒绝。
|
||||
|
||||
AgentRegistry 持有轮次和步骤状态,并提供 `turnBoundaryOf(session)`。AgentLoop 在发布工厂前只获取一次 inbox 句柄,在创建 Agent 作用域前读取最后轮次,并将 inbox 回调与该轮次编号传入同步 Agent 构造过程。loop fiber 移除全局注册后,回调仍在清理期间保留其句柄。这保留既有创建与 setup 顺序,无需逐 Agent 注册或增加初始化状态。
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
**保留注册表级查询并延迟注销。** 这使无关插件的释放顺序依赖每个读取方的最后一次访问。保留读取句柄使清理不再依赖该顺序。
|
||||
|
||||
**返回同步句柄。** 这保留消费者的立即读取,却将获取 API 固定为同步初始化。等待获取过程使时序显式,同时就绪后的状态读取仍同步。
|
||||
|
||||
**为每个 Agent 注册相同单元。** 定义已经包含逐 Session 单元。额外引用和异步 Agent 初始化只会引入创建竞态,不提供独立数据所有权。
|
||||
|
||||
**将函数插件改为类。** Cordis 已等待 `apply` 返回的 Promise。保留命名空间导出可避免发布入口与诊断名称的破坏性变更。
|
||||
|
||||
## Consequences
|
||||
|
||||
投影所有者必须迁移获取方式并保留句柄;其他消费方使用所属服务。只要调用方持有已释放句柄,其定义和弱键单元就仍存在;句柄不会恢复全局 wire 参与。注册读取测试覆盖释放后折叠、版本替换、无关单元隔离、重复释放、并发 owner 释放以及获取期间 owner 卸载。Agent 生命周期测试覆盖清理失败、作用域创建竞态与后台子 Agent 停止。
|
||||
+5
-2
@@ -12,8 +12,11 @@
|
||||
en: a6dca710f2f43b03
|
||||
zh: 49b507d106c5ec81
|
||||
/agent-note-performance-baselines-for-tool-heavy-backend-continuation/calibration-evidence:
|
||||
en: b1bdf65b8b017b68
|
||||
zh: 7e8ed6c18bc6aad1
|
||||
en: 16498006f02baebb
|
||||
zh: 7b64cb0629ee1753
|
||||
/agent-note-performance-baselines-for-tool-heavy-backend-continuation/calibration-evidence/sdk-minimal-checkpoint-calibration:
|
||||
en: f4b52851e35cec56
|
||||
zh: b419546dda8abf21
|
||||
/agent-note-performance-baselines-for-tool-heavy-backend-continuation/alternatives-considered:
|
||||
en: 3a61dff7c926eb22
|
||||
zh: 8f2633b16be78233
|
||||
|
||||
@@ -39,11 +39,11 @@ The implementation reference is the PR #3537 merge on Apple M4 Pro, macOS arm64,
|
||||
| Tool continuation | 358.953, 324.790, 318.861, 320.119, 322.896 | 324.280, 321.952, 340.409, 325.470, 324.312 | 322.896 / 324.312 | 340 | 850 |
|
||||
| Child catalog | 318.730, 309.006, 311.404, 308.565, 310.105 | 308.670, 310.030, 280.086, 303.084, 284.829 | 310.105 / 303.084 | 320 | 800 |
|
||||
|
||||
Continuation retains approximately 22.295 MiB; its source expectation is 23 MiB and its budget is 28.75 MiB. SDK time expectations use the existing [calibration helper](../../../../benchmarks/support/calibration.ts): 2× shared CI time scale and 1.25× variance headroom. Request history uses the direct hosted expectation in the [request-freeze calibration](../../../../benchmarks/agent-continuation/agent-continuation.bench.ts), without the 2× scale. Memory uses only 1.25× headroom. The scale is inherited from the existing lane's calibration, not a new Linux measurement of these cases; CI evidence remains necessary when runner characteristics change. Baseline budgets protect the measured implementation; tighter budgets belong with a measured behavior-preserving fix.
|
||||
Continuation retains approximately 22.295 MiB; its source expectation is 23 MiB and its budget is 28.75 MiB. The historical SDK time expectation used the existing [calibration helper](../../../../benchmarks/support/calibration.ts): 2× shared CI time scale and 1.25× variance headroom. Request history uses the direct hosted expectation in the [request-freeze calibration](../../../../benchmarks/agent-continuation/agent-continuation.bench.ts), without the 2× scale. Memory uses only 1.25× headroom. The scale is inherited from the existing lane's calibration, not a new Linux measurement of these cases; CI evidence remains necessary when runner characteristics change. Baseline budgets protect the measured implementation; tighter budgets belong with a measured behavior-preserving fix.
|
||||
|
||||
A separate plain-Node request-history CPU profile attributes 132.876 ms of sampled self time to deepFreeze called by buildRequest during a 211.300 ms operation. This identifies repeated traversal of already-frozen history as a focused investigation target, not a proven optimization result. Catalog first/repeat timings separately report the first and second parent-catalog observations.
|
||||
|
||||
The shipped SDK variant completes 100 turns, 200 requests, and 800 real file reads. Its five-sample smoke totals are 1,521.773, 1,463.465, 1,689.701, 1,365.485, and 1,417.106 ms (median 1,463.465 ms); a full-suite repeat reports 1,596.183, 1,784.536, 2,120.082, 1,405.365, and 1,355.894 ms (median 1,596.183 ms). Its 1,700 ms reference expectation yields a 4,250 ms CI budget. The repeat also slows the unchanged service cases, so it is validation under variable host load rather than evidence to relax their exclusive calibration. The SDK process receives an allowlisted environment and private home/workspace. A 40-second deadline starts SDK shutdown; every path awaits the same memoized close promise before the outer worker’s 60-second deadline. Profile timing includes boot, all turns, and shutdown, reported separately; no parent-process CPU or heap metric is presented as server memory. The adapter does not serialize requests for an external model provider.
|
||||
The shipped SDK variant completes 100 turns, 200 requests, and 800 real file reads. Its five-sample smoke totals are 1,521.773, 1,463.465, 1,689.701, 1,365.485, and 1,417.106 ms (median 1,463.465 ms); a full-suite repeat reports 1,596.183, 1,784.536, 2,120.082, 1,405.365, and 1,355.894 ms (median 1,596.183 ms). Its historical 1,700 ms reference expectation yielded a 4,250 ms CI budget. The repeat also slows the unchanged service cases, so it is validation under variable host load rather than evidence to relax their exclusive calibration. The SDK process receives an allowlisted environment and private home/workspace. A 40-second deadline starts SDK shutdown; every path awaits the same memoized close promise before the outer worker’s 60-second deadline. Profile timing includes boot, all turns, and shutdown, reported separately; no parent-process CPU or heap metric is presented as server memory. The adapter does not serialize requests for an external model provider.
|
||||
|
||||
The first Linux x64 CI measurement ran on `VM-7-113-ubuntu-ci-10` with Node 24.18.1 (run 34017868081, attempt 1, job 101444810498). The SDK median was 2,753.441 ms against its 4,250 ms budget, and tool-continuation retained-heap median was 22.274 MiB against 28.75 MiB. Request-history and tool-continuation time budgets failed: 785.498 ms against 550 ms and 1,077.285 ms against 850 ms, respectively. The unchanged Session-reopen open phase also failed at 31.6 ms against 30 ms. Attempt 2, job 101447076381 (run 34017868081) passed every benchmark on the same commit and unchanged budgets, but used `VM-7-113-ubuntu-ci-29` with Node 24.19.0. The gate runner suppressed successful child output, so that attempt supplies a passing verdict rather than raw medians. The changed runner and Node version prevent attributing the difference solely to contention or claiming stable repeated CI calibration; neither the budgets nor the shared scale are changed on this evidence.
|
||||
|
||||
@@ -53,6 +53,23 @@ Tool continuation also uses a 900 ms expected CI duration with 1.25× headroom (
|
||||
|
||||
Baseline request history uses a 600 ms expected CI duration with 1.25× headroom (750 ms). With unchanged implementation, standard two-CPU hosted run 34035306987, job 101492163630 reports totals of 618.598, 618.606, 582.035, 582.304, and 581.832 ms: median 582.304 ms exceeds the historical 550 ms budget. The 220 ms M4 expectation remains historical evidence. The same measured-verdict assertion accepts the recorded unrounded median under 750 ms, rejects it under 550 ms, and rejects a synthetic 900 ms regression. This calibrates the unoptimized baseline only; workload, timing, product code, and memory budgets remain unchanged.
|
||||
|
||||
### SDK-minimal checkpoint calibration
|
||||
|
||||
The SDK-minimal profile uses awaited checkpoints before steps, model requests, and top-level tools. This adds repeated durable writes to the timed path; the buffered-write reference above is historical. The accepted hosted expectation is 5,500 ms. Applying the existing 1.25 variance headroom gives 6,875 ms, rounded up to a whole second for a 7,000 ms budget. The 2× local-to-CI scale is not applied to a duration measured directly on CI. Other case budgets, shared factors, sample counts, and worker deadlines remain unchanged.
|
||||
|
||||
The following four complete attempts of CI run 39198687926 in PR #6369 use the same checkpoint-enabled code, 100 turns, 200 requests, 800 real file reads, zstd persistence, and the original 4,250 ms assertion. Each sample starts a fresh process. All runners expose two CPUs on Linux x64 with Node 24.21.0. Times are milliseconds.
|
||||
|
||||
| Attempt / job | CPU | Five raw total times | Median |
|
||||
|---|---|---|---:|
|
||||
| 1 / 117886905086 | AMD EPYC 7763 | 4009.703, 3974.780, 4073.098, 3998.816, 4065.456 | 4009.703 |
|
||||
| 2 / 117898305589 | AMD EPYC 9V74 | 4182.691, 5946.327, 5581.127, 5432.567, 4362.942 | 5432.567 |
|
||||
| 3 / 117906071823 | Intel Xeon 6973P-C | 2841.519, 2897.759, 2985.475, 2920.282, 2863.954 | 2897.759 |
|
||||
| 4 / 117922218658 | AMD EPYC 7763 | 4388.420, 4224.595, 4312.915, 4288.871, 4336.641 | 4312.915 |
|
||||
|
||||
The upper observed run median is 5,432.567 ms; the slowest single sample is 5,946.327 ms. The 5,500 ms expectation rounds above the upper median, and the final 7,000 ms ceiling leaves approximately 29% above it. Runner variation is retained in the evidence, not filtered into a fastest-run result. The later canceled attempt contributes no completed sample group.
|
||||
|
||||
This calibration accepts the measured cost of the stronger durability policy; it is not a performance improvement. Local on/off diagnosis attributes most additional persistence time to frequent file synchronization. The browser input-overlap failures in attempts 1 and 4 are separate failures and receive no exemption from this budget. Deterministic controls use the measured-verdict assertion: the recorded upper-median sample group fails the old 4,250 ms ceiling and passes 7,000 ms, while a synthetic 7,800 ms median remains rejected.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
**Repeat existing migration and first-open variants.** Rejected: those twelve cases already distinguish read-only preparation from writable publication. These cases use the current generation and begin or continue actual model work, or enumerate a corpus rather than open one Session.
|
||||
|
||||
+19
-2
@@ -39,11 +39,11 @@ SDK fixture 通过 profile patch 显式插入 `fs-local` 和 `str_replace_editor
|
||||
| 工具续聊 | 358.953, 324.790, 318.861, 320.119, 322.896 | 324.280, 321.952, 340.409, 325.470, 324.312 | 322.896 / 324.312 | 340 | 850 |
|
||||
| 子会话目录 | 318.730, 309.006, 311.404, 308.565, 310.105 | 308.670, 310.030, 280.086, 303.084, 284.829 | 310.105 / 303.084 | 320 | 800 |
|
||||
|
||||
续聊保留约 22.295 MiB;源码期望值为 23 MiB,预算为 28.75 MiB。SDK 时间期望值使用现有[校准辅助函数](../../../../benchmarks/support/calibration.ts):2× 共享 CI 时间比例和 1.25× 波动余量。请求历史使用[请求历史基准](../../../../benchmarks/agent-continuation/agent-continuation.bench.ts)中的直接托管期望值,不乘以 2× 比例。内存只使用 1.25× 余量。比例继承现有通道的校准,并非这些用例的新 Linux 实测值;runner 特征变化时仍需 CI 证据。基线预算保护实测实现;更紧预算属于有测量依据且保持行为的修复。
|
||||
续聊保留约 22.295 MiB;源码期望值为 23 MiB,预算为 28.75 MiB。历史 SDK 时间期望值使用了现有[校准辅助函数](../../../../benchmarks/support/calibration.ts):2× 共享 CI 时间比例和 1.25× 波动余量。请求历史使用[请求历史基准](../../../../benchmarks/agent-continuation/agent-continuation.bench.ts)中的直接托管期望值,不乘以 2× 比例。内存只使用 1.25× 余量。比例继承现有通道的校准,并非这些用例的新 Linux 实测值;runner 特征变化时仍需 CI 证据。基线预算保护实测实现;更紧预算属于有测量依据且保持行为的修复。
|
||||
|
||||
独立的纯 Node 请求历史 CPU profile 在一次 211.300 ms 操作中,将 132.876 ms 采样自身时间归因于 buildRequest 调用的 deepFreeze。这把重复遍历已冻结历史定位为聚焦调查目标,不是已证实的优化结果。目录首次/重复时间分别报告第一次和第二次 parent 目录观察。
|
||||
|
||||
已发布 SDK 变体完成 100 个轮次、200 次请求和 800 次真实文件读取。五样本 smoke 总时间为 1,521.773、1,463.465、1,689.701、1,365.485 和 1,417.106 ms(中位数 1,463.465 ms);完整套件重复运行报告 1,596.183、1,784.536、2,120.082、1,405.365 和 1,355.894 ms(中位数 1,596.183 ms)。1,700 ms 参考期望对应 4,250 ms CI 预算。重复运行中未改变的服务用例也变慢,因此这是可变主机负载下的验证,不是放宽其独占校准预算的依据。SDK 进程使用白名单环境和私有主目录/工作区。40 秒截止时间启动 SDK 关闭;所有路径等待同一个记忆化 close Promise,并早于外层 worker 的 60 秒截止时间。Profile 时间包含启动、全部轮次和关闭,分别报告;不把父进程 CPU 或堆指标当作服务端内存。适配器不为外部模型服务商序列化请求。
|
||||
已发布 SDK 变体完成 100 个轮次、200 次请求和 800 次真实文件读取。五样本 smoke 总时间为 1,521.773、1,463.465、1,689.701、1,365.485 和 1,417.106 ms(中位数 1,463.465 ms);完整套件重复运行报告 1,596.183、1,784.536、2,120.082、1,405.365 和 1,355.894 ms(中位数 1,596.183 ms)。历史 1,700 ms 参考期望对应当时的 4,250 ms CI 预算。重复运行中未改变的服务用例也变慢,因此这是可变主机负载下的验证,不是放宽其独占校准预算的依据。SDK 进程使用白名单环境和私有主目录/工作区。40 秒截止时间启动 SDK 关闭;所有路径等待同一个记忆化 close Promise,并早于外层 worker 的 60 秒截止时间。Profile 时间包含启动、全部轮次和关闭,分别报告;不把父进程 CPU 或堆指标当作服务端内存。适配器不为外部模型服务商序列化请求。
|
||||
|
||||
首次 Linux x64 CI 测量使用 `VM-7-113-ubuntu-ci-10` 和 Node 24.18.1(run 34017868081,attempt 1,job 101444810498)。SDK 中位数为 2,753.441 ms,预算为 4,250 ms;工具续聊保留堆中位数为 22.274 MiB,预算为 28.75 MiB。请求历史与工具续聊时间预算失败:分别为 785.498 ms 对 550 ms、1,077.285 ms 对 850 ms。未修改的 Session 重开 open 阶段也以 31.6 ms 对 30 ms 失败。Attempt 2,job 101447076381 (run 34017868081) 在同一提交和未修改预算下通过全部基准,但使用 `VM-7-113-ubuntu-ci-29` 和 Node 24.19.0。门禁运行器隐藏成功子进程的输出,因此该次运行只提供通过结论,不提供原始中位数。Runner 与 Node 版本同时变化,不能把差异仅归因于资源争用,也不能宣称已获得稳定的重复 CI 校准;这些证据不改变预算或共享比例。
|
||||
|
||||
@@ -53,6 +53,23 @@ SDK fixture 通过 profile patch 显式插入 `fs-local` 和 `str_replace_editor
|
||||
|
||||
基线请求历史使用 600 ms CI 期望时间与 1.25× 余量(750 ms)。未修改的实现在标准双 CPU 托管 run 34035306987,job 101492163630 中报告总时间为 618.598、618.606、582.035、582.304 和 581.832 ms:中位数 582.304 ms 超出历史 550 ms 预算。220 ms M4 期望保留为历史证据。与实测判定相同的断言在 750 ms 下接受未经舍入的录制中位数,在 550 ms 下拒绝它,并拒绝合成的 900 ms 回退。此校准仅针对未优化基线;负载、计时、产品代码和内存预算均不改变。
|
||||
|
||||
### SDK-minimal 检查点校准
|
||||
|
||||
SDK-minimal profile 在 step、模型请求和顶层工具执行前等待检查点。这使重复持久化写入进入计时路径;上方基于缓冲写入的参考值仅作历史记录。已接受的托管期望值为 5,500 ms,乘现有 1.25 波动余量得到 6,875 ms,再向上取整到整秒,预算为 7,000 ms。直接在 CI 测得的时间不再乘本地到 CI 的 2× 比例。其他用例预算、共享系数、样本数量和 worker 截止时间均保持不变。
|
||||
|
||||
以下是 PR #6369 中 CI run 39198687926 的四次完整尝试,使用相同的 checkpoint 启用代码、100 轮、200 次请求、800 次真实文件读取、zstd 持久化和原 4,250 ms 断言。每个样本启动新进程。所有 runner 均为 Linux x64、两个可用 CPU、Node 24.21.0。时间单位为毫秒。
|
||||
|
||||
| 尝试 / job | CPU | 五个原始总耗时 | 中位数 |
|
||||
|---|---|---|---:|
|
||||
| 1 / 117886905086 | AMD EPYC 7763 | 4009.703, 3974.780, 4073.098, 3998.816, 4065.456 | 4009.703 |
|
||||
| 2 / 117898305589 | AMD EPYC 9V74 | 4182.691, 5946.327, 5581.127, 5432.567, 4362.942 | 5432.567 |
|
||||
| 3 / 117906071823 | Intel Xeon 6973P-C | 2841.519, 2897.759, 2985.475, 2920.282, 2863.954 | 2897.759 |
|
||||
| 4 / 117922218658 | AMD EPYC 7763 | 4388.420, 4224.595, 4312.915, 4288.871, 4336.641 | 4312.915 |
|
||||
|
||||
实测轮次中位数上界为 5,432.567 ms,最慢单次样本为 5,946.327 ms。5,500 ms 期望值向上取整覆盖该中位数,最终 7,000 ms 上限比它高约 29%。Runner 差异保留在证据中,不筛选为最快运行结果。后续取消的尝试不贡献完整样本组。
|
||||
|
||||
此校准接受更强持久化策略的实测成本,不表示性能已改善。本地开关对照将额外持久化时间的大部分归因于频繁的文件同步。第 1、4 次尝试中的浏览器输入重叠失败属于独立问题,不因本预算而豁免。确定性对照与实测判定使用同一断言:记录的最高中位数样本组被旧 4,250 ms 上限拒绝,在 7,000 ms 下通过;合成的 7,800 ms 中位数仍被拒绝。
|
||||
|
||||
## 考虑过的替代方案
|
||||
|
||||
**重复现有迁移和首次打开变体。** 拒绝:现有十二个用例已经区分只读准备与可写发布。这些用例使用当前代际并开始或继续实际模型工作,或者列举语料集合而不是打开单个 Session。
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
en: 1310a89c351e5bfe
|
||||
zh: e6386ae10b893c2d
|
||||
/agent-note-performance-baselines-for-large-session-corpora/decision/measured-endpoints:
|
||||
en: b4d81575ac574f6f
|
||||
zh: f4f291ab7ce83094
|
||||
en: 11937dfa4a349a19
|
||||
zh: b91a136890fe92e8
|
||||
/agent-note-performance-baselines-for-large-session-corpora/calibration-evidence:
|
||||
en: f934c95748dcbfe0
|
||||
zh: df6a68286c21510f
|
||||
|
||||
@@ -37,6 +37,8 @@ The projection cache is part of the list endpoint: each cold row carries cached
|
||||
|
||||
Workers are compiled plain-Node processes, each started fresh against a seeded root. They compose the production Session store, projection registry, JSONL persistence, SQLite Session query with `openAt: first-search` on an in-memory index, JSON storage and projection cache, the Web projection set, AgentLoop, and the real `SessionController`. Transport lookups, upload registration, default model selection, and the Workspace registry are inert providers, because no measured path depends on them.
|
||||
|
||||
The shared corpus and projection-list controller fixture awaits controller activation and supplies the fixed attachment image policy. Its snapshots therefore include the production `imageLimits` unit; attachment I/O remains outside these workloads. Event corpora, measurement endpoints, and budgets are unchanged.
|
||||
|
||||
| Case | Corpus | Samples | Endpoint |
|
||||
|---|---|---:|---|
|
||||
| List | 3,000 | 3 | Host boot; first `session.list`; repeated `session.list` in the same process |
|
||||
|
||||
@@ -37,6 +37,8 @@ Status: implemented
|
||||
|
||||
worker 是编译后的纯 Node 进程,每次都针对已播种根目录全新启动。它们组合生产 Session store、投影注册表、JSONL 持久化、使用 `openAt: first-search` 和内存索引的 SQLite Session query、JSON 存储和投影缓存、Web 投影集合、AgentLoop 以及真实的 `SessionController`。传输查找、上传注册、默认模型选择和 Workspace 注册表使用惰性提供者,因为没有被测路径依赖它们。
|
||||
|
||||
语料与 projection-list 共用的控制器夹具等待控制器激活,并提供固定附件图片策略。因此快照包含生产 `imageLimits` 单元;附件 I/O 仍不在这些负载中。事件语料、测量端点和预算保持不变。
|
||||
|
||||
| 用例 | 语料 | 样本数 | 终点 |
|
||||
|---|---|---:|---|
|
||||
| 列表 | 3,000 | 3 | Host 启动;首次 `session.list`;同一进程中重复的 `session.list` |
|
||||
|
||||
+4
-4
@@ -21,11 +21,11 @@
|
||||
en: f07bb6fbbdd01fe4
|
||||
zh: 9313a0ef2a22af12
|
||||
/agent-note-linear-credential-matching-and-performance-gates-for-user-text/decision/performance-gate:
|
||||
en: bfa4b063696492a5
|
||||
zh: 3cee9b6087dbee91
|
||||
en: fcc52be9892b2bfc
|
||||
zh: ac259aba407efcbc
|
||||
/agent-note-linear-credential-matching-and-performance-gates-for-user-text/alternatives-considered:
|
||||
en: 7600c1671b5dc73c
|
||||
zh: b38e3ae76a5a32d1
|
||||
en: 168bda1a4e9e0325
|
||||
zh: af9cf7b804db84b7
|
||||
/agent-note-linear-credential-matching-and-performance-gates-for-user-text/consequences:
|
||||
en: 8f9d25b72d3e7660
|
||||
zh: e5c6ae0194905876
|
||||
@@ -43,7 +43,7 @@ English | [中文](2026-10-11-user-text-credential-matching.zh.md)
|
||||
| Item | Content |
|
||||
|---|---|
|
||||
| Workloads | Without credentials: ordinary messages, and long prose, code, base64, Markdown, log, JSON, and shell text. With credentials: messages each carrying one kind (model key, GitHub token, cloud key ID, JWT, address password, private-key block), and batches of `.env` keys. Adversarial: repeated input shaped against each format's prefix, length, and separator rules, at two sizes. All generated from constants; credential samples are assembled at run time |
|
||||
| Timing | Ten unmeasured pairs warm each workload with the same evaluation count as measured pairs. Five measured pairs alternate baseline-first and matcher-first order; each evaluation builds a fresh registry, document, and matcher instance and times evaluation only. A single-document sample averages twenty evaluations; a 5,000-message sample evaluates the batch once. Each side selects its minimum sample mean, with the mean process CPU time from that sample. The worker JSON in CI logs includes every measured pair and averaging count; the HTML report shows the selected with-credential mean. Registry disposal completes outside the timer before the next evaluation, preventing cleanup and retained registries from accumulating. Warm-up retains no matcher memo |
|
||||
| Timing | Ten unmeasured pairs warm each workload with the same evaluation count as measured pairs. Five measured pairs alternate baseline-first and matcher-first order; each evaluation builds a fresh registry, document, and matcher instance and times evaluation only. A single-document sample averages twenty evaluations, or forty for the half-sized input of a scaling pair, so both sizes process the same total keys or characters per sample; a 5,000-message sample evaluates the batch once. Garbage collection during evaluation remains included. Each side selects its minimum sample mean, with the mean process CPU time from that sample. The worker JSON in CI logs includes every measured pair and averaging count; the HTML report shows the selected with-credential mean. Registry disposal completes outside the timer before the next evaluation, preventing cleanup and retained registries from accumulating. Warm-up retains no matcher memo |
|
||||
| Allocation | A separate process with `--expose-gc` and a fixed semi-space larger than any evaluation; after warm-up and GC, the matcher alone answers every query once and the young-generation growth is taken; GC event timestamps confirm no GC ran inside the interval |
|
||||
| Assertions | Without credentials: absolute budgets and a cap on the with/without time ratio; with credentials: absolute budgets and the time ratio when the key count doubles; adversarial: absolute budgets and doubling ratios; allocation: a floor plus caps per credential, per line break inside masks, and per masked character |
|
||||
| Excluded | Cold code compilation, React rendering, browser paint and layout; allocation results are asserted and printed to the console, not added to the HTML report |
|
||||
@@ -54,6 +54,7 @@ The reference constants are calibrated for minima of five single evaluations. Mi
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- **Equal evaluation counts at both scaling sizes**: the larger input allocates twice as much per sample, so its fastest sample can include a collection while the smaller input's fastest sample includes none. Equal total input per sample makes the per-evaluation means compare similar allocation volumes without excluding GC or changing the doubling limit.
|
||||
- **Unwarmed, separate baseline and matcher batches**: sub-millisecond baseline measurements can reach optimized code before the matcher’s newly exercised paths do, and separate batches amplify clock-speed drift. Per-workload warm-up, adjacent pairs, and fixed batches compare warmed code while limiting noise in short intervals and preserving fresh documents and matcher state.
|
||||
- **One quantified pattern per format, run from every query**: patterns without a hit scan to the end of the text each time, so cost grows quadratically with credentials, and length quantifiers backtrack on failure.
|
||||
- **One large quantified pattern for all formats**: one scan finds the leftmost match, but branch quantifiers still backtrack on failure.
|
||||
|
||||
@@ -43,7 +43,7 @@ Status: implemented
|
||||
| 项 | 内容 |
|
||||
|---|---|
|
||||
| 负载 | 无凭据:普通消息,以及散文、代码、base64、Markdown、日志、JSON、shell 长文本。含凭据:每条消息含一种凭据(模型密钥、GitHub 令牌、云密钥 ID、JWT、地址密码、私钥块),以及成批的 `.env` 密钥。对抗:针对每种格式的前缀、长度与分隔规则构造的重复输入,两种长度。全部由常量生成,凭据样本运行时拼接 |
|
||||
| 计时 | 每个负载先用十组未计入结果的配对预热,每组的求值次数与测量配对相同。五组测量交替以基线或凭据匹配器开头;每次求值新建注册表、文档和匹配器实例,只计求值。单文档样本取二十次求值的平均值,5,000 条消息的样本则整批求值一次。两侧各取样本均值的最小值,并保留对应样本的平均进程 CPU 时间。CI 日志中的 worker JSON 包含全部测量配对和求平均值的次数,HTML 报告展示选中的带凭据匹配器均值。每次求值在计时结束后等待注册表清理完成,再开始下一次,避免清理工作和未释放注册表积累。预热不保留匹配器记忆 |
|
||||
| 计时 | 每个负载先用十组未计入结果的配对预热,每组的求值次数与测量配对相同。五组测量交替以基线或凭据匹配器开头;每次求值新建注册表、文档和匹配器实例,只计求值。单文档样本取二十次求值的平均值;翻倍比较中的小输入取四十次,使两种大小的每个样本处理相同总量的密钥或字符;5,000 条消息的样本则整批求值一次。求值期间的垃圾回收仍计入耗时。两侧各取样本均值的最小值,并保留对应样本的平均进程 CPU 时间。CI 日志中的 worker JSON 包含全部测量配对和求平均值的次数,HTML 报告展示选中的带凭据匹配器均值。每次求值在计时结束后等待注册表清理完成,再开始下一次,避免清理工作和未释放注册表积累。预热不保留匹配器记忆 |
|
||||
| 分配 | 单独进程,`--expose-gc`,semi-space 固定为大于任何一次求值的大小;预热后 GC,再单独驱动匹配器回答全部查询一遍,取新生代增量;用 GC 事件的时间戳确认区间内没有 GC |
|
||||
| 断言 | 无凭据:绝对预算,以及"有/无凭据匹配器"的耗时比上限;含凭据:绝对预算,密钥数翻倍时的耗时比;对抗:绝对预算与翻倍耗时比;分配:底噪加每个凭据、每个遮挡内换行和每个遮挡字符的上限 |
|
||||
| 排除 | 冷代码编译、React 渲染、浏览器绘制与布局;分配结果只用于断言并输出到控制台,不进 HTML 报告 |
|
||||
@@ -54,6 +54,7 @@ Status: implemented
|
||||
|
||||
## 考虑过的替代方案
|
||||
|
||||
- **两种大小使用相同求值次数**:大输入每个样本的分配量翻倍,因而其最快样本可能包含一次垃圾回收,而小输入的最快样本没有。让每个样本的输入总量相等,使单次求值均值比较相近的分配量,无需排除垃圾回收或修改翻倍上限。
|
||||
- **不预热、分批测量基线和匹配器**:亚毫秒基线测量可能先进入优化后的代码,而匹配器新触及的路径还未完成优化;分批测量也会放大时钟频率漂移。逐负载预热、相邻配对和固定批次比较预热后的代码,降低短区间测量的噪声,同时保持文档和匹配器状态全新。
|
||||
- **每次查询对每个格式各跑一个带量词的正则**:没有命中的正则每次都扫到文末,凭据多时平方增长;长度量词在失败时回溯。
|
||||
- **把全部格式合成一个带量词的大正则**:一次扫描能找到最左匹配,但各分支的量词失败时仍回溯。
|
||||
|
||||
+4
-4
@@ -15,8 +15,8 @@
|
||||
en: e6c93d89a25f0d03
|
||||
zh: 65e1f5842bc74008
|
||||
/agent-note-session-projections-and-command-lifecycle-logging/proposal/host-projection-registry-dsh-session-projection-new-package:
|
||||
en: a0f86a588c9dff31
|
||||
zh: 421803bc8b0dddb5
|
||||
en: bb52ae77e0302d2d
|
||||
zh: 75e6a76ce7d02342
|
||||
/agent-note-session-projections-and-command-lifecycle-logging/proposal/shipped-consumer-the-subagent-identity-unit:
|
||||
en: 10093535660c3cae
|
||||
zh: a7f4ff9e71ee6751
|
||||
@@ -39,8 +39,8 @@
|
||||
en: 64ad8d08a8ab44e0
|
||||
zh: fb0a0f7369d0bd23
|
||||
/agent-note-session-projections-and-command-lifecycle-logging/alternatives-considered:
|
||||
en: e945e84e8360e0c4
|
||||
zh: becdeb100a4c5b6b
|
||||
en: 4776bd01e7ec14f0
|
||||
zh: 9b1c2db96e1120f6
|
||||
/agent-note-session-projections-and-command-lifecycle-logging/acceptance-criteria:
|
||||
en: 9dfcca30fffda225
|
||||
zh: 0beddb81d32e0ca7
|
||||
|
||||
+5
-6
@@ -24,15 +24,14 @@ A state-carrying log event MUST carry the complete post-change state, never a ba
|
||||
|
||||
### Host projection registry (`dsh-session-projection`, new package)
|
||||
|
||||
A light Service Definition package: merge-extensible host-state and client-view type maps, the registry service, and zod validation for persisted state and client values. Capability-seam roles: domain host plugins provide projection units, carriers consume them, and neither knows the other.
|
||||
A light Service Definition package: local host-state definitions, a merge-extensible client-view type map, the registry service, and zod validation for persisted state and client values. Capability-seam roles: domain host plugins provide projection units, carriers consume them, and neither knows the other.
|
||||
|
||||
What a domain registers is a **state-driven computation unit** — a pure fold plus declarations and an optional client view — never an opaque getter. The framework owns driving it (subscription, watermark, caching, and later checkpointing); the domain owns only the computation. Projections serve every business domain (session title, plan, goal, permission, todos); commands are merely one trigger path and hold no special position in this contract.
|
||||
|
||||
```ts ignore-check
|
||||
export interface SessionProjectionStateMap {} // host fold states
|
||||
export interface SessionProjectionMap {} // client-visible whole values
|
||||
export interface SessionProjectionMap {} // client-visible whole values
|
||||
|
||||
export interface ProjectionDefinition<K extends keyof SessionProjectionStateMap, S> {
|
||||
export interface ProjectionDefinition<K extends string, S> {
|
||||
key: K
|
||||
stateSchema: ZodType<S>
|
||||
persist?: boolean // host-only units opt in; client-visible units always persist
|
||||
@@ -54,7 +53,7 @@ declare module 'cordis' {
|
||||
}
|
||||
```
|
||||
|
||||
- `SessionProjectionStateMap` types host fold states; `SessionProjectionMap` remains the one client DTO table shared by the wire block and React hook via `import type`. A unit may remain host-only by omitting `wire`. How a client value is *rendered* is the slot system's business, never the projection layer's. The state/view split is specified by the [implemented state and client-view note](../../archived/architecture/2026-08-19-session-projection-state-and-client-views.md).
|
||||
- `ProjectionDefinition<K, S>` carries each unit's host state type; `SessionProjectionMap` remains the one client DTO table shared by the wire block and React hook via `import type`. A unit may remain host-only by omitting `wire`. How a client value is *rendered* is the slot system's business, never the projection layer's. The state/view split is specified by the [implemented state and client-view note](../../archived/architecture/2026-08-19-session-projection-state-and-client-views.md).
|
||||
- **The host is the only place a projection is computed.** The framework drives every registered unit forward eagerly: each committed session event passes through `apply`; a unit uninterested in an event returns the same state reference, and an unchanged reference (`Object.is`) produces no downstream work. Clients never fold domain events — they receive finished values (baseline block + push frame below). This removes the double-implementation trap (plan's two-event fold written once, on the host) and any client-side domain code.
|
||||
- **State is always computed, never logged.** The log holds events only; the unit's state lives in the framework's per-session watermark cache (`{state, observedSeq}` per unit) and, in a later phase, in a **persisted projection cache** on the domain-KV storage seam: rows of `(sessionId, key, ver, seq, val)` (`ver` = the unit's `stateVersion`, `seq` = the watermark, `val` = the state JSON). A row is never wrong, only possibly stale — its `seq` says exactly how stale. The one read recipe, cold and live alike: take the cached state (or `init()`), forward-apply only the events past its watermark, `view` the result. Cold listings (every session's title across all workspaces) become an index read plus, at worst, a short tail replay; the session-persistence seam grows a read-from-seq primitive for that tail in the same later phase. Write policy: throttled (count/interval, configurable) plus two mandatory points — `turn/end` and detach (the live-to-cold moment). A crash between writes costs a longer tail replay, never a wrong value.
|
||||
- A domain's input event set is its own choice: todos folds `todo/write` alone; plan folds `plan/mode` plus its own `/plan` `command/run` records (see the plan section); goal folds `goal/change` metadata; session title folds its title events (retiring the bespoke `session/title` frame and the client's title-snapshot map — the fourth hand-rolled projection this seam absorbs).
|
||||
@@ -161,7 +160,7 @@ Infrastructure first; the three in-flight PRs are left untouched and re-target a
|
||||
|
||||
**Hanging the registry off `ctx.apiProxy`** — rejected: session projections are not web-specific (TUI, ACP, headless are future consumers), and domain packages must not depend on the apiproxy package. The independent seam also deletes #587's type-only import edge from api-proxy into the plan package.
|
||||
|
||||
**A second client DTO table** — rejected: `SessionProjectionMap` remains the single client vocabulary shared by wire and UI. `SessionProjectionStateMap` is not another client view table; it types host fold state so internal state may differ from the value sent to clients.
|
||||
**A second client DTO table** — rejected: `SessionProjectionMap` remains the single client vocabulary shared by wire and UI. Each unit types its host fold state locally, independently of the value sent to clients.
|
||||
|
||||
**Event-broadcast collection instead of a registry walk** — rejected: async listeners cannot yield the single synchronous cut that makes `asOfSeq` one consistent snapshot across all keys; registries are this repo's shape for contributions (`ctx.tools`, prompt sections, slots).
|
||||
|
||||
|
||||
+5
-6
@@ -24,15 +24,14 @@ Status: proposed
|
||||
|
||||
### host 侧投影注册表(`dsh-session-projection`,新包)
|
||||
|
||||
一个轻量的 Service Definition 包:merge-extensible 的 host 状态与客户端视图类型表、注册表服务,以及针对持久状态和客户端值的 zod 校验。能力 seam 的角色如下:领域 host 插件提供投影单元,载体消费这些单元,两侧互不相识。
|
||||
一个轻量的 Service Definition 包:局部 host 状态定义、merge-extensible 的客户端视图类型表、注册表服务,以及针对持久状态和客户端值的 zod 校验。能力 seam 的角色如下:领域 host 插件提供投影单元,载体消费这些单元,两侧互不相识。
|
||||
|
||||
领域注册的是一个**状态驱动计算单元(state-driven computation unit)**——纯折叠、若干声明及可选客户端视图——绝不是一个不透明的 getter。驱动它是框架的职责(订阅、水位线(watermark)、缓存,以及后续的检查点机制),领域只负责计算。投影服务于所有业务领域(会话标题、plan、goal、权限、todos);命令只是其中一条触发路径,在本约定中没有任何特殊地位。
|
||||
|
||||
```ts ignore-check
|
||||
export interface SessionProjectionStateMap {} // host fold states
|
||||
export interface SessionProjectionMap {} // client-visible whole values
|
||||
export interface SessionProjectionMap {} // client-visible whole values
|
||||
|
||||
export interface ProjectionDefinition<K extends keyof SessionProjectionStateMap, S> {
|
||||
export interface ProjectionDefinition<K extends string, S> {
|
||||
key: K
|
||||
stateSchema: ZodType<S>
|
||||
persist?: boolean // host-only units opt in; client-visible units always persist
|
||||
@@ -54,7 +53,7 @@ declare module 'cordis' {
|
||||
}
|
||||
```
|
||||
|
||||
- `SessionProjectionStateMap` 描述 host 折叠状态;`SessionProjectionMap` 继续作为协议块和 React 钩子经 `import type` 共享的唯一客户端 DTO 表。单元省略 `wire` 即保持 host-only。客户端值如何*渲染*是 slot 体系的事,永远不归投影层管。状态/视图拆分见[已实现的状态与客户端视图记录](../../archived/architecture/2026-08-19-session-projection-state-and-client-views.md)。
|
||||
- `ProjectionDefinition<K, S>` 携带各单元的宿主状态类型;`SessionProjectionMap` 继续作为协议块和 React 钩子经 `import type` 共享的唯一客户端 DTO 表。单元省略 `wire` 即保持 host-only。客户端值如何*渲染*是 slot 体系的事,永远不归投影层管。状态/视图拆分见[已实现的状态与客户端视图记录](../../archived/architecture/2026-08-19-session-projection-state-and-client-views.md)。
|
||||
- **host 是投影唯一的计算地点。** 框架主动驱动(eager drive)每个已注册的单元:每个已提交的会话事件都经过 `apply`;对某事件不感兴趣的单元返回同一个状态引用,而引用未变(`Object.is`)就不产生任何下游工作。客户端从不折叠领域事件——它们收到的是成品值(基线块 + 下文的推送帧)。这消除了双重实现陷阱(plan 的双事件折叠只在 host 写一遍),也消除了一切客户端侧领域代码。
|
||||
- **状态永远靠计算得出,绝不入日志。** 日志只存事件;单元的状态住在框架的按会话水位线缓存里(每单元一份 `{state, observedSeq}`),并在后续阶段进入 domain-KV 存储 seam 上的**持久投影缓存(persisted projection cache)**:形如 `(sessionId, key, ver, seq, val)` 的行(`ver` = 单元的 `stateVersion`,`seq` = 水位线,`val` = 状态 JSON)。一行永远不会是错的,至多是陈旧的——其 `seq` 精确说明陈旧到哪。冷读与活读共用同一套读取配方:取缓存状态(或 `init()`),只对超出其水位线的事件做正向 `apply`,再对结果做 `view`。冷列表(跨全部 workspace 列出每个会话的标题)变成一次索引读,至多外加一小段尾部回放;session-persistence seam 在同一后续阶段为这段尾部补一个按 seq 起读的原语。写入策略:节流(次数/间隔,可配置)外加两个强制点——`turn/end` 与 detach(由活转冷的时刻)。两次写入之间崩溃的代价是尾部回放更长一些,绝不会是值出错。
|
||||
- 领域的输入事件集由领域自己选择:todos 只折叠 `todo/write`;plan 折叠 `plan/mode` 外加它自己的 `/plan` `command/run` 记录(见 plan 一节);goal 折叠 `goal/change` 元数据;会话标题折叠其标题事件(顺带下线专设的 `session/title` 帧与客户端的标题快照表——这是该 seam 收编的第四个手工投影)。
|
||||
@@ -161,7 +160,7 @@ host 侧命令执行器(`packages/interaction/commands`)在调用处理器
|
||||
|
||||
**把注册表挂到 `ctx.apiProxy` 名下**——不予采纳:会话投影并非 web 专属(TUI、ACP(Agent Client Protocol)、headless 都是未来消费方),且领域包不得依赖 apiproxy 包。独立 seam 还顺带删掉了 #587 从 api-proxy 指向 plan 包的 type-only 导入边。
|
||||
|
||||
**第二张客户端 DTO 类型表**——不予采纳:`SessionProjectionMap` 仍是协议与 UI 共享的唯一客户端词汇。`SessionProjectionStateMap` 不是另一张客户端视图表;它描述 host 折叠状态,使内部状态可以不同于发往客户端的值。
|
||||
**第二张客户端 DTO 类型表**——不予采纳:`SessionProjectionMap` 仍是协议与 UI 共享的唯一客户端词汇。各单元在内部声明宿主折叠状态类型,与发往客户端的值相互独立。
|
||||
|
||||
**用事件广播收集、替代注册表遍历**——不予采纳:异步监听器给不出那个单一的同步切面,而正是它让 `asOfSeq` 成为横跨所有 key 的一致快照;注册表才是本仓库承接贡献的通行形状(`ctx.tools`、提示词片段、slot)。
|
||||
|
||||
|
||||
@@ -130,7 +130,7 @@ Real-API tests/demos read `DEEPSEEK_API_KEY`, optional `DEEPSEEK_BASE_URL`, and
|
||||
|
||||
- Packages use `@deepseek-ai/dsh-<name>`; vendor is [rescoped](docs/rescope.md) and `private: true`. Harness packages declare `@deepseek-ai/cordis` in `peerDependencies`/`devDependencies`. Workspace dependency sections use DSH `workspace:*`, vendor/native `workspace:~` ([rules](.agents/notes/implemented/process/2026-08-10-npm-release-sequences.md)).
|
||||
- ESM everywhere (`"type": "module"`). Use package names across packages and `.ts` in local relative imports. Config subprocesses run built `lib/` under plain Node; source regressions use their declared launcher ([testing policy](docs/testing.md#test-subprocess-launch-modes)). The `dsh` CLI source launch runs through tsx's ESM-only hook (`node --import tsx/esm`); modules it reaches must stay ESM (no CJS-only exports) ([source-launch contract](.agents/notes/implemented/architecture/2026-07-29-dsh-source-launch-tsx-esm.md)). Raw/Web `cordis.yml` bare plugins must appear in their resolver manifest's `dependencies`; `verify-cordis-config` enforces it.
|
||||
- **Registrations are effects**: every contribution goes through `ctx.effect()` / `ctx.on()`; a registry's `register()` returns the disposer.
|
||||
- **Registrations are effects**: every contribution goes through `ctx.effect()` / `ctx.on()`; a registry's `register()` returns the disposer, directly or through its documented registration handle.
|
||||
- **Typed events use declaration merging** and merge-extensible maps. Event JSDoc needs `@mode` and payload `@param`. Public service methods document parameters and non-void returns. `SessionEventMap` members are required-on-read unless the envelope carries `ignorable: true`. Bump `SESSION_FORMAT_VERSION` only when existing discriminators cannot prevent unsafe interpretation; same-version changes require safe reader behavior ([review](.agents/notes/implemented/process/2026-10-08-session-reader-compatibility-review.md)).
|
||||
- **Switch on discriminant tags.** Closed unions end in `assertNever`; merge-extensible unions fall through a documented default.
|
||||
- **Waterfall listeners MUST call `next()`** to delegate; returning without it short-circuits the chain ([semantics](docs/cordis-primer.md#cordis-waterfall-semantics)).
|
||||
|
||||
@@ -1276,6 +1276,7 @@ describe.skipIf(!existsSync(dshBin))('dsh BUILT bin (node lib/bin.js, no tsx)',
|
||||
['persistent-bash', '@deepseek-ai/dsh-tool-bash-persistent'],
|
||||
['persistent-pwsh', '@deepseek-ai/dsh-tool-pwsh-persistent'],
|
||||
['sessions', '@deepseek-ai/dsh-session-persistence-jsonl'],
|
||||
['session-checkpoint-policy', '@deepseek-ai/dsh-session-checkpoint-policy'],
|
||||
])
|
||||
expect(stdout).toContain('# == @deepseek-ai/dsh-sdk-minimal')
|
||||
expect(stdout).not.toContain('@deepseek-ai/dsh-base')
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/** The shipped minimal profile must stop model dispatch when a durable checkpoint fails. */
|
||||
import { createServer } from 'node:http'
|
||||
import { existsSync } from 'node:fs'
|
||||
import { mkdtemp, rm, writeFile } from 'node:fs/promises'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { DeepSeekHarness } from '@deepseek-ai/dsh-sdk-client'
|
||||
import { describe, expect, it, onTestFinished } from 'vitest'
|
||||
|
||||
const dshBin = fileURLToPath(new URL('../../../lib/bin.js', import.meta.url))
|
||||
const fixture = fileURLToPath(new URL('./fixtures/checkpoint-failure.mjs', import.meta.url))
|
||||
|
||||
describe.skipIf(!existsSync(dshBin))('sdk-minimal durability checkpoints', () => {
|
||||
it.each([false, true])('checkpoint storage failure with policy disabled=%s', async (disabled) => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'dsh-sdk-checkpoint-'))
|
||||
onTestFinished(async () => { await rm(root, { recursive: true, force: true }) })
|
||||
let modelRequests = 0
|
||||
const server = createServer((request, response) => {
|
||||
request.resume()
|
||||
request.on('end', () => {
|
||||
modelRequests += 1
|
||||
const events = [
|
||||
{ type: 'message_start', message: { id: 'checkpoint-response', model: 'deepseek-v4-flash', usage: { input_tokens: 3, output_tokens: 0 } } },
|
||||
{ type: 'content_block_start', index: 0, content_block: { type: 'text', text: 'OK' } },
|
||||
{ type: 'content_block_stop', index: 0 },
|
||||
{ type: 'message_delta', delta: { stop_reason: 'end_turn' }, usage: { output_tokens: 1 } },
|
||||
{ type: 'message_stop' },
|
||||
]
|
||||
response.writeHead(200, { 'content-type': 'text/event-stream' })
|
||||
response.end(events.map(event => `event: ${event.type}\ndata: ${JSON.stringify(event)}\n\n`).join(''))
|
||||
})
|
||||
})
|
||||
onTestFinished(async () => {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
server.close((error) => {
|
||||
if (error) reject(error)
|
||||
else resolve()
|
||||
})
|
||||
})
|
||||
})
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
server.once('error', reject)
|
||||
server.listen(0, '127.0.0.1', resolve)
|
||||
})
|
||||
const address = server.address()
|
||||
if (address === null || typeof address === 'string') throw new Error('model server has no TCP address')
|
||||
const patch = join(root, 'checkpoint.patch.yml')
|
||||
await writeFile(patch, JSON.stringify([
|
||||
{ id: 'session-log-deepseek', config: { enabled: false } },
|
||||
{ insert: [{ id: 'checkpoint-failure', name: fixture }] },
|
||||
...disabled ? [{ id: 'session-checkpoint-policy', disabled: true }] : [],
|
||||
]))
|
||||
const harness = new DeepSeekHarness({
|
||||
dshBin, profile: 'sdk-minimal', dshHome: join(root, 'home'),
|
||||
cwd: root, processCwd: root, patches: [patch],
|
||||
env: { ...process.env, DEEPSEEK_API_KEY: 'keyless-checkpoint-test', DEEPSEEK_BASE_URL: `http://127.0.0.1:${address.port}` },
|
||||
})
|
||||
onTestFinished(async () => { await harness.close() })
|
||||
const result = await harness.run('Reply OK without calling tools.')
|
||||
expect(modelRequests).toBe(disabled ? 1 : 0)
|
||||
const terminal = result.events.findLast(event => event.type === 'turn/end')
|
||||
expect(terminal).toMatchObject({ data: { reason: { kind: disabled ? 'completed' : 'error' } } })
|
||||
if (disabled) {
|
||||
expect(result.finalResponse).toBe('OK')
|
||||
} else {
|
||||
expect(terminal).toMatchObject({
|
||||
data: { reason: { kind: 'error', error: { message: 'sdk checkpoint persistence unavailable' } } },
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,13 @@
|
||||
/** Reject durable checkpoints without changing background JSONL writes. */
|
||||
export const name = 'sdk-checkpoint-failure'
|
||||
export const inject = ['sessionPersistence']
|
||||
|
||||
/**
|
||||
* Install a storage-failure signal at the public session flush event.
|
||||
* @param ctx - Plugin context owning the flush listener.
|
||||
*/
|
||||
export function apply(ctx) {
|
||||
ctx.on('session/flush', () => {
|
||||
throw new Error('sdk checkpoint persistence unavailable')
|
||||
})
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import { agentPresetProjectionDefinition } from '@deepseek-ai/dsh-agent-preset-registry'
|
||||
import { subagentModelSelectionProjectionDefinition } from '@deepseek-ai/dsh-tool-subagent/src/model-selection-state.ts'
|
||||
import { createRequire } from 'node:module'
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { mkdir, mkdtemp, readFile, realpath, rm, symlink, writeFile } from 'node:fs/promises'
|
||||
@@ -327,13 +329,14 @@ describe('the shipped Web composition', () => {
|
||||
sessionId: SessionId('preset-model-selection-enabled'),
|
||||
setup: agentCtx => ctx.agentPresets.mount(agentCtx, 'standard').then(() => undefined),
|
||||
})
|
||||
const policy = await ctx.sessionProjections.register(subagentModelSelectionProjectionDefinition)
|
||||
try {
|
||||
expect(toolNames(ctx, disabled.agent)).toContain('list_subagent_models')
|
||||
expect(toolParameterNames(ctx, disabled.agent, 'subagent')).toEqual(expect.arrayContaining([
|
||||
'model', 'provider', 'reasoning_effort',
|
||||
]))
|
||||
expect(ctx.sessionProjections.stateOf(disabled.agent.session, 'subagentModelSelectionPolicy')).toBeNull()
|
||||
expect(ctx.sessionProjections.stateOf(enabled.agent.session, 'subagentModelSelectionPolicy')).toEqual([
|
||||
expect(policy.stateOf(disabled.agent.session)).toBeNull()
|
||||
expect(policy.stateOf(enabled.agent.session)).toEqual([
|
||||
{ provider: 'deepseek-official', model: 'deepseek-v4-flash' },
|
||||
])
|
||||
expect(toolNames(ctx, enabled.agent)).toContain('list_subagent_models')
|
||||
@@ -344,6 +347,7 @@ describe('the shipped Web composition', () => {
|
||||
expect(toolParameterNames(ctx, agent, 'subagent_fork')).toEqual(['cwd', 'description', 'prompt'])
|
||||
}
|
||||
} finally {
|
||||
await policy.dispose()
|
||||
await ctx.settings.update(SUBAGENT_MODEL_SELECTION_SETTINGS_NAMESPACE, { enabled: false })
|
||||
await enabled.dispose()
|
||||
await disabled.dispose()
|
||||
@@ -879,7 +883,7 @@ describe('a switch survives the session', () => {
|
||||
|
||||
// The header keeps the creation fact; the log carries what it runs.
|
||||
expect(handle.agent.session.header.agentPreset).toBe('standard')
|
||||
expect(ctx.sessionProjections.stateOf(handle.agent.session, 'agentPreset')).toBe('minimal')
|
||||
expect(ctx.sessionProjections.checkpoint(handle.agent.session).agentPreset?.val).toBe('minimal')
|
||||
} finally {
|
||||
await handle.dispose()
|
||||
}
|
||||
@@ -894,7 +898,8 @@ describe('a forked session', () => {
|
||||
meta: { agentPreset: 'minimal' },
|
||||
setup: agentCtx => ctx.agentPresets.mount(agentCtx, 'minimal').then(() => undefined),
|
||||
})
|
||||
const inherited = ctx.sessionProjections.stateOf(parent.agent.session, 'agentPreset') ?? undefined
|
||||
const projection = await ctx.sessionProjections.register(agentPresetProjectionDefinition)
|
||||
const inherited = projection.stateOf(parent.agent.session) ?? undefined
|
||||
const child = await ctx.agents.create({
|
||||
sessionId: SessionId('preset-fork-child'),
|
||||
seed: [],
|
||||
|
||||
@@ -466,7 +466,7 @@ it.each([false, true])('starts Creator from Plugins with Coding Tools=%s without
|
||||
expect(await composer.innerText()).toBe(draft)
|
||||
expect(await picker.getAttribute('aria-haspopup')).toBe('menu')
|
||||
expect(await page.getByRole('dialog', { name: 'Add plugin', exact: true }).count()).toBe(0)
|
||||
expect(scaffold.ctx.sessionProjections.stateOf(creator.session, 'agentPreset')).toBe('cordis')
|
||||
expect(scaffold.ctx.sessionProjections.checkpoint(creator.session).agentPreset?.val).toBe('cordis')
|
||||
expect(scaffold.ctx.agentPresets.composedPreset(creator.ctx)).toBe('cordis')
|
||||
expect(scaffold.ctx.tools.schemas(creator).map(tool => tool.name))
|
||||
.toEqual(expect.arrayContaining(['cordis_inspect_list', 'cordis_inspect_query', 'plugin_manager']))
|
||||
@@ -492,9 +492,9 @@ it.each([false, true])('starts Creator from Plugins with Coding Tools=%s without
|
||||
const next = scaffold.ctx.agents.list().find(agent => agent.id !== creator.id)!
|
||||
expect(next.id).not.toBe(creator.id)
|
||||
expect(next.session.header.agentPreset).toBe('standard')
|
||||
expect(scaffold.ctx.sessionProjections.stateOf(next.session, 'agentPreset')).toBe('standard')
|
||||
expect(scaffold.ctx.sessionProjections.checkpoint(next.session).agentPreset?.val).toBe('standard')
|
||||
expect(scaffold.ctx.agentPresets.composedPreset(next.ctx)).toBe('standard')
|
||||
expect(scaffold.ctx.sessionProjections.stateOf(creator.session, 'agentPreset')).toBe('cordis')
|
||||
expect(scaffold.ctx.sessionProjections.checkpoint(creator.session).agentPreset?.val).toBe('cordis')
|
||||
await page.getByRole('button', { name: 'Standard mode', exact: true }).waitFor()
|
||||
expect(scaffold.ctx.settings.describe().find(row => row.ns === 'ui-settings')?.value).toMatchObject({ enabled: developerTools })
|
||||
expect(scaffold.ctx.agentPresets.defaultId).toBe('standard')
|
||||
|
||||
@@ -69,10 +69,11 @@ describe('web e2e: blank Session reuse under the requested preset', () => {
|
||||
function liveComposition(sessionId: SessionId) {
|
||||
const agent = scaffold.ctx.agents.get(sessionId)
|
||||
if (agent === undefined) return undefined
|
||||
const projections = scaffold.ctx.sessionProjections.snapshot(agent.session).values
|
||||
return {
|
||||
mounted: scaffold.ctx.agentPresets.composedPreset(agent.ctx),
|
||||
projected: scaffold.ctx.sessionProjections.stateOf(agent.session, 'agentPreset'),
|
||||
plan: scaffold.ctx.sessionProjections.stateOf(agent.session, 'plan'),
|
||||
projected: projections.agentPreset,
|
||||
plan: projections.plan,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ describe.each([false, true])('web e2e: selected blank writer held: %s', (held) =
|
||||
if (!held) {
|
||||
expect(selectedId).toBe(HELD)
|
||||
const reclaimed = scaffold.ctx.agents.get(HELD)!
|
||||
expect(scaffold.ctx.sessionProjections.stateOf(reclaimed.session, 'plan')).toMatchObject({ active: true })
|
||||
expect(scaffold.ctx.sessionProjections.checkpoint(reclaimed.session).plan?.val).toMatchObject({ active: true })
|
||||
await expect(scaffold.ctx.sessionPersistence.open(HELD, 'write'))
|
||||
.rejects.toMatchObject({ name: 'SessionAlreadyOwnedError' })
|
||||
}
|
||||
|
||||
@@ -593,7 +593,7 @@ describe('web e2e: long Chat scroll contract', () => {
|
||||
const artifactRoot = fileURLToPath(new URL('../../../.artifacts/screenshots/visible-fold/', import.meta.url))
|
||||
await mkdir(artifactRoot, { recursive: true })
|
||||
const evidence = await mkdtemp(join(artifactRoot, `${scenario.name}-`))
|
||||
const controlledClock = scenario.name === 'content-growth-during-fold' || scenario.name === 'cancel-all-during-fold'
|
||||
const controlledClock = !scenario.reduced
|
||||
await withScrollWorld({
|
||||
failureShot: `visible-fold-${scenario.name}`,
|
||||
controlledClock,
|
||||
@@ -760,17 +760,19 @@ describe('web e2e: long Chat scroll contract', () => {
|
||||
try {
|
||||
settled = world.scaffold.whenTurnSettled(30_000)
|
||||
if (controlledClock) {
|
||||
// Advance every animation frame so the race cannot miss the intermediate fold height.
|
||||
// Sample each animation frame, including when the CI browser misses real-time frames.
|
||||
await page.clock.pauseAt(await page.evaluate(() => Date.now() + 1000))
|
||||
try {
|
||||
await page.getByRole('button', { name: 'Send message', exact: true }).evaluate((button) => {
|
||||
if (!(button instanceof HTMLButtonElement)) throw new Error('Send control is not a button')
|
||||
button.click()
|
||||
})
|
||||
await expect.poll(async () => {
|
||||
await page.clock.runFor(16)
|
||||
return page.locator('[data-fold-race-applied]').count()
|
||||
}, { timeout: 10_000 }).toBe(1)
|
||||
if (scenario.name === 'content-growth-during-fold' || scenario.name === 'cancel-all-during-fold') {
|
||||
await expect.poll(async () => {
|
||||
await page.clock.runFor(16)
|
||||
return page.locator('[data-fold-race-applied]').count()
|
||||
}, { timeout: 10_000 }).toBe(1)
|
||||
}
|
||||
await expect.poll(async () => {
|
||||
await page.clock.runFor(16)
|
||||
return page.locator('[data-chat-motion="collapse"]').count()
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
- button "Close"
|
||||
- paragraph: “ARCHIVE_ACTIVE run the long command” still has work in progress. Archiving stops it first; you can restore the session later from the “All conversations (show archived)” filter in the sidebar, and the stopped work will not resume on its own.
|
||||
- list "Work that will be stopped":
|
||||
- listitem: The turn in progress
|
||||
- listitem: "1 background job: node -e \"const fs=require('node:fs'); fs.writeFileSync('started.txt', 'started'); setInterval(() => { fs.appendFileSync('heartbeat.txt', '.') }, 200); setTimeout(() => { fs.writeFileSync('finished.txt', 'finished') }, 60000)\""
|
||||
- listitem: The turn in progress
|
||||
- button "Cancel"
|
||||
- button "Stop and archive"
|
||||
@@ -87,6 +87,13 @@ describe('web e2e: plan review takeover round trip', () => {
|
||||
// waitFor is race-free.
|
||||
const card = page.locator('[data-plan-review-key]')
|
||||
await card.waitFor({ timeout: MODE === 'record' ? 120_000 : 30_000 })
|
||||
const firstHeader = sessionEvents.find(event => event.type === 'request/header')
|
||||
const firstPrompt = sessionEvents.find(event => event.type === 'system/message')
|
||||
expect(firstHeader?.data.header.tools?.map(tool => tool.name)).toContain('exit_plan_mode')
|
||||
expect(firstPrompt?.data.message.content.filter(block => block.type === 'text').map(block => block.text).join('\n'))
|
||||
.toContain('You are in plan mode.')
|
||||
const clock = sessionEvents.find(event => event.type === 'user/message' && event.data.source.kind === 'time-context')
|
||||
expect(clock?.seq).toBeLessThan(firstHeader!.seq)
|
||||
// The plan-review request must NOT land on the generic question flow.
|
||||
expect(await page.locator('[data-question-key]').count()).toBe(0)
|
||||
await expect.poll(() => card.getByText('Plan review').count(), { timeout: 10_000 }).toBeGreaterThan(0)
|
||||
|
||||
@@ -162,7 +162,7 @@ describe.skipIf(MODE === 'record')('web e2e: archiving a running Session stops i
|
||||
// A foreground Bash command is a registered job from its start, so the
|
||||
// Host reports it beside the turn; stopping the turn kills it.
|
||||
await expect.poll(() => dialog.getByRole('list', { name: 'Work that will be stopped' }).locator('li').allTextContents())
|
||||
.toEqual(['The turn in progress', `1 background job: ${WAIT_COMMAND}`])
|
||||
.toEqual([`1 background job: ${WAIT_COMMAND}`, 'The turn in progress'])
|
||||
// Asking is not archiving: the Host set is untouched, the turn and its
|
||||
// Bash child keep running, and the row stays in the sidebar.
|
||||
expect([...scaffold.ctx.workspaceRegistry.archivedSessionIds]).toEqual([])
|
||||
|
||||
@@ -43,8 +43,8 @@ async function remote<T>(
|
||||
}
|
||||
|
||||
/** POST one generated Session Remote unary through the API Gateway carrier. */
|
||||
function sessionRemote<T>(scaffold: WebScaffold, method: string, request: unknown): Promise<RpcResult<T>> {
|
||||
return remote<T>(scaffold, `session/${method}`, { request })
|
||||
async function sessionRemote<T>(scaffold: WebScaffold, method: string, request: unknown): Promise<RpcResult<T>> {
|
||||
return await remote<T>(scaffold, `session/${method}`, { request })
|
||||
}
|
||||
|
||||
/** Poll a synchronous condition (hook-safe; expect.poll is test-body only). */
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
en: 66a769ce60d49ff3
|
||||
zh: 7e0bfa7c598157fa
|
||||
/backend-continuation-benchmarks/run:
|
||||
en: 900cdb7cdb11cf21
|
||||
zh: 0dafdd9a3be1b04e
|
||||
en: a9a943044795029c
|
||||
zh: 45c8257f51ec658a
|
||||
/backend-continuation-benchmarks/measurements:
|
||||
en: 84301730c56f0614
|
||||
zh: 6e90a8d6574fa896
|
||||
|
||||
@@ -18,7 +18,7 @@ Measure long-history request processing, cold tool-heavy continuation, and repea
|
||||
|
||||
From the repository root, build the libraries and workers with `pnpm run build:bench`, then run `pnpm exec vitest run --config vitest.bench.config.ts benchmarks/agent-continuation/agent-continuation.bench.ts`. Do not overlap timing runs with builds or other benchmarks.
|
||||
|
||||
The test reports all five fresh-process samples, CPU models, available parallelism, platform/architecture, and Node/V8 versions, and enforces reviewed median budgets. Catalog and tool continuation each use a 900 ms standard hosted CI expectation with 1.25× headroom (1,125 ms); request history uses a separately reviewed 297 ms hosted limit ([archived evidence](../../.agents/notes/archived/simplification/2026-09-06-agent-request-freeze-evidence.md)), and SDK continuation uses reference-machine scaling. A failed worker reports its exit, signal, timeout, and stderr; temporary roots are removed even on failure. The required benchmark lane discovers this file automatically.
|
||||
The test reports all five fresh-process samples, CPU models, available parallelism, platform/architecture, and Node/V8 versions, and enforces reviewed median budgets. Catalog and tool continuation each use a 900 ms standard hosted CI expectation with 1.25× headroom (1,125 ms); request history uses a separately reviewed 297 ms hosted limit ([archived evidence](../../.agents/notes/archived/simplification/2026-09-06-agent-request-freeze-evidence.md)), and checkpoint-enabled SDK-minimal continuation uses a 5,500 ms hosted expectation with 1.25× headroom rounded up to a whole second (7,000 ms). This hosted expectation does not use the 2× reference-machine scale. A failed worker reports its exit, signal, timeout, and stderr; temporary roots are removed even on failure. The required benchmark lane discovers this file automatically.
|
||||
|
||||
<a id="measurements"></a>
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
在仓库根目录使用 `pnpm run build:bench` 构建库和 worker,然后运行 `pnpm exec vitest run --config vitest.bench.config.ts benchmarks/agent-continuation/agent-continuation.bench.ts`。不要让计时运行与构建或其他基准重叠。
|
||||
|
||||
测试报告全部五个新进程样本、CPU 型号、可用并行度、平台/架构和 Node/V8 版本,并约束经审查的中位数预算。目录和工具续聊用例均使用标准托管 CI 的 900 ms 期望值与 1.25× 余量(1,125 ms);请求历史使用单独审查的 297 ms 托管上限([已归档的证据](../../.agents/notes/archived/simplification/2026-09-06-agent-request-freeze-evidence.md)),SDK 续聊使用参考机器缩放。worker 失败时报告退出状态、信号、超时和 stderr;失败时也会删除临时根目录。必需基准通道自动发现此文件。
|
||||
测试报告全部五个新进程样本、CPU 型号、可用并行度、平台/架构和 Node/V8 版本,并约束经审查的中位数预算。目录和工具续聊用例均使用标准托管 CI 的 900 ms 期望值与 1.25× 余量(1,125 ms);请求历史使用单独审查的 297 ms 托管上限([已归档的证据](../../.agents/notes/archived/simplification/2026-09-06-agent-request-freeze-evidence.md)),启用 checkpoint 的 SDK-minimal 续聊使用 5,500 ms 托管期望值,乘 1.25× 余量后向上取整到整秒(7,000 ms)。该托管期望值不使用参考机器的 2× 比例。worker 失败时报告退出状态、信号、超时和 stderr;失败时也会删除临时根目录。必需基准通道自动发现此文件。
|
||||
|
||||
<a id="measurements"></a>
|
||||
|
||||
|
||||
@@ -14,8 +14,12 @@ import { WORKLOAD } from './workload.ts'
|
||||
|
||||
const ATTEMPTS = 5
|
||||
const WORKER_TIMEOUT_MS = 60_000
|
||||
/** M4 Pro / Node 24.19 baseline expectations, before shared CI scaling and variance headroom. */
|
||||
const EXPECTED_MS = { 'profile-continuation': 1_700 } as const
|
||||
/** Checkpoint-enabled two-CPU hosted runs have an upper observed median of 5,432.567 ms. */
|
||||
const EXPECTED_PROFILE_CONTINUATION_CI_MS = 5_500
|
||||
/** Apply variance headroom and round up to a whole second; the baseline is already a CI measurement. */
|
||||
const PROFILE_CONTINUATION_BUDGET_MS = Math.ceil(
|
||||
EXPECTED_PROFILE_CONTINUATION_CI_MS * PERFORMANCE_BUDGET_HEADROOM / 1_000,
|
||||
) * 1_000
|
||||
/** Standard two-CPU hosted CI baseline request-history median is 582.304 ms. */
|
||||
const EXPECTED_BASELINE_REQUEST_CI_MS = 600
|
||||
const BASELINE_REQUEST_BUDGET_MS = Math.ceil(EXPECTED_BASELINE_REQUEST_CI_MS * PERFORMANCE_BUDGET_HEADROOM)
|
||||
@@ -54,7 +58,7 @@ const CASES: Readonly<Record<Scenario, BenchmarkCase>> = {
|
||||
},
|
||||
}
|
||||
|
||||
type Scenario = 'request-history' | 'catalog' | 'tool-continuation' | keyof typeof EXPECTED_MS
|
||||
type Scenario = 'request-history' | 'catalog' | 'tool-continuation' | 'profile-continuation'
|
||||
type Report = ContinuationReport | CatalogReport | ProfileReport
|
||||
|
||||
function workerName(scenario: Scenario): string {
|
||||
@@ -81,6 +85,24 @@ function expectTotalWithinBudget(value: number, budget: number): void {
|
||||
expect(value).toBeLessThanOrEqual(budget)
|
||||
}
|
||||
|
||||
describe('checkpoint-enabled SDK-minimal hosted calibration', () => {
|
||||
it('accepts the recorded checkpoint-enabled workload above the buffered-write budget', () => {
|
||||
const recordedMedian = median([4_182.691, 5_946.326875, 5_581.127338, 5_432.567004, 4_362.941759])
|
||||
|
||||
expect(recordedMedian).toBe(5_432.567004)
|
||||
expect(() => expectTotalWithinBudget(recordedMedian, 4_250)).toThrow()
|
||||
expectTotalWithinBudget(recordedMedian, PROFILE_CONTINUATION_BUDGET_MS)
|
||||
expect(PROFILE_CONTINUATION_BUDGET_MS).toBe(7_000)
|
||||
})
|
||||
|
||||
it('rejects a material SDK-minimal profile slowdown', () => {
|
||||
const regressionMedian = median([7_600, 7_900, 8_000, 7_700, 7_800])
|
||||
|
||||
expect(regressionMedian).toBe(7_800)
|
||||
expect(() => expectTotalWithinBudget(regressionMedian, PROFILE_CONTINUATION_BUDGET_MS)).toThrow()
|
||||
})
|
||||
})
|
||||
|
||||
describe('standard hosted catalog calibration', () => {
|
||||
it('accepts the recorded two-CPU samples that exceed the historical budget', () => {
|
||||
const recordedMedian = median([797.373945, 883.157358, 858.363927, 790.568538, 904.5785669999999])
|
||||
@@ -187,7 +209,7 @@ describe('continuing tool-heavy Sessions with large histories', () => {
|
||||
const totalMs = samples.map(sample => sample.totalMs)
|
||||
const budgetMs = scenario === 'request-history' ? REQUEST_HISTORY_BUDGET_MS
|
||||
: scenario === 'catalog' ? CATALOG_BUDGET_MS
|
||||
: scenario === 'tool-continuation' ? TOOL_CONTINUATION_BUDGET_MS : ciTimeBudget(EXPECTED_MS[scenario])
|
||||
: scenario === 'tool-continuation' ? TOOL_CONTINUATION_BUDGET_MS : PROFILE_CONTINUATION_BUDGET_MS
|
||||
const retainedHeapBudgetMb = EXPECTED_RETAINED_HEAP_MB * PERFORMANCE_BUDGET_HEADROOM
|
||||
console.log(JSON.stringify({
|
||||
benchmark: 'agent-continuation/' + scenario, workload: WORKLOAD,
|
||||
|
||||
@@ -8,7 +8,7 @@ import { scheduler, setTimeout as delay } from 'node:timers/promises'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import AgentRegistry from '@deepseek-ai/dsh-agent'
|
||||
import RemoteAccessService from '@deepseek-ai/dsh-api-remote-access'
|
||||
import SessionController from '@deepseek-ai/dsh-api-session-controller'
|
||||
import type SessionController from '@deepseek-ai/dsh-api-session-controller'
|
||||
import SessionStore, { SESSION_FORMAT_VERSION, SessionId, SessionLogOffset, type SessionHeader } from '@deepseek-ai/dsh-session'
|
||||
import * as JsonlSessionPersistence from '@deepseek-ai/dsh-session-persistence-jsonl'
|
||||
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
|
||||
@@ -21,6 +21,7 @@ import { z } from 'zod'
|
||||
import { generationLogPath, sessionDir, toHeaderLine } from '../../packages/session/session-persistence-jsonl/src/format.ts'
|
||||
import { compressZstdFrame } from '../../packages/session/session-persistence-jsonl/src/zstd.ts'
|
||||
import { assertBuiltBenchmarkRuntime } from '../support/built-worker.ts'
|
||||
import { createBenchmarkSessionController } from './session-controller.ts'
|
||||
|
||||
const KEY = 'syntheticProjectionList'
|
||||
const CWD = '/synthetic-projection-benchmark'
|
||||
@@ -53,7 +54,6 @@ const viewSchema = z.object({ sessionOrdinal: z.number().int(), rows: z.number()
|
||||
type ProjectionState = z.infer<typeof stateSchema>
|
||||
|
||||
declare module '@deepseek-ai/dsh-session-projection/types' {
|
||||
interface SessionProjectionStateMap { syntheticProjectionList: ProjectionState }
|
||||
interface SessionProjectionMap { syntheticProjectionList: z.infer<typeof viewSchema> }
|
||||
}
|
||||
|
||||
@@ -170,9 +170,9 @@ async function seed(root: string, workload: typeof WORKLOADS[keyof typeof WORKLO
|
||||
|
||||
async function mountHost(ctx: Context, root: string, workload: typeof WORKLOADS[keyof typeof WORKLOADS], observation: ViewObservation, listWorkSliceMs?: number): Promise<SessionController> {
|
||||
await ctx.plugin(SessionStore)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
ctx.sessionProjections.register({
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.sessionProjections.register({
|
||||
key: KEY, stateVersion: 1, stateSchema,
|
||||
init: header => makeState(Number(header.id.slice('projection-bench-'.length)), workload.baseRows),
|
||||
apply: state => state,
|
||||
@@ -206,7 +206,7 @@ async function mountHost(ctx: Context, root: string, workload: typeof WORKLOADS[
|
||||
ctx.provide('agentDefaultModel', { currentSelection: () => ({ provider: 'synthetic', model: 'generated-model' }) } as never)
|
||||
ctx.provide('workspaceRegistry', { list: () => [], archivedSessionIds: [] } as never)
|
||||
new RemoteAccessService(ctx)
|
||||
return new SessionController(ctx, { nativeOpen: false, ...(listWorkSliceMs === undefined ? {} : { listWorkSliceMs }) }, { canOpenPath: () => false })
|
||||
return createBenchmarkSessionController(ctx, { nativeOpen: false, ...(listWorkSliceMs === undefined ? {} : { listWorkSliceMs }) })
|
||||
}
|
||||
|
||||
async function memory(): Promise<Memory> {
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/** Session-controller initialization for synthetic corpus workers without attachment I/O. */
|
||||
|
||||
import { Service, type Context } from '@deepseek-ai/cordis'
|
||||
import SessionController, { type Config } from '@deepseek-ai/dsh-api-session-controller'
|
||||
|
||||
class BenchmarkSessionController extends SessionController {
|
||||
initialize(): Promise<void> {
|
||||
return this[Service.init]()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the controller against the worker's partial Host services.
|
||||
* @param ctx - worker context owning service disposal.
|
||||
* @param config - native-opener and list-scheduling settings.
|
||||
* @returns controller ready for list, search, and fork calls.
|
||||
*/
|
||||
export async function createBenchmarkSessionController(ctx: Context, config: Config): Promise<SessionController> {
|
||||
ctx.provide('attachments', {
|
||||
imageLimits: {
|
||||
maxImageBytes: 5 * 1024 * 1024,
|
||||
maxImagesPerMessage: 20,
|
||||
maxMessageImageBytes: 100 * 1024 * 1024,
|
||||
maxImagePixels: 40_000_000,
|
||||
maxImageDimension: 2000,
|
||||
mediaTypes: ['image/png'],
|
||||
},
|
||||
} as never)
|
||||
const controller = new BenchmarkSessionController(ctx, config, { canOpenPath: () => false })
|
||||
await controller.initialize()
|
||||
return controller
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import AgentLoop from '@deepseek-ai/dsh-agent-loop'
|
||||
import { mountAgentLoopTestDependencies } from '@deepseek-ai/dsh-agent-loop-testkit'
|
||||
import { agentPresetProjectionDefinition } from '@deepseek-ai/dsh-agent-preset-registry'
|
||||
import RemoteAccessService from '@deepseek-ai/dsh-api-remote-access'
|
||||
import SessionController from '@deepseek-ai/dsh-api-session-controller'
|
||||
import type SessionController from '@deepseek-ai/dsh-api-session-controller'
|
||||
import { currentSessionMessageProjections } from '@deepseek-ai/dsh-session-format-catalog/message-projections'
|
||||
import { Session, SessionLogOffset } from '@deepseek-ai/dsh-session'
|
||||
import type { SessionId } from '@deepseek-ai/dsh-session'
|
||||
@@ -28,6 +28,7 @@ import TokenMeter from '@deepseek-ai/dsh-token-meter'
|
||||
import { assertBuiltBenchmarkRuntime } from '../support/built-worker.ts'
|
||||
import { serializeRecord } from '../../packages/storage/storage-json/src/format.ts'
|
||||
import { ANCHOR_COUNT } from './corpus-shape.ts'
|
||||
import { createBenchmarkSessionController } from './session-controller.ts'
|
||||
import {
|
||||
anchorHeader,
|
||||
corpusSessionId,
|
||||
@@ -141,7 +142,7 @@ async function mountHost(root: string): Promise<{ readonly ctx: Context; readonl
|
||||
await ctx.plugin(SessionProjectionCache, { writeEveryEvents: 200, writeIntervalMs: 5_000 })
|
||||
// Content search is opt-in; enabled deployments defer the in-memory index to the first search.
|
||||
await ctx.plugin(SqliteSessionQueryEngine, { path: ':memory:', openAt: 'first-search' })
|
||||
ctx.sessionProjections.register(agentPresetProjectionDefinition)
|
||||
await ctx.sessionProjections.register(agentPresetProjectionDefinition)
|
||||
await ctx.plugin(SessionTitleService, { fallbackMaxWords: 5, fallbackMaxBytes: 40, maxTitleBytes: 80 })
|
||||
await ctx.plugin(SessionStatsPlugin)
|
||||
await ctx.plugin(SessionTurnOutlinePlugin)
|
||||
@@ -158,7 +159,7 @@ async function mountHost(root: string): Promise<{ readonly ctx: Context; readonl
|
||||
ctx.provide('agentDefaultModel', { currentSelection: () => BENCH_MODEL } as never)
|
||||
ctx.provide('workspaceRegistry', { list: () => [], archivedSessionIds: [] } as never)
|
||||
new RemoteAccessService(ctx)
|
||||
const controller = new SessionController(ctx, { nativeOpen: false }, { canOpenPath: () => false })
|
||||
const controller = await createBenchmarkSessionController(ctx, { nativeOpen: false })
|
||||
return { ctx, controller }
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { performance } from 'node:perf_hooks'
|
||||
import { scheduler } from 'node:timers/promises'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import AgentLoop, { turnBoundaryProjectionDefinition } from '@deepseek-ai/dsh-agent-loop'
|
||||
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
|
||||
import { mountAgentLoopTestDependencies } from '@deepseek-ai/dsh-agent-loop-testkit'
|
||||
import { agentPresetProjectionDefinition } from '@deepseek-ai/dsh-agent-preset-registry'
|
||||
import SessionStore, {
|
||||
@@ -12,7 +12,7 @@ import SessionStore, {
|
||||
SessionLogOffset,
|
||||
SessionPreparation,
|
||||
} from '@deepseek-ai/dsh-session'
|
||||
import type { AgentHandle } from '@deepseek-ai/dsh-agent'
|
||||
import AgentRegistry, { type AgentHandle } from '@deepseek-ai/dsh-agent'
|
||||
import * as JsonlSessionPersistence from '@deepseek-ai/dsh-session-persistence-jsonl'
|
||||
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
|
||||
import SessionQueryEngine from '@deepseek-ai/dsh-session-query'
|
||||
@@ -30,7 +30,7 @@ import * as SessionTurnOutlinePlugin from '@deepseek-ai/dsh-session-turn-outline
|
||||
import TokenMeter from '@deepseek-ai/dsh-token-meter'
|
||||
// These Host-only adapters have no public Node export and are compiled into the benchmark worker.
|
||||
import { SessionHistoryController } from '../../packages/api/session-controller/src/history.ts'
|
||||
import { installModelSelectionProjection } from '../../packages/api/session-controller/src/model-selection-projection.ts'
|
||||
import { modelSelectionProjection } from '../../packages/api/session-controller/src/model-selection-projection.ts'
|
||||
import { assertBuiltBenchmarkRuntime } from '../support/built-worker.ts'
|
||||
import { SYNTHETIC_SESSION_ID } from './session-open.constants.ts'
|
||||
|
||||
@@ -128,11 +128,10 @@ function memoryDelta(
|
||||
}
|
||||
}
|
||||
|
||||
async function installProjectionSet(ctx: Context, agentLoopOwnsBoundary: boolean): Promise<void> {
|
||||
async function installProjectionSet(ctx: Context): Promise<void> {
|
||||
// Mirrors projection owners mounted by the base and web-app bundles without timing profile boot.
|
||||
if (!agentLoopOwnsBoundary) ctx.sessionProjections.register(turnBoundaryProjectionDefinition)
|
||||
ctx.sessionProjections.register(agentPresetProjectionDefinition)
|
||||
installModelSelectionProjection(ctx)
|
||||
await ctx.sessionProjections.register(agentPresetProjectionDefinition)
|
||||
await ctx.sessionProjections.register(modelSelectionProjection)
|
||||
await ctx.plugin(SessionTitleService, {
|
||||
fallbackMaxWords: 5,
|
||||
fallbackMaxBytes: 40,
|
||||
@@ -163,9 +162,10 @@ class SessionBenchmarkHost {
|
||||
if (agentScenario) await mountAgentLoopTestDependencies(ctx)
|
||||
else {
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(SessionStore)
|
||||
}
|
||||
await installProjectionSet(ctx, agentScenario)
|
||||
await installProjectionSet(ctx)
|
||||
await ctx.plugin(JsonlSessionPersistence, { root, compression: 'zstd' })
|
||||
let history: SessionHistoryController | undefined
|
||||
if (scenario === 'first-history') {
|
||||
|
||||
@@ -82,7 +82,7 @@ const FACTS: Readonly<Partial<UserTextFacts>> = { sessionLabels: ['会话一'],
|
||||
const KIB = 1024
|
||||
/** Exercise each workload's code paths before measuring sub-millisecond baseline evaluations. */
|
||||
const WARMUP_PAIRS = 10
|
||||
/** Batch every single-document workload to limit noise in its shortest evaluation intervals. */
|
||||
/** Base batch size; half-sized scaling inputs use twice as many evaluations. */
|
||||
const SINGLE_DOCUMENT_EVALUATIONS = 20
|
||||
const PROSE = '帮我看看这个函数为什么慢,顺便把 README 也更新一下。The quick brown fox jumps over the lazy dog, 1234 times. '
|
||||
const CODE = 'const value = await fetch(`https://api.example.com/v1/items?id=${id}`); if (!value.ok) throw new Error("bad: " + value.status);\n'
|
||||
@@ -160,22 +160,32 @@ interface Workload {
|
||||
readonly id: string
|
||||
readonly group: UserTextWorkloadGroup
|
||||
readonly texts: readonly string[]
|
||||
/** Scaling pairs process the same total keys or characters per sample, including their GC cost. */
|
||||
readonly evaluationsPerSample: number
|
||||
}
|
||||
|
||||
/** Named fixed workloads; adversarial inputs appear at two sizes so their scaling is observable. */
|
||||
function workloads(): readonly Workload[] {
|
||||
return [
|
||||
{ id: 'typical-messages', group: 'unmatched', texts: messages(5_000) },
|
||||
{ id: 'typical-messages', group: 'unmatched', texts: messages(5_000), evaluationsPerSample: 1 },
|
||||
...Object.entries({ prose: PROSE, code: CODE, base64: BASE64, markdown: MARKDOWN, log: LOG, json: JSON_LINES, shell: SHELL })
|
||||
.map(([name, unit]): Workload => ({ id: `${name}-256k`, group: 'unmatched', texts: [fill(unit, 256 * KIB)] })),
|
||||
.map(([name, unit]): Workload => ({
|
||||
id: `${name}-256k`, group: 'unmatched', texts: [fill(unit, 256 * KIB)], evaluationsPerSample: SINGLE_DOCUMENT_EVALUATIONS,
|
||||
})),
|
||||
...Object.entries(CREDENTIALS).map(([name, credential]): Workload => ({
|
||||
id: `messages-with-${name}`, group: 'matched', texts: credentialMessages(5_000, credential),
|
||||
id: `messages-with-${name}`, group: 'matched', texts: credentialMessages(5_000, credential), evaluationsPerSample: 1,
|
||||
})),
|
||||
{ id: 'env-keys-1000', group: 'matched', texts: [envFile(1_000)] },
|
||||
{ id: 'env-keys-2000', group: 'matched', texts: [envFile(2_000)] },
|
||||
{ id: 'env-keys-1000', group: 'matched', texts: [envFile(1_000)], evaluationsPerSample: 2 * SINGLE_DOCUMENT_EVALUATIONS },
|
||||
{ id: 'env-keys-2000', group: 'matched', texts: [envFile(2_000)], evaluationsPerSample: SINGLE_DOCUMENT_EVALUATIONS },
|
||||
...Object.entries(ADVERSARIAL_UNITS).flatMap(([name, unit]): Workload[] => [
|
||||
{ id: `adversarial-${name}-64k`, group: 'adversarial', texts: [fill(unit, 64 * KIB)] },
|
||||
{ id: `adversarial-${name}-128k`, group: 'adversarial', texts: [fill(unit, 128 * KIB)] },
|
||||
{
|
||||
id: `adversarial-${name}-64k`, group: 'adversarial', texts: [fill(unit, 64 * KIB)],
|
||||
evaluationsPerSample: 2 * SINGLE_DOCUMENT_EVALUATIONS,
|
||||
},
|
||||
{
|
||||
id: `adversarial-${name}-128k`, group: 'adversarial', texts: [fill(unit, 128 * KIB)],
|
||||
evaluationsPerSample: SINGLE_DOCUMENT_EVALUATIONS,
|
||||
},
|
||||
]),
|
||||
]
|
||||
}
|
||||
@@ -364,8 +374,7 @@ async function allocations(): Promise<UserTextAllocationReport> {
|
||||
|
||||
async function timings(attempts: number): Promise<UserTextTimeReport> {
|
||||
const results: UserTextWorkloadTiming[] = []
|
||||
for (const { id, group, texts } of workloads()) {
|
||||
const evaluationsPerSample = texts.length === 1 ? SINGLE_DOCUMENT_EVALUATIONS : 1
|
||||
for (const { id, group, texts, evaluationsPerSample } of workloads()) {
|
||||
const { base, secret, samples } = await measure(texts, attempts, evaluationsPerSample)
|
||||
results.push({
|
||||
id,
|
||||
|
||||
@@ -27,8 +27,8 @@
|
||||
en: e6402b28c0ed15fa
|
||||
zh: 6c07be9047b42056
|
||||
/deepseek-harness-architecture/session-log:
|
||||
en: a2e35c43438fec6d
|
||||
zh: e27bdac8e9feff32
|
||||
en: f1729d805c20df36
|
||||
zh: 6b71b6039663e2e7
|
||||
/deepseek-harness-architecture/capability-seams:
|
||||
en: a5bc20c3f2a1b370
|
||||
zh: 91773a281410b2f4
|
||||
|
||||
@@ -123,7 +123,7 @@ Session consumers know only the current logical format. Header-only `stat` and `
|
||||
|
||||
**Model-visible means logged.** Every model request must be reconstructable from the log. New model-visible inputs require session events. Plugins changing existing message content register [pure message projections](subsystems/session.md#plugin-owned-message-projections); detached readers supply the same definitions explicitly. Tool changes are capability-independent; [Session tool history](../packages/core/session/README.md) supplies provider declarations.
|
||||
|
||||
**Projection seam.** `dsh-session-projection` owns `ctx.sessionProjections`: registered units fold committed events incrementally, host consumers read one typed state with `stateOf()`, and carriers batch cropped client views with `snapshot()`. A host reader either requires this service during activation or fails explicitly when the registry or required key is absent. Contributors may retain `ctx.inject(['sessionProjections'], ...)` registration without silently defaulting a missing host value. The agent loop registers shared `turnBoundary` state for its readers ([decision](../.agents/notes/implemented/architecture/2026-08-19-session-projection-mandatory-seam.md)).
|
||||
**Projection seam.** `dsh-session-projection` owns `ctx.sessionProjections`: registered units fold committed events incrementally, and carriers batch cropped client views with `snapshot()`. Domain services await registration during activation, retain private handles, and expose business methods for other plugins. Releasing registration removes global participation; retained handles continue folding Session events. `AgentRegistry` owns turn and step state and exposes `turnBoundaryOf(session)` ([registration lifecycle](../packages/session/session-projection/README.md)).
|
||||
|
||||
[Working directories](subsystems/working-directory.md) supply user context and execution paths without changing original project identity, sandbox write roots, or existing process directories.
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ Session 消费方只了解当前逻辑格式。仅 header 的 `stat` 与 `list`
|
||||
|
||||
**模型可见即已记录。** 每个模型请求都必须能从日志重建。新增模型可见输入需要会话事件。修改现有消息内容的插件注册[纯消息投影](subsystems/session.zh.md#plugin-owned-message-projections),独立读取器显式传入相同的处理器。 工具变更不依赖能力;[Session 工具历史](../packages/core/session/README.zh.md)提供提供方声明。
|
||||
|
||||
**投影 seam。** `dsh-session-projection` 提供 `ctx.sessionProjections`:已注册单元增量折叠已提交事件,host 消费方通过 `stateOf()` 读取单个类型化状态,载体通过 `snapshot()` 批量取得裁剪后的客户端视图。host 读取方要么在激活时要求该服务,要么在注册表或必需 key 缺席时明确失败。贡献方可以保留 `ctx.inject(['sessionProjections'], ...)` 注册,但不能为缺失的 host 值静默提供默认值。agent loop 为读取方注册共享的 `turnBoundary` 状态([决策](../.agents/notes/implemented/architecture/2026-08-19-session-projection-mandatory-seam.zh.md))。
|
||||
**投影 seam。** `dsh-session-projection` 提供 `ctx.sessionProjections`:已注册单元增量折叠已提交事件,载体通过 `snapshot()` 批量取得裁剪后的客户端视图。领域服务在激活时等待注册完成,在内部保留句柄,并向其他插件提供业务方法。释放注册会移除全局参与关系;已有句柄仍可继续折叠 Session 事件。`AgentRegistry` 持有轮次和步骤状态,并提供 `turnBoundaryOf(session)`([注册生命周期](../packages/session/session-projection/README.zh.md))。
|
||||
|
||||
[工作目录](subsystems/working-directory.zh.md) 提供用户上下文和执行路径,不改变原始项目标识、沙箱写入根目录或已有进程目录。
|
||||
|
||||
|
||||
+22
-22
@@ -59,7 +59,7 @@ export interface Config {
|
||||
|
||||
## `@deepseek-ai/dsh-agent-instructions`
|
||||
|
||||
- `inject`: `sessionProjections` · `workingDirectory`
|
||||
- `inject`: `agents` · `workingDirectory`
|
||||
- `source`: [`packages/context/agent-instructions/src/config.ts:18`](../packages/context/agent-instructions/src/config.ts)
|
||||
|
||||
```ts config-catalog
|
||||
@@ -96,7 +96,7 @@ export interface Config {
|
||||
|
||||
- `inject`: `agents` · `sessions` · `llm` · `tools` · `systemPrompt` · `sessionProjections`
|
||||
- `refs`: [`AgentOptions`](subsystems/core.md) · [`SessionId`](subsystems/core.md) · `Volatile` (`@deepseek-ai/cosmokit`)
|
||||
- `source`: [`packages/core/agent-loop/src/index.ts:303`](../packages/core/agent-loop/src/index.ts)
|
||||
- `source`: [`packages/core/agent-loop/src/index.ts:250`](../packages/core/agent-loop/src/index.ts)
|
||||
|
||||
```ts config-catalog
|
||||
/** Agent-loop plugin configuration. */
|
||||
@@ -141,7 +141,7 @@ export type Config = PresetDefinition
|
||||
|
||||
## `@deepseek-ai/dsh-agent-preset-registry`
|
||||
|
||||
- `inject`: `loader` · `sessionProjections`
|
||||
- `inject`: `loader` · `sessionProjections` · `agents`
|
||||
- `refs`: `Volatile` (`@deepseek-ai/cordis`)
|
||||
- `source`: [`packages/preset/agent-preset-registry/src/preset.ts:13`](../packages/preset/agent-preset-registry/src/preset.ts)
|
||||
|
||||
@@ -226,7 +226,7 @@ export interface Config {
|
||||
## `@deepseek-ai/dsh-api-session-controller`
|
||||
|
||||
- `inject`: `agentDefaultModel` · `agents` · `attachments` · `fileUploads` · `fs` · `llm` · `remoteAccess` · `sessions` · `sessionProjections` · `sessionQuery` · `typert` · `workspaceRegistry`
|
||||
- `source`: [`packages/api/session-controller/src/index.ts:94`](../packages/api/session-controller/src/index.ts)
|
||||
- `source`: [`packages/api/session-controller/src/index.ts:93`](../packages/api/session-controller/src/index.ts)
|
||||
|
||||
```ts config-catalog
|
||||
/** Session Controller deployment policy. */
|
||||
@@ -960,7 +960,7 @@ export interface StagehandModelConfig {
|
||||
|
||||
## `@deepseek-ai/dsh-experimental-claude-code-mods`
|
||||
|
||||
- `inject`: `workingDirectory`
|
||||
- `inject`: `workingDirectory` · `agents`
|
||||
- `source`: [`packages/experimental/claude-code-mods/src/index.ts:53`](../packages/experimental/claude-code-mods/src/index.ts)
|
||||
|
||||
```ts config-catalog
|
||||
@@ -1472,7 +1472,7 @@ export type Config = LocalConfig
|
||||
## `@deepseek-ai/dsh-goal`
|
||||
|
||||
- `inject`: `agents` · `sessionProjections`
|
||||
- `source`: [`packages/goal/goal/src/index.ts:172`](../packages/goal/goal/src/index.ts)
|
||||
- `source`: [`packages/goal/goal/src/index.ts:168`](../packages/goal/goal/src/index.ts)
|
||||
|
||||
```ts config-catalog
|
||||
/** Deployment defaults for goal creation. */
|
||||
@@ -1537,8 +1537,8 @@ export interface HmrConfig extends ChokidarOptions {
|
||||
|
||||
## `@deepseek-ai/dsh-hooks-claude-code`
|
||||
|
||||
- `inject`: `shell` · `sessionProjections` · `workingDirectory`
|
||||
- `source`: [`packages/experimental/hooks-claude-code/src/index.ts:51`](../packages/experimental/hooks-claude-code/src/index.ts)
|
||||
- `inject`: `shell` · `agents` · `workingDirectory`
|
||||
- `source`: [`packages/experimental/hooks-claude-code/src/index.ts:50`](../packages/experimental/hooks-claude-code/src/index.ts)
|
||||
|
||||
```ts config-catalog
|
||||
/** Plugin config: where the CC hook config lives + substitution roots. */
|
||||
@@ -1576,8 +1576,8 @@ export interface Config {
|
||||
|
||||
## `@deepseek-ai/dsh-hooks-codex`
|
||||
|
||||
- `inject`: `shell` · `sessionProjections` · `workingDirectory`
|
||||
- `source`: [`packages/experimental/hooks-codex/src/index.ts:50`](../packages/experimental/hooks-codex/src/index.ts)
|
||||
- `inject`: `shell` · `agents` · `workingDirectory`
|
||||
- `source`: [`packages/experimental/hooks-codex/src/index.ts:49`](../packages/experimental/hooks-codex/src/index.ts)
|
||||
|
||||
```ts config-catalog
|
||||
/** Plugin config: where the Codex hooks.json lives + the model name for payloads. */
|
||||
@@ -2396,7 +2396,7 @@ export interface Config {
|
||||
|
||||
- `inject`: `shell` · `approval` · `sessions` · `sessionProjections`
|
||||
- `refs`: [`ApprovalPolicy`](subsystems/approval.md) · [`SandboxMode`](subsystems/sandbox.md) · `Volatile` (`@deepseek-ai/cordis`)
|
||||
- `source`: [`packages/interaction/permission-presets/src/index.ts:159`](../packages/interaction/permission-presets/src/index.ts)
|
||||
- `source`: [`packages/interaction/permission-presets/src/index.ts:152`](../packages/interaction/permission-presets/src/index.ts)
|
||||
|
||||
```ts config-catalog
|
||||
/** The {@link PermissionPresetService} config: preset table and composition default. */
|
||||
@@ -2464,7 +2464,7 @@ export interface Config {
|
||||
|
||||
## `@deepseek-ai/dsh-plan-mode`
|
||||
|
||||
- `inject`: `tools` · `systemPrompt` · `sessionProjections`
|
||||
- `inject`: `tools` · `systemPrompt` · `sessionProjections` · `agents`
|
||||
- `source`: [`packages/plan/plan-mode/src/index.ts:69`](../packages/plan/plan-mode/src/index.ts)
|
||||
|
||||
```ts config-catalog
|
||||
@@ -3056,7 +3056,7 @@ export enum SessionTelemetryMode {
|
||||
|
||||
## `@deepseek-ai/dsh-session-title`
|
||||
|
||||
- `inject`: `sessions` · `sessionProjections`
|
||||
- `inject`: `sessions` · `sessionProjections` · `agents`
|
||||
- `source`: [`packages/session/session-title/src/index.ts:56`](../packages/session/session-title/src/index.ts)
|
||||
|
||||
```ts config-catalog
|
||||
@@ -3391,7 +3391,7 @@ export type JournalMode = 'wal' | 'delete' | 'truncate' | 'persist'
|
||||
|
||||
## `@deepseek-ai/dsh-subagent`
|
||||
|
||||
- `inject`: `workingDirectory`
|
||||
- `inject`: `workingDirectory` · `sessionProjections`
|
||||
- `refs`: `Volatile` (`@deepseek-ai/cordis`)
|
||||
- `source`: [`packages/subagent/subagent/src/index.ts:186`](../packages/subagent/subagent/src/index.ts)
|
||||
|
||||
@@ -3643,7 +3643,7 @@ export interface Config {
|
||||
|
||||
## `@deepseek-ai/dsh-terminal-bash`
|
||||
|
||||
- `inject`: `terminals` · `sandboxPolicy` · `sessionProjections` · `subprocess` · `workingDirectory`
|
||||
- `inject`: `terminals` · `sandboxPolicy` · `subprocess` · `workingDirectory`
|
||||
- `source`: [`packages/terminal/terminal-bash/src/config.ts:10`](../packages/terminal/terminal-bash/src/config.ts)
|
||||
|
||||
```ts config-catalog
|
||||
@@ -3704,7 +3704,7 @@ export type ShellDialect = 'bash' | 'pwsh'
|
||||
## `@deepseek-ai/dsh-time-context`
|
||||
|
||||
- `inject`: `agents` · `sessionProjections`
|
||||
- `source`: [`packages/context/time-context/src/index.ts:57`](../packages/context/time-context/src/index.ts)
|
||||
- `source`: [`packages/context/time-context/src/index.ts:47`](../packages/context/time-context/src/index.ts)
|
||||
|
||||
```ts config-catalog
|
||||
/** Request-preparation clock formatting and append scheduling. Invalid values fail plugin load. */
|
||||
@@ -3892,7 +3892,7 @@ export interface Config {
|
||||
|
||||
## `@deepseek-ai/dsh-tool-goal`
|
||||
|
||||
- `inject`: `agents` · `goals` · `tools` · `systemPrompt` · `sessionProjections`
|
||||
- `inject`: `agents` · `goals` · `tools` · `systemPrompt`
|
||||
- `source`: [`packages/goal/tool-goal/src/index.ts:32`](../packages/goal/tool-goal/src/index.ts)
|
||||
|
||||
```ts config-catalog
|
||||
@@ -3967,8 +3967,8 @@ export interface Config {
|
||||
|
||||
## `@deepseek-ai/dsh-tool-present`
|
||||
|
||||
- `inject`: `tools` · `fs` · `sessionProjections` · `workingDirectory`
|
||||
- `source`: [`packages/deliverables/tool-present/src/index.ts:16`](../packages/deliverables/tool-present/src/index.ts)
|
||||
- `inject`: `tools` · `fs` · `agents` · `workingDirectory`
|
||||
- `source`: [`packages/deliverables/tool-present/src/index.ts:15`](../packages/deliverables/tool-present/src/index.ts)
|
||||
|
||||
```ts config-catalog
|
||||
/** Per-call delivery limit. */
|
||||
@@ -4060,7 +4060,7 @@ export interface Config {
|
||||
|
||||
## `@deepseek-ai/dsh-tool-session-query`
|
||||
|
||||
- `inject`: `tools` · `systemPrompt` · `sessionQuery` · `sessionProjections`
|
||||
- `inject`: `tools` · `systemPrompt` · `sessionQuery` · `agents`
|
||||
- `source`: [`packages/experimental/tool-session-query/src/index.ts:28`](../packages/experimental/tool-session-query/src/index.ts)
|
||||
|
||||
```ts config-catalog
|
||||
@@ -4619,7 +4619,7 @@ export interface Config {
|
||||
## `@deepseek-ai/dsh-working-directory`
|
||||
|
||||
- `inject`: `fs` · `sessionProjections` · `systemPrompt`
|
||||
- `source`: [`packages/session/working-directory/src/index.ts:56`](../packages/session/working-directory/src/index.ts)
|
||||
- `source`: [`packages/session/working-directory/src/index.ts:51`](../packages/session/working-directory/src/index.ts)
|
||||
|
||||
```ts config-catalog
|
||||
/** Deployment default for Sessions without an original project directory. */
|
||||
@@ -4666,7 +4666,7 @@ These load from a `cordis.yml` entry with no `config:` block; they declare no co
|
||||
| `package` | `inject` | `source` |
|
||||
| --- | --- | --- |
|
||||
| `@deepseek-ai/dsh-acp-app` | `cmdlineArgs` | [`packages/bundle/acp-app/src/index.ts`](../packages/bundle/acp-app/src/index.ts) |
|
||||
| `@deepseek-ai/dsh-agent` | — | [`packages/core/agent/src/index.ts`](../packages/core/agent/src/index.ts) |
|
||||
| `@deepseek-ai/dsh-agent` | `sessionProjections` | [`packages/core/agent/src/index.ts`](../packages/core/agent/src/index.ts) |
|
||||
| `@deepseek-ai/dsh-api-account-controller` | `deepseekAccount` · `agents` | [`packages/api/account-controller/src/index.ts`](../packages/api/account-controller/src/index.ts) |
|
||||
| `@deepseek-ai/dsh-api-remote-access` | — | [`packages/api/remote-access/src/index.ts`](../packages/api/remote-access/src/index.ts) |
|
||||
| `@deepseek-ai/dsh-api-remotes` | `typertGateway` | [`packages/api/remotes/src/index.ts`](../packages/api/remotes/src/index.ts) |
|
||||
|
||||
+22
-22
@@ -61,7 +61,7 @@ export interface Config {
|
||||
|
||||
## `@deepseek-ai/dsh-agent-instructions`
|
||||
|
||||
- `inject`: `sessionProjections` · `workingDirectory`
|
||||
- `inject`: `agents` · `workingDirectory`
|
||||
- `source`: [`packages/context/agent-instructions/src/config.ts:18`](../packages/context/agent-instructions/src/config.ts)
|
||||
|
||||
```ts config-catalog
|
||||
@@ -98,7 +98,7 @@ export interface Config {
|
||||
|
||||
- `inject`: `agents` · `sessions` · `llm` · `tools` · `systemPrompt` · `sessionProjections`
|
||||
- `refs`: [`AgentOptions`](subsystems/core.zh.md) · [`SessionId`](subsystems/core.zh.md) · `Volatile` (`@deepseek-ai/cosmokit`)
|
||||
- `source`: [`packages/core/agent-loop/src/index.ts:303`](../packages/core/agent-loop/src/index.ts)
|
||||
- `source`: [`packages/core/agent-loop/src/index.ts:250`](../packages/core/agent-loop/src/index.ts)
|
||||
|
||||
```ts config-catalog
|
||||
/** Agent-loop plugin configuration. */
|
||||
@@ -143,7 +143,7 @@ export type Config = PresetDefinition
|
||||
|
||||
## `@deepseek-ai/dsh-agent-preset-registry`
|
||||
|
||||
- `inject`: `loader` · `sessionProjections`
|
||||
- `inject`: `loader` · `sessionProjections` · `agents`
|
||||
- `refs`: `Volatile` (`@deepseek-ai/cordis`)
|
||||
- `source`: [`packages/preset/agent-preset-registry/src/preset.ts:13`](../packages/preset/agent-preset-registry/src/preset.ts)
|
||||
|
||||
@@ -228,7 +228,7 @@ export interface Config {
|
||||
## `@deepseek-ai/dsh-api-session-controller`
|
||||
|
||||
- `inject`: `agentDefaultModel` · `agents` · `attachments` · `fileUploads` · `fs` · `llm` · `remoteAccess` · `sessions` · `sessionProjections` · `sessionQuery` · `typert` · `workspaceRegistry`
|
||||
- `source`: [`packages/api/session-controller/src/index.ts:94`](../packages/api/session-controller/src/index.ts)
|
||||
- `source`: [`packages/api/session-controller/src/index.ts:93`](../packages/api/session-controller/src/index.ts)
|
||||
|
||||
```ts config-catalog
|
||||
/** Session Controller deployment policy. */
|
||||
@@ -962,7 +962,7 @@ export interface StagehandModelConfig {
|
||||
|
||||
## `@deepseek-ai/dsh-experimental-claude-code-mods`
|
||||
|
||||
- `inject`: `workingDirectory`
|
||||
- `inject`: `workingDirectory` · `agents`
|
||||
- `source`: [`packages/experimental/claude-code-mods/src/index.ts:53`](../packages/experimental/claude-code-mods/src/index.ts)
|
||||
|
||||
```ts config-catalog
|
||||
@@ -1474,7 +1474,7 @@ export type Config = LocalConfig
|
||||
## `@deepseek-ai/dsh-goal`
|
||||
|
||||
- `inject`: `agents` · `sessionProjections`
|
||||
- `source`: [`packages/goal/goal/src/index.ts:172`](../packages/goal/goal/src/index.ts)
|
||||
- `source`: [`packages/goal/goal/src/index.ts:168`](../packages/goal/goal/src/index.ts)
|
||||
|
||||
```ts config-catalog
|
||||
/** Deployment defaults for goal creation. */
|
||||
@@ -1539,8 +1539,8 @@ export interface HmrConfig extends ChokidarOptions {
|
||||
|
||||
## `@deepseek-ai/dsh-hooks-claude-code`
|
||||
|
||||
- `inject`: `shell` · `sessionProjections` · `workingDirectory`
|
||||
- `source`: [`packages/experimental/hooks-claude-code/src/index.ts:51`](../packages/experimental/hooks-claude-code/src/index.ts)
|
||||
- `inject`: `shell` · `agents` · `workingDirectory`
|
||||
- `source`: [`packages/experimental/hooks-claude-code/src/index.ts:50`](../packages/experimental/hooks-claude-code/src/index.ts)
|
||||
|
||||
```ts config-catalog
|
||||
/** Plugin config: where the CC hook config lives + substitution roots. */
|
||||
@@ -1578,8 +1578,8 @@ export interface Config {
|
||||
|
||||
## `@deepseek-ai/dsh-hooks-codex`
|
||||
|
||||
- `inject`: `shell` · `sessionProjections` · `workingDirectory`
|
||||
- `source`: [`packages/experimental/hooks-codex/src/index.ts:50`](../packages/experimental/hooks-codex/src/index.ts)
|
||||
- `inject`: `shell` · `agents` · `workingDirectory`
|
||||
- `source`: [`packages/experimental/hooks-codex/src/index.ts:49`](../packages/experimental/hooks-codex/src/index.ts)
|
||||
|
||||
```ts config-catalog
|
||||
/** Plugin config: where the Codex hooks.json lives + the model name for payloads. */
|
||||
@@ -2398,7 +2398,7 @@ export interface Config {
|
||||
|
||||
- `inject`: `shell` · `approval` · `sessions` · `sessionProjections`
|
||||
- `refs`: [`ApprovalPolicy`](subsystems/approval.zh.md) · [`SandboxMode`](subsystems/sandbox.zh.md) · `Volatile` (`@deepseek-ai/cordis`)
|
||||
- `source`: [`packages/interaction/permission-presets/src/index.ts:159`](../packages/interaction/permission-presets/src/index.ts)
|
||||
- `source`: [`packages/interaction/permission-presets/src/index.ts:152`](../packages/interaction/permission-presets/src/index.ts)
|
||||
|
||||
```ts config-catalog
|
||||
/** The {@link PermissionPresetService} config: preset table and composition default. */
|
||||
@@ -2466,7 +2466,7 @@ export interface Config {
|
||||
|
||||
## `@deepseek-ai/dsh-plan-mode`
|
||||
|
||||
- `inject`: `tools` · `systemPrompt` · `sessionProjections`
|
||||
- `inject`: `tools` · `systemPrompt` · `sessionProjections` · `agents`
|
||||
- `source`: [`packages/plan/plan-mode/src/index.ts:69`](../packages/plan/plan-mode/src/index.ts)
|
||||
|
||||
```ts config-catalog
|
||||
@@ -3058,7 +3058,7 @@ export enum SessionTelemetryMode {
|
||||
|
||||
## `@deepseek-ai/dsh-session-title`
|
||||
|
||||
- `inject`: `sessions` · `sessionProjections`
|
||||
- `inject`: `sessions` · `sessionProjections` · `agents`
|
||||
- `source`: [`packages/session/session-title/src/index.ts:56`](../packages/session/session-title/src/index.ts)
|
||||
|
||||
```ts config-catalog
|
||||
@@ -3393,7 +3393,7 @@ export type JournalMode = 'wal' | 'delete' | 'truncate' | 'persist'
|
||||
|
||||
## `@deepseek-ai/dsh-subagent`
|
||||
|
||||
- `inject`: `workingDirectory`
|
||||
- `inject`: `workingDirectory` · `sessionProjections`
|
||||
- `refs`: `Volatile` (`@deepseek-ai/cordis`)
|
||||
- `source`: [`packages/subagent/subagent/src/index.ts:186`](../packages/subagent/subagent/src/index.ts)
|
||||
|
||||
@@ -3645,7 +3645,7 @@ export interface Config {
|
||||
|
||||
## `@deepseek-ai/dsh-terminal-bash`
|
||||
|
||||
- `inject`: `terminals` · `sandboxPolicy` · `sessionProjections` · `subprocess` · `workingDirectory`
|
||||
- `inject`: `terminals` · `sandboxPolicy` · `subprocess` · `workingDirectory`
|
||||
- `source`: [`packages/terminal/terminal-bash/src/config.ts:10`](../packages/terminal/terminal-bash/src/config.ts)
|
||||
|
||||
```ts config-catalog
|
||||
@@ -3706,7 +3706,7 @@ export type ShellDialect = 'bash' | 'pwsh'
|
||||
## `@deepseek-ai/dsh-time-context`
|
||||
|
||||
- `inject`: `agents` · `sessionProjections`
|
||||
- `source`: [`packages/context/time-context/src/index.ts:57`](../packages/context/time-context/src/index.ts)
|
||||
- `source`: [`packages/context/time-context/src/index.ts:47`](../packages/context/time-context/src/index.ts)
|
||||
|
||||
```ts config-catalog
|
||||
/** Request-preparation clock formatting and append scheduling. Invalid values fail plugin load. */
|
||||
@@ -3894,7 +3894,7 @@ export interface Config {
|
||||
|
||||
## `@deepseek-ai/dsh-tool-goal`
|
||||
|
||||
- `inject`: `agents` · `goals` · `tools` · `systemPrompt` · `sessionProjections`
|
||||
- `inject`: `agents` · `goals` · `tools` · `systemPrompt`
|
||||
- `source`: [`packages/goal/tool-goal/src/index.ts:32`](../packages/goal/tool-goal/src/index.ts)
|
||||
|
||||
```ts config-catalog
|
||||
@@ -3969,8 +3969,8 @@ export interface Config {
|
||||
|
||||
## `@deepseek-ai/dsh-tool-present`
|
||||
|
||||
- `inject`: `tools` · `fs` · `sessionProjections` · `workingDirectory`
|
||||
- `source`: [`packages/deliverables/tool-present/src/index.ts:16`](../packages/deliverables/tool-present/src/index.ts)
|
||||
- `inject`: `tools` · `fs` · `agents` · `workingDirectory`
|
||||
- `source`: [`packages/deliverables/tool-present/src/index.ts:15`](../packages/deliverables/tool-present/src/index.ts)
|
||||
|
||||
```ts config-catalog
|
||||
/** Per-call delivery limit. */
|
||||
@@ -4062,7 +4062,7 @@ export interface Config {
|
||||
|
||||
## `@deepseek-ai/dsh-tool-session-query`
|
||||
|
||||
- `inject`: `tools` · `systemPrompt` · `sessionQuery` · `sessionProjections`
|
||||
- `inject`: `tools` · `systemPrompt` · `sessionQuery` · `agents`
|
||||
- `source`: [`packages/experimental/tool-session-query/src/index.ts:28`](../packages/experimental/tool-session-query/src/index.ts)
|
||||
|
||||
```ts config-catalog
|
||||
@@ -4621,7 +4621,7 @@ export interface Config {
|
||||
## `@deepseek-ai/dsh-working-directory`
|
||||
|
||||
- `inject`: `fs` · `sessionProjections` · `systemPrompt`
|
||||
- `source`: [`packages/session/working-directory/src/index.ts:56`](../packages/session/working-directory/src/index.ts)
|
||||
- `source`: [`packages/session/working-directory/src/index.ts:51`](../packages/session/working-directory/src/index.ts)
|
||||
|
||||
```ts config-catalog
|
||||
/** Deployment default for Sessions without an original project directory. */
|
||||
@@ -4668,7 +4668,7 @@ export interface Config {
|
||||
| `package` | `inject` | `source` |
|
||||
| --- | --- | --- |
|
||||
| `@deepseek-ai/dsh-acp-app` | `cmdlineArgs` | [`packages/bundle/acp-app/src/index.ts`](../packages/bundle/acp-app/src/index.ts) |
|
||||
| `@deepseek-ai/dsh-agent` | — | [`packages/core/agent/src/index.ts`](../packages/core/agent/src/index.ts) |
|
||||
| `@deepseek-ai/dsh-agent` | `sessionProjections` | [`packages/core/agent/src/index.ts`](../packages/core/agent/src/index.ts) |
|
||||
| `@deepseek-ai/dsh-api-account-controller` | `deepseekAccount` · `agents` | [`packages/api/account-controller/src/index.ts`](../packages/api/account-controller/src/index.ts) |
|
||||
| `@deepseek-ai/dsh-api-remote-access` | — | [`packages/api/remote-access/src/index.ts`](../packages/api/remote-access/src/index.ts) |
|
||||
| `@deepseek-ai/dsh-api-remotes` | `typertGateway` | [`packages/api/remotes/src/index.ts`](../packages/api/remotes/src/index.ts) |
|
||||
|
||||
@@ -24,7 +24,7 @@ function PlanChip({ useProjection }: PlanChipProps) {
|
||||
}
|
||||
|
||||
// The computation is a registered unit; the registry drives it and publishes changes.
|
||||
const planDefinition: ProjectionDefinition<'plan'> = {
|
||||
const planDefinition: ProjectionDefinition<'plan', typeof emptyPlan> = {
|
||||
key: 'plan',
|
||||
stateVersion: 1,
|
||||
init: () => emptyPlan,
|
||||
|
||||
@@ -24,7 +24,7 @@ function PlanChip({ useProjection }: PlanChipProps) {
|
||||
}
|
||||
|
||||
// The computation is a registered unit; the registry drives it and publishes changes.
|
||||
const planDefinition: ProjectionDefinition<'plan'> = {
|
||||
const planDefinition: ProjectionDefinition<'plan', typeof emptyPlan> = {
|
||||
key: 'plan',
|
||||
stateVersion: 1,
|
||||
init: () => emptyPlan,
|
||||
|
||||
@@ -93,16 +93,18 @@ The owning package decides and registers its unit, and the controller only reads
|
||||
|
||||
```ts ignore-check
|
||||
// packages/workspace/<feature>/src/index.ts: the rule lives with the feature it serves.
|
||||
ctx.sessionProjections.register({
|
||||
export const durablePageProjectionDefinition = {
|
||||
key: 'durablePage',
|
||||
stateSchema: durablePageState,
|
||||
init: () => ({ durable: false }),
|
||||
apply: applyDurablePage,
|
||||
stateVersion: 1,
|
||||
})
|
||||
}
|
||||
await ctx.sessionProjections.register(durablePageProjectionDefinition)
|
||||
|
||||
// packages/api/session-controller: generic orchestration reads the projected value.
|
||||
const { durable } = ctx.sessionProjections.stateOf(session, 'durablePage')
|
||||
const durablePage = await ctx.sessionProjections.register(durablePageProjectionDefinition)
|
||||
const { durable } = durablePage.stateOf(session)
|
||||
```
|
||||
|
||||
The wrong version decides the rule inside the shared layer:
|
||||
|
||||
@@ -93,16 +93,18 @@ type SessionRemote = { list(): ...; rename(id: string): ... } // hand-written
|
||||
|
||||
```ts ignore-check
|
||||
// packages/workspace/<feature>/src/index.ts: the rule lives with the feature it serves.
|
||||
ctx.sessionProjections.register({
|
||||
export const durablePageProjectionDefinition = {
|
||||
key: 'durablePage',
|
||||
stateSchema: durablePageState,
|
||||
init: () => ({ durable: false }),
|
||||
apply: applyDurablePage,
|
||||
stateVersion: 1,
|
||||
})
|
||||
}
|
||||
await ctx.sessionProjections.register(durablePageProjectionDefinition)
|
||||
|
||||
// packages/api/session-controller: generic orchestration reads the projected value.
|
||||
const { durable } = ctx.sessionProjections.stateOf(session, 'durablePage')
|
||||
const durablePage = await ctx.sessionProjections.register(durablePageProjectionDefinition)
|
||||
const { durable } = durablePage.stateOf(session)
|
||||
```
|
||||
|
||||
反例把这条规则的决定放进共享层:
|
||||
|
||||
@@ -8,8 +8,8 @@ This matrix shows which packages dispatch each harness-owned event and which pac
|
||||
<!-- BEGIN GENERATED event-producer-consumer:events -->
|
||||
| Event | Mode | Declared in | Dispatchers | Listeners |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `agent-loop/config-start-failed` | `emit` | [`packages/core/agent-loop/src/index.ts:249`](../packages/core/agent-loop/src/index.ts) | [`agent-loop`](../packages/core/agent-loop) (`events.dispatch`) | - |
|
||||
| `agent-preset/selected` | `emit` | [`packages/preset/agent-preset-registry/src/types.ts:69`](../packages/preset/agent-preset-registry/src/types.ts) | [`agent-preset-registry`](../packages/preset/agent-preset-registry) (`emit`) | `remotes` |
|
||||
| `agent-loop/config-start-failed` | `emit` | [`packages/core/agent-loop/src/index.ts:196`](../packages/core/agent-loop/src/index.ts) | [`agent-loop`](../packages/core/agent-loop) (`events.dispatch`) | - |
|
||||
| `agent-preset/selected` | `emit` | [`packages/preset/agent-preset-registry/src/types.ts:66`](../packages/preset/agent-preset-registry/src/types.ts) | [`agent-preset-registry`](../packages/preset/agent-preset-registry) (`emit`) | `remotes` |
|
||||
| `agent/assistant-stream` | `emit` | [`packages/core/agent/src/runtime-types.ts:363`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`headless`](../packages/bundle/headless), `session-controller` |
|
||||
| `agent/created` | `serial` | [`packages/core/agent/src/runtime-types.ts:261`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`serial`) | `agent-team`, `browser-use-runtime`, `claude-code-mods`, [`file-reference-local`](../packages/context/file-reference-local), [`goal`](../packages/goal/goal), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`loader-smoke`](../packages/test-support/loader-smoke), `session-controller`, `tool-agent-team`, [`tool-subagent`](../packages/subagent/tool-subagent) |
|
||||
| `agent/disposed` | `emit` | [`packages/core/agent/src/runtime-types.ts:270`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`agent-loop`](../packages/core/agent-loop), `claude-code-mods`, [`file-reference-local`](../packages/context/file-reference-local), [`goal-round-driver`](../packages/goal/goal-round-driver), `session-controller`, [`subagent`](../packages/subagent/subagent), `tool-agent-team`, [`tool-subagent`](../packages/subagent/tool-subagent) |
|
||||
@@ -22,11 +22,11 @@ This matrix shows which packages dispatch each harness-owned event and which pac
|
||||
| `agent/request-error` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:353`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`compaction-basic`](../packages/compaction/compaction-basic), [`compaction-image-offload`](../packages/compaction/compaction-image-offload), [`llm-retry`](../packages/llm/llm-retry) |
|
||||
| `agent/status` | `emit` | [`packages/core/agent/src/runtime-types.ts:280`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), `agent-team`, [`compaction-basic`](../packages/compaction/compaction-basic), [`goal-round-driver`](../packages/goal/goal-round-driver), `server`, `session-controller` |
|
||||
| `agent/turn-stopping` | `serial` | [`packages/core/agent/src/runtime-types.ts:381`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`workspace-changes`](../packages/deliverables/workspace-changes) |
|
||||
| `api-session/activity` | `emit` | [`packages/api/session-controller/src/types.ts:634`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
|
||||
| `api-session/added` | `emit` | [`packages/api/session-controller/src/types.ts:614`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
|
||||
| `api-session/error` | `emit` | [`packages/api/session-controller/src/types.ts:641`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
|
||||
| `api-session/removed` | `emit` | [`packages/api/session-controller/src/types.ts:620`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
|
||||
| `api-session/status` | `emit` | [`packages/api/session-controller/src/types.ts:627`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
|
||||
| `api-session/activity` | `emit` | [`packages/api/session-controller/src/types.ts:626`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
|
||||
| `api-session/added` | `emit` | [`packages/api/session-controller/src/types.ts:606`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
|
||||
| `api-session/error` | `emit` | [`packages/api/session-controller/src/types.ts:633`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
|
||||
| `api-session/removed` | `emit` | [`packages/api/session-controller/src/types.ts:612`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
|
||||
| `api-session/status` | `emit` | [`packages/api/session-controller/src/types.ts:619`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
|
||||
| `app-boot/config-reload` | `emit` | [`packages/boot/app-boot/src/index.ts:54`](../packages/boot/app-boot/src/index.ts) | [`app-boot`](../packages/boot/app-boot) (`emit`) | [`settings`](../packages/settings/settings) |
|
||||
| `approval/request` | `waterfall` | [`packages/interaction/user-approval/src/types.ts:87`](../packages/interaction/user-approval/src/types.ts) | [`user-approval`](../packages/interaction/user-approval) (`waterfall`) | [`acp`](../packages/acp/acp), `remotes` |
|
||||
| `authorization/settled` | `emit` | [`packages/credentials/authorization/src/index.ts:57`](../packages/credentials/authorization/src/index.ts) | [`authorization`](../packages/credentials/authorization) (`events.dispatch`) | - |
|
||||
@@ -49,7 +49,7 @@ This matrix shows which packages dispatch each harness-owned event and which pac
|
||||
| `fs/edit-intent` | `waterfall` | [`packages/fs/fs/src/index.ts:67`](../packages/fs/fs/src/index.ts) | [`tool-fs`](../packages/fs/tool-fs) (`waterfall`), [`tool-str-replace-editor`](../packages/fs/tool-str-replace-editor) (`waterfall`) | [`fs-observation-policy`](../packages/fs/fs-observation-policy), [`workspace-changes`](../packages/deliverables/workspace-changes) |
|
||||
| `fs/observed` | `emit` | [`packages/fs/fs/src/index.ts:77`](../packages/fs/fs/src/index.ts) | [`tool-fs`](../packages/fs/tool-fs) (`emit`), [`tool-str-replace-editor`](../packages/fs/tool-str-replace-editor) (`emit`) | [`fs-observation-policy`](../packages/fs/fs-observation-policy), [`skill-filesystem`](../packages/skill/skill-filesystem), `workspace-files` |
|
||||
| `fs/write-intent` | `waterfall` | [`packages/fs/fs/src/index.ts:59`](../packages/fs/fs/src/index.ts) | [`tool-fs`](../packages/fs/tool-fs) (`waterfall`), [`tool-str-replace-editor`](../packages/fs/tool-str-replace-editor) (`waterfall`) | [`fs-observation-policy`](../packages/fs/fs-observation-policy), [`workspace-changes`](../packages/deliverables/workspace-changes) |
|
||||
| `goal/activation-changed` | `emit` | [`packages/goal/goal/src/types.ts:150`](../packages/goal/goal/src/types.ts) | [`goal`](../packages/goal/goal) (`emit`) | `remotes` |
|
||||
| `goal/activation-changed` | `emit` | [`packages/goal/goal/src/types.ts:147`](../packages/goal/goal/src/types.ts) | [`goal`](../packages/goal/goal) (`emit`) | `remotes` |
|
||||
| `goal/changed` | `emit` | [`packages/goal/goal/src/domain.ts:114`](../packages/goal/goal/src/domain.ts) | [`goal`](../packages/goal/goal) (`emit`) | [`goal-round-driver`](../packages/goal/goal-round-driver) |
|
||||
| `hmr/change` | `emit` | [`packages/boot/hmr/src/index.ts:35`](../packages/boot/hmr/src/index.ts) | [`hmr`](../packages/boot/hmr) (`emit`) | - |
|
||||
| `hmr/reload` | `emit` | [`packages/boot/hmr/src/index.ts:40`](../packages/boot/hmr/src/index.ts) | [`hmr`](../packages/boot/hmr) (`emit`) | - |
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
<!-- BEGIN GENERATED event-producer-consumer:events -->
|
||||
| Event | Mode | Declared in | Dispatchers | Listeners |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `agent-loop/config-start-failed` | `emit` | [`packages/core/agent-loop/src/index.ts:249`](../packages/core/agent-loop/src/index.ts) | [`agent-loop`](../packages/core/agent-loop) (`events.dispatch`) | - |
|
||||
| `agent-preset/selected` | `emit` | [`packages/preset/agent-preset-registry/src/types.ts:69`](../packages/preset/agent-preset-registry/src/types.ts) | [`agent-preset-registry`](../packages/preset/agent-preset-registry) (`emit`) | `remotes` |
|
||||
| `agent-loop/config-start-failed` | `emit` | [`packages/core/agent-loop/src/index.ts:196`](../packages/core/agent-loop/src/index.ts) | [`agent-loop`](../packages/core/agent-loop) (`events.dispatch`) | - |
|
||||
| `agent-preset/selected` | `emit` | [`packages/preset/agent-preset-registry/src/types.ts:66`](../packages/preset/agent-preset-registry/src/types.ts) | [`agent-preset-registry`](../packages/preset/agent-preset-registry) (`emit`) | `remotes` |
|
||||
| `agent/assistant-stream` | `emit` | [`packages/core/agent/src/runtime-types.ts:363`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`headless`](../packages/bundle/headless), `session-controller` |
|
||||
| `agent/created` | `serial` | [`packages/core/agent/src/runtime-types.ts:261`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`serial`) | `agent-team`, `browser-use-runtime`, `claude-code-mods`, [`file-reference-local`](../packages/context/file-reference-local), [`goal`](../packages/goal/goal), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`loader-smoke`](../packages/test-support/loader-smoke), `session-controller`, `tool-agent-team`, [`tool-subagent`](../packages/subagent/tool-subagent) |
|
||||
| `agent/disposed` | `emit` | [`packages/core/agent/src/runtime-types.ts:270`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`agent-loop`](../packages/core/agent-loop), `claude-code-mods`, [`file-reference-local`](../packages/context/file-reference-local), [`goal-round-driver`](../packages/goal/goal-round-driver), `session-controller`, [`subagent`](../packages/subagent/subagent), `tool-agent-team`, [`tool-subagent`](../packages/subagent/tool-subagent) |
|
||||
@@ -24,11 +24,11 @@
|
||||
| `agent/request-error` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:353`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`compaction-basic`](../packages/compaction/compaction-basic), [`compaction-image-offload`](../packages/compaction/compaction-image-offload), [`llm-retry`](../packages/llm/llm-retry) |
|
||||
| `agent/status` | `emit` | [`packages/core/agent/src/runtime-types.ts:280`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), `agent-team`, [`compaction-basic`](../packages/compaction/compaction-basic), [`goal-round-driver`](../packages/goal/goal-round-driver), `server`, `session-controller` |
|
||||
| `agent/turn-stopping` | `serial` | [`packages/core/agent/src/runtime-types.ts:381`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`workspace-changes`](../packages/deliverables/workspace-changes) |
|
||||
| `api-session/activity` | `emit` | [`packages/api/session-controller/src/types.ts:634`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
|
||||
| `api-session/added` | `emit` | [`packages/api/session-controller/src/types.ts:614`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
|
||||
| `api-session/error` | `emit` | [`packages/api/session-controller/src/types.ts:641`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
|
||||
| `api-session/removed` | `emit` | [`packages/api/session-controller/src/types.ts:620`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
|
||||
| `api-session/status` | `emit` | [`packages/api/session-controller/src/types.ts:627`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
|
||||
| `api-session/activity` | `emit` | [`packages/api/session-controller/src/types.ts:626`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
|
||||
| `api-session/added` | `emit` | [`packages/api/session-controller/src/types.ts:606`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
|
||||
| `api-session/error` | `emit` | [`packages/api/session-controller/src/types.ts:633`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
|
||||
| `api-session/removed` | `emit` | [`packages/api/session-controller/src/types.ts:612`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
|
||||
| `api-session/status` | `emit` | [`packages/api/session-controller/src/types.ts:619`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
|
||||
| `app-boot/config-reload` | `emit` | [`packages/boot/app-boot/src/index.ts:54`](../packages/boot/app-boot/src/index.ts) | [`app-boot`](../packages/boot/app-boot) (`emit`) | [`settings`](../packages/settings/settings) |
|
||||
| `approval/request` | `waterfall` | [`packages/interaction/user-approval/src/types.ts:87`](../packages/interaction/user-approval/src/types.ts) | [`user-approval`](../packages/interaction/user-approval) (`waterfall`) | [`acp`](../packages/acp/acp), `remotes` |
|
||||
| `authorization/settled` | `emit` | [`packages/credentials/authorization/src/index.ts:57`](../packages/credentials/authorization/src/index.ts) | [`authorization`](../packages/credentials/authorization) (`events.dispatch`) | - |
|
||||
@@ -51,7 +51,7 @@
|
||||
| `fs/edit-intent` | `waterfall` | [`packages/fs/fs/src/index.ts:67`](../packages/fs/fs/src/index.ts) | [`tool-fs`](../packages/fs/tool-fs) (`waterfall`), [`tool-str-replace-editor`](../packages/fs/tool-str-replace-editor) (`waterfall`) | [`fs-observation-policy`](../packages/fs/fs-observation-policy), [`workspace-changes`](../packages/deliverables/workspace-changes) |
|
||||
| `fs/observed` | `emit` | [`packages/fs/fs/src/index.ts:77`](../packages/fs/fs/src/index.ts) | [`tool-fs`](../packages/fs/tool-fs) (`emit`), [`tool-str-replace-editor`](../packages/fs/tool-str-replace-editor) (`emit`) | [`fs-observation-policy`](../packages/fs/fs-observation-policy), [`skill-filesystem`](../packages/skill/skill-filesystem), `workspace-files` |
|
||||
| `fs/write-intent` | `waterfall` | [`packages/fs/fs/src/index.ts:59`](../packages/fs/fs/src/index.ts) | [`tool-fs`](../packages/fs/tool-fs) (`waterfall`), [`tool-str-replace-editor`](../packages/fs/tool-str-replace-editor) (`waterfall`) | [`fs-observation-policy`](../packages/fs/fs-observation-policy), [`workspace-changes`](../packages/deliverables/workspace-changes) |
|
||||
| `goal/activation-changed` | `emit` | [`packages/goal/goal/src/types.ts:150`](../packages/goal/goal/src/types.ts) | [`goal`](../packages/goal/goal) (`emit`) | `remotes` |
|
||||
| `goal/activation-changed` | `emit` | [`packages/goal/goal/src/types.ts:147`](../packages/goal/goal/src/types.ts) | [`goal`](../packages/goal/goal) (`emit`) | `remotes` |
|
||||
| `goal/changed` | `emit` | [`packages/goal/goal/src/domain.ts:114`](../packages/goal/goal/src/domain.ts) | [`goal`](../packages/goal/goal) (`emit`) | [`goal-round-driver`](../packages/goal/goal-round-driver) |
|
||||
| `hmr/change` | `emit` | [`packages/boot/hmr/src/index.ts:35`](../packages/boot/hmr/src/index.ts) | [`hmr`](../packages/boot/hmr) (`emit`) | - |
|
||||
| `hmr/reload` | `emit` | [`packages/boot/hmr/src/index.ts:40`](../packages/boot/hmr/src/index.ts) | [`hmr`](../packages/boot/hmr) (`emit`) | - |
|
||||
|
||||
+8
-16
@@ -809,7 +809,6 @@ flowchart TD
|
||||
pkg_terminal_bash --> pkg_sandbox
|
||||
pkg_terminal_bash --> pkg_sandbox_policy
|
||||
pkg_terminal_bash --> pkg_session
|
||||
pkg_terminal_bash --> pkg_session_projection
|
||||
pkg_terminal_bash --> pkg_subprocess
|
||||
pkg_terminal_bash --> pkg_terminal
|
||||
pkg_terminal_bash --> pkg_working_directory
|
||||
@@ -826,7 +825,6 @@ flowchart TD
|
||||
pkg_tool_goal --> pkg_goal
|
||||
pkg_tool_goal --> pkg_llm
|
||||
pkg_tool_goal --> pkg_session
|
||||
pkg_tool_goal --> pkg_session_projection
|
||||
pkg_tool_goal --> pkg_system_prompt
|
||||
pkg_tool_goal --> pkg_tools
|
||||
pkg_tool_fs --> pkg_attachment
|
||||
@@ -886,7 +884,6 @@ flowchart TD
|
||||
pkg_agent_instructions --> pkg_home_paths
|
||||
pkg_agent_instructions --> pkg_llm
|
||||
pkg_agent_instructions --> pkg_session
|
||||
pkg_agent_instructions --> pkg_session_projection
|
||||
pkg_agent_instructions --> pkg_tools
|
||||
pkg_agent_instructions --> pkg_working_directory
|
||||
pkg_file_reference_local --> pkg_agent
|
||||
@@ -898,7 +895,6 @@ flowchart TD
|
||||
pkg_tool_present --> pkg_fs
|
||||
pkg_tool_present --> pkg_llm
|
||||
pkg_tool_present --> pkg_session
|
||||
pkg_tool_present --> pkg_session_projection
|
||||
pkg_tool_present --> pkg_tools
|
||||
pkg_tool_present --> pkg_working_directory
|
||||
pkg_workspace_changes --> pkg_agent
|
||||
@@ -943,7 +939,6 @@ flowchart TD
|
||||
pkg_hooks_codex --> pkg_hook_protocol
|
||||
pkg_hooks_codex --> pkg_llm
|
||||
pkg_hooks_codex --> pkg_session
|
||||
pkg_hooks_codex --> pkg_session_projection
|
||||
pkg_hooks_codex --> pkg_tools
|
||||
pkg_hooks_codex --> pkg_working_directory
|
||||
pkg_session_title_all_prompts_llm --> pkg_llm
|
||||
@@ -1219,7 +1214,6 @@ flowchart TD
|
||||
pkg_experimental_claude_code_mods --> pkg_llm
|
||||
pkg_experimental_claude_code_mods --> pkg_scope
|
||||
pkg_experimental_claude_code_mods --> pkg_session
|
||||
pkg_experimental_claude_code_mods --> pkg_session_projection
|
||||
pkg_experimental_claude_code_mods --> pkg_storage_domain
|
||||
pkg_experimental_claude_code_mods --> pkg_subprocess
|
||||
pkg_experimental_claude_code_mods --> pkg_token_meter
|
||||
@@ -1230,7 +1224,6 @@ flowchart TD
|
||||
pkg_tool_session_query --> pkg_agent
|
||||
pkg_tool_session_query --> pkg_llm
|
||||
pkg_tool_session_query --> pkg_session
|
||||
pkg_tool_session_query --> pkg_session_projection
|
||||
pkg_tool_session_query --> pkg_session_query
|
||||
pkg_tool_session_query --> pkg_system_prompt
|
||||
pkg_tool_session_query --> pkg_timeout
|
||||
@@ -1358,7 +1351,6 @@ flowchart TD
|
||||
pkg_hooks_claude_code --> pkg_hook_protocol
|
||||
pkg_hooks_claude_code --> pkg_llm
|
||||
pkg_hooks_claude_code --> pkg_session
|
||||
pkg_hooks_claude_code --> pkg_session_projection
|
||||
pkg_hooks_claude_code --> pkg_subagent
|
||||
pkg_hooks_claude_code --> pkg_tools
|
||||
pkg_hooks_claude_code --> pkg_working_directory
|
||||
@@ -1688,10 +1680,10 @@ flowchart TD
|
||||
| [`session-title-llm`](../packages/session/session-title-llm) | `session` | [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-title`](../packages/session/session-title), [`timeout`](../packages/util/timeout) |
|
||||
| [`bash-sandbox`](../packages/shell/bash-sandbox) | `shell` | [`bash-local`](../packages/shell/bash-local), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`shell`](../packages/shell/shell) |
|
||||
| [`pwsh-sandbox`](../packages/shell/pwsh-sandbox) | `shell` | [`pwsh-local`](../packages/shell/pwsh-local), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`shell`](../packages/shell/shell) |
|
||||
| [`terminal-bash`](../packages/terminal/terminal-bash) | `terminal` | [`agent`](../packages/core/agent), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`subprocess`](../packages/subprocess/subprocess), [`terminal`](../packages/terminal/terminal), [`working-directory`](../packages/session/working-directory) |
|
||||
| [`terminal-bash`](../packages/terminal/terminal-bash) | `terminal` | [`agent`](../packages/core/agent), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`subprocess`](../packages/subprocess/subprocess), [`terminal`](../packages/terminal/terminal), [`working-directory`](../packages/session/working-directory) |
|
||||
| [`agent-loop`](../packages/core/agent-loop) | `core` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
|
||||
| [`agent-tool-presentation`](../packages/core/agent-tool-presentation) | `core` | [`tools`](../packages/core/tools) |
|
||||
| [`tool-goal`](../packages/goal/tool-goal) | `goal` | [`agent`](../packages/core/agent), [`goal`](../packages/goal/goal), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
|
||||
| [`tool-goal`](../packages/goal/tool-goal) | `goal` | [`agent`](../packages/core/agent), [`goal`](../packages/goal/goal), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
|
||||
| [`tool-fs`](../packages/fs/tool-fs) | `fs` | [`attachment`](../packages/attachment/attachment), [`fs`](../packages/fs/fs), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-approval`](../packages/interaction/user-approval), [`working-directory`](../packages/session/working-directory) |
|
||||
| [`tool-fs-search`](../packages/fs/tool-fs-search) | `fs` | [`llm`](../packages/llm/llm), [`output-retention`](../packages/util/output-retention), [`session`](../packages/core/session), [`spill`](../packages/spill/spill), [`subprocess`](../packages/subprocess/subprocess), [`system-prompt`](../packages/core/system-prompt), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) |
|
||||
| [`tool-str-replace-editor`](../packages/fs/tool-str-replace-editor) | `fs` | [`fs`](../packages/fs/fs), [`output-retention`](../packages/util/output-retention), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`tools`](../packages/core/tools) |
|
||||
@@ -1702,9 +1694,9 @@ flowchart TD
|
||||
| [`plan-mode`](../packages/plan/plan-mode) | `plan` | [`agent`](../packages/core/agent), [`commands`](../packages/interaction/commands), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-questions`](../packages/interaction/user-questions) |
|
||||
| [`command-compact`](../packages/compaction/command-compact) | `compaction` | [`commands`](../packages/interaction/commands), [`compaction`](../packages/compaction/compaction) |
|
||||
| [`compaction-image-offload`](../packages/compaction/compaction-image-offload) | `compaction` | [`agent`](../packages/core/agent), [`compaction`](../packages/compaction/compaction), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
|
||||
| [`agent-instructions`](../packages/context/agent-instructions) | `context` | [`agent`](../packages/core/agent), [`fs`](../packages/fs/fs), [`home-paths`](../packages/util/home-paths), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) |
|
||||
| [`agent-instructions`](../packages/context/agent-instructions) | `context` | [`agent`](../packages/core/agent), [`fs`](../packages/fs/fs), [`home-paths`](../packages/util/home-paths), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) |
|
||||
| [`file-reference-local`](../packages/context/file-reference-local) | `context` | [`agent`](../packages/core/agent), [`file-reference`](../packages/context/file-reference), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) |
|
||||
| [`tool-present`](../packages/deliverables/tool-present) | `deliverables` | [`agent`](../packages/core/agent), [`fs`](../packages/fs/fs), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) |
|
||||
| [`tool-present`](../packages/deliverables/tool-present) | `deliverables` | [`agent`](../packages/core/agent), [`fs`](../packages/fs/fs), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) |
|
||||
| [`workspace-changes`](../packages/deliverables/workspace-changes) | `deliverables` | [`agent`](../packages/core/agent), [`fs`](../packages/fs/fs), [`session`](../packages/core/session), [`subprocess`](../packages/subprocess/subprocess), [`tools`](../packages/core/tools) |
|
||||
| [`cua`](../packages/experimental/cua) | `experimental` | [`agent`](../packages/core/agent), [`attachment`](../packages/attachment/attachment), [`computer-use`](../packages/computer-use/computer-use), [`llm`](../packages/llm/llm), [`subprocess`](../packages/subprocess/subprocess), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-questions`](../packages/interaction/user-questions) |
|
||||
| [`experimental-browser-use-chrome-devtools-mcp`](../packages/experimental/browser-use-chrome-devtools-mcp) | `experimental` | [`agent`](../packages/core/agent), [`browser-use`](../packages/browser-use/browser-use), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) |
|
||||
@@ -1713,7 +1705,7 @@ flowchart TD
|
||||
| [`experimental-computer-use-cua-driver-native`](../packages/experimental/computer-use-cua-driver-native) | `experimental` | [`computer-use`](../packages/computer-use/computer-use), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
|
||||
| [`experimental-cua-macos`](../packages/experimental/cua-macos) | `experimental` | [`agent`](../packages/core/agent), [`computer-use`](../packages/computer-use/computer-use), [`subprocess`](../packages/subprocess/subprocess), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-questions`](../packages/interaction/user-questions) |
|
||||
| [`experimental-tool-worktree`](../packages/experimental/tool-worktree) | `experimental` | [`experimental-worktree`](../packages/experimental/worktree), [`tools`](../packages/core/tools) |
|
||||
| [`hooks-codex`](../packages/experimental/hooks-codex) | `experimental` | [`agent`](../packages/core/agent), [`hook-protocol`](../packages/experimental/hook-protocol), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) |
|
||||
| [`hooks-codex`](../packages/experimental/hooks-codex) | `experimental` | [`agent`](../packages/core/agent), [`hook-protocol`](../packages/experimental/hook-protocol), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) |
|
||||
| [`session-title-all-prompts-llm`](../packages/experimental/session-title-all-prompts-llm) | `experimental` | [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-title`](../packages/session/session-title), [`session-title-llm`](../packages/session/session-title-llm) |
|
||||
| [`tool-terminal`](../packages/experimental/tool-terminal) | `experimental` | [`agent`](../packages/core/agent), [`jobs`](../packages/jobs/jobs), [`llm`](../packages/llm/llm), [`output-retention`](../packages/util/output-retention), [`system-prompt`](../packages/core/system-prompt), [`terminal`](../packages/terminal/terminal), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) |
|
||||
| [`cordis-host-runner`](../packages/extensions/cordis-host-runner) | `extensions` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`tools`](../packages/core/tools), [`typert-protocol`](../packages/typert/protocol) |
|
||||
@@ -1759,8 +1751,8 @@ flowchart TD
|
||||
| [`plugin-manager`](../packages/boot/plugin-manager) | `boot` | [`brand`](../packages/util/brand), [`hmr`](../packages/boot/hmr), [`host-plugin-inventory`](../packages/host/plugin-inventory), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`subprocess`](../packages/subprocess/subprocess), [`tools`](../packages/core/tools), [`typert-protocol`](../packages/typert/protocol), [`user-approval`](../packages/interaction/user-approval) |
|
||||
| [`compaction-tool-result-pruner`](../packages/compaction/compaction-tool-result-pruner) | `compaction` | [`compaction`](../packages/compaction/compaction), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`token-meter`](../packages/llm/token-meter) |
|
||||
| [`experimental-browser-use-runtime`](../packages/experimental/browser-use-runtime) | `experimental` | [`agent`](../packages/core/agent), [`browser-use`](../packages/browser-use/browser-use), [`mcp-client`](../packages/mcp/mcp-client), [`scope`](../packages/core/scope), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) |
|
||||
| [`experimental-claude-code-mods`](../packages/experimental/claude-code-mods) | `experimental` | [`agent`](../packages/core/agent), [`commands`](../packages/interaction/commands), [`fs`](../packages/fs/fs), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`storage-domain`](../packages/storage/storage-domain), [`subprocess`](../packages/subprocess/subprocess), [`token-meter`](../packages/llm/token-meter), [`tools`](../packages/core/tools), [`typert-protocol`](../packages/typert/protocol), [`user-questions`](../packages/interaction/user-questions), [`working-directory`](../packages/session/working-directory) |
|
||||
| [`tool-session-query`](../packages/experimental/tool-session-query) | `experimental` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`session-query`](../packages/session-query/session-query), [`system-prompt`](../packages/core/system-prompt), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) |
|
||||
| [`experimental-claude-code-mods`](../packages/experimental/claude-code-mods) | `experimental` | [`agent`](../packages/core/agent), [`commands`](../packages/interaction/commands), [`fs`](../packages/fs/fs), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`storage-domain`](../packages/storage/storage-domain), [`subprocess`](../packages/subprocess/subprocess), [`token-meter`](../packages/llm/token-meter), [`tools`](../packages/core/tools), [`typert-protocol`](../packages/typert/protocol), [`user-questions`](../packages/interaction/user-questions), [`working-directory`](../packages/session/working-directory) |
|
||||
| [`tool-session-query`](../packages/experimental/tool-session-query) | `experimental` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-query`](../packages/session-query/session-query), [`system-prompt`](../packages/core/system-prompt), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) |
|
||||
| [`webhook-github`](../packages/experimental/webhook-github) | `experimental` | [`credentials`](../packages/credentials/credentials), [`host-webserver`](../packages/host/webserver), [`session`](../packages/core/session), [`webhook`](../packages/experimental/webhook) |
|
||||
| [`goal-round-driver`](../packages/goal/goal-round-driver) | `goal` | [`agent`](../packages/core/agent), [`goal`](../packages/goal/goal), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent) |
|
||||
| [`subagent-acp`](../packages/subagent/subagent-acp) | `subagent` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
|
||||
@@ -1778,7 +1770,7 @@ flowchart TD
|
||||
| [`experimental-agent-team`](../packages/experimental/agent-team) | `experimental` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`subagent`](../packages/subagent/subagent) |
|
||||
| [`experimental-subagent-oar`](../packages/experimental/subagent-oar) | `experimental` | [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subprocess`](../packages/subprocess/subprocess) |
|
||||
| [`experimental-tool-subagent-oar`](../packages/experimental/tool-subagent-oar) | `experimental` | [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools) |
|
||||
| [`hooks-claude-code`](../packages/experimental/hooks-claude-code) | `experimental` | [`agent`](../packages/core/agent), [`hook-protocol`](../packages/experimental/hook-protocol), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) |
|
||||
| [`hooks-claude-code`](../packages/experimental/hooks-claude-code) | `experimental` | [`agent`](../packages/core/agent), [`hook-protocol`](../packages/experimental/hook-protocol), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) |
|
||||
| [`tool-ralph`](../packages/experimental/tool-ralph) | `experimental` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`subagent`](../packages/subagent/subagent), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`workflow`](../packages/workflow/workflow) |
|
||||
| [`sdk-protocol`](../packages/sdk/protocol) | `sdk` | [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent) |
|
||||
| [`workflow-ptc`](../packages/workflow/workflow-ptc) | `workflow` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`ptc-runtime`](../packages/ptc-runtime/ptc-runtime), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools), [`workflow`](../packages/workflow/workflow), [`working-directory`](../packages/session/working-directory) |
|
||||
|
||||
+8
-16
@@ -811,7 +811,6 @@ flowchart TD
|
||||
pkg_terminal_bash --> pkg_sandbox
|
||||
pkg_terminal_bash --> pkg_sandbox_policy
|
||||
pkg_terminal_bash --> pkg_session
|
||||
pkg_terminal_bash --> pkg_session_projection
|
||||
pkg_terminal_bash --> pkg_subprocess
|
||||
pkg_terminal_bash --> pkg_terminal
|
||||
pkg_terminal_bash --> pkg_working_directory
|
||||
@@ -828,7 +827,6 @@ flowchart TD
|
||||
pkg_tool_goal --> pkg_goal
|
||||
pkg_tool_goal --> pkg_llm
|
||||
pkg_tool_goal --> pkg_session
|
||||
pkg_tool_goal --> pkg_session_projection
|
||||
pkg_tool_goal --> pkg_system_prompt
|
||||
pkg_tool_goal --> pkg_tools
|
||||
pkg_tool_fs --> pkg_attachment
|
||||
@@ -888,7 +886,6 @@ flowchart TD
|
||||
pkg_agent_instructions --> pkg_home_paths
|
||||
pkg_agent_instructions --> pkg_llm
|
||||
pkg_agent_instructions --> pkg_session
|
||||
pkg_agent_instructions --> pkg_session_projection
|
||||
pkg_agent_instructions --> pkg_tools
|
||||
pkg_agent_instructions --> pkg_working_directory
|
||||
pkg_file_reference_local --> pkg_agent
|
||||
@@ -900,7 +897,6 @@ flowchart TD
|
||||
pkg_tool_present --> pkg_fs
|
||||
pkg_tool_present --> pkg_llm
|
||||
pkg_tool_present --> pkg_session
|
||||
pkg_tool_present --> pkg_session_projection
|
||||
pkg_tool_present --> pkg_tools
|
||||
pkg_tool_present --> pkg_working_directory
|
||||
pkg_workspace_changes --> pkg_agent
|
||||
@@ -945,7 +941,6 @@ flowchart TD
|
||||
pkg_hooks_codex --> pkg_hook_protocol
|
||||
pkg_hooks_codex --> pkg_llm
|
||||
pkg_hooks_codex --> pkg_session
|
||||
pkg_hooks_codex --> pkg_session_projection
|
||||
pkg_hooks_codex --> pkg_tools
|
||||
pkg_hooks_codex --> pkg_working_directory
|
||||
pkg_session_title_all_prompts_llm --> pkg_llm
|
||||
@@ -1221,7 +1216,6 @@ flowchart TD
|
||||
pkg_experimental_claude_code_mods --> pkg_llm
|
||||
pkg_experimental_claude_code_mods --> pkg_scope
|
||||
pkg_experimental_claude_code_mods --> pkg_session
|
||||
pkg_experimental_claude_code_mods --> pkg_session_projection
|
||||
pkg_experimental_claude_code_mods --> pkg_storage_domain
|
||||
pkg_experimental_claude_code_mods --> pkg_subprocess
|
||||
pkg_experimental_claude_code_mods --> pkg_token_meter
|
||||
@@ -1232,7 +1226,6 @@ flowchart TD
|
||||
pkg_tool_session_query --> pkg_agent
|
||||
pkg_tool_session_query --> pkg_llm
|
||||
pkg_tool_session_query --> pkg_session
|
||||
pkg_tool_session_query --> pkg_session_projection
|
||||
pkg_tool_session_query --> pkg_session_query
|
||||
pkg_tool_session_query --> pkg_system_prompt
|
||||
pkg_tool_session_query --> pkg_timeout
|
||||
@@ -1360,7 +1353,6 @@ flowchart TD
|
||||
pkg_hooks_claude_code --> pkg_hook_protocol
|
||||
pkg_hooks_claude_code --> pkg_llm
|
||||
pkg_hooks_claude_code --> pkg_session
|
||||
pkg_hooks_claude_code --> pkg_session_projection
|
||||
pkg_hooks_claude_code --> pkg_subagent
|
||||
pkg_hooks_claude_code --> pkg_tools
|
||||
pkg_hooks_claude_code --> pkg_working_directory
|
||||
@@ -1690,10 +1682,10 @@ flowchart TD
|
||||
| [`session-title-llm`](../packages/session/session-title-llm) | `session` | [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-title`](../packages/session/session-title), [`timeout`](../packages/util/timeout) |
|
||||
| [`bash-sandbox`](../packages/shell/bash-sandbox) | `shell` | [`bash-local`](../packages/shell/bash-local), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`shell`](../packages/shell/shell) |
|
||||
| [`pwsh-sandbox`](../packages/shell/pwsh-sandbox) | `shell` | [`pwsh-local`](../packages/shell/pwsh-local), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`shell`](../packages/shell/shell) |
|
||||
| [`terminal-bash`](../packages/terminal/terminal-bash) | `terminal` | [`agent`](../packages/core/agent), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`subprocess`](../packages/subprocess/subprocess), [`terminal`](../packages/terminal/terminal), [`working-directory`](../packages/session/working-directory) |
|
||||
| [`terminal-bash`](../packages/terminal/terminal-bash) | `terminal` | [`agent`](../packages/core/agent), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`subprocess`](../packages/subprocess/subprocess), [`terminal`](../packages/terminal/terminal), [`working-directory`](../packages/session/working-directory) |
|
||||
| [`agent-loop`](../packages/core/agent-loop) | `core` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
|
||||
| [`agent-tool-presentation`](../packages/core/agent-tool-presentation) | `core` | [`tools`](../packages/core/tools) |
|
||||
| [`tool-goal`](../packages/goal/tool-goal) | `goal` | [`agent`](../packages/core/agent), [`goal`](../packages/goal/goal), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
|
||||
| [`tool-goal`](../packages/goal/tool-goal) | `goal` | [`agent`](../packages/core/agent), [`goal`](../packages/goal/goal), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
|
||||
| [`tool-fs`](../packages/fs/tool-fs) | `fs` | [`attachment`](../packages/attachment/attachment), [`fs`](../packages/fs/fs), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-approval`](../packages/interaction/user-approval), [`working-directory`](../packages/session/working-directory) |
|
||||
| [`tool-fs-search`](../packages/fs/tool-fs-search) | `fs` | [`llm`](../packages/llm/llm), [`output-retention`](../packages/util/output-retention), [`session`](../packages/core/session), [`spill`](../packages/spill/spill), [`subprocess`](../packages/subprocess/subprocess), [`system-prompt`](../packages/core/system-prompt), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) |
|
||||
| [`tool-str-replace-editor`](../packages/fs/tool-str-replace-editor) | `fs` | [`fs`](../packages/fs/fs), [`output-retention`](../packages/util/output-retention), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`tools`](../packages/core/tools) |
|
||||
@@ -1704,9 +1696,9 @@ flowchart TD
|
||||
| [`plan-mode`](../packages/plan/plan-mode) | `plan` | [`agent`](../packages/core/agent), [`commands`](../packages/interaction/commands), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-questions`](../packages/interaction/user-questions) |
|
||||
| [`command-compact`](../packages/compaction/command-compact) | `compaction` | [`commands`](../packages/interaction/commands), [`compaction`](../packages/compaction/compaction) |
|
||||
| [`compaction-image-offload`](../packages/compaction/compaction-image-offload) | `compaction` | [`agent`](../packages/core/agent), [`compaction`](../packages/compaction/compaction), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
|
||||
| [`agent-instructions`](../packages/context/agent-instructions) | `context` | [`agent`](../packages/core/agent), [`fs`](../packages/fs/fs), [`home-paths`](../packages/util/home-paths), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) |
|
||||
| [`agent-instructions`](../packages/context/agent-instructions) | `context` | [`agent`](../packages/core/agent), [`fs`](../packages/fs/fs), [`home-paths`](../packages/util/home-paths), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) |
|
||||
| [`file-reference-local`](../packages/context/file-reference-local) | `context` | [`agent`](../packages/core/agent), [`file-reference`](../packages/context/file-reference), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) |
|
||||
| [`tool-present`](../packages/deliverables/tool-present) | `deliverables` | [`agent`](../packages/core/agent), [`fs`](../packages/fs/fs), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) |
|
||||
| [`tool-present`](../packages/deliverables/tool-present) | `deliverables` | [`agent`](../packages/core/agent), [`fs`](../packages/fs/fs), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) |
|
||||
| [`workspace-changes`](../packages/deliverables/workspace-changes) | `deliverables` | [`agent`](../packages/core/agent), [`fs`](../packages/fs/fs), [`session`](../packages/core/session), [`subprocess`](../packages/subprocess/subprocess), [`tools`](../packages/core/tools) |
|
||||
| [`cua`](../packages/experimental/cua) | `experimental` | [`agent`](../packages/core/agent), [`attachment`](../packages/attachment/attachment), [`computer-use`](../packages/computer-use/computer-use), [`llm`](../packages/llm/llm), [`subprocess`](../packages/subprocess/subprocess), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-questions`](../packages/interaction/user-questions) |
|
||||
| [`experimental-browser-use-chrome-devtools-mcp`](../packages/experimental/browser-use-chrome-devtools-mcp) | `experimental` | [`agent`](../packages/core/agent), [`browser-use`](../packages/browser-use/browser-use), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) |
|
||||
@@ -1715,7 +1707,7 @@ flowchart TD
|
||||
| [`experimental-computer-use-cua-driver-native`](../packages/experimental/computer-use-cua-driver-native) | `experimental` | [`computer-use`](../packages/computer-use/computer-use), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
|
||||
| [`experimental-cua-macos`](../packages/experimental/cua-macos) | `experimental` | [`agent`](../packages/core/agent), [`computer-use`](../packages/computer-use/computer-use), [`subprocess`](../packages/subprocess/subprocess), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-questions`](../packages/interaction/user-questions) |
|
||||
| [`experimental-tool-worktree`](../packages/experimental/tool-worktree) | `experimental` | [`experimental-worktree`](../packages/experimental/worktree), [`tools`](../packages/core/tools) |
|
||||
| [`hooks-codex`](../packages/experimental/hooks-codex) | `experimental` | [`agent`](../packages/core/agent), [`hook-protocol`](../packages/experimental/hook-protocol), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) |
|
||||
| [`hooks-codex`](../packages/experimental/hooks-codex) | `experimental` | [`agent`](../packages/core/agent), [`hook-protocol`](../packages/experimental/hook-protocol), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) |
|
||||
| [`session-title-all-prompts-llm`](../packages/experimental/session-title-all-prompts-llm) | `experimental` | [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-title`](../packages/session/session-title), [`session-title-llm`](../packages/session/session-title-llm) |
|
||||
| [`tool-terminal`](../packages/experimental/tool-terminal) | `experimental` | [`agent`](../packages/core/agent), [`jobs`](../packages/jobs/jobs), [`llm`](../packages/llm/llm), [`output-retention`](../packages/util/output-retention), [`system-prompt`](../packages/core/system-prompt), [`terminal`](../packages/terminal/terminal), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) |
|
||||
| [`cordis-host-runner`](../packages/extensions/cordis-host-runner) | `extensions` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`tools`](../packages/core/tools), [`typert-protocol`](../packages/typert/protocol) |
|
||||
@@ -1761,8 +1753,8 @@ flowchart TD
|
||||
| [`plugin-manager`](../packages/boot/plugin-manager) | `boot` | [`brand`](../packages/util/brand), [`hmr`](../packages/boot/hmr), [`host-plugin-inventory`](../packages/host/plugin-inventory), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`subprocess`](../packages/subprocess/subprocess), [`tools`](../packages/core/tools), [`typert-protocol`](../packages/typert/protocol), [`user-approval`](../packages/interaction/user-approval) |
|
||||
| [`compaction-tool-result-pruner`](../packages/compaction/compaction-tool-result-pruner) | `compaction` | [`compaction`](../packages/compaction/compaction), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`token-meter`](../packages/llm/token-meter) |
|
||||
| [`experimental-browser-use-runtime`](../packages/experimental/browser-use-runtime) | `experimental` | [`agent`](../packages/core/agent), [`browser-use`](../packages/browser-use/browser-use), [`mcp-client`](../packages/mcp/mcp-client), [`scope`](../packages/core/scope), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) |
|
||||
| [`experimental-claude-code-mods`](../packages/experimental/claude-code-mods) | `experimental` | [`agent`](../packages/core/agent), [`commands`](../packages/interaction/commands), [`fs`](../packages/fs/fs), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`storage-domain`](../packages/storage/storage-domain), [`subprocess`](../packages/subprocess/subprocess), [`token-meter`](../packages/llm/token-meter), [`tools`](../packages/core/tools), [`typert-protocol`](../packages/typert/protocol), [`user-questions`](../packages/interaction/user-questions), [`working-directory`](../packages/session/working-directory) |
|
||||
| [`tool-session-query`](../packages/experimental/tool-session-query) | `experimental` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`session-query`](../packages/session-query/session-query), [`system-prompt`](../packages/core/system-prompt), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) |
|
||||
| [`experimental-claude-code-mods`](../packages/experimental/claude-code-mods) | `experimental` | [`agent`](../packages/core/agent), [`commands`](../packages/interaction/commands), [`fs`](../packages/fs/fs), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`storage-domain`](../packages/storage/storage-domain), [`subprocess`](../packages/subprocess/subprocess), [`token-meter`](../packages/llm/token-meter), [`tools`](../packages/core/tools), [`typert-protocol`](../packages/typert/protocol), [`user-questions`](../packages/interaction/user-questions), [`working-directory`](../packages/session/working-directory) |
|
||||
| [`tool-session-query`](../packages/experimental/tool-session-query) | `experimental` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-query`](../packages/session-query/session-query), [`system-prompt`](../packages/core/system-prompt), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) |
|
||||
| [`webhook-github`](../packages/experimental/webhook-github) | `experimental` | [`credentials`](../packages/credentials/credentials), [`host-webserver`](../packages/host/webserver), [`session`](../packages/core/session), [`webhook`](../packages/experimental/webhook) |
|
||||
| [`goal-round-driver`](../packages/goal/goal-round-driver) | `goal` | [`agent`](../packages/core/agent), [`goal`](../packages/goal/goal), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent) |
|
||||
| [`subagent-acp`](../packages/subagent/subagent-acp) | `subagent` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
|
||||
@@ -1780,7 +1772,7 @@ flowchart TD
|
||||
| [`experimental-agent-team`](../packages/experimental/agent-team) | `experimental` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`subagent`](../packages/subagent/subagent) |
|
||||
| [`experimental-subagent-oar`](../packages/experimental/subagent-oar) | `experimental` | [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subprocess`](../packages/subprocess/subprocess) |
|
||||
| [`experimental-tool-subagent-oar`](../packages/experimental/tool-subagent-oar) | `experimental` | [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools) |
|
||||
| [`hooks-claude-code`](../packages/experimental/hooks-claude-code) | `experimental` | [`agent`](../packages/core/agent), [`hook-protocol`](../packages/experimental/hook-protocol), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) |
|
||||
| [`hooks-claude-code`](../packages/experimental/hooks-claude-code) | `experimental` | [`agent`](../packages/core/agent), [`hook-protocol`](../packages/experimental/hook-protocol), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools), [`working-directory`](../packages/session/working-directory) |
|
||||
| [`tool-ralph`](../packages/experimental/tool-ralph) | `experimental` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`subagent`](../packages/subagent/subagent), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`workflow`](../packages/workflow/workflow) |
|
||||
| [`sdk-protocol`](../packages/sdk/protocol) | `sdk` | [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent) |
|
||||
| [`workflow-ptc`](../packages/workflow/workflow-ptc) | `workflow` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`ptc-runtime`](../packages/ptc-runtime/ptc-runtime), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools), [`workflow`](../packages/workflow/workflow), [`working-directory`](../packages/session/working-directory) |
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
en: 9188862058c6986b
|
||||
zh: 9188862058c6986b
|
||||
/session-persistence-event-catalog/events/agent/agent-inbox-spliced-log-only:
|
||||
en: bc3be962bc8738b1
|
||||
zh: e538aa8ff144468f
|
||||
en: 8309b4babad90744
|
||||
zh: b344ac640d79947e
|
||||
/session-persistence-event-catalog/events/agent-preset:
|
||||
en: 49891ca9be5ac123
|
||||
zh: 49891ca9be5ac123
|
||||
@@ -141,14 +141,14 @@
|
||||
en: 9edf4ef31e60e880
|
||||
zh: 9edf4ef31e60e880
|
||||
/session-persistence-event-catalog/events/model/model-selection-log-only:
|
||||
en: 5332aa18683a642f
|
||||
zh: 025aa9bd6c4e022f
|
||||
en: 06589ec99fb2bb40
|
||||
zh: 3b78f0418d44609b
|
||||
/session-persistence-event-catalog/events/permission:
|
||||
en: 19917bc5438e60af
|
||||
zh: 19917bc5438e60af
|
||||
/session-persistence-event-catalog/events/permission/permission-preset-log-only:
|
||||
en: 41de5f6e1dad8620
|
||||
zh: 92cb5d5e87a23405
|
||||
en: 54423cb464a0a495
|
||||
zh: 8b3a9595255184f0
|
||||
/session-persistence-event-catalog/events/plan:
|
||||
en: 4294f78d26ed985b
|
||||
zh: 4294f78d26ed985b
|
||||
@@ -1146,8 +1146,8 @@
|
||||
en: 3b677f021d7dd4d3
|
||||
zh: 2a875742b22879a2
|
||||
/session-persistence-event-catalog/resolved-persistence-types/modelselection:
|
||||
en: f75d48e8c151b06d
|
||||
zh: c0f0dc5f69465285
|
||||
en: bf5990688bae50ac
|
||||
zh: 9212bca7aa7d31e0
|
||||
/session-persistence-event-catalog/resolved-persistence-types/oneshotscheduledispatchchange:
|
||||
en: a3300babe57b6b67
|
||||
zh: 4aaf06a149b399c3
|
||||
@@ -1347,8 +1347,8 @@
|
||||
en: 2786fef560851bb4
|
||||
zh: 5d787475356b2606
|
||||
/session-persistence-event-catalog/resolved-persistence-types/string:
|
||||
en: 596f0a10b5a493b9
|
||||
zh: 9a2dd1fbe7c29141
|
||||
en: 41c03b03d1bd5a44
|
||||
zh: 1b0e430af6c140b3
|
||||
/session-persistence-event-catalog/resolved-persistence-types/string~2:
|
||||
en: 0089620bf4638263
|
||||
zh: 1922bef1713094bb
|
||||
@@ -1524,8 +1524,8 @@
|
||||
en: 8a9199b1784c70f5
|
||||
zh: 76c98bc74686fda0
|
||||
/session-persistence-event-catalog/resolved-persistence-types/inserted-outcome-removedcount-start:
|
||||
en: e77d5d524dd8b4ea
|
||||
zh: 4dac28dda78a510f
|
||||
en: 452ff7ab39ffb3a9
|
||||
zh: 33af56489f0a45bc
|
||||
/session-persistence-event-catalog/resolved-persistence-types/interrupted-message-step-stream:
|
||||
en: ec10cd8e9c202a81
|
||||
zh: e08f859bcb13f0d4
|
||||
@@ -1833,8 +1833,8 @@
|
||||
en: 809a23c25ded8497
|
||||
zh: e9cd23e281f8d31f
|
||||
/session-persistence-event-catalog/resolved-persistence-types/kind-user:
|
||||
en: a58f339332877b15
|
||||
zh: b7406af7d8197b64
|
||||
en: 53b7abac679ee252
|
||||
zh: 63eb6a015986ef6d
|
||||
/session-persistence-event-catalog/resolved-persistence-types/kind-user-approval:
|
||||
en: a52d2dfbaf844127
|
||||
zh: 4c5306ca40f20873
|
||||
@@ -1887,8 +1887,8 @@
|
||||
en: 00f1d435f82d2753
|
||||
zh: c2ab108ad2748a6c
|
||||
/session-persistence-event-catalog/resolved-persistence-types/preset:
|
||||
en: f60ecbd156c6f063
|
||||
zh: c9149679bbacff21
|
||||
en: 26cf71c850765c59
|
||||
zh: 7a436be430e04898
|
||||
/session-persistence-event-catalog/resolved-persistence-types/reason-turn:
|
||||
en: a4ba4d0ee1396381
|
||||
zh: b6f595f8a696d512
|
||||
|
||||
@@ -185,7 +185,7 @@ Sources: [`packages/core/session/src/types.ts:432`](../packages/core/session/src
|
||||
}
|
||||
```
|
||||
|
||||
Source: [`packages/core/agent/src/types.ts:96`](../packages/core/agent/src/types.ts)
|
||||
Source: [`packages/core/agent/src/types.ts:84`](../packages/core/agent/src/types.ts)
|
||||
|
||||
### `agent-preset/*`
|
||||
|
||||
@@ -700,7 +700,7 @@ Source: [`packages/llm/llm-retry/src/types.ts:11`](../packages/llm/llm-retry/src
|
||||
'model/selection': ModelSelection
|
||||
```
|
||||
|
||||
Source: [`packages/api/session-controller/src/types.ts:45`](../packages/api/session-controller/src/types.ts)
|
||||
Source: [`packages/api/session-controller/src/types.ts:37`](../packages/api/session-controller/src/types.ts)
|
||||
|
||||
### `permission/*`
|
||||
|
||||
@@ -718,7 +718,7 @@ Source: [`packages/api/session-controller/src/types.ts:45`](../packages/api/sess
|
||||
'permission/preset': { preset: string }
|
||||
```
|
||||
|
||||
Source: [`packages/interaction/permission-presets/src/index.ts:59`](../packages/interaction/permission-presets/src/index.ts)
|
||||
Source: [`packages/interaction/permission-presets/src/index.ts:52`](../packages/interaction/permission-presets/src/index.ts)
|
||||
|
||||
### `plan/*`
|
||||
|
||||
@@ -4337,7 +4337,7 @@ Sources: [`packages/llm/llm/src/message.ts:24`](../packages/llm/llm/src/message.
|
||||
|
||||
SHA-256: `f09e5e7acf5ff5ccd193ff2eeec015fa2ff7c29d98bc465f8c3302058d47e29b`
|
||||
|
||||
Sources: [`packages/api/session-controller/src/types.ts:106`](../packages/api/session-controller/src/types.ts)
|
||||
Sources: [`packages/api/session-controller/src/types.ts:98`](../packages/api/session-controller/src/types.ts)
|
||||
|
||||
| Property | Presence | Type |
|
||||
|---|---|---|
|
||||
@@ -5583,7 +5583,7 @@ Array of `number`.
|
||||
|
||||
SHA-256: `4cd48f3e5108bd6ebedf7301c3638839b8b2b9db8dbd493974177fbdeb91a01b`
|
||||
|
||||
Sources: [`packages/api/session-controller/src/types.ts:414`](../packages/api/session-controller/src/types.ts) · [`packages/attachment/attachment/src/brand.ts:6`](../packages/attachment/attachment/src/brand.ts) · [`packages/compaction/compaction/src/brand.ts:4`](../packages/compaction/compaction/src/brand.ts) · [`packages/core/session/src/types.ts:21`](../packages/core/session/src/types.ts) · [`packages/experimental/agent-team/src/types.ts:21`](../packages/experimental/agent-team/src/types.ts) · [`packages/experimental/agent-team/src/types.ts:33`](../packages/experimental/agent-team/src/types.ts) · [`packages/experimental/agent-team/src/types.ts:9`](../packages/experimental/agent-team/src/types.ts) · [`packages/experimental/webhook/src/brand.ts:12`](../packages/experimental/webhook/src/brand.ts) · [`packages/experimental/webhook/src/brand.ts:6`](../packages/experimental/webhook/src/brand.ts) · [`packages/experimental/webhook/src/brand.ts:9`](../packages/experimental/webhook/src/brand.ts) · [`packages/feedback/message-feedback/src/types.ts:14`](../packages/feedback/message-feedback/src/types.ts) · [`packages/goal/goal/src/types.ts:17`](../packages/goal/goal/src/types.ts) · [`packages/interaction/commands/src/brand.ts:31`](../packages/interaction/commands/src/brand.ts) · [`packages/interaction/user-approval/src/types.ts:17`](../packages/interaction/user-approval/src/types.ts) · [`packages/jobs/jobs/src/brand.ts:19`](../packages/jobs/jobs/src/brand.ts) · [`packages/llm/llm-retry/src/brand.ts:4`](../packages/llm/llm-retry/src/brand.ts) · [`packages/llm/llm/src/brand.ts:16`](../packages/llm/llm/src/brand.ts) · [`packages/llm/llm/src/brand.ts:31`](../packages/llm/llm/src/brand.ts) · [`packages/llm/llm/src/brand.ts:43`](../packages/llm/llm/src/brand.ts) · [`packages/llm/llm/src/brand.ts:67`](../packages/llm/llm/src/brand.ts) · [`packages/schedule/schedule/src/types.ts:14`](../packages/schedule/schedule/src/types.ts) · [`packages/session/session-title/src/index.ts:42`](../packages/session/session-title/src/index.ts) · [`packages/session/session-title/src/types.ts:17`](../packages/session/session-title/src/types.ts) · [`packages/workflow/workflow/src/types.ts:13`](../packages/workflow/workflow/src/types.ts)
|
||||
Sources: [`packages/api/session-controller/src/types.ts:406`](../packages/api/session-controller/src/types.ts) · [`packages/attachment/attachment/src/brand.ts:6`](../packages/attachment/attachment/src/brand.ts) · [`packages/compaction/compaction/src/brand.ts:4`](../packages/compaction/compaction/src/brand.ts) · [`packages/core/session/src/types.ts:21`](../packages/core/session/src/types.ts) · [`packages/experimental/agent-team/src/types.ts:21`](../packages/experimental/agent-team/src/types.ts) · [`packages/experimental/agent-team/src/types.ts:33`](../packages/experimental/agent-team/src/types.ts) · [`packages/experimental/agent-team/src/types.ts:9`](../packages/experimental/agent-team/src/types.ts) · [`packages/experimental/webhook/src/brand.ts:12`](../packages/experimental/webhook/src/brand.ts) · [`packages/experimental/webhook/src/brand.ts:6`](../packages/experimental/webhook/src/brand.ts) · [`packages/experimental/webhook/src/brand.ts:9`](../packages/experimental/webhook/src/brand.ts) · [`packages/feedback/message-feedback/src/types.ts:14`](../packages/feedback/message-feedback/src/types.ts) · [`packages/goal/goal/src/types.ts:17`](../packages/goal/goal/src/types.ts) · [`packages/interaction/commands/src/brand.ts:31`](../packages/interaction/commands/src/brand.ts) · [`packages/interaction/user-approval/src/types.ts:17`](../packages/interaction/user-approval/src/types.ts) · [`packages/jobs/jobs/src/brand.ts:19`](../packages/jobs/jobs/src/brand.ts) · [`packages/llm/llm-retry/src/brand.ts:4`](../packages/llm/llm-retry/src/brand.ts) · [`packages/llm/llm/src/brand.ts:16`](../packages/llm/llm/src/brand.ts) · [`packages/llm/llm/src/brand.ts:31`](../packages/llm/llm/src/brand.ts) · [`packages/llm/llm/src/brand.ts:43`](../packages/llm/llm/src/brand.ts) · [`packages/llm/llm/src/brand.ts:67`](../packages/llm/llm/src/brand.ts) · [`packages/schedule/schedule/src/types.ts:14`](../packages/schedule/schedule/src/types.ts) · [`packages/session/session-title/src/index.ts:42`](../packages/session/session-title/src/index.ts) · [`packages/session/session-title/src/types.ts:17`](../packages/session/session-title/src/types.ts) · [`packages/workflow/workflow/src/types.ts:13`](../packages/workflow/workflow/src/types.ts)
|
||||
|
||||
`string`
|
||||
|
||||
@@ -6377,7 +6377,7 @@ Sources: [`packages/core/session/src/types.ts:428`](../packages/core/session/src
|
||||
|
||||
SHA-256: `fa106c5b33b657e8ef16e8cbf39f3b7d44e5cbcdf4f9303d9db6410f28951e42`
|
||||
|
||||
Sources: [`packages/core/agent/src/types.ts:96`](../packages/core/agent/src/types.ts)
|
||||
Sources: [`packages/core/agent/src/types.ts:84`](../packages/core/agent/src/types.ts)
|
||||
|
||||
| Property | Presence | Type |
|
||||
|---|---|---|
|
||||
@@ -7554,7 +7554,7 @@ Sources: [`packages/core/tools/src/index.ts:35`](../packages/core/tools/src/inde
|
||||
|
||||
SHA-256: `6dacb1584f2bfbe209dc6b6f578d51da6d55595f64d0e63c727c98f359278def`
|
||||
|
||||
Sources: [`packages/api/session-controller/src/types.ts:423`](../packages/api/session-controller/src/types.ts)
|
||||
Sources: [`packages/api/session-controller/src/types.ts:415`](../packages/api/session-controller/src/types.ts)
|
||||
|
||||
| Property | Presence | Type |
|
||||
|---|---|---|
|
||||
@@ -7790,7 +7790,7 @@ Sources: [`packages/interaction/user-approval/src/index.ts:40`](../packages/inte
|
||||
|
||||
SHA-256: `8a9c990773f798eac10ca8878e6bcc531ee080bc589cac956730e462cc6a09ad`
|
||||
|
||||
Sources: [`packages/interaction/permission-presets/src/index.ts:59`](../packages/interaction/permission-presets/src/index.ts)
|
||||
Sources: [`packages/interaction/permission-presets/src/index.ts:52`](../packages/interaction/permission-presets/src/index.ts)
|
||||
|
||||
| Property | Presence | Type |
|
||||
|---|---|---|
|
||||
|
||||
@@ -187,7 +187,7 @@ export type SessionEvent<T extends SessionEventType = SessionEventType> = {
|
||||
}
|
||||
```
|
||||
|
||||
来源:[`packages/core/agent/src/types.ts:96`](../packages/core/agent/src/types.ts)
|
||||
来源:[`packages/core/agent/src/types.ts:84`](../packages/core/agent/src/types.ts)
|
||||
|
||||
### `agent-preset/*`
|
||||
|
||||
@@ -702,7 +702,7 @@ export type SessionEvent<T extends SessionEventType = SessionEventType> = {
|
||||
'model/selection': ModelSelection
|
||||
```
|
||||
|
||||
来源:[`packages/api/session-controller/src/types.ts:45`](../packages/api/session-controller/src/types.ts)
|
||||
来源:[`packages/api/session-controller/src/types.ts:37`](../packages/api/session-controller/src/types.ts)
|
||||
|
||||
### `permission/*`
|
||||
|
||||
@@ -720,7 +720,7 @@ export type SessionEvent<T extends SessionEventType = SessionEventType> = {
|
||||
'permission/preset': { preset: string }
|
||||
```
|
||||
|
||||
来源:[`packages/interaction/permission-presets/src/index.ts:59`](../packages/interaction/permission-presets/src/index.ts)
|
||||
来源:[`packages/interaction/permission-presets/src/index.ts:52`](../packages/interaction/permission-presets/src/index.ts)
|
||||
|
||||
### `plan/*`
|
||||
|
||||
@@ -4339,7 +4339,7 @@ SHA-256: `719c51f67074f97034a6c94297fe1f4b5d3c8d392c57586dab732aa5398c7a91`
|
||||
|
||||
SHA-256: `f09e5e7acf5ff5ccd193ff2eeec015fa2ff7c29d98bc465f8c3302058d47e29b`
|
||||
|
||||
来源:[`packages/api/session-controller/src/types.ts:106`](../packages/api/session-controller/src/types.ts)
|
||||
来源:[`packages/api/session-controller/src/types.ts:98`](../packages/api/session-controller/src/types.ts)
|
||||
|
||||
| 属性 | 存在性 | 类型 |
|
||||
|---|---|---|
|
||||
@@ -5585,7 +5585,7 @@ SHA-256: `5d03ba38734809bcbd2a55221bd938b3b3fc34b49bb686c6113e9e4931e3aa78`
|
||||
|
||||
SHA-256: `4cd48f3e5108bd6ebedf7301c3638839b8b2b9db8dbd493974177fbdeb91a01b`
|
||||
|
||||
来源:[`packages/api/session-controller/src/types.ts:414`](../packages/api/session-controller/src/types.ts) · [`packages/attachment/attachment/src/brand.ts:6`](../packages/attachment/attachment/src/brand.ts) · [`packages/compaction/compaction/src/brand.ts:4`](../packages/compaction/compaction/src/brand.ts) · [`packages/core/session/src/types.ts:21`](../packages/core/session/src/types.ts) · [`packages/experimental/agent-team/src/types.ts:21`](../packages/experimental/agent-team/src/types.ts) · [`packages/experimental/agent-team/src/types.ts:33`](../packages/experimental/agent-team/src/types.ts) · [`packages/experimental/agent-team/src/types.ts:9`](../packages/experimental/agent-team/src/types.ts) · [`packages/experimental/webhook/src/brand.ts:12`](../packages/experimental/webhook/src/brand.ts) · [`packages/experimental/webhook/src/brand.ts:6`](../packages/experimental/webhook/src/brand.ts) · [`packages/experimental/webhook/src/brand.ts:9`](../packages/experimental/webhook/src/brand.ts) · [`packages/feedback/message-feedback/src/types.ts:14`](../packages/feedback/message-feedback/src/types.ts) · [`packages/goal/goal/src/types.ts:17`](../packages/goal/goal/src/types.ts) · [`packages/interaction/commands/src/brand.ts:31`](../packages/interaction/commands/src/brand.ts) · [`packages/interaction/user-approval/src/types.ts:17`](../packages/interaction/user-approval/src/types.ts) · [`packages/jobs/jobs/src/brand.ts:19`](../packages/jobs/jobs/src/brand.ts) · [`packages/llm/llm-retry/src/brand.ts:4`](../packages/llm/llm-retry/src/brand.ts) · [`packages/llm/llm/src/brand.ts:16`](../packages/llm/llm/src/brand.ts) · [`packages/llm/llm/src/brand.ts:31`](../packages/llm/llm/src/brand.ts) · [`packages/llm/llm/src/brand.ts:43`](../packages/llm/llm/src/brand.ts) · [`packages/llm/llm/src/brand.ts:67`](../packages/llm/llm/src/brand.ts) · [`packages/schedule/schedule/src/types.ts:14`](../packages/schedule/schedule/src/types.ts) · [`packages/session/session-title/src/index.ts:42`](../packages/session/session-title/src/index.ts) · [`packages/session/session-title/src/types.ts:17`](../packages/session/session-title/src/types.ts) · [`packages/workflow/workflow/src/types.ts:13`](../packages/workflow/workflow/src/types.ts)
|
||||
来源:[`packages/api/session-controller/src/types.ts:406`](../packages/api/session-controller/src/types.ts) · [`packages/attachment/attachment/src/brand.ts:6`](../packages/attachment/attachment/src/brand.ts) · [`packages/compaction/compaction/src/brand.ts:4`](../packages/compaction/compaction/src/brand.ts) · [`packages/core/session/src/types.ts:21`](../packages/core/session/src/types.ts) · [`packages/experimental/agent-team/src/types.ts:21`](../packages/experimental/agent-team/src/types.ts) · [`packages/experimental/agent-team/src/types.ts:33`](../packages/experimental/agent-team/src/types.ts) · [`packages/experimental/agent-team/src/types.ts:9`](../packages/experimental/agent-team/src/types.ts) · [`packages/experimental/webhook/src/brand.ts:12`](../packages/experimental/webhook/src/brand.ts) · [`packages/experimental/webhook/src/brand.ts:6`](../packages/experimental/webhook/src/brand.ts) · [`packages/experimental/webhook/src/brand.ts:9`](../packages/experimental/webhook/src/brand.ts) · [`packages/feedback/message-feedback/src/types.ts:14`](../packages/feedback/message-feedback/src/types.ts) · [`packages/goal/goal/src/types.ts:17`](../packages/goal/goal/src/types.ts) · [`packages/interaction/commands/src/brand.ts:31`](../packages/interaction/commands/src/brand.ts) · [`packages/interaction/user-approval/src/types.ts:17`](../packages/interaction/user-approval/src/types.ts) · [`packages/jobs/jobs/src/brand.ts:19`](../packages/jobs/jobs/src/brand.ts) · [`packages/llm/llm-retry/src/brand.ts:4`](../packages/llm/llm-retry/src/brand.ts) · [`packages/llm/llm/src/brand.ts:16`](../packages/llm/llm/src/brand.ts) · [`packages/llm/llm/src/brand.ts:31`](../packages/llm/llm/src/brand.ts) · [`packages/llm/llm/src/brand.ts:43`](../packages/llm/llm/src/brand.ts) · [`packages/llm/llm/src/brand.ts:67`](../packages/llm/llm/src/brand.ts) · [`packages/schedule/schedule/src/types.ts:14`](../packages/schedule/schedule/src/types.ts) · [`packages/session/session-title/src/index.ts:42`](../packages/session/session-title/src/index.ts) · [`packages/session/session-title/src/types.ts:17`](../packages/session/session-title/src/types.ts) · [`packages/workflow/workflow/src/types.ts:13`](../packages/workflow/workflow/src/types.ts)
|
||||
|
||||
`string`
|
||||
|
||||
@@ -6379,7 +6379,7 @@ SHA-256: `17d1afb770d9941936130996da00dc86782cfef731d8d6526162c301256a4ac3`
|
||||
|
||||
SHA-256: `fa106c5b33b657e8ef16e8cbf39f3b7d44e5cbcdf4f9303d9db6410f28951e42`
|
||||
|
||||
来源:[`packages/core/agent/src/types.ts:96`](../packages/core/agent/src/types.ts)
|
||||
来源:[`packages/core/agent/src/types.ts:84`](../packages/core/agent/src/types.ts)
|
||||
|
||||
| 属性 | 存在性 | 类型 |
|
||||
|---|---|---|
|
||||
@@ -7556,7 +7556,7 @@ SHA-256: `8fcb0b44a575e998665c053643d1ab04c82f39253ebd190ac0c975b78e40b543`
|
||||
|
||||
SHA-256: `6dacb1584f2bfbe209dc6b6f578d51da6d55595f64d0e63c727c98f359278def`
|
||||
|
||||
来源:[`packages/api/session-controller/src/types.ts:423`](../packages/api/session-controller/src/types.ts)
|
||||
来源:[`packages/api/session-controller/src/types.ts:415`](../packages/api/session-controller/src/types.ts)
|
||||
|
||||
| 属性 | 存在性 | 类型 |
|
||||
|---|---|---|
|
||||
@@ -7792,7 +7792,7 @@ SHA-256: `11f70b595aa76897fc4e25a352bfa718c659f44b799e5aa14e84e5a38fe72912`
|
||||
|
||||
SHA-256: `8a9c990773f798eac10ca8878e6bcc531ee080bc589cac956730e462cc6a09ad`
|
||||
|
||||
来源:[`packages/interaction/permission-presets/src/index.ts:59`](../packages/interaction/permission-presets/src/index.ts)
|
||||
来源:[`packages/interaction/permission-presets/src/index.ts:52`](../packages/interaction/permission-presets/src/index.ts)
|
||||
|
||||
| 属性 | 存在性 | 类型 |
|
||||
|---|---|---|
|
||||
|
||||
Generated
+5
-5
@@ -23574,7 +23574,7 @@
|
||||
"packages/workflow/workflow/src/types.ts#WorkflowRunId"
|
||||
],
|
||||
"sources": [
|
||||
"packages/api/session-controller/src/types.ts:414",
|
||||
"packages/api/session-controller/src/types.ts:406",
|
||||
"packages/attachment/attachment/src/brand.ts:6",
|
||||
"packages/compaction/compaction/src/brand.ts:4",
|
||||
"packages/core/session/src/types.ts:21",
|
||||
@@ -24096,7 +24096,7 @@
|
||||
"digest": "6dacb1584f2bfbe209dc6b6f578d51da6d55595f64d0e63c727c98f359278def",
|
||||
"names": [],
|
||||
"sources": [
|
||||
"packages/api/session-controller/src/types.ts:423"
|
||||
"packages/api/session-controller/src/types.ts:415"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -24518,7 +24518,7 @@
|
||||
"digest": "8a9c990773f798eac10ca8878e6bcc531ee080bc589cac956730e462cc6a09ad",
|
||||
"names": [],
|
||||
"sources": [
|
||||
"packages/interaction/permission-presets/src/index.ts:59"
|
||||
"packages/interaction/permission-presets/src/index.ts:52"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -25916,7 +25916,7 @@
|
||||
"packages/api/session-controller/src/types.ts#ModelSelection"
|
||||
],
|
||||
"sources": [
|
||||
"packages/api/session-controller/src/types.ts:106"
|
||||
"packages/api/session-controller/src/types.ts:98"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -26079,7 +26079,7 @@
|
||||
"digest": "fa106c5b33b657e8ef16e8cbf39f3b7d44e5cbcdf4f9303d9db6410f28951e42",
|
||||
"names": [],
|
||||
"sources": [
|
||||
"packages/core/agent/src/types.ts:96"
|
||||
"packages/core/agent/src/types.ts:84"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -35,3 +35,6 @@
|
||||
/core/repo-wide-type-patterns/branded-ids:
|
||||
en: 10f8ac0f97e7c54a
|
||||
zh: 75cd2311aabfe464
|
||||
/core/turnboundaryprojection:
|
||||
en: 3892ad06e90b25c1
|
||||
zh: ee6f592230ad89df
|
||||
+37
-1
@@ -409,6 +409,26 @@ type Branded<B extends string> = string & { readonly [BRAND]: B }
|
||||
|
||||
The two core IDs are `ToolCallId` (correlates a tool call with its result; dsh-llm) and `SessionId` (the shared live agent and durable session identity; dsh-session). Capability packages brand their own ids too, such as `JobId` in [jobs.md](jobs.md).
|
||||
|
||||
## `TurnBoundaryProjection`
|
||||
|
||||
`ctx.agents.turnBoundaryOf(session)` reads recorded turn and step positions without requiring a live Agent. The registry owns this state; consumers do not register its projection definition.
|
||||
|
||||
Source: [`packages/core/agent/src/types.ts`](../../packages/core/agent/src/types.ts)
|
||||
|
||||
```ts type-equiv
|
||||
/** Turn and step positions recorded in a Session. */
|
||||
interface TurnBoundaryProjection {
|
||||
/** Seq of the open turn's `turn/start`, or null between turns. */
|
||||
readonly openTurnStartSeq: OptionalSessionSeq
|
||||
/** Seq of the latest `step/start` event, or null before the first step. */
|
||||
readonly lastStepStartSeq: OptionalSessionSeq
|
||||
/** The latest step boundary (`step/start` or `step/end`) and its seq, or null before the first step boundary. */
|
||||
readonly lastStepBoundary: { readonly kind: 'start' | 'end'; readonly seq: SessionSeq } | null
|
||||
/** Turn number of the latest `turn/start`; 0 before the first turn. */
|
||||
readonly lastTurn: number
|
||||
}
|
||||
```
|
||||
|
||||
<!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
|
||||
|
||||
<a id="cordis-surface"></a>
|
||||
@@ -489,6 +509,13 @@ Source: [`packages/core/agent-loop/src/index.ts`](../../packages/core/agent-loop
|
||||
Registry of YAML-declared presets and the revisions live Agents retain.
|
||||
|
||||
```ts cordis-catalog
|
||||
/**
|
||||
* Read the preset recorded for a Session, including selections no longer in the roster.
|
||||
* @param session - Session whose committed preset selection is read.
|
||||
* @returns the recorded preset, or null when none was selected.
|
||||
*/
|
||||
presetForSession(session: Session): string | null
|
||||
|
||||
/** Register and eagerly load a definition; activation failure remains visible in the roster.
|
||||
* @param definition Parsed configuration supplied by the declaring plugin.
|
||||
* @returns Definition disposer after activation or its diagnostic settles; the declaring plugin owns it.
|
||||
@@ -578,7 +605,7 @@ async acquireScope(id?: string): Promise<{ key: ScopeKey } & AsyncDisposable>
|
||||
compositionInventory(): Promise<AgentPresetComposition[]>
|
||||
```
|
||||
|
||||
Types: [ScopeKey](scope.md)
|
||||
Types: [ScopeKey](scope.md) · [Session](session.md)
|
||||
|
||||
Source: [`packages/preset/agent-preset-registry/src/index.ts`](../../packages/preset/agent-preset-registry/src/index.ts)
|
||||
|
||||
@@ -591,6 +618,13 @@ Agent service (`ctx.agents`): tracks live agents and carries the initiating Agen
|
||||
Initiator methods provide same-process causal attribution only. Ambient presence is neither liveness proof nor authorization; subjects and owners remain explicit, as does identity at worker, process, persistence, and wire boundaries. Returned Promise boundaries drain during teardown, except a nested lineage that starts an owning-fiber unload is excluded from its own drain.
|
||||
|
||||
```ts cordis-catalog
|
||||
/**
|
||||
* Read the turn and step positions recorded in a Session.
|
||||
* @param session - Session whose committed events are read.
|
||||
* @returns the current turn and step positions; do not mutate the returned value.
|
||||
*/
|
||||
turnBoundaryOf(session: Session): TurnBoundaryProjection
|
||||
|
||||
/**
|
||||
* Read the Agent that initiated the inherited asynchronous driver chain.
|
||||
* Use this optional form for logging, tracing, metrics, or host attribution
|
||||
@@ -757,6 +791,8 @@ list(): Agent[]
|
||||
roots(): Agent[]
|
||||
```
|
||||
|
||||
Types: [Session](session.md)
|
||||
|
||||
Source: [`packages/core/agent/src/index.ts`](../../packages/core/agent/src/index.ts)
|
||||
|
||||
<a id="agent-events"></a>
|
||||
|
||||
@@ -421,6 +421,26 @@ type Branded<B extends string> = string & { readonly [BRAND]: B }
|
||||
|
||||
两个核心 ID 是 `ToolCallId`(关联工具调用及其结果;dsh-llm)和 `SessionId`(活跃 agent 与持久会话共享的标识;dsh-session)。能力包也会品牌化各自的 id,例如 [jobs.md](jobs.zh.md) 中的 `JobId`。
|
||||
|
||||
## `TurnBoundaryProjection`
|
||||
|
||||
`ctx.agents.turnBoundaryOf(session)` 读取已记录的轮次和步骤位置,无需活跃 Agent。注册表持有此状态;消费方不注册其投影定义。
|
||||
|
||||
来源: [`packages/core/agent/src/types.ts`](../../packages/core/agent/src/types.ts)
|
||||
|
||||
```ts type-equiv
|
||||
/** Turn and step positions recorded in a Session. */
|
||||
interface TurnBoundaryProjection {
|
||||
/** Seq of the open turn's `turn/start`, or null between turns. */
|
||||
readonly openTurnStartSeq: OptionalSessionSeq
|
||||
/** Seq of the latest `step/start` event, or null before the first step. */
|
||||
readonly lastStepStartSeq: OptionalSessionSeq
|
||||
/** The latest step boundary (`step/start` or `step/end`) and its seq, or null before the first step boundary. */
|
||||
readonly lastStepBoundary: { readonly kind: 'start' | 'end'; readonly seq: SessionSeq } | null
|
||||
/** Turn number of the latest `turn/start`; 0 before the first turn. */
|
||||
readonly lastTurn: number
|
||||
}
|
||||
```
|
||||
|
||||
<!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
|
||||
|
||||
<a id="cordis-surface"></a>
|
||||
@@ -501,6 +521,13 @@ Source: [`packages/core/agent-loop/src/index.ts`](../../packages/core/agent-loop
|
||||
Registry of YAML-declared presets and the revisions live Agents retain.
|
||||
|
||||
```ts cordis-catalog
|
||||
/**
|
||||
* Read the preset recorded for a Session, including selections no longer in the roster.
|
||||
* @param session - Session whose committed preset selection is read.
|
||||
* @returns the recorded preset, or null when none was selected.
|
||||
*/
|
||||
presetForSession(session: Session): string | null
|
||||
|
||||
/** Register and eagerly load a definition; activation failure remains visible in the roster.
|
||||
* @param definition Parsed configuration supplied by the declaring plugin.
|
||||
* @returns Definition disposer after activation or its diagnostic settles; the declaring plugin owns it.
|
||||
@@ -590,7 +617,7 @@ async acquireScope(id?: string): Promise<{ key: ScopeKey } & AsyncDisposable>
|
||||
compositionInventory(): Promise<AgentPresetComposition[]>
|
||||
```
|
||||
|
||||
Types: [ScopeKey](scope.zh.md)
|
||||
Types: [ScopeKey](scope.zh.md) · [Session](session.zh.md)
|
||||
|
||||
Source: [`packages/preset/agent-preset-registry/src/index.ts`](../../packages/preset/agent-preset-registry/src/index.ts)
|
||||
|
||||
@@ -603,6 +630,13 @@ Agent service (`ctx.agents`): tracks live agents and carries the initiating Agen
|
||||
Initiator methods provide same-process causal attribution only. Ambient presence is neither liveness proof nor authorization; subjects and owners remain explicit, as does identity at worker, process, persistence, and wire boundaries. Returned Promise boundaries drain during teardown, except a nested lineage that starts an owning-fiber unload is excluded from its own drain.
|
||||
|
||||
```ts cordis-catalog
|
||||
/**
|
||||
* Read the turn and step positions recorded in a Session.
|
||||
* @param session - Session whose committed events are read.
|
||||
* @returns the current turn and step positions; do not mutate the returned value.
|
||||
*/
|
||||
turnBoundaryOf(session: Session): TurnBoundaryProjection
|
||||
|
||||
/**
|
||||
* Read the Agent that initiated the inherited asynchronous driver chain.
|
||||
* Use this optional form for logging, tracing, metrics, or host attribution
|
||||
@@ -769,6 +803,8 @@ list(): Agent[]
|
||||
roots(): Agent[]
|
||||
```
|
||||
|
||||
Types: [Session](session.zh.md)
|
||||
|
||||
Source: [`packages/core/agent/src/index.ts`](../../packages/core/agent/src/index.ts)
|
||||
|
||||
<a id="agent-events"></a>
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
en: 234f6e4c49714997
|
||||
zh: add95d36252b572d
|
||||
/plan-mode/logged-state-and-recovery:
|
||||
en: dd1e8ba31e6fd535
|
||||
zh: a22dbccc60c73bc9
|
||||
en: 4c83897d7a6d8a67
|
||||
zh: 22dcc171a439ed19
|
||||
/plan-mode/pending-selections-and-the-pre-step-append:
|
||||
en: 8d7e5b7824921ed5
|
||||
zh: 910fad43593f29f4
|
||||
|
||||
@@ -8,7 +8,7 @@ Source: [`packages/plan/plan-mode/src/index.ts`](../../packages/plan/plan-mode/s
|
||||
|
||||
## Logged state and recovery
|
||||
|
||||
`plan/mode` (`{ active: boolean }`) is a log-only, whole-value-replace [session event](session.md): durable and replayable, never in the model transcript. The optionally registered `plan` unit folds committed mode, command settlement, and the mode recorded at the latest request header. `ctx.planMode` reads that state through `stateOf()`; the first dependent access fails if the registry, `plan` key, or `turnBoundary` key is absent. Clients receive only `{ active, pending }`; resume, fork, and compaction recover both from the log. The complete event declaration is in the [persistence log event catalog](../persistence-catalog.md).
|
||||
`plan/mode` (`{ active: boolean }`) is a log-only, whole-value-replace [session event](session.md): durable and replayable, never in the model transcript. The `plan` unit folds committed mode, command settlement, and the mode recorded at the latest request header. `ctx.planMode` requires the registry and awaits registration of `plan` and `turnBoundary` during activation, then reads through their retained handles. Clients receive only `{ active, pending }`; resume, fork, and compaction recover both from the log. The complete event declaration is in the [persistence log event catalog](../persistence-catalog.md).
|
||||
|
||||
## Pending selections and the pre-step append
|
||||
|
||||
@@ -50,7 +50,7 @@ Generated from source by `scripts/gen-cordis-catalog.ts` (verified fresh by `pnp
|
||||
|
||||
### `ctx.planMode` — `PlanModeController`
|
||||
|
||||
`ctx.planMode`: owns logged plan state, applies and narrates selected state at step start, the `plan:policy` section, the `/plan` command, and the stable exit tool. Client carriers expose the projection's cropped `{ active, pending }` view.
|
||||
`ctx.planMode`: owns logged plan state, applies and narrates selected state at step start, the `plan:policy` section, the `/plan` command, and the stable exit tool. Client carriers expose the projection's cropped `{ active, pending }` view. Agent prompt assembly fails while the projection reader is not ready; callers must await plugin activation before running an agent.
|
||||
|
||||
```ts cordis-catalog
|
||||
/**
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
## 已记录状态与恢复
|
||||
|
||||
`plan/mode`(`{ active: boolean }`)是仅记日志、整值替换的[会话事件](session.zh.md):持久且可回放,绝不进入模型 transcript(文本记录)。可选注册的 `plan` 单元折叠已提交模式、命令结算结果和最近一次请求头记录的模式。`ctx.planMode` 通过 `stateOf()` 读取该状态;注册表、`plan` key 或 `turnBoundary` key 缺失时,第一次依赖它们的访问会失败。客户端只接收 `{ active, pending }`;恢复、fork 与压缩(compaction)都能从日志恢复两者。完整事件声明见[持久化日志事件目录](../persistence-catalog.zh.md)。
|
||||
`plan/mode`(`{ active: boolean }`)是仅记日志、整值替换的[会话事件](session.zh.md):持久且可回放,绝不进入模型 transcript(文本记录)。`plan` 单元折叠已提交模式、命令结算结果和最近一次请求头记录的模式。`ctx.planMode` 要求注册表存在,并在激活期间等待 `plan` 和 `turnBoundary` 注册完成,之后通过保留的句柄读取状态。客户端只接收 `{ active, pending }`;恢复、fork 与压缩(compaction)都能从日志恢复两者。完整事件声明见[持久化日志事件目录](../persistence-catalog.zh.md)。
|
||||
|
||||
## 待生效选择与 pre-step 追加
|
||||
|
||||
@@ -50,7 +50,7 @@ Generated from source by `scripts/gen-cordis-catalog.ts` (verified fresh by `pnp
|
||||
|
||||
### `ctx.planMode` — `PlanModeController`
|
||||
|
||||
`ctx.planMode`: owns logged plan state, applies and narrates selected state at step start, the `plan:policy` section, the `/plan` command, and the stable exit tool. Client carriers expose the projection's cropped `{ active, pending }` view.
|
||||
`ctx.planMode`: owns logged plan state, applies and narrates selected state at step start, the `plan:policy` section, the `/plan` command, and the stable exit tool. Client carriers expose the projection's cropped `{ active, pending }` view. Agent prompt assembly fails while the projection reader is not ready; callers must await plugin activation before running an agent.
|
||||
|
||||
```ts cordis-catalog
|
||||
/**
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
# After editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/subsystems/session-projection.md
|
||||
/session-projections:
|
||||
en: a6ab5134ca1771fd
|
||||
zh: 523039a9d84e3f97
|
||||
en: e9330151d3eb579a
|
||||
zh: 53e47acd1d810da9
|
||||
/session-projections/the-unit:
|
||||
en: fad897af8b8839ff
|
||||
zh: 3c68c5256973bc39
|
||||
en: d48814d3ab0e997f
|
||||
zh: d0337d8eba097a63
|
||||
/session-projections/the-snapshot-and-the-change-feed:
|
||||
en: a49a57c600c3cbd0
|
||||
zh: c92f00b8a9d71bad
|
||||
en: 630c1041f41f89bf
|
||||
zh: a3361bdead116cd7
|
||||
/session-projections/summary-reads:
|
||||
en: d2bc9bcc775acfb7
|
||||
zh: b1d2ba74496305b8
|
||||
@@ -18,5 +18,5 @@
|
||||
en: 6a93a4e1682fd6e6
|
||||
zh: 28729129c3d8a3b1
|
||||
/session-projections/the-registry-ctx-sessionprojections:
|
||||
en: 50e79c1bc16abb63
|
||||
zh: 200ecbf0e4ab19d2
|
||||
en: 67fe0f49dee90e25
|
||||
zh: c23de3a2803040f0
|
||||
@@ -2,13 +2,33 @@
|
||||
|
||||
English | [中文](session-projection.zh.md)
|
||||
|
||||
The session-projection seam — a [capability seam](../capability-seams.md) through which domain host plugins serve whole current values of log-derived per-session state to client carriers: the Service Definition and registry ([dsh-session-projection](../../packages/session/session-projection), `ctx.sessionProjections`), domain contributors (each registering one pure unit), and carriers ([dsh-session-controller](../../packages/api/session-controller)'s history tail page and `session/projection` push frame). It is one optional capability, not part of the agent-loop spine. The framework drives, the domain computes: the registry subscribes to `session/event` once and folds every committed event through every unit; domains hold no subscriptions and clients never fold domain events — they receive finished values. Design authority: the [session-projection RFC](../../.agents/notes/proposed/architecture/2026-07-27-session-projection-and-command-log.md); drive/cache/feed contracts: the [package README](../../packages/session/session-projection/README.md).
|
||||
The session-projection seam — a [capability seam](../capability-seams.md) through which domain host plugins serve whole current values of log-derived per-session state to client carriers: the Service Definition and registry ([dsh-session-projection](../../packages/session/session-projection), `ctx.sessionProjections`), domain contributors (each registering one pure unit), and carriers ([dsh-session-controller](../../packages/api/session-controller)'s history tail page and `session/projection` push frame). Host state readers require the registry during activation. The framework drives, the domain computes: the registry subscribes to `session/event` once and folds every committed event through every unit; domains hold no subscriptions and clients never fold domain events — they receive finished values. Design authority: the [session-projection RFC](../../.agents/notes/proposed/architecture/2026-07-27-session-projection-and-command-log.md); drive/cache/feed contracts: the [package README](../../packages/session/session-projection/README.md).
|
||||
|
||||
Source: [`packages/session/session-projection/src/index.ts`](../../packages/session/session-projection/src/index.ts)
|
||||
|
||||
A `ProjectionHandle` retains its synchronous reader after unregistration. `stateOf(session)` folds newly committed events with the original unit; it does not rejoin global snapshots, checkpoints, or notifications. Registration must still finish before the handle can be used. See the [registration lifecycle](../../packages/session/session-projection/README.md).
|
||||
|
||||
```ts type-equiv
|
||||
/** An effect-owned registration with a reader retained independently of registration. */
|
||||
interface ProjectionHandle<S> {
|
||||
/**
|
||||
* Read the current state, including after unregistering, by folding new
|
||||
* Session events with this handle's original unit. Do not mutate the live value.
|
||||
* @param session - Session whose committed events are folded.
|
||||
* @returns the projection state at the Session's current cursor.
|
||||
*/
|
||||
stateOf(session: Session): S
|
||||
/**
|
||||
* Unregister from global drives and snapshots without invalidating this reader.
|
||||
* @returns a promise that resolves when this registration is released.
|
||||
*/
|
||||
dispose(): Promise<void>
|
||||
}
|
||||
```
|
||||
|
||||
## The unit
|
||||
|
||||
`SessionProjectionStateMap` is the merge-extensible table of host fold states, while `SessionProjectionMap` retains the client-visible whole values. A domain contributes one `ProjectionDefinition` per state key; a `wire` block makes that key client-visible, and rendering belongs to the slot system, never this layer:
|
||||
`ProjectionDefinition<K, S>` carries each unit's host state type locally; registration infers `S` from `stateSchema` and returns `ProjectionHandle<S>`. Only client-visible keys merge into `SessionProjectionMap`, which types their whole values. A `wire` block makes a unit client-visible, and rendering belongs to the slot system, never this layer:
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
@@ -19,11 +39,8 @@ Source: [`packages/session/session-projection/src/index.ts`](../../packages/sess
|
||||
* synchronous (an async unit would tear the carriers' consistency cut), and
|
||||
* `state` MUST be plain JSON (the persisted-cache precondition).
|
||||
*/
|
||||
interface ProjectionDefinition<
|
||||
K extends keyof SessionProjectionStateMap,
|
||||
S extends SessionProjectionStateMap[K] = SessionProjectionStateMap[K],
|
||||
> {
|
||||
/** The projection key this unit owns (its `SessionProjectionStateMap` entry). */
|
||||
interface ProjectionDefinition<K extends string, S> {
|
||||
/** The projection key this unit owns. */
|
||||
key: K
|
||||
/** Validates persisted state before it seeds a fold. */
|
||||
stateSchema: ZodType<S>
|
||||
@@ -107,7 +124,7 @@ type ProjectionChangeListener = (
|
||||
) => void
|
||||
```
|
||||
|
||||
`snapshot(session)` is fully synchronous: a carrier reads it in the same tick as its page slice, so `asOfSeq` covers both reads at one sequence number. It returns only client views, and every value passes its unit's `viewSchema` before return. `stateOf(session, key)` reads one live host state without computing unrelated views; callers must not mutate the borrowed reference. A state-reference change computes one cached raw view, and the change feed fires only when that result changes by `Object.is`; an object-valued view must preserve its reference to suppress publication across internal-only state changes.
|
||||
`snapshot(session)` is fully synchronous: a carrier reads it in the same tick as its page slice, so `asOfSeq` covers both reads at one sequence number. It returns only client views, and every value passes its unit's `viewSchema` before return. `handle.stateOf(session)` reads one live host state without computing unrelated views; callers must not mutate the borrowed reference. A state-reference change computes one cached raw view, and the change feed fires only when that result changes by `Object.is`; an object-valued view must preserve its reference to suppress publication across internal-only state changes.
|
||||
|
||||
## Summary reads
|
||||
|
||||
@@ -187,7 +204,7 @@ type ProjectionKeySelection =
|
||||
|
||||
## The registry: `ctx.sessionProjections`
|
||||
|
||||
`SessionProjectionRegistry` ([signatures](#ctxsessionprojections--sessionprojectionregistry)) owns the drive: one `session/event` subscription, eager `apply` over every registered unit, and per-session per-unit watermark cells. Cells build lazily — a unit registered after events flowed, or a session older than the registry, folds `init` over the in-memory log on first touch (event or read). Registration is an effect whose disposer rides the calling fiber: an unloaded domain plugin's key (with its cached cells) disappears from subsequent drives and snapshots, and clients read that as capability absence; a duplicate key with a different `stateVersion` throws, while same-version registrants share one unit and are counted. Domain plugins register under `ctx.inject(['sessionProjections'], …)` so headless assemblies without the registry stay unaffected.
|
||||
`SessionProjectionRegistry` ([signatures](#ctxsessionprojections--sessionprojectionregistry)) owns the drive: one `session/event` subscription, eager `apply` over every registered unit, and per-session per-unit watermark cells. Cells build lazily — a unit registered after events flowed, or a session older than the registry, folds `init` over the in-memory log on first touch (event or read). Domains await `register(definition)` during activation and retain its handle. Registration belongs to the calling fiber: its last release removes the key from global drives and snapshots, while retained handles remain readable. Same-key, same-version registrants share one unit; incompatible versions reject registration.
|
||||
|
||||
<!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
|
||||
|
||||
@@ -288,26 +305,27 @@ Source: [`packages/session/session-projection-cache/src/index.ts`](../../package
|
||||
|
||||
### `ctx.sessionProjections` — `SessionProjectionRegistry`
|
||||
|
||||
`ctx.sessionProjections`: the projection unit table and its drive. The service subscribes to `session/event` once; every committed event passes every registered unit's `apply` (eager drive). A changed state reference computes the next client view; the change feed is notified only when its raw result changes by `Object.is`. Cells build lazily — a unit registered after events flowed, or a session older than the registry, folds `init` over the in-memory log on first touch (event or read). Registration is an effect (disposer rides the calling fiber): an unloaded domain plugin's key disappears from snapshots and clients read it as capability absence. A host reader either declares `sessionProjections` in its plugin `inject` or fails explicitly when the registry or required key is absent. Contributors may preserve optional registration through `ctx.inject(['sessionProjections'], ...)`. Registrants sharing a key share one unit and are counted: the same tool package mounted in N agent presets registers N times, and the key survives until the last one unloads.
|
||||
`ctx.sessionProjections`: the projection unit table and its drive. The service subscribes to `session/event` once; every committed event passes every registered unit's `apply` (eager drive). A changed state reference computes the next client view; the change feed is notified only when its raw result changes by `Object.is`. Cells build lazily — a unit registered after events flowed, or a session older than the registry, folds `init` over the in-memory log on first touch (event or read). Consumers declare `sessionProjections` as a required injection, await registration during activation, and retain its ready handle. Registrants sharing a key share one unit and are counted. The last release removes the unit from live drives, checkpoints, and client snapshots; retained handles still fold new events on demand using the original unit.
|
||||
|
||||
```ts cordis-catalog
|
||||
/**
|
||||
* Register one domain's unit. The registration is an effect on the calling
|
||||
* context's fiber: disposing the fiber (or calling the returned disposer)
|
||||
* removes the key — and the unit's cached cells — from subsequent drives
|
||||
* and snapshots.
|
||||
* Register one domain's unit on the calling context's fiber. Fiber or handle
|
||||
* disposal releases one reference; the final release removes live participation.
|
||||
* Retained handles remain readable after release.
|
||||
* Callers must await registration before publishing consumers; registration may perform asynchronous work.
|
||||
* @param definition - key, state schema, pure unit functions, and stateVersion.
|
||||
* @returns the exact disposer that unregisters this unit.
|
||||
* @returns a ready handle with synchronous state reads and the effect disposer.
|
||||
*/
|
||||
register< K extends keyof SessionProjectionMap, S extends SessionProjectionStateMap[K], >( definition: Omit<ProjectionDefinition<K, S>, 'wire'> & { wire: NonNullable<ProjectionDefinition<K, S>['wire']> }, ): () => void
|
||||
register<K extends keyof SessionProjectionMap, S>( definition: Omit<ProjectionDefinition<K, S>, 'wire'> & { wire: NonNullable<ProjectionDefinition<K, S>['wire']> }, ): Promise<ProjectionHandle<S>>
|
||||
|
||||
/**
|
||||
* Register one host-only unit. Its state is omitted from client snapshots
|
||||
* and always checkpointed like every other unit.
|
||||
* Callers must await registration before publishing consumers; registration may perform asynchronous work.
|
||||
* @param definition - key, state schema, pure unit functions, and stateVersion.
|
||||
* @returns the exact disposer that unregisters this unit.
|
||||
* @returns a ready handle with synchronous state reads and the effect disposer.
|
||||
*/
|
||||
register< K extends Exclude<keyof SessionProjectionStateMap, keyof SessionProjectionMap>, S extends SessionProjectionStateMap[K], >( definition: Omit<ProjectionDefinition<K, S>, 'wire'>, ): () => void
|
||||
register<K extends string, S>( definition: ProjectionDefinition<K, S> & { key: Exclude<K, keyof SessionProjectionMap> }, ): Promise<ProjectionHandle<S>>
|
||||
|
||||
/**
|
||||
* Subscribe to the change feed. The registration is an effect on the
|
||||
@@ -317,16 +335,6 @@ register< K extends Exclude<keyof SessionProjectionStateMap, keyof SessionProjec
|
||||
*/
|
||||
onChanged(listener: ProjectionChangeListener): () => void
|
||||
|
||||
/**
|
||||
* Read one unit's current host state after materializing every registered
|
||||
* unit at the Session cursor. Unrelated wire views are not produced.
|
||||
* The returned value is live; callers must not mutate it.
|
||||
* @param session - the session whose state is read.
|
||||
* @param key - the registered unit key.
|
||||
* @returns current state, or `undefined` when the key is not registered.
|
||||
*/
|
||||
stateOf<K extends keyof SessionProjectionStateMap>( session: Session, key: K, ): SessionProjectionStateMap[K] | undefined
|
||||
|
||||
/**
|
||||
* One consistent cut over every registered client-visible unit for one session, read from
|
||||
* the watermark cache (missing cells fold lazily over the in-memory log).
|
||||
|
||||
@@ -2,13 +2,33 @@
|
||||
|
||||
[English](session-projection.md) | 中文
|
||||
|
||||
会话投影 seam 是一项[能力 seam](../capability-seams.zh.md):领域 host 插件经由它向客户端载体供给按会话的日志派生状态的当前全量值;三方分别是 Service Definition 与注册表([dsh-session-projection](../../packages/session/session-projection),`ctx.sessionProjections`)、领域贡献方(每个领域注册一个纯单元)与载体([dsh-session-controller](../../packages/api/session-controller) 的历史尾页与 `session/projection` 推送帧)。它是一项可选能力,不属于 agent loop(智能体循环)主干。框架负责驱动,领域负责计算:注册表只订阅一次 `session/event`,并把每个已提交事件折叠进每个单元;领域不持有任何订阅,客户端也从不折叠领域事件——它们收到的是成品值。设计权威:[session-projection RFC](../../.agents/notes/proposed/architecture/2026-07-27-session-projection-and-command-log.zh.md);驱动、缓存与变更流约定:[包 README](../../packages/session/session-projection/README.zh.md)。
|
||||
会话投影 seam 是一项[能力 seam](../capability-seams.zh.md):领域 host 插件经由它向客户端载体供给按会话的日志派生状态的当前全量值;三方分别是 Service Definition 与注册表([dsh-session-projection](../../packages/session/session-projection),`ctx.sessionProjections`)、领域贡献方(每个领域注册一个纯单元)与载体([dsh-session-controller](../../packages/api/session-controller) 的历史尾页与 `session/projection` 推送帧)。Host 状态读取方在激活期间要求注册表存在。框架负责驱动,领域负责计算:注册表只订阅一次 `session/event`,并把每个已提交事件折叠进每个单元;领域不持有任何订阅,客户端也从不折叠领域事件——它们收到的是成品值。设计权威:[session-projection RFC](../../.agents/notes/proposed/architecture/2026-07-27-session-projection-and-command-log.zh.md);驱动、缓存与变更流约定:[包 README](../../packages/session/session-projection/README.zh.md)。
|
||||
|
||||
源码:[`packages/session/session-projection/src/index.ts`](../../packages/session/session-projection/src/index.ts)
|
||||
|
||||
`ProjectionHandle` 在注销后仍保留同步 reader。`stateOf(session)` 用原有单元折叠新提交的事件,不会重新加入全局快照、检查点或通知。必须等待注册完成才能使用 handle。参见[注册生命周期](../../packages/session/session-projection/README.zh.md)。
|
||||
|
||||
```ts type-equiv
|
||||
/** An effect-owned registration with a reader retained independently of registration. */
|
||||
interface ProjectionHandle<S> {
|
||||
/**
|
||||
* Read the current state, including after unregistering, by folding new
|
||||
* Session events with this handle's original unit. Do not mutate the live value.
|
||||
* @param session - Session whose committed events are folded.
|
||||
* @returns the projection state at the Session's current cursor.
|
||||
*/
|
||||
stateOf(session: Session): S
|
||||
/**
|
||||
* Unregister from global drives and snapshots without invalidating this reader.
|
||||
* @returns a promise that resolves when this registration is released.
|
||||
*/
|
||||
dispose(): Promise<void>
|
||||
}
|
||||
```
|
||||
|
||||
## 投影单元
|
||||
|
||||
`SessionProjectionStateMap` 是 host 侧折叠状态的 merge-extensible 类型表,`SessionProjectionMap` 则继续表示客户端可见的全量值。领域为每个状态 key 贡献一个 `ProjectionDefinition`;`wire` 块使该 key 对客户端可见,渲染归 slot 体系管,永远不归本层:
|
||||
`ProjectionDefinition<K, S>` 在各单元内部携带宿主状态类型;注册从 `stateSchema` 推导 `S`,并返回 `ProjectionHandle<S>`。只有客户端可见的 key 合并到 `SessionProjectionMap`,由它约束对应的全量值。`wire` 块使单元对客户端可见,渲染归 slot 体系管,永远不归本层:
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
@@ -19,11 +39,8 @@
|
||||
* synchronous (an async unit would tear the carriers' consistency cut), and
|
||||
* `state` MUST be plain JSON (the persisted-cache precondition).
|
||||
*/
|
||||
interface ProjectionDefinition<
|
||||
K extends keyof SessionProjectionStateMap,
|
||||
S extends SessionProjectionStateMap[K] = SessionProjectionStateMap[K],
|
||||
> {
|
||||
/** The projection key this unit owns (its `SessionProjectionStateMap` entry). */
|
||||
interface ProjectionDefinition<K extends string, S> {
|
||||
/** The projection key this unit owns. */
|
||||
key: K
|
||||
/** Validates persisted state before it seeds a fold. */
|
||||
stateSchema: ZodType<S>
|
||||
@@ -107,7 +124,7 @@ type ProjectionChangeListener = (
|
||||
) => void
|
||||
```
|
||||
|
||||
`snapshot(session)` 完全同步:载体在切出页面切片的同一 tick 内读取它,因此 `asOfSeq` 使两次读取使用同一个序号。它只返回客户端视图,并在返回前通过各单元的 `viewSchema` 校验。`stateOf(session, key)` 可在不计算无关视图的情况下读取一份实时 host 状态;调用方不得修改这一借用引用。state 引用变化时,注册表计算并缓存一次原始 view;只有该结果通过 `Object.is` 判定为变化时才触发变更流,对象 view 若要在仅内部 state 变化时抑制发布就必须保留引用。
|
||||
`snapshot(session)` 完全同步:载体在切出页面切片的同一 tick 内读取它,因此 `asOfSeq` 使两次读取使用同一个序号。它只返回客户端视图,并在返回前通过各单元的 `viewSchema` 校验。`handle.stateOf(session)` 可在不计算无关视图的情况下读取一份实时 host 状态;调用方不得修改这一借用引用。state 引用变化时,注册表计算并缓存一次原始 view;只有该结果通过 `Object.is` 判定为变化时才触发变更流,对象 view 若要在仅内部 state 变化时抑制发布就必须保留引用。
|
||||
|
||||
## 摘要读取
|
||||
|
||||
@@ -187,7 +204,7 @@ type ProjectionKeySelection =
|
||||
|
||||
## 注册表:`ctx.sessionProjections`
|
||||
|
||||
`SessionProjectionRegistry`([签名](#ctxsessionprojections--sessionprojectionregistry))拥有驱动权:一份 `session/event` 订阅、对每个已注册单元即时调用 `apply`,以及每会话每单元的水位线(watermark)cell。cell 惰性构建:在事件流过之后才注册的单元,或比注册表更早的会话,都在首次触达(事件或读取)时从 `init` 出发在内存日志上折叠。注册是一个 effect,其 disposer 随调用方 fiber 走:领域插件卸载后,其 key(连同缓存的 cell)从后续驱动与快照中消失,客户端将其读作能力缺失;key 以不同 `stateVersion` 重复时直接 throw,同版本注册方则共享一个单元并被计数。领域插件在 `ctx.inject(['sessionProjections'], …)` 下注册,因此不带注册表的 headless 组装完全不受影响。
|
||||
`SessionProjectionRegistry`([签名](#ctxsessionprojections--sessionprojectionregistry))拥有驱动逻辑:一个 `session/event` 订阅、对所有已注册单元立即执行的 `apply`,以及按会话、按单元保存的水位线缓存。缓存单元惰性构建:若单元在事件产生后才注册,或会话早于注册表存在,第一次访问(事件或读取)会从 `init` 开始折叠内存日志。领域在激活期间等待 `register(definition)`,并保留返回的 handle(句柄)。注册归调用 fiber 所有:最后一次释放会从全局驱动和快照中移除该 key,已有句柄仍可读取。同 key、同版本的注册者共享一个单元;版本不兼容时注册失败。
|
||||
|
||||
<!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
|
||||
|
||||
@@ -288,26 +305,27 @@ Source: [`packages/session/session-projection-cache/src/index.ts`](../../package
|
||||
|
||||
### `ctx.sessionProjections` — `SessionProjectionRegistry`
|
||||
|
||||
`ctx.sessionProjections`: the projection unit table and its drive. The service subscribes to `session/event` once; every committed event passes every registered unit's `apply` (eager drive). A changed state reference computes the next client view; the change feed is notified only when its raw result changes by `Object.is`. Cells build lazily — a unit registered after events flowed, or a session older than the registry, folds `init` over the in-memory log on first touch (event or read). Registration is an effect (disposer rides the calling fiber): an unloaded domain plugin's key disappears from snapshots and clients read it as capability absence. A host reader either declares `sessionProjections` in its plugin `inject` or fails explicitly when the registry or required key is absent. Contributors may preserve optional registration through `ctx.inject(['sessionProjections'], ...)`. Registrants sharing a key share one unit and are counted: the same tool package mounted in N agent presets registers N times, and the key survives until the last one unloads.
|
||||
`ctx.sessionProjections`: the projection unit table and its drive. The service subscribes to `session/event` once; every committed event passes every registered unit's `apply` (eager drive). A changed state reference computes the next client view; the change feed is notified only when its raw result changes by `Object.is`. Cells build lazily — a unit registered after events flowed, or a session older than the registry, folds `init` over the in-memory log on first touch (event or read). Consumers declare `sessionProjections` as a required injection, await registration during activation, and retain its ready handle. Registrants sharing a key share one unit and are counted. The last release removes the unit from live drives, checkpoints, and client snapshots; retained handles still fold new events on demand using the original unit.
|
||||
|
||||
```ts cordis-catalog
|
||||
/**
|
||||
* Register one domain's unit. The registration is an effect on the calling
|
||||
* context's fiber: disposing the fiber (or calling the returned disposer)
|
||||
* removes the key — and the unit's cached cells — from subsequent drives
|
||||
* and snapshots.
|
||||
* Register one domain's unit on the calling context's fiber. Fiber or handle
|
||||
* disposal releases one reference; the final release removes live participation.
|
||||
* Retained handles remain readable after release.
|
||||
* Callers must await registration before publishing consumers; registration may perform asynchronous work.
|
||||
* @param definition - key, state schema, pure unit functions, and stateVersion.
|
||||
* @returns the exact disposer that unregisters this unit.
|
||||
* @returns a ready handle with synchronous state reads and the effect disposer.
|
||||
*/
|
||||
register< K extends keyof SessionProjectionMap, S extends SessionProjectionStateMap[K], >( definition: Omit<ProjectionDefinition<K, S>, 'wire'> & { wire: NonNullable<ProjectionDefinition<K, S>['wire']> }, ): () => void
|
||||
register<K extends keyof SessionProjectionMap, S>( definition: Omit<ProjectionDefinition<K, S>, 'wire'> & { wire: NonNullable<ProjectionDefinition<K, S>['wire']> }, ): Promise<ProjectionHandle<S>>
|
||||
|
||||
/**
|
||||
* Register one host-only unit. Its state is omitted from client snapshots
|
||||
* and always checkpointed like every other unit.
|
||||
* Callers must await registration before publishing consumers; registration may perform asynchronous work.
|
||||
* @param definition - key, state schema, pure unit functions, and stateVersion.
|
||||
* @returns the exact disposer that unregisters this unit.
|
||||
* @returns a ready handle with synchronous state reads and the effect disposer.
|
||||
*/
|
||||
register< K extends Exclude<keyof SessionProjectionStateMap, keyof SessionProjectionMap>, S extends SessionProjectionStateMap[K], >( definition: Omit<ProjectionDefinition<K, S>, 'wire'>, ): () => void
|
||||
register<K extends string, S>( definition: ProjectionDefinition<K, S> & { key: Exclude<K, keyof SessionProjectionMap> }, ): Promise<ProjectionHandle<S>>
|
||||
|
||||
/**
|
||||
* Subscribe to the change feed. The registration is an effect on the
|
||||
@@ -317,16 +335,6 @@ register< K extends Exclude<keyof SessionProjectionStateMap, keyof SessionProjec
|
||||
*/
|
||||
onChanged(listener: ProjectionChangeListener): () => void
|
||||
|
||||
/**
|
||||
* Read one unit's current host state after materializing every registered
|
||||
* unit at the Session cursor. Unrelated wire views are not produced.
|
||||
* The returned value is live; callers must not mutate it.
|
||||
* @param session - the session whose state is read.
|
||||
* @param key - the registered unit key.
|
||||
* @returns current state, or `undefined` when the key is not registered.
|
||||
*/
|
||||
stateOf<K extends keyof SessionProjectionStateMap>( session: Session, key: K, ): SessionProjectionStateMap[K] | undefined
|
||||
|
||||
/**
|
||||
* One consistent cut over every registered client-visible unit for one session, read from
|
||||
* the watermark cache (missing cells fold lazily over the in-memory log).
|
||||
|
||||
@@ -11,3 +11,6 @@
|
||||
/token-meter/tokensurfacenode:
|
||||
en: e445d12b10f651d0
|
||||
zh: e93be5fb9da1da15
|
||||
/token-meter/contextpressureprojection:
|
||||
en: 37e477a82a9c3a83
|
||||
zh: ad7143becb29afe9
|
||||
@@ -53,6 +53,44 @@ interface TokenSurfaceNode {
|
||||
|
||||
Surface order is authoritative; replacement nodes can have higher durable seqs than later positional nodes. The snapshot is immutable and does not grow when the underlying replay fold advances.
|
||||
|
||||
## `ContextPressureProjection`
|
||||
|
||||
`ctx.tokenMeter.contextPressure(session)` synchronously reads the latest logged `pressureTokens` and `contextWindow`; the client projection also derives `projectedTokens` from surface changes. These display values do not price a new request; use `measure()` for request pricing.
|
||||
|
||||
Source: [`packages/llm/token-meter/src/projection.ts`](../../packages/llm/token-meter/src/projection.ts)
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Approximate context occupancy for a status display.
|
||||
*
|
||||
* The fields, when present, are deliberately NOT one atomic request
|
||||
* observation: each is a last-wins record of a different moment. Switching
|
||||
* models can therefore pair a fresh capacity with the previous route's
|
||||
* pressure until the next request reports usage. This is an intentional trade
|
||||
* — the value is a user-facing reference, not a billing or gating input. See
|
||||
* the token-meter README for the full rationale.
|
||||
*/
|
||||
interface ContextPressureProjection {
|
||||
/**
|
||||
* Provider-reported prompt size of the most recent request: uncached input
|
||||
* plus cache reads and writes. Response output is excluded, so this does not
|
||||
* grow as the current turn streams. Absent until a provider reports usage.
|
||||
*/
|
||||
pressureTokens?: number
|
||||
/**
|
||||
* What the NEXT request's prompt would cost: {@link pressureTokens} plus the
|
||||
* heuristic repricing of everything the surface gained or lost since that
|
||||
* sample. Only the delta is estimated, so the figure stays anchored to the
|
||||
* provider while still reacting the moment a compaction shadows a span —
|
||||
* which `pressureTokens` alone cannot do, since compaction reports no usage
|
||||
* of its own. Absent until a provider reports usage.
|
||||
*/
|
||||
projectedTokens?: number
|
||||
/** Newest recorded route capacity; absent when no adapter advertised one. */
|
||||
contextWindow?: number
|
||||
}
|
||||
```
|
||||
|
||||
<!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
|
||||
|
||||
<a id="cordis-surface"></a>
|
||||
@@ -68,6 +106,13 @@ Generated from source by `scripts/gen-cordis-catalog.ts` (verified fresh by `pnp
|
||||
Replay owner for one service-wide estimator and isolated per-session folds.
|
||||
|
||||
```ts cordis-catalog
|
||||
/**
|
||||
* Read the latest logged provider prompt size and route capacity.
|
||||
* @param session - Session whose committed usage and request capacity are read.
|
||||
* @returns context pressure; fields remain absent until their source events are recorded.
|
||||
*/
|
||||
contextPressure(session: Session): Pick<ContextPressureProjection, 'pressureTokens' | 'contextWindow'>
|
||||
|
||||
/**
|
||||
* Measure current request pressure and surface through the durable tail.
|
||||
*
|
||||
|
||||
@@ -53,6 +53,44 @@ interface TokenSurfaceNode {
|
||||
|
||||
表层顺序具有权威性;替换节点的持久 seq 可能高于位置排在其后的节点。该快照不可变,不会随底层回放折叠推进而增长。
|
||||
|
||||
## `ContextPressureProjection`
|
||||
|
||||
`ctx.tokenMeter.contextPressure(session)` 同步读取最新记录的 `pressureTokens` 和 `contextWindow`;客户端投影还根据表层变化派生 `projectedTokens`。这些显示值不为新请求计价;请求计价使用 `measure()`。
|
||||
|
||||
来源: [`packages/llm/token-meter/src/projection.ts`](../../packages/llm/token-meter/src/projection.ts)
|
||||
|
||||
```ts type-equiv
|
||||
/**
|
||||
* Approximate context occupancy for a status display.
|
||||
*
|
||||
* The fields, when present, are deliberately NOT one atomic request
|
||||
* observation: each is a last-wins record of a different moment. Switching
|
||||
* models can therefore pair a fresh capacity with the previous route's
|
||||
* pressure until the next request reports usage. This is an intentional trade
|
||||
* — the value is a user-facing reference, not a billing or gating input. See
|
||||
* the token-meter README for the full rationale.
|
||||
*/
|
||||
interface ContextPressureProjection {
|
||||
/**
|
||||
* Provider-reported prompt size of the most recent request: uncached input
|
||||
* plus cache reads and writes. Response output is excluded, so this does not
|
||||
* grow as the current turn streams. Absent until a provider reports usage.
|
||||
*/
|
||||
pressureTokens?: number
|
||||
/**
|
||||
* What the NEXT request's prompt would cost: {@link pressureTokens} plus the
|
||||
* heuristic repricing of everything the surface gained or lost since that
|
||||
* sample. Only the delta is estimated, so the figure stays anchored to the
|
||||
* provider while still reacting the moment a compaction shadows a span —
|
||||
* which `pressureTokens` alone cannot do, since compaction reports no usage
|
||||
* of its own. Absent until a provider reports usage.
|
||||
*/
|
||||
projectedTokens?: number
|
||||
/** Newest recorded route capacity; absent when no adapter advertised one. */
|
||||
contextWindow?: number
|
||||
}
|
||||
```
|
||||
|
||||
<!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
|
||||
|
||||
<a id="cordis-surface"></a>
|
||||
@@ -68,6 +106,13 @@ Generated from source by `scripts/gen-cordis-catalog.ts` (verified fresh by `pnp
|
||||
Replay owner for one service-wide estimator and isolated per-session folds.
|
||||
|
||||
```ts cordis-catalog
|
||||
/**
|
||||
* Read the latest logged provider prompt size and route capacity.
|
||||
* @param session - Session whose committed usage and request capacity are read.
|
||||
* @returns context pressure; fields remain absent until their source events are recorded.
|
||||
*/
|
||||
contextPressure(session: Session): Pick<ContextPressureProjection, 'pressureTokens' | 'contextWindow'>
|
||||
|
||||
/**
|
||||
* Measure current request pressure and surface through the durable tail.
|
||||
*
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
en: 6cc2cd9ced70bb09
|
||||
zh: 1c0e6cb4ba2ed637
|
||||
/tool-schema-catalog/tool-package-map:
|
||||
en: dec5cbb4c122c7e7
|
||||
zh: 90edee4efc205cf3
|
||||
en: 19883cdd5d6181cb
|
||||
zh: 7d0cac18206184ec
|
||||
/tool-schema-catalog/deepseek-ai-dsh-plugin-manager:
|
||||
en: e0143916bb634762
|
||||
zh: e0143916bb634762
|
||||
|
||||
@@ -21,9 +21,9 @@ This table connects model-visible tool names to the plugin package and service s
|
||||
| `@deepseek-ai/dsh-experimental-browser-use-stagehand-native` | `stagehand_act`, `stagehand_extract`, `stagehand_navigate`, `stagehand_observe`, `stagehand_screenshot`, `stagehand_tabs` | `ctx.browserUse`, `ctx.agents`, `ctx.tools`, `ctx.systemPrompt` | `tool/call`, `tool/result` | - | - |
|
||||
| `@deepseek-ai/dsh-tool-ask-user` | `ask_user_question` | `ctx.tools`, `ctx.userQuestions` | `tool/call`, `tool/result after an answer or timeout`, `late user/message` | - | ask_user_question keeps the original blocking behavior by default; set `mode: timed` to opt into a foreground timeout and pending result while the question remains answerable. In timed mode, `timeout: -1` keeps that call blocking indefinitely. |
|
||||
| `@deepseek-ai/dsh-tools` | `run_code` | `ctx.workingDirectory (Agent executions)`, `ctx.tools`, `ctx.ptcRuntime (execution time)`, `ctx.systemPrompt` | `tool/call`, `one tool/ptc-dispatch-start + tool/ptc-dispatch pair per bridged sub-call`, `tool/result` | - | Owned by the tool registry as a reserved transport outside filterable capability layers under `mode: ptc` (see the PTC mode Agent Note). Under `ptc` it is the registry's only wire contribution; the other visible capabilities are declared in a generated SDK section in the loaded runtime's language, and a program calls them through bindings scheduled under the native concurrency contract (submission-ordered starts and policy; concurrency-safe bodies overlap up to `maxParallelSubCalls`) that re-enter the complete guarded tool pipeline and link each nested execution to this outer result. |
|
||||
| `@deepseek-ai/dsh-plan-mode` | `exit_plan_mode` | `ctx.tools`, `ctx.systemPrompt`, `ctx.userQuestions (execution time, opportunistic)` | `tool/call`, `plan/mode inactive on an approved review`, `tool/result` | - | exit_plan_mode stays in the model-facing schema while planning is inactive so transitions add no tool-catalog churn on top of the plan-policy change. Its execute path rejects calls outside plan mode; in plan mode it presents the plan over the user-questions seam (approve / keep planning with feedback), and approval logs plan mode inactive at the step boundary. |
|
||||
| `@deepseek-ai/dsh-plan-mode` | `exit_plan_mode` | `ctx.tools`, `ctx.systemPrompt`, `ctx.agents`, `ctx.userQuestions (execution time, opportunistic)` | `tool/call`, `plan/mode inactive on an approved review`, `tool/result` | - | exit_plan_mode stays in the model-facing schema while planning is inactive so transitions add no tool-catalog churn on top of the plan-policy change. Its execute path rejects calls outside plan mode; in plan mode it presents the plan over the user-questions seam (approve / keep planning with feedback), and approval logs plan mode inactive at the step boundary. |
|
||||
| `@deepseek-ai/dsh-tool-bash` | `bash` | `ctx.workingDirectory`, `ctx.tools`, `ctx.shell`, `ctx.systemPrompt`, `ctx.shellEnv`, `ctx.jobs for run_in_background and the job-backed foreground path` | `tool/call`, `tool/result` | - | The bash tool is the model-facing consumer of the bash executor seam. With a job registry composed every call registers with the generic `ctx.jobs` runtime as it starts, collected/stopped through the `job_*` tools from `@deepseek-ai/dsh-tool-jobs`; without one, or with `enableRunInBackground: false`, the tool registers a foreground-only schema without the `run_in_background` parameter. |
|
||||
| `@deepseek-ai/dsh-tool-present` | `present` | `ctx.workingDirectory`, `ctx.tools`, `ctx.fs`, `ctx.sessionProjections` | `tool/call`, `deliverables/presented after a successful final result`, `tool/result` | - | Deliveries belong to the calling Session; Web ui-deliverables supplies source-file opening and cards. |
|
||||
| `@deepseek-ai/dsh-tool-present` | `present` | `ctx.workingDirectory`, `ctx.tools`, `ctx.fs`, `ctx.agents` | `tool/call`, `deliverables/presented after a successful final result`, `tool/result` | - | Deliveries belong to the calling Session; Web ui-deliverables supplies source-file opening and cards. |
|
||||
| `@deepseek-ai/dsh-tool-pwsh` | `pwsh` | `ctx.workingDirectory`, `ctx.tools`, `ctx.shell`, `ctx.systemPrompt`, `ctx.shellEnv`, `ctx.jobs for run_in_background and the job-backed foreground path` | `tool/call`, `tool/result` | - | The pwsh tool is the PowerShell-dialect consumer of the bash executor seam for Windows compositions (a PowerShell executor such as `@deepseek-ai/dsh-pwsh-local` backs `ctx.shell`); it mirrors the bash tool call-for-call minus sandbox controls — `run_in_background` runs register with the generic `ctx.jobs` runtime and are collected/stopped through the `job_*` tools, and the managed `DSH_*` environment comes from `@deepseek-ai/dsh-shell-env`. Each call runs in a fresh process (no persistent PTY session), with native `C:\...` paths and `$env:NAME` variables. |
|
||||
| `@deepseek-ai/dsh-tool-cordis` | `cordis_inspect_list`, `cordis_inspect_query` | `ctx.tools`, `ctx.cordisInspect` | `tool/call`, `tool/result` | - | Creator mode provides two read-only runtime inspection tools. The Cordis host runner supplies the inspection registry; Client queries require a connected page. Author persistent changes as bundles and install them with plugin_manager. |
|
||||
| `@deepseek-ai/dsh-tool-bash-persistent` | `bash` | `ctx.workingDirectory`, `ctx.tools`, `ctx.terminals`, `an owning Agent at execution time` | `tool/call`, `PTY shell state`, `tool/result` | - | One owner-isolated persistent bash tool; deployment composition supplies the PTY backend and may override the model-facing environment description. |
|
||||
@@ -37,7 +37,7 @@ This table connects model-visible tool names to the plugin package and service s
|
||||
| `@deepseek-ai/dsh-tool-lsp` | `lsp` | `ctx.workingDirectory`, `ctx.tools`, `ctx.lsp`, `ctx.systemPrompt` | `tool/call`, `tool/result` | - | The lsp tool keeps provider selection and language-server subprocesses behind ctx.lsp, so its model-visible schema stays stable across providers. Requires a registered provider (e.g. `@deepseek-ai/dsh-lsp-stdio`) at runtime; without one, a query returns the structured `LSP_UNAVAILABLE` error rather than changing the schema. |
|
||||
| `@deepseek-ai/dsh-tool-ralph` | `ralph` | `ctx.tools`, `ctx.workflowEngine`, `ctx.subagents`, `ctx.systemPrompt`, `a calling Agent (exec.agent parents every fresh round)` | `tool/call`, `tool/result`, `workflow and child session events during execution` | - | A fixed foreground workflow starts one fresh structured child per round; the model selects only the immutable objective and an optional round cap. |
|
||||
| `@deepseek-ai/dsh-tool-skill` | `skill` | `ctx.workingDirectory`, `ctx.tools`, `ctx.agents`, `ctx.skills` | `tool/call`, `tool/result`, `user/message replacement catalogs via agent.inject()` | - | - |
|
||||
| `@deepseek-ai/dsh-tool-session-query` | `session_event_read`, `session_event_search`, `session_event_trace`, `session_search`, `session_trace` | `ctx.tools`, `ctx.systemPrompt`, `ctx.sessionQuery`, `a calling Agent for workspace authority` | `tool/call`, `tool/result` | - | The five read-only tools hide provider cursors and authorize every result from the immutable calling agent session. The package is opt-in; compositions that need enforced deadlines or bounded inline output also mount the generic timeout or spill policies. |
|
||||
| `@deepseek-ai/dsh-tool-session-query` | `session_event_read`, `session_event_search`, `session_event_trace`, `session_search`, `session_trace` | `ctx.tools`, `ctx.systemPrompt`, `ctx.sessionQuery`, `ctx.agents`, `a calling Agent for workspace authority` | `tool/call`, `tool/result` | - | The five read-only tools hide provider cursors and authorize every result from the immutable calling agent session. The package is opt-in; compositions that need enforced deadlines or bounded inline output also mount the generic timeout or spill policies. |
|
||||
| `@deepseek-ai/dsh-experimental-tool-subagent-oar` | `subagent_oar` | `ctx.tools`, `ctx.subagents` | `tool/call`, `tool/result`, `parent subagent catalog and completion notices` | - | The subagent_oar tool selects among configured aliases for external providers. This catalog shows Codex and Claude Code aliases; each deployment chooses its providers. Native model and permission settings belong to the selected provider configuration. |
|
||||
| `@deepseek-ai/dsh-tool-subagent` | `list_subagent_models`, `subagent` | `ctx.tools`, `ctx.subagents`, `ctx.systemPrompt`, `ctx.sessionProjections`, `ctx.llm for model discovery and selected-route validation` | `tool/call`, `tool/result`, `child session events through the chosen provider` | `subagent`, `subagent_fork` | The load-time `toolName` selects the delegation name (default `subagent`); `modelSelection` adds optional route fields. `modelDiscovery` defaults off and registers shared discovery on one instance per scope. Web and Desktop presets enable spawn discovery and route fields and leave `subagent_fork` route fields disabled. The Host settings service records authorization for each new top-level Session; child Sessions inherit it. Without a recorded policy, discovery and explicit selection permit only the current parent route. Every delegation returns a child id promptly; local children accept follow-ups, while external backends report their single execution automatically. |
|
||||
| `@deepseek-ai/dsh-tool-subagent-control` | `interrupt_agent`, `list_agents`, `send_message` | `ctx.tools`, `ctx.subagents`, `ctx.agents` | `tool/call`, `tool/result`, `child session events through ctx.subagents` | - | The globally named discovery, interruption, and local-message tools over managed subagents: provider-bound `tool-subagent` instances register distinct delegation tools, while one instance of this package registers `send_message`, `interrupt_agent`, and `list_agents`. |
|
||||
|
||||
@@ -25,9 +25,9 @@
|
||||
| `@deepseek-ai/dsh-experimental-browser-use-stagehand-native` | `stagehand_act`、`stagehand_extract`、`stagehand_navigate`、`stagehand_observe`、`stagehand_screenshot`、`stagehand_tabs` | `ctx.browserUse`、`ctx.agents`、`ctx.tools`、`ctx.systemPrompt` | `tool/call`、`tool/result` | - | - |
|
||||
| `@deepseek-ai/dsh-tool-ask-user` | `ask_user_question` | `ctx.tools`、`ctx.userQuestions` | `tool/call`、`tool/result after an answer or timeout`、`late user/message` | - | ask_user_question 默认保持原有阻塞行为;设置 `mode: timed` 后才启用前台超时与 pending 结果,同时问题仍可回答;timed 模式内 `timeout: -1` 让本次调用无限期阻塞。 |
|
||||
| `@deepseek-ai/dsh-tools` | `run_code` | `ctx.workingDirectory (Agent executions)`、`ctx.tools`、`ctx.ptcRuntime (execution time)`、`ctx.systemPrompt` | `tool/call`、`one tool/ptc-dispatch-start + tool/ptc-dispatch pair per bridged sub-call`、`tool/result` | - | 在 `mode: ptc` 下,它由工具注册表所有,作为可过滤能力层之外的保留传输机制(参见 PTC mode Agent Note)。在 `ptc` 下,它是注册表对协议格式(wire format)的唯一贡献;其他可见能力在使用已加载运行时语言生成的 SDK 章节中声明。程序通过 binding 调用这些能力,调用按照原生并发约定调度:启动顺序和策略遵循提交顺序,并发安全的函数体最多重叠执行 `maxParallelSubCalls` 个。调用会重新进入完整且受守卫保护的工具流水线,并将每个嵌套执行关联到此外层结果。 |
|
||||
| `@deepseek-ai/dsh-plan-mode` | `exit_plan_mode` | `ctx.tools`、`ctx.systemPrompt`、`ctx.userQuestions (execution time, opportunistic)` | `tool/call`、`plan/mode inactive on an approved review`、`tool/result` | - | 规划未激活时,exit_plan_mode 仍保留在面向模型的 schema 中,这样状态转换不会在规划策略变更之外额外造成工具目录变动。其执行路径会拒绝规划模式之外的调用;在规划模式下,它通过用户交互 seam 提交计划(批准/根据反馈继续规划),批准后会在步骤边界记录规划模式已停用。 |
|
||||
| `@deepseek-ai/dsh-plan-mode` | `exit_plan_mode` | `ctx.tools`、`ctx.systemPrompt`、`ctx.agents`、`ctx.userQuestions (execution time, opportunistic)` | `tool/call`、`plan/mode inactive on an approved review`、`tool/result` | - | 规划未激活时,exit_plan_mode 仍保留在面向模型的 schema 中,这样状态转换不会在规划策略变更之外额外造成工具目录变动。其执行路径会拒绝规划模式之外的调用;在规划模式下,它通过用户交互 seam 提交计划(批准/根据反馈继续规划),批准后会在步骤边界记录规划模式已停用。 |
|
||||
| `@deepseek-ai/dsh-tool-bash` | `bash` | `ctx.workingDirectory`、`ctx.tools`、`ctx.shell`、`ctx.systemPrompt`、`ctx.shellEnv`、`ctx.jobs for run_in_background and the job-backed foreground path` | `tool/call`、`tool/result` | - | bash 工具是 bash 执行器 seam 面向模型的消费方。组合中有 job 注册表时,每次调用一启动就注册到通用 `ctx.jobs` 运行时,并通过 `job_*` 工具(来自 `@deepseek-ai/dsh-tool-jobs`)收集/停止;没有注册表或 `enableRunInBackground: false` 时,工具注册不带 `run_in_background` 参数的纯前台 schema。 |
|
||||
| `@deepseek-ai/dsh-tool-present` | `present` | `ctx.workingDirectory`、`ctx.tools`, `ctx.fs`, `ctx.sessionProjections` | `tool/call`, `deliverables/presented 在成功的最终结果之后`, `tool/result` | - | 交付归调用方 Session 所有;Web ui-deliverables 提供源文件打开与卡片。 |
|
||||
| `@deepseek-ai/dsh-tool-present` | `present` | `ctx.workingDirectory`、`ctx.tools`, `ctx.fs`, `ctx.agents` | `tool/call`, `deliverables/presented 在成功的最终结果之后`, `tool/result` | - | 交付归调用方 Session 所有;Web ui-deliverables 提供源文件打开与卡片。 |
|
||||
| `@deepseek-ai/dsh-tool-pwsh` | `pwsh` | `ctx.workingDirectory`、`ctx.tools`、`ctx.shell`、`ctx.systemPrompt`、`ctx.shellEnv`、`ctx.jobs for run_in_background and the job-backed foreground path` | `tool/call`、`tool/result` | - | pwsh 工具是 Windows 组合中 bash 执行器 seam 的 PowerShell 方言消费方(由 `@deepseek-ai/dsh-pwsh-local` 等 PowerShell 执行器为 `ctx.shell` 提供后端);除沙箱接口外,它逐项对应 bash 工具调用。使用 `run_in_background` 的运行会注册到通用 `ctx.jobs` 运行时,并通过 `job_*` 工具收集/停止;托管的 `DSH_*` 环境来自 `@deepseek-ai/dsh-shell-env`。每次调用都在新进程中运行,不使用持久 PTY 会话。路径采用原生 `C:\...` 形式,变量采用 `$env:NAME`。 |
|
||||
| `@deepseek-ai/dsh-tool-cordis` | `cordis_inspect_list`, `cordis_inspect_query` | `ctx.tools`, `ctx.cordisInspect` | `tool/call`, `tool/result` | - | 创造模式提供两个只读运行时检查工具。Cordis host runner 提供检查注册表;Client 查询需要已连接页面。持久化变更编写为组合包,再通过 plugin_manager 安装。 |
|
||||
| `@deepseek-ai/dsh-tool-bash-persistent` | `bash` | `ctx.workingDirectory`、`ctx.tools`、`ctx.terminals`、`an owning Agent at execution time` | `tool/call`、`PTY shell state`、`tool/result` | - | 一个按所有者隔离的持久 bash 工具;部署组合提供 PTY 后端,并可覆盖面向模型的环境描述。 |
|
||||
@@ -41,7 +41,7 @@
|
||||
| `@deepseek-ai/dsh-tool-lsp` | `lsp` | `ctx.workingDirectory`、`ctx.tools`、`ctx.lsp`、`ctx.systemPrompt` | `tool/call`、`tool/result` | - | lsp 工具将提供方选择和语言服务器子进程置于 ctx.lsp 之后,因此其模型可见 schema 在更换提供方时保持稳定。运行时要求已注册提供方,例如 `@deepseek-ai/dsh-lsp-stdio`;如果没有提供方,查询会返回结构化 `LSP_UNAVAILABLE` 错误,而不会改变 schema。 |
|
||||
| `@deepseek-ai/dsh-tool-ralph` | `ralph` | `ctx.tools`、`ctx.workflowEngine`、`ctx.subagents`、`ctx.systemPrompt`、`a calling Agent (exec.agent parents every fresh round)` | `tool/call`、`tool/result`、`workflow and child session events during execution` | - | 固定的前台工作流会在每个 Round 启动一个全新的结构化子级;模型只能选择不可变目标和可选的 Round 上限。 |
|
||||
| `@deepseek-ai/dsh-tool-skill` | `skill` | `ctx.workingDirectory`、`ctx.tools`、`ctx.agents`、`ctx.skills` | `tool/call`、`tool/result`、`user/message replacement catalogs via agent.inject()` | - | - |
|
||||
| `@deepseek-ai/dsh-tool-session-query` | `session_event_read`、`session_event_search`、`session_event_trace`、`session_search`、`session_trace` | `ctx.tools`、`ctx.systemPrompt`、`ctx.sessionQuery`、`a calling Agent for workspace authority` | `tool/call`、`tool/result` | - | 这 5 个只读工具会隐藏提供方游标,并根据不可变的调用 agent 会话为每个结果授权。该包需要选择启用;需要强制截止时间或限制行内输出的组合还会挂载通用超时或 spill 策略。 |
|
||||
| `@deepseek-ai/dsh-tool-session-query` | `session_event_read`、`session_event_search`、`session_event_trace`、`session_search`、`session_trace` | `ctx.tools`、`ctx.systemPrompt`、`ctx.sessionQuery`、`ctx.agents`、`a calling Agent for workspace authority` | `tool/call`、`tool/result` | - | 这 5 个只读工具会隐藏提供方游标,并根据不可变的调用 agent 会话为每个结果授权。该包需要选择启用;需要强制截止时间或限制行内输出的组合还会挂载通用超时或 spill 策略。 |
|
||||
| `@deepseek-ai/dsh-experimental-tool-subagent-oar` | `subagent_oar` | `ctx.tools`, `ctx.subagents` | `tool/call`, `tool/result`, `parent subagent catalog and completion notices` | - | subagent_oar 工具在为外部提供方配置的别名之间选择。本目录展示 Codex 和 Claude Code 别名;每个部署自行选择提供方。原生模型和权限设置由所选提供方的配置决定。 |
|
||||
| `@deepseek-ai/dsh-tool-subagent` | `list_subagent_models`、`subagent` | `ctx.tools`、`ctx.subagents`、`ctx.systemPrompt`、`ctx.sessionProjections`、`用于模型发现和所选路由校验的 ctx.llm` | `tool/call`、`tool/result`、`child session events through the chosen provider` | `subagent`、`subagent_fork` | 加载时的 `toolName` 选择委派名称(默认为 `subagent`);`modelSelection` 增加可选路由字段。`modelDiscovery` 默认关闭,每个作用域由一个实例注册共享发现工具。Web 与 Desktop preset 启用 spawn 发现和路由字段,并为 `subagent_fork` 保持路由字段关闭。Host 设置服务为每个新顶层 Session 记录授权;子 Session 继承该授权。没有已记录策略时,发现和显式选择只允许当前父级路由。每次委派都会立即返回子代理 id;本地子代理接受后续消息,外部后端自动汇报单次执行结果。 |
|
||||
| `@deepseek-ai/dsh-tool-subagent-control` | `interrupt_agent`、`list_agents`、`send_message` | `ctx.tools`、`ctx.subagents`、`ctx.agents` | `tool/call`、`tool/result`、`child session events through ctx.subagents` | - | 这些是用于发现、中断托管 subagent 和向本地子代理发送消息的全局命名工具:绑定提供方的 `tool-subagent` 实例注册不同的委派工具;本包的一个实例注册 `send_message`、`interrupt_agent` 和 `list_agents`。 |
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
# Bilingual-pair consistency record for guide.md (docs/i18n/README.md): per heading
|
||||
# section, a hash of its English and Chinese blocks outside code blocks and generated regions.
|
||||
# After editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/upgrade-guide/v0.2.1-alpha.3/session-projection-handles/guide.md
|
||||
/:
|
||||
en: b3f54a7eadb3398a
|
||||
zh: 6815b996b2cebc6f
|
||||
/session-projection-registration-handles:
|
||||
en: 5cf8cb0051860df5
|
||||
zh: 2578c6c3e77bc1c7
|
||||
/session-projection-registration-handles/change:
|
||||
en: 64c2492c43c7a263
|
||||
zh: ba5a15b959df496c
|
||||
/session-projection-registration-handles/migration:
|
||||
en: bd4310bb946b6f08
|
||||
zh: 2086e6e9c11bc4ed
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
kind: upgrade-guide
|
||||
description: "Session projection registration is asynchronous and returns a retained reader instead of a disposer."
|
||||
---
|
||||
# Session projection registration handles
|
||||
|
||||
English | [中文](guide.zh.md)
|
||||
|
||||
## Change
|
||||
|
||||
`@deepseek-ai/dsh-session-projection` changes `SessionProjectionRegistry.register(definition)` from a synchronous disposer to `Promise<ProjectionHandle<S>>`. Registry-level `stateOf(session, key)` and `SessionProjectionStateMap` are removed. Host state types stay local to `ProjectionDefinition<K, S>` and are inferred from `stateSchema` during registration. Registration errors now reject the returned promise. After the last registration is disposed, the unit leaves snapshots, checkpoints, and live notifications, but retained handles can still fold Session events on demand.
|
||||
|
||||
`@deepseek-ai/dsh-agent` and `@deepseek-ai/dsh-subagent` require `sessionProjections`. AgentRegistry owns turn and step state; consumers use `ctx.agents.turnBoundaryOf(session)` instead of importing `turnBoundaryProjectionDefinition`. Turn-state consumers, including the Claude Code mods bridge, require `agents`. Preset and context-pressure reads use `ctx.agentPresets.presetForSession(session)` and `ctx.tokenMeter.contextPressure(session)`. The API controller and mods bridge no longer register those definitions independently; their optional-service behavior applies when the owning service is absent. `@deepseek-ai/dsh-api-session-controller` also removes `installModelSelectionProjection`.
|
||||
|
||||
## Migration
|
||||
|
||||
1. Mount `SessionProjectionRegistry` before `AgentRegistry`, `SubagentRuntime`, and other projection owners in custom compositions. Include `@deepseek-ai/dsh-agent` wherever a plugin requires `agents`. Consumers of domain data inject the owning service.
|
||||
2. Remove host-state map augmentations and imports; pass the local state type as the second `ProjectionDefinition<K, S>` argument. Keep client-value declarations in `SessionProjectionMap`. For a plugin's own definitions, await `ctx.sessionProjections.register(definition)` in asynchronous `apply()` or `[Service.init]()` and retain the handle privately before publishing dependent operations. Keep existing function-plugin namespace exports.
|
||||
3. Replace `registry.stateOf(session, key)` with a business service method for another domain, or `handle.stateOf(session)` inside the owner. Replace stored unregister callbacks with `handle.dispose`, which always returns `Promise<void>`, and await explicit disposal; fiber disposal still releases registrations automatically. Await registration when handling initialization failures.
|
||||
4. Replace `installModelSelectionProjection(ctx)` with `await ctx.sessionProjections.register(modelSelectionProjection)`, importing the definition from `@deepseek-ai/dsh-api-session-controller/src/model-selection-projection.ts`.
|
||||
5. Typecheck custom plugins, then verify activation, a Session state read, and owner disposal. A released reader remains usable, while the released unit disappears from new snapshots once its final registration is gone.
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
kind: upgrade-guide
|
||||
description: "Session 投影注册改为异步,并返回保留读取能力的句柄而不是释放函数。"
|
||||
---
|
||||
# Session 投影注册句柄
|
||||
|
||||
[English](guide.md) | 中文
|
||||
|
||||
## 变更
|
||||
|
||||
`@deepseek-ai/dsh-session-projection` 的 `SessionProjectionRegistry.register(definition)` 从同步释放函数改为返回 `Promise<ProjectionHandle<S>>`。注册表级 `stateOf(session, key)` 与 `SessionProjectionStateMap` 已移除。宿主状态类型保留在各自的 `ProjectionDefinition<K, S>` 中,注册时从 `stateSchema` 推导。注册错误现在使返回的 Promise 拒绝。最后一次注册释放后,该投影单元不再参与快照、检查点和实时通知,但保留的句柄仍可按需折叠 Session 事件。
|
||||
|
||||
`@deepseek-ai/dsh-agent` 与 `@deepseek-ai/dsh-subagent` 依赖 `sessionProjections`。AgentRegistry 持有轮次和步骤状态;消费方使用 `ctx.agents.turnBoundaryOf(session)`,不再导入 `turnBoundaryProjectionDefinition`。包括 Claude Code mods 桥接在内的轮次状态消费方依赖 `agents`。预设与上下文压力分别通过 `ctx.agentPresets.presetForSession(session)` 和 `ctx.tokenMeter.contextPressure(session)` 读取。API 控制器与 mods 桥接不再独立注册这些定义;所属服务缺席时采用各自的可选服务行为。`@deepseek-ai/dsh-api-session-controller` 同时移除了 `installModelSelectionProjection`。
|
||||
|
||||
## 迁移
|
||||
|
||||
1. 在自定义组合中,先挂载 `SessionProjectionRegistry`,再挂载 `AgentRegistry`、`SubagentRuntime` 及其他投影所有者。组合中的插件依赖 `agents` 时,加入 `@deepseek-ai/dsh-agent`。领域数据的消费方注入所属服务。
|
||||
2. 删除宿主状态映射的声明合并与导入,将局部状态类型作为 `ProjectionDefinition<K, S>` 的第二个类型参数。保留 `SessionProjectionMap` 中的客户端值声明。对插件自己的定义,在异步 `apply()` 或 `[Service.init]()` 中等待 `ctx.sessionProjections.register(definition)`,并在提供依赖操作前于内部保存句柄。保留现有函数插件命名空间导出。
|
||||
3. 将 `registry.stateOf(session, key)` 替换为其他领域的业务服务方法,或所有者内部的 `handle.stateOf(session)`。将保存的取消注册回调替换为始终返回 `Promise<void>` 的 `handle.dispose`,并等待显式释放完成;fiber 释放仍会自动释放注册。处理初始化失败时须等待注册。
|
||||
4. 将 `installModelSelectionProjection(ctx)` 替换为 `await ctx.sessionProjections.register(modelSelectionProjection)`,从 `@deepseek-ai/dsh-api-session-controller/src/model-selection-projection.ts` 导入该定义。
|
||||
5. 对自定义插件执行类型检查,再验证激活、Session 状态读取与 owner 释放。已释放的读取句柄仍可使用,而投影单元在最后一次注册解除后会从新快照中消失。
|
||||
@@ -0,0 +1,16 @@
|
||||
# Bilingual-pair consistency record for guide.md (docs/i18n/README.md): per heading
|
||||
# section, a hash of its English and Chinese blocks outside code blocks and generated regions.
|
||||
# After editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/upgrade-guide/v0.2.1-rc.2/sdk-minimal-checkpoints/guide.md
|
||||
/:
|
||||
en: c81d35a7d1605742
|
||||
zh: 49a149c848fdbb35
|
||||
/sdk-minimal-requires-durable-checkpoints:
|
||||
en: a238e25e459a20cd
|
||||
zh: 871ee52936d06b24
|
||||
/sdk-minimal-requires-durable-checkpoints/change:
|
||||
en: 3ba980ee74955e34
|
||||
zh: 495fe6c0dd9001bf
|
||||
/sdk-minimal-requires-durable-checkpoints/migration:
|
||||
en: 43c3d55a0fa7a562
|
||||
zh: f2b087fb1387b517
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
kind: upgrade-guide
|
||||
description: "The sdk-minimal profile now stops model and tool dispatch when session persistence cannot complete a checkpoint."
|
||||
---
|
||||
|
||||
# SDK-minimal requires durable checkpoints
|
||||
|
||||
English | [中文](guide.zh.md)
|
||||
|
||||
## Change
|
||||
|
||||
The `sdk-minimal` profile enables `session-checkpoint-policy`. It waits for session history to flush before model requests, top-level tool execution, and agent steps. Previously these operations could proceed while JSONL writes were buffered; persistence errors could remain background warnings. A checkpoint failure now stops the dependent operation. First-use file-lock initialization is paid before the first model request, and checkpoint writes contribute to request latency. This does not change the Session format or guarantee that forced termination preserves the final unflushed response.
|
||||
|
||||
## Migration
|
||||
|
||||
1. Keep the selected `dsh_home` writable and allow request deadlines to cover storage initialization and checkpoint writes. A longer shutdown timeout alone does not affect a pre-request checkpoint.
|
||||
2. Check profile and invocation patches for overrides of `session-checkpoint-policy`. To receive the durability policy, retain its enabled default alongside `sessions`.
|
||||
3. Run a short SDK turn, close normally, and use `resume_session()` with the same home and session ID to verify restoration. Investigate checkpoint failures before retrying model or tool work; the operation does not run until the checkpoint succeeds.
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
kind: upgrade-guide
|
||||
description: "sdk-minimal profile 现在会在会话持久化无法完成检查点时停止模型和工具调度。"
|
||||
---
|
||||
|
||||
# SDK-minimal 要求持久化检查点
|
||||
|
||||
[English](guide.md) | 中文
|
||||
|
||||
## 变更
|
||||
|
||||
`sdk-minimal` profile 启用 `session-checkpoint-policy`,在模型请求、顶层工具执行和 agent step 之前等待会话历史刷盘。此前,JSONL 写入尚在缓冲时这些操作也可以继续,持久化错误可能只记录为后台警告。现在,检查点失败会停止依赖它的操作。首次文件锁初始化发生在第一次模型请求之前,检查点写入也计入请求延迟。此变更不修改 Session 格式,也不保证强制终止时保留最后尚未刷盘的回复。
|
||||
|
||||
## 迁移
|
||||
|
||||
1. 确保选定的 `dsh_home` 可写,并让请求超时覆盖存储初始化和检查点写入。仅延长关闭超时不会影响请求前的检查点。
|
||||
2. 检查 profile 和调用级 patch 是否覆盖了 `session-checkpoint-policy`。要使用此持久化策略,应在 `sessions` 旁保留其默认启用状态。
|
||||
3. 运行一轮 SDK 短对话,正常关闭,再使用相同 home 和会话 ID 调用 `resume_session()` 验证恢复。重试模型或工具操作前,先排查检查点失败;只有检查点成功后,操作才会执行。
|
||||
@@ -235,6 +235,7 @@
|
||||
"@deepseek-ai/dsh-cua": "workspace:*",
|
||||
"@deepseek-ai/dsh-experimental-claude-code-mods": "workspace:*",
|
||||
"@deepseek-ai/dsh-llm-replay": "workspace:*",
|
||||
"@deepseek-ai/dsh-subagent-dsh-sdk": "workspace:*",
|
||||
"@deepseek-ai/dsh-mcp-client": "workspace:*",
|
||||
"@deepseek-ai/dsh-tool-session-query": "workspace:*",
|
||||
"@deepseek-ai/dsh-web-fetch-http": "workspace:*",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import AgentRegistry from '@deepseek-ai/dsh-agent'
|
||||
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
|
||||
import { unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit'
|
||||
import type { Agent } from '@deepseek-ai/dsh-agent'
|
||||
import type { JobAppendOptions, JobHandle, JobOutcome } from '@deepseek-ai/dsh-jobs'
|
||||
@@ -9,6 +10,7 @@ import { Session, SessionId } from '@deepseek-ai/dsh-session'
|
||||
/** A registry with a tiny ring so eviction is cheap to reach. */
|
||||
export async function harness(): Promise<Context> {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(LocalJobRegistry, { retainBytes: 64, settledRetainBytes: 64 })
|
||||
ctx.jobs.attachController('job-controller-test')
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import AgentRegistry from '@deepseek-ai/dsh-agent'
|
||||
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
|
||||
import { unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit'
|
||||
import type { Agent } from '@deepseek-ai/dsh-agent'
|
||||
import type { JobId, JobOutcome } from '@deepseek-ai/dsh-jobs'
|
||||
@@ -45,6 +46,7 @@ async function harness(): Promise<{
|
||||
}> {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SessionStore)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(LocalJobRegistry)
|
||||
ctx.jobs.attachController('kill-test')
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import AgentRegistry from '@deepseek-ai/dsh-agent'
|
||||
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
|
||||
import { unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit'
|
||||
import type { Agent } from '@deepseek-ai/dsh-agent'
|
||||
import { JobId } from '@deepseek-ai/dsh-jobs'
|
||||
@@ -215,6 +216,7 @@ describe('JobController', () => {
|
||||
async function controllerHarness() {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SessionStore)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(LocalJobRegistry)
|
||||
await ctx.plugin(TypertRegistry)
|
||||
@@ -247,6 +249,7 @@ describe('JobController', () => {
|
||||
it('waits for the job registry instead of serving without one', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SessionStore)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(TypertRegistry)
|
||||
ctx.plugin(JobController, {})
|
||||
|
||||
@@ -86,10 +86,10 @@ describe.skipIf(!requiredArtifacts)('Goal Remote built LIB chain', () => {
|
||||
})
|
||||
await host.plugin({ inject: connectionHost.inject, apply: connectionHost.apply })
|
||||
await host.plugin(TypertRegistry)
|
||||
await host.plugin(SessionProjectionRegistry)
|
||||
await host.plugin(AgentRegistry)
|
||||
await host.plugin(RemoteAccessService)
|
||||
await host.plugin(TypertRemoteService)
|
||||
await host.plugin(SessionProjectionRegistry)
|
||||
await host.plugin(GoalService)
|
||||
host.typert.register(TYPERT)
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
en: 1fdf707322fb6996
|
||||
zh: 0204f7e9d4aa3cce
|
||||
/session-controller/use-this-package:
|
||||
en: f9e4042fae88f801
|
||||
zh: f379f78966d57c2e
|
||||
en: 8fcfa14545bd9d1c
|
||||
zh: c10c2bba792699ef
|
||||
/session-controller/client-references:
|
||||
en: 41bdda1ac32df558
|
||||
zh: aa5d0e325924066b
|
||||
|
||||
@@ -27,6 +27,8 @@ Desktop analytics follows the [product collection policy](../../client/product-a
|
||||
<a id="use-this-package"></a>
|
||||
## Use this package
|
||||
|
||||
The controller reads a Session's recorded preset through the optional `agentPresets.presetForSession(session)` service method. A null result means no preset was recorded; an absent service means preset identity is unavailable to the controller.
|
||||
|
||||
History pages and follow opening snapshots carry one `{ type: 'event', event: SessionWireEvent }` record per durable Session event. The Client retains each accepted record as one durable `SessionEventLikeEntry`; Assistant token boundaries remain inside the compact stream on `assistant/message` or `assistant/attempt`. Tool arguments, result content, failures, and `tool/result.data.meta` pass through unchanged; the controller does not resolve a Tool definition, run a presenter, or attach UI data.
|
||||
|
||||
The Client journal validates current Session event envelopes before publishing follow snapshots, live entries, or history pages. It reuses the browser-safe Session validators for required surface markers, exact replacement endpoints, earlier unique source seqs, embedded Assistant provider metadata, request-header omissions, and tool-error consistency. Invalid records fail without field stripping or normalization; range membership and source existence remain durable-log checks on the Host.
|
||||
|
||||
@@ -27,6 +27,8 @@ kind: "package-reference"
|
||||
<a id="use-this-package"></a>
|
||||
## 使用本包
|
||||
|
||||
控制器通过可选服务方法 `agentPresets.presetForSession(session)` 读取 Session 已记录的预设。null 结果表示未记录预设;服务缺席表示控制器无法获知预设身份。
|
||||
|
||||
历史页与 follow opening 快照为每个持久 Session 事件携带一条 `{ type: 'event', event: SessionWireEvent }` record。Client 把每条已接受 record 保留为一个持久 `SessionEventLikeEntry`;Assistant token 边界保留在 `assistant/message` 或 `assistant/attempt` 的紧凑流内。工具参数、结果内容、失败信息和 `tool/result.data.meta` 原样通过;控制器不解析工具定义、不运行展示转换器,也不附加 UI 数据。
|
||||
|
||||
Client journal 在发布 follow 快照、live entry 或历史页之前验证当前 Session 事件 envelope。它复用浏览器安全的 Session validator,检查必需的 surface marker、精确的 replacement endpoint、更早且唯一的 source seq、内嵌 Assistant 提供方元数据、request header 可选字段的省略规则以及工具错误一致性。无效 record 直接失败,不删除字段或归一化;范围成员与来源存在性仍由 Host 的持久日志检查。
|
||||
|
||||
@@ -16,7 +16,9 @@ import type { SessionInspection } from '@deepseek-ai/dsh-session-persistence'
|
||||
import { SessionQueryError, type SessionObservation } from '@deepseek-ai/dsh-session-query'
|
||||
import { RemoteError, remoteErrorOf } from '@deepseek-ai/dsh-typert-protocol'
|
||||
import type {} from '@deepseek-ai/dsh-typert-registry'
|
||||
import type { ModelSelection } from './types.ts'
|
||||
import type { ModelSelection, ModelSelectionProjectionState } from './types.ts'
|
||||
import type { ProjectionHandle } from '@deepseek-ai/dsh-session-projection'
|
||||
import { modelSelectionProjection } from './model-selection-projection.ts'
|
||||
|
||||
/** Cold Session identity absent from persistence. */
|
||||
export class ApiSessionNotFound extends Error {}
|
||||
@@ -168,13 +170,22 @@ export async function inspectApiSession(
|
||||
|
||||
/** Owns every operation that may create, resume, or configure a Web Agent. */
|
||||
export class ApiSessionAgentController {
|
||||
private modelSelectionProjectionHandle!: ProjectionHandle<ModelSelectionProjectionState>
|
||||
private readonly resumes = new Map<SessionId, Promise<Agent>>()
|
||||
private readonly claims = new Map<SessionId, Promise<void>>()
|
||||
private readonly selections = new WeakMap<Agent, InstalledSelection>()
|
||||
private readonly imageAdmissionChains = new WeakMap<Agent, Promise<void>>()
|
||||
|
||||
/** @param ctx - Host context carrying Agent, model, persistence, and Typert services. */
|
||||
constructor(private readonly ctx: Context) {
|
||||
constructor(private readonly ctx: Context) {}
|
||||
|
||||
/**
|
||||
* Acquire projection readers before exposing dependent operations.
|
||||
* @returns when all required registrations are ready.
|
||||
*/
|
||||
async initialize(): Promise<void> {
|
||||
const ctx = this.ctx
|
||||
this.modelSelectionProjectionHandle = await ctx.sessionProjections.register(modelSelectionProjection)
|
||||
ctx.typert.lookups.configure('agent', async (sessionId: SessionId) => {
|
||||
const found = await this.resolveAgent(sessionId)
|
||||
if ('error' in found) throw found.error
|
||||
@@ -289,10 +300,7 @@ export class ApiSessionAgentController {
|
||||
selectionFor(agent: Agent): InstalledSelection {
|
||||
const installed = this.selections.get(agent)
|
||||
if (installed !== undefined) return installed
|
||||
const projectionState = this.ctx.sessionProjections.stateOf(agent.session, 'modelSelection')
|
||||
if (projectionState === undefined) {
|
||||
throw new Error('api-session: required modelSelection projection is not registered')
|
||||
}
|
||||
const projectionState = this.modelSelectionProjectionHandle.stateOf(agent.session)
|
||||
let picked = projectionState.pending === null
|
||||
? undefined
|
||||
: agentModelSelection(projectionState.pending)
|
||||
@@ -361,10 +369,10 @@ export class ApiSessionAgentController {
|
||||
/**
|
||||
* Read the current Agent preset from the Session projection.
|
||||
* @param session - live Session whose projection state is available.
|
||||
* @returns the current preset, or undefined when the capability is absent.
|
||||
* @returns the current preset, or undefined without a preset service or recorded selection.
|
||||
*/
|
||||
presetForSession(session: Session): string | undefined {
|
||||
return this.ctx.sessionProjections.stateOf(session, 'agentPreset') ?? undefined
|
||||
return this.ctx.get('agentPresets')?.presetForSession(session) ?? undefined
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -542,8 +550,8 @@ export class ApiSessionAgentController {
|
||||
|
||||
/** Whether no turn has started, by the predicate the preset registry's `select` enforces. */
|
||||
private isBlank(session: Session): boolean {
|
||||
const boundary = this.ctx.sessionProjections.stateOf(session, 'turnBoundary')
|
||||
return boundary === undefined || (boundary.openTurnStartSeq === null && boundary.lastTurn === 0)
|
||||
const boundary = this.ctx.agents.turnBoundaryOf(session)
|
||||
return boundary.openTurnStartSeq === null && boundary.lastTurn === 0
|
||||
}
|
||||
|
||||
/** Without a roster, reuse cannot apply a named preset. */
|
||||
|
||||
@@ -4,7 +4,7 @@ import { hostname } from 'node:os'
|
||||
import { resolve } from 'node:path'
|
||||
import type { Agent } from '@deepseek-ai/dsh-agent'
|
||||
import type {} from '@deepseek-ai/dsh-fs'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import { Context, Service } from '@deepseek-ai/cordis'
|
||||
import z from '@deepseek-ai/schemastery'
|
||||
import { errorChain, ReasoningEffortId, type MessageSource } from '@deepseek-ai/dsh-llm'
|
||||
import type {} from '@deepseek-ai/dsh-client-file-upload'
|
||||
@@ -28,7 +28,6 @@ import { SessionHistoryController } from './history.ts'
|
||||
import { SessionFileReferences } from './file-references.ts'
|
||||
import { ApiSessionList } from './list.ts'
|
||||
import { buildModelCatalog, hasProviderApiKey } from './catalog.ts'
|
||||
import { installModelSelectionProjection } from './model-selection-projection.ts'
|
||||
import { SessionSkillCatalog } from './skill-catalog.ts'
|
||||
import { SessionMediaReferences } from './media-references.ts'
|
||||
import { ArchivedSessionGate } from './archived-session-gate.ts'
|
||||
@@ -137,16 +136,17 @@ export class SessionController extends TypertRemoteService {
|
||||
listWorkSliceMs: z.natural().min(1).default(16),
|
||||
})
|
||||
|
||||
private readonly agents: ApiSessionAgentController
|
||||
private readonly commands: SessionCommandController
|
||||
private readonly controlState: SessionControlController
|
||||
private readonly history: SessionHistoryController
|
||||
private readonly listState: ApiSessionList
|
||||
private agents!: ApiSessionAgentController
|
||||
private commands!: SessionCommandController
|
||||
private controlState!: SessionControlController
|
||||
private history!: SessionHistoryController
|
||||
private listState!: ApiSessionList
|
||||
private readonly openPath: (path: string, signal: AbortSignal) => Promise<void>
|
||||
private readonly fileApplications: typeof nativeFileApplications
|
||||
private readonly openFileApplication: typeof openNativeFileApplication
|
||||
private readonly revealPath: (path: string, signal: AbortSignal) => Promise<void>
|
||||
private readonly canOpenPath: () => boolean
|
||||
private readonly listWorkSliceMs: number
|
||||
private readonly promotions = new Set<Promise<void>>()
|
||||
|
||||
/**
|
||||
@@ -156,10 +156,20 @@ export class SessionController extends TypertRemoteService {
|
||||
*/
|
||||
constructor(ctx: Context, config: Config, internals: SessionControllerInternals = {}) {
|
||||
super(ctx, 'sessionController', { namespace: 'session' })
|
||||
this.listWorkSliceMs = SessionController.Config(config).listWorkSliceMs
|
||||
this.fileApplications = internals.fileApplications ?? nativeFileApplications
|
||||
this.openFileApplication = internals.openFileApplication ?? openNativeFileApplication
|
||||
this.openPath = internals.openPath ?? openNativeAssociatedPath
|
||||
this.revealPath = internals.revealPath ?? revealNativePath
|
||||
this.canOpenPath = internals.canOpenPath
|
||||
?? (() => config.nativeOpen ?? (internals.openPath !== undefined || canOpenNativePath()))
|
||||
}
|
||||
|
||||
protected async [Service.init](): Promise<void> {
|
||||
const ctx = this.ctx
|
||||
ctx.typert.resources.register('session', { field: 'sessionId' })
|
||||
const resolved = SessionController.Config(config)
|
||||
installModelSelectionProjection(ctx)
|
||||
this.agents = new ApiSessionAgentController(ctx)
|
||||
await this.agents.initialize()
|
||||
this.commands = new SessionCommandController(ctx, this.agents, process.cwd())
|
||||
ctx.effect(() => ctx.fileUploads.registerAgentResolver(async (sessionId) => {
|
||||
const result = await this.agents.resolveAgent(sessionId)
|
||||
@@ -173,13 +183,8 @@ export class SessionController extends TypertRemoteService {
|
||||
await Promise.allSettled([...this.promotions])
|
||||
}, 'session-controller.promotions')
|
||||
this.history = new SessionHistoryController(ctx, (observation) => { this.promote(observation) })
|
||||
this.listState = new ApiSessionList(ctx, resolved.listWorkSliceMs)
|
||||
this.fileApplications = internals.fileApplications ?? nativeFileApplications
|
||||
this.openFileApplication = internals.openFileApplication ?? openNativeFileApplication
|
||||
this.openPath = internals.openPath ?? openNativeAssociatedPath
|
||||
this.revealPath = internals.revealPath ?? revealNativePath
|
||||
this.canOpenPath = internals.canOpenPath
|
||||
?? (() => config.nativeOpen ?? (internals.openPath !== undefined || canOpenNativePath()))
|
||||
this.listState = new ApiSessionList(ctx, this.listWorkSliceMs)
|
||||
await this.listState.initialize()
|
||||
ctx.plugin(SessionFileReferences)
|
||||
ctx.plugin(SessionMediaReferences)
|
||||
ctx.plugin(SessionSkillCatalog)
|
||||
|
||||
@@ -80,8 +80,14 @@ export class ApiSessionList {
|
||||
* @param ctx - Host context carrying Session, query, persistence, and projection services.
|
||||
* @param workSliceMs - Resolved positive integral list-work budget in milliseconds.
|
||||
*/
|
||||
constructor(private readonly ctx: Context, private readonly workSliceMs: number) {
|
||||
ctx.sessionProjections.register<'sessionListMetadata', SessionListMetadata>({
|
||||
constructor(private readonly ctx: Context, private readonly workSliceMs: number) {}
|
||||
|
||||
/**
|
||||
* Acquire projection readers before exposing dependent operations.
|
||||
* @returns when all required registrations are ready.
|
||||
*/
|
||||
async initialize(): Promise<void> {
|
||||
await this.ctx.sessionProjections.register<'sessionListMetadata', SessionListMetadata>({
|
||||
key: 'sessionListMetadata',
|
||||
stateSchema: sessionListMetadataSchema,
|
||||
init: () => ({ blank: true, lastPromptAt: null }),
|
||||
@@ -89,19 +95,17 @@ export class ApiSessionList {
|
||||
wire: { viewSchema: sessionListMetadataSchema, view: state => state },
|
||||
stateVersion: 1,
|
||||
})
|
||||
ctx.inject(['attachments'], (attachmentCtx) => {
|
||||
ctx.sessionProjections.register<'imageLimits', null>({
|
||||
key: 'imageLimits',
|
||||
stateSchema: z.null(),
|
||||
init: () => null,
|
||||
apply: state => state,
|
||||
wire: {
|
||||
viewSchema: imageLimitsSchema,
|
||||
view: () => attachmentCtx.attachments.imageLimits,
|
||||
summary: false,
|
||||
},
|
||||
stateVersion: 1,
|
||||
})
|
||||
await this.ctx.sessionProjections.register<'imageLimits', null>({
|
||||
key: 'imageLimits',
|
||||
stateSchema: z.null(),
|
||||
init: () => null,
|
||||
apply: state => state,
|
||||
wire: {
|
||||
viewSchema: imageLimitsSchema,
|
||||
view: () => this.ctx.attachments.imageLimits,
|
||||
summary: false,
|
||||
},
|
||||
stateVersion: 1,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/** Durable model-selection intent and request-use projection. */
|
||||
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import type { SessionEvent } from '@deepseek-ai/dsh-session'
|
||||
import type { ProjectionDefinition } from '@deepseek-ai/dsh-session-projection'
|
||||
import { z } from 'zod'
|
||||
@@ -55,7 +54,8 @@ function applyModelSelectionProjection(
|
||||
: { lastUsed, pending }
|
||||
}
|
||||
|
||||
const modelSelectionProjection = {
|
||||
/** Last-used and pending model selections shared by Session controller readers. */
|
||||
export const modelSelectionProjection = {
|
||||
key: 'modelSelection',
|
||||
stateSchema: modelSelectionProjectionStateSchema,
|
||||
init: () => ({ lastUsed: null, pending: null }),
|
||||
@@ -73,11 +73,3 @@ function sameSelection(left: ModelSelection | null, right: ModelSelection | null
|
||||
&& left.model === right.model
|
||||
&& left.reasoningEffort === right.reasoningEffort)
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the durable model-selection projection when the registry is present.
|
||||
* @param ctx - Session Controller context.
|
||||
*/
|
||||
export function installModelSelectionProjection(ctx: Context): void {
|
||||
ctx.sessionProjections.register(modelSelectionProjection)
|
||||
}
|
||||
@@ -14,14 +14,6 @@ import type { JsonValue } from '@deepseek-ai/dsh-util-values'
|
||||
import type { WorkspaceId } from '@deepseek-ai/dsh-workspace/types'
|
||||
|
||||
declare module '@deepseek-ai/dsh-session-projection/types' {
|
||||
interface SessionProjectionStateMap {
|
||||
/** Host state persisted for cold Session list summaries. */
|
||||
sessionListMetadata: SessionListMetadata
|
||||
/** Host state for the boot-constant image-limit view. */
|
||||
imageLimits: null
|
||||
/** Durable model selection already used by a request and still pending for a later request. */
|
||||
modelSelection: ModelSelectionProjectionState
|
||||
}
|
||||
interface SessionProjectionMap {
|
||||
/** Persisted facts used to summarize a Session without activating it. */
|
||||
sessionListMetadata: SessionListMetadata
|
||||
|
||||
@@ -3,6 +3,7 @@ import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import AgentRegistry from '@deepseek-ai/dsh-agent'
|
||||
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
|
||||
import type { Agent } from '@deepseek-ai/dsh-agent'
|
||||
import { agentPresetProjectionDefinition } from '@deepseek-ai/dsh-agent-preset-registry'
|
||||
import SessionStore, { SESSION_FORMAT_VERSION, SessionLogOffset, SessionId } from '@deepseek-ai/dsh-session'
|
||||
@@ -17,7 +18,6 @@ import {
|
||||
ApiSessionNotFound,
|
||||
inspectApiSession,
|
||||
} from '../src/agent.ts'
|
||||
import { installModelSelectionProjection } from '../src/model-selection-projection.ts'
|
||||
import { installSessionReadTestServices, testSessionPersistence } from './test-remote.ts'
|
||||
|
||||
const roots: Context[] = []
|
||||
@@ -35,15 +35,17 @@ async function harness(): Promise<{ ctx: Context; agents: ApiSessionAgentControl
|
||||
roots.push(ctx)
|
||||
await ctx.plugin(TypertRegistry)
|
||||
await ctx.plugin(SessionStore)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
installSessionReadTestServices(ctx)
|
||||
ctx.sessionProjections.register(agentPresetProjectionDefinition)
|
||||
installModelSelectionProjection(ctx)
|
||||
await ctx.sessionProjections.register(agentPresetProjectionDefinition)
|
||||
ctx.provide('agentDefaultModel', {
|
||||
currentSelection: () => ({ provider: 'fixture', model: 'fixture-model' }),
|
||||
saveSelection: () => Promise.resolve(),
|
||||
} as never)
|
||||
return { ctx, agents: new ApiSessionAgentController(ctx) }
|
||||
const agents = new ApiSessionAgentController(ctx)
|
||||
await agents.initialize()
|
||||
return { ctx, agents }
|
||||
}
|
||||
|
||||
function header(id: string, cwd: string | null = '/workspace'): SessionHeader {
|
||||
@@ -287,14 +289,6 @@ describe('ApiSession Agent lookup and recovery', () => {
|
||||
})
|
||||
|
||||
describe('ApiSession model selection', () => {
|
||||
it('requires the model-selection projection', async () => {
|
||||
const { ctx, agents } = await harness()
|
||||
const live = agent(ctx, header('missing-model-projection'))
|
||||
vi.spyOn(ctx.sessionProjections, 'stateOf').mockReturnValue(undefined)
|
||||
|
||||
expect(() => agents.selectionFor(live)).toThrow('required modelSelection projection')
|
||||
})
|
||||
|
||||
it('reads a reasoning-free request and consumes only the exact pending selection', async () => {
|
||||
const { ctx, agents } = await harness()
|
||||
const logged = agent(ctx, header('logged-model'))
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import AgentRegistry from '@deepseek-ai/dsh-agent'
|
||||
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
|
||||
import type { Agent } from '@deepseek-ai/dsh-agent'
|
||||
import { unsupportedInbox } from '@deepseek-ai/dsh-agent-loop-testkit'
|
||||
import SessionStore, { SessionId } from '@deepseek-ai/dsh-session'
|
||||
@@ -16,6 +17,7 @@ interface Harness {
|
||||
async function harness(): Promise<Harness> {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SessionStore)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
const archived: SessionId[] = []
|
||||
ctx.provide('workspaceRegistry', { get archivedSessionIds() { return archived } } as never)
|
||||
@@ -107,6 +109,7 @@ describe('Archived-session gate: lifetime', () => {
|
||||
it('stops rejecting once the plugin fiber is disposed', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SessionStore)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
const archived: SessionId[] = []
|
||||
ctx.provide('workspaceRegistry', { get archivedSessionIds() { return archived } } as never)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import AgentRegistry from '@deepseek-ai/dsh-agent'
|
||||
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
|
||||
import type { Agent, AgentHandle, CreateAgentOptions } from '@deepseek-ai/dsh-agent'
|
||||
import type {} from '@deepseek-ai/dsh-agent-preset-registry'
|
||||
import { createUserMessage } from '@deepseek-ai/dsh-llm'
|
||||
@@ -47,6 +48,7 @@ async function baseContext(): Promise<Context> {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(TypertRegistry)
|
||||
await ctx.plugin(SessionStore)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
installSessionReadTestServices(ctx)
|
||||
ctx.provide('agentDefaultModel', {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { RemoteError } from '@deepseek-ai/dsh-typert-protocol'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import AgentRegistry from '@deepseek-ai/dsh-agent'
|
||||
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
|
||||
import type { Agent, Inbox, ModelSelectionRef } from '@deepseek-ai/dsh-agent'
|
||||
import { AttachmentError, AttachmentId } from '@deepseek-ai/dsh-attachment'
|
||||
import type { ImageAttachmentRef } from '@deepseek-ai/dsh-attachment'
|
||||
@@ -36,9 +37,10 @@ async function commandHarness(
|
||||
}> {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SessionStore)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
installSessionReadTestServices(ctx)
|
||||
ctx.sessionProjections.register(subagentIdentityProjectionDefinition)
|
||||
await ctx.sessionProjections.register(subagentIdentityProjectionDefinition)
|
||||
const sessionId = SessionId('commands-session')
|
||||
const ancestor = Session.create(SessionId('ancestor'))
|
||||
ancestor.append('subagent/descriptor', snapshotSubagentDescriptor({
|
||||
@@ -126,6 +128,7 @@ describe('Session queue commands', () => {
|
||||
it('preserves the cold Agent resolver rejection', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SessionStore)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
const error = new RemoteError('session/agent-busy', 'owned by a child', { reason: 'subagent-owned' })
|
||||
const controller = new SessionCommandController(ctx, {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import AgentRegistry from '@deepseek-ai/dsh-agent'
|
||||
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
|
||||
import { createInboxStub } from '@deepseek-ai/dsh-agent-loop-testkit'
|
||||
import type { Agent, ModelSelectionRef } from '@deepseek-ai/dsh-agent'
|
||||
import AttachmentStore, { AttachmentId } from '@deepseek-ai/dsh-attachment'
|
||||
@@ -35,6 +36,7 @@ async function uploadHarness(origin?: 'subagent'): Promise<{
|
||||
const ctx = new Context()
|
||||
onTestFinished(() => ctx.fiber.dispose())
|
||||
await ctx.plugin(SessionStore)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(CommandRuntime)
|
||||
const session = ctx.sessions.create(SESSION, {
|
||||
@@ -321,13 +323,10 @@ describe('Session file uploads', () => {
|
||||
new AbortController().signal,
|
||||
)
|
||||
await first.controller.prompt(promptRequest([{ type: 'file', receiptId: observed.receiptId }]), USER_SOURCE)
|
||||
first.ctx.emit('session/event', first.agent.session, {
|
||||
type: 'user/message',
|
||||
data: createUserMessage({
|
||||
content: [{ type: 'text', text: 'extension event' }],
|
||||
source: { kind: 'user', rpcId: 1 } as never,
|
||||
}),
|
||||
} as never)
|
||||
first.agent.session.append('user/message', createUserMessage({
|
||||
content: [{ type: 'text', text: 'extension event' }],
|
||||
source: { kind: 'user', rpcId: 1 } as never,
|
||||
}), { surfaceOp: 'append' })
|
||||
expect(first.uploads.resolve(first.agent, observed.receiptId)).toEqual(observed.file)
|
||||
first.agent.session.append('user/message', createUserMessage({
|
||||
content: [{ type: 'text', text: 'observed' }],
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import AgentRegistry from '@deepseek-ai/dsh-agent'
|
||||
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
|
||||
import type { Agent } from '@deepseek-ai/dsh-agent'
|
||||
import { createUserMessage } from '@deepseek-ai/dsh-llm'
|
||||
import SessionStore, { SESSION_FORMAT_VERSION, SessionId, SessionLogOffset } from '@deepseek-ai/dsh-session'
|
||||
@@ -23,6 +24,7 @@ describe('SessionController facade', () => {
|
||||
it('owns Host service methods and publishes Agent lifecycle projections', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SessionStore)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
const sessionId = SessionId('controller-session')
|
||||
const header: SessionHeader = {
|
||||
@@ -52,7 +54,7 @@ describe('SessionController facade', () => {
|
||||
bindPrompt: () => ({ commit: () => {}, [Symbol.dispose]: () => {} }),
|
||||
retirePrompt: () => {},
|
||||
} as never)
|
||||
const controller = createSessionTestController(ctx, defaults)
|
||||
const controller = await createSessionTestController(ctx, defaults)
|
||||
const status = vi.fn()
|
||||
const failure = vi.fn()
|
||||
const activity = vi.fn()
|
||||
@@ -144,6 +146,7 @@ describe('SessionController facade', () => {
|
||||
async (outcome) => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SessionStore)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
const sessionId = SessionId(`background-${outcome}`)
|
||||
const header: SessionHeader = {
|
||||
@@ -157,7 +160,7 @@ describe('SessionController facade', () => {
|
||||
events: [],
|
||||
}),
|
||||
}) as never)
|
||||
const controller = createSessionTestController(ctx, defaults)
|
||||
const controller = await createSessionTestController(ctx, defaults)
|
||||
const agents = (controller as unknown as { agents: ApiSessionAgentController }).agents
|
||||
const apiError = vi.fn()
|
||||
ctx.on('api-session/error', apiError)
|
||||
@@ -200,6 +203,7 @@ describe('SessionController facade', () => {
|
||||
it('waits for an admitted background promotion during teardown', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SessionStore)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
const sessionId = SessionId('background-disposal')
|
||||
const header: SessionHeader = {
|
||||
@@ -213,7 +217,7 @@ describe('SessionController facade', () => {
|
||||
events: [],
|
||||
}),
|
||||
}) as never)
|
||||
const controller = createSessionTestController(ctx, defaults)
|
||||
const controller = await createSessionTestController(ctx, defaults)
|
||||
const agents = (controller as unknown as { agents: ApiSessionAgentController }).agents
|
||||
const started = Promise.withResolvers<undefined>()
|
||||
const release = Promise.withResolvers<undefined>()
|
||||
|
||||
@@ -8,8 +8,8 @@ import type { SessionRecord } from '@deepseek-ai/dsh-session-query'
|
||||
import { resolve } from 'node:path'
|
||||
import { performance } from 'node:perf_hooks'
|
||||
import { scheduler } from 'node:timers/promises'
|
||||
import RemoteAccessService from '@deepseek-ai/dsh-api-remote-access'
|
||||
import SessionController from '../src/index.ts'
|
||||
import { createSessionTestController, TEST_IMAGE_LIMITS } from './test-remote.ts'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { ApiSessionList } from '../src/list.ts'
|
||||
|
||||
@@ -48,7 +48,9 @@ async function harness(rows: readonly Row[], summaryMs = 8, workSliceMs = 8) {
|
||||
await ctx.plugin(SessionStore)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
ctx.provide('attachments', { imageLimits: TEST_IMAGE_LIMITS } as never)
|
||||
const list = new ApiSessionList(ctx, workSliceMs)
|
||||
await list.initialize()
|
||||
const clock = { now: 0 }
|
||||
const prepared = new Map<string, Session>()
|
||||
const agents = new Map<string, { status: Agent['status'] }>()
|
||||
@@ -190,8 +192,10 @@ describe('Session-list work slices', () => {
|
||||
h.ctx.provide('fileUploads', { registerAgentResolver: () => dispose } as never)
|
||||
h.ctx.provide('agentDefaultModel', { currentSelection: () => ({ provider: 'test', model: 'test' }) } as never)
|
||||
h.ctx.provide('workspaceRegistry', { list: () => [], archivedSessionIds: [] } as never)
|
||||
new RemoteAccessService(h.ctx)
|
||||
const controller = new SessionController(h.ctx, { nativeOpen: false, listWorkSliceMs: 4 }, { canOpenPath: () => false })
|
||||
const controller = await createSessionTestController(h.ctx, {
|
||||
nativeOpen: false, listWorkSliceMs: 4, canOpenPath: () => false,
|
||||
defaultModelSelection: () => ({ provider: 'test', model: 'test' }), cwd: '/tmp',
|
||||
})
|
||||
const yieldWork = vi.spyOn(scheduler, 'yield')
|
||||
const result = await controller.list({}, new AbortController().signal)
|
||||
expect(result.items).toHaveLength(3)
|
||||
|
||||
@@ -47,10 +47,10 @@ async function harness(withPersistence = true) {
|
||||
const ctx = new Context()
|
||||
contexts.push(ctx)
|
||||
await ctx.plugin(SessionStore)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
ctx.sessionProjections.register(subagentCatalogProjectionDefinition)
|
||||
ctx.sessionProjections.register(titleProjectionDefinition)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.sessionProjections.register(subagentCatalogProjectionDefinition)
|
||||
await ctx.sessionProjections.register(titleProjectionDefinition)
|
||||
if (withPersistence) {
|
||||
await ctx.plugin(JsonlSessionPersistence, { root: join(root, 'logs'), compression: 'none' })
|
||||
const list = vi.spyOn(ctx.sessionPersistence, 'list').mockRejectedValue(new Error('unexpected corpus listing'))
|
||||
@@ -58,7 +58,7 @@ async function harness(withPersistence = true) {
|
||||
expect(list).not.toHaveBeenCalled()
|
||||
})
|
||||
}
|
||||
const controller = createSessionTestController(ctx, {
|
||||
const controller = await createSessionTestController(ctx, {
|
||||
defaultModelSelection: () => ({ provider: 'test', model: 'test' }),
|
||||
cwd: root,
|
||||
})
|
||||
|
||||
@@ -88,7 +88,7 @@ async function harness(): Promise<{
|
||||
const loop = await mountAgentLoopTestHarness(ctx)
|
||||
const shared = await loop.create(SHARED)
|
||||
const hidden = await loop.create(PRIVATE)
|
||||
const controller = createSessionTestController(ctx, { defaultModelSelection: () => ({ provider: 'p', model: 'm' }), cwd: '/tmp' })
|
||||
const controller = await createSessionTestController(ctx, { defaultModelSelection: () => ({ provider: 'p', model: 'm' }), cwd: '/tmp' })
|
||||
const as = (principal: Principal, method: string): Context => {
|
||||
const invocation: RemoteInvocation = {
|
||||
request: { namespace: 'session', method, args: {} },
|
||||
|
||||
Loaded 100 of 374 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user