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.
Files changed (126) hide show
  1. package/README.md +316 -79
  2. package/lib/api.js +364 -78
  3. package/lib/api.js.map +1 -1
  4. package/lib/block-types.js +25 -0
  5. package/lib/block-types.js.map +1 -0
  6. package/lib/client.js +9952 -821
  7. package/lib/client.js.map +1 -1
  8. package/lib/diagnostics.js +170 -0
  9. package/lib/diagnostics.js.map +1 -0
  10. package/lib/execution-key.js +91 -0
  11. package/lib/execution-key.js.map +1 -0
  12. package/lib/handoff.js +44 -1
  13. package/lib/handoff.js.map +1 -1
  14. package/lib/index.js +87 -51
  15. package/lib/index.js.map +1 -1
  16. package/lib/insights.js +82 -0
  17. package/lib/insights.js.map +1 -0
  18. package/lib/interactions.js +70 -0
  19. package/lib/interactions.js.map +1 -0
  20. package/lib/journal.js +122 -0
  21. package/lib/journal.js.map +1 -0
  22. package/lib/lesson-markdown.js +72 -0
  23. package/lib/lesson-markdown.js.map +1 -0
  24. package/lib/lesson.js +152 -3
  25. package/lib/lesson.js.map +1 -1
  26. package/lib/math.js +126 -0
  27. package/lib/math.js.map +1 -0
  28. package/lib/path-api.js +140 -0
  29. package/lib/path-api.js.map +1 -0
  30. package/lib/path-runtime.js +361 -0
  31. package/lib/path-runtime.js.map +1 -0
  32. package/lib/path-tools.js +107 -0
  33. package/lib/path-tools.js.map +1 -0
  34. package/lib/path.js +179 -0
  35. package/lib/path.js.map +1 -0
  36. package/lib/plan-tools.js +89 -0
  37. package/lib/plan-tools.js.map +1 -0
  38. package/lib/plan.js +82 -0
  39. package/lib/plan.js.map +1 -0
  40. package/lib/project-session.js +48 -0
  41. package/lib/project-session.js.map +1 -0
  42. package/lib/prompt.js +4 -19
  43. package/lib/prompt.js.map +1 -1
  44. package/lib/review-sync.js +51 -0
  45. package/lib/review-sync.js.map +1 -0
  46. package/lib/review-tools.js +115 -0
  47. package/lib/review-tools.js.map +1 -0
  48. package/lib/review.js +103 -0
  49. package/lib/review.js.map +1 -0
  50. package/lib/source-schema.js +24 -0
  51. package/lib/source-schema.js.map +1 -0
  52. package/lib/source-tools.js +61 -0
  53. package/lib/source-tools.js.map +1 -0
  54. package/lib/sources.js +78 -0
  55. package/lib/sources.js.map +1 -0
  56. package/lib/state.js +353 -47
  57. package/lib/state.js.map +1 -1
  58. package/lib/tools.js +620 -123
  59. package/lib/tools.js.map +1 -1
  60. package/lib/types/api.d.ts +10 -3
  61. package/lib/types/api.d.ts.map +1 -1
  62. package/lib/types/block-types.d.ts +12 -0
  63. package/lib/types/block-types.d.ts.map +1 -0
  64. package/lib/types/contract.d.ts +81 -7
  65. package/lib/types/contract.d.ts.map +1 -1
  66. package/lib/types/diagnostics.d.ts +95 -0
  67. package/lib/types/diagnostics.d.ts.map +1 -0
  68. package/lib/types/execution-key.d.ts +59 -0
  69. package/lib/types/execution-key.d.ts.map +1 -0
  70. package/lib/types/handoff.d.ts +2 -2
  71. package/lib/types/handoff.d.ts.map +1 -1
  72. package/lib/types/index.d.ts +19 -14
  73. package/lib/types/index.d.ts.map +1 -1
  74. package/lib/types/insights.d.ts +57 -0
  75. package/lib/types/insights.d.ts.map +1 -0
  76. package/lib/types/interactions.d.ts +23 -0
  77. package/lib/types/interactions.d.ts.map +1 -0
  78. package/lib/types/journal.d.ts +57 -0
  79. package/lib/types/journal.d.ts.map +1 -0
  80. package/lib/types/lesson-markdown.d.ts +11 -0
  81. package/lib/types/lesson-markdown.d.ts.map +1 -0
  82. package/lib/types/lesson.d.ts +697 -5
  83. package/lib/types/lesson.d.ts.map +1 -1
  84. package/lib/types/math.d.ts +50 -0
  85. package/lib/types/math.d.ts.map +1 -0
  86. package/lib/types/path-api.d.ts +5 -0
  87. package/lib/types/path-api.d.ts.map +1 -0
  88. package/lib/types/path-runtime.d.ts +1558 -0
  89. package/lib/types/path-runtime.d.ts.map +1 -0
  90. package/lib/types/path-tools.d.ts +4 -0
  91. package/lib/types/path-tools.d.ts.map +1 -0
  92. package/lib/types/path.d.ts +744 -0
  93. package/lib/types/path.d.ts.map +1 -0
  94. package/lib/types/plan-tools.d.ts +4 -0
  95. package/lib/types/plan-tools.d.ts.map +1 -0
  96. package/lib/types/plan.d.ts +53 -0
  97. package/lib/types/plan.d.ts.map +1 -0
  98. package/lib/types/project-session.d.ts +33 -0
  99. package/lib/types/project-session.d.ts.map +1 -0
  100. package/lib/types/prompt.d.ts.map +1 -1
  101. package/lib/types/review-sync.d.ts +17 -0
  102. package/lib/types/review-sync.d.ts.map +1 -0
  103. package/lib/types/review-tools.d.ts +4 -0
  104. package/lib/types/review-tools.d.ts.map +1 -0
  105. package/lib/types/review.d.ts +65 -0
  106. package/lib/types/review.d.ts.map +1 -0
  107. package/lib/types/source-schema.d.ts +31 -0
  108. package/lib/types/source-schema.d.ts.map +1 -0
  109. package/lib/types/source-tools.d.ts +4 -0
  110. package/lib/types/source-tools.d.ts.map +1 -0
  111. package/lib/types/sources.d.ts +33 -0
  112. package/lib/types/sources.d.ts.map +1 -0
  113. package/lib/types/state.d.ts +607 -23
  114. package/lib/types/state.d.ts.map +1 -1
  115. package/lib/types/tools.d.ts +3 -1
  116. package/lib/types/tools.d.ts.map +1 -1
  117. package/lib/types/udt.d.ts +20 -24
  118. package/lib/types/udt.d.ts.map +1 -1
  119. package/lib/types/version.d.ts +11 -0
  120. package/lib/types/version.d.ts.map +1 -0
  121. package/lib/types/vocabulary.d.ts +2 -2
  122. package/lib/udt.js +72 -19
  123. package/lib/udt.js.map +1 -1
  124. package/lib/version.js +26 -0
  125. package/lib/version.js.map +1 -0
  126. package/package.json +27 -22
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Read-only health report for the tutoring runtime.
3
+ *
4
+ * The report is what "doctor" shows and what the local `/diagnostics` route
5
+ * returns. It is computed from durable state plus the recent event journal; it
6
+ * never writes, never calls a model and never includes lesson or chat content.
7
+ */
8
+ import { briefReady } from './path.js';
9
+ const STALE_REQUEST_MINUTES = 10;
10
+ const STALLED_ATTEMPT_MINUTES = 15;
11
+ function minutesBetween(now, iso) {
12
+ const at = Date.parse(iso);
13
+ return Number.isFinite(at) ? Math.max(0, Math.round((now - at) / 60_000)) : 0;
14
+ }
15
+ function assessmentReady(path) {
16
+ return path.assessment !== null
17
+ && path.assessment.briefRevision === path.briefRevision
18
+ && (path.assessment.status === 'ready' || path.assessment.status === 'skipped');
19
+ }
20
+ function unresolved(request, path) {
21
+ if (request.kind === 'diagnosis') {
22
+ const assessment = path.assessment;
23
+ return assessment === null || assessment.status === 'in-progress' || assessment.updatedAt < request.createdAt;
24
+ }
25
+ return !path.curricula.some((entry) => entry.createdAt >= request.createdAt);
26
+ }
27
+ /**
28
+ * Build one diagnostics report.
29
+ *
30
+ * @param state - open state handle.
31
+ * @param journal - journal whose recent events are included.
32
+ * @param options - clock and plugin identity for tests.
33
+ * @returns the report.
34
+ */
35
+ export async function buildDiagnostics(state, journal, options = {}) {
36
+ const now = options.now ?? new Date().toISOString();
37
+ const nowMs = Date.parse(now);
38
+ const exported = state.exportState(now);
39
+ const paths = new Map(exported.learningPaths.map((path) => [path.courseId, path]));
40
+ const runs = new Map(exported.courseRuns.map((run) => [run.courseId, run]));
41
+ const issues = [];
42
+ const projects = [];
43
+ for (const course of [...exported.courses].sort((left, right) => left.id.localeCompare(right.id))) {
44
+ const path = paths.get(course.id);
45
+ if (path === undefined) {
46
+ projects.push({
47
+ courseId: course.id, title: course.title, status: course.status, stage: 'legacy',
48
+ briefRevision: 0, assessment: null, curricula: 0, acceptedVersion: null,
49
+ requests: [], activeAttempt: null, completedItems: 0, totalItems: 0,
50
+ });
51
+ continue;
52
+ }
53
+ const ready = assessmentReady(path);
54
+ const accepted = path.acceptedVersion !== null
55
+ && path.curricula.some((entry) => entry.version === path.acceptedVersion);
56
+ const stage = !briefReady(path.brief)
57
+ ? 'goal'
58
+ : !ready
59
+ ? 'diagnosis'
60
+ : accepted
61
+ ? 'learning'
62
+ : 'route';
63
+ const run = runs.get(course.id);
64
+ const attempt = run?.attempts.find((entry) => entry.id === run.activeAttemptId);
65
+ const activeAttempt = attempt === undefined ? null : {
66
+ attemptId: attempt.id,
67
+ itemId: attempt.itemId,
68
+ status: attempt.status,
69
+ startedAt: attempt.startedAt,
70
+ ageMinutes: minutesBetween(nowMs, attempt.startedAt),
71
+ units: attempt.units.length,
72
+ nodeId: attempt.nodeId,
73
+ };
74
+ const pending = path.requests
75
+ .filter((request) => request.prompted
76
+ && ((request.failure !== null && request.failure !== undefined) || unresolved(request, path)))
77
+ .map((request) => ({
78
+ id: request.id,
79
+ kind: request.kind,
80
+ sessionId: request.sessionId,
81
+ createdAt: request.createdAt,
82
+ ageMinutes: minutesBetween(nowMs, request.createdAt),
83
+ prompted: request.prompted,
84
+ failure: request.failure ?? null,
85
+ }));
86
+ const acceptedPlan = path.curricula.find((entry) => entry.version === path.acceptedVersion) ?? path.curricula.at(-1);
87
+ const completedItems = new Set((run?.attempts ?? []).filter((entry) => entry.status === 'completed').map((entry) => entry.itemId)).size;
88
+ projects.push({
89
+ courseId: course.id,
90
+ title: course.title,
91
+ status: course.status,
92
+ stage,
93
+ briefRevision: path.briefRevision,
94
+ assessment: path.assessment?.status ?? null,
95
+ curricula: path.curricula.length,
96
+ acceptedVersion: path.acceptedVersion,
97
+ requests: pending,
98
+ activeAttempt,
99
+ completedItems,
100
+ totalItems: acceptedPlan?.items.length ?? 0,
101
+ });
102
+ for (const request of pending) {
103
+ if (request.failure !== null) {
104
+ issues.push({
105
+ code: 'failed-turn',
106
+ severity: 'error',
107
+ courseId: course.id,
108
+ message: `${request.kind} request ${request.id.slice(0, 8)} failed after waking the tutor: ${request.failure.slice(0, 240)}`,
109
+ });
110
+ }
111
+ else if (request.ageMinutes >= STALE_REQUEST_MINUTES) {
112
+ issues.push({
113
+ code: 'stale-request',
114
+ severity: 'warn',
115
+ courseId: course.id,
116
+ message: `${request.kind} request ${request.id.slice(0, 8)} has been waiting for ${request.ageMinutes} min without advancing`,
117
+ });
118
+ }
119
+ }
120
+ if (path.acceptedVersion !== null && !accepted) {
121
+ issues.push({
122
+ code: 'accepted-route-missing',
123
+ severity: 'error',
124
+ courseId: course.id,
125
+ message: `acceptedVersion ${path.acceptedVersion} has no matching curriculum revision`,
126
+ });
127
+ }
128
+ if (activeAttempt !== null && activeAttempt.status !== 'completed' && activeAttempt.units === 0
129
+ && activeAttempt.ageMinutes >= STALLED_ATTEMPT_MINUTES) {
130
+ issues.push({
131
+ code: 'stalled-attempt',
132
+ severity: 'warn',
133
+ courseId: course.id,
134
+ message: `lesson attempt ${activeAttempt.attemptId.slice(0, 8)} has no teaching units after ${activeAttempt.ageMinutes} min`,
135
+ });
136
+ }
137
+ }
138
+ const courseIds = new Set(exported.courses.map((course) => course.id));
139
+ for (const project of exported.projects) {
140
+ if (project.courseId !== undefined && !courseIds.has(project.courseId)) {
141
+ issues.push({
142
+ code: 'orphan-project-selection',
143
+ severity: 'warn',
144
+ message: `session ${project.sessionId} is bound to missing project ${project.courseId}`,
145
+ });
146
+ }
147
+ }
148
+ return {
149
+ ok: true,
150
+ generatedAt: now,
151
+ plugin: { name: 'dsh-diagnostic-tutor', version: options.pluginVersion ?? 'unknown' },
152
+ domain: exported.domain,
153
+ counts: {
154
+ courses: exported.courses.length,
155
+ nodes: exported.nodes.length,
156
+ lessons: exported.lessons.length,
157
+ focus: exported.focus.length,
158
+ projects: exported.projects.length,
159
+ learningPaths: exported.learningPaths.length,
160
+ courseRuns: exported.courseRuns.length,
161
+ },
162
+ issues,
163
+ projects,
164
+ journal: {
165
+ file: journal.file,
166
+ recent: await journal.tail(options.events ?? 40),
167
+ },
168
+ };
169
+ }
170
+ //# sourceMappingURL=diagnostics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../src/diagnostics.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Where execution state lives.
3
+ *
4
+ * The knowledge a learner accumulates — the course, the diagnosis map, the
5
+ * evidence, the lessons — belongs to the learner and is shared by every
6
+ * conversation they open. Where they have *got to* does not: a person can be
7
+ * part-way through one node in one conversation and a different node in
8
+ * another, and those two positions are not the same position.
9
+ *
10
+ * The store used to disagree. Focus was keyed by course, decisions and handoffs
11
+ * by node, so a course held exactly one of each — and starting a node in one
12
+ * conversation silently replaced another conversation's. A learner switched
13
+ * back and found someone else's lesson under their own chat.
14
+ *
15
+ * So the three execution records are keyed by conversation *and* by the thing
16
+ * they are about. This module is the only place that composition happens: a
17
+ * key built by hand in two files is a key that will eventually be built two
18
+ * different ways.
19
+ *
20
+ * Keys are storage-internal. They are never returned to the model or to a
21
+ * surface — `udt_decide_next` takes a node id, not a key — so the separator and
22
+ * the encoding are free to change as long as every reader changes with them.
23
+ */
24
+ /**
25
+ * Separator between the conversation and the logical id.
26
+ *
27
+ * A unit separator. Node ids are `course:subject-topic` and course ids are
28
+ * subject names, so anything printable risks colliding with real data; this
29
+ * cannot appear in either.
30
+ */
31
+ const SEP = '\u001f';
32
+ /**
33
+ * Escape the separator inside a part, so the composition stays injective.
34
+ *
35
+ * Splitting on the separator is only unambiguous if no part contains one. In
36
+ * practice none does — session ids are `session-<uuid>` and node ids are
37
+ * `course:slug` — but "in practice none does" is an assumption about other
38
+ * people's data, and the failure mode is silent: two conversations quietly
39
+ * sharing one key. Doubling the separator makes the composition reversible
40
+ * whatever the parts contain, for the cost of one string replace.
41
+ *
42
+ * @param part - a session id or a logical id.
43
+ * @returns the part with any separator doubled.
44
+ */
45
+ function escapePart(part) {
46
+ return part.split(SEP).join(SEP + SEP);
47
+ }
48
+ /**
49
+ * Build the storage key for one conversation's execution record.
50
+ *
51
+ * Deterministic and injective: escaped parts mean `('a', 'b␟c')` and
52
+ * `('a␟b', 'c')` cannot produce the same key, so two conversations can never
53
+ * resolve to one record.
54
+ *
55
+ * @param kind - which table this key is for, so keys are readable in a dump.
56
+ * @param sessionId - the conversation that owns the record.
57
+ * @param logicalId - the course, node or target the record is about.
58
+ * @returns the key to store under.
59
+ */
60
+ export function executionKey(kind, sessionId, logicalId) {
61
+ return `${kind}${SEP}${escapePart(sessionId)}${SEP}${escapePart(logicalId)}`;
62
+ }
63
+ /** The focus a conversation is working on, within one course. */
64
+ export function focusKey(sessionId, courseId) {
65
+ return executionKey('focus', sessionId, courseId);
66
+ }
67
+ /** The decision a conversation reached after one node. */
68
+ export function nextStepKey(sessionId, fromNodeId) {
69
+ return executionKey('next', sessionId, fromNodeId);
70
+ }
71
+ /** The handoff a conversation is waiting on, for one target node. */
72
+ export function handoffKey(sessionId, targetNodeId) {
73
+ return executionKey('handoff', sessionId, targetNodeId);
74
+ }
75
+ /**
76
+ * Whether a stored key predates session scoping.
77
+ *
78
+ * Records written by an older build are keyed by the bare course or node id.
79
+ * They are left exactly where they are: not migrated, not deleted, not
80
+ * attributed to a conversation on a guess. They are simply never read, because
81
+ * a record that cannot say whose it is cannot be shown to anyone without
82
+ * risking showing it to the wrong person — which is the bug this all exists to
83
+ * fix.
84
+ *
85
+ * @param key - a key found in one of the three tables.
86
+ * @returns true when the key carries no conversation.
87
+ */
88
+ export function isLegacyExecutionKey(key) {
89
+ return !key.includes(SEP);
90
+ }
91
+ //# sourceMappingURL=execution-key.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"execution-key.js","sourceRoot":"","sources":["../src/execution-key.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH;;;;;;GAMG;AACH,MAAM,GAAG,GAAG,QAAQ,CAAA;AAKpB;;;;;;;;;;;;GAYG;AACH,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC,CAAA;AACxC,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,YAAY,CAAC,IAAmB,EAAE,SAAiB,EAAE,SAAiB;IACpF,OAAO,GAAG,IAAI,GAAG,GAAG,GAAG,UAAU,CAAC,SAAS,CAAC,GAAG,GAAG,GAAG,UAAU,CAAC,SAAS,CAAC,EAAE,CAAA;AAC9E,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,QAAQ,CAAC,SAAiB,EAAE,QAAgB;IAC1D,OAAO,YAAY,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAA;AACnD,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,WAAW,CAAC,SAAiB,EAAE,UAAkB;IAC/D,OAAO,YAAY,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,CAAC,CAAA;AACpD,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,UAAU,CAAC,SAAiB,EAAE,YAAoB;IAChE,OAAO,YAAY,CAAC,SAAS,EAAE,SAAS,EAAE,YAAY,CAAC,CAAA;AACzD,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAW;IAC9C,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAA;AAC3B,CAAC"}
package/lib/handoff.js CHANGED
@@ -137,6 +137,43 @@ function offset(from, requestedMs) {
137
137
  const at = Date.parse(from);
138
138
  return Number.isNaN(at) ? undefined : Math.max(0, at - requestedMs);
139
139
  }
140
+ /** Parse a timestamp, or undefined when it is missing or unreadable. */
141
+ function ms(at) {
142
+ if (at === undefined)
143
+ return undefined;
144
+ const value = Date.parse(at);
145
+ return Number.isNaN(value) ? undefined : value;
146
+ }
147
+ /**
148
+ * When this handoff stopped being a wait.
149
+ *
150
+ * A finished handoff's elapsed time is a fact about what happened, not a
151
+ * running total, so it has to stop at the moment the wait ended:
152
+ *
153
+ * failed `failedAt` — when the tutor was found to be unreachable
154
+ * ready `lessonAt` — when the teaching arrived
155
+ *
156
+ * `observedAt` is deliberately not used when `lessonAt` exists. It records when
157
+ * a surface happened to render the lesson, which is later and is not part of
158
+ * how long the learner waited; letting it win would make the number depend on
159
+ * whether a tab was open.
160
+ *
161
+ * When neither is present — a lesson that already existed when the handoff was
162
+ * requested — the record's last write is the honest answer, and it is the
163
+ * request itself, so the elapsed time is zero rather than a growing count.
164
+ *
165
+ * @param record - the handoff.
166
+ * @param finished - whether the handoff has reached a terminal state.
167
+ * @returns the freeze point in milliseconds, or undefined while still waiting.
168
+ */
169
+ function finishedAtMs(record, finished) {
170
+ if (!finished)
171
+ return undefined;
172
+ if (record.status === 'failed') {
173
+ return ms(record.failedAt) ?? ms(record.updatedAt);
174
+ }
175
+ return ms(record.lessonAt) ?? ms(record.updatedAt);
176
+ }
140
177
  /**
141
178
  * Derive what a surface should show.
142
179
  *
@@ -164,7 +201,13 @@ export function handoffView(record, nowMs, hasLesson) {
164
201
  add('toFirstActivity', record.firstActivityAt);
165
202
  add('toLesson', record.lessonAt);
166
203
  add('toObserved', record.observedAt);
167
- const elapsedMs = Math.max(0, nowMs - (Number.isNaN(requested) ? nowMs : requested));
204
+ // Elapsed time measures the WAIT. Once the wait is over it is a fact, and a
205
+ // fact does not keep growing — which is what produced "Lesson ready 33h 16m"
206
+ // for a handoff that had finished half a minute after it was asked.
207
+ const finished = record.status === 'failed' || record.status === 'ready' || hasLesson;
208
+ const startedMs = Number.isNaN(requested) ? nowMs : requested;
209
+ const endMs = finishedAtMs(record, finished) ?? nowMs;
210
+ const elapsedMs = Math.max(0, endMs - startedMs);
168
211
  const base = { targetNodeId: record.targetNodeId, elapsedMs, attempts: record.attempts, stages };
169
212
  if (record.status === 'failed') {
170
213
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"handoff.js","sourceRoot":"","sources":["../src/handoff.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"handoff.js","sourceRoot":"","sources":["../src/handoff.ts"],"names":[],"mappings":"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"}
package/lib/index.js CHANGED
@@ -6,17 +6,23 @@
6
6
  * on unload.
7
7
  *
8
8
  * ---------------------------------------------------------------------------
9
- * v0.0.3 scope (see docs/planning/PLAN.md §7)
9
+ * Scope in v0.2.0 (content/input/review loop on top of the published learning runtime)
10
10
  * ---------------------------------------------------------------------------
11
- * Goal → detection → diagnosis map. The runtime now carries real product
12
- * semantics: it records a learning goal in the learner's words, plants a map
13
- * root, and lets a map grow one diagnosis at a time under rules that make
14
- * unverified mastery impossible to store.
11
+ * The runtime owns learner state and the surfaces that show it; it owns no
12
+ * teaching decisions. What it registers:
15
13
  *
16
- * Still deliberately absent:
17
- * - no lesson generation, no quiz system -> v0.0.4+
18
- * - no client half / UI -> v0.0.4
19
- * - no resource ingestion, no RAG -> out of scope for v0.1
14
+ * - a goal → diagnosis map (courses, nodes, evidence) per project;
15
+ * - one learning focus, next step and handoff per conversation;
16
+ * - versioned course routes (goal brief, evidence-referenced assessment,
17
+ * immutable curriculum revisions, explicit learner acceptance);
18
+ * - session-scoped course runs: an attempt, teaching snapshots and the
19
+ * evidence that completion is allowed to cite;
20
+ * - the browser half: one "Learning map" main page, one "Current lesson"
21
+ * docked tab, and the HTTP routes both read through.
22
+ *
23
+ * Still deliberately absent: resource ingestion and RAG (out of scope), and
24
+ * any rule that decides what to teach. The Universal Diagnostic Tutor skill is
25
+ * the teaching brain; this plugin only records what it did.
20
26
  *
21
27
  * ---------------------------------------------------------------------------
22
28
  * Cross-version discipline (see PLAN.md 2.12 #9 / #16)
@@ -40,10 +46,13 @@
40
46
  */
41
47
  import { API_PREFIX, registerApi } from './api.js';
42
48
  import { withActivity } from './handoff.js';
49
+ import { openJournal } from './journal.js';
50
+ import { stampPathRequestFailure } from './path-runtime.js';
51
+ import { packageVersion } from './version.js';
43
52
  import { promptSession } from './prompt.js';
44
53
  import { UDT_DOMAIN_NAME, openUdState } from './state.js';
45
54
  import { registerTools } from './tools.js';
46
- import { describeUdtStatus, detectUdtSkill } from './udt.js';
55
+ import { teachingBrainForSession } from './udt.js';
47
56
  /**
48
57
  * Plugin module name. Stable kebab-case, equal to the loader row `id` in
49
58
  * cordis.patch.yml and distinct from the npm package name.
@@ -53,11 +62,10 @@ export const name = 'diagnostic-tutor';
53
62
  * Services this plugin requires before it may load.
54
63
  *
55
64
  * Only services the standard profiles guarantee are declared here — both come
56
- * from `@deepseek-ai/dsh-base`. Everything optional (the skill catalog, the
57
- * system prompt) is resolved lazily with `ctx.get(...)`, so a profile without
58
- * them loses the corresponding enhancement instead of leaving the whole plugin
59
- * tree pending. A pending plugin prints nothing at all, which is hard to
60
- * diagnose.
65
+ * from `@deepseek-ai/dsh-base`. Optional Agent and Skill services are resolved
66
+ * on each availability request with `ctx.reflect.get(...)`, so a profile without
67
+ * them reports inconclusive availability instead of leaving the whole plugin
68
+ * tree pending. A pending plugin prints nothing at all, which is hard to diagnose.
61
69
  */
62
70
  export const inject = ['tools', 'storageDomain'];
63
71
  /**
@@ -108,30 +116,9 @@ export async function apply(ctx) {
108
116
  // rejects new writes, drains queued ones, and releases the backend unit) and
109
117
  // Cordis awaits the returned promise before the plugin counts as unloaded.
110
118
  ctx.effect(() => () => state.close());
111
- // Is the teaching brain present? Resolved lazily and never fatal: the
112
- // runtime is useful without it, and a missing skill is a degraded mode, not
113
- // an error. The result stays internal — it is logged, never surfaced to a
114
- // learner through a tool, because the skill's own protocol forbids naming
115
- // its files and versions in learner-facing text.
116
- //
117
- // Nothing is installed on the strength of it. Until v0.0.8 this gated a
118
- // runtime-semantics system-prompt section that explained the storage model to
119
- // a skill whose guardrails read as forbidding it; UDT v2.1's
120
- // `learning_runtime_contract.md` now says all of that in the skill's own
121
- // words, so the bridge was deleted rather than kept as a second voice.
122
- const udt = await detectUdtSkill(ctx.get('skills'));
123
- if (udt.available) {
124
- ctx.logger.debug(`[diagnostic-tutor] teaching brain: ${describeUdtStatus(udt)}`);
125
- }
126
- else {
127
- // Not debug: without the skill nothing will ever be taught, and a learner
128
- // staring at an empty panel deserves a reason in the log. The panel says
129
- // the same thing in its own words; neither names the skill's files or
130
- // version, because its protocol forbids that in learner-facing text.
131
- ctx.logger.warn(`[diagnostic-tutor] no teaching brain in this profile: ${describeUdtStatus(udt)}. ` +
132
- 'The runtime will record and display state, but no lesson will be written until the ' +
133
- 'Universal Diagnostic Tutor skill is installed for this workspace.');
134
- }
119
+ // Local, content-free monitoring. Disabled under the test runner and never
120
+ // allowed to fail the runtime (all writes are best-effort).
121
+ const journal = openJournal();
135
122
  // Mount the browser API if this profile has a web surface.
136
123
  //
137
124
  // Resolved eagerly first: a profile's web server is mounted by the base and
@@ -145,12 +132,19 @@ export async function apply(ctx) {
145
132
  return;
146
133
  ctx.effect(() => registerApi(server, {
147
134
  state,
148
- prompt: (sessionId, text) => promptSession(ctx, sessionId, text),
149
- // Three states, because two would be a lie. `null` means this scope
150
- // cannot see the catalog well enough to say — the normal case in a web
151
- // profile, where skills are mounted per agent — and a surface must not
152
- // turn that into "no tutor installed".
153
- teachingBrain: udt.available ? true : udt.catalogVisible ? false : null,
135
+ prompt: (sessionId, text) => {
136
+ const result = promptSession(ctx, sessionId, text);
137
+ journal.append('tutor.prompt', {
138
+ sessionId: sessionId ?? null,
139
+ prompted: result.prompted,
140
+ ...(result.reason === undefined ? {} : { reason: result.reason.slice(0, 240) }),
141
+ });
142
+ return result;
143
+ },
144
+ teachingBrain: (sessionId) => teachingBrainForSession(ctx, sessionId),
145
+ journal,
146
+ pluginVersion: packageVersion(),
147
+ onEvent: (event, fields) => journal.append(event, fields),
154
148
  }));
155
149
  ctx.logger.debug(`[diagnostic-tutor] browser API mounted at ${API_PREFIX}`);
156
150
  };
@@ -166,21 +160,63 @@ export async function apply(ctx) {
166
160
  // happening". Only the first event counts, and only from the session the
167
161
  // handoff asked in, so a busy conversation elsewhere cannot make a quiet
168
162
  // handoff look alive.
163
+ // A prompted turn can fail after the request was accepted (a provider error,
164
+ // for example). The browser snapshot that would show it is memory-only, so
165
+ // stamp the durable request record here; the map page can then explain and
166
+ // retry even after a restart.
167
+ ctx.on('agent/error', ({ agent, error }) => {
168
+ const reason = error instanceof Error ? error.message : String(error);
169
+ journal.append('agent.error', { sessionId: String(agent.session.id), error: reason.slice(0, 240) });
170
+ void stampPathRequestFailure(state, String(agent.session.id), reason).catch(() => { });
171
+ });
172
+ const toolCalls = new Map();
169
173
  ctx.on('session/event', (session, event) => {
170
- const handoff = state.activeHandoff();
174
+ const sessionId = String(session.id);
175
+ // Tool metadata only: the name, the operation and the duration. Arguments
176
+ // stay out of the journal, so no lesson or goal text is recorded.
177
+ if (event.type === 'tool/call') {
178
+ const data = event.data;
179
+ if (typeof data.name === 'string' && data.name.startsWith('udt_')) {
180
+ let operation;
181
+ try {
182
+ const parsed = JSON.parse(data.arguments ?? '');
183
+ if (typeof parsed.op === 'string')
184
+ operation = parsed.op;
185
+ else if (typeof parsed.action === 'string')
186
+ operation = parsed.action;
187
+ }
188
+ catch { /* the tool gets to explain its own arguments */ }
189
+ journal.append('tool.call', { sessionId, tool: data.name, ...(operation === undefined ? {} : { op: operation }) });
190
+ if (data.callId !== undefined)
191
+ toolCalls.set(`${sessionId}:${data.callId}`, { name: data.name, start: Date.now() });
192
+ }
193
+ }
194
+ else if (event.type === 'tool/result') {
195
+ const message = event.data.message;
196
+ const key = message?.toolCallId === undefined ? undefined : `${sessionId}:${message.toolCallId}`;
197
+ const tracked = key === undefined ? undefined : toolCalls.get(key);
198
+ if (tracked !== undefined && key !== undefined) {
199
+ toolCalls.delete(key);
200
+ journal.append('tool.result', { sessionId, tool: tracked.name, durationMs: Math.max(0, Date.now() - tracked.start) });
201
+ }
202
+ }
203
+ // The event says which conversation produced it, so the handoff is looked
204
+ // up in that conversation rather than in the course at large. A busy
205
+ // conversation elsewhere cannot make a quiet handoff look alive — and two
206
+ // conversations working on the same target keep separate records.
207
+ const handoff = state.activeHandoff(sessionId);
171
208
  if (handoff === undefined || handoff.status !== 'prompted')
172
209
  return;
173
- if (handoff.sessionId !== undefined && String(session.id) !== handoff.sessionId)
174
- return;
175
210
  void event;
176
211
  // Atomic, and re-checked inside the transform: the lesson write races this
177
212
  // listener, and a plain read-modify-write here would clobber a handoff that
178
213
  // had already become ready.
179
214
  void state
180
- .updateHandoff(handoff.targetNodeId, (current) => current.status === 'prompted' ? withActivity(current, new Date().toISOString()) : current)
215
+ .updateHandoff(sessionId, handoff.targetNodeId, (current) => current.status === 'prompted' ? withActivity(current, new Date().toISOString()) : current)
181
216
  .catch(() => { });
182
217
  });
183
- registerTools({ tools }, state);
184
- ctx.logger.debug(`[diagnostic-tutor] ready — domain "${state.name}" v${state.version}, 4 tools registered`);
218
+ const toolCount = registerTools({ tools }, state);
219
+ journal.append('plugin.ready', { tools: toolCount, version: packageVersion() });
220
+ ctx.logger.debug(`[diagnostic-tutor] ready — domain "${state.name}" v${state.version}, ${toolCount} tools registered`);
185
221
  }
186
222
  //# sourceMappingURL=index.js.map
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAIH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAElD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC1C,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAE5D;;;GAGG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,kBAAkB,CAAA;AAEtC;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,OAAO,EAAE,eAAe,CAAC,CAAA;AAEhD;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,GAAY;IACtC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAA;IAErD,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IAC9B,MAAM,QAAQ,GAAG,GAAG,CAAC,GAAG,CAAC,eAAe,CAAC,CAAA;IACzC,IAAI,CAAC,KAAK,IAAI,CAAC,QAAQ,EAAE,CAAC;QACxB,mEAAmE;QACnE,GAAG,CAAC,MAAM,CAAC,KAAK,CACd,kDAAkD,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,gBAAgB,EAAE,CAC7G,CAAA;QACD,OAAM;IACR,CAAC;IAED,0EAA0E;IAC1E,6EAA6E;IAC7E,4EAA4E;IAC5E,uEAAuE;IACvE,yEAAyE;IACzE,6DAA6D;IAC7D,EAAE;IACF,4EAA4E;IAC5E,wEAAwE;IACxE,6EAA6E;IAC7E,kCAAkC;IAClC,IAAI,KAAK,CAAA;IACT,IAAI,CAAC;QACH,KAAK,GAAG,MAAM,WAAW,CAAC,QAAQ,CAAC,CAAA;QACnC,sEAAsE;QACtE,MAAM,KAAK,CAAC,aAAa,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAA;IACrD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,GAAG,CAAC,MAAM,CAAC,KAAK,CACd,qDAAqD,eAAe,MAAO,KAAe,CAAC,OAAO,IAAI;YACpG,6DAA6D;YAC7D,iGAAiG,CACpG,CAAA;QACD,OAAM;IACR,CAAC;IAED,0EAA0E;IAC1E,6EAA6E;IAC7E,2EAA2E;IAC3E,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAA;IAErC,sEAAsE;IACtE,4EAA4E;IAC5E,0EAA0E;IAC1E,0EAA0E;IAC1E,iDAAiD;IACjD,EAAE;IACF,wEAAwE;IACxE,8EAA8E;IAC9E,6DAA6D;IAC7D,yEAAyE;IACzE,uEAAuE;IACvE,MAAM,GAAG,GAAG,MAAM,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAA;IACnD,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;QAClB,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,sCAAsC,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IAClF,CAAC;SAAM,CAAC;QACN,0EAA0E;QAC1E,yEAAyE;QACzE,sEAAsE;QACtE,qEAAqE;QACrE,GAAG,CAAC,MAAM,CAAC,IAAI,CACb,yDAAyD,iBAAiB,CAAC,GAAG,CAAC,IAAI;YACjF,qFAAqF;YACrF,mEAAmE,CACtE,CAAA;IACH,CAAC;IAED,2DAA2D;IAC3D,EAAE;IACF,4EAA4E;IAC5E,2EAA2E;IAC3E,6EAA6E;IAC7E,4EAA4E;IAC5E,0EAA0E;IAC1E,4CAA4C;IAC5C,MAAM,QAAQ,GAAG,CAAC,MAAe,EAAQ,EAAE;QACzC,IAAI,CAAC,MAAM;YAAE,OAAM;QACnB,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,CACd,WAAW,CAAC,MAAuB,EAAE;YACnC,KAAK;YACL,MAAM,EAAE,CAAC,SAAS,EAAE,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,CAAC;YAChE,oEAAoE;YACpE,uEAAuE;YACvE,uEAAuE;YACvE,uCAAuC;YACvC,aAAa,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI;SACxE,CAAC,CACH,CAAA;QACD,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,6CAA6C,UAAU,EAAE,CAAC,CAAA;IAC7E,CAAC,CAAA;IACD,MAAM,SAAS,GAAG,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;IACtC,IAAI,SAAS;QAAE,QAAQ,CAAC,SAAS,CAAC,CAAA;;QAC7B,GAAG,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAA;IAE7E,qCAAqC;IACrC,EAAE;IACF,8EAA8E;IAC9E,qEAAqE;IACrE,yEAAyE;IACzE,yEAAyE;IACzE,sBAAsB;IACtB,GAAG,CAAC,EAAE,CAAC,eAAe,EAAE,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE;QACzC,MAAM,OAAO,GAAG,KAAK,CAAC,aAAa,EAAE,CAAA;QACrC,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,CAAC,MAAM,KAAK,UAAU;YAAE,OAAM;QAClE,IAAI,OAAO,CAAC,SAAS,KAAK,SAAS,IAAI,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC,SAAS;YAAE,OAAM;QACvF,KAAK,KAAK,CAAA;QACV,2EAA2E;QAC3E,4EAA4E;QAC5E,4BAA4B;QAC5B,KAAK,KAAK;aACP,aAAa,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,OAAO,EAAE,EAAE,CAC/C,OAAO,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,YAAY,CAAC,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAC1F;aACA,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;IACpB,CAAC,CAAC,CAAA;IAEF,aAAa,CAAC,EAAE,KAAK,EAAE,EAAE,KAAK,CAAC,CAAA;IAC/B,GAAG,CAAC,MAAM,CAAC,KAAK,CACd,sCAAsC,KAAK,CAAC,IAAI,MAAM,KAAK,CAAC,OAAO,sBAAsB,CAC1F,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}