dsh-date-wrapper 0.2.0 → 0.4.0

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/INSTALL.ru.md ADDED
@@ -0,0 +1,130 @@
1
+ # Руководство по установке (официальный DSH CLI)
2
+
3
+ - [English README](./README.md)
4
+ - [中文 README](./README.zh.md)
5
+ - [日本語 README](./README.ja.md)
6
+ - [한국어 README](./README.ko.md)
7
+ - [Français README](./README.fr.md)
8
+ - [Deutsch README](./README.de.md)
9
+ - [Italiano README](./README.it.md)
10
+ - [Русский README](./README.ru.md)
11
+ - [Español README](./README.es.md)
12
+ - [Installation guide](./INSTALL.md)
13
+ - [中文安装指南](./INSTALL.zh.md)
14
+ - [日本語インストールガイド](./INSTALL.ja.md)
15
+ - [한국어 설치 안내](./INSTALL.ko.md)
16
+ - [Guide d'installation](./INSTALL.fr.md)
17
+ - [Installationsanleitung](./INSTALL.de.md)
18
+ - [Guida all'installazione](./INSTALL.it.md)
19
+ - [Руководство по установке](./INSTALL.ru.md)
20
+ - [Guía de instalación](./INSTALL.es.md)
21
+ - [Changelog](./CHANGELOG.md)
22
+ - [日本語 changelog](./CHANGELOG.ja.md)
23
+ - [한국어 changelog](./CHANGELOG.ko.md)
24
+ - [Français changelog](./CHANGELOG.fr.md)
25
+ - [Deutsch changelog](./CHANGELOG.de.md)
26
+ - [Italiano changelog](./CHANGELOG.it.md)
27
+ - [Русский changelog](./CHANGELOG.ru.md)
28
+ - [Español changelog](./CHANGELOG.es.md)
29
+
30
+ ## 0. Предварительные требования
31
+
32
+ ```powershell
33
+ echo $env:DSH_HOME # usually C:\Users\<you>\.dsh
34
+ dsh --version # verified on 0.1.1-rc.2 (0.1.x line, plugin <= 0.2.0); 0.2.0-rc.1+ needs plugin >= 0.3.0 (engines >=0.2.0-rc.1 <0.2.1-0)
35
+ pnpm --version # `dsh plugin` is a pnpm forwarder, so pnpm must be on PATH
36
+ ```
37
+
38
+ ## 1. Установка
39
+
40
+ Из GitHub (рекомендуется — pnpm копирует пакет в `node_modules`, а lockfile закрепляет точный коммит):
41
+
42
+ ```powershell
43
+ dsh plugin --profile web add github:drscrewdriver/dsh-date-wrapper
44
+ ```
45
+
46
+ Или из локальной копии (разработка):
47
+
48
+ ```powershell
49
+ dsh plugin --profile web add E:\test\rewrite-agently\mine-dsh-plugins\dsh-date-wrapper
50
+ ```
51
+
52
+ Или в режиме link (правки исходников вступают в силу после перезапуска, без переустановки):
53
+
54
+ ```powershell
55
+ dsh plugin --profile web add link:E:\test\rewrite-agently\mine-dsh-plugins\dsh-date-wrapper
56
+ ```
57
+
58
+ > ⚠️ Локальная установка через `file:` / `link:` делает профиль зависимым от этого пути. Переименование или удаление каталога затем ломает **все** pnpm-операции в профиле с ошибкой `ENOENT`, пока устаревшая зависимость не будет удалена — ровно это произошло, когда пакет переименовали из `dsh-time-wrapper`.
59
+ > ⚠️ Относительный путь привязывается к **вашему текущему каталогу**, только если он начинается с `.` или `..`;
60
+ > `mine-dsh-plugins\dsh-date-wrapper` разрешается внутри каталога профиля и найден не будет. Абсолютные пути надёжнее всего.
61
+
62
+ Признаки успешной установки:
63
+
64
+ 1. pnpm завершается с кодом 0;
65
+ 2. `C:\Users\<you>\.dsh\profiles\web\package.json` перечисляет `dsh-date-wrapper` в `dependencies`;
66
+ 3. в `dsh.profile.bundles` того же файла в конце появляется `dsh-date-wrapper` (пакет объявляет `dsh.bundle.patch`, поэтому автоматически попадает в список слоёв).
67
+
68
+ ## 2. Перезапуск
69
+
70
+ ```powershell
71
+ # stop the running dsh web process, then start it again
72
+ dsh web
73
+ ```
74
+
75
+ Затем обновите страницу в браузере.
76
+
77
+ > Bundle-patch **не** перезагружаются на лету: наблюдению подлежат только слои patch профиля / домашнего каталога. Изменение
78
+ > собственного `cordis.patch.yml` плагина или обновление плагина всегда требуют перезапуска.
79
+
80
+ ## 3. Проверка
81
+
82
+ Откройте новую сессию и отправьте любое сообщение. Дата подвешивается к **снимку контекста времени выполнения** (в сессии показывается как строка внедрённого контекста с источником `system-prompt`):
83
+
84
+ ```
85
+ Current date: 2026-09-08 Asia/Shanghai Tuesday
86
+ ```
87
+
88
+ Это `Current date: ` + `<ISO date> <IANA zone> <English weekday>`, 46 символов (~12 токенов), **без часов, минут и секунд**.
89
+
90
+ Также можно потренировать сам плагин из командной строки:
91
+
92
+ ```powershell
93
+ cd E:\test\rewrite-agently\mine-dsh-plugins\dsh-date-wrapper
94
+ node tests/format.test.mjs
95
+ node tests/context.test.mjs
96
+ ```
97
+
98
+ ## 4. Устранение неполадок
99
+
100
+ | Симптом | Причина и решение |
101
+ |---------|---------------|
102
+ | Старт падает с `date-wrapper: 非法 IANA timeZone` | Неверный `timeZone` в `cordis.patch.yml`. Это **намеренный fail-fast**: лучше упасть на старте, чем молча выдавать неверную дату в UTC |
103
+ | Старт падает с `duplicate loader entry id: date-wrapper` | В дереве сборки уже есть строка с таким id; удалите дубликат |
104
+ | Даты нет после установки | ① убедитесь, что `dsh-date-wrapper` есть в `dsh.profile.bundles`; ② убедитесь, что вы перезапустили и обновили страницу; ③ **убедитесь, что текущий пресет не fixed-prompt** (следующая строка) |
105
+ | Даты нет с некоторыми пресетами | Persona этого пресета задаёт `includeRuntimeContext: false` (так делают и официальный `minimal`, и локальный `simple-reply`). Такие пресеты явно запрещают последующим listener'ам добавлять содержимое в промпт, поэтому контекст времени выполнения этого плагина отбрасывается — ожидаемое поведение |
106
+ | Дата сдвинута на один день | `timeZone` не совпадает с вашей реальной зоной; через границу зон (например, 00:30 в Пекине = 16:30 UTC предыдущего дня) это проявляется как разница в один день |
107
+ | Вы также видите `Time sampled …` | Какой-то пресет явно монтирует `@deepseek-ai/dsh-time-context`. Этот плагин не загружает и не фильтрует его; вместе их использовать нельзя |
108
+ | Любая pnpm-операция в профиле падает с `ENOENT: no such file or directory, open '…'` | Зависимость `file:` / `link:` указывает на путь, которого больше не существует (пакет переименовали или удалили его tarball). Удалите устаревшую зависимость командой `dsh plugin --profile web remove <name>` и установите заново; установки через `github:` таким сбоем не страдают |
109
+
110
+ ## 5. Включение/выключение (панели-переключателя нет — переключатель это активация)
111
+
112
+ Отключите или включите его в своём слое profile patch — `C:\Users\<you>\.dsh\profiles\web\cordis.patch.yml`:
113
+
114
+ ```yaml
115
+ - id: date-wrapper
116
+ disabled: true # disabled; set back to false to restore
117
+ ```
118
+
119
+ - **Горячо, без перезапуска**: файл наблюдается Cordis HMR; `disabled: true` уничтожает fiber строки, и внедрение прекращается немедленно.
120
+ - Встроенная страница DSH **Settings → Plugins** показывает `enabled / disabled` (только для чтения).
121
+ - Файл должен быть YAML-массивом верхнего уровня; если его повредить, **старт упадёт** (fail-loud).
122
+ - Полное удаление идёт через `dsh plugin remove` (следующий раздел) и **требует перезапуска**.
123
+
124
+ ## 6. Удаление
125
+
126
+ ```powershell
127
+ dsh plugin --profile web remove dsh-date-wrapper
128
+ ```
129
+
130
+ Перезапустите dsh web.
package/INSTALL.zh.md CHANGED
@@ -4,19 +4,34 @@
4
4
  - [中文 README](./README.zh.md)
5
5
  - [日本語 README](./README.ja.md)
6
6
  - [한국어 README](./README.ko.md)
7
+ - [Français README](./README.fr.md)
8
+ - [Deutsch README](./README.de.md)
9
+ - [Italiano README](./README.it.md)
10
+ - [Русский README](./README.ru.md)
11
+ - [Español README](./README.es.md)
7
12
  - [Installation guide](./INSTALL.md)
8
13
  - [中文安装指南](./INSTALL.zh.md)
9
14
  - [日本語インストールガイド](./INSTALL.ja.md)
10
15
  - [한국어 설치 안내](./INSTALL.ko.md)
16
+ - [Guide d'installation](./INSTALL.fr.md)
17
+ - [Installationsanleitung](./INSTALL.de.md)
18
+ - [Guida all'installazione](./INSTALL.it.md)
19
+ - [Руководство по установке](./INSTALL.ru.md)
20
+ - [Guía de instalación](./INSTALL.es.md)
11
21
  - [Changelog](./CHANGELOG.md)
12
22
  - [日本語 changelog](./CHANGELOG.ja.md)
13
23
  - [한국어 changelog](./CHANGELOG.ko.md)
24
+ - [Français changelog](./CHANGELOG.fr.md)
25
+ - [Deutsch changelog](./CHANGELOG.de.md)
26
+ - [Italiano changelog](./CHANGELOG.it.md)
27
+ - [Русский changelog](./CHANGELOG.ru.md)
28
+ - [Español changelog](./CHANGELOG.es.md)
14
29
 
15
30
  ## 0. 前置条件
16
31
 
17
32
  ```powershell
18
33
  echo $env:DSH_HOME # 通常是 C:\Users\<你>\.dsh
19
- dsh --version # 本指南验证于 0.1.1-rc.2
34
+ dsh --version # 本指南验证于 0.1.1-rc.2(0.1.x 线,插件 ≤ 0.2.0);0.2.0-rc.1+ 需插件 ≥ 0.3.0(engines >=0.2.0-rc.1 <0.2.1-0)
20
35
  pnpm --version # dsh plugin 是 pnpm 转发器,pnpm 必须在 PATH 上
21
36
  ```
22
37
 
package/README.de.md ADDED
@@ -0,0 +1,236 @@
1
+ # dsh-date-wrapper
2
+
3
+ - [English README](./README.md)
4
+ - [中文 README](./README.zh.md)
5
+ - [日本語 README](./README.ja.md)
6
+ - [한국어 README](./README.ko.md)
7
+ - [Français README](./README.fr.md)
8
+ - [Deutsch README](./README.de.md)
9
+ - [Italiano README](./README.it.md)
10
+ - [Русский README](./README.ru.md)
11
+ - [Español README](./README.es.md)
12
+ - [Installation guide](./INSTALL.md)
13
+ - [中文安装指南](./INSTALL.zh.md)
14
+ - [日本語インストールガイド](./INSTALL.ja.md)
15
+ - [한국어 설치 안내](./INSTALL.ko.md)
16
+ - [Guide d'installation](./INSTALL.fr.md)
17
+ - [Installationsanleitung](./INSTALL.de.md)
18
+ - [Guida all'installazione](./INSTALL.it.md)
19
+ - [Руководство по установке](./INSTALL.ru.md)
20
+ - [Guía de instalación](./INSTALL.es.md)
21
+ - [Changelog](./CHANGELOG.md)
22
+ - [日本語 changelog](./CHANGELOG.ja.md)
23
+ - [한국어 changelog](./CHANGELOG.ko.md)
24
+ - [Français changelog](./CHANGELOG.fr.md)
25
+ - [Deutsch changelog](./CHANGELOG.de.md)
26
+ - [Italiano changelog](./CHANGELOG.it.md)
27
+ - [Русский changelog](./CHANGELOG.ru.md)
28
+ - [Español changelog](./CHANGELOG.es.md)
29
+
30
+ > Die Liste der unterstützten DSH-Versionen wird **per Skript verwaltet, nicht von Hand geschrieben** — siehe den generierten Block unten (einzige Quelle: `scripts/hosts.mjs`; verteilt durch `scripts/sync-hosts.mjs` an `package.json` peerDependencies + engines.dsh, `dsh.plugin.json` und dieses README in neun Sprachen). Der einzige Hostvertrag des Plugins ist `systemPrompt.context({ name, order, text })`; es registriert keinen Settings-Namespace, liest keine Sitzungsdaten und macht keine RPC-Aufrufe — hostseitige Umbauten berühren es nicht.
31
+
32
+ > Eine minimale Datumszeile: Sie hängt `Current date: 2026-09-08 Asia/Shanghai Tuesday` (46 Zeichen, ~12 Tokens) an den Laufzeitkontext-Snapshot an, den DSH ohnehin schon sendet.
33
+ > Sie lädt `@deepseek-ai/dsh-time-context` **nicht**, fügt **keine** zusätzlichen Sitzungsnachrichten hinzu, patcht den DSH-Quellcode **nicht** und braucht keinen PR.
34
+
35
+ - [Wie es funktioniert: DSH-Sitzungen, JSONL und Request-Assemblierung](./docs/dsh-session-and-context-mechanics.md) (Chinesisch)
36
+ - [HANDOVER.md](./HANDOVER.md) (Chinesisch)
37
+
38
+ <!-- host-compat:begin — generated by scripts/sync-hosts.mjs from scripts/hosts.mjs; DO NOT EDIT -->
39
+ - **Unterstützte DSH-Hosts:** `0.1.0-rc.2` `0.1.0-rc.3` `0.1.0-rc.6` `0.1.0-rc.7` `0.1.0-rc.8` `0.1.1-rc.1` `0.1.1-rc.2` `0.1.2-rc.1` `0.1.5-rc.1` `0.1.5-rc.2` `0.1.5-rc.3` `0.1.7-rc.1` `0.1.7-rc.2` `0.2.0-rc.1` `0.2.0-rc.2` (15 RC über die Linien 0.1.0 / 0.1.1 / 0.1.2 / 0.1.5 / 0.1.7 / 0.2.0)
40
+ - **Zur Laufzeit verifiziert:** `0.1.0-rc.2`, `0.1.0-rc.3`, `0.1.0-rc.6`, `0.1.0-rc.7`, `0.1.0-rc.8`, `0.1.1-rc.1`, `0.1.1-rc.2`, `0.1.2-rc.1`, `0.1.5-rc.1`, `0.1.5-rc.2`, `0.1.5-rc.3`, `0.1.7-rc.1`, `0.1.7-rc.2`, `0.2.0-rc.1`, `0.2.0-rc.2` — Nachweise in `.compat-results/`
41
+ <!-- host-compat:end -->
42
+
43
+ ## Was dieses Plugin löst
44
+
45
+ DSHs eigenes `@deepseek-ai/dsh-time-context` injiziert bei jeder Anfrage etwa **280 Zeichen** Metadaten:
46
+
47
+ ```
48
+ Time sampled while preparing turn 3, step 2: 2026-09-08T16:05:36+08:00[Asia/Shanghai]
49
+ Browser time zone for this request: Asia/Shanghai. Interpret otherwise-unqualified dates and times in this zone.
50
+ Elapsed since the preceding model-visible message: 2m 34s.
51
+ ```
52
+
53
+ Dieses Plugin komprimiert dieselbe Information in eine einzige Zeile von **46 Zeichen** und verlegt den Ablageort — sie landet nicht mehr im Nachrichtenstrom:
54
+
55
+ ```
56
+ Current date: 2026-09-08 Asia/Shanghai Tuesday
57
+ ```
58
+
59
+ | Dimension | `dsh-time-context` | `dsh-date-wrapper` |
60
+ |-----------|--------------------|--------------------|
61
+ | Injizierter Text | ~280 Zeichen | 46 Zeichen (↓84 %), ~12 Tokens |
62
+ | Ablageort | Eine Nachricht pro Pre-Step (`user/message`) | Der Laufzeitkontext-Snapshot der Plattform (`systemPrompt.context`) |
63
+ | Häufigkeit | Ein Ereignis pro geeignetem Step | Wird mit dem Snapshot nur erneut gesendet, wenn sich der Text ändert (0 Ereignisse innerhalb eines Tages) |
64
+ | Abhängigkeit | Dienst `agents` | Dienst `systemPrompt` |
65
+ | Laufzeit-Abhängigkeiten | — | keine |
66
+
67
+ ## Versionskompatibilität
68
+
69
+ | Punkt | Befund |
70
+ |------|---------|
71
+ | Ziel-DSH-Versionen | 0.1.0-rc.7 → 0.1.7.x (0.1.x-Linie — Artefakt ≤ 0.2.0) und 0.2.0-rc.1 → 0.2.0.x (0.2.0-Linie, `engines.dsh: >=0.2.0-rc.1 <0.2.1-0` — Artefakt ≥ 0.3.0) |
72
+ | Settings-API | **Nicht anwendbar**: Das Plugin registriert keine Settings und exportiert kein schemastery-`Config` |
73
+ | Genutzte Kontraktpunkte | Genau einer — `systemPrompt.context()` |
74
+ | Konflikt mit nativer Funktion | Überschneidet sich mit `@deepseek-ai/dsh-time-context`; **nicht beide gleichzeitig verwenden**. Nicht standardmäßig installiert = standardmäßig aus |
75
+ | Browser-Seite | **Keine**: kein Slot, kein DOM, keine CSS-Semantik-Tokens |
76
+ | DSH-Paket-Imports | **Null**: nichts aus `@deepseek-ai/*`, was strenger ist als das Muster „Laufzeiterkennung + doppeltes API-Fallback“ |
77
+
78
+ | Kontraktpunkt | 0.1.0-rc.7 | 0.1.1-rc.2 | 0.1.2-rc.1 | 0.1.3-alpha.2 | 0.1.7-rc.2 | 0.2.0-rc.1 |
79
+ |---|---|---|---|---|---|---|
80
+ | `systemPrompt.context(ctx): () => void` | ja | ja (auf diesem Host verifiziert) | ja | ja | ja | ja (Diff gegenüber 0.1.7-rc.2: nur die Versionszeichenkette) |
81
+ | `PromptContext = { name, order, text }`, kein `complete`-Feld | ja | ja | ja | ja | ja | ja |
82
+ | `includeRuntimeContext` / `suppressRuntimeContext` | ja | ja | ja | ja | ja | ja |
83
+ | agent-loop `project()`-Textdeduplizierung und `surfaceOp: "append"` | ja | ja | ja | nicht verglichen | ja | ja |
84
+ | `order: 116` kollisionsfrei (110 / 115 / 120 vergeben) | ja | ja | ja | ja | ja | ja |
85
+
86
+ > Methode: `npm pack @deepseek-ai/dsh-system-prompt@<version>`, entpacken und `lib/types/index.d.ts` sowie `lib/index.js` vergleichen; mit `@deepseek-ai/dsh-agent-loop` ebenso.
87
+ > Zwischen `dsh-v0.1.7-rc.2` und `dsh-v0.2.0-rc.1` ist der Diff von `packages/core/system-prompt` eine einzelne Versionszeile, die `systemPrompt.context`-Aufrufstellen bei 110/115/120 sind unverändert, und der Plugin-Migrationsguide enthält keinen `systemPrompt`-Eintrag.
88
+ > Nur 0.1.1-rc.2 wurde auf diesem Host **zur Laufzeit** verifiziert; der Laufzeit-Smoketest für 0.2.0-rc.1 ist in `HANDOVER.md` §7 dokumentiert.
89
+
90
+ ## Warum ein Laufzeitkontext-Snapshot statt einer Nachricht
91
+
92
+ Der erste Versuch kopierte `dsh-time-context` und hängte in `agent/pre-step` ein `user/message` an. Die gemessenen Kosten waren zu hoch: Jedes JSONL-Ereignis wiegt **339 Byte** (der Text macht nur 46 davon aus, weil `content` und `sections` je eine Kopie speichern), und es wurde **bei jedem Zug** eines geschrieben.
93
+
94
+ Werden stattdessen ein Laufzeitkontext registriert, wird das Datum in die Snapshot-Nachricht eingefaltet, die die Plattform ohnehin schon sendet:
95
+
96
+ - Die Plattform **dedupliziert Snapshots nach Text** (`RuntimeContextProjection.project()` in `dsh-agent-loop`: `if (this.retained?.text === snapshot) return`); solange sich das Datum nicht ändert, wird **kein einziges zusätzliches Ereignis geschrieben**;
97
+ - Snapshots **hängen** eine neue Nachricht an (`surfaceOp: 'append'`), statt sie an Ort und Stelle umzuschreiben; die Anfragesequenz wächst nur → **der Präfix-Cache bleibt erhalten**;
98
+ - Unsere Grenzkosten sind diese 46 Byte, und nur wenn der Snapshot erneut gesendet wird, weil sich sein Text geändert hat.
99
+
100
+ Gemessen auf diesem Host (eine echte Sitzung, 10 Züge / 231 Steps):
101
+
102
+ | Punkt | Gemessen |
103
+ |------|----------|
104
+ | Laufzeitkontext-Snapshots der Plattform | 2 Ereignisse, je 1133 B, insgesamt 2,3 KB |
105
+ | Echte Nutzernachrichten | 10 Ereignisse, je 396 B |
106
+ | Alter Ansatz (eine Nachricht pro Zug) | 10 × 339 B ≈ 3,4 KB |
107
+ | Dieser Ansatz | 0 zusätzliche Ereignisse; ~46 B in einen bestehenden Snapshot gefaltet |
108
+
109
+ ## Konfiguration
110
+
111
+ Wird mit `cordis.patch.yml` ausgeliefert; nach Änderungen neu starten:
112
+
113
+ ```yaml
114
+ - insert:
115
+ - id: date-wrapper
116
+ name: dsh-date-wrapper
117
+ config:
118
+ timeZone: Asia/Shanghai # IANA zone; omit to use the process zone
119
+ ```
120
+
121
+ - Ein ungültiger `timeZone` wirft beim Start eine Ausnahme (**kein** stilles Zurückfallen auf UTC).
122
+ - Der Zonenname im Text ist der aufgelöste IANA-Name (der Zonenname des Prozesses, wenn `timeZone` weggelassen wird).
123
+ - Der Laufzeitkontext-Eintrag heißt `date-wrapper:date` mit der Order `116` (bereits vergeben: 110 sandbox, 115 approval, 120 subagent).
124
+ - Das Plugin exportiert **kein schemastery-`Config`**, deshalb überspringt seine Konfiguration die Schemavalidierung des Hosts; alles wird von Hand in `validateConfig()` geprüft. Deshalb hat die Seite Settings → Plugins auch kein Konfigurationsformular dafür.
125
+
126
+ ## An/Aus: Die Aktivierung des Plugins ist der Schalter, es gibt keinen Panel-Umschalter
127
+
128
+ Das Plugin liefert **weder** einen Settings-Panel-Umschalter **noch** ein `enabled`-Konfigurationsfeld, weil:
129
+
130
+ - Der Funktionsschalter *ist* die Frage, ob die Plugin-Zeile aktiv ist. Inaktiv → `apply()` läuft nie → der Laufzeitkontext-Eintrag existiert nicht → es wird kein einziges Zeichen injiziert.
131
+ - Es gibt keine Browser-Seite (`dsh.client`), also besitzt die UI kein Widget von uns.
132
+ - DSHs eingebaute Seite **Settings → Plugins** zeigt jeden Eintrag bereits als `enabled / disabled` (schreibgeschützt).
133
+
134
+ ### So schaltet man es aus
135
+
136
+ Überschreiben Sie es über die `id` in **Ihrer eigenen** Profil-Patch-Schicht — `C:\Users\<you>\.dsh\profiles\web\cordis.patch.yml`:
137
+
138
+ ```yaml
139
+ - id: date-wrapper
140
+ disabled: true # disabled; set back to false to restore
141
+ ```
142
+
143
+ - **Heiß, ohne Neustart**: Diese Datei wird vom Cordis-HMR überwacht, und `disabled: true` entsorgt die Fiber der Zeile direkt.
144
+ - Existiert die Zeile `date-wrapper` noch nicht (nicht installiert), schreibt dieser Patch nur eine Warnung `entry "date-wrapper" not found` ins Log; der Start gelingt trotzdem.
145
+ - ⚠️ Die Datei muss ein **YAML-Array auf oberster Ebene** sein; ist sie fehlerhaft, **schlägt der Start fehl** (DSH ist bei Nutzer-Patch-Schichten fail-loud).
146
+
147
+ ### So entfernt man es vollständig
148
+
149
+ ```bash
150
+ dsh plugin --profile web remove dsh-date-wrapper
151
+ ```
152
+
153
+ Die Entfernung läuft über die Bundle-Schicht und **erfordert einen Neustart** von dsh web (Bundle-Patches werden nicht heiß nachgeladen).
154
+
155
+ ## Installation
156
+
157
+ ```bash
158
+ dsh plugin --profile web add github:drscrewdriver/dsh-date-wrapper
159
+ ```
160
+
161
+ Starten Sie dsh web neu und laden Sie die Seite neu. Lokale Pfade, Link-Modus und Fehlerbehebung: [INSTALL.de.md](./INSTALL.de.md).
162
+
163
+ ## Verifikation
164
+
165
+ | # | Wie | Erwartet |
166
+ |---|-----|----------|
167
+ | A1 | Neue Sitzung öffnen und eine Nachricht senden | Der Laufzeitkontext-Snapshot enthält `Current date: YYYY-MM-DD <zone> <weekday>` (angezeigt als injizierte Kontextzeile mit der Quelle `system-prompt`) |
168
+ | A2 | Diese Zeile prüfen | ≤50 Zeichen (46 gemessen; der PRD-Grenzwert von 30 wurde für das gewünschte Format gelockert) |
169
+ | A3 | Plugin deaktivieren (Profil-Patch `disabled: true`) | Die Zeile erscheint in den Snapshots späterer Sitzungen nicht mehr |
170
+ | A4 | Sitzungslog durchsuchen | Kein `Time sampled` / `Elapsed since` / `Browser time zone` |
171
+ | A5 | `timeZone` auf `UTC` setzen und neu starten | Das Datum folgt UTC (an Zonengrenzen kann es um einen Tag abweichen) |
172
+
173
+ ## Hinweise zur Implementierung
174
+
175
+ ```
176
+ dsh-date-wrapper/
177
+ ├── package.json # name / type: module / main / exports["."] / dsh.bundle.patch / files
178
+ ├── cordis.patch.yml # one insert row (no patch-level id → lands at the profile root = host plane)
179
+ ├── src/
180
+ │ ├── format.js # pure functions: resolveZone / renderDate / createDateContextText / validateConfig / TEXT_LABEL
181
+ │ └── index.js # apply(ctx, config) → ctx.inject(['systemPrompt'], …) → systemPrompt.context(...)
182
+ └── tests/
183
+ ├── format.test.mjs # 11 cases (zone projection, weekday, format and length, degradation, config validation)
184
+ └── context.test.mjs # 7 cases (registration contract against a fake ctx)
185
+ ```
186
+
187
+ - **Host-Ebenen-Zeile**: `ctx.inject(['systemPrompt'], …)` öffnet eine Kind-Fiber; fehlt der Dienst, registriert das Plugin stillschweigend nichts, statt den gesamten Boot-Vorgang scheitern zu lassen.
188
+ - **Fail-soft-Textanbieter**: Ein Wurf während der Prompt-Assemblierung würde **jede** Anfrage scheitern lassen; ein Renderfehler gibt daher eine leere Zeichenkette zurück (die Plattform filtert leeren Text heraus).
189
+ - **Kein `complete`**: Es gesetzt würde das gesamte Systemprompt verschatten.
190
+ - **Deduplizierung ist Sache der Plattform**: Es wird kein Agent-Zustand gepflegt; über Mitternacht trägt der Snapshot einfach das neue Datum.
191
+ - **Lebenszyklus**: Die Registrierung gehört zur Kind-Fiber von `ctx.inject` und wird eingesammelt, wenn das Plugin deaktiviert wird.
192
+
193
+ ## Entwicklung: TDD + Lint
194
+
195
+ ```bash
196
+ npm install # devDependencies only (eslint / @eslint/js); zero runtime dependencies
197
+
198
+ npm run tdd # watch mode: rerun on src/ or tests/ changes (node --test --watch)
199
+ npm test # one full run: node --test "tests/*.test.mjs"
200
+ node tests/format.test.mjs # run a single file (most reliable under a sandbox: no child process)
201
+
202
+ npm run lint # eslint . (src + tests + eslint.config.mjs)
203
+ npm run lint:fix # auto-fix what can be fixed
204
+ npm run verify # lint + test; run this before committing
205
+ ```
206
+
207
+ ### Red-Green-Refactor
208
+
209
+ Testfälle bilden die Akzeptanzkriterien direkt ab: erst eine fehlschlagende Behauptung schreiben, dann sie bestehen lassen.
210
+
211
+ | Schritt | Aktion | Befehl |
212
+ |------|--------|---------|
213
+ | 1 rot | In `tests/*.test.mjs` eine nach dem Akzeptanzkriterium benannte Behauptung ergänzen, die ein Verhalten prüft, das Sie **noch nicht** haben | `npm run tdd` |
214
+ | 2 grün | In `src/` die minimale Implementierung schreiben, ohne andere Behauptungen anzufassen | `npm run tdd` |
215
+ | 3 refactor | Im Grünen umbenennen und reine Funktionen extrahieren; `src/format.js` trägt die gesamte reine Logik, `src/index.js` registriert nur | `npm run tdd` |
216
+ | 4 Tor | Vor dem Commit lint + die gesamte Suite laufen lassen | `npm run verify` |
217
+
218
+ Heute 18 Behauptungen: `format.test.mjs` (11) deckt die reinen Funktionen ab, `context.test.mjs` (7) prüft den Registrierungskontrakt gegen ein falsches ctx.
219
+
220
+ ### Highlights der Lint-Konfiguration
221
+
222
+ - ESLint 10 Flat Config (`eslint.config.mjs`) mit `@eslint/js` recommended als Basislinie.
223
+ - Verschärfte Regeln: `eqeqeq`, `prefer-const`, `object-shorthand`, `no-unused-vars` (`_`-Präfix ausgenommen).
224
+ - Die Node-Globals `crypto` / `console` / `process` sind explizit deklariert, sonst meldet `no-undef` Fehlalarme.
225
+
226
+ ## Bekannte Einschränkungen
227
+
228
+ - **Inaktiv unter Fixed-Prompt-Presets**: Setzt die Persona eines Presets `includeRuntimeContext: false` (das offizielle `minimal` und das lokale `simple-reply` tun beide), gibt `assemble()` `contexts: []` zurück und der Eintrag dieses Plugins wird komplett verworfen. Solche Presets sind darauf ausgelegt, späteren Listeners das Hinzufügen von Prompt-Inhalt zu verbieten.
229
+ - **Alte Snapshots bleiben in der Historie**: Ändert sich das Datum, hängt die Plattform einen neuen Snapshot an (der alte bleibt erhalten), und der neue wird über seine eigene Erklärung „This snapshot supersedes earlier runtime-context snapshots“ wirksam — genauso, wie die Plattform Änderungen an cwd / Sandbox / Freigaberichtlinie behandelt.
230
+ - **Bundle-Patches werden nicht heiß nachgeladen**: Änderungen an `cordis.patch.yml` oder ein Plugin-Upgrade erfordern einen Neustart von dsh web (das Ändern von `disabled` im Profil-Patch ist heiß).
231
+ - **`dsh-time-context` wird weder geladen noch gefiltert**: Montieren Sie es explizit in einem Preset, erscheint sein ausführlicher Text wie üblich. Nicht beide gleichzeitig verwenden.
232
+ - **Keine Laufzeitsonde für den Kontraktpunkt**: `systemPrompt.context` wird ungeschützt aufgerufen; eine künftige Umbenennung durch DSH würde sich als Plugin-Ladefehler statt als stille Verschlechterung zeigen (siehe `HANDOVER.md` §7).
233
+
234
+ ## Lizenz
235
+
236
+ MIT
package/README.es.md ADDED
@@ -0,0 +1,236 @@
1
+ # dsh-date-wrapper
2
+
3
+ - [English README](./README.md)
4
+ - [中文 README](./README.zh.md)
5
+ - [日本語 README](./README.ja.md)
6
+ - [한국어 README](./README.ko.md)
7
+ - [Français README](./README.fr.md)
8
+ - [Deutsch README](./README.de.md)
9
+ - [Italiano README](./README.it.md)
10
+ - [Русский README](./README.ru.md)
11
+ - [Español README](./README.es.md)
12
+ - [Installation guide](./INSTALL.md)
13
+ - [中文安装指南](./INSTALL.zh.md)
14
+ - [日本語インストールガイド](./INSTALL.ja.md)
15
+ - [한국어 설치 안내](./INSTALL.ko.md)
16
+ - [Guide d'installation](./INSTALL.fr.md)
17
+ - [Installationsanleitung](./INSTALL.de.md)
18
+ - [Guida all'installazione](./INSTALL.it.md)
19
+ - [Руководство по установке](./INSTALL.ru.md)
20
+ - [Guía de instalación](./INSTALL.es.md)
21
+ - [Changelog](./CHANGELOG.md)
22
+ - [日本語 changelog](./CHANGELOG.ja.md)
23
+ - [한국어 changelog](./CHANGELOG.ko.md)
24
+ - [Français changelog](./CHANGELOG.fr.md)
25
+ - [Deutsch changelog](./CHANGELOG.de.md)
26
+ - [Italiano changelog](./CHANGELOG.it.md)
27
+ - [Русский changelog](./CHANGELOG.ru.md)
28
+ - [Español changelog](./CHANGELOG.es.md)
29
+
30
+ > La lista de versiones de DSH compatibles se **gestiona por script, no se escribe a mano** — véase el bloque generado abajo (fuente única: `scripts/hosts.mjs`; distribuido por `scripts/sync-hosts.mjs` a `package.json` peerDependencies + engines.dsh, `dsh.plugin.json` y este README en nueve idiomas). El único contrato del plugin con el host es `systemPrompt.context({ name, order, text })`; no registra espacios de nombres de ajustes, no lee datos de sesión ni hace llamadas RPC — las reescrituras del host no le afectan.
31
+
32
+ > Una línea de fecha minimalista: cuelga `Current date: 2026-09-08 Asia/Shanghai Tuesday` (46 caracteres, ~12 tokens) de la instantánea del contexto de ejecución que DSH ya envía de por sí.
33
+ > **No** carga `@deepseek-ai/dsh-time-context`, **no** añade mensajes de sesión extra, **no** parchea el código fuente de DSH y no necesita ningún PR.
34
+
35
+ - [Cómo funciona: sesiones de DSH, JSONL y ensamblado de la petición](./docs/dsh-session-and-context-mechanics.md) (chino)
36
+ - [HANDOVER.md](./HANDOVER.md) (chino)
37
+
38
+ <!-- host-compat:begin — generated by scripts/sync-hosts.mjs from scripts/hosts.mjs; DO NOT EDIT -->
39
+ - **Hosts DSH compatibles:** `0.1.0-rc.2` `0.1.0-rc.3` `0.1.0-rc.6` `0.1.0-rc.7` `0.1.0-rc.8` `0.1.1-rc.1` `0.1.1-rc.2` `0.1.2-rc.1` `0.1.5-rc.1` `0.1.5-rc.2` `0.1.5-rc.3` `0.1.7-rc.1` `0.1.7-rc.2` `0.2.0-rc.1` `0.2.0-rc.2` (15 rc en las líneas 0.1.0 / 0.1.1 / 0.1.2 / 0.1.5 / 0.1.7 / 0.2.0)
40
+ - **Verificado en tiempo de ejecución:** `0.1.0-rc.2`, `0.1.0-rc.3`, `0.1.0-rc.6`, `0.1.0-rc.7`, `0.1.0-rc.8`, `0.1.1-rc.1`, `0.1.1-rc.2`, `0.1.2-rc.1`, `0.1.5-rc.1`, `0.1.5-rc.2`, `0.1.5-rc.3`, `0.1.7-rc.1`, `0.1.7-rc.2`, `0.2.0-rc.1`, `0.2.0-rc.2` — evidencia en `.compat-results/`
41
+ <!-- host-compat:end -->
42
+
43
+ ## Qué resuelve este plugin
44
+
45
+ El `@deepseek-ai/dsh-time-context` nativo de DSH inyecta unos **280 caracteres** de metadatos en cada petición:
46
+
47
+ ```
48
+ Time sampled while preparing turn 3, step 2: 2026-09-08T16:05:36+08:00[Asia/Shanghai]
49
+ Browser time zone for this request: Asia/Shanghai. Interpret otherwise-unqualified dates and times in this zone.
50
+ Elapsed since the preceding model-visible message: 2m 34s.
51
+ ```
52
+
53
+ Este plugin comprime la misma información en una sola línea de **46 caracteres** y cambia su punto de destino — ya no va al flujo de mensajes:
54
+
55
+ ```
56
+ Current date: 2026-09-08 Asia/Shanghai Tuesday
57
+ ```
58
+
59
+ | Dimensión | `dsh-time-context` | `dsh-date-wrapper` |
60
+ |-----------|--------------------|--------------------|
61
+ | Texto inyectado | ~280 caracteres | 46 caracteres (↓84 %), ~12 tokens |
62
+ | Punto de destino | Un mensaje por cada pre-step (`user/message`) | La instantánea del contexto de ejecución de la plataforma (`systemPrompt.context`) |
63
+ | Frecuencia | Un evento por cada step elegible | Se reenvía con la instantánea solo cuando el texto cambia (0 eventos dentro de un mismo día) |
64
+ | Dependencia | Servicio `agents` | Servicio `systemPrompt` |
65
+ | Dependencias de ejecución | — | ninguna |
66
+
67
+ ## Compatibilidad de versiones
68
+
69
+ | Punto | Veredicto |
70
+ |------|---------|
71
+ | Versiones de DSH objetivo | 0.1.0-rc.7 → 0.1.7.x (línea 0.1.x — artefacto ≤ 0.2.0) y 0.2.0-rc.1 → 0.2.0.x (línea 0.2.0, `engines.dsh: >=0.2.0-rc.1 <0.2.1-0` — artefacto ≥ 0.3.0) |
72
+ | API de settings | **No aplica**: el plugin no registra ajustes ni exporta un `Config` de schemastery |
73
+ | Puntos de contrato usados | Exactamente uno — `systemPrompt.context()` |
74
+ | Conflicto con una función nativa | Solapa con `@deepseek-ai/dsh-time-context`; **no usar ambos**. No instalado por defecto = desactivado por defecto |
75
+ | Mitad de navegador | **Ninguna**: sin slot, sin DOM, sin tokens semánticos de CSS |
76
+ | Imports de paquetes de DSH | **Cero**: nada de `@deepseek-ai/*`, lo cual es más estricto que el patrón «detección en tiempo de ejecución + doble API de reserva» |
77
+
78
+ | Punto de contrato | 0.1.0-rc.7 | 0.1.1-rc.2 | 0.1.2-rc.1 | 0.1.3-alpha.2 | 0.1.7-rc.2 | 0.2.0-rc.1 |
79
+ |---|---|---|---|---|---|---|
80
+ | `systemPrompt.context(ctx): () => void` | sí | sí (verificado en este host) | sí | sí | sí | sí (diff frente a 0.1.7-rc.2: solo la cadena de versión) |
81
+ | `PromptContext = { name, order, text }`, sin campo `complete` | sí | sí | sí | sí | sí | sí |
82
+ | `includeRuntimeContext` / `suppressRuntimeContext` | sí | sí | sí | sí | sí | sí |
83
+ | deduplicación por texto del `project()` de agent-loop y `surfaceOp: "append"` | sí | sí | sí | no comparado | sí | sí |
84
+ | `order: 116` sin colisiones (110 / 115 / 120 ocupados) | sí | sí | sí | sí | sí | sí |
85
+
86
+ > Método: `npm pack @deepseek-ai/dsh-system-prompt@<version>`, descomprimir y comparar `lib/types/index.d.ts` y `lib/index.js`; lo mismo para `@deepseek-ai/dsh-agent-loop`.
87
+ > Entre `dsh-v0.1.7-rc.2` y `dsh-v0.2.0-rc.1`, el diff de `packages/core/system-prompt` es una única línea de versión, los puntos de llamada `systemPrompt.context` en 110/115/120 no cambian y la guía de migración de plugins no tiene ninguna entrada de `systemPrompt`.
88
+ > Solo se ha verificado **en ejecución** la 0.1.1-rc.2 en este host; la prueba de humo en ejecución de 0.2.0-rc.1 está registrada en `HANDOVER.md` §7.
89
+
90
+ ## Por qué una instantánea del contexto de ejecución en lugar de un mensaje
91
+
92
+ El primer intento copiaba a `dsh-time-context` y añadía un `user/message` en `agent/pre-step`. El coste medido era demasiado alto: cada evento JSONL pesa **339 bytes** (el texto ocupa solo 46, porque `content` y `sections` guardan cada uno una copia) y escribía **uno en cada turno**.
93
+
94
+ Al registrar en su lugar un contexto de ejecución, la fecha se pliega en el mensaje de instantánea que la plataforma ya envía:
95
+
96
+ - La plataforma **deduplica las instantáneas por texto** (`RuntimeContextProjection.project()` en `dsh-agent-loop`: `if (this.retained?.text === snapshot) return`), así que mientras la fecha no cambie **no se escribe ni un solo evento extra**;
97
+ - Las instantáneas **añaden** un mensaje nuevo (`surfaceOp: 'append'`) en lugar de reescribir en el sitio, así que la secuencia de la petición solo crece → **la caché de prefijo se conserva**;
98
+ - Nuestro coste marginal son esos 46 bytes, y solo cuando la instantánea se reenvía porque su texto cambió.
99
+
100
+ Medido en este host (una sesión real, 10 turnos / 231 steps):
101
+
102
+ | Punto | Medido |
103
+ |------|----------|
104
+ | Instantáneas de contexto de ejecución de la plataforma | 2 eventos, 1133 B cada uno, 2,3 KB en total |
105
+ | Mensajes reales del usuario | 10 eventos, 396 B cada uno |
106
+ | Enfoque antiguo (un mensaje por turno) | 10 × 339 B ≈ 3,4 KB |
107
+ | Este enfoque | 0 eventos extra; ~46 B plegados en una instantánea existente |
108
+
109
+ ## Configuración
110
+
111
+ Se distribuye con `cordis.patch.yml`; reiniciar tras cambiarlo:
112
+
113
+ ```yaml
114
+ - insert:
115
+ - id: date-wrapper
116
+ name: dsh-date-wrapper
117
+ config:
118
+ timeZone: Asia/Shanghai # IANA zone; omit to use the process zone
119
+ ```
120
+
121
+ - Un `timeZone` no válido lanza una excepción al arrancar (**ningún** retroceso silencioso a UTC).
122
+ - El nombre de zona en el texto es el nombre IANA resuelto (el nombre de la zona del proceso cuando se omite `timeZone`).
123
+ - La entrada del contexto de ejecución se llama `date-wrapper:date` con order `116` (ya ocupados: 110 sandbox, 115 approval, 120 subagent).
124
+ - El plugin **no exporta ningún `Config` de schemastery**, así que su configuración se salta la validación de esquema del host; todo se valida a mano en `validateConfig()`. Por eso mismo la página Settings → Plugins no tiene un formulario de configuración para él.
125
+
126
+ ## Activar/desactivar: la activación del plugin es el interruptor, no hay toggle en el panel
127
+
128
+ El plugin no incluye **ni** un toggle en el panel de ajustes **ni** un campo de configuración `enabled`, porque:
129
+
130
+ - El interruptor de la función *es* si la fila del plugin está activa. Inactiva → `apply()` nunca se ejecuta → la entrada del contexto de ejecución no existe → no se inyecta ni un carácter.
131
+ - No hay mitad de navegador (`dsh.client`), así que la UI no posee ningún widget nuestro.
132
+ - La página **Settings → Plugins** integrada en DSH ya muestra cada entrada como `enabled / disabled` (solo lectura).
133
+
134
+ ### Cómo desactivarlo
135
+
136
+ Sobrescríbelo por `id` en **tu propia** capa de parche de perfil — `C:\Users\<you>\.dsh\profiles\web\cordis.patch.yml`:
137
+
138
+ ```yaml
139
+ - id: date-wrapper
140
+ disabled: true # disabled; set back to false to restore
141
+ ```
142
+
143
+ - **En caliente, sin reinicio**: ese archivo está vigilado por el HMR de Cordis, y `disabled: true` dispone directamente la fiber de la fila.
144
+ - Si la fila `date-wrapper` aún no existe (no instalado), este parche solo registra una advertencia `entry "date-wrapper" not found`; el arranque sigue siendo exitoso.
145
+ - ⚠️ El archivo debe ser un **array YAML de nivel superior**; si está mal formado, **el arranque falla** (DSH es fail-loud con las capas de parche de usuario).
146
+
147
+ ### Cómo eliminarlo por completo
148
+
149
+ ```bash
150
+ dsh plugin --profile web remove dsh-date-wrapper
151
+ ```
152
+
153
+ La eliminación pasa por la capa de bundle y **requiere reiniciar** dsh web (los parches de bundle no se recargan en caliente).
154
+
155
+ ## Instalación
156
+
157
+ ```bash
158
+ dsh plugin --profile web add github:drscrewdriver/dsh-date-wrapper
159
+ ```
160
+
161
+ Reinicia dsh web y recarga la página. Rutas locales, modo link y resolución de problemas: [INSTALL.es.md](./INSTALL.es.md).
162
+
163
+ ## Verificación
164
+
165
+ | # | Cómo | Esperado |
166
+ |---|-----|----------|
167
+ | A1 | Abrir una sesión nueva y enviar un mensaje | La instantánea del contexto de ejecución contiene `Current date: YYYY-MM-DD <zone> <weekday>` (se muestra como una fila de contexto inyectado procedente de `system-prompt`) |
168
+ | A2 | Comprobar esa línea | ≤50 caracteres (46 medidos; el umbral PRD de 30 se relajó por el formato solicitado) |
169
+ | A3 | Desactivar el plugin (parche de perfil `disabled: true`) | La línea deja de aparecer en las instantáneas de las sesiones posteriores |
170
+ | A4 | Buscar en el registro de la sesión | No hay `Time sampled` / `Elapsed since` / `Browser time zone` |
171
+ | A5 | Poner `timeZone` en `UTC` y reiniciar | La fecha sigue UTC (puede variar un día al cruzar un límite de zona) |
172
+
173
+ ## Notas de implementación
174
+
175
+ ```
176
+ dsh-date-wrapper/
177
+ ├── package.json # name / type: module / main / exports["."] / dsh.bundle.patch / files
178
+ ├── cordis.patch.yml # one insert row (no patch-level id → lands at the profile root = host plane)
179
+ ├── src/
180
+ │ ├── format.js # pure functions: resolveZone / renderDate / createDateContextText / validateConfig / TEXT_LABEL
181
+ │ └── index.js # apply(ctx, config) → ctx.inject(['systemPrompt'], …) → systemPrompt.context(...)
182
+ └── tests/
183
+ ├── format.test.mjs # 11 cases (zone projection, weekday, format and length, degradation, config validation)
184
+ └── context.test.mjs # 7 cases (registration contract against a fake ctx)
185
+ ```
186
+
187
+ - **Fila en el plano del host**: `ctx.inject(['systemPrompt'], …)` abre una fiber hija; si el servicio falta, el plugin no registra nada en silencio en lugar de hacer fallar todo el arranque.
188
+ - **Proveedor de texto fail-soft**: una excepción durante el ensamblado del prompt haría fallar **cada** petición, así que un fallo de renderizado devuelve una cadena vacía (la plataforma filtra el texto vacío).
189
+ - **Sin `complete`**: ponerlo ensombrecería todo el system prompt.
190
+ - **La deduplicación es asunto de la plataforma**: no se mantiene ningún estado por agente; al pasar la medianoche la instantánea simplemente lleva la nueva fecha.
191
+ - **Ciclo de vida**: el registro pertenece a la fiber hija de `ctx.inject` y se reclama cuando el plugin se desactiva.
192
+
193
+ ## Desarrollo: TDD + lint
194
+
195
+ ```bash
196
+ npm install # devDependencies only (eslint / @eslint/js); zero runtime dependencies
197
+
198
+ npm run tdd # watch mode: rerun on src/ or tests/ changes (node --test --watch)
199
+ npm test # one full run: node --test "tests/*.test.mjs"
200
+ node tests/format.test.mjs # run a single file (most reliable under a sandbox: no child process)
201
+
202
+ npm run lint # eslint . (src + tests + eslint.config.mjs)
203
+ npm run lint:fix # auto-fix what can be fixed
204
+ npm run verify # lint + test; run this before committing
205
+ ```
206
+
207
+ ### Rojo-verde-refactorización
208
+
209
+ Los casos de prueba se corresponden directamente con los criterios de aceptación: primero se escribe una aserción que falla y luego se hace que pase.
210
+
211
+ | Paso | Acción | Comando |
212
+ |------|--------|---------|
213
+ | 1 rojo | Añadir en `tests/*.test.mjs` una aserción nombrada según el criterio de aceptación, que afirme un comportamiento que todavía **no** tienes | `npm run tdd` |
214
+ | 2 verde | Escribir en `src/` la implementación mínima para hacerla pasar, sin tocar otras aserciones | `npm run tdd` |
215
+ | 3 refactor | Renombrar y extraer funciones puras manteniéndose en verde; `src/format.js` concentra toda la lógica pura, `src/index.js` solo registra | `npm run tdd` |
216
+ | 4 puerta | Ejecutar lint + la suite completa antes de hacer commit | `npm run verify` |
217
+
218
+ Hoy hay 18 aserciones: `format.test.mjs` (11) cubre las funciones puras, `context.test.mjs` (7) afirma el contrato de registro contra un ctx falso.
219
+
220
+ ### Aspectos destacados de la configuración de lint
221
+
222
+ - ESLint 10 flat config (`eslint.config.mjs`) con `@eslint/js` recommended como línea base.
223
+ - Reglas endurecidas: `eqeqeq`, `prefer-const`, `object-shorthand`, `no-unused-vars` (prefijo `_` exento).
224
+ - Los globales de Node `crypto` / `console` / `process` se declaran explícitamente; de lo contrario `no-undef` da falsos positivos.
225
+
226
+ ## Limitaciones conocidas
227
+
228
+ - **Inactivo con presets de prompt fijo**: si la persona de un preset pone `includeRuntimeContext: false` (lo hacen el `minimal` oficial y el `simple-reply` local), `assemble()` devuelve `contexts: []` y la entrada de este plugin se descarta por completo. Esos presets están diseñados para prohibir que listeners posteriores añadan cualquier cosa al prompt.
229
+ - **Las instantáneas antiguas se quedan en el historial**: cuando cambia la fecha, la plataforma añade una instantánea nueva (la antigua se conserva) y la nueva surte efecto mediante su propia declaración "This snapshot supersedes earlier runtime-context snapshots" — igual que la plataforma gestiona los cambios de cwd / sandbox / política de aprobación.
230
+ - **Los parches de bundle no se recargan en caliente**: cambiar `cordis.patch.yml` o actualizar el plugin exige reiniciar dsh web (cambiar `disabled` en el parche de perfil sí es en caliente).
231
+ - **`dsh-time-context` no se carga ni se filtra**: si lo montas explícitamente en un preset, su texto verboso aparece como siempre. No usar ambos.
232
+ - **Sin sonda en tiempo de ejecución para el punto de contrato**: `systemPrompt.context` se llama sin protección, así que un futuro cambio de nombre por parte de DSH se manifestaría como un fallo de carga del plugin en lugar de una degradación silenciosa (ver `HANDOVER.md` §7).
233
+
234
+ ## Licencia
235
+
236
+ MIT