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 | 1x 1x 1x 1x 1x 1x 1x 1x 15x 15x 15x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 2x 2x 2x 1x 1x 1x 1x 1x 25x 25x 1x 1x 1x 1x 1x 24x 25x 3x 3x 3x 3x 3x 21x 21x 20x 25x 1x 1x 1x 25x 1x | /**
* 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.
if (!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);
|