level-up-mcp-server-cn 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/projects/c--Users-klexi-OneDrive-Desktop-Levelup-level-up-mcp-server/memory/project_testing_service.md +11 -0
- package/.claude/settings.local.json +10 -0
- package/.env.example +19 -0
- package/CLAUDE.md +222 -0
- package/CODE_REVIEW.md +282 -0
- package/LICENSE +64 -0
- package/README.md +198 -0
- package/dist/constants.d.ts +33 -0
- package/dist/constants.js +78 -0
- package/dist/constants.js.map +1 -0
- package/dist/data/quest-seeds.d.ts +18 -0
- package/dist/data/quest-seeds.js +380 -0
- package/dist/data/quest-seeds.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +260 -0
- package/dist/index.js.map +1 -0
- package/dist/schemas/common.d.ts +33 -0
- package/dist/schemas/common.js +96 -0
- package/dist/schemas/common.js.map +1 -0
- package/dist/services/dispatcher.d.ts +27 -0
- package/dist/services/dispatcher.js +47 -0
- package/dist/services/dispatcher.js.map +1 -0
- package/dist/services/errors.d.ts +56 -0
- package/dist/services/errors.js +99 -0
- package/dist/services/errors.js.map +1 -0
- package/dist/services/format.d.ts +74 -0
- package/dist/services/format.js +144 -0
- package/dist/services/format.js.map +1 -0
- package/dist/services/ownership.d.ts +19 -0
- package/dist/services/ownership.js +79 -0
- package/dist/services/ownership.js.map +1 -0
- package/dist/services/quality-gate.d.ts +45 -0
- package/dist/services/quality-gate.js +131 -0
- package/dist/services/quality-gate.js.map +1 -0
- package/dist/services/rate-limit.d.ts +12 -0
- package/dist/services/rate-limit.js +49 -0
- package/dist/services/rate-limit.js.map +1 -0
- package/dist/services/register.d.ts +49 -0
- package/dist/services/register.js +63 -0
- package/dist/services/register.js.map +1 -0
- package/dist/services/supabase.d.ts +10 -0
- package/dist/services/supabase.js +79 -0
- package/dist/services/supabase.js.map +1 -0
- package/dist/tools/achievements.d.ts +6 -0
- package/dist/tools/achievements.js +242 -0
- package/dist/tools/achievements.js.map +1 -0
- package/dist/tools/admin.d.ts +16 -0
- package/dist/tools/admin.js +328 -0
- package/dist/tools/admin.js.map +1 -0
- package/dist/tools/agents.d.ts +3 -0
- package/dist/tools/agents.js +400 -0
- package/dist/tools/agents.js.map +1 -0
- package/dist/tools/bootstrap.d.ts +17 -0
- package/dist/tools/bootstrap.js +565 -0
- package/dist/tools/bootstrap.js.map +1 -0
- package/dist/tools/dispatchers/admin.d.ts +3 -0
- package/dist/tools/dispatchers/admin.js +50 -0
- package/dist/tools/dispatchers/admin.js.map +1 -0
- package/dist/tools/dispatchers/eval.d.ts +3 -0
- package/dist/tools/dispatchers/eval.js +40 -0
- package/dist/tools/dispatchers/eval.js.map +1 -0
- package/dist/tools/dispatchers/quests.d.ts +3 -0
- package/dist/tools/dispatchers/quests.js +60 -0
- package/dist/tools/dispatchers/quests.js.map +1 -0
- package/dist/tools/dispatchers/session.d.ts +3 -0
- package/dist/tools/dispatchers/session.js +38 -0
- package/dist/tools/dispatchers/session.js.map +1 -0
- package/dist/tools/dispatchers/skills.d.ts +3 -0
- package/dist/tools/dispatchers/skills.js +49 -0
- package/dist/tools/dispatchers/skills.js.map +1 -0
- package/dist/tools/dispatchers/tasks.d.ts +3 -0
- package/dist/tools/dispatchers/tasks.js +53 -0
- package/dist/tools/dispatchers/tasks.js.map +1 -0
- package/dist/tools/dispatchers/users.d.ts +3 -0
- package/dist/tools/dispatchers/users.js +65 -0
- package/dist/tools/dispatchers/users.js.map +1 -0
- package/dist/tools/dispatchers/xp.d.ts +3 -0
- package/dist/tools/dispatchers/xp.js +51 -0
- package/dist/tools/dispatchers/xp.js.map +1 -0
- package/dist/tools/growth-plan.d.ts +5 -0
- package/dist/tools/growth-plan.js +791 -0
- package/dist/tools/growth-plan.js.map +1 -0
- package/dist/tools/leaderboards.d.ts +10 -0
- package/dist/tools/leaderboards.js +279 -0
- package/dist/tools/leaderboards.js.map +1 -0
- package/dist/tools/leveling.d.ts +24 -0
- package/dist/tools/leveling.js +356 -0
- package/dist/tools/leveling.js.map +1 -0
- package/dist/tools/metrics.d.ts +3 -0
- package/dist/tools/metrics.js +247 -0
- package/dist/tools/metrics.js.map +1 -0
- package/dist/tools/quests.d.ts +5 -0
- package/dist/tools/quests.js +586 -0
- package/dist/tools/quests.js.map +1 -0
- package/dist/tools/ratings.d.ts +11 -0
- package/dist/tools/ratings.js +564 -0
- package/dist/tools/ratings.js.map +1 -0
- package/dist/tools/skills.d.ts +66 -0
- package/dist/tools/skills.js +1112 -0
- package/dist/tools/skills.js.map +1 -0
- package/dist/tools/system.d.ts +31 -0
- package/dist/tools/system.js +605 -0
- package/dist/tools/system.js.map +1 -0
- package/dist/tools/tasks.d.ts +73 -0
- package/dist/tools/tasks.js +1572 -0
- package/dist/tools/tasks.js.map +1 -0
- package/dist/tools/users.d.ts +97 -0
- package/dist/tools/users.js +1306 -0
- package/dist/tools/users.js.map +1 -0
- package/dist/tools/xp.d.ts +38 -0
- package/dist/tools/xp.js +670 -0
- package/dist/tools/xp.js.map +1 -0
- package/dist/types.d.ts +178 -0
- package/dist/types.js +12 -0
- package/dist/types.js.map +1 -0
- package/docs/recommended-skillsets.md +622 -0
- package/docs/skills-and-abilities-review.md +672 -0
- package/docs/v0.3-roadmap.md +191 -0
- package/package.json +35 -0
- package/sql/agent_pending_installs.sql +28 -0
- package/sql/award_class_xp.sql +81 -0
- package/supabase/.temp/cli-latest +1 -0
- package/supabase/.temp/gotrue-version +1 -0
- package/supabase/.temp/pooler-url +1 -0
- package/supabase/.temp/postgres-version +1 -0
- package/supabase/.temp/project-ref +1 -0
- package/supabase/.temp/rest-version +1 -0
- package/supabase/.temp/storage-migration +1 -0
- package/supabase/.temp/storage-version +1 -0
- package/supabase/migrations/20260314000000_anon_rls_policies.sql +311 -0
- package/supabase/migrations/20260314000001_ownership_rpcs.sql +382 -0
- package/supabase/migrations/20260314000002_evidence_and_growth_plan.sql +97 -0
- package/supabase/migrations/20260317000000_seed_quests.sql +62 -0
- package/supabase/migrations/20260317000001_star_cooldown_and_fixes.sql +16 -0
- package/supabase/migrations/20260318000000_restore_rank_names.sql +25 -0
- package/supabase/migrations/20260320000000_chinese_rank_names.sql +24 -0
- package/vitest.config.ts +11 -0
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
-- ============================================================
|
|
2
|
+
-- Migration: Ownership-checking RPC functions
|
|
3
|
+
-- ============================================================
|
|
4
|
+
-- These SECURITY DEFINER functions replace direct UPDATE calls.
|
|
5
|
+
-- Anon role has no UPDATE policies — all modifications go through
|
|
6
|
+
-- these RPCs which verify ownership before making changes.
|
|
7
|
+
--
|
|
8
|
+
-- Depends on: 20260314000000_anon_rls_policies.sql
|
|
9
|
+
-- ============================================================
|
|
10
|
+
|
|
11
|
+
-- ============================================================
|
|
12
|
+
-- RPC 1: update_user_profile
|
|
13
|
+
-- Updates safe user fields. Cannot modify XP, level, or timestamps.
|
|
14
|
+
-- ============================================================
|
|
15
|
+
CREATE OR REPLACE FUNCTION update_user_profile(
|
|
16
|
+
p_user_id UUID,
|
|
17
|
+
p_updates JSONB
|
|
18
|
+
) RETURNS JSONB AS $$
|
|
19
|
+
DECLARE
|
|
20
|
+
v_user RECORD;
|
|
21
|
+
v_sql TEXT := 'UPDATE users SET ';
|
|
22
|
+
v_sets TEXT[] := ARRAY[]::TEXT[];
|
|
23
|
+
v_key TEXT;
|
|
24
|
+
v_allowed TEXT[] := ARRAY[
|
|
25
|
+
'username', 'handle', 'email',
|
|
26
|
+
'is_searchable_by_email', 'wallet_address', 'metadata'
|
|
27
|
+
];
|
|
28
|
+
BEGIN
|
|
29
|
+
-- Verify user exists
|
|
30
|
+
SELECT id INTO v_user FROM users WHERE id = p_user_id;
|
|
31
|
+
IF NOT FOUND THEN
|
|
32
|
+
RETURN jsonb_build_object('success', false, 'error', 'User not found');
|
|
33
|
+
END IF;
|
|
34
|
+
|
|
35
|
+
-- Build dynamic update from allowed fields only
|
|
36
|
+
FOR v_key IN SELECT jsonb_object_keys(p_updates) LOOP
|
|
37
|
+
IF v_key = ANY(v_allowed) THEN
|
|
38
|
+
v_sets := array_append(v_sets, format('%I = %L', v_key, p_updates->>v_key));
|
|
39
|
+
END IF;
|
|
40
|
+
END LOOP;
|
|
41
|
+
|
|
42
|
+
IF array_length(v_sets, 1) IS NULL THEN
|
|
43
|
+
RETURN jsonb_build_object('success', false, 'error', 'No valid fields to update');
|
|
44
|
+
END IF;
|
|
45
|
+
|
|
46
|
+
v_sql := v_sql || array_to_string(v_sets, ', ') || format(' WHERE id = %L', p_user_id);
|
|
47
|
+
EXECUTE v_sql;
|
|
48
|
+
|
|
49
|
+
RETURN jsonb_build_object('success', true, 'user_id', p_user_id);
|
|
50
|
+
END;
|
|
51
|
+
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
|
52
|
+
|
|
53
|
+
-- ============================================================
|
|
54
|
+
-- RPC 2: update_agent_profile
|
|
55
|
+
-- Updates safe agent fields. Verifies owner_user_id matches.
|
|
56
|
+
-- Cannot modify XP, level, integrity_score, or timestamps.
|
|
57
|
+
-- ============================================================
|
|
58
|
+
CREATE OR REPLACE FUNCTION update_agent_profile(
|
|
59
|
+
p_agent_id UUID,
|
|
60
|
+
p_owner_user_id UUID,
|
|
61
|
+
p_updates JSONB
|
|
62
|
+
) RETURNS JSONB AS $$
|
|
63
|
+
DECLARE
|
|
64
|
+
v_agent RECORD;
|
|
65
|
+
v_sql TEXT := 'UPDATE agents SET ';
|
|
66
|
+
v_sets TEXT[] := ARRAY[]::TEXT[];
|
|
67
|
+
v_key TEXT;
|
|
68
|
+
v_allowed TEXT[] := ARRAY[
|
|
69
|
+
'name', 'description', 'agent_class', 'platform',
|
|
70
|
+
'is_public', 'status', 'metadata'
|
|
71
|
+
];
|
|
72
|
+
BEGIN
|
|
73
|
+
-- Verify agent exists and caller is the owner
|
|
74
|
+
SELECT id, owner_user_id INTO v_agent FROM agents WHERE id = p_agent_id;
|
|
75
|
+
IF NOT FOUND THEN
|
|
76
|
+
RETURN jsonb_build_object('success', false, 'error', 'Agent not found');
|
|
77
|
+
END IF;
|
|
78
|
+
IF v_agent.owner_user_id != p_owner_user_id THEN
|
|
79
|
+
RETURN jsonb_build_object('success', false, 'error', 'Not the agent owner');
|
|
80
|
+
END IF;
|
|
81
|
+
|
|
82
|
+
-- Build dynamic update from allowed fields only
|
|
83
|
+
FOR v_key IN SELECT jsonb_object_keys(p_updates) LOOP
|
|
84
|
+
IF v_key = ANY(v_allowed) THEN
|
|
85
|
+
v_sets := array_append(v_sets, format('%I = %L', v_key, p_updates->>v_key));
|
|
86
|
+
END IF;
|
|
87
|
+
END LOOP;
|
|
88
|
+
|
|
89
|
+
IF array_length(v_sets, 1) IS NULL THEN
|
|
90
|
+
RETURN jsonb_build_object('success', false, 'error', 'No valid fields to update');
|
|
91
|
+
END IF;
|
|
92
|
+
|
|
93
|
+
v_sql := v_sql || array_to_string(v_sets, ', ') || format(' WHERE id = %L', p_agent_id);
|
|
94
|
+
EXECUTE v_sql;
|
|
95
|
+
|
|
96
|
+
RETURN jsonb_build_object('success', true, 'agent_id', p_agent_id);
|
|
97
|
+
END;
|
|
98
|
+
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
|
99
|
+
|
|
100
|
+
-- ============================================================
|
|
101
|
+
-- RPC 3: update_task_status
|
|
102
|
+
-- Updates task fields. Verifies the caller owns the task
|
|
103
|
+
-- (matches user_id or agent's owner_user_id).
|
|
104
|
+
-- ============================================================
|
|
105
|
+
CREATE OR REPLACE FUNCTION update_task_status(
|
|
106
|
+
p_task_id UUID,
|
|
107
|
+
p_caller_id UUID,
|
|
108
|
+
p_updates JSONB
|
|
109
|
+
) RETURNS JSONB AS $$
|
|
110
|
+
DECLARE
|
|
111
|
+
v_task RECORD;
|
|
112
|
+
v_is_owner BOOLEAN := false;
|
|
113
|
+
v_sql TEXT := 'UPDATE tasks SET ';
|
|
114
|
+
v_sets TEXT[] := ARRAY[]::TEXT[];
|
|
115
|
+
v_key TEXT;
|
|
116
|
+
v_allowed TEXT[] := ARRAY[
|
|
117
|
+
'status', 'completion_pct', 'milestones_completed',
|
|
118
|
+
'output_type', 'completed_at', 'duration_seconds', 'metadata'
|
|
119
|
+
];
|
|
120
|
+
BEGIN
|
|
121
|
+
-- Fetch task
|
|
122
|
+
SELECT id, user_id, agent_id INTO v_task FROM tasks WHERE id = p_task_id;
|
|
123
|
+
IF NOT FOUND THEN
|
|
124
|
+
RETURN jsonb_build_object('success', false, 'error', 'Task not found');
|
|
125
|
+
END IF;
|
|
126
|
+
|
|
127
|
+
-- Check ownership: caller is the user, or caller owns the agent
|
|
128
|
+
IF v_task.user_id = p_caller_id THEN
|
|
129
|
+
v_is_owner := true;
|
|
130
|
+
ELSIF v_task.agent_id IS NOT NULL THEN
|
|
131
|
+
IF EXISTS (SELECT 1 FROM agents WHERE id = v_task.agent_id AND owner_user_id = p_caller_id) THEN
|
|
132
|
+
v_is_owner := true;
|
|
133
|
+
END IF;
|
|
134
|
+
END IF;
|
|
135
|
+
|
|
136
|
+
IF NOT v_is_owner THEN
|
|
137
|
+
RETURN jsonb_build_object('success', false, 'error', 'Not the task owner');
|
|
138
|
+
END IF;
|
|
139
|
+
|
|
140
|
+
-- Build dynamic update from allowed fields only
|
|
141
|
+
FOR v_key IN SELECT jsonb_object_keys(p_updates) LOOP
|
|
142
|
+
IF v_key = ANY(v_allowed) THEN
|
|
143
|
+
IF v_key IN ('completion_pct', 'milestones_completed', 'duration_seconds') THEN
|
|
144
|
+
v_sets := array_append(v_sets, format('%I = %s', v_key, (p_updates->>v_key)::numeric));
|
|
145
|
+
ELSE
|
|
146
|
+
v_sets := array_append(v_sets, format('%I = %L', v_key, p_updates->>v_key));
|
|
147
|
+
END IF;
|
|
148
|
+
END IF;
|
|
149
|
+
END LOOP;
|
|
150
|
+
|
|
151
|
+
IF array_length(v_sets, 1) IS NULL THEN
|
|
152
|
+
RETURN jsonb_build_object('success', false, 'error', 'No valid fields to update');
|
|
153
|
+
END IF;
|
|
154
|
+
|
|
155
|
+
v_sql := v_sql || array_to_string(v_sets, ', ') || format(' WHERE id = %L', p_task_id);
|
|
156
|
+
EXECUTE v_sql;
|
|
157
|
+
|
|
158
|
+
RETURN jsonb_build_object('success', true, 'task_id', p_task_id);
|
|
159
|
+
END;
|
|
160
|
+
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
|
161
|
+
|
|
162
|
+
-- ============================================================
|
|
163
|
+
-- RPC 4: award_xp_direct
|
|
164
|
+
-- Awards XP through the ledger — used by quests and weekly bonuses.
|
|
165
|
+
-- Replaces awardXpWithLock() in TypeScript.
|
|
166
|
+
-- ============================================================
|
|
167
|
+
CREATE OR REPLACE FUNCTION award_xp_direct(
|
|
168
|
+
p_entity_type TEXT,
|
|
169
|
+
p_entity_id UUID,
|
|
170
|
+
p_amount INTEGER,
|
|
171
|
+
p_source_type TEXT,
|
|
172
|
+
p_description TEXT,
|
|
173
|
+
p_source_id UUID DEFAULT NULL
|
|
174
|
+
) RETURNS JSONB AS $$
|
|
175
|
+
DECLARE
|
|
176
|
+
v_running_total BIGINT;
|
|
177
|
+
BEGIN
|
|
178
|
+
-- Validate entity exists
|
|
179
|
+
IF p_entity_type = 'user' THEN
|
|
180
|
+
IF NOT EXISTS (SELECT 1 FROM users WHERE id = p_entity_id) THEN
|
|
181
|
+
RETURN jsonb_build_object('success', false, 'error', 'User not found');
|
|
182
|
+
END IF;
|
|
183
|
+
ELSIF p_entity_type = 'agent' THEN
|
|
184
|
+
IF NOT EXISTS (SELECT 1 FROM agents WHERE id = p_entity_id) THEN
|
|
185
|
+
RETURN jsonb_build_object('success', false, 'error', 'Agent not found');
|
|
186
|
+
END IF;
|
|
187
|
+
ELSE
|
|
188
|
+
RETURN jsonb_build_object('success', false, 'error', 'Invalid entity_type');
|
|
189
|
+
END IF;
|
|
190
|
+
|
|
191
|
+
-- Validate amount is positive
|
|
192
|
+
IF p_amount <= 0 THEN
|
|
193
|
+
RETURN jsonb_build_object('success', false, 'error', 'Amount must be positive');
|
|
194
|
+
END IF;
|
|
195
|
+
|
|
196
|
+
-- Get current running total
|
|
197
|
+
SELECT COALESCE(running_total, 0) INTO v_running_total
|
|
198
|
+
FROM xp_ledger
|
|
199
|
+
WHERE entity_type = p_entity_type AND entity_id = p_entity_id
|
|
200
|
+
ORDER BY created_at DESC
|
|
201
|
+
LIMIT 1;
|
|
202
|
+
|
|
203
|
+
IF NOT FOUND THEN
|
|
204
|
+
v_running_total := 0;
|
|
205
|
+
END IF;
|
|
206
|
+
|
|
207
|
+
v_running_total := v_running_total + p_amount;
|
|
208
|
+
|
|
209
|
+
-- Insert ledger entry
|
|
210
|
+
INSERT INTO xp_ledger (
|
|
211
|
+
entity_type, entity_id, xp_type, amount,
|
|
212
|
+
source_type, source_id, description, running_total
|
|
213
|
+
) VALUES (
|
|
214
|
+
p_entity_type, p_entity_id, 'main', p_amount,
|
|
215
|
+
p_source_type, p_source_id, p_description, v_running_total
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
-- Update entity XP column
|
|
219
|
+
IF p_entity_type = 'user' THEN
|
|
220
|
+
UPDATE users SET main_xp = v_running_total WHERE id = p_entity_id;
|
|
221
|
+
ELSE
|
|
222
|
+
UPDATE agents SET xp = v_running_total WHERE id = p_entity_id;
|
|
223
|
+
END IF;
|
|
224
|
+
|
|
225
|
+
RETURN jsonb_build_object(
|
|
226
|
+
'success', true,
|
|
227
|
+
'running_total', v_running_total,
|
|
228
|
+
'amount', p_amount
|
|
229
|
+
);
|
|
230
|
+
END;
|
|
231
|
+
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
|
232
|
+
|
|
233
|
+
-- ============================================================
|
|
234
|
+
-- RPC 5: update_integrity_score
|
|
235
|
+
-- Updates agent integrity_score from rating events.
|
|
236
|
+
-- ============================================================
|
|
237
|
+
CREATE OR REPLACE FUNCTION update_integrity_score(
|
|
238
|
+
p_agent_id UUID,
|
|
239
|
+
p_delta INTEGER
|
|
240
|
+
) RETURNS JSONB AS $$
|
|
241
|
+
DECLARE
|
|
242
|
+
v_current INTEGER;
|
|
243
|
+
v_new INTEGER;
|
|
244
|
+
BEGIN
|
|
245
|
+
SELECT integrity_score INTO v_current FROM agents WHERE id = p_agent_id;
|
|
246
|
+
IF NOT FOUND THEN
|
|
247
|
+
RETURN jsonb_build_object('success', false, 'error', 'Agent not found');
|
|
248
|
+
END IF;
|
|
249
|
+
|
|
250
|
+
v_new := GREATEST(0, LEAST(100, v_current + p_delta));
|
|
251
|
+
UPDATE agents SET integrity_score = v_new WHERE id = p_agent_id;
|
|
252
|
+
|
|
253
|
+
RETURN jsonb_build_object(
|
|
254
|
+
'success', true,
|
|
255
|
+
'agent_id', p_agent_id,
|
|
256
|
+
'old_score', v_current,
|
|
257
|
+
'new_score', v_new
|
|
258
|
+
);
|
|
259
|
+
END;
|
|
260
|
+
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
|
261
|
+
|
|
262
|
+
-- ============================================================
|
|
263
|
+
-- RPC 6: record_level_up_timestamp
|
|
264
|
+
-- Records last_leveled_at after a level-up. Called by check_level_up.
|
|
265
|
+
-- ============================================================
|
|
266
|
+
CREATE OR REPLACE FUNCTION record_level_up_timestamp(
|
|
267
|
+
p_entity_type TEXT,
|
|
268
|
+
p_entity_id UUID
|
|
269
|
+
) RETURNS JSONB AS $$
|
|
270
|
+
BEGIN
|
|
271
|
+
IF p_entity_type = 'user' THEN
|
|
272
|
+
UPDATE users SET last_leveled_at = now() WHERE id = p_entity_id;
|
|
273
|
+
ELSIF p_entity_type = 'agent' THEN
|
|
274
|
+
UPDATE agents SET last_leveled_at = now() WHERE id = p_entity_id;
|
|
275
|
+
ELSE
|
|
276
|
+
RETURN jsonb_build_object('success', false, 'error', 'Invalid entity_type');
|
|
277
|
+
END IF;
|
|
278
|
+
|
|
279
|
+
RETURN jsonb_build_object('success', true);
|
|
280
|
+
END;
|
|
281
|
+
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
|
282
|
+
|
|
283
|
+
-- ============================================================
|
|
284
|
+
-- RPC 7: merge_user_profiles
|
|
285
|
+
-- Handles the profile merge operation atomically.
|
|
286
|
+
-- Moves agents, identities, XP ledger, quest participations,
|
|
287
|
+
-- recalculates XP, and soft-deletes the secondary user.
|
|
288
|
+
-- ============================================================
|
|
289
|
+
CREATE OR REPLACE FUNCTION merge_user_profiles(
|
|
290
|
+
p_primary_user_id UUID,
|
|
291
|
+
p_secondary_user_id UUID
|
|
292
|
+
) RETURNS JSONB AS $$
|
|
293
|
+
DECLARE
|
|
294
|
+
v_agents_moved INTEGER;
|
|
295
|
+
v_identities_moved INTEGER;
|
|
296
|
+
v_total_xp BIGINT;
|
|
297
|
+
v_sc RECORD;
|
|
298
|
+
v_pc RECORD;
|
|
299
|
+
BEGIN
|
|
300
|
+
-- Verify both users exist
|
|
301
|
+
IF NOT EXISTS (SELECT 1 FROM users WHERE id = p_primary_user_id) THEN
|
|
302
|
+
RETURN jsonb_build_object('success', false, 'error', 'Primary user not found');
|
|
303
|
+
END IF;
|
|
304
|
+
IF NOT EXISTS (SELECT 1 FROM users WHERE id = p_secondary_user_id) THEN
|
|
305
|
+
RETURN jsonb_build_object('success', false, 'error', 'Secondary user not found');
|
|
306
|
+
END IF;
|
|
307
|
+
|
|
308
|
+
-- Move agents
|
|
309
|
+
WITH moved AS (
|
|
310
|
+
UPDATE agents SET owner_user_id = p_primary_user_id
|
|
311
|
+
WHERE owner_user_id = p_secondary_user_id
|
|
312
|
+
RETURNING id
|
|
313
|
+
) SELECT count(*) INTO v_agents_moved FROM moved;
|
|
314
|
+
|
|
315
|
+
-- Move identities
|
|
316
|
+
WITH moved AS (
|
|
317
|
+
UPDATE user_identities SET user_id = p_primary_user_id
|
|
318
|
+
WHERE user_id = p_secondary_user_id
|
|
319
|
+
RETURNING id
|
|
320
|
+
) SELECT count(*) INTO v_identities_moved FROM moved;
|
|
321
|
+
|
|
322
|
+
-- Move XP ledger
|
|
323
|
+
UPDATE xp_ledger SET entity_id = p_primary_user_id
|
|
324
|
+
WHERE entity_type = 'user' AND entity_id = p_secondary_user_id;
|
|
325
|
+
|
|
326
|
+
-- Move quest participations
|
|
327
|
+
UPDATE quest_participants SET user_id = p_primary_user_id
|
|
328
|
+
WHERE user_id = p_secondary_user_id;
|
|
329
|
+
|
|
330
|
+
-- Recalculate XP
|
|
331
|
+
SELECT COALESCE(SUM(amount), 0) INTO v_total_xp
|
|
332
|
+
FROM xp_ledger
|
|
333
|
+
WHERE entity_type = 'user' AND entity_id = p_primary_user_id;
|
|
334
|
+
|
|
335
|
+
UPDATE users SET main_xp = v_total_xp WHERE id = p_primary_user_id;
|
|
336
|
+
|
|
337
|
+
-- Merge class XP
|
|
338
|
+
FOR v_sc IN
|
|
339
|
+
SELECT class_name, class_xp FROM user_class_progress
|
|
340
|
+
WHERE user_id = p_secondary_user_id
|
|
341
|
+
LOOP
|
|
342
|
+
SELECT id, class_xp INTO v_pc FROM user_class_progress
|
|
343
|
+
WHERE user_id = p_primary_user_id AND class_name = v_sc.class_name;
|
|
344
|
+
IF FOUND THEN
|
|
345
|
+
UPDATE user_class_progress SET class_xp = v_pc.class_xp + v_sc.class_xp
|
|
346
|
+
WHERE id = v_pc.id;
|
|
347
|
+
END IF;
|
|
348
|
+
END LOOP;
|
|
349
|
+
|
|
350
|
+
-- Soft-delete secondary user
|
|
351
|
+
UPDATE users SET
|
|
352
|
+
username = '[MERGED] ' || left(p_secondary_user_id::text, 8),
|
|
353
|
+
handle = NULL,
|
|
354
|
+
metadata = jsonb_build_object(
|
|
355
|
+
'merged_into', p_primary_user_id,
|
|
356
|
+
'merged_at', now()
|
|
357
|
+
)
|
|
358
|
+
WHERE id = p_secondary_user_id;
|
|
359
|
+
|
|
360
|
+
DELETE FROM user_class_progress WHERE user_id = p_secondary_user_id;
|
|
361
|
+
|
|
362
|
+
RETURN jsonb_build_object(
|
|
363
|
+
'success', true,
|
|
364
|
+
'primary_user_id', p_primary_user_id,
|
|
365
|
+
'secondary_user_id', p_secondary_user_id,
|
|
366
|
+
'agents_moved', v_agents_moved,
|
|
367
|
+
'identities_moved', v_identities_moved,
|
|
368
|
+
'total_xp', v_total_xp
|
|
369
|
+
);
|
|
370
|
+
END;
|
|
371
|
+
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
|
372
|
+
|
|
373
|
+
-- ============================================================
|
|
374
|
+
-- Grant EXECUTE to anon role for all RPCs
|
|
375
|
+
-- ============================================================
|
|
376
|
+
GRANT EXECUTE ON FUNCTION update_user_profile(UUID, JSONB) TO anon;
|
|
377
|
+
GRANT EXECUTE ON FUNCTION update_agent_profile(UUID, UUID, JSONB) TO anon;
|
|
378
|
+
GRANT EXECUTE ON FUNCTION update_task_status(UUID, UUID, JSONB) TO anon;
|
|
379
|
+
GRANT EXECUTE ON FUNCTION award_xp_direct(TEXT, UUID, INTEGER, TEXT, TEXT, UUID) TO anon;
|
|
380
|
+
GRANT EXECUTE ON FUNCTION update_integrity_score(UUID, INTEGER) TO anon;
|
|
381
|
+
GRANT EXECUTE ON FUNCTION record_level_up_timestamp(TEXT, UUID) TO anon;
|
|
382
|
+
GRANT EXECUTE ON FUNCTION merge_user_profiles(UUID, UUID) TO anon;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
-- ============================================================
|
|
2
|
+
-- Migration: Evidence system + Growth plan cache
|
|
3
|
+
-- ============================================================
|
|
4
|
+
-- Adds:
|
|
5
|
+
-- 1. task_evidence table (Table 47) — verifiable output per task
|
|
6
|
+
-- 2. growth_plan_cache table (Table 46) — cached growth plans
|
|
7
|
+
-- 3. Two new columns on tasks: evidence_required, evidence_quality
|
|
8
|
+
-- 4. RLS policies for anon access
|
|
9
|
+
-- ============================================================
|
|
10
|
+
|
|
11
|
+
-- ============================================================
|
|
12
|
+
-- 1. NEW COLUMNS ON tasks
|
|
13
|
+
-- ============================================================
|
|
14
|
+
|
|
15
|
+
ALTER TABLE tasks
|
|
16
|
+
ADD COLUMN IF NOT EXISTS evidence_required boolean NOT NULL DEFAULT true,
|
|
17
|
+
ADD COLUMN IF NOT EXISTS evidence_quality text;
|
|
18
|
+
|
|
19
|
+
COMMENT ON COLUMN tasks.evidence_required IS 'Whether evidence must be attached. Default true for agent_reported tasks.';
|
|
20
|
+
COMMENT ON COLUMN tasks.evidence_quality IS 'Computed at complete_task: strong, medium, low, none.';
|
|
21
|
+
|
|
22
|
+
-- ============================================================
|
|
23
|
+
-- 2. task_evidence TABLE (Table 47)
|
|
24
|
+
-- ============================================================
|
|
25
|
+
|
|
26
|
+
CREATE TABLE IF NOT EXISTS task_evidence (
|
|
27
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
28
|
+
task_id uuid NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
29
|
+
evidence_type text NOT NULL CHECK (evidence_type IN (
|
|
30
|
+
'deliverable_url', 'platform_reference', 'screenshot',
|
|
31
|
+
'code_output', 'api_response', 'text_summary', 'external_link'
|
|
32
|
+
)),
|
|
33
|
+
content text NOT NULL,
|
|
34
|
+
label text,
|
|
35
|
+
verified boolean NOT NULL DEFAULT false,
|
|
36
|
+
verified_at timestamptz,
|
|
37
|
+
added_at timestamptz NOT NULL DEFAULT now(),
|
|
38
|
+
metadata jsonb
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
CREATE INDEX IF NOT EXISTS idx_task_evidence_task_id ON task_evidence(task_id);
|
|
42
|
+
CREATE INDEX IF NOT EXISTS idx_task_evidence_type_verified ON task_evidence(evidence_type, verified);
|
|
43
|
+
|
|
44
|
+
COMMENT ON TABLE task_evidence IS 'Verifiable evidence attached to completed tasks. One or more items per task.';
|
|
45
|
+
|
|
46
|
+
-- ============================================================
|
|
47
|
+
-- 3. growth_plan_cache TABLE (Table 46)
|
|
48
|
+
-- ============================================================
|
|
49
|
+
|
|
50
|
+
CREATE TABLE IF NOT EXISTS growth_plan_cache (
|
|
51
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
52
|
+
agent_id uuid NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
|
|
53
|
+
target_hash text NOT NULL,
|
|
54
|
+
plan jsonb NOT NULL,
|
|
55
|
+
generated_at timestamptz NOT NULL DEFAULT now(),
|
|
56
|
+
expires_at timestamptz NOT NULL DEFAULT (now() + interval '7 days'),
|
|
57
|
+
invalidated boolean NOT NULL DEFAULT false
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
CREATE INDEX IF NOT EXISTS idx_growth_plan_cache_lookup
|
|
61
|
+
ON growth_plan_cache(agent_id, target_hash, invalidated, expires_at);
|
|
62
|
+
|
|
63
|
+
COMMENT ON TABLE growth_plan_cache IS 'Cached growth plans per agent+target. Invalidated on agent state changes.';
|
|
64
|
+
|
|
65
|
+
-- ============================================================
|
|
66
|
+
-- 4. RLS POLICIES
|
|
67
|
+
-- ============================================================
|
|
68
|
+
|
|
69
|
+
ALTER TABLE task_evidence ENABLE ROW LEVEL SECURITY;
|
|
70
|
+
ALTER TABLE growth_plan_cache ENABLE ROW LEVEL SECURITY;
|
|
71
|
+
|
|
72
|
+
-- task_evidence: anon can read and insert (MCP server enforces rules)
|
|
73
|
+
CREATE POLICY "anon_select_task_evidence" ON task_evidence
|
|
74
|
+
FOR SELECT TO anon USING (true);
|
|
75
|
+
|
|
76
|
+
CREATE POLICY "anon_insert_task_evidence" ON task_evidence
|
|
77
|
+
FOR INSERT TO anon WITH CHECK (true);
|
|
78
|
+
|
|
79
|
+
CREATE POLICY "anon_update_task_evidence" ON task_evidence
|
|
80
|
+
FOR UPDATE TO anon USING (true) WITH CHECK (true);
|
|
81
|
+
|
|
82
|
+
-- growth_plan_cache: anon can read, insert, and update (invalidation)
|
|
83
|
+
CREATE POLICY "anon_select_growth_plan_cache" ON growth_plan_cache
|
|
84
|
+
FOR SELECT TO anon USING (true);
|
|
85
|
+
|
|
86
|
+
CREATE POLICY "anon_insert_growth_plan_cache" ON growth_plan_cache
|
|
87
|
+
FOR INSERT TO anon WITH CHECK (true);
|
|
88
|
+
|
|
89
|
+
CREATE POLICY "anon_update_growth_plan_cache" ON growth_plan_cache
|
|
90
|
+
FOR UPDATE TO anon USING (true) WITH CHECK (true);
|
|
91
|
+
|
|
92
|
+
-- ============================================================
|
|
93
|
+
-- 5. UPDATE tasks RLS (allow updating new columns)
|
|
94
|
+
-- ============================================================
|
|
95
|
+
-- The existing anon UPDATE policy on tasks should already cover
|
|
96
|
+
-- evidence_required and evidence_quality since they're new columns
|
|
97
|
+
-- on the same table. No additional policy needed.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
-- ============================================================
|
|
2
|
+
-- Seed 35 quests across 6 categories
|
|
3
|
+
-- ============================================================
|
|
4
|
+
|
|
5
|
+
-- Onboarding Track (sequential, order 1-7)
|
|
6
|
+
INSERT INTO quests (title, description, source, quest_type, target_type, base_xp_reward, requirements, safe_for_autonomous, status, metadata)
|
|
7
|
+
VALUES
|
|
8
|
+
('First Steps', 'Register your Level-Up profile to start tracking XP and progress.', 'internal', 'default', 'user', 5, '{"type":"registration","target":1}', true, 'active', '{"category":"onboarding","order":1}'),
|
|
9
|
+
('Equip Your Agent', 'Add 3 skills to your agent. Skills improve task quality and reduce leveling costs.', 'internal', 'default', 'both', 15, '{"type":"skill_count","target":3}', true, 'active', '{"category":"onboarding","order":2}'),
|
|
10
|
+
('Tool Collector', 'Install 3 MCP tools. Each install awards +20 XP and unlocks new skill categories.', 'internal', 'default', 'both', 20, '{"type":"mcp_install","target":3}', true, 'active', '{"category":"onboarding","order":3}'),
|
|
11
|
+
('First Delivery', 'Complete your first task with evidence. This is where real XP starts flowing.', 'internal', 'default', 'both', 15, '{"type":"task_count","target":1,"filter":{"status":"completed"}}', true, 'active', '{"category":"onboarding","order":4}'),
|
|
12
|
+
('Quality Work', 'Complete a task with a quality score above 3.5. Evidence and proper difficulty help.', 'internal', 'default', 'both', 20, '{"type":"quality_score","target":3.5}', true, 'active', '{"category":"onboarding","order":5}'),
|
|
13
|
+
('Evidence Keeper', 'Complete a standard or higher task with strong evidence (URL, code output, or platform reference).', 'internal', 'default', 'both', 15, '{"type":"evidence_quality","target":1,"filter":{"quality":"strong"}}', true, 'active', '{"category":"onboarding","order":6}'),
|
|
14
|
+
('Rising Star', 'Reach Level 5. You''re no longer a beginner — complex tasks are now unlocked.', 'internal', 'default', 'agent', 30, '{"type":"level","target":5}', true, 'active', '{"category":"onboarding","order":7}');
|
|
15
|
+
|
|
16
|
+
-- Skill Development Track (unlocks at level 3)
|
|
17
|
+
INSERT INTO quests (title, description, source, quest_type, target_type, base_xp_reward, requirements, safe_for_autonomous, min_user_level, status, metadata)
|
|
18
|
+
VALUES
|
|
19
|
+
('Code Warrior', 'Complete 5 coding tasks (Bug Fix, Feature Build, or Code Review).', 'internal', 'default', 'both', 25, '{"type":"task_count","target":5,"filter":{"task_types":["Bug Fix","Feature Build","Code Review"]}}', true, 3, 'active', '{"category":"skill"}'),
|
|
20
|
+
('Wordsmith', 'Complete 5 writing tasks (Documentation, Email Draft, or Content Writing).', 'internal', 'default', 'both', 25, '{"type":"task_count","target":5,"filter":{"task_types":["Process Documentation","Email Draft","Content Writing"]}}', true, 3, 'active', '{"category":"skill"}'),
|
|
21
|
+
('Researcher', 'Complete 3 research tasks (Research Report or Competitive Analysis).', 'internal', 'default', 'both', 20, '{"type":"task_count","target":3,"filter":{"task_types":["Research Report","Competitive Analysis"]}}', true, 3, 'active', '{"category":"skill"}'),
|
|
22
|
+
('Ops Master', 'Complete 3 infrastructure tasks (Deployment or Pipeline Build).', 'internal', 'default', 'both', 30, '{"type":"task_count","target":3,"filter":{"task_types":["Deployment","Pipeline Build"]}}', true, 3, 'active', '{"category":"skill"}'),
|
|
23
|
+
('Multi-Talented', 'Have 5+ skills on your agent at proficiency 2 or higher.', 'internal', 'default', 'agent', 25, '{"type":"skill_proficiency_count","target":5,"filter":{"min_proficiency":2}}', true, 3, 'active', '{"category":"skill"}'),
|
|
24
|
+
('Specialist', 'Reach proficiency 5 in any single skill. Deep expertise pays off.', 'internal', 'default', 'agent', 35, '{"type":"skill_proficiency_max","target":5}', true, 3, 'active', '{"category":"skill"}');
|
|
25
|
+
|
|
26
|
+
-- Automation Track (unlocks at level 5)
|
|
27
|
+
INSERT INTO quests (title, description, source, quest_type, target_type, base_xp_reward, requirements, safe_for_autonomous, min_user_level, status, metadata)
|
|
28
|
+
VALUES
|
|
29
|
+
('Hands Off', 'Complete a task with agent_solo performer type. Let the agent fly solo.', 'internal', 'default', 'agent', 20, '{"type":"performer_type","target":1,"filter":{"performer_type":"agent_solo"}}', true, 5, 'active', '{"category":"automation"}'),
|
|
30
|
+
('Dynamic Duo', 'Complete 10 tasks together as user_with_agent. Teamwork makes the dream work.', 'internal', 'default', 'both', 30, '{"type":"performer_type","target":10,"filter":{"performer_type":"user_with_agent"}}', true, 5, 'active', '{"category":"automation"}'),
|
|
31
|
+
('Orchestrator', 'Complete a task with orchestrated performer type involving multiple agents.', 'internal', 'default', 'both', 50, '{"type":"performer_type","target":1,"filter":{"performer_type":"orchestrated"}}', false, 5, 'active', '{"category":"automation"}'),
|
|
32
|
+
('Deployer', 'Complete a task with output_type deployed. Ship something live.', 'internal', 'default', 'both', 40, '{"type":"output_type","target":1,"filter":{"output_type":"deployed"}}', true, 5, 'active', '{"category":"automation"}'),
|
|
33
|
+
('Chain Reaction', 'Complete 3 different task types in a single day. Variety is the spice of XP.', 'internal', 'default', 'both', 25, '{"type":"daily_task_diversity","target":3}', true, 5, 'active', '{"category":"automation"}');
|
|
34
|
+
|
|
35
|
+
-- Weekly Challenges (recurring)
|
|
36
|
+
INSERT INTO quests (title, description, source, quest_type, target_type, base_xp_reward, requirements, safe_for_autonomous, status, metadata)
|
|
37
|
+
VALUES
|
|
38
|
+
('Productive Week', 'Complete 5 tasks in 7 days. Consistency beats intensity.', 'internal', 'recurring', 'both', 25, '{"type":"task_count","target":5,"filter":{"period":"week"}}', true, 'active', '{"category":"weekly"}'),
|
|
39
|
+
('Skill Builder', 'Increase any skill proficiency by 1 level this week.', 'internal', 'recurring', 'agent', 15, '{"type":"skill_proficiency_gain","target":1,"filter":{"period":"week"}}', true, 'active', '{"category":"weekly"}'),
|
|
40
|
+
('Streak Keeper', 'Complete at least 1 task per day for 5 consecutive days.', 'internal', 'recurring', 'both', 30, '{"type":"streak","target":5}', true, 'active', '{"category":"weekly"}'),
|
|
41
|
+
('Diversity Bonus', 'Complete 3 different task types this week. Breadth earns bonus XP.', 'internal', 'recurring', 'both', 20, '{"type":"weekly_task_diversity","target":3}', true, 'active', '{"category":"weekly"}'),
|
|
42
|
+
('High Standards', 'Achieve an average quality score above 4.0 across all tasks this week.', 'internal', 'recurring', 'both', 25, '{"type":"quality_score_avg","target":4.0,"filter":{"period":"week"}}', true, 'active', '{"category":"weekly"}');
|
|
43
|
+
|
|
44
|
+
-- Achievement Quests (one-off milestones)
|
|
45
|
+
INSERT INTO quests (title, description, source, quest_type, target_type, base_xp_reward, requirements, safe_for_autonomous, status, metadata)
|
|
46
|
+
VALUES
|
|
47
|
+
('Century', 'Earn 100 total XP. Your first major milestone.', 'internal', 'one_off', 'both', 10, '{"type":"total_xp","target":100}', true, 'active', '{"category":"achievement"}'),
|
|
48
|
+
('Rank D Achiever', 'Reach Rank D — Pathfinder. You''re finding your way.', 'internal', 'one_off', 'agent', 20, '{"type":"rank","target":1,"filter":{"rank":"D"}}', true, 'active', '{"category":"achievement"}'),
|
|
49
|
+
('Rank C Achiever', 'Reach Rank C — Specialist. Real expertise recognized.', 'internal', 'one_off', 'agent', 40, '{"type":"rank","target":1,"filter":{"rank":"C"}}', true, 'active', '{"category":"achievement"}'),
|
|
50
|
+
('Half Century Tasks', 'Complete 50 tasks total. Dedication pays off.', 'internal', 'one_off', 'both', 30, '{"type":"task_count","target":50}', true, 'active', '{"category":"achievement"}'),
|
|
51
|
+
('Century Tasks', 'Complete 100 tasks total. You''re a machine (or working with one).', 'internal', 'one_off', 'both', 50, '{"type":"task_count","target":100}', true, 'active', '{"category":"achievement"}'),
|
|
52
|
+
('Integrity Champion', 'Maintain integrity score above 80 for 30 consecutive days.', 'internal', 'one_off', 'agent', 40, '{"type":"integrity_duration","target":30,"filter":{"min_score":80}}', true, 'active', '{"category":"achievement"}'),
|
|
53
|
+
('Community Builder', 'Have your work reviewed by another agent. Collaboration earns respect.', 'internal', 'one_off', 'both', 25, '{"type":"peer_review","target":1}', false, 'active', '{"category":"achievement"}');
|
|
54
|
+
|
|
55
|
+
-- Exploration Quests (discover features)
|
|
56
|
+
INSERT INTO quests (title, description, source, quest_type, target_type, base_xp_reward, requirements, safe_for_autonomous, status, metadata)
|
|
57
|
+
VALUES
|
|
58
|
+
('Growth Minded', 'Check your growth plan to see the path to the next rank.', 'internal', 'one_off', 'both', 5, '{"type":"tool_call","target":1,"filter":{"tool":"levelup_get_growth_plan"}}', true, 'active', '{"category":"exploration"}'),
|
|
59
|
+
('Skill Browser', 'Browse recommended skillsets for 3 or more ability categories.', 'internal', 'one_off', 'both', 10, '{"type":"tool_call","target":3,"filter":{"tool":"levelup_browse_skillsets"}}', true, 'active', '{"category":"exploration"}'),
|
|
60
|
+
('Leaderboard Climber', 'Check the leaderboard to see where you stand.', 'internal', 'one_off', 'both', 5, '{"type":"tool_call","target":1,"filter":{"tool":"levelup_get_leaderboard"}}', true, 'active', '{"category":"exploration"}'),
|
|
61
|
+
('Self Aware', 'Submit an agent self-evaluation after completing a task.', 'internal', 'one_off', 'agent', 10, '{"type":"tool_call","target":1,"filter":{"tool":"levelup_submit_agent_evaluation"}}', true, 'active', '{"category":"exploration"}'),
|
|
62
|
+
('Profile Complete', 'Set a vanity handle and link an identity to your profile.', 'internal', 'one_off', 'user', 10, '{"type":"profile_complete","target":1}', true, 'active', '{"category":"exploration"}');
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
-- ============================================================
|
|
2
|
+
-- Add last_leveled_at for star cooldown enforcement
|
|
3
|
+
-- Fix quest_completions split_pct default
|
|
4
|
+
-- ============================================================
|
|
5
|
+
|
|
6
|
+
-- Add last_leveled_at columns for star cooldown tracking
|
|
7
|
+
ALTER TABLE users ADD COLUMN IF NOT EXISTS last_leveled_at timestamptz;
|
|
8
|
+
ALTER TABLE agents ADD COLUMN IF NOT EXISTS last_leveled_at timestamptz;
|
|
9
|
+
|
|
10
|
+
-- Set default for quest_completions.split_pct so it doesn't require explicit value
|
|
11
|
+
ALTER TABLE quest_completions ALTER COLUMN split_pct SET DEFAULT 100;
|
|
12
|
+
|
|
13
|
+
-- Add user_star_cooldown_hours config if missing (matches agent version)
|
|
14
|
+
INSERT INTO xp_config (config_key, config_value, description)
|
|
15
|
+
VALUES ('user_star_cooldown_hours', '{"E":0,"D":12,"C":72,"B":168,"A":504,"S":504}', 'Hours between level-ups per rank for users')
|
|
16
|
+
ON CONFLICT (config_key) DO NOTHING;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
-- ============================================================
|
|
2
|
+
-- Restore canonical rank names for users and agents
|
|
3
|
+
-- ============================================================
|
|
4
|
+
-- User ranks: Analyst → Tactician → Strategist → Commander → Grandmaster → Supreme
|
|
5
|
+
-- Agent ranks: Novice → Pathfinder → Enlightened → Paragon → Transcendent → Celestial
|
|
6
|
+
--
|
|
7
|
+
-- These are the original rank names designed for the Level-Up system.
|
|
8
|
+
-- This migration ensures they are correct regardless of prior DB state.
|
|
9
|
+
-- ============================================================
|
|
10
|
+
|
|
11
|
+
-- User ranks (entity_type = 'user')
|
|
12
|
+
UPDATE rank_definitions SET rank_name = 'Analyst' WHERE rank_letter = 'E' AND entity_type = 'user';
|
|
13
|
+
UPDATE rank_definitions SET rank_name = 'Tactician' WHERE rank_letter = 'D' AND entity_type = 'user';
|
|
14
|
+
UPDATE rank_definitions SET rank_name = 'Strategist' WHERE rank_letter = 'C' AND entity_type = 'user';
|
|
15
|
+
UPDATE rank_definitions SET rank_name = 'Commander' WHERE rank_letter = 'B' AND entity_type = 'user';
|
|
16
|
+
UPDATE rank_definitions SET rank_name = 'Grandmaster' WHERE rank_letter = 'A' AND entity_type = 'user';
|
|
17
|
+
UPDATE rank_definitions SET rank_name = 'Supreme' WHERE rank_letter = 'S' AND entity_type = 'user';
|
|
18
|
+
|
|
19
|
+
-- Agent ranks (entity_type = 'agent')
|
|
20
|
+
UPDATE rank_definitions SET rank_name = 'Novice' WHERE rank_letter = 'E' AND entity_type = 'agent';
|
|
21
|
+
UPDATE rank_definitions SET rank_name = 'Pathfinder' WHERE rank_letter = 'D' AND entity_type = 'agent';
|
|
22
|
+
UPDATE rank_definitions SET rank_name = 'Enlightened' WHERE rank_letter = 'C' AND entity_type = 'agent';
|
|
23
|
+
UPDATE rank_definitions SET rank_name = 'Paragon' WHERE rank_letter = 'B' AND entity_type = 'agent';
|
|
24
|
+
UPDATE rank_definitions SET rank_name = 'Transcendent' WHERE rank_letter = 'A' AND entity_type = 'agent';
|
|
25
|
+
UPDATE rank_definitions SET rank_name = 'Celestial' WHERE rank_letter = 'S' AND entity_type = 'agent';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
-- ============================================================
|
|
2
|
+
-- Migration: Chinese (zh) rank names for Level-Up CN
|
|
3
|
+
-- ============================================================
|
|
4
|
+
-- User ranks: 分析师 → 战术师 → 策略师 → 指挥官 → 大师 → 至尊
|
|
5
|
+
-- Agent ranks (xianxia cultivation): 练器境 → 筑基期 → 金丹期 → 元婴期 → 大乘期 → 人工大能
|
|
6
|
+
-- ============================================================
|
|
7
|
+
|
|
8
|
+
INSERT INTO rank_definitions (rank_letter, entity_type, rank_name, min_level, max_level, locale)
|
|
9
|
+
VALUES
|
|
10
|
+
-- User ranks (zh)
|
|
11
|
+
('E', 'user', '分析师', 1, 5, 'zh'),
|
|
12
|
+
('D', 'user', '战术师', 6, 10, 'zh'),
|
|
13
|
+
('C', 'user', '策略师', 11, 15, 'zh'),
|
|
14
|
+
('B', 'user', '指挥官', 16, 20, 'zh'),
|
|
15
|
+
('A', 'user', '大师', 21, 25, 'zh'),
|
|
16
|
+
('S', 'user', '至尊', 26, 30, 'zh'),
|
|
17
|
+
-- Agent ranks (zh) — xianxia cultivation realms
|
|
18
|
+
('E', 'agent', '练器境', 1, 5, 'zh'),
|
|
19
|
+
('D', 'agent', '筑基期', 6, 10, 'zh'),
|
|
20
|
+
('C', 'agent', '金丹期', 11, 15, 'zh'),
|
|
21
|
+
('B', 'agent', '元婴期', 16, 20, 'zh'),
|
|
22
|
+
('A', 'agent', '大乘期', 21, 25, 'zh'),
|
|
23
|
+
('S', 'agent', '人工大能', 26, 30, 'zh')
|
|
24
|
+
ON CONFLICT DO NOTHING;
|