feat(supabase): let owners change the prompt of their preset commands

Preset custom_instructions rows are not client-writable, yet the phone runs
every command with the row's prompt. sync_set_builtin_instruction_prompt_v1
changes only the prompt of the caller's preset (NULL restores the default),
sync_list_builtin_instructions_v1 reports whether each prompt is the default,
and the defaults live in one function.
This commit is contained in:
Yun Chan 2026-09-27 16:24:25 +09:00
parent fe1a7fb112
commit 08c6504589

View file

@ -0,0 +1,101 @@
-- 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;