feat(T2a): tool-toggle seam + phase-based question principles
Part 1 — tool availability (the settings seam, machinery generic): - core/pending.ts: TOGGLEABLE_TOOLS (today: research, demo, visual — the model-facing tool names), EnabledTools = the JSON-native seam shape Partial<Record<string, boolean>> (absent key = enabled, so old records behave exactly as today), filterEnabledTools shared helper. quiz/ask_user_question are always-on FOR NOW (user-decided); the filtering is generic so future toggleables (question-tool variants, the read tool, …) need no factory change. - createProberTools/createTeacherTools take enabled?: EnabledTools; the composition root (chat-handler, Platform's lane this wave) passes the resolved settings through. probe_done is re-added after the filter — always-on by construction (it ends the phase; a prober that can never conclude is a lockout). - Prompts stay STATIC under toggles this wave (user-decided); standing principle recorded: tools ship self-contained usage guidance in their descriptions; system prompts reference only always-on tools. Part 2 — phase-based question-writing principles (user-locked wording, drafted with the user over three iterations): - NEW teacher/21-question-principles.md (composes after 20-teaching): answerable-by-reasoning-from-the-session (the fairness rule that filters unanswerable questions without forbidding visible answers), never-just-reciting-your-words-back, PREFER problems that apply the knowledge (preference, not hard rule — honest recall is the fallback, not the habit), catch-confusion-early-and-nameable (wrong options as real misconceptions), answer-is-signal-not-verdict. - prober/10-probe-doctrine.md: 'no rush' retired; quick-measurement directive in — every question moves an edge (bracket, narrow, or characterize); a bracketed strand is done. Tests: TOGGLEABLE_TOOLS pin, prober/teacher filtering (absent = full, false drops exactly that tool, probe_done survives), prompt pins (the principles compose verbatim into the teacher prompt, the no-rush framing is gone from the prober). 547/547, typecheck/lint/build green.
This commit is contained in:
@@ -30,6 +30,38 @@ export const QUESTION_TOOLS = ["quiz", "ask_user_question"] as const;
|
||||
/** Tools fulfilled by background sub-agent jobs (W2/W6). */
|
||||
export const JOB_TOOLS = ["research", "demo", "visual"] as const;
|
||||
|
||||
// ─── Tool availability (2026-09 settings seam) ─────────────────────────────
|
||||
|
||||
/** The tools a user may toggle off in settings (Wave 2 T2a, user-decided
|
||||
* 2026-09-15): for now only the SUB-AGENT dispatch tools — quiz,
|
||||
* ask_user_question (and the read tool, when it lands) are always on;
|
||||
* the long-term vision is that everything becomes toggleable as new
|
||||
* tool variants (e.g. alternative question tools) ship, so the factory
|
||||
* seam below is GENERIC: it honors any tool name it receives. New
|
||||
* toggleables join this list when settings exposes them.
|
||||
* The names are the MODEL-FACING tool names (research, not researcher). */
|
||||
export const TOGGLEABLE_TOOLS = ["research", "demo", "visual"] as const;
|
||||
|
||||
/** The resolved settings → factory seam shape (Platform wires chat-handler
|
||||
* to pass this through from the settings record; storage and defaults are
|
||||
* Platform's lane). JSON-native: a partial record keyed by tool name.
|
||||
* Absent key / undefined = enabled — old records behave exactly as today.
|
||||
* Any explicit `false` removes that tool from the registry, whatever its
|
||||
* name — the machinery stays generic for future toggleables. */
|
||||
export type EnabledTools = Partial<Record<string, boolean>>;
|
||||
|
||||
/** Drop every tool the resolved settings mark as disabled. The single
|
||||
* filtering site every factory shares (prober/teacher). */
|
||||
export function filterEnabledTools<T>(tools: Record<string, T>, enabled?: EnabledTools): Record<string, T> {
|
||||
if (!enabled) return tools;
|
||||
const out: Record<string, T> = {};
|
||||
for (const [name, tool] of Object.entries(tools)) {
|
||||
if (enabled[name] === false) continue;
|
||||
out[name] = tool;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function findPending(messages: UIMessage[], names: readonly string[]): PendingToolCall | null {
|
||||
for (let m = messages.length - 1; m >= 0; m--) {
|
||||
const msg = messages[m]!;
|
||||
|
||||
Reference in New Issue
Block a user