@deepseek-ai/dsh-time-context 0.1.1-rc.2 → 0.1.2-alpha.3

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/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/context/time-context/README.md
5
- README.md: 0bdb0d463362427d6a7050c2d7d6d55f96779f9c
6
- README.zh.md: 8f95b133afed5cd872f80d8f0f5ae69e53062325
5
+ README.md: 52690900b0ac65743345c226128217e2c9ec156b
6
+ README.zh.md: 64299a57a37ec32647e7a2ab0062e02b71516a6e
package/README.md CHANGED
@@ -1,41 +1,101 @@
1
+ ---
2
+ description: "Opt-in per-step clock context with the current time, browser zone, and elapsed time, for users and maintainers enabling or tuning the plugin."
3
+ kind: "package-reference"
4
+ ---
5
+
1
6
  # @deepseek-ai/dsh-time-context
2
7
 
3
8
  English | [中文](README.zh.md)
4
9
 
5
- Opt-in durable context with the current zoned time, the browser zone attached to the open request, and elapsed time sampled during model-request preparation. Default compositions leave it disabled; the Schedule Web overlay mounts it so the model can interpret otherwise-unqualified dates and times in the user's browser zone. Decision record: [the durable time-context Agent Note](../../../.agents/notes/implemented/feature/2026-07-16-durable-per-step-time-context.md).
10
+ ## Summary
11
+
12
+ `dsh-time-context` gives the model a clock: on eligible steps it appends a durable, source-attributed reading with the current time, the browser zone attached to the open request, and the elapsed time since the preceding model-visible message. It helps the model interpret otherwise-unqualified dates and times in the user's browser zone, and tells it to ask when zone provenance is mixed or missing. The plugin is opt-in: default compositions leave it disabled, and the Schedule Web overlay mounts it. A positive `refreshIntervalMs` reduces how often readings accumulate; omission or `0` injects at every eligible step.
13
+
14
+ ## Table of Contents
15
+
16
+ - [Use this package](#use-this-package)
17
+ - [Understand the implementation](#understand-the-implementation)
18
+ - [Further Exploration](#further-exploration)
19
+ - [Model Experience](#model-experience)
20
+ - [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
21
+ - [Dev Note](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## Use this package
27
+
28
+ Mount this plugin when the model should interpret unqualified dates and times in the user's zone, and when a request-local browser zone is available or a configured fallback is acceptable. Each injection is one additional user-role message in the durable history; schedule it with `refreshIntervalMs` when per-step readings are more than the conversation needs.
6
29
 
7
- ## Config
30
+ ### What the agent gets
31
+
32
+ Each injected reading has three lines: an ISO-shaped timestamp with numeric offset and IANA zone, the browser-zone policy for the request, and the elapsed duration in compact whole-second units. Step 1 measures from the latest preceding model-visible message; later steps measure from the preceding time-context event in the same turn. A missing baseline reports `unavailable`, and backward wall-clock movement clamps elapsed time to zero.
33
+
34
+ ### Configuration
35
+
36
+ The minimal mount needs no configuration. A positive `refreshIntervalMs` suppresses injections that fall within that many milliseconds of the latest one; omission or `0` injects at every eligible entering pre-step whose signal is not already aborted.
8
37
 
9
38
  ```yaml
10
- - id: time-context
11
- name: '@deepseek-ai/dsh-time-context'
39
+ - name: '@deepseek-ai/dsh-time-context'
12
40
  config:
13
- timeZone: Asia/Shanghai # optional fallback when the request has no unique browser zone
14
- refreshIntervalMs: 60000 # optional; omit or set to 0 for every eligible attempt
41
+ timeZone: Asia/Shanghai
15
42
  ```
16
43
 
17
- When the open turn contains one Host-validated browser zone, that request-local zone formats the timestamp. With missing or mixed browser provenance, `timeZone` supplies the display fallback; omitting it resolves the Node process zone once at plugin load. Node honors `TZ`, and every explicit fallback is validated through `Intl.DateTimeFormat`.
44
+ | Field | Default | Meaning |
45
+ |---|---|---|
46
+ | `timeZone` | process zone | Fallback display zone when the open turn has no unique browser zone |
47
+ | `refreshIntervalMs` | `0` (every eligible step) | Minimum milliseconds between durable injections in one session |
48
+
49
+ The generated [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-time-context) is the exhaustive source for every accepted field and its JSDoc.
50
+
51
+ ### Choosing the zone
52
+
53
+ When the open turn contains exactly one Host-validated browser zone, the timestamp is formatted in that request-local zone. With missing or mixed browser provenance, the configured `timeZone` formats the display; omitting it resolves the Node process zone once at plugin load, and every explicit fallback is validated through `Intl.DateTimeFormat`. The resolved instruction tells the model to interpret unqualified dates and times in the chosen zone, and to ask the user to clarify when provenance is mixed or unavailable.
18
54
 
19
- `refreshIntervalMs` must be a non-negative safe integer. Omission or `0` adds context to every eligible entering pre-step whose signal is not already aborted. A positive value adds it only when the Session has no earlier time-context injection, wall time moved backward, or at least that many milliseconds elapsed since the latest injection.
55
+ -----
20
56
 
21
- ## Request-zone ownership
57
+ <a id="understand-the-implementation"></a>
58
+ ## Understand the implementation
22
59
 
23
- The browser samples `Intl.DateTimeFormat().resolvedOptions().timeZone` for each prompt. The Host validates and canonicalizes that value before binding it to the exact durable `user-rpc` message source. Time-context examines only those sources in the open turn: one unique zone resolves the request, multiple zones are `mixed`, and none are `unavailable`. It does not read or mutate Session headers, connection state, or Schedule records.
60
+ <details>
61
+ <summary>Implementation internals — click to expand</summary>
24
62
 
25
- The resolved instruction tells the model to interpret otherwise-unqualified dates and times in that browser zone. Mixed or unavailable provenance tells the model to ask the user to clarify. This is natural-language context, not an input default at another package boundary: a tool that accepts local calendar fields still owns its explicit zone requirement.
63
+ This section explains the design of the plugin; the observable behavior is covered in [Use this package](#use-this-package).
26
64
 
27
- ## Timing semantics
65
+ ### Design concept
28
66
 
29
- The plugin prepends an `agent/pre-step` listener and delegates first. When an injection is due and the downstream decision enters, it appends one sourced `UserMessage` to the returned batch. AgentLoop records the final batch after `step/start` and before request derivation. Rejection, listener failure, or an already-aborted signal records nothing.
67
+ The plugin prepends an `agent/pre-step` listener that delegates first and appends one sourced `UserMessage` when an injection is due and the downstream decision enters the step. Each reading uses the exact snapshot source `{ kind: 'plugin', plugin: 'time-context', form: 'snapshot', sections: [{ name: 'time-context', text }] }`, and the invariant companion validates that shape, re-derives the current-turn browser policy from the original `user-rpc` messages, and checks the timestamp zone and elapsed baseline.
30
68
 
31
- Each reading uses the exact snapshot source `{ kind: 'plugin', plugin: 'time-context', form: 'snapshot', sections: [{ name: 'time-context', text: <same text> }] }`. The `./invariant` companion validates that shape, re-derives the current-turn browser policy from the original `user-rpc` messages, and checks the timestamp zone and elapsed baseline.
69
+ ### Source map
32
70
 
33
- Positive-interval scheduling scans raw durable Session events for the latest plugin-attributed message, including a reading shadowed by compaction. It therefore survives resume without a process-local cache. A positive interval can intentionally let a later request reuse existing history without a fresh reading; the Schedule Web overlay omits the interval.
71
+ | File | Role |
72
+ |---|---|
73
+ | [`src/index.ts`](src/index.ts) | Plugin entry: pre-step listener, due scheduling, reading composition |
74
+ | [`src/request-zone.ts`](src/request-zone.ts) | Browser-zone policy derivation from open-turn `user-rpc` sources |
75
+ | [`src/timestamp.ts`](src/timestamp.ts) | `Intl.DateTimeFormat` creation and timestamp formatting |
76
+ | [`src/invariant.ts`](src/invariant.ts) | Invariant companion for the snapshot contract |
34
77
 
35
- Step 1 measures from the latest preceding durable user, assistant, or tool-result message. The prompt proposed for that step has not been appended yet. Later steps measure from the preceding time-context event in the same turn. Missing baselines report `unavailable`, and backward wall-clock movement clamps elapsed time to zero.
78
+ ### Main flow
36
79
 
37
- A reading records an entered step, not a completed or transmitted request. A later preparation failure can leave it in history. The message remains in derived conversation history until compaction shadows it; `request/header` contains no time-context state, and request reconstruction uses the complete durable surface prefix after each `step/start`.
80
+ When an injection is due, the plugin samples the wall clock, derives the browser-zone policy from the open turn's `user-rpc` messages, resolves the display zone (request-local or fallback), and renders the three-line reading. Positive-interval scheduling scans raw durable session events for the latest plugin-attributed message including one shadowed by compaction so the schedule survives resume without a process-local cache. A reading records an entered step, not a completed or transmitted request; a later preparation failure can leave it in history.
38
81
 
82
+ </details>
83
+
84
+ -----
85
+
86
+ <a id="further-exploration"></a>
87
+ ## Further Exploration
88
+
89
+ Read these pages when the package-level contract is not enough. They move from the design decision to the composition that mounts the plugin and the exhaustive configuration.
90
+
91
+ - [Durable per-step time-context decision record](../../../.agents/notes/implemented/feature/2026-07-16-durable-per-step-time-context.md) — design rationale for the durable reading.
92
+ - [Schedule user guide](../../../docs/user/guide/schedule.md) — the official configuration path for mounting this plugin.
93
+ - [Context group map](../README.md) — sibling request-context packages.
94
+ - [Generated configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-time-context) — every accepted config field and its source declaration.
95
+
96
+ -----
97
+
98
+ <a id="model-experience"></a>
39
99
  ## Model Experience
40
100
 
41
101
  ### Preparation-time temporal context
@@ -70,8 +130,23 @@ Append-only; newly visible content follows the reusable request prefix and does
70
130
 
71
131
  ## Known Limitations and Deferred Work
72
132
 
133
+ <a id="known-limitations-and-deferred-work"></a>
134
+
135
+
136
+ These limits define when clock context is a poor fit. They are current package constraints.
137
+
73
138
  - **Prompt provenance only** — browser-zone context guides natural-language interpretation but does not silently supply another tool's required zone field.
74
139
  - **Mixed turns ask** — if one open turn contains prompts from different browser zones, the model is told to clarify rather than guess which one owns an unqualified time.
75
140
  - **Fallback is not user authority** — the configured or process zone formats the clock when browser provenance is missing or mixed, but the model-facing policy still says to clarify.
76
141
  - **Whole-second display** — timestamps and durations omit sub-second precision even though durable event times retain milliseconds.
77
142
  - **History cost between compactions** — omission or `0` retains one reading for every eligible attempt; a positive interval reduces but does not eliminate this cost and may leave a later request without fresh browser-zone guidance.
143
+
144
+ <a id="dev-note"></a>
145
+ ### Dev Note
146
+
147
+ <details>
148
+ <summary>Working context for maintainers — click to expand</summary>
149
+
150
+ None.
151
+
152
+ </details>
package/README.zh.md CHANGED
@@ -1,41 +1,101 @@
1
+ ---
2
+ description: "可选的按步骤时钟上下文,包含当前时间、浏览器时区与经过时长,供启用或调优本插件的用户与维护者阅读。"
3
+ kind: "package-reference"
4
+ ---
5
+
1
6
  # @deepseek-ai/dsh-time-context
2
7
 
3
8
  [English](README.md) | 中文
4
9
 
5
- 可选的持久上下文,包含当前带时区时间、附加到当前开放请求的浏览器时区,以及在模型请求准备期间采样的经过时长。默认组合不启用它;Schedule Web overlay 会挂载它,使模型可以按用户的浏览器时区解释未明确限定时区的日期和时间。决策记录:[持久 time-context Agent Note](../../../.agents/notes/implemented/feature/2026-07-16-durable-per-step-time-context.zh.md)。
10
+ ## 概述
11
+
12
+ `dsh-time-context` 给模型一只时钟:在符合条件的步骤上,它追加一条持久、带来源的读数,包含当前时间、附加到当前开放请求的浏览器时区,以及自前一条模型可见消息以来的经过时长。它帮助模型按用户的浏览器时区解释未明确限定时区的日期与时间;时区来源混杂或缺失时,它告诉模型去询问。本插件需主动启用:默认组合不启用它,Schedule Web overlay 会挂载它。正的 `refreshIntervalMs` 会减少读数累积的频率;省略或设为 `0` 时,每个符合条件的步骤都会注入。
13
+
14
+ ## 目录
15
+
16
+ - [使用本包](#use-this-package)
17
+ - [理解实现](#understand-the-implementation)
18
+ - [进一步探索](#further-exploration)
19
+ - [模型体验](#model-experience)
20
+ - [已知限制与延期工作](#known-limitations-and-deferred-work)
21
+ - [开发备注](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## 使用本包
27
+
28
+ 当模型需要按用户所在时区解释未限定的日期与时间,且请求本地浏览器时区可用或已配置的回退值可接受时,挂载此插件。每次注入都是持久历史中额外的一条 user 角色消息;当按步骤读数超出对话需要时,用 `refreshIntervalMs` 调度。
6
29
 
7
- ## 配置
30
+ ### 模型能得到什么
31
+
32
+ 每条注入读数包含三行:带数字偏移与 IANA 时区、形如 ISO 的时间戳,该请求的浏览器时区策略,以及以紧凑整秒单位表示的经过时长。第 1 步从最新一条先前模型可见消息起测量;后续步骤从同一轮次中前一个 time-context 事件起测量。缺少基线时报告 `unavailable`,挂钟时间倒退时把经过时长钳制为零。
33
+
34
+ ### 配置
35
+
36
+ 最小挂载无需任何配置。正的 `refreshIntervalMs` 会抑制距最近一次注入不足该毫秒数的注入;省略或设为 `0` 时,每个信号尚未中止且将进入步骤的合格 pre-step 都会注入。
8
37
 
9
38
  ```yaml
10
- - id: time-context
11
- name: '@deepseek-ai/dsh-time-context'
39
+ - name: '@deepseek-ai/dsh-time-context'
12
40
  config:
13
- timeZone: Asia/Shanghai # optional fallback when the request has no unique browser zone
14
- refreshIntervalMs: 60000 # optional; omit or set to 0 for every eligible attempt
41
+ timeZone: Asia/Shanghai
15
42
  ```
16
43
 
17
- 当当前开放轮次只包含一个经 Host 校验的浏览器时区时,使用该请求本地时区格式化时间戳。浏览器来源信息缺失或混杂时,`timeZone` 提供显示回退;省略它则会在插件加载时解析一次 Node 进程时区。Node 遵循 `TZ`,每个显式回退值都经 `Intl.DateTimeFormat` 校验。
44
+ | 字段 | 默认值 | 含义 |
45
+ |---|---|---|
46
+ | `timeZone` | 进程时区 | 当前开放轮次没有唯一浏览器时区时的显示回退时区 |
47
+ | `refreshIntervalMs` | `0`(每个合格步骤) | 同一会话中两次持久注入之间的最小毫秒数 |
48
+
49
+ 生成的[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-time-context)是每个受支持字段及其 JSDoc 的穷尽式真源。
50
+
51
+ ### 选择时区
52
+
53
+ 当当前开放轮次只包含一个经 Host 校验的浏览器时区时,时间戳按该请求本地时区格式化。浏览器来源信息缺失或混杂时,配置的 `timeZone` 格式化显示;省略它则在插件加载时解析一次 Node 进程时区,每个显式回退值都经 `Intl.DateTimeFormat` 校验。解析后的指令告诉模型按所选时区解释未限定的日期与时间;来源信息混杂或不可用时,则要求用户澄清。
18
54
 
19
- `refreshIntervalMs` 必须是非负安全整数。省略或设为 `0` 时,会为每个信号尚未中止且将进入步骤的合格 pre-step 添加上下文。正数值只会在会话没有更早的 time-context 注入、挂钟时间倒退,或自最新注入起已经过至少相应毫秒数时添加上下文。
55
+ -----
20
56
 
21
- ## 请求时区归属
57
+ <a id="understand-the-implementation"></a>
58
+ ## 理解实现
22
59
 
23
- 浏览器会为每条提示词采样 `Intl.DateTimeFormat().resolvedOptions().timeZone`。Host 校验并规范化该值,再将其绑定到确切的持久 `user-rpc` 消息来源。Time-context 只检查当前开放轮次中的这些来源:唯一一个时区可解析请求,多个时区记为 `mixed`,没有时区则记为 `unavailable`。它不会读取或修改会话标头、连接状态或 Schedule 记录。
60
+ <details>
61
+ <summary>实现细节——点击展开</summary>
24
62
 
25
- 解析后的指令告诉模型,把未明确限定时区的日期和时间解释为该浏览器时区。来源信息为 mixed 或 unavailable 时,模型会收到要求用户澄清的指令。这是自然语言上下文,并非另一个包边界上的输入默认值:接受本地日历字段的工具仍自行负责其显式时区要求。
63
+ 本节解释插件的设计;可观察行为见[使用本包](#use-this-package)。
26
64
 
27
- ## 时序语义
65
+ ### 设计理念
28
66
 
29
- 该插件会前置一个 `agent/pre-step` 监听器,并先行委托下游。需要注入且下游决策进入步骤时,它会向返回批次追加一条带来源的 `UserMessage`。AgentLoop `step/start` 之后、请求派生之前记录最终批次。决策被拒绝、监听器失败或信号已经中止时,不会记录任何内容。
67
+ 插件前置注册一个 `agent/pre-step` 监听器,先委托下游,需要注入且下游决策进入步骤时追加一条带来源的 `UserMessage`。每个读数都使用确切的快照来源 `{ kind: 'plugin', plugin: 'time-context', form: 'snapshot', sections: [{ name: 'time-context', text }] }`,不变式伴生插件会校验该形状,根据原始 `user-rpc` 消息重新派生当前轮次的浏览器策略,并检查时间戳时区与经过时长基线。
30
68
 
31
- 每个读数都使用确切的快照来源 `{ kind: 'plugin', plugin: 'time-context', form: 'snapshot', sections: [{ name: 'time-context', text: <same text> }] }`。`./invariant` 配套模块会校验该形状,根据原始 `user-rpc` 消息重新派生当前轮次的浏览器策略,并检查时间戳时区与经过时长基线。
69
+ ### 源码地图
32
70
 
33
- 正数间隔调度会扫描原始持久会话事件,查找最新一条归因于插件的消息,其中包括已被压缩(compaction)遮蔽的读数。因此,它无需进程本地缓存也能在恢复后继续生效。正数间隔可以有意让后续请求复用现有历史,而不添加新读数;Schedule Web overlay 会省略该间隔。
71
+ | 文件 | 职责 |
72
+ |---|---|
73
+ | [`src/index.ts`](src/index.ts) | 插件入口:pre-step 监听器、到期调度、读数组合 |
74
+ | [`src/request-zone.ts`](src/request-zone.ts) | 从开放轮次 `user-rpc` 来源派生浏览器时区策略 |
75
+ | [`src/timestamp.ts`](src/timestamp.ts) | `Intl.DateTimeFormat` 创建与时间戳格式化 |
76
+ | [`src/invariant.ts`](src/invariant.ts) | 快照约定的不变式伴生插件 |
34
77
 
35
- 1 步从最新一条在其之前持久化的用户、助手或工具结果消息起测量。为该步骤拟议的提示词尚未追加。后续步骤从同一轮次中前一个 time-context 事件起测量。缺少基线时报告 `unavailable`,挂钟时间倒退时将经过时长限制为零。
78
+ ### 主要流程
36
79
 
37
- 读数记录的是已进入的步骤,不是已完成或已传输的请求。后续准备失败时,该读数可能留在历史中。消息会保留在派生会话历史中,直到压缩将其遮蔽;`request/header` 不含 time-context 状态,请求重建会使用每个 `step/start` 之后的完整持久表层前缀。
80
+ 需要注入时,插件采样挂钟时间,从开放轮次的 `user-rpc` 消息派生浏览器时区策略,解析显示时区(请求本地或回退),并渲染三行读数。正数间隔调度会扫描原始持久会话事件,查找最新一条归因于插件的消息——包括被压缩(compaction)遮蔽的读数——因此调度无需进程本地缓存也能在恢复后存续。读数记录的是已进入的步骤,不是已完成或已传输的请求;后续准备失败时,该读数可能留在历史中。
38
81
 
82
+ </details>
83
+
84
+ -----
85
+
86
+ <a id="further-exploration"></a>
87
+ ## 进一步探索
88
+
89
+ 包级约定不够用时阅读以下页面。它们从设计决策进入挂载本插件的组合与穷尽式配置。
90
+
91
+ - [持久按步骤 time-context 决策记录](../../../.agents/notes/implemented/feature/2026-07-16-durable-per-step-time-context.zh.md)——持久读数的设计理由。
92
+ - [Schedule 用户指南](../../../docs/user/guide/schedule.zh.md)——挂载本插件的官方配置路径。
93
+ - [context 组地图](../README.zh.md)——相邻的请求上下文包。
94
+ - [生成的配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-time-context)——每个受支持配置字段及其源声明。
95
+
96
+ -----
97
+
98
+ <a id="model-experience"></a>
39
99
  ## 模型体验
40
100
 
41
101
  ### 准备期时间上下文
@@ -68,10 +128,25 @@ Elapsed since the preceding step context: <duration-or-unavailable>.
68
128
 
69
129
  仅追加;新可见内容位于可复用请求前缀之后,不会使现有 KV Cache 条目失效。
70
130
 
71
- ## 已知限制与暂缓事项
131
+ ## 已知限制与延期工作
132
+
133
+ <a id="known-limitations-and-deferred-work"></a>
134
+
135
+
136
+ 这些限制说明时钟上下文何时不合适。它们是当前包约束。
72
137
 
73
138
  - **仅限提示词来源信息**:浏览器时区上下文用于指导自然语言解释,但不会悄然填入另一工具所要求的时区字段。
74
139
  - **混合轮次会询问**:如果同一个开放轮次包含来自不同浏览器时区的提示词,模型会收到要求澄清的指令,而不会猜测哪个时区拥有未限定的时间。
75
140
  - **回退值不代表用户权威**:浏览器来源信息缺失或混杂时,配置或进程时区用于格式化时钟,但面向模型的策略仍要求澄清。
76
141
  - **整秒显示**:时间戳与持续时间省略亚秒精度,尽管持久事件时间保留毫秒。
77
142
  - **压缩之间的历史成本**:省略或设为 `0` 时,每次合格尝试都会保留一条读数;正数间隔可以降低但无法消除该成本,也可能使后续请求缺少新鲜的浏览器时区指导。
143
+
144
+ <a id="dev-note"></a>
145
+ ### 开发备注
146
+
147
+ <details>
148
+ <summary>维护者的工作上下文——点击展开</summary>
149
+
150
+ 无。
151
+
152
+ </details>
package/lib/index.js CHANGED
@@ -1,183 +1,7 @@
1
- import { createRequire } from "node:module";
2
1
  import z from "@deepseek-ai/schemastery";
3
- import "@deepseek-ai/cordis";
4
- //#region ../../llm/llm/src/brand.ts
5
- /**
6
- * Brand a message identifier.
7
- * @param id - the opaque message identifier.
8
- * @returns the same string, branded; no validation is performed.
9
- */
10
- function MessageId(id) {
11
- return id;
12
- }
13
- //#endregion
14
- //#region ../../llm/llm/src/call-config.ts
15
- /**
16
- * Deep-freeze a value in place with an iterative traversal, guarding cycles,
17
- * so later mutation throws without imposing a JavaScript call-stack depth cap.
18
- * {@link AbortSignal} objects are deliberately skipped because they are the
19
- * request's live cancellation channel and freezing them breaks abort.
20
- * @param value - the value to freeze in place.
21
- * @returns the same value, frozen.
22
- */
23
- function deepFreeze(value) {
24
- const seen = /* @__PURE__ */ new WeakSet();
25
- const pending = [{
26
- kind: "visit",
27
- node: value
28
- }];
29
- while (pending.length > 0) {
30
- const task = pending.pop();
31
- /* v8 ignore next -- the loop condition guarantees one pending task. */
32
- if (task === void 0) continue;
33
- if (task.kind === "property") {
34
- pending.push({
35
- kind: "visit",
36
- node: task.source[task.key]
37
- });
38
- continue;
39
- }
40
- const node = task.node;
41
- if (node === null || typeof node !== "object") continue;
42
- if (node instanceof AbortSignal) continue;
43
- if (seen.has(node)) continue;
44
- seen.add(node);
45
- Object.freeze(node);
46
- const keys = Object.keys(node);
47
- for (let index = keys.length - 1; index >= 0; index--) {
48
- const key = keys[index];
49
- /* v8 ignore next -- the loop is bounded by the captured key count. */
50
- if (key === void 0) continue;
51
- pending.push({
52
- kind: "property",
53
- source: node,
54
- key
55
- });
56
- }
57
- }
58
- return value;
59
- }
60
- //#endregion
61
- //#region ../../llm/llm/src/message.ts
62
- /** Message value types, identity, and immutable construction helpers. */
63
- /**
64
- * Detach and deep-freeze a message whose identity already exists.
65
- * @param message - complete message, including its stable identity.
66
- * @returns an immutable snapshot that preserves the identity.
67
- */
68
- function freezeMessage(message) {
69
- return deepFreeze(structuredClone(message));
70
- }
71
- /**
72
- * Create one identified message and freeze it before publication.
73
- * @param input - complete role, content, and source for a new message.
74
- * @returns an immutable message with a fresh stable identity.
75
- */
76
- function createMessage(input) {
77
- return freezeMessage({
78
- ...input,
79
- id: MessageId(crypto.randomUUID())
80
- });
81
- }
82
- /**
83
- * Create one identified user-role message and freeze it before publication.
84
- * @param input - complete content and source for a new user message.
85
- * @returns an immutable user message with a fresh stable identity.
86
- */
87
- function createUserMessage(input) {
88
- return createMessage({
89
- ...input,
90
- role: "user"
91
- });
92
- }
93
- //#endregion
94
- //#region ../../util/timeout/src/index.ts
95
- /** Largest delay Node schedules without clamping it to one millisecond. */
96
- const MAX_TIMER_DELAY_MS = 2147483647;
97
- //#endregion
98
- //#region ../../llm/llm/src/error.ts
99
- /**
100
- * Canonical provider-neutral code for a response that completed normally but
101
- * carried no content blocks at all. Providers occasionally emit a degenerate
102
- * completion (a terminal stop with zero output); adapters classify it as this
103
- * failure instead of yielding an empty assistant message, because an empty
104
- * message silently ends the turn with nothing for the user or the loop to act
105
- * on. The attempt produced nothing durable, so retry policy treats it as safe
106
- * to repeat.
107
- */
108
- const EMPTY_RESPONSE_CODE = "EMPTY_RESPONSE";
109
- new RegExp(String.raw`(?:^|[^a-z0-9])context[\s_-](?:length|window)[\s_-]` + String.raw`(?:exceed(?:ed|s)?|overflow(?:ed)?|limit[\s_-]exceeded)(?:$|[^a-z0-9])`, "i");
110
- new RegExp(String.raw`\b(?:request|prompt|input|messages?)\s+(?:is\s+|are\s+)?` + String.raw`too\s+(?:large|long)\s+for\s+(?:(?:this|the)\s+)?` + String.raw`(?:model(?:'s)?\s+)?context(?:\s+window)?\b`, "i");
111
- new RegExp(String.raw`\b(?:input|prompt|request|messages?)\b.{0,40}` + String.raw`\b(?:exceed(?:s|ed)?|overflows?|is\s+larger\s+than)\b.{0,40}` + String.raw`\b(?:the\s+)?(?:model(?:'s)?\s+)?context(?:\s+(?:length|window))?\b`, "i");
112
- //#endregion
113
- //#region ../../llm/llm/src/retry-policy.ts
114
- /**
115
- * Provider-owned request-retry policy configuration and resolution.
116
- *
117
- * Adapters expose one resolved policy per registered provider route; the
118
- * optional dsh-llm-retry plugin executes it on the agent's failed-step extension point.
119
- *
120
- * @module @deepseek-ai/dsh-llm/retry-policy
121
- */
122
- const DEFAULT_MAX_RETRIES = 5;
123
- const DEFAULT_INITIAL_DELAY_MS = 500;
124
- const DEFAULT_MAX_DELAY_MS = 1e4;
125
- const DEFAULT_JITTER_RATIO = .1;
126
- const DEFAULT_RETRYABLE_CODES = Object.freeze([
127
- EMPTY_RESPONSE_CODE,
128
- "RATE_LIMIT",
129
- "SERVER",
130
- "TIMEOUT",
131
- "TRANSPORT"
132
- ]);
133
- const backoffSchema = z.object({
134
- initialDelayMs: z.number().max(MAX_TIMER_DELAY_MS).default(DEFAULT_INITIAL_DELAY_MS),
135
- maxDelayMs: z.number().max(MAX_TIMER_DELAY_MS).default(DEFAULT_MAX_DELAY_MS),
136
- jitterRatio: z.number().min(0).max(1).default(DEFAULT_JITTER_RATIO)
137
- });
138
- const normalPolicySchema = z.object({
139
- mode: z.const("normal").required(),
140
- maxRetries: z.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER).default(DEFAULT_MAX_RETRIES),
141
- retryableCodes: z.array(z.string()).default([...DEFAULT_RETRYABLE_CODES]),
142
- backoff: backoffSchema
143
- });
144
- const alwaysPolicySchema = z.object({
145
- mode: z.const("always").required(),
146
- backoff: backoffSchema
147
- });
148
- z.union([normalPolicySchema, alwaysPolicySchema]);
149
- //#endregion
150
- //#region ../../llm/llm/src/attribution.ts
151
- /**
152
- * Centralize the non-secret product identity every provider request sends as `User-Agent`, keeping
153
- * adapters from drifting. See
154
- * `.agents/notes/implemented/architecture/2026-06-21-mandatory-app-attribution-headers.md`.
155
- *
156
- * App-attribution vocabulary for provider requests.
157
- * @module @deepseek-ai/dsh-llm/attribution
158
- */
159
- const { version } = createRequire(import.meta.url)("../package.json");
160
- //#endregion
161
- //#region ../../llm/llm/src/never.ts
162
- /**
163
- * Exhaustiveness helper for closed core unions. Use {@link assertNever} at the default branch so a
164
- * new variant fails compilation at every required handler. Do not use it for declaration-merged
165
- * unions such as session events or content blocks: handle known variants and explicitly fall
166
- * through because plugins may add valid unknown cases.
167
- * @module @deepseek-ai/dsh-llm/never
168
- */
169
- /**
170
- * Mark an unreachable closed-union branch. A newly unhandled typed variant fails at the call site;
171
- * a value that escaped its type throws with diagnostics at runtime.
172
- * @param value - the impossible value; typed `never` so an unhandled variant fails compilation at the call site.
173
- * @param context - optional label (e.g. the switch site) prefixed into the throw message.
174
- * @returns never — it always throws, with the offending value JSON-rendered in the message.
175
- */
176
- function assertNever(value, context) {
177
- const rendered = JSON.stringify(value) ?? String(value);
178
- throw new Error(`unreachable variant${context ? ` in ${context}` : ""}: ${rendered}`);
179
- }
180
- //#endregion
2
+ import { z as z$1 } from "zod";
3
+ import { createUserMessage } from "@deepseek-ai/dsh-llm";
4
+ import { assertNever } from "@deepseek-ai/dsh-util-values";
181
5
  //#region lib/types/request-zone.js
182
6
  /** Browser-zone derivation and model-facing policy text for one open request turn. */
183
7
  const IANA_TIME_ZONE = /^[A-Za-z][A-Za-z0-9_+.-]*(?:\/[A-Za-z0-9_+.-]+)+$/;
@@ -275,8 +99,16 @@ function formatTimestamp(now, formatter, timeZone) {
275
99
  */
276
100
  /** Cordis plugin name used by loader diagnostics. */
277
101
  const name = "time-context";
102
+ const timeContextStateSchema = z$1.object({
103
+ /** Time of the latest model-visible event (user/assistant message, tool result), or null. */
104
+ lastMessageTime: z$1.number().nullable(),
105
+ /** Time of this plugin's latest durable injection, or null. */
106
+ lastInjectionTime: z$1.number().nullable(),
107
+ /** Latest injection time in the open turn, or null before that turn receives one. */
108
+ lastTurnInjectionTime: z$1.number().nullable()
109
+ });
278
110
  /** The agent registry that owns pre-step processing. */
279
- const inject = ["agents"];
111
+ const inject = ["agents", "sessionProjections"];
280
112
  /** Schemastery validation for {@link Config}. */
281
113
  const Config = z.object({
282
114
  timeZone: z.string(),
@@ -298,26 +130,6 @@ function formatDuration(elapsedMs) {
298
130
  parts.push(`${seconds}s`);
299
131
  return parts.join(" ");
300
132
  }
301
- /** Find the latest model-visible event, excluding this plugin's pending append. */
302
- function precedingMessageTime(agent) {
303
- for (const event of [...agent.session.events].reverse()) switch (event.type) {
304
- case "user/message":
305
- case "assistant/message":
306
- case "tool/result": return event.time;
307
- default: break;
308
- }
309
- }
310
- /** Find the preceding time-context event within the open turn. */
311
- function precedingStepContextTime(agent, turn) {
312
- for (const event of [...agent.session.events].reverse()) {
313
- if (event.type === "turn/start" && event.data.turn === turn) return void 0;
314
- if (event.type === "user/message" && event.data.source.kind === "plugin" && event.data.source.plugin === "time-context") return event.time;
315
- }
316
- }
317
- /** Find this plugin's latest durable injection, including a shadowed surface event. */
318
- function latestInjectionTime(agent) {
319
- for (const event of [...agent.session.events].reverse()) if (event.type === "user/message" && event.data.source.kind === "plugin" && event.data.source.plugin === "time-context") return event.time;
320
- }
321
133
  /** Collect already-entered and proposed user messages belonging to one open turn. */
322
134
  function requestMessages(agent, turn, proposed) {
323
135
  const start = agent.session.events.findLastIndex((event) => event.type === "turn/start" && event.data.turn === turn);
@@ -360,20 +172,56 @@ function apply(ctx, config) {
360
172
  formatters.set(selectedTimeZone, created);
361
173
  return created;
362
174
  };
175
+ ctx.sessionProjections.register({
176
+ key: "timeContext",
177
+ stateVersion: 2,
178
+ stateSchema: timeContextStateSchema,
179
+ init: () => ({
180
+ lastMessageTime: null,
181
+ lastInjectionTime: null,
182
+ lastTurnInjectionTime: null
183
+ }),
184
+ apply: (state, event) => {
185
+ if (event.type === "turn/start" || event.type === "turn/end") return state.lastTurnInjectionTime === null ? state : {
186
+ ...state,
187
+ lastTurnInjectionTime: null
188
+ };
189
+ if (event.type === "user/message") {
190
+ const injected = event.data.source.kind === "plugin" && event.data.source.plugin === "time-context";
191
+ const withMessage = state.lastMessageTime === event.time ? state : {
192
+ ...state,
193
+ lastMessageTime: event.time
194
+ };
195
+ if (!injected) return withMessage;
196
+ return {
197
+ ...withMessage,
198
+ lastInjectionTime: event.time,
199
+ lastTurnInjectionTime: event.time
200
+ };
201
+ }
202
+ if (event.type === "assistant/message" || event.type === "tool/result") return state.lastMessageTime === event.time ? state : {
203
+ ...state,
204
+ lastMessageTime: event.time
205
+ };
206
+ return state;
207
+ }
208
+ });
363
209
  ctx.on("agent/pre-step", async ({ agent, turn, step, signal }, next) => {
364
210
  const decision = await next();
365
211
  if (decision.kind === "reject" || signal.aborted) return decision;
366
212
  const now = Date.now();
213
+ const state = ctx.sessionProjections.stateOf(agent.session, "timeContext");
367
214
  if (refreshIntervalMs !== void 0 && refreshIntervalMs > 0) {
368
- const lastInjection = latestInjectionTime(agent);
369
- if (lastInjection !== void 0 && now >= lastInjection && now - lastInjection < refreshIntervalMs) return decision;
215
+ const lastInjection = state.lastInjectionTime;
216
+ if (lastInjection != null && now >= lastInjection && now - lastInjection < refreshIntervalMs) return decision;
370
217
  }
371
- const previous = step === 1 ? precedingMessageTime(agent) : precedingStepContextTime(agent, turn);
218
+ /* v8 ignore next 6 -- every later step follows a recorded injection in the same turn */
219
+ const previous = step === 1 ? state.lastMessageTime ?? void 0 : state.lastTurnInjectionTime ?? void 0;
372
220
  const browser = deriveBrowserTimeZoneContext(requestMessages(agent, turn, decision.messages));
373
221
  const selectedTimeZone = browser.kind === "resolved" ? browser.timeZone : fallbackTimeZone;
374
222
  const text = renderText(now, turn, step, previous, formatterFor(selectedTimeZone), selectedTimeZone, browser);
375
223
  return {
376
- kind: "enter",
224
+ ...decision,
377
225
  messages: [...decision.messages, createUserMessage({
378
226
  content: [{
379
227
  type: "text",
package/lib/invariant.js CHANGED
@@ -1,93 +1,4 @@
1
- import { createRequire } from "node:module";
2
- import "@deepseek-ai/cordis";
3
- import z from "@deepseek-ai/schemastery";
4
- //#region ../../util/timeout/src/index.ts
5
- /** Largest delay Node schedules without clamping it to one millisecond. */
6
- const MAX_TIMER_DELAY_MS = 2147483647;
7
- //#endregion
8
- //#region ../../llm/llm/src/error.ts
9
- /**
10
- * Canonical provider-neutral code for a response that completed normally but
11
- * carried no content blocks at all. Providers occasionally emit a degenerate
12
- * completion (a terminal stop with zero output); adapters classify it as this
13
- * failure instead of yielding an empty assistant message, because an empty
14
- * message silently ends the turn with nothing for the user or the loop to act
15
- * on. The attempt produced nothing durable, so retry policy treats it as safe
16
- * to repeat.
17
- */
18
- const EMPTY_RESPONSE_CODE = "EMPTY_RESPONSE";
19
- new RegExp(String.raw`(?:^|[^a-z0-9])context[\s_-](?:length|window)[\s_-]` + String.raw`(?:exceed(?:ed|s)?|overflow(?:ed)?|limit[\s_-]exceeded)(?:$|[^a-z0-9])`, "i");
20
- new RegExp(String.raw`\b(?:request|prompt|input|messages?)\s+(?:is\s+|are\s+)?` + String.raw`too\s+(?:large|long)\s+for\s+(?:(?:this|the)\s+)?` + String.raw`(?:model(?:'s)?\s+)?context(?:\s+window)?\b`, "i");
21
- new RegExp(String.raw`\b(?:input|prompt|request|messages?)\b.{0,40}` + String.raw`\b(?:exceed(?:s|ed)?|overflows?|is\s+larger\s+than)\b.{0,40}` + String.raw`\b(?:the\s+)?(?:model(?:'s)?\s+)?context(?:\s+(?:length|window))?\b`, "i");
22
- //#endregion
23
- //#region ../../llm/llm/src/retry-policy.ts
24
- /**
25
- * Provider-owned request-retry policy configuration and resolution.
26
- *
27
- * Adapters expose one resolved policy per registered provider route; the
28
- * optional dsh-llm-retry plugin executes it on the agent's failed-step extension point.
29
- *
30
- * @module @deepseek-ai/dsh-llm/retry-policy
31
- */
32
- const DEFAULT_MAX_RETRIES = 5;
33
- const DEFAULT_INITIAL_DELAY_MS = 500;
34
- const DEFAULT_MAX_DELAY_MS = 1e4;
35
- const DEFAULT_JITTER_RATIO = .1;
36
- const DEFAULT_RETRYABLE_CODES = Object.freeze([
37
- EMPTY_RESPONSE_CODE,
38
- "RATE_LIMIT",
39
- "SERVER",
40
- "TIMEOUT",
41
- "TRANSPORT"
42
- ]);
43
- const backoffSchema = z.object({
44
- initialDelayMs: z.number().max(MAX_TIMER_DELAY_MS).default(DEFAULT_INITIAL_DELAY_MS),
45
- maxDelayMs: z.number().max(MAX_TIMER_DELAY_MS).default(DEFAULT_MAX_DELAY_MS),
46
- jitterRatio: z.number().min(0).max(1).default(DEFAULT_JITTER_RATIO)
47
- });
48
- const normalPolicySchema = z.object({
49
- mode: z.const("normal").required(),
50
- maxRetries: z.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER).default(DEFAULT_MAX_RETRIES),
51
- retryableCodes: z.array(z.string()).default([...DEFAULT_RETRYABLE_CODES]),
52
- backoff: backoffSchema
53
- });
54
- const alwaysPolicySchema = z.object({
55
- mode: z.const("always").required(),
56
- backoff: backoffSchema
57
- });
58
- z.union([normalPolicySchema, alwaysPolicySchema]);
59
- //#endregion
60
- //#region ../../llm/llm/src/attribution.ts
61
- /**
62
- * Centralize the non-secret product identity every provider request sends as `User-Agent`, keeping
63
- * adapters from drifting. See
64
- * `.agents/notes/implemented/architecture/2026-06-21-mandatory-app-attribution-headers.md`.
65
- *
66
- * App-attribution vocabulary for provider requests.
67
- * @module @deepseek-ai/dsh-llm/attribution
68
- */
69
- const { version } = createRequire(import.meta.url)("../package.json");
70
- //#endregion
71
- //#region ../../llm/llm/src/never.ts
72
- /**
73
- * Exhaustiveness helper for closed core unions. Use {@link assertNever} at the default branch so a
74
- * new variant fails compilation at every required handler. Do not use it for declaration-merged
75
- * unions such as session events or content blocks: handle known variants and explicitly fall
76
- * through because plugins may add valid unknown cases.
77
- * @module @deepseek-ai/dsh-llm/never
78
- */
79
- /**
80
- * Mark an unreachable closed-union branch. A newly unhandled typed variant fails at the call site;
81
- * a value that escaped its type throws with diagnostics at runtime.
82
- * @param value - the impossible value; typed `never` so an unhandled variant fails compilation at the call site.
83
- * @param context - optional label (e.g. the switch site) prefixed into the throw message.
84
- * @returns never — it always throws, with the offending value JSON-rendered in the message.
85
- */
86
- function assertNever(value, context) {
87
- const rendered = JSON.stringify(value) ?? String(value);
88
- throw new Error(`unreachable variant${context ? ` in ${context}` : ""}: ${rendered}`);
89
- }
90
- //#endregion
1
+ import { assertNever } from "@deepseek-ai/dsh-util-values";
91
2
  //#region lib/types/request-zone.js
92
3
  /** Browser-zone derivation and model-facing policy text for one open request turn. */
93
4
  const IANA_TIME_ZONE = /^[A-Za-z][A-Za-z0-9_+.-]*(?:\/[A-Za-z0-9_+.-]+)+$/;
@@ -6,8 +6,22 @@
6
6
  */
7
7
  import type { Context } from '@deepseek-ai/cordis';
8
8
  import z from '@deepseek-ai/schemastery';
9
+ import { z as zod } from 'zod';
9
10
  /** Cordis plugin name used by loader diagnostics. */
10
11
  export declare const name = "time-context";
12
+ declare module '@deepseek-ai/dsh-session-projection/types' {
13
+ interface SessionProjectionStateMap {
14
+ /** Latest time-context readings. */
15
+ timeContext: TimeContextProjection;
16
+ }
17
+ }
18
+ declare const timeContextStateSchema: zod.ZodObject<{
19
+ lastMessageTime: zod.ZodNullable<zod.ZodNumber>;
20
+ lastInjectionTime: zod.ZodNullable<zod.ZodNumber>;
21
+ lastTurnInjectionTime: zod.ZodNullable<zod.ZodNumber>;
22
+ }, zod.core.$strip>;
23
+ /** Folded time-context readings. */
24
+ type TimeContextProjection = zod.infer<typeof timeContextStateSchema>;
11
25
  /** The agent registry that owns pre-step processing. */
12
26
  export declare const inject: string[];
13
27
  /** Request-preparation clock formatting and append scheduling. Invalid values fail plugin load. */
@@ -26,4 +40,5 @@ export declare const Config: z<Config>;
26
40
  * @throws when the refresh interval is invalid or the configured or process time zone cannot be resolved.
27
41
  */
28
42
  export declare function apply(ctx: Context, config: Config): void;
43
+ export {};
29
44
  //# sourceMappingURL=index.d.ts.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-time-context",
3
3
  "description": "Opt-in durable per-step context with the current time and elapsed time",
4
- "version": "0.1.1-rc.2",
4
+ "version": "0.1.2-alpha.3",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -32,24 +32,34 @@
32
32
  ],
33
33
  "license": "MIT",
34
34
  "dependencies": {
35
- "@deepseek-ai/schemastery": "^3.18.1"
35
+ "zod": "^4.4.3",
36
+ "@deepseek-ai/dsh-util-values": "^0.1.2-alpha.3",
37
+ "@deepseek-ai/schemastery": "^3.18.2"
36
38
  },
37
39
  "peerDependencies": {
38
- "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
39
- "@deepseek-ai/dsh-session": "^0.1.1-rc.2",
40
- "@deepseek-ai/cordis": "^4.0.1",
41
- "@deepseek-ai/dsh-agent": "^0.1.1-rc.2"
40
+ "@deepseek-ai/cordis": "^4.0.2",
41
+ "@deepseek-ai/dsh-agent": "^0.1.2-alpha.3",
42
+ "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.3",
43
+ "@deepseek-ai/dsh-llm": "^0.1.2-alpha.3",
44
+ "@deepseek-ai/dsh-session": "^0.1.2-alpha.3",
45
+ "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.3"
42
46
  },
43
47
  "devDependencies": {
44
- "@deepseek-ai/dsh-agent-loop-testkit": "^0.1.1-rc.2",
45
- "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
46
- "@deepseek-ai/dsh-loader-smoke": "^0.1.1-rc.2",
47
- "@deepseek-ai/dsh-agent-loop": "^0.1.1-rc.2",
48
- "@deepseek-ai/dsh-session": "^0.1.1-rc.2",
49
- "@deepseek-ai/dsh-tools": "^0.1.1-rc.2",
50
- "@deepseek-ai/dsh-system-prompt": "^0.1.1-rc.2",
51
- "@deepseek-ai/cordis": "^4.0.1",
52
- "@deepseek-ai/dsh-llm": "^0.1.1-rc.2",
53
- "@deepseek-ai/dsh-agent": "^0.1.1-rc.2"
48
+ "@deepseek-ai/cordis": "^4.0.2",
49
+ "@deepseek-ai/dsh-agent": "^0.1.2-alpha.3",
50
+ "@deepseek-ai/dsh-agent-loop": "^0.1.2-alpha.3",
51
+ "@deepseek-ai/dsh-agent-loop-testkit": "^0.1.2-alpha.3",
52
+ "@deepseek-ai/dsh-app-boot": "^0.1.2-alpha.3",
53
+ "@deepseek-ai/dsh-bash-local": "^0.1.2-alpha.3",
54
+ "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.3",
55
+ "@deepseek-ai/dsh-llm": "^0.1.2-alpha.3",
56
+ "@deepseek-ai/dsh-loader-smoke": "^0.1.2-alpha.3",
57
+ "@deepseek-ai/dsh-session": "^0.1.2-alpha.3",
58
+ "@deepseek-ai/dsh-session-checkpoint-policy": "^0.1.2-alpha.3",
59
+ "@deepseek-ai/dsh-session-persistence-jsonl": "^0.1.2-alpha.3",
60
+ "@deepseek-ai/dsh-subprocess-local": "^0.1.2-alpha.3",
61
+ "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.3",
62
+ "@deepseek-ai/dsh-tools": "^0.1.2-alpha.3",
63
+ "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.3"
54
64
  }
55
65
  }