dsh-diagnostic-tutor 0.1.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.
Files changed (54) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +658 -0
  3. package/cordis.patch.yml +23 -0
  4. package/lib/api.js +413 -0
  5. package/lib/api.js.map +1 -0
  6. package/lib/client.js +2029 -0
  7. package/lib/client.js.map +1 -0
  8. package/lib/contract.js +14 -0
  9. package/lib/contract.js.map +1 -0
  10. package/lib/diagnosis.js +224 -0
  11. package/lib/diagnosis.js.map +1 -0
  12. package/lib/handoff.js +194 -0
  13. package/lib/handoff.js.map +1 -0
  14. package/lib/index.js +186 -0
  15. package/lib/index.js.map +1 -0
  16. package/lib/lesson.js +285 -0
  17. package/lib/lesson.js.map +1 -0
  18. package/lib/prompt.js +96 -0
  19. package/lib/prompt.js.map +1 -0
  20. package/lib/state.js +500 -0
  21. package/lib/state.js.map +1 -0
  22. package/lib/tools.js +994 -0
  23. package/lib/tools.js.map +1 -0
  24. package/lib/trust-fence.js +101 -0
  25. package/lib/trust-fence.js.map +1 -0
  26. package/lib/types/api.d.ts +62 -0
  27. package/lib/types/api.d.ts.map +1 -0
  28. package/lib/types/contract.d.ts +147 -0
  29. package/lib/types/contract.d.ts.map +1 -0
  30. package/lib/types/diagnosis.d.ts +116 -0
  31. package/lib/types/diagnosis.d.ts.map +1 -0
  32. package/lib/types/handoff.d.ts +141 -0
  33. package/lib/types/handoff.d.ts.map +1 -0
  34. package/lib/types/index.d.ts +71 -0
  35. package/lib/types/index.d.ts.map +1 -0
  36. package/lib/types/lesson.d.ts +295 -0
  37. package/lib/types/lesson.d.ts.map +1 -0
  38. package/lib/types/prompt.d.ts +85 -0
  39. package/lib/types/prompt.d.ts.map +1 -0
  40. package/lib/types/state.d.ts +627 -0
  41. package/lib/types/state.d.ts.map +1 -0
  42. package/lib/types/tools.d.ts +38 -0
  43. package/lib/types/tools.d.ts.map +1 -0
  44. package/lib/types/trust-fence.d.ts +53 -0
  45. package/lib/types/trust-fence.d.ts.map +1 -0
  46. package/lib/types/udt.d.ts +95 -0
  47. package/lib/types/udt.d.ts.map +1 -0
  48. package/lib/types/vocabulary.d.ts +162 -0
  49. package/lib/types/vocabulary.d.ts.map +1 -0
  50. package/lib/udt.js +141 -0
  51. package/lib/udt.js.map +1 -0
  52. package/lib/vocabulary.js +182 -0
  53. package/lib/vocabulary.js.map +1 -0
  54. package/package.json +104 -0
package/lib/lesson.js ADDED
@@ -0,0 +1,285 @@
1
+ /**
2
+ * Learning Block and Lesson schemas.
3
+ *
4
+ * The data schema lives here (host side, zod-validated at the durable
5
+ * boundary). The **renderers live in the client half** and are keyed by
6
+ * `block.type`, so adding Formula, Code, Comparison, Practice or Resource later
7
+ * means adding one renderer entry — never rewriting the lesson renderer.
8
+ *
9
+ * A block is `{ id, type, content, metadata? }`: the envelope is uniform and
10
+ * `content` is shaped per type. The envelope is what the renderer registry
11
+ * dispatches on; `content` is what a renderer understands. An unrecognised
12
+ * `type` is not an error — the client falls back to a readable placeholder, so
13
+ * a lesson authored by a newer host still renders in an older browser half.
14
+ *
15
+ * ---------------------------------------------------------------------------
16
+ * What v0.0.4 deliberately does NOT do
17
+ * ---------------------------------------------------------------------------
18
+ * There is no model-generated lesson here. `buildPrototypeLesson` is a
19
+ * **deterministic projection of state the runtime already holds** — the node's
20
+ * own title, relation, state and evidence — assembled into the four block
21
+ * types. It exists to prove the schema and the renderer end to end.
22
+ *
23
+ * That is why every lesson carries `origin`. A prototype lesson can never be
24
+ * mistaken for teaching content, and when real generation arrives it will be
25
+ * distinguishable in the stored record rather than by convention.
26
+ */
27
+ import { z } from 'zod';
28
+ /* -------------------------------------------------------------------------- */
29
+ /* Size limits */
30
+ /* -------------------------------------------------------------------------- */
31
+ /**
32
+ * A teaching unit is a few blocks, not a chapter.
33
+ *
34
+ * The cap is what makes "never generate a whole course in one call" structural
35
+ * rather than advisory — the same reasoning as the diagnosis map's node caps.
36
+ * Six leaves room for a real unit (orient, example, diagram, check) plus a
37
+ * correction.
38
+ */
39
+ export const MAX_BLOCKS_PER_UPDATE = 6;
40
+ /**
41
+ * A lesson accumulates across a session, so its ceiling is higher than one
42
+ * call's — but it is still a ceiling. Past this the lesson has stopped being a
43
+ * learning surface and become a document.
44
+ */
45
+ export const MAX_BLOCKS_PER_LESSON = 24;
46
+ /**
47
+ * Per-field length caps, enforced by zod at the durable boundary.
48
+ *
49
+ * Structural validation alone would accept a single 5 MB text block; these make
50
+ * "oversized payload" a rejection rather than a rendering problem.
51
+ */
52
+ export const MAX_TEXT_CHARS = 6000;
53
+ export const MAX_DIAGRAM_CHARS = 4000;
54
+ export const MAX_PROMPT_CHARS = 1000;
55
+ export const MAX_TITLE_CHARS = 200;
56
+ export const MAX_STEP_CHARS = 400;
57
+ export const MAX_STEPS = 12;
58
+ /* -------------------------------------------------------------------------- */
59
+ /* Blocks */
60
+ /* -------------------------------------------------------------------------- */
61
+ /** Fields every block carries, whatever its type. */
62
+ const blockBase = {
63
+ id: z.string().min(1),
64
+ /** Renderer-free annotations; never interpreted by the schema. */
65
+ metadata: z.record(z.string(), z.unknown()).optional(),
66
+ };
67
+ export const TextBlockSchema = z.object({
68
+ ...blockBase,
69
+ type: z.literal('text'),
70
+ content: z.object({
71
+ /** Markdown. Math uses the skill's convention: `\(...\)` / `\[...\]`. */
72
+ md: z.string().min(1).max(MAX_TEXT_CHARS),
73
+ }),
74
+ });
75
+ export const ExampleBlockSchema = z.object({
76
+ ...blockBase,
77
+ type: z.literal('example'),
78
+ content: z.object({
79
+ title: z.string().min(1).max(MAX_TITLE_CHARS),
80
+ steps: z.array(z.string().min(1).max(MAX_STEP_CHARS)).min(1).max(MAX_STEPS),
81
+ takeaway: z.string().min(1).max(MAX_STEP_CHARS).optional(),
82
+ }),
83
+ });
84
+ export const DiagramBlockSchema = z.object({
85
+ ...blockBase,
86
+ type: z.literal('diagram'),
87
+ content: z.object({
88
+ /**
89
+ * `ascii` renders as preformatted text and needs no dependency.
90
+ * `mermaid` is carried by the schema from the start so a lesson authored
91
+ * for a mermaid-capable client stays valid here; this client renders it as
92
+ * a labelled source block rather than pulling in a renderer.
93
+ */
94
+ format: z.enum(['ascii', 'mermaid']),
95
+ spec: z.string().min(1).max(MAX_DIAGRAM_CHARS),
96
+ caption: z.string().min(1).max(MAX_TITLE_CHARS).optional(),
97
+ }),
98
+ });
99
+ export const CheckBlockSchema = z.object({
100
+ ...blockBase,
101
+ type: z.literal('check'),
102
+ content: z.object({
103
+ prompt: z.string().min(1).max(MAX_PROMPT_CHARS),
104
+ /** What the check is really probing; a hint for the teaching brain. */
105
+ expect: z.enum(['reasoning', 'answer']).optional(),
106
+ hint: z.string().min(1).max(MAX_PROMPT_CHARS).optional(),
107
+ }),
108
+ });
109
+ /** Every block type this version understands. */
110
+ export const BLOCK_SCHEMAS = [
111
+ TextBlockSchema,
112
+ ExampleBlockSchema,
113
+ DiagramBlockSchema,
114
+ CheckBlockSchema,
115
+ ];
116
+ export const BlockSchema = z.discriminatedUnion('type', [
117
+ TextBlockSchema,
118
+ ExampleBlockSchema,
119
+ DiagramBlockSchema,
120
+ CheckBlockSchema,
121
+ ]);
122
+ /** The block types this build can render, for diagnostics and tests. */
123
+ export const SUPPORTED_BLOCK_TYPES = BLOCK_SCHEMAS.map((schema) => schema.shape.type.value);
124
+ /* -------------------------------------------------------------------------- */
125
+ /* Lesson */
126
+ /* -------------------------------------------------------------------------- */
127
+ /**
128
+ * `tutor` is what the runtime writes now: blocks the teaching brain submitted
129
+ * through `udt_lesson_update`.
130
+ *
131
+ * `prototype` is retained rather than removed because v0.0.4 wrote records with
132
+ * it, and the domain version cannot be bumped to invalidate them (the `single`
133
+ * layout rejects a version mismatch outright — see `state.ts`). It is no longer
134
+ * produced by the runtime.
135
+ */
136
+ export const LESSON_ORIGINS = ['prototype', 'tutor'];
137
+ export const LessonOriginSchema = z.enum(LESSON_ORIGINS);
138
+ export const LessonSchema = z.object({
139
+ id: z.string().min(1),
140
+ courseId: z.string().min(1),
141
+ nodeId: z.string().min(1),
142
+ title: z.string().min(1),
143
+ blocks: z.array(BlockSchema).min(1).max(MAX_BLOCKS_PER_LESSON),
144
+ /** Who produced the blocks: the teaching brain, or the v0.0.4 scaffold. */
145
+ origin: LessonOriginSchema,
146
+ createdAt: z.string(),
147
+ updatedAt: z.string(),
148
+ });
149
+ /* -------------------------------------------------------------------------- */
150
+ /* Deterministic prototype lesson */
151
+ /* -------------------------------------------------------------------------- */
152
+ /** Render the node's position in the map as an indented tree. */
153
+ function asciiTree(nodes, focusId) {
154
+ const childrenOf = new Map();
155
+ for (const node of nodes) {
156
+ const bucket = childrenOf.get(node.parentId) ?? [];
157
+ bucket.push(node);
158
+ childrenOf.set(node.parentId, bucket);
159
+ }
160
+ const lines = [];
161
+ const walk = (node, depth, isLast) => {
162
+ const marker = depth === 0 ? '' : isLast ? '└─ ' : '├─ ';
163
+ const focus = node.id === focusId ? ' ◀ this node' : '';
164
+ lines.push(`${' '.repeat(Math.max(0, depth - 1))}${marker}${node.title} [${node.state}]${focus}`);
165
+ const children = childrenOf.get(node.id) ?? [];
166
+ children.forEach((child, index) => walk(child, depth + 1, index === children.length - 1));
167
+ };
168
+ for (const root of childrenOf.get(undefined) ?? [])
169
+ walk(root, 0, true);
170
+ return lines.join('\n');
171
+ }
172
+ /** One readable line per evidence entry, newest last. */
173
+ function evidenceSteps(node) {
174
+ if (node.evidence.length === 0) {
175
+ return ['No observation has been recorded against this node yet.'];
176
+ }
177
+ return node.evidence.map((entry) => {
178
+ const readiness = entry.readiness === undefined ? '' : ` — readiness: ${entry.readiness}`;
179
+ const note = entry.note === undefined ? '' : `: ${entry.note}`;
180
+ return `${entry.kind}${readiness}${note}`;
181
+ });
182
+ }
183
+ /** How a node came to be on the map, in the map's own terms. */
184
+ function relationExplanation(node) {
185
+ switch (node.relation) {
186
+ case 'goal':
187
+ return 'This is the goal itself — the frame the rest of the map hangs from. A goal is never marked confirmed, because it is not a claim about what you can do.';
188
+ case 'prerequisite':
189
+ return 'This was recorded as a **prerequisite**: something diagnosed as blocking the node above it.';
190
+ case 'part-of':
191
+ return 'This is a **part of** the node above it — a component the diagnosis separated out.';
192
+ case 'related':
193
+ return 'This is **related** to the node above it: useful context, but not a component and not a blocker.';
194
+ }
195
+ }
196
+ /**
197
+ * Build a prototype lesson for one node.
198
+ *
199
+ * **Not on the runtime path any more.** v0.0.5 teaches through the tutor, and
200
+ * `udt_lesson_update` is how blocks arrive. This builder is kept for two
201
+ * honest reasons: v0.0.4 wrote `origin: 'prototype'` records that must still
202
+ * parse, and it is a convenient deterministic fixture for the preview and the
203
+ * block-renderer tests.
204
+ *
205
+ * It projects stored state into the four block types — it does not invent
206
+ * teaching content.
207
+ *
208
+ * @param input.course - the course the node belongs to.
209
+ * @param input.node - the node to build for.
210
+ * @param input.nodes - every node of the course, for the map diagram.
211
+ * @param input.now - ISO timestamp.
212
+ * @returns a validated lesson record.
213
+ */
214
+ export function buildPrototypeLesson(input) {
215
+ const { course, node, nodes, now } = input;
216
+ const evidenceCount = node.evidence.length;
217
+ return LessonSchema.parse({
218
+ id: `${node.id}:prototype`,
219
+ courseId: course.id,
220
+ nodeId: node.id,
221
+ title: node.title,
222
+ origin: 'prototype',
223
+ createdAt: now,
224
+ updatedAt: now,
225
+ blocks: [
226
+ {
227
+ id: 'where',
228
+ type: 'text',
229
+ content: {
230
+ md: `**${node.title}** is on your map for *${course.title}* in the state \`${node.state}\`, ` +
231
+ `with ${evidenceCount} recorded observation${evidenceCount === 1 ? '' : 's'}.\n\n` +
232
+ relationExplanation(node),
233
+ },
234
+ metadata: { role: 'orientation' },
235
+ },
236
+ {
237
+ id: 'evidence',
238
+ type: 'example',
239
+ content: {
240
+ title: 'What the runtime has actually recorded',
241
+ steps: evidenceSteps(node),
242
+ takeaway: 'A node only moves off `unconfirmed` because something was observed — never because it was asserted.',
243
+ },
244
+ metadata: { role: 'evidence' },
245
+ },
246
+ {
247
+ id: 'map',
248
+ type: 'diagram',
249
+ content: {
250
+ format: 'ascii',
251
+ spec: asciiTree(nodes, node.id),
252
+ caption: `Where ${node.title} sits in the diagnosis map.`,
253
+ },
254
+ metadata: { role: 'context' },
255
+ },
256
+ {
257
+ id: 'check',
258
+ type: 'check',
259
+ content: {
260
+ prompt: `Before any teaching starts: in your own words, what do you already know about **${node.title}**, ` +
261
+ 'and where does it stop being clear?',
262
+ expect: 'reasoning',
263
+ hint: 'A rough answer is more useful than a polished one — the gaps are the point.',
264
+ },
265
+ metadata: { role: 'check', stopAndWait: true },
266
+ },
267
+ ],
268
+ });
269
+ }
270
+ /**
271
+ * Read a block's type without trusting the value.
272
+ *
273
+ * Used by the client's renderer registry so an unknown type degrades instead of
274
+ * throwing.
275
+ *
276
+ * @param value - any parsed block-shaped value.
277
+ * @returns the declared type, or `undefined`.
278
+ */
279
+ export function blockTypeOf(value) {
280
+ if (value === null || typeof value !== 'object')
281
+ return undefined;
282
+ const type = value.type;
283
+ return typeof type === 'string' ? type : undefined;
284
+ }
285
+ //# sourceMappingURL=lesson.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lesson.js","sourceRoot":"","sources":["../src/lesson.ts"],"names":[],"mappings":"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"}
package/lib/prompt.js ADDED
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Waking the teaching brain.
3
+ *
4
+ * "Start learning" is a UI act, but teaching happens in the chat — so the panel
5
+ * needs a way to tell the tutor that a node is now in focus. This module is
6
+ * that hand-off: it composes one ordinary user-role turn from the plugin and
7
+ * delivers it to the agent that owns the learner's session.
8
+ *
9
+ * Why a user-role message rather than injected context: `agent.inject()` adds
10
+ * model-visible context but **does not wake an idle agent**, so nothing would
11
+ * happen until the learner typed something. `followup()` opens a turn, which is
12
+ * exactly the intent of pressing the button. `@deepseek-ai/dsh-command-goal`
13
+ * and `dsh-headless` do the same thing for the same reason.
14
+ *
15
+ * The source is `{ kind: 'diagnostic-tutor' }` — the plugin declares its own
16
+ * kind. Harness 0.1.7 removed the shared catch-all `plugin` kind: a producer now
17
+ * merges its own entry into `MessageSourceMap`, and the augmentation below is
18
+ * that declaration. It is also what makes the value type-check at all, so a
19
+ * future rename cannot drift out of sync silently.
20
+ *
21
+ * Note what this module does **not** do: it does not decide what to teach. It
22
+ * reports which node the learner selected and asks the tutor to begin. Every
23
+ * teaching decision stays with the teaching brain.
24
+ */
25
+ import { createUserMessage } from '@deepseek-ai/dsh-llm';
26
+ /**
27
+ * Attribution for every turn this plugin opens.
28
+ *
29
+ * Must equal the declared kind below; TypeScript enforces it, because the value
30
+ * is passed where the augmented union is expected.
31
+ */
32
+ export const PLUGIN_SOURCE = 'diagnostic-tutor';
33
+ /**
34
+ * Compose the turn text.
35
+ *
36
+ * Written in the learner's voice, because it lands in the transcript they are
37
+ * reading: it should look like the button they just pressed, not like a system
38
+ * directive. It states the node and the expected shape of the reply, and stops
39
+ * — the tool descriptions carry the detail about blocks and caps.
40
+ *
41
+ * @param course - the course being learned.
42
+ * @param node - the node put in focus.
43
+ * @returns the message text.
44
+ */
45
+ export function focusPromptText(course, node) {
46
+ return (`Let's start on **${node.title}** (${node.relation} of ${course.title}).\n\n` +
47
+ 'Please teach me this node. Put the teaching itself in the Learning Surface beside the map, ' +
48
+ 'end with a check I can answer here in the chat, and stop there.');
49
+ }
50
+ /**
51
+ * Deliver one turn to the agent that owns a session.
52
+ *
53
+ * Never throws for a missing agent: the focus has already been recorded by the
54
+ * time this runs, and the tutor can pick it up on the next turn regardless. A
55
+ * graceful "not prompted" keeps the button useful in every configuration.
56
+ *
57
+ * @param ctx - the plugin's context.
58
+ * @param sessionId - the session the panel is showing, when it knows one.
59
+ * @param text - the turn text.
60
+ * @returns whether a turn was opened, and why not when it was not.
61
+ */
62
+ export function promptSession(ctx, sessionId, text) {
63
+ if (sessionId === undefined || sessionId.length === 0) {
64
+ return { prompted: false, reason: 'the panel did not report a session' };
65
+ }
66
+ const agents = ctx.get('agents');
67
+ if (!agents) {
68
+ return { prompted: false, reason: 'this profile has no agent registry' };
69
+ }
70
+ // The requested session is the right target. DSH creates an agent lazily —
71
+ // a brand-new session has none until something is said in it — so fall back
72
+ // to the registry only when doing so is unambiguous, and never guess between
73
+ // several conversations.
74
+ let agent = agents.get(sessionId);
75
+ if (!agent) {
76
+ const live = agents.list();
77
+ if (live.length === 1 && live[0] !== undefined) {
78
+ agent = live[0];
79
+ }
80
+ else {
81
+ return {
82
+ prompted: false,
83
+ reason: live.length === 0
84
+ ? 'that session has no live agent yet — say anything in the chat and the tutor will pick the focus up'
85
+ : `that session has no live agent, and ${live.length} other conversations are open`,
86
+ };
87
+ }
88
+ }
89
+ const target = agent;
90
+ target.followup(createUserMessage({
91
+ content: [{ type: 'text', text }],
92
+ source: { kind: PLUGIN_SOURCE },
93
+ }));
94
+ return { prompted: true };
95
+ }
96
+ //# sourceMappingURL=prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.js","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAGH,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AAIxD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,kBAAkB,CAAA;AA2B/C;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,eAAe,CAAC,MAAoB,EAAE,IAAgB;IACpE,OAAO,CACL,oBAAoB,IAAI,CAAC,KAAK,OAAO,IAAI,CAAC,QAAQ,OAAO,MAAM,CAAC,KAAK,QAAQ;QAC7E,6FAA6F;QAC7F,iEAAiE,CAClE,CAAA;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,aAAa,CAAC,GAAY,EAAE,SAA6B,EAAE,IAAY;IACrF,IAAI,SAAS,KAAK,SAAS,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtD,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,oCAAoC,EAAE,CAAA;IAC1E,CAAC;IAED,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IAChC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,oCAAoC,EAAE,CAAA;IAC1E,CAAC;IAED,2EAA2E;IAC3E,4EAA4E;IAC5E,6EAA6E;IAC7E,yBAAyB;IACzB,IAAI,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,SAAkB,CAAC,CAAA;IAC1C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,EAAE,CAAA;QAC1B,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,SAAS,EAAE,CAAC;YAC/C,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;QACjB,CAAC;aAAM,CAAC;YACN,OAAO;gBACL,QAAQ,EAAE,KAAK;gBACf,MAAM,EACJ,IAAI,CAAC,MAAM,KAAK,CAAC;oBACf,CAAC,CAAC,oGAAoG;oBACtG,CAAC,CAAC,uCAAuC,IAAI,CAAC,MAAM,+BAA+B;aACxF,CAAA;QACH,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,KAAK,CAAA;IACpB,MAAM,CAAC,QAAQ,CACb,iBAAiB,CAAC;QAChB,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;QACjC,MAAM,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE;KAChC,CAAC,CACH,CAAA;IACD,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAA;AAC3B,CAAC"}