@deepseek-ai/dsh-time-context 0.2.0-rc.2 → 0.2.1-alpha.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/README.i18n.yaml CHANGED
@@ -9,14 +9,14 @@
9
9
  en: ec051915b3d81cda
10
10
  zh: 3a0ef832e3a5e419
11
11
  /deepseek-ai-dsh-time-context/summary:
12
- en: d152f34674644f83
13
- zh: 5521772386c3737e
12
+ en: d28b1e42b7fb9f84
13
+ zh: 729afbd7a4510c1b
14
14
  /deepseek-ai-dsh-time-context/table-of-contents:
15
15
  en: d152484eb41ac6b4
16
16
  zh: 09388d293f9be9cb
17
17
  /deepseek-ai-dsh-time-context/use-this-package:
18
- en: dedd1cdaec7140f1
19
- zh: 9399f5b677ff8f34
18
+ en: 0289a0503ae95934
19
+ zh: 8aa8e24f9fcba4f4
20
20
  /deepseek-ai-dsh-time-context/use-this-package/what-the-model-gets:
21
21
  en: 104b5d5609b00f33
22
22
  zh: f6115b4eaa24bda7
@@ -30,11 +30,11 @@
30
30
  en: 2d21fa2aad5a729d
31
31
  zh: cfbfada0eb647435
32
32
  /deepseek-ai-dsh-time-context/understand-the-implementation/design-concept:
33
- en: f2c19187501f7027
34
- zh: 48d31d0ea9bd8b16
33
+ en: 48381ad4596a177a
34
+ zh: be025dba74956fc8
35
35
  /deepseek-ai-dsh-time-context/understand-the-implementation/source-map:
36
- en: 05a0a4627013d3d2
37
- zh: 281429e6ef7f26ec
36
+ en: 269d7aa1f64c4278
37
+ zh: 086018a60cc3a351
38
38
  /deepseek-ai-dsh-time-context/understand-the-implementation/main-flow:
39
39
  en: ae9f13873dde5271
40
40
  zh: 873f32e67f8b202b
package/README.md CHANGED
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
9
9
 
10
10
  ## Summary
11
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 current-turn browser zones are mixed or missing. The shipped Web composition carries no row for it; the optional `@deepseek-ai/dsh-experimental-schedule-bundle` inserts and mounts it with Schedule from the Plugins page. Readings default to a 10-minute minimum interval; `refreshIntervalMs: 0` injects at every eligible step.
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 current-turn browser zones are mixed or missing. The shipped Web composition carries no row for it; `standard`, `cordis`, and `ptc` declare it. Readings default to a 10-minute minimum interval; `refreshIntervalMs: 0` injects at every eligible step.
13
13
 
14
14
  ## Table of Contents
15
15
 
@@ -25,7 +25,7 @@ English | [中文](README.zh.md)
25
25
  <a id="use-this-package"></a>
26
26
  ## Use this package
27
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.
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. The shipped Web composition carries no Host `time-context` row, and the `standard`, `cordis`, and `ptc` presets declare it beside the reminder tools that consume it, while `minimal` declares neither.
29
29
 
30
30
  ### What the model gets
31
31
 
@@ -64,7 +64,7 @@ This section explains the design of the plugin; the observable behavior is cover
64
64
 
65
65
  ### Design concept
66
66
 
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.
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: 'time-context', form: 'snapshot', sections: [{ name: 'time-context', text }] }`.
68
68
 
69
69
  ### Source map
70
70
 
@@ -73,7 +73,6 @@ The plugin prepends an `agent/pre-step` listener that delegates first and append
73
73
  | [`src/index.ts`](src/index.ts) | Plugin entry: pre-step listener, due scheduling, reading composition |
74
74
  | [`src/request-zone.ts`](src/request-zone.ts) | Browser-zone policy derivation from open-turn `user-rpc` sources |
75
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 |
77
76
 
78
77
  ### Main flow
79
78
 
package/README.zh.md CHANGED
@@ -9,7 +9,7 @@ kind: "package-reference"
9
9
 
10
10
  ## 概述
11
11
 
12
- `dsh-time-context` 给模型一只时钟:在符合条件的步骤上,它追加一条持久、带来源的读数,包含当前时间、附加到当前开放请求的浏览器时区,以及自前一条模型可见消息以来的经过时长。它帮助模型按用户的浏览器时区解释未明确限定时区的日期与时间;时区来源混杂或缺失时,它告诉模型去询问。随发行版交付的 Web 组合不含该行;可选的 `@deepseek-ai/dsh-experimental-schedule-bundle` 在插件管理页会插入它并随 Schedule 一起挂载。读数默认采用 10 分钟的最小间隔;`refreshIntervalMs: 0` 会在每个符合条件的步骤注入。
12
+ `dsh-time-context` 给模型一只时钟:在符合条件的步骤上,它追加一条持久、带来源的读数,包含当前时间、附加到当前开放请求的浏览器时区,以及自前一条模型可见消息以来的经过时长。它帮助模型按用户的浏览器时区解释未明确限定时区的日期与时间;时区来源混杂或缺失时,它告诉模型去询问。随发行版交付的 Web 组合不含该行;`standard`、`cordis` 与 `ptc` 声明它。读数默认采用 10 分钟的最小间隔;`refreshIntervalMs: 0` 会在每个符合条件的步骤注入。
13
13
 
14
14
  ## 目录
15
15
 
@@ -25,7 +25,7 @@ kind: "package-reference"
25
25
  <a id="use-this-package"></a>
26
26
  ## 使用本包
27
27
 
28
- 当模型需要按用户所在时区解释未限定的日期与时间,且请求本地浏览器时区可用或已配置的回退值可接受时,挂载此插件。每次注入都是持久历史中额外的一条 user 角色消息;当按步骤读数超出对话需要时,用 `refreshIntervalMs` 调度。
28
+ 当模型需要按用户所在时区解释未限定的日期与时间,且请求本地浏览器时区可用或已配置的回退值可接受时,挂载此插件。每次注入都是持久历史中额外的一条 user 角色消息;当按步骤读数超出对话需要时,用 `refreshIntervalMs` 调度。随发行版交付的 Web 组合不含宿主 `time-context` 行,由 `standard`、`cordis` 与 `ptc` preset 把它声明在消费它的提醒工具旁,而 `minimal` 两者都不声明。
29
29
 
30
30
  ### 模型能得到什么
31
31
 
@@ -64,7 +64,7 @@ kind: "package-reference"
64
64
 
65
65
  ### 设计理念
66
66
 
67
- 插件前置注册一个 `agent/pre-step` 监听器,先委托下游,需要注入且下游决策进入步骤时追加一条带来源的 `UserMessage`。每个读数都使用确切的快照来源 `{ kind: 'plugin', plugin: 'time-context', form: 'snapshot', sections: [{ name: 'time-context', text }] }`,不变式配套模块会校验该形状,根据原始 `user-rpc` 消息重新派生当前轮次的浏览器策略,并检查时间戳时区与经过时长基线。
67
+ 插件前置注册一个 `agent/pre-step` 监听器,先委托下游,需要注入且下游决策进入步骤时追加一条带来源的 `UserMessage`。每个读数都使用确切的快照来源 `{ kind: 'time-context', form: 'snapshot', sections: [{ name: 'time-context', text }] }`。
68
68
 
69
69
  ### 源码地图
70
70
 
@@ -73,7 +73,6 @@ kind: "package-reference"
73
73
  | [`src/index.ts`](src/index.ts) | 插件入口:pre-step 监听器、到期调度、读数组合 |
74
74
  | [`src/request-zone.ts`](src/request-zone.ts) | 从开放轮次 `user-rpc` 来源派生浏览器时区策略 |
75
75
  | [`src/timestamp.ts`](src/timestamp.ts) | `Intl.DateTimeFormat` 创建与时间戳格式化 |
76
- | [`src/invariant.ts`](src/invariant.ts) | 快照约定的不变式配套模块 |
77
76
 
78
77
  ### 主要流程
79
78
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-time-context",
3
3
  "description": "Durable per-step context with the current time and elapsed time",
4
- "version": "0.2.0-rc.2",
4
+ "version": "0.2.1-alpha.1",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -18,10 +18,6 @@
18
18
  "types": "./lib/types/index.d.ts",
19
19
  "default": "./lib/index.js"
20
20
  },
21
- "./invariant": {
22
- "types": "./lib/types/invariant.d.ts",
23
- "default": "./lib/invariant.js"
24
- },
25
21
  "./src/*": "./src/*",
26
22
  "./package.json": "./package.json",
27
23
  "./locale/*.json": "./locale/*.json"
@@ -29,39 +25,36 @@
29
25
  "files": [
30
26
  "locale/*.json",
31
27
  "lib/index.js",
32
- "lib/invariant.js",
33
28
  "lib/types/**/*.d.ts"
34
29
  ],
35
30
  "license": "MIT",
36
31
  "dependencies": {
37
32
  "zod": "^4.4.3",
38
- "@deepseek-ai/dsh-util-values": "0.2.0-rc.2",
39
- "@deepseek-ai/schemastery": "~3.18.4"
33
+ "@deepseek-ai/schemastery": "~3.18.5-alpha.1",
34
+ "@deepseek-ai/dsh-util-values": "0.2.1-alpha.1"
40
35
  },
41
36
  "peerDependencies": {
42
- "@deepseek-ai/dsh-agent": "0.2.0-rc.2",
43
- "@deepseek-ai/dsh-invariants": "0.2.0-rc.2",
44
- "@deepseek-ai/dsh-llm": "0.2.0-rc.2",
45
- "@deepseek-ai/dsh-session": "0.2.0-rc.2",
46
- "@deepseek-ai/dsh-session-projection": "0.2.0-rc.2",
47
- "@deepseek-ai/cordis": "~4.0.4"
37
+ "@deepseek-ai/dsh-agent": "0.2.1-alpha.1",
38
+ "@deepseek-ai/dsh-llm": "0.2.1-alpha.1",
39
+ "@deepseek-ai/dsh-session": "0.2.1-alpha.1",
40
+ "@deepseek-ai/dsh-session-projection": "0.2.1-alpha.1",
41
+ "@deepseek-ai/cordis": "~4.0.5-alpha.1"
48
42
  },
49
43
  "devDependencies": {
50
- "@deepseek-ai/cordis": "~4.0.4",
51
- "@deepseek-ai/dsh-agent": "0.2.0-rc.2",
52
- "@deepseek-ai/dsh-agent-loop": "0.2.0-rc.2",
53
- "@deepseek-ai/dsh-agent-loop-testkit": "0.2.0-rc.2",
54
- "@deepseek-ai/dsh-bash-local": "0.2.0-rc.2",
55
- "@deepseek-ai/dsh-app-boot": "0.2.0-rc.2",
56
- "@deepseek-ai/dsh-invariants": "0.2.0-rc.2",
57
- "@deepseek-ai/dsh-loader-smoke": "0.2.0-rc.2",
58
- "@deepseek-ai/dsh-session": "0.2.0-rc.2",
59
- "@deepseek-ai/dsh-session-checkpoint-policy": "0.2.0-rc.2",
60
- "@deepseek-ai/dsh-llm": "0.2.0-rc.2",
61
- "@deepseek-ai/dsh-session-persistence-jsonl": "0.2.0-rc.2",
62
- "@deepseek-ai/dsh-subprocess-local": "0.2.0-rc.2",
63
- "@deepseek-ai/dsh-system-prompt": "0.2.0-rc.2",
64
- "@deepseek-ai/dsh-tools": "0.2.0-rc.2",
65
- "@deepseek-ai/dsh-session-projection": "0.2.0-rc.2"
44
+ "@deepseek-ai/cordis": "~4.0.5-alpha.1",
45
+ "@deepseek-ai/dsh-agent": "0.2.1-alpha.1",
46
+ "@deepseek-ai/dsh-agent-loop-testkit": "0.2.1-alpha.1",
47
+ "@deepseek-ai/dsh-agent-loop": "0.2.1-alpha.1",
48
+ "@deepseek-ai/dsh-app-boot": "0.2.1-alpha.1",
49
+ "@deepseek-ai/dsh-bash-local": "0.2.1-alpha.1",
50
+ "@deepseek-ai/dsh-llm": "0.2.1-alpha.1",
51
+ "@deepseek-ai/dsh-loader-smoke": "0.2.1-alpha.1",
52
+ "@deepseek-ai/dsh-session": "0.2.1-alpha.1",
53
+ "@deepseek-ai/dsh-session-checkpoint-policy": "0.2.1-alpha.1",
54
+ "@deepseek-ai/dsh-session-persistence-jsonl": "0.2.1-alpha.1",
55
+ "@deepseek-ai/dsh-session-projection": "0.2.1-alpha.1",
56
+ "@deepseek-ai/dsh-subprocess-local": "0.2.1-alpha.1",
57
+ "@deepseek-ai/dsh-system-prompt": "0.2.1-alpha.1",
58
+ "@deepseek-ai/dsh-tools": "0.2.1-alpha.1"
66
59
  }
67
60
  }
package/lib/invariant.js DELETED
@@ -1,209 +0,0 @@
1
- import { assertNever } from "@deepseek-ai/dsh-util-values";
2
- //#region lib/types/request-zone.js
3
- /** Browser-zone derivation and model-facing policy text for one open request turn. */
4
- const IANA_TIME_ZONE = /^[A-Za-z][A-Za-z0-9_+.-]*(?:\/[A-Za-z0-9_+.-]+)+$/;
5
- /** Read and validate a Host-canonicalized browser zone from one ordinary user-rpc message. */
6
- function browserTimeZone(message) {
7
- const source = message.source;
8
- const value = source.kind === "user" && "rpcId" in source && typeof source.rpcId === "string" && "clientTimeZone" in source && typeof source.clientTimeZone === "string" ? source.clientTimeZone : void 0;
9
- if (value === void 0) return void 0;
10
- if (value !== "UTC" && !IANA_TIME_ZONE.test(value)) throw new TypeError(`browser time zone must be canonical UTC or IANA Area/Location: ${JSON.stringify(value)}`);
11
- let canonical;
12
- try {
13
- canonical = new Intl.DateTimeFormat("en-US", { timeZone: value }).resolvedOptions().timeZone;
14
- } catch (error) {
15
- throw new TypeError(`browser time zone is unsupported: ${JSON.stringify(value)}`, { cause: error });
16
- }
17
- if (canonical !== value) throw new TypeError(`browser time zone must be canonical: ${JSON.stringify(value)}`);
18
- return value;
19
- }
20
- /**
21
- * Derive the unique, mixed, or missing browser zone for one open turn.
22
- * @param messages - Entered and proposed user messages belonging to the turn.
23
- * @returns Sorted, duplicate-free browser-zone facts.
24
- * @throws TypeError when a user-rpc source carries an invalid or noncanonical zone.
25
- */
26
- function deriveBrowserTimeZoneContext(messages) {
27
- const timeZones = [...new Set(messages.flatMap((message) => {
28
- const timeZone = browserTimeZone(message);
29
- return timeZone === void 0 ? [] : [timeZone];
30
- }))].sort();
31
- const [timeZone, ...remaining] = timeZones;
32
- if (timeZone === void 0) return { kind: "missing" };
33
- if (remaining.length === 0) return {
34
- kind: "resolved",
35
- timeZone
36
- };
37
- return {
38
- kind: "mixed",
39
- timeZones
40
- };
41
- }
42
- /**
43
- * Render the model instruction for one browser-zone context.
44
- * @param context - Browser-zone facts for the open turn.
45
- * @returns One durable policy line.
46
- */
47
- function renderBrowserTimeZoneContext(context) {
48
- switch (context.kind) {
49
- case "resolved": return `Browser time zone for this request: ${context.timeZone}. Interpret otherwise-unqualified dates and times in this zone.`;
50
- case "mixed": return `Browser time zone for this request: mixed ${JSON.stringify(context.timeZones)}. Ask the user to clarify otherwise-unqualified dates and times.`;
51
- case "missing": return "Browser time zone for this request: unavailable. Ask the user to clarify otherwise-unqualified dates and times.";
52
- /* v8 ignore next 2 -- the closed BrowserTimeZoneContext union is exhausted above. */
53
- default: return assertNever(context, "BrowserTimeZoneContext");
54
- }
55
- }
56
- //#endregion
57
- //#region lib/types/timestamp.js
58
- /** ISO-shaped time-context timestamp formatting shared by production and replay validation. */
59
- /**
60
- * Create the exact formatter used by durable time-context readings.
61
- * @param timeZone - Explicit display zone, or `undefined` for the process fallback.
62
- * @returns A formatter with stable numeric local fields and long numeric offset.
63
- */
64
- function createTimestampFormatter(timeZone) {
65
- return new Intl.DateTimeFormat("en-US", {
66
- ...timeZone === void 0 ? {} : { timeZone },
67
- year: "numeric",
68
- month: "2-digit",
69
- day: "2-digit",
70
- hour: "2-digit",
71
- minute: "2-digit",
72
- second: "2-digit",
73
- hourCycle: "h23",
74
- timeZoneName: "longOffset"
75
- });
76
- }
77
- /**
78
- * Format an epoch millisecond value as an ISO-shaped timestamp with offset and IANA zone.
79
- * @param now - Epoch milliseconds to display.
80
- * @param formatter - Formatter created for `timeZone`.
81
- * @param timeZone - Canonical zone label carried in brackets.
82
- * @returns The durable timestamp text.
83
- */
84
- function formatTimestamp(now, formatter, timeZone) {
85
- const parts = Object.fromEntries(formatter.formatToParts(now).map((part) => [part.type, part.value]));
86
- const offset = parts.timeZoneName.replace(/^GMT$/, "GMT+00:00").slice(3);
87
- return `${parts["year"]}-${parts["month"]}-${parts["day"]}T${parts["hour"]}:${parts["minute"]}:${parts["second"]}${offset}[${timeZone}]`;
88
- }
89
- //#endregion
90
- //#region lib/types/invariant.js
91
- /** Package-owned durable clock-context invariants. @module @deepseek-ai/dsh-time-context/invariant */
92
- const PACKAGE_NAME = "@deepseek-ai/dsh-time-context";
93
- const SOURCE_NAME = "time-context";
94
- const READING = /* @__PURE__ */ new RegExp("^Time sampled while preparing turn (\\d+), step (\\d+): (\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:Z|[+-]\\d{2}:\\d{2})\\[[^\\]]+\\])\\n(Browser time zone for this request: .+)\\nElapsed since the preceding (model-visible message|step context): (?:unavailable|(?:(?:\\d+d )?(?:\\d+h )?(?:\\d+m )?\\d+s))\\.$");
95
- /** Cordis companion plugin name. */
96
- const name = "time-context-invariant";
97
- /** Service required before the companion can reserve package ownership. */
98
- const inject = ["invariants"];
99
- /** Derive the open step boundary at which a time-context reading may append. */
100
- function preparationPosition(history, fail) {
101
- let openTurn;
102
- let openStep;
103
- let requestStarted = false;
104
- for (const event of history) switch (event.type) {
105
- case "turn/start":
106
- openTurn = event.data.turn;
107
- openStep = void 0;
108
- requestStarted = false;
109
- break;
110
- case "step/start":
111
- openStep = event.data.step;
112
- requestStarted = false;
113
- break;
114
- case "request/header":
115
- requestStarted = true;
116
- break;
117
- case "step/end":
118
- openStep = void 0;
119
- requestStarted = false;
120
- break;
121
- case "turn/end":
122
- openTurn = void 0;
123
- openStep = void 0;
124
- requestStarted = false;
125
- break;
126
- default: break;
127
- }
128
- if (openTurn === void 0) fail("time-context reading must be appended inside an open turn");
129
- if (openStep === void 0) fail("time-context reading must follow step/start");
130
- if (requestStarted) fail("time-context reading must precede request/header");
131
- return {
132
- turn: openTurn,
133
- step: openStep
134
- };
135
- }
136
- /** Collect the entered user messages belonging to one open turn. */
137
- function requestMessages(history, turn) {
138
- const start = history.findLastIndex((event) => event.type === "turn/start" && event.data.turn === turn);
139
- return history.slice(start + 1).flatMap((event) => event.type === "user/message" ? [event.data] : []);
140
- }
141
- /** Validate one plugin-attributed time reading against its session position and timestamp. */
142
- function validateReading(history, event, fail) {
143
- const blockValue = event.data.content[0];
144
- const block = typeof blockValue === "object" && blockValue !== null ? blockValue : void 0;
145
- const blockText = block?.text;
146
- if (event.data.content.length !== 1 || block === void 0 || Object.keys(block).length !== 2 || block.type !== "text" || typeof blockText !== "string") fail("time-context messages must contain exactly one text block");
147
- const match = READING.exec(blockText);
148
- if (match === null) fail("time-context message does not match the durable reading format");
149
- const turn = Number(match[1]);
150
- const step = Number(match[2]);
151
- if (!Number.isSafeInteger(turn) || turn < 1 || !Number.isSafeInteger(step) || step < 1) fail("time-context turn and step must be positive safe integers");
152
- const expected = preparationPosition(history, fail);
153
- if (turn !== expected.turn || step !== expected.step) fail(`time-context reading names turn ${turn}/step ${step}, expected turn ${expected.turn}/step ${expected.step}`);
154
- const source = event.data.source;
155
- /* v8 ignore next 2 -- replay and dispatch callers select this exact package-owned source before validation. */
156
- if (source.kind !== SOURCE_NAME) fail("time-context source must retain package ownership");
157
- const sections = "sections" in source ? source.sections : void 0;
158
- const sectionValue = Array.isArray(sections) ? sections[0] : void 0;
159
- const section = typeof sectionValue === "object" && sectionValue !== null ? sectionValue : void 0;
160
- if (Object.keys(source).length !== 3 || source.form !== "snapshot" || !Array.isArray(sections) || sections.length !== 1 || section === void 0 || Object.keys(section).length !== 2 || section.name !== SOURCE_NAME || section.text !== blockText) fail("time-context source must carry only the exact snapshot text, not request authority");
161
- const renderedBrowserContext = match[4];
162
- const browserContext = deriveBrowserTimeZoneContext(requestMessages(history, turn));
163
- if (renderedBrowserContext !== renderBrowserTimeZoneContext(browserContext)) fail("time-context browser-zone text does not match current-turn user messages");
164
- const baseline = match[5];
165
- if (step === 1 !== (baseline === "model-visible message")) fail(`time-context step ${step} uses the wrong elapsed-time baseline ${JSON.stringify(baseline)}`);
166
- const rendered = match[3];
167
- /* v8 ignore next -- the preceding fixed regexp always supplies capture group three. */
168
- if (rendered === void 0) fail("time-context reading omitted its rendered timestamp");
169
- const renderedTime = Date.parse(rendered.replace(/\[[^\]]+\]$/, ""));
170
- if (!Number.isFinite(renderedTime) || !Number.isSafeInteger(event.time) || event.time < renderedTime) fail("time-context rendered timestamp must parse and not postdate its durable event");
171
- if (browserContext.kind === "resolved") {
172
- let expectedTimestamp;
173
- try {
174
- expectedTimestamp = formatTimestamp(renderedTime, createTimestampFormatter(browserContext.timeZone), browserContext.timeZone);
175
- } catch (error) {
176
- fail(`time-context browser zone cannot format its durable timestamp: ${String(error)}`);
177
- }
178
- if (rendered !== expectedTimestamp) fail("time-context rendered timestamp does not match the unique browser zone");
179
- }
180
- }
181
- /** Validate all package-owned readings already present in one session. */
182
- function validateSession(session, fail) {
183
- const events = session.snapshotEvents();
184
- for (const [index, event] of events.entries()) {
185
- if (event.type !== "user/message" || event.data.source.kind !== SOURCE_NAME) continue;
186
- validateReading(events.slice(0, index), event, fail);
187
- }
188
- }
189
- /** Install validation for loaded and newly appended context readings. */
190
- const install = Object.assign((ctx, fail) => {
191
- for (const session of ctx.sessions.list()) validateSession(session, fail);
192
- ctx.on("session/created", (session) => {
193
- validateSession(session, fail);
194
- }, { global: true });
195
- ctx.on("internal/dispatch", (_mode, eventName, args) => {
196
- if (eventName !== "session/event") return;
197
- const [session, event] = args;
198
- if (event.type !== "user/message" || event.data.source.kind !== SOURCE_NAME) return;
199
- validateReading(session.snapshotEvents(), event, fail);
200
- }, { global: true });
201
- }, { inject: ["sessions"] });
202
- /**
203
- * Register the time-context invariant companion.
204
- * @param ctx - Cordis context carrying the invariant service.
205
- * @returns the installed registration's disposer after setup succeeds.
206
- */
207
- const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
208
- //#endregion
209
- export { apply, inject, name };
@@ -1,13 +0,0 @@
1
- /** Package-owned durable clock-context invariants. @module @deepseek-ai/dsh-time-context/invariant */
2
- import type { Context } from '@deepseek-ai/cordis';
3
- /** Cordis companion plugin name. */
4
- export declare const name = "time-context-invariant";
5
- /** Service required before the companion can reserve package ownership. */
6
- export declare const inject: string[];
7
- /**
8
- * Register the time-context invariant companion.
9
- * @param ctx - Cordis context carrying the invariant service.
10
- * @returns the installed registration's disposer after setup succeeds.
11
- */
12
- export declare const apply: (ctx: Context) => Promise<() => void>;
13
- //# sourceMappingURL=invariant.d.ts.map