DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

CNyaotian-Lunar /

CNyaotian-Lunar/dsh-time-inject

Verified

DSH 插件:每个 turn 首个 step 往模型上下文追加一条单行中文时间读数(当前时间 / 星期 / 时区 / 距上条消息)

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@7fea74b0

dsh-time-inject

架构

⚠️ 非官方插件 —— 社区作品,与 DeepSeek 官方无关。官方同类包是 @deepseek-ai/dsh-time-context, 本插件是它的中文单行变体(差异见下)。

⚠️ Unofficial plugin — a community project, unaffiliated with DeepSeek. The official package doing this job is @deepseek-ai/dsh-time-context; this plugin is its single-line Chinese variant (differences below).

给 DSH(DeepSeek Harness) 的 host 插件: 每个 turn 的第一个 step 往模型上下文里追加一条单行中文时间读数, 让 agent 永远知道「现在几点、距上一条消息过了多久」。

English: A host plugin for DSH (DeepSeek Harness): on the first step of every turn it appends a single-line Chinese clock reading to the model context, so the agent always knows what time it is and how long ago the previous message arrived.

文案样例(就是模型实际看到的那一行,一字不多): Sample output (exactly the single line the model actually sees, nothing more):

[时钟] 2026-09-25 21:34:07 +08:00 周五 · 距上条消息 8 分 12 秒
[时钟] 2026-09-25 21:34:07 +08:00 周五 · 距上条消息 刚刚
[时钟] 2026-09-26 00:00:03 +08:00 周六 · 距上次读数 1 小时 3 分
[时钟] 2026-09-25 21:34:07 +08:00 周五 · 距上条消息 未知
  • 刚刚:距上条消息不足 1 秒(时钟回退也钳到这里,elapsed 永不为负) English: 刚刚 ("just now"): less than 1 second since the previous message (clock rollback is clamped here too, so elapsed is never negative).
  • 距上次读数:本会话内还没有「上一条模型可见消息」时,退化到本插件上一次注入 English: 距上次读数 ("since the last reading"): when the session has no "previous model-visible message" yet, it degrades to this plugin's own last injection.
  • 未知:连上次读数都没有(会话刚开、或投影服务不可用) English: 未知 ("unknown"): there is not even a previous reading (brand-new session, or the projection service is unavailable).
  • 时长最多保留两个单位:8 分 12 秒 / 1 小时 3 分 / 1 天 2 小时 / 59 秒 English: A duration keeps at most two units: 8 分 12 秒 / 1 小时 3 分 / 1 天 2 小时 / 59 秒.

它怎么工作 / How It Works

agent/pre-step (waterfall, prepend: true)
      │
      ├─ await next()                       ← 先让下游/宿主把所有决策做完
      ├─ decision.kind !== 'enter'      → 原样放行
      ├─ signal.aborted                 → 原样放行
      ├─ messages.length === 0          → 🩸 原样放行(空轮保护,见下)
      ├─ step !== 1                     → 原样放行(一个 turn 只注入一次)
      ├─ 读自己的 sessionProjections('timeInject') 折叠状态
      └─ 返回 { ...decision, messages: [...messages, 那条读数] }

sessionProjections 折叠(key = timeInject, stateVersion = 1)
      lastMessageTime    = 最近一条**模型可见且非本插件注入**的事件时间
      lastInjectionTime  = 本插件最近一次注入时间
      injections         = 本插件注入条数

English: What the diagram says, line by line — it hangs off agent/pre-step as a prepend: true waterfall hook; it first await next() so that downstream/host code has finished all its decisions; then it passes the decision through untouched when decision.kind !== 'enter', when signal.aborted, when messages.length === 0 (🩸 empty-turn guard, see below), or when step !== 1 (only one injection per turn); otherwise it reads its own sessionProjections('timeInject') folded state and returns { ...decision, messages: [...messages, that reading] }. The folded state (key = timeInject, stateVersion = 1) holds: lastMessageTime = the time of the most recent event that is model-visible and not injected by this plugin; lastInjectionTime = this plugin's latest injection time; injections = the number of injections this plugin has made.

🩸 空轮保护(本插件最重要的一条)/ 🩸 Empty-Turn Guard (the single most important rule in this plugin)

dsh-agent-loop 的 lib/index.js:963 用 decision.messages.length === 0 判「本轮没有内容 ⇒ 正常收尾」。 往 messages 里塞任何一条消息,都会把本该结束的轮次变成一个真步骤 ⇒ 白烧一次模型请求。 所以 decision.messages.length === 0 时本插件原样返回 decision(lib/index.mjs:241)。

English: dsh-agent-loop's lib/index.js:963 uses decision.messages.length === 0 to decide "this turn has no content ⇒ wrap up normally". Pushing any message into messages turns a turn that should have ended into a real step ⇒ one model request burned for nothing. So when decision.messages.length === 0, this plugin returns the decision untouched (lib/index.mjs:241).

与官方 @deepseek-ai/dsh-time-context 的差异 / Differences from the Official @deepseek-ai/dsh-time-context

维度 官方 本插件
文案 英文三行 中文单行紧凑
触发 每步 + refreshIntervalMs 节流(默认 10 min) 只在 step === 1(官方那套节流逻辑整体不要)
时区 从 user-rpc.clientTimeZone 派生三态 固定 Asia/Shanghai(可配)
source.kind 'time-context' 'time-inject'(绝不复用官方标识)
invariant companion 可选提供 不提供
折叠里的自己 注入消息也算进 lastMessageTime 排除自己,距上条消息 量的确实是别人的消息

English:

Dimension Official This plugin
Text three lines of English one compact line of Chinese
Trigger every step + refreshIntervalMs throttling (default 10 min) only when step === 1 (the whole throttling scheme is deliberately dropped)
Time zone three states derived from user-rpc.clientTimeZone fixed Asia/Shanghai (configurable)
source.kind 'time-context' 'time-inject' (never reuses the official identifier)
invariant companion optionally provided not provided
Self in the fold injected messages also count towards lastMessageTime self is excluded, so 距上条消息 really does measure someone else's message

装法 / Installation

路线 A(推荐·改动面最小)——profile 的 cordis.patch.yml 里加一条 insert Route A (recommended · smallest footprint) — add one insert entry to the profile's cordis.patch.yml

  1. 把本仓源码拷进 profile 的 vendor/,并 link: 进 profile: English: Copy this repo's source into the profile's vendor/ and link: it into the profile:

    // ~/.dsh/profiles/web/package.json
    "dependencies": { "dsh-time-inject": "link:vendor/dsh-time-inject-src" }
    
  2. 在 ~/.dsh/profiles/web/cordis.patch.yml 追加: English: Append to ~/.dsh/profiles/web/cordis.patch.yml:

    - insert:
        - id: time-inject
          name: 'dsh-time-inject'
    
  3. 在 profile 目录跑一次 pnpm install(link: 依赖需要它建 junction),然后重启 dsh web。 English: Run pnpm install once in the profile directory (link: dependencies need it to create the junction), then restart dsh web.

    💡 如果你想跳过 pnpm install(例如你的 profile 里也有 file:vendor/*.tgz 型依赖, 而 install 会重新解包它们),可以手动建 Junction: mklink /J ~/.dsh/profiles/web/node_modules/dsh-time-inject <源码目录>(Windows), 效果与 link: 相同,且不改动任何锁文件。本仓的 tools/install.mjs 就是这条路线。

    💡 English: If you want to skip pnpm install (for example your profile also has file:vendor/*.tgz dependencies and install would re-unpack them), you can create the Junction by hand: mklink /J ~/.dsh/profiles/web/node_modules/dsh-time-inject <source dir> (Windows). The effect is identical to link:, and no lockfile is touched. This repo's tools/install.mjs takes exactly this route.

路线 B(当 bundle 包挂)——进 bundles 列表 Route B (mount it as a bundle) — add it to the bundles list

  1. 同 A 的 link: 依赖; English: Same link: dependency as route A;
  2. ~/.dsh/profiles/web/package.json 的 dsh.profile.bundles 里加 "dsh-time-inject" (本包的 dsh.bundle.patch 会让它自己的 cordis.patch.yml 被当成一层 patch); English: add "dsh-time-inject" to dsh.profile.bundles in ~/.dsh/profiles/web/package.json (this package's dsh.bundle.patch makes its own cordis.patch.yml count as one patch layer);
  3. pnpm install + 重启 dsh web。 English: pnpm install + restart dsh web.

⚠️ 别想用 patch 的 name 字段「把官方那行换成我们」:name 是断言不是改名, 不匹配就整条 warn + skip(静默失效)。官方那行 id: time-context 本来就 disabled: true,保持关闭、另起一条 insert 即可。

⚠️ English: Don't try to swap the official line for ours via the patch name field: name is an assertion, not a rename — on a mismatch the whole entry does warn + skip (a silent no-op). The official line id: time-context is already disabled: true, so leave it off and add a separate insert.

自带的安装 / 回滚脚本(默认干跑)/ Bundled Install & Rollback Scripts (dry-run by default)

tools/install.mjs 自动做上面的路线 A,四步全部可回滚: ① 先备份 profile 的 cordis.patch.yml → ② 把源码铺到 vendor/ → ③ 在 profile 的 node_modules/ 建 Junction → ④ 在 patch 末尾追加一行 insert。 tools/uninstall.mjs 负责回滚:只删 Junction(源码与备份保留);加 --backup <备份目录> 才额外从备份还原 cordis.patch.yml。 两个脚本默认都是干跑,只有加 --apply 才真的动手。

node tools/install.mjs                                 # 干跑:只打印将要做的事 + 现状检查
node tools/install.mjs --apply                         # 真装(第 ① 步先备份 cordis.patch.yml)
node tools/install.mjs --apply --backups <备份根目录>    # 真装 + 自定义备份根目录(默认 ~/.dsh-plugin-backups)
node tools/uninstall.mjs                               # 干跑:只打印将要做的事
node tools/uninstall.mjs --apply                       # 回滚:删 Junction(源码、备份都保留)
node tools/uninstall.mjs --apply --backup <备份目录>     # 回滚:删 Junction + 从该备份目录还原 cordis.patch.yml

English: tools/install.mjs automates route A above; all four steps are reversible: ① back up the profile's cordis.patch.yml first → ② lay the source down into vendor/ → ③ create the Junction under the profile's node_modules/ → ④ append the insert line to the patch. tools/uninstall.mjs rolls it back: it removes only the Junction (source and backups are kept); add --backup <backup dir> to also restore cordis.patch.yml from a backup. Both scripts are dry-run by default — they only act when --apply is passed.

node tools/install.mjs                                 # dry run: print what it would do + current-state checks
node tools/install.mjs --apply                         # actually install (step ① backs up cordis.patch.yml first)
node tools/install.mjs --apply --backups <backup root>  # actually install with a custom backup root (default ~/.dsh-plugin-backups)
node tools/uninstall.mjs                               # dry run: print what it would do
node tools/uninstall.mjs --apply                       # roll back: remove the Junction (source and backups kept)
node tools/uninstall.mjs --apply --backup <backup dir>  # roll back: remove the Junction + restore the patch from that backup

配置项 / Configuration

字段 类型 默认 说明
enabled boolean true 总开关
timeZone string 'Asia/Shanghai' 显示时区(IANA 名)。填非法名不会炸,会降级回默认时区
includeSubagents boolean true 子代理的轮次是否也注入

English:

Field Type Default Description
enabled boolean true master switch
timeZone string 'Asia/Shanghai' display time zone (IANA name). An invalid name will not crash it, it degrades to the default zone
includeSubagents boolean true whether subagent turns are injected as well

改配置 = 改 profile patch 里那一行的 config(整字段替换,不是深合并),或由 cordis 重载插件触发重新 apply。 例: English: Changing the configuration means editing config on that one line in the profile patch (whole-field replacement, not a deep merge), or letting cordis reload the plugin to trigger a fresh apply. Example:

- id: time-inject
  config:
    timeZone: 'Asia/Shanghai'
    includeSubagents: false

回滚(三步,任选其一即可停掉注入)/ Rollback (any one of three steps stops the injection)

  1. 最快:把 patch 里那条 insert 改成 disabled: true(或直接删掉那条 insert),重载/重启; English: Fastest: change that insert entry in the patch to disabled: true (or just delete the insert), then reload/restart;
  2. 从 dsh.profile.bundles / dependencies 里移除 dsh-time-inject,pnpm install,重启; English: remove dsh-time-inject from dsh.profile.bundles / dependencies, run pnpm install, restart;
  3. 想在不碰配置的前提下临时关:把 config.enabled 设为 false。 English: to switch it off temporarily without touching the mount configuration: set config.enabled to false.

用本仓脚本回滚(默认干跑): English: Rolling back with this repo's script (dry-run by default):

node tools/uninstall.mjs --apply --backup <备份目录>   # 删 Junction + 从该备份目录还原 cordis.patch.yml

(不加 --backup 只删 Junction、patch 里的 insert 行保留;脚本说明见「装法 / Installation」一节。) English: (without --backup it only removes the Junction and leaves the patch's insert line in place; the script is described under Installation.)

回滚不需要清任何数据:投影只是 sessionProjections 的一格折叠状态, 键消失后宿主读作「能力缺失」,不会留下垃圾;已注入的历史消息本来就在会话日志里, 那是只读事实、也不影响后续(本插件关掉后不再产生新的 source.kind === 'time-inject' 消息)。

English: Rollback does not require cleaning up any data: the projection is just one cell of folded state in sessionProjections; once the key is gone the host reads it as "capability missing" and no garbage is left behind. Messages that were already injected sit in the session log anyway — that is a read-only fact and it does not affect anything later (with the plugin off, no new source.kind === 'time-inject' messages are produced).

自测 / Self-Test

node test/selftest.mjs     # 纯函数 + 源码断言,零依赖(npm test)
node test/harness.mjs      # 宿主桩:真跑 apply() 与 handler
  • test/selftest.mjs:118 项,覆盖文案格式 / 中文星期 / 时长边界(0 秒·59 秒·59 分·跨天·跨月·跨年·闰日)/ 时钟回退钳 0 / UTC 偏移 / 非法输入不抛错,外加 lib/index.mjs 的 🩸 硬要求静态断言。 English: test/selftest.mjs: 118 cases, covering text format / Chinese weekday names / duration boundaries (0 s · 59 s · 59 min · across days · across months · across years · leap day) / clock rollback clamped to 0 / UTC offset / invalid input not throwing, plus static assertions for the 🩸 hard requirements in lib/index.mjs.
  • test/harness.mjs:81 项,用 module.registerHooks 把 schemastery / @deepseek-ai/dsh-llm 换成内存桩,真的 import 并调用 apply(),用假 ctx / 假 payload 验空轮保护、 注入消息形状(source 恰好 3 个 key、section.text === content[0].text)、 step 触发时机、异常语义、投影折叠与配置兜底。 English: test/harness.mjs: 81 cases. It uses module.registerHooks to replace schemastery / @deepseek-ai/dsh-llm with in-memory stubs, really imports and calls apply(), and with a fake ctx / fake payload it verifies the empty-turn guard, the injected message shape (source has exactly 3 keys, section.text === content[0].text), the step trigger timing, exception semantics, projection folding and configuration fallbacks.

文件 / Files

文件 职责
lib/text.mjs 中文文案纯函数(时间戳 / 星期 / 时长 / 偏移),零宿主依赖
lib/index.mjs 插件入口:name / inject / Config / apply,注册投影 + agent/pre-step
cordis.patch.yml bundle patch:一条 insert 把插件行挂进 loader 树
test/selftest.mjs 纯函数自测(进 npm test)
test/harness.mjs 宿主桩 harness(不进 npm test,它依赖 module.registerHooks)
tools/install.mjs 装机辅助:先备份 profile patch + 铺源码 + 建 Junction + 追加 patch 行(默认干跑、幂等、只增不删)
tools/uninstall.mjs 回滚辅助(默认干跑;只删 Junction,源码与备份保留;加 --backup <备份目录> 才还原 patch)
tools/scan-session.mjs 诊断:读多帧 zstd 会话日志,统计事件类型与注入来源(source.kind 分布)

English:

File Responsibility
lib/text.mjs pure functions for the Chinese text (timestamp / weekday / duration / offset), zero host dependencies
lib/index.mjs plugin entry: name / inject / Config / apply; registers the projection + agent/pre-step
cordis.patch.yml bundle patch: a single insert hooks the plugin line into the loader tree
test/selftest.mjs pure-function self-test (part of npm test)
test/harness.mjs host-stub harness (not part of npm test; it depends on module.registerHooks)
tools/install.mjs install helper: back up the profile patch first + lay down the source + create the Junction + append the patch line (dry-run by default, idempotent, add-only)
tools/uninstall.mjs rollback helper (dry-run by default; removes only the Junction, source and backups are kept; add --backup <backup dir> to also restore the patch)
tools/scan-session.mjs diagnostics: read multi-frame zstd session logs and tally event types and injection sources (source.kind distribution)

运行环境 / Requirements

  • DSH(DeepSeek Harness)≥ 0.1.7-rc.2 —— package.json 里 dsh.compatibility.dshReleases 声明为 compatible;本仓在该版本上实测通过。
  • Node.js ≥ 20 —— package.json 的 engines.node;本机在 Node 24 上实测通过。
  • 运行依赖:无。代码用到 schemastery,但它由 DSH 宿主提供(见 peerDependencies),本仓不随包安装任何第三方运行依赖。

English:

  • DSH (DeepSeek Harness) ≥ 0.1.7-rc.2 — declared compatible in package.json's dsh.compatibility.dshReleases; verified on that release.
  • Node.js ≥ 20 — see engines.node in package.json; verified on Node 24 here.
  • Runtime dependencies: none. The code uses schemastery, but it is provided by the DSH host (see peerDependencies); this package installs no third-party runtime dependency.

权限与依赖 / Permissions & Dependencies

  • 文件 / Files:插件运行时只读宿主给的会话事件与投影状态,自己不直接读写文件。tools/install.mjs --apply 会写 profile 的 vendor/dsh-time-inject-src/、在 profile 的 node_modules/ 建一个 Junction、并追加 profile 的 cordis.patch.yml(第 ① 步先把该文件备份到 ~/.dsh-plugin-backups/<时间戳>/);tools/uninstall.mjs --apply 只删那个 Junction,加 --backup <目录> 才还原 cordis.patch.yml(还原前把当前文件另存 .before-restore)。两个脚本不带 --apply 时不写任何文件。
  • 网络 / Network:未发现任何网络访问 —— 插件与脚本都不发起请求(无 HTTP 客户端、无遥测)。
  • 命令 / Commands:不调用外部命令,不需要管理员 / root(Windows 上建 Junction 也无需提权)。
  • 凭据 / Credentials:未发现读取或写入任何凭据(无 token、无密钥、无账号配置)。
  • 生命周期脚本 / Lifecycle scripts:无 —— package.json 里没有 preinstall / install / postinstall 等钩子。
  • 运行时依赖 / Runtime dependencies:schemastery 声明为 peerDependencies(由 DSH profile 提供,本包不自带);宿主侧 @deepseek-ai/dsh-llm 由 DSH 提供。自测不依赖任何外部包。
  • 已知风险 / Known risks:tools/uninstall.mjs --apply 会删除 profile 里指向本插件源码目录的 Junction(删除前已在代码里就地断言目标必须落在 profile 的 node_modules/ 之下、且不等于根自身);tools/install.mjs --apply 会覆盖 vendor/dsh-time-inject-src/ 里的旧源码(原 patch 已先备份)。

English:

  • Files: at runtime the plugin only reads the session events and projection state the host hands it; it does not read or write files itself. tools/install.mjs --apply writes the profile's vendor/dsh-time-inject-src/, creates one Junction under the profile's node_modules/, and appends to the profile's cordis.patch.yml (step ① backs that file up to ~/.dsh-plugin-backups/<timestamp>/ first); tools/uninstall.mjs --apply removes only that Junction, and only with --backup <dir> restores cordis.patch.yml (saving the current file as .before-restore first). Without --apply neither script writes anything at all.
  • Network: no network access found — neither the plugin nor the scripts issue requests (no HTTP client, no telemetry).
  • Commands: no external commands are invoked and no administrator/root is needed (creating a Junction on Windows needs no elevation either).
  • Credentials: none found being read or written (no tokens, keys or account configuration).
  • Lifecycle scripts: none — package.json defines no preinstall / install / postinstall hooks.
  • Runtime dependencies: schemastery is declared under peerDependencies (provided by the DSH profile, not bundled here); the host-side @deepseek-ai/dsh-llm is provided by DSH. The self-tests need no external packages.
  • Known risks: tools/uninstall.mjs --apply deletes the Junction in the profile that points at this plugin's source directory (before deleting, the code asserts in place that the target lies under the profile's node_modules/ and is not the root itself); tools/install.mjs --apply overwrites old sources in vendor/dsh-time-inject-src/ (the original patch has already been backed up).

许可 / License

MIT © 2026 CNyaotian

—/ 5

No ratings yet

Verified DSH bundle

Commit 7fea74b09ec0

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout