dsh-context-codex 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +100 -0
- package/README.zh-CN.md +100 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/dist/pipeline.d.ts +19 -0
- package/dist/pipeline.d.ts.map +1 -0
- package/dist/pipeline.js +316 -0
- package/dist/pipeline.js.map +1 -0
- package/package.json +60 -0
- package/src/index.ts +37 -0
- package/src/pipeline.ts +284 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 dsh-context-zoo contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# dsh-context-codex
|
|
2
|
+
|
|
3
|
+
English | [简体中文](README.zh-CN.md)
|
|
4
|
+
|
|
5
|
+
This package adapts the local summary workflow from Codex to DeepSeek Harness. It handles token accounting, pressure checks, summary requests, history reduction, retries, and recent user-text retention. The default export is a DSH Cordis plugin; `createPipeline(config)` provides a standalone pipeline, and `strategy` exposes budgets and source metadata.
|
|
6
|
+
|
|
7
|
+
## npm installation
|
|
8
|
+
|
|
9
|
+
After release, install with `dsh plugin --profile web add dsh-context-codex`. Apply the host Session patch and generate the activation overlay as described in the [npm guide](https://github.com/zonemeen/dsh-context-zoo/blob/main/docs/publishing.md#using-the-published-packages). Installing the package alone does not replace the active context engine.
|
|
10
|
+
|
|
11
|
+
## Source checkout installation
|
|
12
|
+
|
|
13
|
+
Use DSH `0.1.7-rc.2` with the [Session writer patch](https://github.com/zonemeen/dsh-context-zoo/blob/main/patches/README.md) applied to the actual host. Follow the root [installation and activation instructions](https://github.com/zonemeen/dsh-context-zoo/blob/main/README.md#use-with-dsh), using `./packages/codex` as the package path and `codex` as the overlay generator's agent id.
|
|
14
|
+
|
|
15
|
+
The package declares `dsh.bundle.patch: []` in `package.json`, so DSH recognizes it as a bundle without default configuration layers. Activate Codex with the generated overlay after installation. The generator places the plugin in the profile's active compaction scopes and disables their native tool-result pruner. Use one context strategy per profile and rebuild this checkout after source changes. Persistent activation and strategy switching are covered in the root instructions.
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import codexContext from 'dsh-context-codex';
|
|
19
|
+
|
|
20
|
+
ctx.plugin(codexContext);
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Context flow
|
|
24
|
+
|
|
25
|
+
The diagram shows the default local compaction path. Recent user text is preserved inside the checkpoint; system/developer messages and unfinished tool calls stay outside the selected range.
|
|
26
|
+
|
|
27
|
+
```mermaid
|
|
28
|
+
flowchart TD
|
|
29
|
+
A["Estimate context from valid usage<br/>and new messages"] --> B{"Compaction trigger?"}
|
|
30
|
+
B -->|"Below 90% of window"| N["Keep current context"]
|
|
31
|
+
B -->|"Ordinary request overflow or HTTP 413"| H["Leave recovery to the host"]
|
|
32
|
+
B -->|"At least 90%, or manual"| C["Select a completed contiguous span"]
|
|
33
|
+
C --> D["Request a summary with live<br/>system/developer instructions"]
|
|
34
|
+
D -->|"Summary request overflows"| E["Drop oldest request item<br/>and paired tool counterpart"]
|
|
35
|
+
E --> D
|
|
36
|
+
D -->|"Retryable call error"| F["Back off; retry up to five times"]
|
|
37
|
+
F --> D
|
|
38
|
+
D -->|"Text returned"| G["Checkpoint: summary + newest user text<br/>(up to 20k tokens) + injected context"]
|
|
39
|
+
G --> V{"Valid, smaller, and input unchanged?"}
|
|
40
|
+
V -->|"Yes"| K["Atomically replace selected span<br/>with checkpoint"]
|
|
41
|
+
V -->|"No"| X["No checkpoint committed;<br/>selected history stays"]
|
|
42
|
+
D -->|"Cancelled or unrecoverable"| X
|
|
43
|
+
F -->|"Retries exhausted"| X
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
There is no independent tool-result prune stage. Overflow retries shorten only the auxiliary summary request. Original messages remain in the durable session log after a checkpoint is committed.
|
|
47
|
+
|
|
48
|
+
## Workflow
|
|
49
|
+
|
|
50
|
+
1. Estimate each message from its model-visible UTF-8 bytes at roughly four bytes per token. Plaintext reasoning does not add to the estimate. Images use a fixed estimate rather than their data URL length. Use the latest valid assistant usage after the live checkpoints as an anchor and add estimates for messages appended afterward; cache usage is counted once.
|
|
51
|
+
2. Trigger automatic compaction at 90% of the context window by default. The output-token limit is not subtracted from this threshold. Manual compaction bypasses the pressure check. Overflow or HTTP 413 from an ordinary model request is left to the host.
|
|
52
|
+
3. Select one completed contiguous span, preserving system/developer messages and unfinished tool calls. Prepare a local summary request with that span plus the live system/developer messages. The summary instruction is defined in `src/pipeline.ts`. Model calls and results are recorded in the DSH session.
|
|
53
|
+
4. If the summary request exceeds the context window, remove the oldest input item together with its tool-call or tool-result counterpart, then retry with the reduced input. Each reduction resets the ordinary error retry budget. Ordinary model-call errors allow five retries by default, with exponential backoff and jitter; cancellation stops the operation.
|
|
54
|
+
5. Retain the newest real user text within a default 20000-token budget, excluding contextual injections. When the oldest retained text exceeds the remaining budget, preserve its head and tail at valid UTF-8 boundaries and insert a truncation marker. The marker itself is additional to the text budget. Retained user content is text-only; images are not reattached. Recognized instructions, catalogs, snapshots, skills, plans, and agent context are preserved separately from the user-text budget. Newer snapshots, catalogs, and plans supersede earlier entries with the same source kind and form; distinct instruction and skill sources remain separate.
|
|
55
|
+
6. Commit the retained user text and summary together as a DSH checkpoint. A failed, cancelled, empty, truncated, or expanded summary leaves the selected history intact. Summary output containing tool calls or media is rejected. DSH also rejects a commit if the selected input changed during summarization. Retained user text remains available after session reload for both ordinary and explicit range compaction.
|
|
56
|
+
|
|
57
|
+
## Configuration
|
|
58
|
+
|
|
59
|
+
Find each `dsh-context-codex` row in the generated overlay and edit its `config`. The row is nested inside the selected group or preset; its id depends on that scope. DSH replaces the whole config object, so include the desired settings together. Example config fragment:
|
|
60
|
+
|
|
61
|
+
```yaml
|
|
62
|
+
config:
|
|
63
|
+
auto: true
|
|
64
|
+
thresholdRatio: 0.9
|
|
65
|
+
keepRecentTokens: 20000
|
|
66
|
+
maxSummaryAttempts: 6
|
|
67
|
+
summaryRetryDelayMs: 200
|
|
68
|
+
restoreContext: true
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
| Setting | Default and behavior |
|
|
72
|
+
| --- | --- |
|
|
73
|
+
| `auto` | `true`; enables pressure-triggered compaction |
|
|
74
|
+
| `thresholdRatio` | `0.9`; lower ratios trigger earlier, values above `0.9` remain capped at `0.9` |
|
|
75
|
+
| `reserveTokens` | `0`; caps the trigger at `contextWindow - reserveTokens` |
|
|
76
|
+
| `keepRecentTokens` | `20000`; budget for retained real user text |
|
|
77
|
+
| `maxSummaryTokens` | The active model route's output-token limit |
|
|
78
|
+
| `maxSummaryAttempts` | `6`; one initial request and up to five ordinary error retries, reset after each input reduction |
|
|
79
|
+
| `summaryRetryDelayMs` | `200`; initial exponential-backoff delay, with 10% jitter in either direction and a 60-second cap |
|
|
80
|
+
| `restoreContext` | `true`; preserves recognized injected context already recorded by DSH |
|
|
81
|
+
| `summarizationProvider`, `summarizationModel` | The active model route; set both to use a separate summary model |
|
|
82
|
+
|
|
83
|
+
This pipeline performs no tool-result pruning. `prune`, `summaryToolChars`, `maxOverflowRetries`, and `maxConsecutiveFailures` have no effect here. Summary input reduction continues until no removable item remains; it does not consume the ordinary error retry budget.
|
|
84
|
+
|
|
85
|
+
## Host adaptation and limits
|
|
86
|
+
|
|
87
|
+
DSH preserves its system/developer messages and unfinished tool calls. Retained user text is stored in checkpoint blocks, while native Codex rebuilds separate user-role items. Initial-context reinjection in native Codex depends on its world state and turn context; this plugin uses the messages recorded by DSH and cannot manufacture that native state.
|
|
88
|
+
|
|
89
|
+
The following native Codex modes are unsupported by this plugin:
|
|
90
|
+
|
|
91
|
+
- Remote V2 compaction with native `CompactionTrigger` and opaque `Compaction` response items.
|
|
92
|
+
- Token-budget resets that rebuild Codex initial context and world state.
|
|
93
|
+
- Before-turn compaction using the previous model and native context reinjection.
|
|
94
|
+
- Optional compaction after completed assistant output, which requires a matching DSH lifecycle hook and is disabled by default upstream.
|
|
95
|
+
|
|
96
|
+
Encrypted reasoning and opaque native response items are not represented by the DSH message format. File blocks and offloaded images are estimated from serialized attachment metadata, which can differ from the content sent by the provider. Original-detail image sizing is unavailable; inline images use a fixed default estimate. Native compact hooks, model-compatibility hashes, provider transport retries, and token accounting that excludes the context prefix also remain outside this plugin. The plugin implements the local summary workflow within these host capabilities; it does not implement every compaction mode in current Codex.
|
|
97
|
+
|
|
98
|
+
## Source
|
|
99
|
+
|
|
100
|
+
Original project: [openai/codex](https://github.com/openai/codex), inspected at [`e72da2b53805894878023d01949a25a082e0a5cb`](https://github.com/openai/codex/tree/e72da2b53805894878023d01949a25a082e0a5cb), licensed under [Apache-2.0](https://github.com/openai/codex/blob/e72da2b53805894878023d01949a25a082e0a5cb/LICENSE). The reference includes `codex-rs/core/src/compact.rs`, `codex-rs/core/src/context_manager/history.rs`, and `codex-rs/utils/output-truncation/src/lib.rs`.
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# dsh-context-codex
|
|
2
|
+
|
|
3
|
+
[English](README.md) | 简体中文
|
|
4
|
+
|
|
5
|
+
本包将 Codex 的本地摘要流程适配为 DeepSeek Harness 插件,独立负责 token 计量、压力检查、摘要请求、历史缩减、重试和最近用户文本保留。默认导出 DSH Cordis 插件;`createPipeline(config)` 提供独立流程,`strategy` 导出预算和来源信息。
|
|
6
|
+
|
|
7
|
+
## npm 安装
|
|
8
|
+
|
|
9
|
+
发布后可用 `dsh plugin --profile web add dsh-context-codex` 安装。按 [npm 指南](https://github.com/zonemeen/dsh-context-zoo/blob/main/docs/publishing.zh-CN.md#使用已发布的包) 应用宿主 Session 补丁并生成启用配置。仅安装包不会替换当前上下文引擎。
|
|
10
|
+
|
|
11
|
+
## 源码安装
|
|
12
|
+
|
|
13
|
+
使用 DSH `0.1.7-rc.2`,并将 [Session 写入补丁](https://github.com/zonemeen/dsh-context-zoo/blob/main/patches/README.zh-CN.md) 应用到实际宿主。按照根目录的 [安装与启用说明](https://github.com/zonemeen/dsh-context-zoo/blob/main/README.zh-CN.md#接入-dsh) 操作,包路径使用 `./packages/codex`,覆盖补丁生成器的 agent id 使用 `codex`。
|
|
14
|
+
|
|
15
|
+
本包在 `package.json` 中声明 `dsh.bundle.patch: []`,因此 DSH 将其识别为 bundle,但不会加载默认配置层。安装后通过生成的覆盖补丁启用 Codex。生成器会将插件放到 profile 已启用的压缩作用域中,并禁用相应的原生工具结果清理器。每个 profile 使用一种上下文策略,修改源码后重新构建本仓库。持久启用和策略切换步骤见根目录说明。
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import codexContext from 'dsh-context-codex';
|
|
19
|
+
|
|
20
|
+
ctx.plugin(codexContext);
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## 上下文流程图
|
|
24
|
+
|
|
25
|
+
下图展示默认的本地压缩流程。最近用户原文写入检查点;system/developer 消息和未完成的工具调用保留在所选区间之外。
|
|
26
|
+
|
|
27
|
+
```mermaid
|
|
28
|
+
flowchart TD
|
|
29
|
+
A["根据有效 usage 和新增消息估算上下文"] --> B{"压缩触发条件?"}
|
|
30
|
+
B -->|"低于窗口的 90%"| N["保留当前上下文"]
|
|
31
|
+
B -->|"普通请求溢出或 HTTP 413"| H["交给宿主处理恢复"]
|
|
32
|
+
B -->|"达到 90%,或手动触发"| C["选择一段已完成的连续历史"]
|
|
33
|
+
C --> D["请求摘要,同时提供当前<br/>system/developer 指令"]
|
|
34
|
+
D -->|"摘要请求溢出"| E["移除请求中最旧条目<br/>及配对工具调用或结果"]
|
|
35
|
+
E --> D
|
|
36
|
+
D -->|"可重试的调用错误"| F["退避等待;最多重试五次"]
|
|
37
|
+
F --> D
|
|
38
|
+
D -->|"返回文本"| G["检查点:摘要 + 最近用户原文<br/>(最多 20k tokens)+ 注入上下文"]
|
|
39
|
+
G --> V{"结果有效、更小,且输入未变化?"}
|
|
40
|
+
V -->|"是"| K["原子替换所选区间为检查点"]
|
|
41
|
+
V -->|"否"| X["不提交检查点;所选历史保留"]
|
|
42
|
+
D -->|"取消或不可恢复"| X
|
|
43
|
+
F -->|"重试耗尽"| X
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
本流程没有独立的工具结果 prune 阶段。溢出重试只缩减用于生成摘要的辅助请求。检查点提交后,原始消息仍保留在持久会话日志中。
|
|
47
|
+
|
|
48
|
+
## 流程
|
|
49
|
+
|
|
50
|
+
1. 按模型可见内容的 UTF-8 字节数逐条估算消息,约每四字节一个 token。明文 reasoning 不增加估算值,图片使用固定估算值,不按 data URL 长度计量。使用当前检查点之后最近一次有效 assistant usage 作为锚点,加上其后新增消息的估算值;缓存用量只计算一次。
|
|
51
|
+
2. 默认在上下文窗口的 90% 触发自动压缩,不从阈值中扣除输出 token 上限。手动压缩绕过压力检查;普通模型请求的上下文溢出和 HTTP 413 由宿主处理。
|
|
52
|
+
3. 选择一段已完成的连续历史,保留 system/developer 消息和未完成的工具调用;将选定历史连同当前的 system/developer 消息发送给本地摘要流程,使用 `src/pipeline.ts` 中定义的摘要指令。模型调用和结果记录到 DSH 会话。
|
|
53
|
+
4. 摘要请求超出上下文窗口时,移除最旧输入条目及其配对的工具调用或工具结果,再以缩减后的输入重试。每次缩减都会重置普通错误的重试计数。普通模型调用错误默认最多重试五次,采用带随机抖动的指数退避;取消操作会停止流程。
|
|
54
|
+
5. 默认在 20000-token 预算内保留最近的真实用户文本,排除上下文注入。最旧一条待保留文本超过剩余预算时,在有效 UTF-8 边界保留首尾并插入截断标记;标记本身的长度不计入文本预算。只保留用户文本,不重新附加图片。已识别的指令、目录、快照、技能、计划和 agent 上下文单独保留,不占用户文本预算。source kind 和 form 相同的快照、目录与计划保留最新版本;不同来源的指令和技能分别保留。
|
|
55
|
+
6. 将保留的用户文本与摘要一起提交为 DSH 检查点。摘要失败、取消、为空、被截断或结果膨胀时保留所选历史;包含工具调用或媒体的摘要也会被拒绝。摘要过程中输入发生变化时,DSH 同样拒绝提交。普通压缩和指定范围压缩保留的用户文本在重新加载会话后仍可恢复。
|
|
56
|
+
|
|
57
|
+
## 配置
|
|
58
|
+
|
|
59
|
+
在生成的覆盖补丁中找到各个 `dsh-context-codex` 配置行,编辑其 `config`。这些行位于对应分组或预设内部,id 取决于作用域。DSH 替换整个 config 对象,需要的设置应放在一起。下面展示 config 片段:
|
|
60
|
+
|
|
61
|
+
```yaml
|
|
62
|
+
config:
|
|
63
|
+
auto: true
|
|
64
|
+
thresholdRatio: 0.9
|
|
65
|
+
keepRecentTokens: 20000
|
|
66
|
+
maxSummaryAttempts: 6
|
|
67
|
+
summaryRetryDelayMs: 200
|
|
68
|
+
restoreContext: true
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
| 设置 | 默认值与行为 |
|
|
72
|
+
| --- | --- |
|
|
73
|
+
| `auto` | `true`;启用压力触发的自动压缩 |
|
|
74
|
+
| `thresholdRatio` | `0.9`;调低可提前触发,大于 `0.9` 时仍以 `0.9` 为上限 |
|
|
75
|
+
| `reserveTokens` | `0`;将触发阈值限制在 `contextWindow - reserveTokens` 以内 |
|
|
76
|
+
| `keepRecentTokens` | `20000`;保留真实用户文本的预算 |
|
|
77
|
+
| `maxSummaryTokens` | 当前模型路由的输出 token 上限 |
|
|
78
|
+
| `maxSummaryAttempts` | `6`;首次请求加最多五次普通错误重试,每次缩减输入后重置 |
|
|
79
|
+
| `summaryRetryDelayMs` | `200`;指数退避的初始延迟,上下浮动 10%,最多等待 60 秒 |
|
|
80
|
+
| `restoreContext` | `true`;保留 DSH 已记录且已识别的注入上下文 |
|
|
81
|
+
| `summarizationProvider`、`summarizationModel` | 当前模型路由;使用独立摘要模型时需同时设置 |
|
|
82
|
+
|
|
83
|
+
本流程不执行工具结果清理,`prune`、`summaryToolChars`、`maxOverflowRetries` 和 `maxConsecutiveFailures` 在本插件中不生效。摘要输入持续缩减,直到没有可移除的条目;这个过程不消耗普通错误重试预算。
|
|
84
|
+
|
|
85
|
+
## 宿主适配与限制
|
|
86
|
+
|
|
87
|
+
DSH 保留自身的 system/developer 消息和未完成的工具调用。保留的用户文本写入检查点内容块,原生 Codex 则重建独立的 user 角色条目。Codex 的初始上下文重新注入依赖其 world state 和回合上下文;本插件使用 DSH 已记录的消息,无法生成缺失的原生状态。
|
|
88
|
+
|
|
89
|
+
本插件不支持以下原生 Codex 模式:
|
|
90
|
+
|
|
91
|
+
- 使用原生 `CompactionTrigger` 和不透明 `Compaction` 响应条目的远程 V2 压缩。
|
|
92
|
+
- 重新构建 Codex 初始上下文和 world state 的 token-budget 重置。
|
|
93
|
+
- 在回合开始前使用上一个模型压缩并重新注入原生上下文。
|
|
94
|
+
- assistant 输出完成后的可选压缩调度,需要对应的 DSH 生命周期钩子;该功能在上游默认关闭。
|
|
95
|
+
|
|
96
|
+
DSH 消息格式不能表示加密 reasoning 和不透明的原生响应条目。文件块与已外置图片按序列化后的附件元数据估算,可能与 provider 实际发送的内容不同。无法按图片原始细节级别计量;内联图片使用固定的默认估算值。原生 compact hooks、模型兼容性哈希、provider 传输重试,以及排除上下文前缀后的正文 token 计量,也不在本插件的实现范围内。本插件在这些宿主能力范围内实现本地摘要流程,不包含当前 Codex 的所有压缩模式。
|
|
97
|
+
|
|
98
|
+
## 来源
|
|
99
|
+
|
|
100
|
+
源项目为 [openai/codex](https://github.com/openai/codex),实际参考版本为 [`e72da2b53805894878023d01949a25a082e0a5cb`](https://github.com/openai/codex/tree/e72da2b53805894878023d01949a25a082e0a5cb),采用 [Apache-2.0](https://github.com/openai/codex/blob/e72da2b53805894878023d01949a25a082e0a5cb/LICENSE) 许可证。参考文件包括 `codex-rs/core/src/compact.rs`、`codex-rs/core/src/context_manager/history.rs` 和 `codex-rs/utils/output-truncation/src/lib.rs`。
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Local context compaction plugin for DeepSeek Harness. */
|
|
2
|
+
import { type ContextStrategy } from 'dsh-context-core';
|
|
3
|
+
export { createPipeline } from './pipeline.js';
|
|
4
|
+
/** Comparison metadata for the pinned Codex local summarization workflow. */
|
|
5
|
+
export declare const strategy: ContextStrategy;
|
|
6
|
+
declare const _default: import("dsh-context-core").ContextPlugin;
|
|
7
|
+
export default _default;
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,OAAO,EAAuB,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAE7E,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,6EAA6E;AAC7E,eAAO,MAAM,QAAQ,EAAE,eA4BtB,CAAC;;AAEF,wBAAgF"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** Local context compaction plugin for DeepSeek Harness. */
|
|
2
|
+
import { createContextPlugin } from 'dsh-context-core';
|
|
3
|
+
import { createPipeline, summaryInstruction } from './pipeline.js';
|
|
4
|
+
export { createPipeline } from './pipeline.js';
|
|
5
|
+
/** Comparison metadata for the pinned Codex local summarization workflow. */
|
|
6
|
+
export const strategy = {
|
|
7
|
+
id: 'codex',
|
|
8
|
+
name: 'Codex',
|
|
9
|
+
source: {
|
|
10
|
+
url: 'https://github.com/openai/codex',
|
|
11
|
+
revision: 'e72da2b53805894878023d01949a25a082e0a5cb',
|
|
12
|
+
license: 'Apache-2.0',
|
|
13
|
+
},
|
|
14
|
+
limitations: [
|
|
15
|
+
'Implements the local summarizing path. Remote Responses compaction V2 requires native CompactionTrigger and opaque Compaction items that the DSH model interface cannot carry.',
|
|
16
|
+
'DSH preserves system/developer messages and compacts one completed contiguous span into one user checkpoint. Retained user blocks and injected context keep their order inside that checkpoint; original message identities are not preserved.',
|
|
17
|
+
'Token-budget resets, post-turn scheduling, model-switch compatibility hashes, provider transport retries, and Codex compact hooks require native host capabilities and are not simulated.',
|
|
18
|
+
'Usage uses DSH aggregate prompt/output accounting and UTF-8 estimates. Body-after-prefix budgets, encrypted reasoning, original-detail image sizing, and Codex world-state reconstruction are unavailable through this host.',
|
|
19
|
+
'DSH commits checkpoints atomically and rejects empty, truncated, or non-shrinking summaries. Context-window recovery applies to summary requests; ordinary failed model requests are left to the host.',
|
|
20
|
+
],
|
|
21
|
+
budget(input) {
|
|
22
|
+
return {
|
|
23
|
+
triggerTokens: Math.max(0, Math.min(Math.floor(input.contextWindow * Math.min(0.9, input.thresholdRatio ?? 0.9)), input.contextWindow - (input.reserveTokens ?? 0))),
|
|
24
|
+
retainTokens: input.keepRecentTokens ?? 20_000,
|
|
25
|
+
};
|
|
26
|
+
},
|
|
27
|
+
summaryInstructions: summaryInstruction,
|
|
28
|
+
maxSummaryTokens: 32_000,
|
|
29
|
+
summaryToolChars: 0,
|
|
30
|
+
prune: { enabled: false, keepRecentTools: 0, protectTokens: 0, minSavingsTokens: 0 },
|
|
31
|
+
};
|
|
32
|
+
export default createContextPlugin({ id: strategy.id, create: createPipeline });
|
|
33
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,OAAO,EAAE,mBAAmB,EAAwB,MAAM,kBAAkB,CAAC;AAC7E,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,6EAA6E;AAC7E,MAAM,CAAC,MAAM,QAAQ,GAAoB;IACvC,EAAE,EAAE,OAAO;IACX,IAAI,EAAE,OAAO;IACb,MAAM,EAAE;QACN,GAAG,EAAE,iCAAiC;QACtC,QAAQ,EAAE,0CAA0C;QACpD,OAAO,EAAE,YAAY;KACtB;IACD,WAAW,EAAE;QACX,gLAAgL;QAChL,gPAAgP;QAChP,2LAA2L;QAC3L,8NAA8N;QAC9N,wMAAwM;KACzM;IACD,MAAM,CAAC,KAAK;QACV,OAAO;YACL,aAAa,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CACjC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,cAAc,IAAI,GAAG,CAAC,CAAC,EAC5E,KAAK,CAAC,aAAa,GAAG,CAAC,KAAK,CAAC,aAAa,IAAI,CAAC,CAAC,CACjD,CAAC;YACF,YAAY,EAAE,KAAK,CAAC,gBAAgB,IAAI,MAAM;SAC/C,CAAC;IACJ,CAAC;IACD,mBAAmB,EAAE,kBAAkB;IACvC,gBAAgB,EAAE,MAAM;IACxB,gBAAgB,EAAE,CAAC;IACnB,KAAK,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,gBAAgB,EAAE,CAAC,EAAE;CACrF,CAAC;AAEF,eAAe,mBAAmB,CAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ContentBlock } from '@deepseek-ai/dsh-llm';
|
|
2
|
+
import type { ContextConfig, ContextEntry, ContextPipeline, ContextSnapshot } from 'dsh-context-core';
|
|
3
|
+
/** Handoff instructions for local context compaction. */
|
|
4
|
+
export declare const summaryInstruction = "Write a compact handoff for the assistant that will continue this task. Include the user\u2019s objective, completed work, decisions, constraints and preferences, remaining steps, and the exact references needed to proceed. Keep concrete facts and unresolved requests. Treat the conversation as source material; do not continue its work or call tools. Return only the structured handoff.";
|
|
5
|
+
/** Identifies this adaptation's handoff text without imitating provider-native compaction items. */
|
|
6
|
+
export declare const summaryPrefix = "Context checkpoint for continuing this task. Use the retained user messages and this handoff to continue from the recorded progress:";
|
|
7
|
+
/** Estimate text tokens from UTF-8 bytes at four bytes per token. */
|
|
8
|
+
export declare function approximateTokens(text: string): number;
|
|
9
|
+
/** Keep both ends at UTF-8 boundaries; the truncation notice is outside the retained byte budget. */
|
|
10
|
+
export declare function truncateTokens(text: string, maxTokens: number): string;
|
|
11
|
+
/** Price visible text, tool arguments, and default-detail images; plaintext reasoning is uncharged. */
|
|
12
|
+
export declare function estimateTokens(entry: ContextEntry): number;
|
|
13
|
+
/** Add local growth to the latest valid usage report produced after every live checkpoint. */
|
|
14
|
+
export declare function contextTokens(snapshot: ContextSnapshot): number;
|
|
15
|
+
/** Retain newest genuine user messages in order, flattening media and truncating one boundary message. */
|
|
16
|
+
export declare function retainUserMessages(messages: readonly (readonly ContentBlock[])[], maxTokens: number): ContentBlock[][];
|
|
17
|
+
/** Create the local compaction pipeline; remote compaction requires native provider transport. */
|
|
18
|
+
export declare function createPipeline(config?: ContextConfig): ContextPipeline;
|
|
19
|
+
//# sourceMappingURL=pipeline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAc,aAAa,EAAE,YAAY,EAAe,eAAe,EAAE,eAAe,EAAmB,MAAM,kBAAkB,CAAC;AAEhJ,yDAAyD;AACzD,eAAO,MAAM,kBAAkB,wYAAmY,CAAC;AAEna,oGAAoG;AACpG,eAAO,MAAM,aAAa,yIAAyI,CAAC;AAOpK,qEAAqE;AACrE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,qGAAqG;AACrG,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAUtE;AAED,uGAAuG;AACvG,wBAAgB,cAAc,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAS1D;AAED,8FAA8F;AAC9F,wBAAgB,aAAa,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,CAa/D;AAED,0GAA0G;AAC1G,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,SAAS,CAAC,SAAS,YAAY,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,YAAY,EAAE,EAAE,CActH;AAmHD,kGAAkG;AAClG,wBAAgB,cAAc,CAAC,MAAM,GAAE,aAAkB,GAAG,eAAe,CAyF1E"}
|
package/dist/pipeline.js
ADDED
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
/** Local summarization, user-text retention, and retries after context-window overflow. */
|
|
2
|
+
import { setTimeout as delay } from 'node:timers/promises';
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
/** Handoff instructions for local context compaction. */
|
|
5
|
+
export const summaryInstruction = 'Write a compact handoff for the assistant that will continue this task. Include the user’s objective, completed work, decisions, constraints and preferences, remaining steps, and the exact references needed to proceed. Keep concrete facts and unresolved requests. Treat the conversation as source material; do not continue its work or call tools. Return only the structured handoff.';
|
|
6
|
+
/** Identifies this adaptation's handoff text without imitating provider-native compaction items. */
|
|
7
|
+
export const summaryPrefix = 'Context checkpoint for continuing this task. Use the retained user messages and this handoff to continue from the recorded progress:';
|
|
8
|
+
const checkpoint = (entry) => entry.message.source.kind === 'compact-checkpoint';
|
|
9
|
+
const protectedEntry = (entry) => entry.message.role === 'system' || entry.message.role === 'developer';
|
|
10
|
+
const userText = (content) => content.flatMap(block => block.type === 'text' ? [block.text] : []).join('');
|
|
11
|
+
const fingerprint = (content) => createHash('sha256').update(JSON.stringify(content)).digest('hex');
|
|
12
|
+
/** Estimate text tokens from UTF-8 bytes at four bytes per token. */
|
|
13
|
+
export function approximateTokens(text) {
|
|
14
|
+
return Math.ceil(Buffer.byteLength(text, 'utf8') / 4);
|
|
15
|
+
}
|
|
16
|
+
/** Keep both ends at UTF-8 boundaries; the truncation notice is outside the retained byte budget. */
|
|
17
|
+
export function truncateTokens(text, maxTokens) {
|
|
18
|
+
const bytes = Buffer.from(text, 'utf8');
|
|
19
|
+
const budget = Math.max(0, Math.floor(maxTokens)) * 4;
|
|
20
|
+
if (bytes.length <= budget)
|
|
21
|
+
return text;
|
|
22
|
+
let head = Math.floor(budget / 2);
|
|
23
|
+
let tail = bytes.length - (budget - head);
|
|
24
|
+
while (head > 0 && (bytes[head] & 0xc0) === 0x80)
|
|
25
|
+
head--;
|
|
26
|
+
while (tail < bytes.length && (bytes[tail] & 0xc0) === 0x80)
|
|
27
|
+
tail++;
|
|
28
|
+
const removed = Math.ceil((bytes.length - budget) / 4);
|
|
29
|
+
return `${bytes.subarray(0, head).toString('utf8')}…${removed} tokens truncated…${bytes.subarray(tail).toString('utf8')}`;
|
|
30
|
+
}
|
|
31
|
+
/** Price visible text, tool arguments, and default-detail images; plaintext reasoning is uncharged. */
|
|
32
|
+
export function estimateTokens(entry) {
|
|
33
|
+
let bytes = entry.message.role === 'tool' ? Buffer.byteLength(entry.message.toolCallId + (entry.toolName ?? ''), 'utf8') : 0;
|
|
34
|
+
for (const block of entry.message.content) {
|
|
35
|
+
if (block.type === 'text')
|
|
36
|
+
bytes += Buffer.byteLength(block.text, 'utf8');
|
|
37
|
+
else if (block.type === 'tool-call')
|
|
38
|
+
bytes += Buffer.byteLength(block.name + block.arguments, 'utf8');
|
|
39
|
+
else if (block.type === 'image')
|
|
40
|
+
bytes += block.offloaded ? Buffer.byteLength(JSON.stringify(block.attachment), 'utf8') : 7_373;
|
|
41
|
+
else if (block.type === 'file')
|
|
42
|
+
bytes += Buffer.byteLength(JSON.stringify(block.attachment), 'utf8');
|
|
43
|
+
}
|
|
44
|
+
return Math.ceil(bytes / 4);
|
|
45
|
+
}
|
|
46
|
+
/** Add local growth to the latest valid usage report produced after every live checkpoint. */
|
|
47
|
+
export function contextTokens(snapshot) {
|
|
48
|
+
const entries = snapshot.entries;
|
|
49
|
+
const lastCheckpoint = entries.reduce((latest, entry) => checkpoint(entry) ? Math.max(latest, entry.seq) : latest, -1);
|
|
50
|
+
for (let index = entries.length - 1; index >= 0; index--) {
|
|
51
|
+
const entry = entries[index];
|
|
52
|
+
if (entry.message.role !== 'assistant' || entry.seq < lastCheckpoint || entry.finish === 'error' || entry.finish === 'aborted' || !entry.usage)
|
|
53
|
+
continue;
|
|
54
|
+
const usage = entry.usage;
|
|
55
|
+
// DSH reports uncached and cached input separately; the Codex input counter includes both.
|
|
56
|
+
const tokens = usage.totalTokens ?? usage.inputTokens + (usage.cacheReadTokens ?? 0) + (usage.cacheWriteTokens ?? 0) + usage.outputTokens;
|
|
57
|
+
if (!Number.isFinite(tokens) || tokens <= 0)
|
|
58
|
+
continue;
|
|
59
|
+
return tokens + entries.slice(index + 1).reduce((sum, value) => sum + estimateTokens(value), 0);
|
|
60
|
+
}
|
|
61
|
+
return entries.reduce((sum, entry) => sum + estimateTokens(entry), 0);
|
|
62
|
+
}
|
|
63
|
+
/** Retain newest genuine user messages in order, flattening media and truncating one boundary message. */
|
|
64
|
+
export function retainUserMessages(messages, maxTokens) {
|
|
65
|
+
const retained = [];
|
|
66
|
+
let remaining = Math.max(0, Math.floor(maxTokens));
|
|
67
|
+
for (const content of [...messages].reverse()) {
|
|
68
|
+
if (remaining === 0)
|
|
69
|
+
break;
|
|
70
|
+
const text = userText(content);
|
|
71
|
+
const tokens = approximateTokens(text);
|
|
72
|
+
retained.push(tokens <= remaining && content.every(block => block.type === 'text')
|
|
73
|
+
? content.map(block => ({ ...block }))
|
|
74
|
+
: [{ type: 'text', text: truncateTokens(text, remaining) }]);
|
|
75
|
+
if (tokens > remaining)
|
|
76
|
+
break;
|
|
77
|
+
remaining -= tokens;
|
|
78
|
+
}
|
|
79
|
+
return retained.reverse();
|
|
80
|
+
}
|
|
81
|
+
/** Select one contiguous, completed span while preserving every system/developer message and tool pair. */
|
|
82
|
+
function select(entries) {
|
|
83
|
+
const pending = new Set();
|
|
84
|
+
let start = -1;
|
|
85
|
+
let end = -1;
|
|
86
|
+
const candidate = () => {
|
|
87
|
+
if (start < 0 || end <= start)
|
|
88
|
+
return undefined;
|
|
89
|
+
const span = entries.slice(start, end);
|
|
90
|
+
return span.some(entry => entry.message.role === 'assistant' || entry.message.role === 'tool') ? span : undefined;
|
|
91
|
+
};
|
|
92
|
+
for (let index = 0; index < entries.length; index++) {
|
|
93
|
+
const entry = entries[index];
|
|
94
|
+
if (protectedEntry(entry)) {
|
|
95
|
+
const span = candidate();
|
|
96
|
+
if (span)
|
|
97
|
+
return span;
|
|
98
|
+
start = -1;
|
|
99
|
+
end = -1;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (start < 0 && pending.size === 0 && entry.message.role !== 'tool')
|
|
103
|
+
start = index;
|
|
104
|
+
for (const block of entry.message.content)
|
|
105
|
+
if (block.type === 'tool-call')
|
|
106
|
+
pending.add(block.id);
|
|
107
|
+
if (entry.message.role === 'tool')
|
|
108
|
+
pending.delete(entry.message.toolCallId);
|
|
109
|
+
if (start >= 0 && pending.size === 0)
|
|
110
|
+
end = index + 1;
|
|
111
|
+
}
|
|
112
|
+
return candidate();
|
|
113
|
+
}
|
|
114
|
+
/** Drop one oldest request item together with all tool counterparts it would orphan. */
|
|
115
|
+
function dropOldest(entries) {
|
|
116
|
+
const first = entries.find(entry => !protectedEntry(entry));
|
|
117
|
+
if (!first)
|
|
118
|
+
return undefined;
|
|
119
|
+
const removed = new Set([first.seq]);
|
|
120
|
+
const calls = new Set();
|
|
121
|
+
let changed = true;
|
|
122
|
+
while (changed) {
|
|
123
|
+
changed = false;
|
|
124
|
+
for (const entry of entries) {
|
|
125
|
+
const ids = entry.message.content.flatMap(block => block.type === 'tool-call' ? [block.id] : []);
|
|
126
|
+
if (entry.message.role === 'tool')
|
|
127
|
+
ids.push(entry.message.toolCallId);
|
|
128
|
+
if (!removed.has(entry.seq) && ids.some(id => calls.has(id))) {
|
|
129
|
+
removed.add(entry.seq);
|
|
130
|
+
changed = true;
|
|
131
|
+
}
|
|
132
|
+
if (removed.has(entry.seq))
|
|
133
|
+
for (const id of ids)
|
|
134
|
+
if (!calls.has(id)) {
|
|
135
|
+
calls.add(id);
|
|
136
|
+
changed = true;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return entries.filter(entry => !removed.has(entry.seq));
|
|
141
|
+
}
|
|
142
|
+
function errorCode(error) {
|
|
143
|
+
return error !== null && typeof error === 'object' && 'code' in error ? String(error.code) : '';
|
|
144
|
+
}
|
|
145
|
+
function contextOverflow(error) {
|
|
146
|
+
return /CONTEXT.*(EXCEED|OVERFLOW|LENGTH)|PROMPT_TOO_LONG/.test(errorCode(error).toUpperCase())
|
|
147
|
+
|| (error instanceof Error && /context.{0,35}(exceed|overflow|limit)|prompt.{0,16}too.?long|maximum context length/i.test(error.message));
|
|
148
|
+
}
|
|
149
|
+
function terminal(error) {
|
|
150
|
+
return /ABORT|CANCEL|INTERRUPT|SESSION_BUDGET_EXCEEDED/.test(errorCode(error).toUpperCase())
|
|
151
|
+
|| error instanceof Error && error.name === 'AbortError';
|
|
152
|
+
}
|
|
153
|
+
function injectedContext(entry) {
|
|
154
|
+
const source = entry.message.source;
|
|
155
|
+
if (entry.message.role !== 'user' || source.kind === 'user' || source.kind === 'compact-checkpoint')
|
|
156
|
+
return undefined;
|
|
157
|
+
const form = 'form' in source ? source.form : undefined;
|
|
158
|
+
if (form !== 'instructions' && form !== 'catalog' && form !== 'snapshot' && !/skill|plan|subagent|agent-instructions/.test(source.kind))
|
|
159
|
+
return undefined;
|
|
160
|
+
const key = form === 'snapshot' || form === 'catalog' || /plan/.test(source.kind)
|
|
161
|
+
? JSON.stringify([source.kind, form ?? null]) : JSON.stringify(source);
|
|
162
|
+
return { key, seq: entry.seq, content: entry.message.content.filter(block => block.type === 'text') };
|
|
163
|
+
}
|
|
164
|
+
/** Recover only state whose complete checkpoint content matches a live surface message. */
|
|
165
|
+
function retainedState(host, entries) {
|
|
166
|
+
const users = [];
|
|
167
|
+
const context = [];
|
|
168
|
+
for (const entry of entries) {
|
|
169
|
+
const source = entry.message.source;
|
|
170
|
+
if (source.kind === 'compact-checkpoint') {
|
|
171
|
+
const hash = fingerprint(entry.message.content);
|
|
172
|
+
const saved = host.records('codex/checkpoint').findLast(record => record.fingerprint === hash
|
|
173
|
+
&& typeof record.preparedAfterSeq === 'number' && record.preparedAfterSeq < entry.seq);
|
|
174
|
+
if (!saved)
|
|
175
|
+
continue;
|
|
176
|
+
if (Array.isArray(saved.users))
|
|
177
|
+
for (const message of saved.users) {
|
|
178
|
+
if (Array.isArray(message) && message.every(text => typeof text === 'string'))
|
|
179
|
+
users.push(message.map(text => ({ type: 'text', text })));
|
|
180
|
+
}
|
|
181
|
+
if (Array.isArray(saved.context))
|
|
182
|
+
for (const raw of saved.context) {
|
|
183
|
+
const value = raw;
|
|
184
|
+
if (value && typeof value === 'object' && 'key' in value && typeof value.key === 'string'
|
|
185
|
+
&& 'seq' in value && typeof value.seq === 'number' && Number.isSafeInteger(value.seq)
|
|
186
|
+
&& 'texts' in value && Array.isArray(value.texts)) {
|
|
187
|
+
const texts = value.texts.filter((text) => typeof text === 'string');
|
|
188
|
+
if (texts.length === value.texts.length)
|
|
189
|
+
context.push({ key: value.key, seq: value.seq, content: texts.map(text => ({ type: 'text', text })) });
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
else if (entry.message.role === 'user' && source.kind === 'user') {
|
|
194
|
+
if (!userText(entry.message.content).startsWith(summaryPrefix))
|
|
195
|
+
users.push([...entry.message.content]);
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
const injected = injectedContext(entry);
|
|
199
|
+
if (injected)
|
|
200
|
+
context.push(injected);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return { users, context };
|
|
204
|
+
}
|
|
205
|
+
function validateSummary(response) {
|
|
206
|
+
if (response.finish === 'max-tokens' || response.finish === 'length')
|
|
207
|
+
throw new Error('Codex summary was truncated at its output limit');
|
|
208
|
+
if (response.finish === 'error' || response.finish === 'aborted')
|
|
209
|
+
throw new Error(`Codex summary ${response.finish}`);
|
|
210
|
+
if (response.content.some(block => block.type !== 'text' && block.type !== 'reasoning'))
|
|
211
|
+
throw new Error('Codex summary must contain text without tool calls or media');
|
|
212
|
+
if (!response.text.trim())
|
|
213
|
+
throw new Error('Codex returned an empty context summary');
|
|
214
|
+
}
|
|
215
|
+
/** Create the local compaction pipeline; remote compaction requires native provider transport. */
|
|
216
|
+
export function createPipeline(config = {}) {
|
|
217
|
+
async function prepare(host, entries) {
|
|
218
|
+
host.signal.throwIfAborted();
|
|
219
|
+
const snapshot = await host.snapshot();
|
|
220
|
+
const preparedAfterSeq = snapshot.archive.reduce((latest, entry) => Math.max(latest, entry.seq), -1);
|
|
221
|
+
const selected = new Set(entries.map(entry => entry.seq));
|
|
222
|
+
let requestEntries = snapshot.entries.filter(entry => protectedEntry(entry) || selected.has(entry.seq));
|
|
223
|
+
const original = retainedState(host, entries);
|
|
224
|
+
const contexts = new Map();
|
|
225
|
+
for (const value of original.context)
|
|
226
|
+
if (!contexts.has(value.key) || contexts.get(value.key).seq < value.seq)
|
|
227
|
+
contexts.set(value.key, value);
|
|
228
|
+
for (const entry of snapshot.archive) {
|
|
229
|
+
const value = injectedContext(entry);
|
|
230
|
+
if (value && contexts.has(value.key) && contexts.get(value.key).seq < value.seq)
|
|
231
|
+
contexts.set(value.key, value);
|
|
232
|
+
}
|
|
233
|
+
for (const entry of snapshot.entries) {
|
|
234
|
+
const value = injectedContext(entry);
|
|
235
|
+
if (value && !selected.has(entry.seq) && value.seq >= (contexts.get(value.key)?.seq ?? Infinity))
|
|
236
|
+
contexts.delete(value.key);
|
|
237
|
+
}
|
|
238
|
+
const retained = {
|
|
239
|
+
users: retainUserMessages(original.users, config.keepRecentTokens ?? 20_000),
|
|
240
|
+
context: config.restoreContext === false ? [] : [...contexts.values()].sort((left, right) => left.seq - right.seq),
|
|
241
|
+
};
|
|
242
|
+
let retries = 0;
|
|
243
|
+
let response;
|
|
244
|
+
for (;;) {
|
|
245
|
+
host.signal.throwIfAborted();
|
|
246
|
+
try {
|
|
247
|
+
response = await host.summarize({
|
|
248
|
+
messages: requestEntries.map(entry => entry.message),
|
|
249
|
+
instruction: summaryInstruction,
|
|
250
|
+
maxTokens: config.maxSummaryTokens ?? snapshot.maxOutputTokens,
|
|
251
|
+
includeTools: false,
|
|
252
|
+
...config.summarizationProvider === undefined ? {} : { provider: config.summarizationProvider },
|
|
253
|
+
...config.summarizationModel === undefined ? {} : { model: config.summarizationModel },
|
|
254
|
+
});
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
catch (error) {
|
|
258
|
+
host.signal.throwIfAborted();
|
|
259
|
+
if (terminal(error))
|
|
260
|
+
throw error;
|
|
261
|
+
if (contextOverflow(error)) {
|
|
262
|
+
const smaller = dropOldest(requestEntries);
|
|
263
|
+
if (!smaller)
|
|
264
|
+
throw error;
|
|
265
|
+
requestEntries = smaller;
|
|
266
|
+
retries = 0;
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
if (retries >= (config.maxSummaryAttempts ?? 6) - 1)
|
|
270
|
+
throw error;
|
|
271
|
+
const wait = Math.min(60_000, Math.floor((config.summaryRetryDelayMs ?? 200) * 2 ** retries++ * (0.9 + Math.random() * 0.2)));
|
|
272
|
+
await delay(wait, undefined, { signal: host.signal });
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
host.signal.throwIfAborted();
|
|
276
|
+
validateSummary(response);
|
|
277
|
+
const beforeSummary = [];
|
|
278
|
+
const context = retained.context.flatMap(value => value.content);
|
|
279
|
+
retained.users.forEach((content, index) => {
|
|
280
|
+
if (index === retained.users.length - 1)
|
|
281
|
+
beforeSummary.push(...context);
|
|
282
|
+
beforeSummary.push({ type: 'text', text: `[Retained user message ${index + 1}]` }, ...content);
|
|
283
|
+
});
|
|
284
|
+
if (!retained.users.length)
|
|
285
|
+
beforeSummary.push(...context);
|
|
286
|
+
const summary = `${summaryPrefix}\n${response.text}`;
|
|
287
|
+
// Content correlation also covers explicit range compaction, whose host owns the later commit.
|
|
288
|
+
host.record('codex/checkpoint', {
|
|
289
|
+
fingerprint: fingerprint([...beforeSummary, { type: 'text', text: summary }]),
|
|
290
|
+
preparedAfterSeq,
|
|
291
|
+
users: retained.users.map(content => content.flatMap(block => block.type === 'text' ? [block.text] : [])),
|
|
292
|
+
context: retained.context.map(value => ({ key: value.key, seq: value.seq, texts: value.content.flatMap(block => block.type === 'text' ? [block.text] : []) })),
|
|
293
|
+
});
|
|
294
|
+
return { summary, beforeSummary };
|
|
295
|
+
}
|
|
296
|
+
return {
|
|
297
|
+
summarizeRange: prepare,
|
|
298
|
+
async run(host, trigger) {
|
|
299
|
+
host.signal.throwIfAborted();
|
|
300
|
+
// Upstream recovers overflow within the compaction request, not arbitrary failed user turns.
|
|
301
|
+
if (trigger === 'context-overflow' || trigger === 'request-too-large')
|
|
302
|
+
return null;
|
|
303
|
+
if (trigger === 'pressure' && config.auto === false)
|
|
304
|
+
return null;
|
|
305
|
+
const snapshot = await host.snapshot();
|
|
306
|
+
const threshold = Math.max(0, Math.min(Math.floor(snapshot.contextWindow * Math.min(0.9, config.thresholdRatio ?? 0.9)), snapshot.contextWindow - (config.reserveTokens ?? 0)));
|
|
307
|
+
if (trigger === 'pressure' && (snapshot.contextWindow <= 0 || contextTokens(snapshot) < threshold))
|
|
308
|
+
return null;
|
|
309
|
+
const selected = select(snapshot.entries);
|
|
310
|
+
if (!selected)
|
|
311
|
+
return null;
|
|
312
|
+
return host.compact(selected, () => prepare(host, selected));
|
|
313
|
+
},
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
//# sourceMappingURL=pipeline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dsh-context-codex",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"description": "Codex context management strategy adapted for DeepSeek Harness.",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/zonemeen/dsh-context-zoo.git",
|
|
10
|
+
"directory": "packages/codex"
|
|
11
|
+
},
|
|
12
|
+
"main": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"import": "./dist/index.js"
|
|
18
|
+
},
|
|
19
|
+
"./package.json": "./package.json"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist/**/*.js",
|
|
23
|
+
"dist/**/*.js.map",
|
|
24
|
+
"dist/**/*.d.ts",
|
|
25
|
+
"dist/**/*.d.ts.map",
|
|
26
|
+
"README.md",
|
|
27
|
+
"README.zh-CN.md",
|
|
28
|
+
"src",
|
|
29
|
+
"LICENSE"
|
|
30
|
+
],
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": "^22.19.0 || >=24.0.0"
|
|
33
|
+
},
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public",
|
|
36
|
+
"registry": "https://registry.npmjs.org/"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"dsh-context-core": "^0.1.1"
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"@deepseek-ai/dsh-agent": "0.1.7-rc.2",
|
|
43
|
+
"@deepseek-ai/dsh-commands": "0.1.7-rc.2",
|
|
44
|
+
"@deepseek-ai/dsh-compaction": "0.1.7-rc.2",
|
|
45
|
+
"@deepseek-ai/dsh-llm": "0.1.7-rc.2",
|
|
46
|
+
"@deepseek-ai/dsh-session": "0.1.7-rc.2",
|
|
47
|
+
"@deepseek-ai/dsh-token-meter": "0.1.7-rc.2",
|
|
48
|
+
"@deepseek-ai/cordis": "~4.0.4",
|
|
49
|
+
"@deepseek-ai/dsh-fs": "0.1.7-rc.2"
|
|
50
|
+
},
|
|
51
|
+
"dsh": {
|
|
52
|
+
"bundle": {
|
|
53
|
+
"patch": []
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"homepage": "https://github.com/zonemeen/dsh-context-zoo/tree/main/packages/codex#readme",
|
|
57
|
+
"bugs": {
|
|
58
|
+
"url": "https://github.com/zonemeen/dsh-context-zoo/issues"
|
|
59
|
+
}
|
|
60
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/** Local context compaction plugin for DeepSeek Harness. */
|
|
2
|
+
import { createContextPlugin, type ContextStrategy } from 'dsh-context-core';
|
|
3
|
+
import { createPipeline, summaryInstruction } from './pipeline.js';
|
|
4
|
+
export { createPipeline } from './pipeline.js';
|
|
5
|
+
|
|
6
|
+
/** Comparison metadata for the pinned Codex local summarization workflow. */
|
|
7
|
+
export const strategy: ContextStrategy = {
|
|
8
|
+
id: 'codex',
|
|
9
|
+
name: 'Codex',
|
|
10
|
+
source: {
|
|
11
|
+
url: 'https://github.com/openai/codex',
|
|
12
|
+
revision: 'e72da2b53805894878023d01949a25a082e0a5cb',
|
|
13
|
+
license: 'Apache-2.0',
|
|
14
|
+
},
|
|
15
|
+
limitations: [
|
|
16
|
+
'Implements the local summarizing path. Remote Responses compaction V2 requires native CompactionTrigger and opaque Compaction items that the DSH model interface cannot carry.',
|
|
17
|
+
'DSH preserves system/developer messages and compacts one completed contiguous span into one user checkpoint. Retained user blocks and injected context keep their order inside that checkpoint; original message identities are not preserved.',
|
|
18
|
+
'Token-budget resets, post-turn scheduling, model-switch compatibility hashes, provider transport retries, and Codex compact hooks require native host capabilities and are not simulated.',
|
|
19
|
+
'Usage uses DSH aggregate prompt/output accounting and UTF-8 estimates. Body-after-prefix budgets, encrypted reasoning, original-detail image sizing, and Codex world-state reconstruction are unavailable through this host.',
|
|
20
|
+
'DSH commits checkpoints atomically and rejects empty, truncated, or non-shrinking summaries. Context-window recovery applies to summary requests; ordinary failed model requests are left to the host.',
|
|
21
|
+
],
|
|
22
|
+
budget(input) {
|
|
23
|
+
return {
|
|
24
|
+
triggerTokens: Math.max(0, Math.min(
|
|
25
|
+
Math.floor(input.contextWindow * Math.min(0.9, input.thresholdRatio ?? 0.9)),
|
|
26
|
+
input.contextWindow - (input.reserveTokens ?? 0),
|
|
27
|
+
)),
|
|
28
|
+
retainTokens: input.keepRecentTokens ?? 20_000,
|
|
29
|
+
};
|
|
30
|
+
},
|
|
31
|
+
summaryInstructions: summaryInstruction,
|
|
32
|
+
maxSummaryTokens: 32_000,
|
|
33
|
+
summaryToolChars: 0,
|
|
34
|
+
prune: { enabled: false, keepRecentTools: 0, protectTokens: 0, minSavingsTokens: 0 },
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default createContextPlugin({ id: strategy.id, create: createPipeline });
|
package/src/pipeline.ts
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/** Local summarization, user-text retention, and retries after context-window overflow. */
|
|
2
|
+
import { setTimeout as delay } from 'node:timers/promises';
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
import type { ContentBlock } from '@deepseek-ai/dsh-llm';
|
|
5
|
+
import type { Checkpoint, ContextConfig, ContextEntry, ContextHost, ContextPipeline, ContextSnapshot, SummaryResponse } from 'dsh-context-core';
|
|
6
|
+
|
|
7
|
+
/** Handoff instructions for local context compaction. */
|
|
8
|
+
export const summaryInstruction = 'Write a compact handoff for the assistant that will continue this task. Include the user’s objective, completed work, decisions, constraints and preferences, remaining steps, and the exact references needed to proceed. Keep concrete facts and unresolved requests. Treat the conversation as source material; do not continue its work or call tools. Return only the structured handoff.';
|
|
9
|
+
|
|
10
|
+
/** Identifies this adaptation's handoff text without imitating provider-native compaction items. */
|
|
11
|
+
export const summaryPrefix = 'Context checkpoint for continuing this task. Use the retained user messages and this handoff to continue from the recorded progress:';
|
|
12
|
+
|
|
13
|
+
const checkpoint = (entry: ContextEntry): boolean => entry.message.source.kind === 'compact-checkpoint';
|
|
14
|
+
const protectedEntry = (entry: ContextEntry): boolean => entry.message.role === 'system' || entry.message.role === 'developer';
|
|
15
|
+
const userText = (content: readonly ContentBlock[]): string => content.flatMap(block => block.type === 'text' ? [block.text] : []).join('');
|
|
16
|
+
const fingerprint = (content: readonly ContentBlock[]): string => createHash('sha256').update(JSON.stringify(content)).digest('hex');
|
|
17
|
+
|
|
18
|
+
/** Estimate text tokens from UTF-8 bytes at four bytes per token. */
|
|
19
|
+
export function approximateTokens(text: string): number {
|
|
20
|
+
return Math.ceil(Buffer.byteLength(text, 'utf8') / 4);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Keep both ends at UTF-8 boundaries; the truncation notice is outside the retained byte budget. */
|
|
24
|
+
export function truncateTokens(text: string, maxTokens: number): string {
|
|
25
|
+
const bytes = Buffer.from(text, 'utf8');
|
|
26
|
+
const budget = Math.max(0, Math.floor(maxTokens)) * 4;
|
|
27
|
+
if (bytes.length <= budget) return text;
|
|
28
|
+
let head = Math.floor(budget / 2);
|
|
29
|
+
let tail = bytes.length - (budget - head);
|
|
30
|
+
while (head > 0 && (bytes[head]! & 0xc0) === 0x80) head--;
|
|
31
|
+
while (tail < bytes.length && (bytes[tail]! & 0xc0) === 0x80) tail++;
|
|
32
|
+
const removed = Math.ceil((bytes.length - budget) / 4);
|
|
33
|
+
return `${bytes.subarray(0, head).toString('utf8')}…${removed} tokens truncated…${bytes.subarray(tail).toString('utf8')}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Price visible text, tool arguments, and default-detail images; plaintext reasoning is uncharged. */
|
|
37
|
+
export function estimateTokens(entry: ContextEntry): number {
|
|
38
|
+
let bytes = entry.message.role === 'tool' ? Buffer.byteLength(entry.message.toolCallId + (entry.toolName ?? ''), 'utf8') : 0;
|
|
39
|
+
for (const block of entry.message.content) {
|
|
40
|
+
if (block.type === 'text') bytes += Buffer.byteLength(block.text, 'utf8');
|
|
41
|
+
else if (block.type === 'tool-call') bytes += Buffer.byteLength(block.name + block.arguments, 'utf8');
|
|
42
|
+
else if (block.type === 'image') bytes += block.offloaded ? Buffer.byteLength(JSON.stringify(block.attachment), 'utf8') : 7_373;
|
|
43
|
+
else if (block.type === 'file') bytes += Buffer.byteLength(JSON.stringify(block.attachment), 'utf8');
|
|
44
|
+
}
|
|
45
|
+
return Math.ceil(bytes / 4);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Add local growth to the latest valid usage report produced after every live checkpoint. */
|
|
49
|
+
export function contextTokens(snapshot: ContextSnapshot): number {
|
|
50
|
+
const entries = snapshot.entries;
|
|
51
|
+
const lastCheckpoint = entries.reduce((latest, entry) => checkpoint(entry) ? Math.max(latest, entry.seq) : latest, -1);
|
|
52
|
+
for (let index = entries.length - 1; index >= 0; index--) {
|
|
53
|
+
const entry = entries[index]!;
|
|
54
|
+
if (entry.message.role !== 'assistant' || entry.seq < lastCheckpoint || entry.finish === 'error' || entry.finish === 'aborted' || !entry.usage) continue;
|
|
55
|
+
const usage = entry.usage;
|
|
56
|
+
// DSH reports uncached and cached input separately; the Codex input counter includes both.
|
|
57
|
+
const tokens = usage.totalTokens ?? usage.inputTokens + (usage.cacheReadTokens ?? 0) + (usage.cacheWriteTokens ?? 0) + usage.outputTokens;
|
|
58
|
+
if (!Number.isFinite(tokens) || tokens <= 0) continue;
|
|
59
|
+
return tokens + entries.slice(index + 1).reduce((sum, value) => sum + estimateTokens(value), 0);
|
|
60
|
+
}
|
|
61
|
+
return entries.reduce((sum, entry) => sum + estimateTokens(entry), 0);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Retain newest genuine user messages in order, flattening media and truncating one boundary message. */
|
|
65
|
+
export function retainUserMessages(messages: readonly (readonly ContentBlock[])[], maxTokens: number): ContentBlock[][] {
|
|
66
|
+
const retained: ContentBlock[][] = [];
|
|
67
|
+
let remaining = Math.max(0, Math.floor(maxTokens));
|
|
68
|
+
for (const content of [...messages].reverse()) {
|
|
69
|
+
if (remaining === 0) break;
|
|
70
|
+
const text = userText(content);
|
|
71
|
+
const tokens = approximateTokens(text);
|
|
72
|
+
retained.push(tokens <= remaining && content.every(block => block.type === 'text')
|
|
73
|
+
? content.map(block => ({ ...block }))
|
|
74
|
+
: [{ type: 'text', text: truncateTokens(text, remaining) }]);
|
|
75
|
+
if (tokens > remaining) break;
|
|
76
|
+
remaining -= tokens;
|
|
77
|
+
}
|
|
78
|
+
return retained.reverse();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Select one contiguous, completed span while preserving every system/developer message and tool pair. */
|
|
82
|
+
function select(entries: readonly ContextEntry[]): readonly ContextEntry[] | undefined {
|
|
83
|
+
const pending = new Set<string>();
|
|
84
|
+
let start = -1;
|
|
85
|
+
let end = -1;
|
|
86
|
+
const candidate = (): readonly ContextEntry[] | undefined => {
|
|
87
|
+
if (start < 0 || end <= start) return undefined;
|
|
88
|
+
const span = entries.slice(start, end);
|
|
89
|
+
return span.some(entry => entry.message.role === 'assistant' || entry.message.role === 'tool') ? span : undefined;
|
|
90
|
+
};
|
|
91
|
+
for (let index = 0; index < entries.length; index++) {
|
|
92
|
+
const entry = entries[index]!;
|
|
93
|
+
if (protectedEntry(entry)) {
|
|
94
|
+
const span = candidate();
|
|
95
|
+
if (span) return span;
|
|
96
|
+
start = -1;
|
|
97
|
+
end = -1;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (start < 0 && pending.size === 0 && entry.message.role !== 'tool') start = index;
|
|
101
|
+
for (const block of entry.message.content) if (block.type === 'tool-call') pending.add(block.id);
|
|
102
|
+
if (entry.message.role === 'tool') pending.delete(entry.message.toolCallId);
|
|
103
|
+
if (start >= 0 && pending.size === 0) end = index + 1;
|
|
104
|
+
}
|
|
105
|
+
return candidate();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Drop one oldest request item together with all tool counterparts it would orphan. */
|
|
109
|
+
function dropOldest(entries: readonly ContextEntry[]): ContextEntry[] | undefined {
|
|
110
|
+
const first = entries.find(entry => !protectedEntry(entry));
|
|
111
|
+
if (!first) return undefined;
|
|
112
|
+
const removed = new Set([first.seq]);
|
|
113
|
+
const calls = new Set<string>();
|
|
114
|
+
let changed = true;
|
|
115
|
+
while (changed) {
|
|
116
|
+
changed = false;
|
|
117
|
+
for (const entry of entries) {
|
|
118
|
+
const ids = entry.message.content.flatMap(block => block.type === 'tool-call' ? [block.id] : []);
|
|
119
|
+
if (entry.message.role === 'tool') ids.push(entry.message.toolCallId);
|
|
120
|
+
if (!removed.has(entry.seq) && ids.some(id => calls.has(id))) { removed.add(entry.seq); changed = true; }
|
|
121
|
+
if (removed.has(entry.seq)) for (const id of ids) if (!calls.has(id)) { calls.add(id); changed = true; }
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return entries.filter(entry => !removed.has(entry.seq));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function errorCode(error: unknown): string {
|
|
128
|
+
return error !== null && typeof error === 'object' && 'code' in error ? String(error.code) : '';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function contextOverflow(error: unknown): boolean {
|
|
132
|
+
return /CONTEXT.*(EXCEED|OVERFLOW|LENGTH)|PROMPT_TOO_LONG/.test(errorCode(error).toUpperCase())
|
|
133
|
+
|| (error instanceof Error && /context.{0,35}(exceed|overflow|limit)|prompt.{0,16}too.?long|maximum context length/i.test(error.message));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function terminal(error: unknown): boolean {
|
|
137
|
+
return /ABORT|CANCEL|INTERRUPT|SESSION_BUDGET_EXCEEDED/.test(errorCode(error).toUpperCase())
|
|
138
|
+
|| error instanceof Error && error.name === 'AbortError';
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
interface RetainedContext { key: string; seq: number; content: ContentBlock[] }
|
|
142
|
+
interface RetainedState { users: ContentBlock[][]; context: RetainedContext[] }
|
|
143
|
+
|
|
144
|
+
function injectedContext(entry: ContextEntry): RetainedContext | undefined {
|
|
145
|
+
const source = entry.message.source;
|
|
146
|
+
if (entry.message.role !== 'user' || source.kind === 'user' || source.kind === 'compact-checkpoint') return undefined;
|
|
147
|
+
const form = 'form' in source ? source.form : undefined;
|
|
148
|
+
if (form !== 'instructions' && form !== 'catalog' && form !== 'snapshot' && !/skill|plan|subagent|agent-instructions/.test(source.kind)) return undefined;
|
|
149
|
+
const key = form === 'snapshot' || form === 'catalog' || /plan/.test(source.kind)
|
|
150
|
+
? JSON.stringify([source.kind, form ?? null]) : JSON.stringify(source);
|
|
151
|
+
return { key, seq: entry.seq, content: entry.message.content.filter(block => block.type === 'text') };
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Recover only state whose complete checkpoint content matches a live surface message. */
|
|
155
|
+
function retainedState(host: ContextHost, entries: readonly ContextEntry[]): RetainedState {
|
|
156
|
+
const users: ContentBlock[][] = [];
|
|
157
|
+
const context: RetainedContext[] = [];
|
|
158
|
+
for (const entry of entries) {
|
|
159
|
+
const source = entry.message.source;
|
|
160
|
+
if (source.kind === 'compact-checkpoint') {
|
|
161
|
+
const hash = fingerprint(entry.message.content);
|
|
162
|
+
const saved = host.records('codex/checkpoint').findLast(record => record.fingerprint === hash
|
|
163
|
+
&& typeof record.preparedAfterSeq === 'number' && record.preparedAfterSeq < entry.seq);
|
|
164
|
+
if (!saved) continue;
|
|
165
|
+
if (Array.isArray(saved.users)) for (const message of saved.users) {
|
|
166
|
+
if (Array.isArray(message) && message.every(text => typeof text === 'string')) users.push(message.map(text => ({ type: 'text', text })));
|
|
167
|
+
}
|
|
168
|
+
if (Array.isArray(saved.context)) for (const raw of saved.context) {
|
|
169
|
+
const value: unknown = raw;
|
|
170
|
+
if (value && typeof value === 'object' && 'key' in value && typeof value.key === 'string'
|
|
171
|
+
&& 'seq' in value && typeof value.seq === 'number' && Number.isSafeInteger(value.seq)
|
|
172
|
+
&& 'texts' in value && Array.isArray(value.texts)) {
|
|
173
|
+
const texts = value.texts.filter((text: unknown): text is string => typeof text === 'string');
|
|
174
|
+
if (texts.length === value.texts.length) context.push({ key: value.key, seq: value.seq, content: texts.map(text => ({ type: 'text', text })) });
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
} else if (entry.message.role === 'user' && source.kind === 'user') {
|
|
178
|
+
if (!userText(entry.message.content).startsWith(summaryPrefix)) users.push([...entry.message.content]);
|
|
179
|
+
} else {
|
|
180
|
+
const injected = injectedContext(entry);
|
|
181
|
+
if (injected) context.push(injected);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return { users, context };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function validateSummary(response: SummaryResponse): void {
|
|
188
|
+
if (response.finish === 'max-tokens' || response.finish === 'length') throw new Error('Codex summary was truncated at its output limit');
|
|
189
|
+
if (response.finish === 'error' || response.finish === 'aborted') throw new Error(`Codex summary ${response.finish}`);
|
|
190
|
+
if (response.content.some(block => block.type !== 'text' && block.type !== 'reasoning')) throw new Error('Codex summary must contain text without tool calls or media');
|
|
191
|
+
if (!response.text.trim()) throw new Error('Codex returned an empty context summary');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** Create the local compaction pipeline; remote compaction requires native provider transport. */
|
|
195
|
+
export function createPipeline(config: ContextConfig = {}): ContextPipeline {
|
|
196
|
+
async function prepare(host: ContextHost, entries: readonly ContextEntry[]): Promise<Checkpoint> {
|
|
197
|
+
host.signal.throwIfAborted();
|
|
198
|
+
const snapshot = await host.snapshot();
|
|
199
|
+
const preparedAfterSeq = snapshot.archive.reduce((latest, entry) => Math.max(latest, entry.seq), -1);
|
|
200
|
+
const selected = new Set(entries.map(entry => entry.seq));
|
|
201
|
+
let requestEntries = snapshot.entries.filter(entry => protectedEntry(entry) || selected.has(entry.seq));
|
|
202
|
+
const original = retainedState(host, entries);
|
|
203
|
+
const contexts = new Map<string, RetainedContext>();
|
|
204
|
+
for (const value of original.context) if (!contexts.has(value.key) || contexts.get(value.key)!.seq < value.seq) contexts.set(value.key, value);
|
|
205
|
+
for (const entry of snapshot.archive) {
|
|
206
|
+
const value = injectedContext(entry);
|
|
207
|
+
if (value && contexts.has(value.key) && contexts.get(value.key)!.seq < value.seq) contexts.set(value.key, value);
|
|
208
|
+
}
|
|
209
|
+
for (const entry of snapshot.entries) {
|
|
210
|
+
const value = injectedContext(entry);
|
|
211
|
+
if (value && !selected.has(entry.seq) && value.seq >= (contexts.get(value.key)?.seq ?? Infinity)) contexts.delete(value.key);
|
|
212
|
+
}
|
|
213
|
+
const retained: RetainedState = {
|
|
214
|
+
users: retainUserMessages(original.users, config.keepRecentTokens ?? 20_000),
|
|
215
|
+
context: config.restoreContext === false ? [] : [...contexts.values()].sort((left, right) => left.seq - right.seq),
|
|
216
|
+
};
|
|
217
|
+
let retries = 0;
|
|
218
|
+
let response: SummaryResponse;
|
|
219
|
+
for (;;) {
|
|
220
|
+
host.signal.throwIfAborted();
|
|
221
|
+
try {
|
|
222
|
+
response = await host.summarize({
|
|
223
|
+
messages: requestEntries.map(entry => entry.message),
|
|
224
|
+
instruction: summaryInstruction,
|
|
225
|
+
maxTokens: config.maxSummaryTokens ?? snapshot.maxOutputTokens,
|
|
226
|
+
includeTools: false,
|
|
227
|
+
...config.summarizationProvider === undefined ? {} : { provider: config.summarizationProvider },
|
|
228
|
+
...config.summarizationModel === undefined ? {} : { model: config.summarizationModel },
|
|
229
|
+
});
|
|
230
|
+
break;
|
|
231
|
+
} catch (error) {
|
|
232
|
+
host.signal.throwIfAborted();
|
|
233
|
+
if (terminal(error)) throw error;
|
|
234
|
+
if (contextOverflow(error)) {
|
|
235
|
+
const smaller = dropOldest(requestEntries);
|
|
236
|
+
if (!smaller) throw error;
|
|
237
|
+
requestEntries = smaller;
|
|
238
|
+
retries = 0;
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
if (retries >= (config.maxSummaryAttempts ?? 6) - 1) throw error;
|
|
242
|
+
const wait = Math.min(60_000, Math.floor((config.summaryRetryDelayMs ?? 200) * 2 ** retries++ * (0.9 + Math.random() * 0.2)));
|
|
243
|
+
await delay(wait, undefined, { signal: host.signal });
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
host.signal.throwIfAborted();
|
|
247
|
+
validateSummary(response);
|
|
248
|
+
const beforeSummary: ContentBlock[] = [];
|
|
249
|
+
const context = retained.context.flatMap(value => value.content);
|
|
250
|
+
retained.users.forEach((content, index) => {
|
|
251
|
+
if (index === retained.users.length - 1) beforeSummary.push(...context);
|
|
252
|
+
beforeSummary.push({ type: 'text', text: `[Retained user message ${index + 1}]` }, ...content);
|
|
253
|
+
});
|
|
254
|
+
if (!retained.users.length) beforeSummary.push(...context);
|
|
255
|
+
const summary = `${summaryPrefix}\n${response.text}`;
|
|
256
|
+
// Content correlation also covers explicit range compaction, whose host owns the later commit.
|
|
257
|
+
host.record('codex/checkpoint', {
|
|
258
|
+
fingerprint: fingerprint([...beforeSummary, { type: 'text', text: summary }]),
|
|
259
|
+
preparedAfterSeq,
|
|
260
|
+
users: retained.users.map(content => content.flatMap(block => block.type === 'text' ? [block.text] : [])),
|
|
261
|
+
context: retained.context.map(value => ({ key: value.key, seq: value.seq, texts: value.content.flatMap(block => block.type === 'text' ? [block.text] : []) })),
|
|
262
|
+
});
|
|
263
|
+
return { summary, beforeSummary };
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return {
|
|
267
|
+
summarizeRange: prepare,
|
|
268
|
+
async run(host, trigger) {
|
|
269
|
+
host.signal.throwIfAborted();
|
|
270
|
+
// Upstream recovers overflow within the compaction request, not arbitrary failed user turns.
|
|
271
|
+
if (trigger === 'context-overflow' || trigger === 'request-too-large') return null;
|
|
272
|
+
if (trigger === 'pressure' && config.auto === false) return null;
|
|
273
|
+
const snapshot = await host.snapshot();
|
|
274
|
+
const threshold = Math.max(0, Math.min(
|
|
275
|
+
Math.floor(snapshot.contextWindow * Math.min(0.9, config.thresholdRatio ?? 0.9)),
|
|
276
|
+
snapshot.contextWindow - (config.reserveTokens ?? 0),
|
|
277
|
+
));
|
|
278
|
+
if (trigger === 'pressure' && (snapshot.contextWindow <= 0 || contextTokens(snapshot) < threshold)) return null;
|
|
279
|
+
const selected = select(snapshot.entries);
|
|
280
|
+
if (!selected) return null;
|
|
281
|
+
return host.compact(selected, () => prepare(host, selected));
|
|
282
|
+
},
|
|
283
|
+
};
|
|
284
|
+
}
|