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
|
@@ -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
|
-
|
|
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 {
|
package/lib/handoff.js.map
CHANGED
|
@@ -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
|
|
9
|
+
* Scope in v0.2.0 (content/input/review loop on top of the published learning runtime)
|
|
10
10
|
* ---------------------------------------------------------------------------
|
|
11
|
-
*
|
|
12
|
-
*
|
|
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
|
-
*
|
|
17
|
-
* -
|
|
18
|
-
* -
|
|
19
|
-
*
|
|
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 {
|
|
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`.
|
|
57
|
-
*
|
|
58
|
-
* them
|
|
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
|
-
//
|
|
112
|
-
//
|
|
113
|
-
|
|
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) =>
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|