-- Preset command prompts edited on the desktop reach the phone. -- -- Built-in custom_instructions rows are not writable by clients (RLS allows -- only builtin_key IS NULL), and the phone executes every command with the -- row's prompt. These RPCs let the owner change only the prompt of their own -- preset row, or restore its default; name, key and order stay server-owned. -- Defaults live in one function that bootstrap_custom_instructions' values -- mirror, so "is this the default?" has a single answer. BEGIN; CREATE OR REPLACE FUNCTION public.builtin_instruction_default_prompt_v1(p_builtin_key text) RETURNS text LANGUAGE sql IMMUTABLE SET search_path = '' AS $$ SELECT CASE p_builtin_key WHEN 'translate_en' THEN 'Translate the following text into natural English. Return only the translation.' WHEN 'summarize' THEN 'Summarize the following text in no more than three concise lines.' WHEN 'formal' THEN 'Rewrite the following text in a formal business style while preserving its meaning. Return only the rewritten text.' WHEN 'explain_code' THEN 'Explain the following code in Korean, including its responsibilities and important caveats.' END $$; REVOKE ALL ON FUNCTION public.builtin_instruction_default_prompt_v1(text) FROM PUBLIC, anon; GRANT EXECUTE ON FUNCTION public.builtin_instruction_default_prompt_v1(text) TO authenticated; -- p_prompt NULL (or blank) restores the default prompt. CREATE OR REPLACE FUNCTION public.sync_set_builtin_instruction_prompt_v1( p_builtin_key text, p_prompt text DEFAULT NULL ) RETURNS public.custom_instructions LANGUAGE plpgsql SECURITY DEFINER SET search_path = pg_catalog, public, auth AS $$ DECLARE actor_id uuid := auth.uid(); default_prompt text := public.builtin_instruction_default_prompt_v1(p_builtin_key); next_prompt text; saved public.custom_instructions; BEGIN IF actor_id IS NULL THEN RAISE EXCEPTION 'authentication_required' USING ERRCODE = '42501'; END IF; IF default_prompt IS NULL THEN RAISE EXCEPTION 'invalid_builtin_key' USING ERRCODE = '22023'; END IF; next_prompt := coalesce(nullif(btrim(p_prompt), ''), default_prompt); IF char_length(next_prompt) > 4000 THEN RAISE EXCEPTION 'invalid_instruction_prompt' USING ERRCODE = '22023'; END IF; -- The preset rows are created lazily; make sure this user has them. PERFORM public.bootstrap_custom_instructions(); UPDATE public.custom_instructions SET prompt = next_prompt WHERE user_id = actor_id AND builtin_key = p_builtin_key AND prompt IS DISTINCT FROM next_prompt RETURNING * INTO saved; IF saved.id IS NULL THEN SELECT * INTO saved FROM public.custom_instructions WHERE user_id = actor_id AND builtin_key = p_builtin_key; END IF; RETURN saved; END; $$; REVOKE ALL ON FUNCTION public.sync_set_builtin_instruction_prompt_v1(text, text) FROM PUBLIC, anon; GRANT EXECUTE ON FUNCTION public.sync_set_builtin_instruction_prompt_v1(text, text) TO authenticated; -- Preset rows with a flag telling whether the prompt is still the default. CREATE OR REPLACE FUNCTION public.sync_list_builtin_instructions_v1() RETURNS TABLE (builtin_key text, prompt text, is_default boolean, updated_at timestamptz) LANGUAGE sql STABLE SECURITY INVOKER SET search_path = '' AS $$ SELECT instruction.builtin_key, instruction.prompt, instruction.prompt = public.builtin_instruction_default_prompt_v1(instruction.builtin_key), instruction.updated_at FROM public.custom_instructions AS instruction WHERE instruction.user_id = auth.uid() AND instruction.builtin_key IS NOT NULL ORDER BY instruction.sort_order $$; REVOKE ALL ON FUNCTION public.sync_list_builtin_instructions_v1() FROM PUBLIC, anon; GRANT EXECUTE ON FUNCTION public.sync_list_builtin_instructions_v1() TO authenticated; COMMIT;