Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | 2x 15x 15x 1x 1x 1x 1x 1x 1x 2x 2x 1x 1x 25x 1x 24x 24x 3x 21x 21x 20x 1x 1x 2x | /**
* Tool dispatch registry for computation tools available to the AI.
*
* Maps tool names to their implementations. When Claude calls a tool,
* the provider routes the call through dispatchTool() and returns the
* result as a tool_result block.
*/
import { estimate1rm } from './tools/estimate-1rm.js';
import { calculateVolume } from './tools/calculate-volume.js';
import { estimateWorkoutTime } from './tools/estimate-workout-time.js';
import { suggestProgression } from './tools/suggest-progression.js';
import { validateSession } from './tools/validate-session.js';
import { lookupExercises, type LookupExercisesContext, type LookupExercisesInput } from './tools/lookup-exercises.js';
// ── Tool handler type ────────────────────────────────────────
// DB-backed tools (lookup_exercises) receive the optional dispatch context;
// pure tools ignore it.
type ToolHandler = (input: unknown, context?: LookupExercisesContext) => unknown | Promise<unknown>;
// ── Registry ─────────────────────────────────────────────────
const toolRegistry: Record<string, ToolHandler> = {
estimate_1rm: (input) => {
const { weight_kg, reps } = input as { weight_kg: number; reps: number };
return { one_rep_max_kg: estimate1rm(weight_kg, reps) };
},
calculate_volume: (input) => {
const { sets } = input as { sets: Parameters<typeof calculateVolume>[0] };
return calculateVolume(sets);
},
estimate_workout_time: (input) => {
const { prescribed_exercises } = input as { prescribed_exercises: Parameters<typeof estimateWorkoutTime>[0] };
return { estimated_minutes: estimateWorkoutTime(prescribed_exercises) };
},
suggest_progression: (input) => {
const { previous_sets, target_rep_range } = input as {
previous_sets: Parameters<typeof suggestProgression>[0];
target_rep_range: Parameters<typeof suggestProgression>[1];
};
return suggestProgression(previous_sets, target_rep_range);
},
validate_session: (input) => {
// Membership check is skipped here — only the platform call site in generate-workouts/index.ts supplies validExerciseIds.
const workout = input as Parameters<typeof validateSession>[0];
return validateSession(workout);
},
lookup_exercises: (input, context) => {
// lookup_exercises requires a Supabase client + athleteId in context.
// When called without context (e.g. pure test env or mis-wired caller),
// return a clear error so the model knows to reconfigure.
Iif (!context) {
return Promise.resolve(null).then(() => {
throw new Error('lookup_exercises requires a dispatch context (supabase + athleteId) — missing from call site');
});
}
return lookupExercises(input as LookupExercisesInput, context);
},
};
// ── Dispatch function ─────────────────────────────────────────
export interface ToolDispatchResult {
/** The serialisable result to return to Claude as a tool_result */
output: unknown;
/** True if the tool threw an error */
isError: boolean;
}
/**
* Dispatch a tool call by name.
*
* Accepts an optional `context` (Supabase client + athleteId) for DB-backed
* tools like `lookup_exercises`. Pure computation tools ignore the context.
*
* Returns a result object with `output` and `isError`. Unknown tool names
* produce an error result so Claude can recover gracefully.
*/
export async function dispatchTool(toolName: string, input: unknown, context?: LookupExercisesContext): Promise<ToolDispatchResult> {
if (toolName === 'lookup_exercises' && !context) {
return {
output: `lookup_exercises requires a dispatch context (supabase + athleteId) — context was not provided`,
isError: true,
};
}
const handler = toolRegistry[toolName];
if (!handler) {
return {
output: `Unknown tool: "${toolName}". Available tools: ${Object.keys(toolRegistry).join(', ')}`,
isError: true,
};
}
try {
const output = await handler(input, context);
return { output, isError: false };
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
return { output: `Tool "${toolName}" threw an error: ${message}`, isError: true };
}
}
/** Names of all registered computation tools */
export const REGISTERED_TOOL_NAMES = Object.keys(toolRegistry);
|