@vibe-hero/server 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.
- package/LICENSE +190 -0
- package/README.md +151 -0
- package/dist/catalog/bundled/claude-code/.gitkeep +0 -0
- package/dist/catalog/bundled/claude-code/context-management.yaml +302 -0
- package/dist/catalog/bundled/claude-code/planning.yaml +313 -0
- package/dist/catalog/bundled/claude-code/subagents.yaml +357 -0
- package/dist/catalog/bundled/general/.gitkeep +0 -0
- package/dist/catalog/bundled/general/_placeholder.yaml +39 -0
- package/dist/catalog/bundled/general/task-decomposition.yaml +390 -0
- package/dist/catalog/bundled/index.d.ts +39 -0
- package/dist/catalog/bundled/index.d.ts.map +1 -0
- package/dist/catalog/bundled/index.js +41 -0
- package/dist/catalog/bundled/index.js.map +1 -0
- package/dist/catalog/fetcher.d.ts +201 -0
- package/dist/catalog/fetcher.d.ts.map +1 -0
- package/dist/catalog/fetcher.js +452 -0
- package/dist/catalog/fetcher.js.map +1 -0
- package/dist/catalog/loader.d.ts +165 -0
- package/dist/catalog/loader.d.ts.map +1 -0
- package/dist/catalog/loader.js +241 -0
- package/dist/catalog/loader.js.map +1 -0
- package/dist/catalog/resolve.d.ts +85 -0
- package/dist/catalog/resolve.d.ts.map +1 -0
- package/dist/catalog/resolve.js +103 -0
- package/dist/catalog/resolve.js.map +1 -0
- package/dist/cli/getOffer.d.ts +38 -0
- package/dist/cli/getOffer.d.ts.map +1 -0
- package/dist/cli/getOffer.js +150 -0
- package/dist/cli/getOffer.js.map +1 -0
- package/dist/cli/index.d.ts +46 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +88 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/config.d.ts +34 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +63 -0
- package/dist/config.js.map +1 -0
- package/dist/engine/elo.d.ts +76 -0
- package/dist/engine/elo.d.ts.map +1 -0
- package/dist/engine/elo.js +79 -0
- package/dist/engine/elo.js.map +1 -0
- package/dist/engine/graduation.d.ts +108 -0
- package/dist/engine/graduation.d.ts.map +1 -0
- package/dist/engine/graduation.js +161 -0
- package/dist/engine/graduation.js.map +1 -0
- package/dist/engine/lapse.d.ts +80 -0
- package/dist/engine/lapse.d.ts.map +1 -0
- package/dist/engine/lapse.js +125 -0
- package/dist/engine/lapse.js.map +1 -0
- package/dist/engine/selection.d.ts +84 -0
- package/dist/engine/selection.d.ts.map +1 -0
- package/dist/engine/selection.js +119 -0
- package/dist/engine/selection.js.map +1 -0
- package/dist/grading/deterministic.d.ts +102 -0
- package/dist/grading/deterministic.d.ts.map +1 -0
- package/dist/grading/deterministic.js +118 -0
- package/dist/grading/deterministic.js.map +1 -0
- package/dist/grading/freeform.d.ts +64 -0
- package/dist/grading/freeform.d.ts.map +1 -0
- package/dist/grading/freeform.js +85 -0
- package/dist/grading/freeform.js.map +1 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +91 -0
- package/dist/index.js.map +1 -0
- package/dist/observation/hookEvents.d.ts +113 -0
- package/dist/observation/hookEvents.d.ts.map +1 -0
- package/dist/observation/hookEvents.js +170 -0
- package/dist/observation/hookEvents.js.map +1 -0
- package/dist/observation/offers.d.ts +215 -0
- package/dist/observation/offers.d.ts.map +1 -0
- package/dist/observation/offers.js +327 -0
- package/dist/observation/offers.js.map +1 -0
- package/dist/observation/source.d.ts +133 -0
- package/dist/observation/source.d.ts.map +1 -0
- package/dist/observation/source.js +105 -0
- package/dist/observation/source.js.map +1 -0
- package/dist/profile/migrate.d.ts +122 -0
- package/dist/profile/migrate.d.ts.map +1 -0
- package/dist/profile/migrate.js +147 -0
- package/dist/profile/migrate.js.map +1 -0
- package/dist/profile/store.d.ts +84 -0
- package/dist/profile/store.d.ts.map +1 -0
- package/dist/profile/store.js +267 -0
- package/dist/profile/store.js.map +1 -0
- package/dist/schemas/common.d.ts +95 -0
- package/dist/schemas/common.d.ts.map +1 -0
- package/dist/schemas/common.js +106 -0
- package/dist/schemas/common.js.map +1 -0
- package/dist/schemas/content.d.ts +828 -0
- package/dist/schemas/content.d.ts.map +1 -0
- package/dist/schemas/content.js +219 -0
- package/dist/schemas/content.js.map +1 -0
- package/dist/schemas/profile.d.ts +599 -0
- package/dist/schemas/profile.d.ts.map +1 -0
- package/dist/schemas/profile.js +177 -0
- package/dist/schemas/profile.js.map +1 -0
- package/dist/schemas/tools.d.ts +1581 -0
- package/dist/schemas/tools.d.ts.map +1 -0
- package/dist/schemas/tools.js +286 -0
- package/dist/schemas/tools.js.map +1 -0
- package/dist/tools/config.d.ts +51 -0
- package/dist/tools/config.d.ts.map +1 -0
- package/dist/tools/config.js +104 -0
- package/dist/tools/config.js.map +1 -0
- package/dist/tools/gate.d.ts +50 -0
- package/dist/tools/gate.d.ts.map +1 -0
- package/dist/tools/gate.js +67 -0
- package/dist/tools/gate.js.map +1 -0
- package/dist/tools/guidance.d.ts +36 -0
- package/dist/tools/guidance.d.ts.map +1 -0
- package/dist/tools/guidance.js +117 -0
- package/dist/tools/guidance.js.map +1 -0
- package/dist/tools/listTopics.d.ts +55 -0
- package/dist/tools/listTopics.d.ts.map +1 -0
- package/dist/tools/listTopics.js +78 -0
- package/dist/tools/listTopics.js.map +1 -0
- package/dist/tools/offers.d.ts +60 -0
- package/dist/tools/offers.d.ts.map +1 -0
- package/dist/tools/offers.js +152 -0
- package/dist/tools/offers.js.map +1 -0
- package/dist/tools/placeholders.d.ts +27 -0
- package/dist/tools/placeholders.d.ts.map +1 -0
- package/dist/tools/placeholders.js +49 -0
- package/dist/tools/placeholders.js.map +1 -0
- package/dist/tools/recordObservation.d.ts +52 -0
- package/dist/tools/recordObservation.d.ts.map +1 -0
- package/dist/tools/recordObservation.js +87 -0
- package/dist/tools/recordObservation.js.map +1 -0
- package/dist/tools/startQuiz.d.ts +82 -0
- package/dist/tools/startQuiz.d.ts.map +1 -0
- package/dist/tools/startQuiz.js +180 -0
- package/dist/tools/startQuiz.js.map +1 -0
- package/dist/tools/status.d.ts +59 -0
- package/dist/tools/status.d.ts.map +1 -0
- package/dist/tools/status.js +133 -0
- package/dist/tools/status.js.map +1 -0
- package/dist/tools/submitAnswer.d.ts +156 -0
- package/dist/tools/submitAnswer.d.ts.map +1 -0
- package/dist/tools/submitAnswer.js +402 -0
- package/dist/tools/submitAnswer.js.map +1 -0
- package/dist/tools/types.d.ts +82 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +48 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/tools/us2/standing.d.ts +111 -0
- package/dist/tools/us2/standing.d.ts.map +1 -0
- package/dist/tools/us2/standing.js +143 -0
- package/dist/tools/us2/standing.js.map +1 -0
- package/package.json +62 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Real `submit_answer` tool module — DETERMINISTIC + FREE-FORM paths
|
|
3
|
+
* (T032 deterministic, US-1; T048 free-form, US-4).
|
|
4
|
+
*
|
|
5
|
+
* The core scoring entry point. For a deterministic item (`multiple_choice` /
|
|
6
|
+
* `short_answer`) it: finds the live {@link QuizRecord} + catalog item by
|
|
7
|
+
* `quizId`/`itemId`, grades it in-engine via {@link gradeMultipleChoice} /
|
|
8
|
+
* {@link gradeShortAnswer} (FR-011, instant + reproducible — SC-004), updates the
|
|
9
|
+
* learner's Elo ability against the item's FIXED authored difficulty
|
|
10
|
+
* ({@link updateAbility}; item difficulty never self-updates — E3), and persists
|
|
11
|
+
* the result.
|
|
12
|
+
*
|
|
13
|
+
* Privacy (FR-018): the persisted {@link AnsweredItem} carries ONLY derived
|
|
14
|
+
* fields — `itemId`, `tier`, `difficulty`, `grade`, `score`,
|
|
15
|
+
* `gradedBy: "engine"`, `answeredAt`. The raw answer text / chosen id is used to
|
|
16
|
+
* grade and then DISCARDED; it never reaches disk. The continuous `score` drives
|
|
17
|
+
* the Elo update; the binary {@link Grade} is the projection persisted in
|
|
18
|
+
* history.
|
|
19
|
+
*
|
|
20
|
+
* Persistence (atomic, FR-023a): a single {@link updateProfile} read-modify-write
|
|
21
|
+
* appends the {@link AnsweredItem} to the matching {@link QuizRecord} and updates
|
|
22
|
+
* the {@link AbilityEstimate} (`value`, `itemsSeen++`, `lastAssessedAt`, push
|
|
23
|
+
* `itemId` to `lastItemIds`). Re-running on the same answer is naturally
|
|
24
|
+
* idempotent in grade (SC-004) though it appends another graded item — quiz
|
|
25
|
+
* sessions are append-only event logs.
|
|
26
|
+
*
|
|
27
|
+
* Graduation (T046, US-3): after the ability update, the PURE
|
|
28
|
+
* {@link evaluateGraduation} engine decides whether this graded item promotes
|
|
29
|
+
* the learner to the next tier (hysteresis band + `dwell` consecutive
|
|
30
|
+
* qualifying items — SC-014) or demotes/flags below the lower band (FR-008/009).
|
|
31
|
+
* The decision updates `profile.graduations[key]` and the dwell counter on the
|
|
32
|
+
* AbilityEstimate; on a promotion a proactive spaced {@link ReviewEntry} is
|
|
33
|
+
* enqueued (FR-010) so a one-time streak is later re-verified, and the
|
|
34
|
+
* `graduation` field is surfaced on the result so the host informs the user.
|
|
35
|
+
*
|
|
36
|
+
* Free-form path (T048, US-4): when the item is `free_form` and the input carries
|
|
37
|
+
* a per-criterion `verdict`, the host agent's verdict is scored by the PURE
|
|
38
|
+
* {@link scoreVerdict} against the item's MCP-supplied rubric — the MCP computes
|
|
39
|
+
* the score (fraction of criteria met) and derives the grade; the agent never
|
|
40
|
+
* returns a bare score/boolean (anti-gaming, FR-012/013). The graded result then
|
|
41
|
+
* flows through the SAME ability-update / persistence / graduation pipeline as a
|
|
42
|
+
* deterministic grade, recorded as `gradedBy: "host_agent"` with NO raw answer
|
|
43
|
+
* text (FR-018). The two paths cross-guard: a deterministic answer on a free-form
|
|
44
|
+
* item, or a free-form verdict on a deterministic item, is rejected with a clear
|
|
45
|
+
* error.
|
|
46
|
+
*
|
|
47
|
+
* Gated (FR-032): NOT exempt — the gate runs before this handler.
|
|
48
|
+
*
|
|
49
|
+
* Exposed as a `dirOverride`-closing factory mirroring `config.ts` / `status.ts`.
|
|
50
|
+
*
|
|
51
|
+
* Source of truth: specs/001-vibe-hero-mvp/contracts/mcp-tools.md
|
|
52
|
+
* (`submit_answer` deterministic + free-form paths), spec.md FR-005 / FR-011 /
|
|
53
|
+
* FR-012 / FR-013 / FR-018 / SC-004, data-model.md (AnsweredItem /
|
|
54
|
+
* AbilityEstimate), research.md (OD-002 / OD-005).
|
|
55
|
+
*/
|
|
56
|
+
import type { CatalogLoadResult } from "../catalog/loader.js";
|
|
57
|
+
import { z } from "zod";
|
|
58
|
+
import { type AnyToolModule } from "./types.js";
|
|
59
|
+
/**
|
|
60
|
+
* Permissive OBJECT schema registered with the SDK. `submit_answer`'s contract
|
|
61
|
+
* input is a *union* (deterministic answer XOR free-form verdict) which has no
|
|
62
|
+
* single `.shape` for `registerTool`, so the wire schema is a superset:
|
|
63
|
+
* `quizId` + `itemId` required, `answer`/`verdict` optional. The handler
|
|
64
|
+
* re-validates against the authoritative {@link SubmitAnswerInputSchema} union
|
|
65
|
+
* (discriminated structurally) before grading — a malformed payload is rejected
|
|
66
|
+
* there, not silently mis-graded.
|
|
67
|
+
*/
|
|
68
|
+
export declare const SubmitAnswerToolInputSchema: z.ZodObject<{
|
|
69
|
+
quizId: z.ZodString;
|
|
70
|
+
itemId: z.ZodString;
|
|
71
|
+
answer: z.ZodOptional<z.ZodObject<{
|
|
72
|
+
choiceId: z.ZodOptional<z.ZodString>;
|
|
73
|
+
text: z.ZodOptional<z.ZodString>;
|
|
74
|
+
}, "strip", z.ZodTypeAny, {
|
|
75
|
+
text?: string | undefined;
|
|
76
|
+
choiceId?: string | undefined;
|
|
77
|
+
}, {
|
|
78
|
+
text?: string | undefined;
|
|
79
|
+
choiceId?: string | undefined;
|
|
80
|
+
}>>;
|
|
81
|
+
verdict: z.ZodOptional<z.ZodObject<{
|
|
82
|
+
criteria: z.ZodArray<z.ZodObject<{
|
|
83
|
+
id: z.ZodString;
|
|
84
|
+
met: z.ZodBoolean;
|
|
85
|
+
justification: z.ZodString;
|
|
86
|
+
}, "strip", z.ZodTypeAny, {
|
|
87
|
+
id: string;
|
|
88
|
+
met: boolean;
|
|
89
|
+
justification: string;
|
|
90
|
+
}, {
|
|
91
|
+
id: string;
|
|
92
|
+
met: boolean;
|
|
93
|
+
justification: string;
|
|
94
|
+
}>, "many">;
|
|
95
|
+
}, "strip", z.ZodTypeAny, {
|
|
96
|
+
criteria: {
|
|
97
|
+
id: string;
|
|
98
|
+
met: boolean;
|
|
99
|
+
justification: string;
|
|
100
|
+
}[];
|
|
101
|
+
}, {
|
|
102
|
+
criteria: {
|
|
103
|
+
id: string;
|
|
104
|
+
met: boolean;
|
|
105
|
+
justification: string;
|
|
106
|
+
}[];
|
|
107
|
+
}>>;
|
|
108
|
+
}, "strip", z.ZodTypeAny, {
|
|
109
|
+
itemId: string;
|
|
110
|
+
quizId: string;
|
|
111
|
+
answer?: {
|
|
112
|
+
text?: string | undefined;
|
|
113
|
+
choiceId?: string | undefined;
|
|
114
|
+
} | undefined;
|
|
115
|
+
verdict?: {
|
|
116
|
+
criteria: {
|
|
117
|
+
id: string;
|
|
118
|
+
met: boolean;
|
|
119
|
+
justification: string;
|
|
120
|
+
}[];
|
|
121
|
+
} | undefined;
|
|
122
|
+
}, {
|
|
123
|
+
itemId: string;
|
|
124
|
+
quizId: string;
|
|
125
|
+
answer?: {
|
|
126
|
+
text?: string | undefined;
|
|
127
|
+
choiceId?: string | undefined;
|
|
128
|
+
} | undefined;
|
|
129
|
+
verdict?: {
|
|
130
|
+
criteria: {
|
|
131
|
+
id: string;
|
|
132
|
+
met: boolean;
|
|
133
|
+
justification: string;
|
|
134
|
+
}[];
|
|
135
|
+
} | undefined;
|
|
136
|
+
}>;
|
|
137
|
+
/** Inferred wire-input type for `submit_answer` (permissive superset). */
|
|
138
|
+
export type SubmitAnswerToolInput = z.infer<typeof SubmitAnswerToolInputSchema>;
|
|
139
|
+
/**
|
|
140
|
+
* A catalog source: returns the loaded topics (+ any per-file errors). Defaults
|
|
141
|
+
* to {@link loadBundledCatalog}; tests inject a fixture-dir loader so grading can
|
|
142
|
+
* resolve a topic with ≥5 deterministic items without disturbing the shared
|
|
143
|
+
* bundled snapshot.
|
|
144
|
+
*/
|
|
145
|
+
export type CatalogLoader = () => CatalogLoadResult;
|
|
146
|
+
/**
|
|
147
|
+
* Build the `submit_answer` tool module (US-1 deterministic + US-4 free-form).
|
|
148
|
+
*
|
|
149
|
+
* @param dirOverride - Profile-directory override (test seam); see `profileDir`.
|
|
150
|
+
* @param catalogLoader - Catalog source override (test seam); defaults to the
|
|
151
|
+
* bundled snapshot {@link loadBundledCatalog}.
|
|
152
|
+
*/
|
|
153
|
+
export declare const makeSubmitAnswerTool: (dirOverride?: string, catalogLoader?: CatalogLoader) => AnyToolModule;
|
|
154
|
+
/** Default `submit_answer` module (env / `~/.vibe-hero`), used by the registry. */
|
|
155
|
+
export declare const submitAnswerTool: AnyToolModule;
|
|
156
|
+
//# sourceMappingURL=submitAnswer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"submitAnswer.d.ts","sourceRoot":"","sources":["../../src/tools/submitAnswer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAwB9D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAOxB,OAAO,EAAc,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAE5D;;;;;;;;GAQG;AACH,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoBtC,CAAC;AACH,0EAA0E;AAC1E,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AA0ShF;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,iBAAiB,CAAC;AAiDpD;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,GAC/B,cAAc,MAAM,EACpB,gBAAe,aAAkC,KAChD,aAwEC,CAAC;AAEL,mFAAmF;AACnF,eAAO,MAAM,gBAAgB,EAAE,aAAsC,CAAC"}
|
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Real `submit_answer` tool module — DETERMINISTIC + FREE-FORM paths
|
|
3
|
+
* (T032 deterministic, US-1; T048 free-form, US-4).
|
|
4
|
+
*
|
|
5
|
+
* The core scoring entry point. For a deterministic item (`multiple_choice` /
|
|
6
|
+
* `short_answer`) it: finds the live {@link QuizRecord} + catalog item by
|
|
7
|
+
* `quizId`/`itemId`, grades it in-engine via {@link gradeMultipleChoice} /
|
|
8
|
+
* {@link gradeShortAnswer} (FR-011, instant + reproducible — SC-004), updates the
|
|
9
|
+
* learner's Elo ability against the item's FIXED authored difficulty
|
|
10
|
+
* ({@link updateAbility}; item difficulty never self-updates — E3), and persists
|
|
11
|
+
* the result.
|
|
12
|
+
*
|
|
13
|
+
* Privacy (FR-018): the persisted {@link AnsweredItem} carries ONLY derived
|
|
14
|
+
* fields — `itemId`, `tier`, `difficulty`, `grade`, `score`,
|
|
15
|
+
* `gradedBy: "engine"`, `answeredAt`. The raw answer text / chosen id is used to
|
|
16
|
+
* grade and then DISCARDED; it never reaches disk. The continuous `score` drives
|
|
17
|
+
* the Elo update; the binary {@link Grade} is the projection persisted in
|
|
18
|
+
* history.
|
|
19
|
+
*
|
|
20
|
+
* Persistence (atomic, FR-023a): a single {@link updateProfile} read-modify-write
|
|
21
|
+
* appends the {@link AnsweredItem} to the matching {@link QuizRecord} and updates
|
|
22
|
+
* the {@link AbilityEstimate} (`value`, `itemsSeen++`, `lastAssessedAt`, push
|
|
23
|
+
* `itemId` to `lastItemIds`). Re-running on the same answer is naturally
|
|
24
|
+
* idempotent in grade (SC-004) though it appends another graded item — quiz
|
|
25
|
+
* sessions are append-only event logs.
|
|
26
|
+
*
|
|
27
|
+
* Graduation (T046, US-3): after the ability update, the PURE
|
|
28
|
+
* {@link evaluateGraduation} engine decides whether this graded item promotes
|
|
29
|
+
* the learner to the next tier (hysteresis band + `dwell` consecutive
|
|
30
|
+
* qualifying items — SC-014) or demotes/flags below the lower band (FR-008/009).
|
|
31
|
+
* The decision updates `profile.graduations[key]` and the dwell counter on the
|
|
32
|
+
* AbilityEstimate; on a promotion a proactive spaced {@link ReviewEntry} is
|
|
33
|
+
* enqueued (FR-010) so a one-time streak is later re-verified, and the
|
|
34
|
+
* `graduation` field is surfaced on the result so the host informs the user.
|
|
35
|
+
*
|
|
36
|
+
* Free-form path (T048, US-4): when the item is `free_form` and the input carries
|
|
37
|
+
* a per-criterion `verdict`, the host agent's verdict is scored by the PURE
|
|
38
|
+
* {@link scoreVerdict} against the item's MCP-supplied rubric — the MCP computes
|
|
39
|
+
* the score (fraction of criteria met) and derives the grade; the agent never
|
|
40
|
+
* returns a bare score/boolean (anti-gaming, FR-012/013). The graded result then
|
|
41
|
+
* flows through the SAME ability-update / persistence / graduation pipeline as a
|
|
42
|
+
* deterministic grade, recorded as `gradedBy: "host_agent"` with NO raw answer
|
|
43
|
+
* text (FR-018). The two paths cross-guard: a deterministic answer on a free-form
|
|
44
|
+
* item, or a free-form verdict on a deterministic item, is rejected with a clear
|
|
45
|
+
* error.
|
|
46
|
+
*
|
|
47
|
+
* Gated (FR-032): NOT exempt — the gate runs before this handler.
|
|
48
|
+
*
|
|
49
|
+
* Exposed as a `dirOverride`-closing factory mirroring `config.ts` / `status.ts`.
|
|
50
|
+
*
|
|
51
|
+
* Source of truth: specs/001-vibe-hero-mvp/contracts/mcp-tools.md
|
|
52
|
+
* (`submit_answer` deterministic + free-form paths), spec.md FR-005 / FR-011 /
|
|
53
|
+
* FR-012 / FR-013 / FR-018 / SC-004, data-model.md (AnsweredItem /
|
|
54
|
+
* AbilityEstimate), research.md (OD-002 / OD-005).
|
|
55
|
+
*/
|
|
56
|
+
import { ASSESSMENT_CONFIG } from "../config.js";
|
|
57
|
+
import { loadBundledCatalog } from "../catalog/bundled/index.js";
|
|
58
|
+
import { updateAbility } from "../engine/elo.js";
|
|
59
|
+
import { evaluateGraduation, } from "../engine/graduation.js";
|
|
60
|
+
import { gradeMultipleChoice, gradeShortAnswer, toGrade, } from "../grading/deterministic.js";
|
|
61
|
+
import { scoreVerdict } from "../grading/freeform.js";
|
|
62
|
+
import { loadProfile, updateProfile } from "../profile/store.js";
|
|
63
|
+
import { abilityKey } from "../schemas/common.js";
|
|
64
|
+
import { z } from "zod";
|
|
65
|
+
import { SubmitAnswerInputSchema, } from "../schemas/tools.js";
|
|
66
|
+
import { defineTool } from "./types.js";
|
|
67
|
+
/**
|
|
68
|
+
* Permissive OBJECT schema registered with the SDK. `submit_answer`'s contract
|
|
69
|
+
* input is a *union* (deterministic answer XOR free-form verdict) which has no
|
|
70
|
+
* single `.shape` for `registerTool`, so the wire schema is a superset:
|
|
71
|
+
* `quizId` + `itemId` required, `answer`/`verdict` optional. The handler
|
|
72
|
+
* re-validates against the authoritative {@link SubmitAnswerInputSchema} union
|
|
73
|
+
* (discriminated structurally) before grading — a malformed payload is rejected
|
|
74
|
+
* there, not silently mis-graded.
|
|
75
|
+
*/
|
|
76
|
+
export const SubmitAnswerToolInputSchema = z.object({
|
|
77
|
+
quizId: z.string(),
|
|
78
|
+
itemId: z.string(),
|
|
79
|
+
answer: z
|
|
80
|
+
.object({
|
|
81
|
+
choiceId: z.string().optional(),
|
|
82
|
+
text: z.string().optional(),
|
|
83
|
+
})
|
|
84
|
+
.optional(),
|
|
85
|
+
verdict: z
|
|
86
|
+
.object({
|
|
87
|
+
criteria: z.array(z.object({
|
|
88
|
+
id: z.string(),
|
|
89
|
+
met: z.boolean(),
|
|
90
|
+
justification: z.string(),
|
|
91
|
+
})),
|
|
92
|
+
})
|
|
93
|
+
.optional(),
|
|
94
|
+
});
|
|
95
|
+
/**
|
|
96
|
+
* A type guard for the deterministic input variant (carries `answer`, not
|
|
97
|
+
* `verdict`). Free-form verdicts route to the T048 path; until then they are
|
|
98
|
+
* rejected so a free-form answer never silently mis-grades.
|
|
99
|
+
*/
|
|
100
|
+
const isDeterministic = (input) => "answer" in input;
|
|
101
|
+
/** Find the catalog item with `itemId` across all topics; returns its topic too. */
|
|
102
|
+
const findItem = (topics, itemId) => {
|
|
103
|
+
for (const topic of topics) {
|
|
104
|
+
const item = topic.items.find((i) => i.id === itemId);
|
|
105
|
+
if (item !== undefined)
|
|
106
|
+
return { topic, item };
|
|
107
|
+
}
|
|
108
|
+
return undefined;
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* Grade a deterministic item, returning the continuous score plus (for MC) the
|
|
112
|
+
* authored correct choice id so the caller can surface `correctAnswer`.
|
|
113
|
+
*
|
|
114
|
+
* @throws {Error} if `item` is `free_form` — a deterministic answer was submitted
|
|
115
|
+
* for a free-form item; the caller must send a per-criterion `verdict` instead
|
|
116
|
+
* (cross-guard, T048).
|
|
117
|
+
*/
|
|
118
|
+
const gradeDeterministic = (item, answer) => {
|
|
119
|
+
switch (item.type) {
|
|
120
|
+
case "multiple_choice": {
|
|
121
|
+
const { score, correctChoiceId } = gradeMultipleChoice(item, answer.choiceId);
|
|
122
|
+
return { score, correctAnswer: correctChoiceId };
|
|
123
|
+
}
|
|
124
|
+
case "short_answer": {
|
|
125
|
+
const { score } = gradeShortAnswer(item, answer.text);
|
|
126
|
+
return { score };
|
|
127
|
+
}
|
|
128
|
+
case "free_form":
|
|
129
|
+
throw new Error(`submit_answer: item ${JSON.stringify(item.id)} is free_form; submit a per-criterion \`verdict\`, not a deterministic \`answer\` (T048)`);
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* Build the AnsweredItem persisted in history. Derived fields ONLY — never the
|
|
134
|
+
* raw answer text / chosen id / verdict justifications (FR-018). The item's
|
|
135
|
+
* authored `tier` + fixed `difficulty` are recorded so history is
|
|
136
|
+
* self-describing without re-joining the catalog. `gradedBy` records WHO graded:
|
|
137
|
+
* the engine (deterministic) or the host agent (free-form verdict).
|
|
138
|
+
*/
|
|
139
|
+
const buildAnsweredItem = (item, score, grade, gradedBy, answeredAt) => ({
|
|
140
|
+
itemId: item.id,
|
|
141
|
+
tier: item.tier,
|
|
142
|
+
difficulty: item.difficulty,
|
|
143
|
+
grade,
|
|
144
|
+
score,
|
|
145
|
+
gradedBy,
|
|
146
|
+
answeredAt,
|
|
147
|
+
});
|
|
148
|
+
/**
|
|
149
|
+
* Advance an ability estimate by one graded item: apply the Elo update against
|
|
150
|
+
* the item's fixed difficulty, stamp `lastAssessedAt`, and push the item id onto
|
|
151
|
+
* `lastItemIds` (so the next `start_quiz` avoids re-serving it). A cold estimate
|
|
152
|
+
* (no prior entry) starts from {@link ASSESSMENT_CONFIG.startingAbility} with
|
|
153
|
+
* zero items seen.
|
|
154
|
+
*/
|
|
155
|
+
const advanceEstimate = (prior, item, score, answeredAt) => {
|
|
156
|
+
const before = prior?.value ?? ASSESSMENT_CONFIG.startingAbility;
|
|
157
|
+
const itemsSeen = prior?.itemsSeen ?? 0;
|
|
158
|
+
const update = updateAbility(before, itemsSeen, item.difficulty, score);
|
|
159
|
+
return {
|
|
160
|
+
before,
|
|
161
|
+
estimate: {
|
|
162
|
+
value: update.value,
|
|
163
|
+
itemsSeen: update.itemsSeen,
|
|
164
|
+
lastAssessedAt: answeredAt,
|
|
165
|
+
lastItemIds: [...(prior?.lastItemIds ?? []), item.id],
|
|
166
|
+
// Carry the prior dwell forward unchanged here; the graduation step
|
|
167
|
+
// (applyGraduation) reads it, then overwrites with the engine's next
|
|
168
|
+
// dwell so the consecutive-streak counter advances/resets per item.
|
|
169
|
+
dwell: prior?.dwell ?? 0,
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
};
|
|
173
|
+
/**
|
|
174
|
+
* Apply the PURE graduation decision (T046, US-3) to one ability key inside the
|
|
175
|
+
* store transaction. Reads the just-updated ability + the prior dwell counter,
|
|
176
|
+
* asks {@link evaluateGraduation} for the decision, then returns the next
|
|
177
|
+
* `graduations[key]`, the dwell to persist on the estimate, and (on a
|
|
178
|
+
* promotion) a proactive spaced {@link ReviewEntry} to enqueue (FR-010).
|
|
179
|
+
*
|
|
180
|
+
* - On `"graduated"`: write a `current` graduation at the new tier and enqueue a
|
|
181
|
+
* `reason: "spaced"` review one staleness window out (so a one-time streak is
|
|
182
|
+
* later re-verified — durable-knowledge semantics).
|
|
183
|
+
* - On `"demoted"`: step the tier down and flag `due_for_review` (FR-009). No
|
|
184
|
+
* spaced entry — the lapse path (status/standing, T046) owns lapsed entries.
|
|
185
|
+
* - On no change: leave the existing graduation untouched, but still persist the
|
|
186
|
+
* engine's dwell so the consecutive streak carries across items.
|
|
187
|
+
*
|
|
188
|
+
* @returns The decision plus the derived graduation/dwell/review side-outputs.
|
|
189
|
+
*/
|
|
190
|
+
const applyGraduation = (key, newAbility, priorDwell, existing, answeredAt) => {
|
|
191
|
+
const currentTier = existing?.currentTier ?? 0;
|
|
192
|
+
const decision = evaluateGraduation({
|
|
193
|
+
ability: newAbility,
|
|
194
|
+
currentTier,
|
|
195
|
+
dwell: priorDwell,
|
|
196
|
+
});
|
|
197
|
+
if (decision.reason === "graduated") {
|
|
198
|
+
const dueAt = new Date(Date.parse(answeredAt) +
|
|
199
|
+
ASSESSMENT_CONFIG.stalenessWindowDays * 24 * 60 * 60 * 1000).toISOString();
|
|
200
|
+
return {
|
|
201
|
+
decision,
|
|
202
|
+
dwell: decision.dwell,
|
|
203
|
+
graduation: {
|
|
204
|
+
currentTier: decision.tier,
|
|
205
|
+
status: "current",
|
|
206
|
+
graduatedAt: answeredAt,
|
|
207
|
+
lastChangeReason: "graduated",
|
|
208
|
+
},
|
|
209
|
+
spacedReview: { key, dueAt, reason: "spaced" },
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
if (decision.reason === "demoted") {
|
|
213
|
+
return {
|
|
214
|
+
decision,
|
|
215
|
+
dwell: decision.dwell,
|
|
216
|
+
graduation: {
|
|
217
|
+
currentTier: decision.tier,
|
|
218
|
+
status: "due_for_review",
|
|
219
|
+
// Preserve the original graduation timestamp if we have one; this is an
|
|
220
|
+
// audit field, and a demotion is a status change, not a re-graduation.
|
|
221
|
+
graduatedAt: existing?.graduatedAt ?? answeredAt,
|
|
222
|
+
lastChangeReason: "demoted",
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
// No change: keep the existing graduation row but persist the (possibly
|
|
227
|
+
// advanced) dwell streak.
|
|
228
|
+
return { decision, dwell: decision.dwell, graduation: existing };
|
|
229
|
+
};
|
|
230
|
+
/**
|
|
231
|
+
* Project a {@link GraduationDecision} into the `submit_answer` result's
|
|
232
|
+
* `graduation` field `{ changed, tier?, status?, reason? }`. On a change the
|
|
233
|
+
* `status` reflects the resulting graduation state (`current` on promotion,
|
|
234
|
+
* `due_for_review` on demotion); `tier` is included only when the resulting tier
|
|
235
|
+
* is a real tier (100–500) — a demotion all the way to `0` (un-graduated) omits
|
|
236
|
+
* it, matching the result schema (`TierSchema.optional()`). A no-change decision
|
|
237
|
+
* reports `changed: false` and nothing else.
|
|
238
|
+
*/
|
|
239
|
+
const toGraduationResult = (decision) => {
|
|
240
|
+
if (!decision.changed)
|
|
241
|
+
return { changed: false };
|
|
242
|
+
const status = decision.reason === "graduated" ? "current" : "due_for_review";
|
|
243
|
+
const base = {
|
|
244
|
+
changed: true,
|
|
245
|
+
status,
|
|
246
|
+
...(decision.reason !== null ? { reason: decision.reason } : {}),
|
|
247
|
+
};
|
|
248
|
+
return decision.tier === 0 ? base : { ...base, tier: decision.tier };
|
|
249
|
+
};
|
|
250
|
+
/**
|
|
251
|
+
* Apply a graded answer to the profile inside the store's atomic
|
|
252
|
+
* read-modify-write: append the {@link AnsweredItem} to the matching live
|
|
253
|
+
* {@link QuizRecord} (and roll its `abilityAfter`), and replace the
|
|
254
|
+
* {@link AbilityEstimate} for the quiz's key. Returns the new profile plus the
|
|
255
|
+
* before/after abilities for the tool result.
|
|
256
|
+
*
|
|
257
|
+
* @throws {Error} if no live (un-completed) quiz with `quizId` exists.
|
|
258
|
+
*/
|
|
259
|
+
const persistGrade = async (quizId, item, score, grade, gradedBy, answeredAt, dirOverride) => {
|
|
260
|
+
let outcome;
|
|
261
|
+
await updateProfile((current) => {
|
|
262
|
+
const recordIndex = current.quizHistory.findIndex((q) => q.id === quizId);
|
|
263
|
+
const record = current.quizHistory[recordIndex];
|
|
264
|
+
if (record === undefined) {
|
|
265
|
+
throw new Error(`submit_answer: no quiz session found for quizId ${JSON.stringify(quizId)}`);
|
|
266
|
+
}
|
|
267
|
+
if (record.completedAt !== undefined) {
|
|
268
|
+
throw new Error(`submit_answer: quiz ${JSON.stringify(quizId)} is already completed`);
|
|
269
|
+
}
|
|
270
|
+
const key = record.key;
|
|
271
|
+
const prior = current.abilities[key];
|
|
272
|
+
const { before, estimate } = advanceEstimate(prior, item, score, answeredAt);
|
|
273
|
+
// Graduation (T046): evaluate hysteresis/dwell against the JUST-updated
|
|
274
|
+
// ability, threading the prior dwell counter through the pure engine.
|
|
275
|
+
const grad = applyGraduation(key, estimate.value, prior?.dwell ?? 0, current.graduations[key], answeredAt);
|
|
276
|
+
// Persist the engine's next dwell on the estimate so the consecutive streak
|
|
277
|
+
// carries across submit_answer calls (advanceEstimate seeded it from prior).
|
|
278
|
+
const estimateWithDwell = { ...estimate, dwell: grad.dwell };
|
|
279
|
+
outcome = { before, after: estimate.value, decision: grad.decision };
|
|
280
|
+
const answered = buildAnsweredItem(item, score, grade, gradedBy, answeredAt);
|
|
281
|
+
const updatedRecord = {
|
|
282
|
+
...record,
|
|
283
|
+
items: [...record.items, answered],
|
|
284
|
+
abilityAfter: estimate.value,
|
|
285
|
+
};
|
|
286
|
+
const quizHistory = [...current.quizHistory];
|
|
287
|
+
quizHistory[recordIndex] = updatedRecord;
|
|
288
|
+
const graduations = grad.graduation === undefined
|
|
289
|
+
? current.graduations
|
|
290
|
+
: { ...current.graduations, [key]: grad.graduation };
|
|
291
|
+
// On promotion, enqueue a proactive spaced review (FR-010), de-duped by key
|
|
292
|
+
// so repeated promotions don't pile up duplicate spaced entries.
|
|
293
|
+
const reviewSchedule = grad.spacedReview === undefined
|
|
294
|
+
? current.reviewSchedule
|
|
295
|
+
: [
|
|
296
|
+
...current.reviewSchedule.filter((e) => !(e.key === key && e.reason === "spaced")),
|
|
297
|
+
grad.spacedReview,
|
|
298
|
+
];
|
|
299
|
+
return {
|
|
300
|
+
...current,
|
|
301
|
+
abilities: { ...current.abilities, [key]: estimateWithDwell },
|
|
302
|
+
graduations,
|
|
303
|
+
reviewSchedule,
|
|
304
|
+
quizHistory,
|
|
305
|
+
};
|
|
306
|
+
}, dirOverride);
|
|
307
|
+
if (outcome === undefined) {
|
|
308
|
+
// Unreachable: the transform either set `outcome` or threw above.
|
|
309
|
+
throw new Error("submit_answer: ability update did not run");
|
|
310
|
+
}
|
|
311
|
+
return outcome;
|
|
312
|
+
};
|
|
313
|
+
/**
|
|
314
|
+
* Grade one item, dispatching on the input variant AND cross-guarding against
|
|
315
|
+
* the item type (so a deterministic answer on a free-form item, or a free-form
|
|
316
|
+
* verdict on a deterministic item, is rejected — never silently mis-graded):
|
|
317
|
+
*
|
|
318
|
+
* - DETERMINISTIC input (`answer`) → {@link gradeDeterministic}; all-or-nothing
|
|
319
|
+
* score graded `"engine"`. The `free_form` branch there throws the cross-guard.
|
|
320
|
+
* - FREE-FORM input (`verdict`) → the item MUST be `free_form` with a rubric;
|
|
321
|
+
* the PURE {@link scoreVerdict} computes the fraction-met score + grade against
|
|
322
|
+
* the MCP-supplied rubric (FR-012/013), graded `"host_agent"`. A bare/partial
|
|
323
|
+
* verdict is rejected inside `scoreVerdict` (anti-gaming, E2).
|
|
324
|
+
*/
|
|
325
|
+
const gradeItem = (input, item) => {
|
|
326
|
+
if (isDeterministic(input)) {
|
|
327
|
+
const { score, correctAnswer } = gradeDeterministic(item, input.answer);
|
|
328
|
+
// Deterministic items are all-or-nothing: any threshold in (0,1] suffices.
|
|
329
|
+
// Use the free-form pass threshold so there is one "correct" definition.
|
|
330
|
+
const grade = toGrade(score, ASSESSMENT_CONFIG.freeFormPassThreshold);
|
|
331
|
+
return correctAnswer !== undefined
|
|
332
|
+
? { score, grade, gradedBy: "engine", correctAnswer }
|
|
333
|
+
: { score, grade, gradedBy: "engine" };
|
|
334
|
+
}
|
|
335
|
+
// Free-form verdict path (T048): the item must be free_form with a rubric.
|
|
336
|
+
if (item.type !== "free_form" || item.rubric === undefined) {
|
|
337
|
+
throw new Error(`submit_answer: item ${JSON.stringify(item.id)} is not free_form; submit a deterministic \`answer\`, not a \`verdict\``);
|
|
338
|
+
}
|
|
339
|
+
// The MCP computes the score from the per-criterion verdict against its own
|
|
340
|
+
// rubric — the agent never returns a bare score/boolean (FR-012/013).
|
|
341
|
+
const { score, grade } = scoreVerdict(input.verdict, item.rubric);
|
|
342
|
+
return { score, grade, gradedBy: "host_agent" };
|
|
343
|
+
};
|
|
344
|
+
/**
|
|
345
|
+
* Build the `submit_answer` tool module (US-1 deterministic + US-4 free-form).
|
|
346
|
+
*
|
|
347
|
+
* @param dirOverride - Profile-directory override (test seam); see `profileDir`.
|
|
348
|
+
* @param catalogLoader - Catalog source override (test seam); defaults to the
|
|
349
|
+
* bundled snapshot {@link loadBundledCatalog}.
|
|
350
|
+
*/
|
|
351
|
+
export const makeSubmitAnswerTool = (dirOverride, catalogLoader = loadBundledCatalog) => defineTool({
|
|
352
|
+
name: "submit_answer",
|
|
353
|
+
description: "Grade one quiz item (deterministic answer or free-form host verdict) and update ability.",
|
|
354
|
+
// The SDK needs an object schema; the handler re-validates against the real
|
|
355
|
+
// discriminated union below (the authoritative parse).
|
|
356
|
+
inputSchema: SubmitAnswerToolInputSchema,
|
|
357
|
+
handler: async (raw) => {
|
|
358
|
+
// Authoritative validation: deterministic answer XOR free-form verdict.
|
|
359
|
+
// A bare-boolean / shapeless verdict fails the union here (anti-gaming,
|
|
360
|
+
// E2) before it ever reaches grading.
|
|
361
|
+
const input = SubmitAnswerInputSchema.parse(raw);
|
|
362
|
+
// Resolve the quiz's topic key so we can find the item in catalog scope.
|
|
363
|
+
const profile = await loadProfile(dirOverride);
|
|
364
|
+
const record = profile.quizHistory.find((q) => q.id === input.quizId);
|
|
365
|
+
if (record === undefined) {
|
|
366
|
+
throw new Error(`submit_answer: no quiz session found for quizId ${JSON.stringify(input.quizId)}`);
|
|
367
|
+
}
|
|
368
|
+
const { topics } = catalogLoader();
|
|
369
|
+
const found = findItem(topics, input.itemId);
|
|
370
|
+
if (found === undefined) {
|
|
371
|
+
throw new Error(`submit_answer: no catalog item matches itemId ${JSON.stringify(input.itemId)}`);
|
|
372
|
+
}
|
|
373
|
+
const { topic, item } = found;
|
|
374
|
+
// Defensive: the item must belong to the quiz's topic.
|
|
375
|
+
if (abilityKey(topic.class, topic.id) !== record.key) {
|
|
376
|
+
throw new Error(`submit_answer: item ${JSON.stringify(input.itemId)} does not belong to quiz ${JSON.stringify(input.quizId)}`);
|
|
377
|
+
}
|
|
378
|
+
// Grade via the path matching the input + item type (deterministic engine
|
|
379
|
+
// or free-form host verdict); both yield a continuous score + binary grade.
|
|
380
|
+
const { score, grade, gradedBy, correctAnswer } = gradeItem(input, item);
|
|
381
|
+
const answeredAt = new Date().toISOString();
|
|
382
|
+
const { before, after, decision } = await persistGrade(input.quizId, item, score, grade, gradedBy, answeredAt, dirOverride);
|
|
383
|
+
const result = {
|
|
384
|
+
grade,
|
|
385
|
+
score,
|
|
386
|
+
guidance: item.guidance,
|
|
387
|
+
ability: { before, after },
|
|
388
|
+
// Surface the graduation outcome so the host can congratulate (on a
|
|
389
|
+
// promotion) or flag for review (on a demotion). When nothing changed we
|
|
390
|
+
// still report `changed: false` with the holding tier (US-3).
|
|
391
|
+
graduation: toGraduationResult(decision),
|
|
392
|
+
};
|
|
393
|
+
// Only surface the correct answer for MC (where `correctAnswer` is set);
|
|
394
|
+
// free-form items have no single key to reveal.
|
|
395
|
+
return correctAnswer !== undefined
|
|
396
|
+
? { ...result, correctAnswer }
|
|
397
|
+
: result;
|
|
398
|
+
},
|
|
399
|
+
});
|
|
400
|
+
/** Default `submit_answer` module (env / `~/.vibe-hero`), used by the registry. */
|
|
401
|
+
export const submitAnswerTool = makeSubmitAnswerTool();
|
|
402
|
+
//# sourceMappingURL=submitAnswer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"submitAnswer.js","sourceRoot":"","sources":["../../src/tools/submitAnswer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Tool-registry contract shared by every MCP tool module (T020).
|
|
3
|
+
*
|
|
4
|
+
* Each tool lives in its own module exporting a {@link ToolModule}: a name, a
|
|
5
|
+
* human description, a Zod `inputSchema` (an *object* schema — `index.ts` passes
|
|
6
|
+
* its `.shape` to the SDK's `registerTool`), and a `handler`. The handler is the
|
|
7
|
+
* pure tool logic; `index.ts` wraps it with the setup gate and adapts its plain
|
|
8
|
+
* JSON result into the SDK's `CallToolResult` shape.
|
|
9
|
+
*
|
|
10
|
+
* Keeping the handler return type a plain JSON object (not a `CallToolResult`)
|
|
11
|
+
* means tool logic stays decoupled from the transport: tests call handlers
|
|
12
|
+
* directly and assert on the JSON, and later tasks can replace placeholder
|
|
13
|
+
* handlers without touching transport wiring.
|
|
14
|
+
*
|
|
15
|
+
* Source of truth: specs/001-vibe-hero-mvp/contracts/mcp-tools.md.
|
|
16
|
+
*/
|
|
17
|
+
import type { z } from "zod";
|
|
18
|
+
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
19
|
+
/**
|
|
20
|
+
* A tool's JSON result: any serializable object. Concrete tools narrow this to
|
|
21
|
+
* their contract's output schema (e.g. `GetConfigResult`); the registry keeps it
|
|
22
|
+
* open so all 10 modules share one shape.
|
|
23
|
+
*/
|
|
24
|
+
export type ToolResult = Record<string, unknown>;
|
|
25
|
+
/** A tool handler: parsed+validated input in, JSON result out. */
|
|
26
|
+
export type ToolHandler<Args> = (args: Args) => Promise<ToolResult>;
|
|
27
|
+
/**
|
|
28
|
+
* One MCP tool definition. `inputSchema` is a Zod object schema; its `.shape`
|
|
29
|
+
* (a raw Zod shape, i.e. `Record<string, ZodType>`) is what the SDK's
|
|
30
|
+
* `registerTool` expects as `inputSchema`. The `Schema` generic ties the
|
|
31
|
+
* handler's argument to the schema's inferred type, so per-tool modules are
|
|
32
|
+
* fully typed at their definition site.
|
|
33
|
+
*/
|
|
34
|
+
export interface ToolModule<Schema extends z.ZodObject<z.ZodRawShape> = z.ZodObject<z.ZodRawShape>> {
|
|
35
|
+
/** The tool's wire name (e.g. `"get_status"`). */
|
|
36
|
+
readonly name: string;
|
|
37
|
+
/** One-line description surfaced to the host agent. */
|
|
38
|
+
readonly description: string;
|
|
39
|
+
/** Zod object schema for the tool's input. */
|
|
40
|
+
readonly inputSchema: Schema;
|
|
41
|
+
/** Pure tool logic, operating on parsed input. */
|
|
42
|
+
readonly handler: ToolHandler<z.infer<Schema>>;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Schema-erased registry entry. The per-tool `Schema` generic makes precise
|
|
46
|
+
* {@link ToolModule} types mutually unassignable (the handler arg is
|
|
47
|
+
* contravariant), so the registry collection stores this widened form:
|
|
48
|
+
* `inputSchema` is a generic object schema and `handler` accepts `unknown`. The
|
|
49
|
+
* SDK validates input against the shape before our handler runs, so the handler
|
|
50
|
+
* may parse/narrow as needed; `index.ts` passes `unknown` straight through the
|
|
51
|
+
* gate to it.
|
|
52
|
+
*/
|
|
53
|
+
export interface AnyToolModule {
|
|
54
|
+
readonly name: string;
|
|
55
|
+
readonly description: string;
|
|
56
|
+
readonly inputSchema: z.ZodObject<z.ZodRawShape>;
|
|
57
|
+
readonly handler: ToolHandler<unknown>;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Adapt a typed {@link ToolModule} into a schema-erased {@link AnyToolModule}
|
|
61
|
+
* for the registry. The returned handler parses raw input through the module's
|
|
62
|
+
* `inputSchema` (so the typed handler always receives validated, narrowed args)
|
|
63
|
+
* and delegates. Placeholder handlers that ignore their input pass through
|
|
64
|
+
* unchanged.
|
|
65
|
+
*
|
|
66
|
+
* @param tool - A fully-typed tool module.
|
|
67
|
+
* @returns The erased registry entry.
|
|
68
|
+
*/
|
|
69
|
+
export declare const defineTool: <Schema extends z.ZodObject<z.ZodRawShape>>(tool: ToolModule<Schema>) => AnyToolModule;
|
|
70
|
+
/**
|
|
71
|
+
* Adapt a plain JSON {@link ToolResult} into the SDK's `CallToolResult`.
|
|
72
|
+
*
|
|
73
|
+
* We populate both `content` (a JSON text block, the universally-readable form
|
|
74
|
+
* for hosts/tests with no output schema) and `structuredContent` (the same
|
|
75
|
+
* object, machine-readable). This keeps results inspectable without registering
|
|
76
|
+
* an output schema for every tool yet.
|
|
77
|
+
*
|
|
78
|
+
* @param result - The tool's JSON result.
|
|
79
|
+
* @returns A well-formed `CallToolResult`.
|
|
80
|
+
*/
|
|
81
|
+
export declare const toCallToolResult: (result: ToolResult) => CallToolResult;
|
|
82
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/tools/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAEzE;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEjD,kEAAkE;AAClE,MAAM,MAAM,WAAW,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;AAEpE;;;;;;GAMG;AACH,MAAM,WAAW,UAAU,CACzB,MAAM,SAAS,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC;IAEtE,kDAAkD;IAClD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,uDAAuD;IACvD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,8CAA8C;IAC9C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,kDAAkD;IAClD,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;CAChD;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;IACjD,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;CACxC;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,UAAU,GAAI,MAAM,SAAS,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,EAClE,MAAM,UAAU,CAAC,MAAM,CAAC,KACvB,aAMD,CAAC;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,gBAAgB,GAAI,QAAQ,UAAU,KAAG,cAGpD,CAAC"}
|