dsh-diagnostic-tutor 0.1.0 → 0.2.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/README.md +316 -79
- package/lib/api.js +364 -78
- package/lib/api.js.map +1 -1
- package/lib/block-types.js +25 -0
- package/lib/block-types.js.map +1 -0
- package/lib/client.js +9952 -821
- package/lib/client.js.map +1 -1
- package/lib/diagnostics.js +170 -0
- package/lib/diagnostics.js.map +1 -0
- package/lib/execution-key.js +91 -0
- package/lib/execution-key.js.map +1 -0
- package/lib/handoff.js +44 -1
- package/lib/handoff.js.map +1 -1
- package/lib/index.js +87 -51
- package/lib/index.js.map +1 -1
- package/lib/insights.js +82 -0
- package/lib/insights.js.map +1 -0
- package/lib/interactions.js +70 -0
- package/lib/interactions.js.map +1 -0
- package/lib/journal.js +122 -0
- package/lib/journal.js.map +1 -0
- package/lib/lesson-markdown.js +72 -0
- package/lib/lesson-markdown.js.map +1 -0
- package/lib/lesson.js +152 -3
- package/lib/lesson.js.map +1 -1
- package/lib/math.js +126 -0
- package/lib/math.js.map +1 -0
- package/lib/path-api.js +140 -0
- package/lib/path-api.js.map +1 -0
- package/lib/path-runtime.js +361 -0
- package/lib/path-runtime.js.map +1 -0
- package/lib/path-tools.js +107 -0
- package/lib/path-tools.js.map +1 -0
- package/lib/path.js +179 -0
- package/lib/path.js.map +1 -0
- package/lib/plan-tools.js +89 -0
- package/lib/plan-tools.js.map +1 -0
- package/lib/plan.js +82 -0
- package/lib/plan.js.map +1 -0
- package/lib/project-session.js +48 -0
- package/lib/project-session.js.map +1 -0
- package/lib/prompt.js +4 -19
- package/lib/prompt.js.map +1 -1
- package/lib/review-sync.js +51 -0
- package/lib/review-sync.js.map +1 -0
- package/lib/review-tools.js +115 -0
- package/lib/review-tools.js.map +1 -0
- package/lib/review.js +103 -0
- package/lib/review.js.map +1 -0
- package/lib/source-schema.js +24 -0
- package/lib/source-schema.js.map +1 -0
- package/lib/source-tools.js +61 -0
- package/lib/source-tools.js.map +1 -0
- package/lib/sources.js +78 -0
- package/lib/sources.js.map +1 -0
- package/lib/state.js +353 -47
- package/lib/state.js.map +1 -1
- package/lib/tools.js +620 -123
- package/lib/tools.js.map +1 -1
- package/lib/types/api.d.ts +10 -3
- package/lib/types/api.d.ts.map +1 -1
- package/lib/types/block-types.d.ts +12 -0
- package/lib/types/block-types.d.ts.map +1 -0
- package/lib/types/contract.d.ts +81 -7
- package/lib/types/contract.d.ts.map +1 -1
- package/lib/types/diagnostics.d.ts +95 -0
- package/lib/types/diagnostics.d.ts.map +1 -0
- package/lib/types/execution-key.d.ts +59 -0
- package/lib/types/execution-key.d.ts.map +1 -0
- package/lib/types/handoff.d.ts +2 -2
- package/lib/types/handoff.d.ts.map +1 -1
- package/lib/types/index.d.ts +19 -14
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/insights.d.ts +57 -0
- package/lib/types/insights.d.ts.map +1 -0
- package/lib/types/interactions.d.ts +23 -0
- package/lib/types/interactions.d.ts.map +1 -0
- package/lib/types/journal.d.ts +57 -0
- package/lib/types/journal.d.ts.map +1 -0
- package/lib/types/lesson-markdown.d.ts +11 -0
- package/lib/types/lesson-markdown.d.ts.map +1 -0
- package/lib/types/lesson.d.ts +697 -5
- package/lib/types/lesson.d.ts.map +1 -1
- package/lib/types/math.d.ts +50 -0
- package/lib/types/math.d.ts.map +1 -0
- package/lib/types/path-api.d.ts +5 -0
- package/lib/types/path-api.d.ts.map +1 -0
- package/lib/types/path-runtime.d.ts +1558 -0
- package/lib/types/path-runtime.d.ts.map +1 -0
- package/lib/types/path-tools.d.ts +4 -0
- package/lib/types/path-tools.d.ts.map +1 -0
- package/lib/types/path.d.ts +744 -0
- package/lib/types/path.d.ts.map +1 -0
- package/lib/types/plan-tools.d.ts +4 -0
- package/lib/types/plan-tools.d.ts.map +1 -0
- package/lib/types/plan.d.ts +53 -0
- package/lib/types/plan.d.ts.map +1 -0
- package/lib/types/project-session.d.ts +33 -0
- package/lib/types/project-session.d.ts.map +1 -0
- package/lib/types/prompt.d.ts.map +1 -1
- package/lib/types/review-sync.d.ts +17 -0
- package/lib/types/review-sync.d.ts.map +1 -0
- package/lib/types/review-tools.d.ts +4 -0
- package/lib/types/review-tools.d.ts.map +1 -0
- package/lib/types/review.d.ts +65 -0
- package/lib/types/review.d.ts.map +1 -0
- package/lib/types/source-schema.d.ts +31 -0
- package/lib/types/source-schema.d.ts.map +1 -0
- package/lib/types/source-tools.d.ts +4 -0
- package/lib/types/source-tools.d.ts.map +1 -0
- package/lib/types/sources.d.ts +33 -0
- package/lib/types/sources.d.ts.map +1 -0
- package/lib/types/state.d.ts +607 -23
- package/lib/types/state.d.ts.map +1 -1
- package/lib/types/tools.d.ts +3 -1
- package/lib/types/tools.d.ts.map +1 -1
- package/lib/types/udt.d.ts +20 -24
- package/lib/types/udt.d.ts.map +1 -1
- package/lib/types/version.d.ts +11 -0
- package/lib/types/version.d.ts.map +1 -0
- package/lib/types/vocabulary.d.ts +2 -2
- package/lib/udt.js +72 -19
- package/lib/udt.js.map +1 -1
- package/lib/version.js +26 -0
- package/lib/version.js.map +1 -0
- package/package.json +27 -22
package/README.md
CHANGED
|
@@ -11,19 +11,35 @@ learn interactively, and move to the next best step.
|
|
|
11
11
|
|
|
12
12
|
## Status
|
|
13
13
|
|
|
14
|
-
`v0.
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
`v0.2.0` is the published release. It includes the learning-path flow — saved
|
|
15
|
+
goal and time budget, evidence-referenced initial assessment, versioned course
|
|
16
|
+
routes with per-lesson Overviews, session-scoped attempts, and the
|
|
17
|
+
English/Simplified-Chinese interface — plus the content/input/review loop:
|
|
18
|
+
|
|
19
|
+
- typeset math, formula/code/quiz/flashcard/resource/comparison blocks;
|
|
20
|
+
- learner sources with `sourceRefs` citations;
|
|
21
|
+
- Today plan with ICS export and learner-controlled completion;
|
|
22
|
+
- spaced review with automatic flashcard cards, four ratings and undo;
|
|
23
|
+
- read-only Insights.
|
|
24
|
+
|
|
25
|
+
Install it with
|
|
26
|
+
`dsh plugin --profile web add dsh-diagnostic-tutor@0.2.0` — no clone, no
|
|
27
|
+
`pnpm install`, no build step. A checkout can be installed instead (see
|
|
28
|
+
[Installing a checkout](#installing-a-checkout-instead-of-the-package)).
|
|
17
29
|
|
|
18
30
|
The learning loop works end to end: a real goal in your own words, a diagnosis
|
|
19
31
|
map grown one node at a time, a tutor-written lesson, a check answered in the
|
|
20
32
|
chat, recorded evidence, and a next step the tutor chose — which the learner
|
|
21
|
-
presses Continue to take.
|
|
33
|
+
presses Continue to take. The route flow adds a full plan the learner reviews
|
|
34
|
+
and accepts, lesson by lesson; the content loop adds math, sources, Today and
|
|
35
|
+
review on top of it.
|
|
22
36
|
|
|
23
37
|
It is also early. The [limitations](#known-limitations) are listed rather than
|
|
24
38
|
left to be discovered, and nothing below claims a capability that is not there:
|
|
25
|
-
no PDF
|
|
26
|
-
|
|
39
|
+
no PDF extraction, no RAG, no automatic external resource fetching, no analytics.
|
|
40
|
+
Review scheduling is a local interval heuristic, not FSRS. Course routes are
|
|
41
|
+
proposed by the teaching model, never generated by the runtime, and a route never
|
|
42
|
+
marks knowledge as mastered.
|
|
27
43
|
|
|
28
44
|
## Two halves, one system
|
|
29
45
|
|
|
@@ -119,20 +135,19 @@ see is a *diagnosis map*, not a syllabus.
|
|
|
119
135
|
|
|
120
136
|
Stated plainly, because each one is a real edge a user can reach.
|
|
121
137
|
|
|
122
|
-
**Math is
|
|
123
|
-
inline, `\[...\]` display — and the
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
appear, check the skill first.
|
|
138
|
+
**Math is rendered as MathML on the host.** The tutor writes LaTeX by convention
|
|
139
|
+
— `\(...\)` inline, `\[...\]` display — and the host pre-renders it with
|
|
140
|
+
KaTeX's MathML output before storing the block. The browser renders native
|
|
141
|
+
MathML through a tag/attribute whitelist; no font or CSS is fetched. Invalid
|
|
142
|
+
TeX inside a text block falls back to the old styled source; an invalid
|
|
143
|
+
`formula` block is refused at write time. Browser MathML quality varies, and
|
|
144
|
+
this build ships no KaTeX CSS/fonts.
|
|
145
|
+
|
|
146
|
+
**Skill presence requires a live Agent.** The runtime checks the open
|
|
147
|
+
conversation's Agent scope on each overview, including its workspace. Before an
|
|
148
|
+
Agent exists, or while discovery is empty or incomplete, it reports `cannot tell`
|
|
149
|
+
and the panel stays quiet. Polling picks up later registration and Agent changes.
|
|
150
|
+
If lessons never appear, check the skill first.
|
|
136
151
|
|
|
137
152
|
**Storage is one document.** Every record lives in
|
|
138
153
|
`<dsh-home>/storages/udt.json`. It is readable, copyable and easy to export, and
|
|
@@ -154,8 +169,12 @@ matrix as load-bearing, not decoration.
|
|
|
154
169
|
|
|
155
170
|
| This plugin | Verified against DSH | Node |
|
|
156
171
|
| --- | --- | --- |
|
|
172
|
+
| `0.2.0` | `0.2.1-rc.3`, `0.2.1-rc.2`, `0.2.0-rc.2` (test suite also on `0.1.7-alpha.2`) | `^22.19.0 \|\| >=24.0.0` |
|
|
173
|
+
| `0.1.1` | `0.2.1-rc.3`, `0.2.1-rc.2`, `0.2.0-rc.2` (test suite also on `0.1.7-alpha.2`) | `^22.19.0 \|\| >=24.0.0` |
|
|
157
174
|
| `0.1.0` | `0.1.7-alpha.2` (also composed under `0.1.5-rc.1`) | `^22.19.0 \|\| >=24.0.0` |
|
|
158
175
|
|
|
176
|
+
Phase 1–6 的本机 boot 矩阵(2026-10-29)覆盖 `0.2.1-rc.3`、`0.2.1-rc.2`、`0.2.0-rc.2`、`0.1.7-alpha.2` 的插件加载、本地路由、domain v1 与客户端启动;其中 rc3/alpha.2 还通过了 tarball 安装,并用真实模型完成 lesson → Review 评分/撤销 → Insights;rc2/rc20 的完整真实会话仍是补充项。详见 [`docs/planning/COMPATIBILITY.md`](docs/planning/COMPATIBILITY.md)。
|
|
177
|
+
|
|
159
178
|
Rules this repository enforces mechanically:
|
|
160
179
|
|
|
161
180
|
- **Named exports only.** A `export default` plugin makes Cordis' loader prefer
|
|
@@ -165,7 +184,42 @@ Rules this repository enforces mechanically:
|
|
|
165
184
|
second runtime copy can be resolved. Enforced by `verbatimModuleSyntax`, and
|
|
166
185
|
verified on the built artifact (it contains zero imports).
|
|
167
186
|
- **No `instanceof` across package boundaries.** Take runtime objects from `ctx`.
|
|
168
|
-
- Peer ranges
|
|
187
|
+
- Peer ranges name the release lines this plugin was verified on. DSH ships
|
|
188
|
+
prereleases, and npm's semver only admits a prerelease when its line is named
|
|
189
|
+
in the range — so the compatibility matrix above, not the range floor, is the
|
|
190
|
+
contract. Dev dependencies pin one exact cohort for the type baseline.
|
|
191
|
+
|
|
192
|
+
## UI baseline for redesign
|
|
193
|
+
|
|
194
|
+
The current surfaces, state model and screenshots (generated from fixtures) live in
|
|
195
|
+
[`docs/design/ui-baseline/index.html`](docs/design/ui-baseline/index.html) with a
|
|
196
|
+
detailed handoff in
|
|
197
|
+
[`docs/design/ui-baseline/README.md`](docs/design/ui-baseline/README.md). It covers
|
|
198
|
+
the system structure, Learning map / Current lesson surfaces, every route-page state
|
|
199
|
+
and the design constraints a new UI must keep.
|
|
200
|
+
|
|
201
|
+
## Diagnostics
|
|
202
|
+
|
|
203
|
+
Monitoring is local and content-free: no chat, goal, lesson or evidence text is
|
|
204
|
+
recorded, and nothing is sent anywhere.
|
|
205
|
+
|
|
206
|
+
```sh
|
|
207
|
+
pnpm doctor # human-readable health report
|
|
208
|
+
node scripts/doctor.mjs --json # machine-readable
|
|
209
|
+
node scripts/doctor.mjs --strict # exit 1 if an error-severity issue exists
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
It reads `<dsh-home>/storages/udt.json` plus the event journal and reports each
|
|
213
|
+
project's stage, pending/failed tutor requests (with age), stalled attempts and
|
|
214
|
+
route-length conflicts. The same report is available at
|
|
215
|
+
`GET /diagnostic-tutor/api/diagnostics` (loopback-only, like every plugin
|
|
216
|
+
route).
|
|
217
|
+
|
|
218
|
+
The host journal lives at `<dsh-home>/logs/diagnostic-tutor-events.jsonl`
|
|
219
|
+
(`DSH_DIAGNOSTIC_TUTOR_EVENTS` overrides the path, `off` disables it). It logs
|
|
220
|
+
lifecycle events only: tool names and operations, durations, request outcomes,
|
|
221
|
+
provider error messages (truncated). A single file is capped at 1 MB and
|
|
222
|
+
rotated once.
|
|
169
223
|
|
|
170
224
|
---
|
|
171
225
|
|
|
@@ -175,7 +229,7 @@ Rules this repository enforces mechanically:
|
|
|
175
229
|
|
|
176
230
|
| | |
|
|
177
231
|
| --- | --- |
|
|
178
|
-
| [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) | verified against `0.1
|
|
232
|
+
| [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) | verified against `0.2.1-rc.3` |
|
|
179
233
|
| the [Universal Diagnostic Tutor](https://github.com/SenmuuuuW/universal-diagnostic-tutor-skill) skill | the teaching brain. Without it the runtime loads, records and displays state, but **no lesson is ever written** |
|
|
180
234
|
| Node | `^22.19.0 \|\| >=24.0.0` |
|
|
181
235
|
|
|
@@ -227,7 +281,7 @@ learn?*.
|
|
|
227
281
|
|
|
228
282
|
```sh
|
|
229
283
|
npm pack
|
|
230
|
-
dsh plugin --profile web add "$PWD/dsh-diagnostic-tutor-0.
|
|
284
|
+
dsh plugin --profile web add "$PWD/dsh-diagnostic-tutor-0.2.0.tgz"
|
|
231
285
|
```
|
|
232
286
|
|
|
233
287
|
**What gets installed is prebuilt.** The package ships `lib/` — the host half
|
|
@@ -258,10 +312,30 @@ With the json backend you get exactly one document, `<storage root>/udt.json`
|
|
|
258
312
|
|
|
259
313
|
| Slot | Holds |
|
|
260
314
|
| --- | --- |
|
|
261
|
-
| `global` | the learner singleton — preferences,
|
|
315
|
+
| `global` | the learner singleton — preferences, `initializedAt`, and the legacy shared `activeCourseId` marker |
|
|
262
316
|
| `tables.courses` | one record per learning goal, in the learner's own words |
|
|
263
317
|
| `tables.nodes` | the diagnosis map: `id`, `courseId`, `title`, `parentId?`, `relation`, `state`, `evidence[]` |
|
|
264
318
|
|
|
319
|
+
Courses, maps, lessons and evidence are shared by the learner's conversations.
|
|
320
|
+
Project selection, focus, next step and handoff belong to one conversation,
|
|
321
|
+
identified by its session ID. The legacy `activeCourseId` remains in storage for
|
|
322
|
+
compatibility; it does not select a project for a conversation. A fresh
|
|
323
|
+
conversation starts with the library, even when historical projects exist.
|
|
324
|
+
|
|
325
|
+
| Conversation selection | Project binding |
|
|
326
|
+
| --- | --- |
|
|
327
|
+
| `none` | A new goal, or a validated write explicitly naming a course or node in this conversation, may select its project. Reading shared data does not select it. |
|
|
328
|
+
| `new` | The learner pressed **Start something new**. Only a new goal created here may bind; historical map or lesson writes leave this choice intact. |
|
|
329
|
+
| `project` | The selected course is the default for tools. Creating another goal does not replace it; writes to another course require selecting that course first. |
|
|
330
|
+
|
|
331
|
+
`udt_status` reports this conversation's `selection` and, only for `project`,
|
|
332
|
+
its `courseId`. Goal, map and lesson writes return `projectAdopted` and
|
|
333
|
+
`projectBinding`: `adopted`, `already-selected`, `not-selected`, or
|
|
334
|
+
`missing-session-id`. A choice made while a write is pending takes precedence
|
|
335
|
+
over that write's adoption. Without a reliable session ID, status reports
|
|
336
|
+
`sessionAvailable: false`; explicit shared writes can still be stored, but their
|
|
337
|
+
result is `missing-session-id` and they claim no conversation binding.
|
|
338
|
+
|
|
265
339
|
Two deliberate rules:
|
|
266
340
|
|
|
267
341
|
- **zod is the contract.** Every record is validated at the durable boundary, so
|
|
@@ -294,16 +368,28 @@ route around them:
|
|
|
294
368
|
|
|
295
369
|
### Tools
|
|
296
370
|
|
|
297
|
-
|
|
371
|
+
Eighteen. None of them decides anything about teaching; they record or report.
|
|
298
372
|
|
|
299
373
|
| Tool | Does |
|
|
300
374
|
| --- | --- |
|
|
301
|
-
| `udt_status` | reports
|
|
302
|
-
| `udt_goal_create` | records a goal and plants the map root
|
|
303
|
-
| `udt_map_get` | reads the map with each node's relation, state and evidence |
|
|
375
|
+
| `udt_status` | reports shared goals and counts, plus this conversation's selection, focus and pending next step |
|
|
376
|
+
| `udt_goal_create` | records a goal and plants only the map root; may also select it for this conversation |
|
|
377
|
+
| `udt_map_get` | reads the selected project's map, or an explicitly named shared course, with each node's relation, state and evidence |
|
|
304
378
|
| `udt_map_update` | `add-nodes` · `set-state` · `add-evidence` |
|
|
305
|
-
| `udt_lesson_update` | writes teaching into the learning surface as blocks |
|
|
379
|
+
| `udt_lesson_update` | writes teaching into the learning surface as blocks; pre-renders math; attaches source citations |
|
|
380
|
+
| `udt_quiz_update` | records a quiz/flashcard submission; never grades and never changes node state |
|
|
306
381
|
| `udt_decide_next` | records where the learner should go next, and why |
|
|
382
|
+
| `udt_path_get` | reads the goal brief, assessment, routes and this conversation's active attempt |
|
|
383
|
+
| `udt_path_update` | saves a brief, assessment or route proposal |
|
|
384
|
+
| `udt_course_run_update` | binds a diagnosed node to an attempt, or completes with evidence |
|
|
385
|
+
| `udt_source_add` | stores pasted text or one explicitly named local `.md`/`.txt` file; deduplicates by SHA-256 |
|
|
386
|
+
| `udt_source_list` | lists source metadata without content |
|
|
387
|
+
| `udt_source_get` | returns one stored source for citation |
|
|
388
|
+
| `udt_plan_get` | reads the learner-owned Today plan |
|
|
389
|
+
| `udt_plan_update` | adds, completes or removes plan items; planning is policy, not teaching |
|
|
390
|
+
| `udt_review_add` | queues review cards; scheduling is policy, not mastery |
|
|
391
|
+
| `udt_review_next` | reads cards due now |
|
|
392
|
+
| `udt_review_grade` | records a review rating and reschedules; never confirms a node |
|
|
307
393
|
|
|
308
394
|
The division is the architecture: the tutor decides **what** to teach, when to
|
|
309
395
|
check, and what an answer showed; the runtime decides **what may be stored** and
|
|
@@ -377,36 +463,36 @@ the button means.
|
|
|
377
463
|
|
|
378
464
|
| Surface | Where | For |
|
|
379
465
|
| --- | --- | --- |
|
|
380
|
-
| **Learning
|
|
381
|
-
| **
|
|
382
|
-
|
|
383
|
-
The tab is the reason the loop is usable: the full panel fills the main column,
|
|
384
|
-
which is also where the conversation lives, so with only that panel answering a
|
|
385
|
-
check meant leaving the lesson. The right sidebar is a separate column.
|
|
466
|
+
| **Learning map** | the main column (`main`, key `diagnostic-tutor-map`) | browsing — course route, every lesson Overview, knowledge & evidence |
|
|
467
|
+
| **Current lesson** | the right sidebar tab, beside the chat | the lesson itself — teaching units, and checks answered in the chat |
|
|
386
468
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
469
|
+
One page for browsing, one surface for learning. The page never starts a lesson;
|
|
470
|
+
the tab never decides what to teach. Both read the same `useLearning` state and
|
|
471
|
+
the same path client, so they cannot disagree about what is focused, accepted or
|
|
472
|
+
written. **Open current lesson** on the map returns to the conversation with the
|
|
473
|
+
lesson tab open in the same step.
|
|
390
474
|
|
|
391
|
-
The
|
|
392
|
-
|
|
393
|
-
|
|
475
|
+
The container for the main column is the map page. A full-page lesson panel used
|
|
476
|
+
to exist as a second main destination; it was removed so the same lesson is not
|
|
477
|
+
offered in two places. The standalone full-page component is still used by the
|
|
478
|
+
preview and the tests, but the plugin does not register it.
|
|
394
479
|
|
|
395
480
|
### Teaching-brain detection
|
|
396
481
|
|
|
397
|
-
|
|
398
|
-
Universal Diagnostic Tutor skill is
|
|
399
|
-
|
|
400
|
-
|
|
482
|
+
Each overview asks the platform's own Agent and Skill registries whether the
|
|
483
|
+
Universal Diagnostic Tutor skill is available in the requesting session's live
|
|
484
|
+
Agent scope and cwd. No path is hardcoded, no skill root is assumed, and nothing
|
|
485
|
+
is copied. Missing services and incomplete discovery return an inconclusive
|
|
486
|
+
status; complete scoped discovery can establish presence or absence.
|
|
401
487
|
|
|
402
488
|
Compatibility is probed by **capability**, not by a version string: the skill's
|
|
403
489
|
maintenance contract permits only `name` and `description` in frontmatter, so
|
|
404
490
|
it cannot declare a version. The result carries a short content digest as a
|
|
405
491
|
version hint.
|
|
406
492
|
|
|
407
|
-
|
|
408
|
-
output. The
|
|
409
|
-
|
|
493
|
+
Paths, fingerprints and compatibility details stay **internal**, absent from
|
|
494
|
+
every tool output and overview. The overview exposes only availability. The skill
|
|
495
|
+
forbids naming its files, versions or repository in learner-facing text.
|
|
410
496
|
|
|
411
497
|
### How the two halves agree
|
|
412
498
|
|
|
@@ -422,20 +508,76 @@ which judged an answer is *not finished* until the next step is recorded — so
|
|
|
422
508
|
the bridge was **deleted rather than kept as a second voice**. The plugin got
|
|
423
509
|
better at it, which is the evidence it belonged upstream.
|
|
424
510
|
|
|
425
|
-
## The
|
|
426
|
-
|
|
427
|
-
The browser half registers
|
|
428
|
-
(`sidebar.panellist`, a `list`)
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
511
|
+
## The page and the lesson surface
|
|
512
|
+
|
|
513
|
+
The browser half registers three things: the **Learning map** icon in the
|
|
514
|
+
sidebar (`sidebar.panellist`, a `list`), the page it opens (`main`, a `keyed`
|
|
515
|
+
slot, key `diagnostic-tutor-map`), and the **Current lesson** tab in the right
|
|
516
|
+
sidebar (a tab type plus its body and title). The map's sidebar `id` and its
|
|
517
|
+
`main` key come from one constant — a drift between them would leave the icon
|
|
518
|
+
opening nothing.
|
|
519
|
+
|
|
520
|
+
The page has two tabs: **Course route** (the versioned plan, route canvas,
|
|
521
|
+
ordered lesson list and every Overview) and **Knowledge & evidence** (the
|
|
522
|
+
diagnosis map and its recorded observations). Neither one teaches: they show
|
|
523
|
+
stored state.
|
|
524
|
+
|
|
525
|
+
The route tab shows one stage at a time and one next step. A progress bar names
|
|
526
|
+
the four stages — goal & time, initial diagnosis, route, learning — and a
|
|
527
|
+
**Next step** card always answers "what now?" with a single primary action
|
|
528
|
+
(set the goal, request or answer the diagnosis, request the route, accept the
|
|
529
|
+
version, start the first lesson, continue the current one) plus at most one
|
|
530
|
+
real alternative. Competing action rows live in the folded history, not beside
|
|
531
|
+
it. Before a goal is saved the wizard owns the page; while the tutor is asking
|
|
532
|
+
diagnostic questions the card points back to the conversation, shows how long it
|
|
533
|
+
has waited, says whether the tutor is still thinking, and on a failed turn
|
|
534
|
+
retries with the current model and opens the chat (the failure is recorded on
|
|
535
|
+
the path request, so it survives a restart; provider errors are translated into
|
|
536
|
+
billing / auth / rate-limit / network / context advice); route versions
|
|
537
|
+
appear only after the diagnosis is complete or skipped; once accepted, the page
|
|
538
|
+
leads with the route canvas. A proposed or accepted route shows its total study
|
|
539
|
+
time, the weeks it takes at the saved weekly budget, and any lessons longer than
|
|
540
|
+
the preferred session. Next step moves to the first unfinished lesson after a
|
|
541
|
+
completion, and says when the whole route is done. Completed diagnosis details
|
|
542
|
+
and route settings stay reachable in folded sections.
|
|
543
|
+
|
|
544
|
+
The plugin's own accent is **DeepSeek's brand blue** — `#4d6bfe` on the light
|
|
545
|
+
canvas, `#6a84ff` on the dark one — used sparingly for progress, the active tab,
|
|
546
|
+
the current/selected item, focus rings and action links. Primary buttons keep the
|
|
547
|
+
host's near-black/white fill: the blue marks, it does not act. All other colours
|
|
548
|
+
continue to come from the host theme.
|
|
549
|
+
|
|
550
|
+
The initial diagnosis is asked through DSH's **interactive question card**
|
|
551
|
+
(`ask_user_question`) when the profile provides it: options are tapped rather
|
|
552
|
+
than typed, multi-select works, and a free-text question covers explanations.
|
|
553
|
+
The tutor still owns the questions, evidence and assessment; profiles without
|
|
554
|
+
that tool fall back to chat text.
|
|
555
|
+
|
|
556
|
+
The lesson lives in the docked tab, where the conversation is still on screen.
|
|
557
|
+
That is what makes the check loop work without leaving the lesson. The sidebar
|
|
558
|
+
action **Learn** opens that tab and never takes over the main column; the map's
|
|
559
|
+
**Open current lesson** button returns to the conversation and opens the same
|
|
560
|
+
tab.
|
|
433
561
|
|
|
434
562
|
```
|
|
435
|
-
|
|
563
|
+
sidebar Learn ─────────────▶ Current lesson (right sidebar)
|
|
564
|
+
sidebar Learning map ──────▶ Learning map (main column: route | knowledge)
|
|
565
|
+
|
|
566
|
+
Open current lesson ◀────── returns to chat + lesson tab
|
|
436
567
|
```
|
|
437
568
|
|
|
438
|
-
|
|
569
|
+
Entering learning follows the conversation, not the window. A project is
|
|
570
|
+
shared, but its focus and planned runs belong to a conversation, so the project
|
|
571
|
+
library and **Open current lesson** ask the host which conversation was last
|
|
572
|
+
learning the project (`GET /project/session`, read-only) and switch there with
|
|
573
|
+
the shell's session navigation when it differs. The order is: newest active
|
|
574
|
+
focus, then newest course run with an attempt, then newest project selection;
|
|
575
|
+
only a project no conversation has ever touched falls back to the conversation
|
|
576
|
+
on screen.
|
|
577
|
+
|
|
578
|
+
A full-page lesson panel used to exist as a second main destination. It was
|
|
579
|
+
removed: one page for browsing, one surface for learning, and the conversation
|
|
580
|
+
keeps the main column.
|
|
439
581
|
|
|
440
582
|
### Learning Blocks
|
|
441
583
|
|
|
@@ -445,25 +587,42 @@ keyed by `type`:
|
|
|
445
587
|
|
|
446
588
|
| Type | Content |
|
|
447
589
|
| --- | --- |
|
|
448
|
-
| `text` | `md` — markdown,
|
|
590
|
+
| `text` | `md` — markdown, plus host `math[]` fragments for typeset formulas |
|
|
449
591
|
| `example` | `title`, `steps[]`, `takeaway?` |
|
|
450
592
|
| `diagram` | `format` (`ascii` \| `mermaid`), `spec`, `caption?` |
|
|
451
593
|
| `check` | `prompt`, `expect?`, `hint?` — the stop-and-wait surface |
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
594
|
+
| `formula` | `tex`, `display?`, host-rendered `mathml`, `caption?` |
|
|
595
|
+
| `code` | `language?`, `source`, `caption?` — displayed, never executed |
|
|
596
|
+
| `quiz` | `title?`, `questions[]` (`mcq`/`short`); answers are recorded, never graded by the runtime |
|
|
597
|
+
| `flashcard` | `front`, `back`, `tags?`; self-report is recorded, not scheduled as mastery |
|
|
598
|
+
| `resource` | `title`, optional `sourceId`/`locator`/`url`/`note` |
|
|
599
|
+
| `comparison` | `columns[]`, `rows[][]`, `caption?` |
|
|
600
|
+
|
|
601
|
+
Blocks may also carry `sourceRefs: [{sourceId, locator}]`; every reference is
|
|
602
|
+
checked against the `sources` table at write time. The stored schema accepts an
|
|
603
|
+
**unknown type** from a newer host and the client renders a readable placeholder;
|
|
604
|
+
the current model tool still writes only known types.
|
|
605
|
+
The Learning map also shows a compact **Today** card when `udt_plan_update` has created open plan items, with a done action and an **Export calendar** action for dated items. DSH's own `schedule` is session-local, so this is an explicit calendar export rather than a push notification. Review cards and their local interval scheduler are stored but have no separate review surface yet.
|
|
457
606
|
|
|
458
607
|
### Browser API
|
|
459
608
|
|
|
460
|
-
|
|
609
|
+
The browser's core calls, at `/diagnostic-tutor/api` (the full route list is in
|
|
610
|
+
[the architecture notes](docs/planning/CURRENT-ARCHITECTURE.md)):
|
|
461
611
|
|
|
462
612
|
| Route | Returns |
|
|
463
613
|
| --- | --- |
|
|
464
614
|
| `GET /overview` | the current course and its whole map (`course: null` on a first run) |
|
|
465
615
|
| `GET /node?id=` | one node with its evidence, parent and children |
|
|
616
|
+
| `GET /project/session?courseId=` | the conversation that should resume a project (read-only) |
|
|
466
617
|
| `POST /lesson {nodeId}` | the prototype lesson, built once and reused after that |
|
|
618
|
+
| `GET /source?id=` | one stored source and its text, for a citation preview |
|
|
619
|
+
| `POST /path/answer` | record a quiz/flashcard submission on the active attempt |
|
|
620
|
+
| `GET /plan/ics` | download open, dated Today plan items as an ICS calendar |
|
|
621
|
+
| `GET /review` | due review cards (read-only) |
|
|
622
|
+
| `POST /review/grade` | record a review rating and reschedule |
|
|
623
|
+
| `POST /review/undo` | restore the previous schedule after the latest grade |
|
|
624
|
+
| `GET /insights?courseId=` | read-only course insight counts |
|
|
625
|
+
| `POST /plan/task` | mark a Today plan item done (learner action; plan only) |
|
|
467
626
|
|
|
468
627
|
Every request passes a **trust fence**: a bare `ctx.webServer.register()` route
|
|
469
628
|
inherits no authentication, so the route checks that the request arrived at a
|
|
@@ -471,6 +630,27 @@ loopback `Host`, from a loopback `Origin`, and is not marked cross-site. Anythin
|
|
|
471
630
|
else gets `403` and no body. The browser receives **views only** — no storage
|
|
472
631
|
path, no domain handle, no raw record.
|
|
473
632
|
|
|
633
|
+
### Interface language
|
|
634
|
+
|
|
635
|
+
Every plugin surface ships copy in **English and Simplified Chinese**: the
|
|
636
|
+
Learning map page, the course route, the docked Current-lesson tab, the
|
|
637
|
+
standalone full-page panel used by the preview and tests, and the shared
|
|
638
|
+
next-step card, handoff line and welcome row. The sidebar Learn action and its
|
|
639
|
+
missing-conversation explanation switch too. A toggle in the map header (and in
|
|
640
|
+
the lesson surfaces) changes the copy in place; the language is saved on the
|
|
641
|
+
learner profile (`preferredLanguage`, via `GET /preferences` and
|
|
642
|
+
`POST /preferences/language`), so it survives a restart and applies to every
|
|
643
|
+
conversation. The choice applies immediately and is saved in the background: a
|
|
644
|
+
failed request keeps the local choice rather than flipping the UI back.
|
|
645
|
+
|
|
646
|
+
It is copy, not content: the tutor's teaching, the learner's goal text, lesson
|
|
647
|
+
overviews, node titles and recorded evidence are never machine-translated, and
|
|
648
|
+
the model is not instructed to change language by the toggle. Three things stay
|
|
649
|
+
outside the switch: the **Learning map** sidebar label (registered once when the
|
|
650
|
+
plugin loads, so it cannot follow an in-place change), server/model error text
|
|
651
|
+
and dynamic handoff details as generated, and the development preview's own
|
|
652
|
+
toolbar.
|
|
653
|
+
|
|
474
654
|
## Preview
|
|
475
655
|
|
|
476
656
|
The panel takes its API as a prop, so the UI runs with no DSH and no agent:
|
|
@@ -492,18 +672,67 @@ Machine Learning [goal, unconfirmed]
|
|
|
492
672
|
└─ Python [prerequisite, unconfirmed]
|
|
493
673
|
```
|
|
494
674
|
|
|
675
|
+
Two query parameters reach the states that are otherwise awkward to produce on
|
|
676
|
+
demand — `?scenario=` (`first-use`, `map`, `handoff`, `next`, `stay`, `noskill`,
|
|
677
|
+
`long`, `scroll`, `library`, `new`, `showcase`, `curriculum`, `path-draft`,
|
|
678
|
+
`path-setup`, `path-assessment`) and `?surface=tab` / `?surface=map` for the
|
|
679
|
+
docked right-sidebar tab and the standalone map page. The scenario switch is in
|
|
680
|
+
the page's toolbar, and the theme toggle sets the same
|
|
681
|
+
`body[data-ds-dark-theme]` attribute DSH does. The page carries the real DSH
|
|
682
|
+
design tokens for the same reason: with hardcoded fallbacks in their place, a
|
|
683
|
+
token that resolves to near-white in dark mode looks perfectly fine locally.
|
|
684
|
+
|
|
495
685
|
To capture the panel from a *live* profile instead:
|
|
496
686
|
|
|
497
687
|
```sh
|
|
498
688
|
pnpm screenshot "<dsh-url-with-token>" preview/dsh-ui.png
|
|
499
689
|
```
|
|
500
690
|
|
|
691
|
+
### Auditing the presentation
|
|
692
|
+
|
|
693
|
+
```sh
|
|
694
|
+
pnpm preview # in one shell
|
|
695
|
+
pnpm audit:ui http://127.0.0.1:8391/?scenario=next # in another
|
|
696
|
+
pnpm audit:ui "<dsh-url-with-token>" /tmp/audit --surface=tab
|
|
697
|
+
pnpm check:scroll http://127.0.0.1:8391/ /tmp/udt-scroll-check
|
|
698
|
+
```
|
|
699
|
+
|
|
700
|
+
The scroll regression requires local Chrome and runs against the built bundle.
|
|
701
|
+
The preview uses a clipped, fixed-height slot like DSH, so the whole document
|
|
702
|
+
cannot scroll to mask an unscrollable plugin. The check sends real wheel events
|
|
703
|
+
and verifies bottom content remains visible in the tab, expanded tab, stacked
|
|
704
|
+
panel, all three wide-panel panes, and long library/welcome views, in both themes.
|
|
705
|
+
Only fixture data is used; no live learner state is changed. The ordinary visual
|
|
706
|
+
audit alone does **not** verify scrolling.
|
|
707
|
+
|
|
708
|
+
The visual audit measures, at each width this project targets and in both themes: the contrast
|
|
709
|
+
of every control's own text against its own fill, class names in the DOM that no
|
|
710
|
+
rule matches, how many controls carry primary weight in one view, tap targets
|
|
711
|
+
under 28px, type under 10.5px, and anything spilling or clipped. It prints a
|
|
712
|
+
table, writes `findings.json`, and saves screenshots — it does not fail the run,
|
|
713
|
+
so a human can disagree with it.
|
|
714
|
+
|
|
715
|
+
That is not a substitute for looking. It is what makes looking affordable: the
|
|
716
|
+
docked tab's invisible primary button and the dark-mode 1.05:1 controls were
|
|
717
|
+
both found by it, in a live profile, in seconds.
|
|
718
|
+
|
|
719
|
+
What the surfaces look like now, captured from a live profile:
|
|
720
|
+
|
|
721
|
+
| | light | dark |
|
|
722
|
+
| --- | --- | --- |
|
|
723
|
+
| standalone panel (preview/tests only) |  |  |
|
|
724
|
+
| docked tab |  |  |
|
|
725
|
+
|
|
726
|
+
These captures predate the current layout: the standalone full-page panel is no
|
|
727
|
+
longer a product surface. Today the Learning map page owns the main column and
|
|
728
|
+
the Current lesson tab holds the teaching.
|
|
729
|
+
|
|
501
730
|
## Development
|
|
502
731
|
|
|
503
732
|
```sh
|
|
504
733
|
pnpm install
|
|
505
734
|
pnpm typecheck # tsc --noEmit (host and client)
|
|
506
|
-
pnpm test #
|
|
735
|
+
pnpm test # unit, guard, DOM, render, real composition
|
|
507
736
|
pnpm build # tsc -> lib/ (host) + tsdown -> lib/client.js
|
|
508
737
|
```
|
|
509
738
|
|
|
@@ -546,8 +775,10 @@ shows a loader row exists.
|
|
|
546
775
|
| `v0.0.10` | export and delete your data |
|
|
547
776
|
| `v0.0.11` | storage layout tested; `single` kept, with the reason |
|
|
548
777
|
| `v0.1.0-rc.1` | packaged install, no clone or build required |
|
|
549
|
-
| `v0.1.0` |
|
|
550
|
-
| `v0.
|
|
778
|
+
| `v0.1.0` | first stable release, installable from npm |
|
|
779
|
+
| `v0.1.1` | packaged release with the docked learning surface |
|
|
780
|
+
| `v0.2.0` | **published** — Phase 1–6 content/input/review loop, learning-path flow, Today/Review/Insights |
|
|
781
|
+
| next | settings, automatic replan suggestions (the tutor already proposes revisions on request; no runtime auto-replan) |
|
|
551
782
|
|
|
552
783
|
## Trust
|
|
553
784
|
|
|
@@ -555,7 +786,10 @@ DSH does not sandbox plugin code: an installed plugin runs in-process with your
|
|
|
555
786
|
privileges, and a bare `ctx.webServer.register()` route inherits no
|
|
556
787
|
authentication. This plugin's commitments:
|
|
557
788
|
|
|
558
|
-
- reads and writes only its own storage domain
|
|
789
|
+
- reads and writes only its own storage domain, with one explicit exception:
|
|
790
|
+
`udt_source_add(path)` reads exactly one regular `.md`/`.markdown`/`.txt` file
|
|
791
|
+
the caller names, once, then stores its text in that domain. It never scans a
|
|
792
|
+
directory, follows a symlink, accepts a URL, or fetches anything;
|
|
559
793
|
- serves its browser half only from loopback-fenced routes it checks itself;
|
|
560
794
|
- makes no outbound network requests;
|
|
561
795
|
- writes no files outside the harness's own storage;
|
|
@@ -571,18 +805,19 @@ panel opens, the map and the lesson surface render, and `GET /export` and
|
|
|
571
805
|
`POST /reset` work. What does not happen is teaching — nothing writes a lesson,
|
|
572
806
|
because nothing is making teaching decisions.
|
|
573
807
|
|
|
574
|
-
**
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
808
|
+
**Availability is checked for the conversation you have open.** On every
|
|
809
|
+
`GET /overview?sessionId=…`, the plugin resolves that session's live Agent from
|
|
810
|
+
DSH's Agent registry and asks the Skill registry for a complete snapshot using
|
|
811
|
+
`{ scope: agent, cwd: agent.session.header.cwd }`. A root catalog containing
|
|
812
|
+
Office skills cannot establish whether that Agent has a tutor. Services are
|
|
813
|
+
resolved for each request, so delayed registration, removal and preset changes
|
|
814
|
+
are picked up by the panel's existing polling. The runtime reports three states:
|
|
580
815
|
|
|
581
816
|
| `teachingBrain` | meaning | what the panel does |
|
|
582
817
|
| --- | --- | --- |
|
|
583
|
-
| `true` | found | nothing |
|
|
584
|
-
| `false` |
|
|
585
|
-
| `null` |
|
|
818
|
+
| `true` | found and model-invocable in the live Agent scope | nothing |
|
|
819
|
+
| `false` | unavailable after complete scoped discovery with entries | says a tutor is needed |
|
|
820
|
+
| `null` | no live Agent, empty/incomplete discovery, or unsupported/unavailable service | says nothing |
|
|
586
821
|
|
|
587
822
|
An earlier revision treated "not in my catalog" as "not installed" and showed a
|
|
588
823
|
notice on that basis. In a web profile that was a confident, wrong answer — the
|
|
@@ -655,4 +890,6 @@ MIT. The Universal Diagnostic Tutor skill is a separate MIT project by the same
|
|
|
655
890
|
author and is not vendored here.
|
|
656
891
|
|
|
657
892
|
Planning and architecture research for this project live in
|
|
658
|
-
[`docs/planning/`](docs/planning/).
|
|
893
|
+
[`docs/planning/`](docs/planning/). Phase 1–6 merge order, acceptance checks and the DSH compatibility checklist are in
|
|
894
|
+
[`docs/planning/PHASE-1-6-MERGE.md`](docs/planning/PHASE-1-6-MERGE.md). 0.2.0 发布清单与验证记录见
|
|
895
|
+
[`docs/planning/RELEASE-0.2.0.md`](docs/planning/RELEASE-0.2.0.md).
|