devchain-cli 0.7.2 → 0.8.1

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.
Files changed (135) hide show
  1. package/dist/drizzle/0027_legal_malice.sql +17 -0
  2. package/dist/drizzle/0028_populate_provider_configs.sql +19 -0
  3. package/dist/drizzle/0029_merge_profiles_by_family_slug.sql +116 -0
  4. package/dist/drizzle/0030_set_agents_provider_config_id.sql +14 -0
  5. package/dist/drizzle/0031_remove_legacy_profile_columns.sql +68 -0
  6. package/dist/drizzle/0032_agents_provider_config_not_null.sql +52 -0
  7. package/dist/drizzle/0033_profile_provider_configs_name.sql +38 -0
  8. package/dist/drizzle/0034_sessions_agent_running_unique.sql +30 -0
  9. package/dist/drizzle/0035_provider_config_position.sql +26 -0
  10. package/dist/drizzle/PHASE2_MIGRATION_GUIDE.md +109 -0
  11. package/dist/drizzle/meta/0027_snapshot.json +3648 -0
  12. package/dist/drizzle/meta/0035_snapshot.json +3670 -0
  13. package/dist/drizzle/meta/_journal.json +64 -1
  14. package/dist/drizzle/migration-guard.sql +17 -0
  15. package/dist/drizzle/phase2-rollback.sql +44 -0
  16. package/dist/node_modules/@devchain/shared/schemas/export-schema.d.ts +101 -12
  17. package/dist/node_modules/@devchain/shared/schemas/export-schema.d.ts.map +1 -1
  18. package/dist/node_modules/@devchain/shared/schemas/export-schema.js +20 -0
  19. package/dist/node_modules/@devchain/shared/schemas/export-schema.js.map +1 -1
  20. package/dist/node_modules/@devchain/shared/tsconfig.tsbuildinfo +1 -1
  21. package/dist/server/modules/agents/controllers/agents.controller.d.ts +15 -0
  22. package/dist/server/modules/agents/controllers/agents.controller.js +120 -57
  23. package/dist/server/modules/agents/controllers/agents.controller.js.map +1 -1
  24. package/dist/server/modules/core/services/preflight.service.d.ts +6 -0
  25. package/dist/server/modules/core/services/preflight.service.js +143 -57
  26. package/dist/server/modules/core/services/preflight.service.js.map +1 -1
  27. package/dist/server/modules/epics/services/epics.service.d.ts +10 -4
  28. package/dist/server/modules/epics/services/epics.service.js +8 -5
  29. package/dist/server/modules/epics/services/epics.service.js.map +1 -1
  30. package/dist/server/modules/events/catalog/epic.created.d.ts +18 -0
  31. package/dist/server/modules/events/catalog/epic.created.js +8 -0
  32. package/dist/server/modules/events/catalog/epic.created.js.map +1 -1
  33. package/dist/server/modules/events/catalog/epic.updated.d.ts +18 -0
  34. package/dist/server/modules/events/catalog/epic.updated.js +8 -0
  35. package/dist/server/modules/events/catalog/epic.updated.js.map +1 -1
  36. package/dist/server/modules/events/catalog/index.d.ts +36 -0
  37. package/dist/server/modules/events/subscribers/epic-assignment-notifier.subscriber.d.ts +1 -0
  38. package/dist/server/modules/events/subscribers/epic-assignment-notifier.subscriber.js +33 -2
  39. package/dist/server/modules/events/subscribers/epic-assignment-notifier.subscriber.js.map +1 -1
  40. package/dist/server/modules/events/subscribers/review-comment-notifier.subscriber.js +12 -5
  41. package/dist/server/modules/events/subscribers/review-comment-notifier.subscriber.js.map +1 -1
  42. package/dist/server/modules/mcp/services/mcp.service.js +16 -2
  43. package/dist/server/modules/mcp/services/mcp.service.js.map +1 -1
  44. package/dist/server/modules/mcp/services/terminal-activity.service.d.ts +1 -1
  45. package/dist/server/modules/mcp/services/terminal-activity.service.js +22 -2
  46. package/dist/server/modules/mcp/services/terminal-activity.service.js.map +1 -1
  47. package/dist/server/modules/profiles/controllers/profiles.controller.d.ts +11 -8
  48. package/dist/server/modules/profiles/controllers/profiles.controller.js +88 -14
  49. package/dist/server/modules/profiles/controllers/profiles.controller.js.map +1 -1
  50. package/dist/server/modules/profiles/controllers/provider-configs.controller.d.ts +9 -0
  51. package/dist/server/modules/profiles/controllers/provider-configs.controller.js +94 -0
  52. package/dist/server/modules/profiles/controllers/provider-configs.controller.js.map +1 -0
  53. package/dist/server/modules/profiles/dto.d.ts +81 -6
  54. package/dist/server/modules/profiles/dto.js +45 -3
  55. package/dist/server/modules/profiles/dto.js.map +1 -1
  56. package/dist/server/modules/profiles/profiles.module.js +2 -1
  57. package/dist/server/modules/profiles/profiles.module.js.map +1 -1
  58. package/dist/server/modules/projects/controllers/projects.controller.d.ts +78 -19
  59. package/dist/server/modules/projects/controllers/projects.controller.js +243 -17
  60. package/dist/server/modules/projects/controllers/projects.controller.js.map +1 -1
  61. package/dist/server/modules/projects/dtos/export.dto.d.ts +44 -0
  62. package/dist/server/modules/projects/dtos/export.dto.js +10 -0
  63. package/dist/server/modules/projects/dtos/export.dto.js.map +1 -1
  64. package/dist/server/modules/projects/services/projects.service.d.ts +48 -11
  65. package/dist/server/modules/projects/services/projects.service.js +359 -27
  66. package/dist/server/modules/projects/services/projects.service.js.map +1 -1
  67. package/dist/server/modules/providers/controllers/providers.controller.js +12 -7
  68. package/dist/server/modules/providers/controllers/providers.controller.js.map +1 -1
  69. package/dist/server/modules/registry/controllers/templates.controller.d.ts +2 -2
  70. package/dist/server/modules/registry/services/registry-orchestration.service.js +11 -0
  71. package/dist/server/modules/registry/services/registry-orchestration.service.js.map +1 -1
  72. package/dist/server/modules/registry/services/template-upgrade.service.js +6 -0
  73. package/dist/server/modules/registry/services/template-upgrade.service.js.map +1 -1
  74. package/dist/server/modules/registry/services/unified-template.service.d.ts +2 -1
  75. package/dist/server/modules/registry/services/unified-template.service.js +61 -0
  76. package/dist/server/modules/registry/services/unified-template.service.js.map +1 -1
  77. package/dist/server/modules/reviews/services/reviews.service.js +8 -0
  78. package/dist/server/modules/reviews/services/reviews.service.js.map +1 -1
  79. package/dist/server/modules/sessions/services/activity-tracker.service.d.ts +2 -0
  80. package/dist/server/modules/sessions/services/activity-tracker.service.js +20 -0
  81. package/dist/server/modules/sessions/services/activity-tracker.service.js.map +1 -1
  82. package/dist/server/modules/sessions/services/sessions-message-pool.service.js +2 -2
  83. package/dist/server/modules/sessions/services/sessions-message-pool.service.js.map +1 -1
  84. package/dist/server/modules/sessions/services/sessions.service.d.ts +3 -1
  85. package/dist/server/modules/sessions/services/sessions.service.js +275 -144
  86. package/dist/server/modules/sessions/services/sessions.service.js.map +1 -1
  87. package/dist/server/modules/sessions/utils/env-builder.d.ts +8 -0
  88. package/dist/server/modules/sessions/utils/env-builder.js +62 -0
  89. package/dist/server/modules/sessions/utils/env-builder.js.map +1 -0
  90. package/dist/server/modules/settings/dtos/settings.dto.d.ts +84 -8
  91. package/dist/server/modules/settings/dtos/settings.dto.js +15 -2
  92. package/dist/server/modules/settings/dtos/settings.dto.js.map +1 -1
  93. package/dist/server/modules/settings/services/settings.service.d.ts +10 -1
  94. package/dist/server/modules/settings/services/settings.service.js +275 -0
  95. package/dist/server/modules/settings/services/settings.service.js.map +1 -1
  96. package/dist/server/modules/storage/db/db.provider.js +2 -0
  97. package/dist/server/modules/storage/db/db.provider.js.map +1 -1
  98. package/dist/server/modules/storage/db/schema.d.ts +185 -28
  99. package/dist/server/modules/storage/db/schema.js +32 -6
  100. package/dist/server/modules/storage/db/schema.js.map +1 -1
  101. package/dist/server/modules/storage/interfaces/storage.interface.d.ts +17 -1
  102. package/dist/server/modules/storage/interfaces/storage.interface.js.map +1 -1
  103. package/dist/server/modules/storage/local/local-storage.service.d.ts +15 -1
  104. package/dist/server/modules/storage/local/local-storage.service.js +352 -21
  105. package/dist/server/modules/storage/local/local-storage.service.js.map +1 -1
  106. package/dist/server/modules/storage/models/domain.models.d.ts +26 -7
  107. package/dist/server/modules/subscribers/actions/restart-agent.action.js +17 -19
  108. package/dist/server/modules/subscribers/actions/restart-agent.action.js.map +1 -1
  109. package/dist/server/modules/subscribers/events/event-fields-catalog.js +4 -0
  110. package/dist/server/modules/subscribers/events/event-fields-catalog.js.map +1 -1
  111. package/dist/server/modules/terminal/services/pty.service.js +6 -1
  112. package/dist/server/modules/terminal/services/pty.service.js.map +1 -1
  113. package/dist/server/modules/watchers/services/watcher-runner.service.js +27 -8
  114. package/dist/server/modules/watchers/services/watcher-runner.service.js.map +1 -1
  115. package/dist/{templates/claude-opus.json → server/templates/dev-loop.json} +379 -197
  116. package/dist/server/tsconfig.tsbuildinfo +1 -1
  117. package/dist/server/ui/assets/{ReviewDetailPage-I54h-2L-.js → ReviewDetailPage-YlFGGJv7.js} +2 -2
  118. package/dist/server/ui/assets/ReviewsPage-Diegg4jt.js +19 -0
  119. package/dist/server/ui/assets/index-DuMIsIyY.css +32 -0
  120. package/dist/server/ui/assets/index-sEtQpiB4.js +868 -0
  121. package/dist/server/ui/assets/{useReviewSubscription-C0GEsiRw.js → useReviewSubscription-CMhQ2m0h.js} +22 -22
  122. package/dist/server/ui/index.html +2 -2
  123. package/dist/{server/templates/claude-opus.json → templates/dev-loop.json} +379 -197
  124. package/package.json +12 -4
  125. package/dist/node_modules/@devchain/shared/schemas/export-schema.spec.d.ts +0 -2
  126. package/dist/node_modules/@devchain/shared/schemas/export-schema.spec.d.ts.map +0 -1
  127. package/dist/node_modules/@devchain/shared/schemas/export-schema.spec.js +0 -88
  128. package/dist/node_modules/@devchain/shared/schemas/export-schema.spec.js.map +0 -1
  129. package/dist/server/templates/claude-codex-advanced.json +0 -470
  130. package/dist/server/templates/simple-codex.json +0 -469
  131. package/dist/server/ui/assets/ReviewsPage-B4ua5hiX.js +0 -19
  132. package/dist/server/ui/assets/index-CqcmnFBh.css +0 -32
  133. package/dist/server/ui/assets/index-JbUMpbg7.js +0 -858
  134. package/dist/templates/claude-codex-advanced.json +0 -470
  135. package/dist/templates/simple-codex.json +0 -469
@@ -0,0 +1,17 @@
1
+ CREATE TABLE `profile_provider_configs` (
2
+ `id` text PRIMARY KEY NOT NULL,
3
+ `profile_id` text NOT NULL,
4
+ `provider_id` text NOT NULL,
5
+ `options` text,
6
+ `env` text,
7
+ `created_at` text NOT NULL,
8
+ `updated_at` text NOT NULL,
9
+ FOREIGN KEY (`profile_id`) REFERENCES `agent_profiles`(`id`) ON UPDATE no action ON DELETE cascade,
10
+ FOREIGN KEY (`provider_id`) REFERENCES `providers`(`id`) ON UPDATE no action ON DELETE no action
11
+ );
12
+ --> statement-breakpoint
13
+ CREATE INDEX `profile_provider_configs_profile_id_idx` ON `profile_provider_configs` (`profile_id`);
14
+ --> statement-breakpoint
15
+ CREATE INDEX `profile_provider_configs_provider_id_idx` ON `profile_provider_configs` (`provider_id`);
16
+ --> statement-breakpoint
17
+ ALTER TABLE `agents` ADD `provider_config_id` text REFERENCES profile_provider_configs(id);
@@ -0,0 +1,19 @@
1
+ -- Phase 2 Migration: Populate profile_provider_configs from existing profiles
2
+ -- For each profile with a providerId, create a corresponding provider config record
3
+
4
+ INSERT INTO profile_provider_configs (id, profile_id, provider_id, options, env, created_at, updated_at)
5
+ SELECT
6
+ lower(hex(randomblob(4))) || '-' || lower(hex(randomblob(2))) || '-4' || substr(lower(hex(randomblob(2))),2) || '-' || substr('89ab',abs(random()) % 4 + 1, 1) || substr(lower(hex(randomblob(2))),2) || '-' || lower(hex(randomblob(6))) as id,
7
+ id as profile_id,
8
+ provider_id,
9
+ options,
10
+ NULL as env,
11
+ datetime('now') as created_at,
12
+ datetime('now') as updated_at
13
+ FROM agent_profiles
14
+ WHERE provider_id IS NOT NULL
15
+ AND NOT EXISTS (
16
+ SELECT 1 FROM profile_provider_configs ppc
17
+ WHERE ppc.profile_id = agent_profiles.id
18
+ AND ppc.provider_id = agent_profiles.provider_id
19
+ );
@@ -0,0 +1,116 @@
1
+ -- Phase 2 Migration: Auto-merge profiles by familySlug
2
+ -- For each (projectId, familySlug) group with multiple profiles:
3
+ -- 1. Keep oldest as canonical
4
+ -- 2. Capture agent→providerId mapping (BEFORE profile deletion)
5
+ -- 3. Move provider configs to canonical
6
+ -- 4. Set agents.provider_config_id based on original providerId
7
+ -- 5. Update agents to point to canonical profile
8
+ -- 6. Delete merged profiles
9
+
10
+ -- Step 1: Create temp table identifying canonical profiles (oldest per group)
11
+ CREATE TEMP TABLE canonical_profiles AS
12
+ WITH ranked AS (
13
+ SELECT
14
+ id,
15
+ project_id,
16
+ family_slug,
17
+ created_at,
18
+ ROW_NUMBER() OVER (PARTITION BY project_id, family_slug ORDER BY created_at ASC) as rn
19
+ FROM agent_profiles
20
+ WHERE family_slug IS NOT NULL
21
+ )
22
+ SELECT id as canonical_id, project_id, family_slug
23
+ FROM ranked
24
+ WHERE rn = 1
25
+ AND EXISTS (
26
+ SELECT 1 FROM ranked r2
27
+ WHERE r2.project_id = ranked.project_id
28
+ AND r2.family_slug = ranked.family_slug
29
+ AND r2.rn > 1
30
+ );
31
+
32
+ --> statement-breakpoint
33
+
34
+ -- Step 2: Create temp table with profiles to merge (non-canonical in groups)
35
+ CREATE TEMP TABLE profiles_to_merge AS
36
+ WITH ranked AS (
37
+ SELECT
38
+ id,
39
+ project_id,
40
+ family_slug,
41
+ ROW_NUMBER() OVER (PARTITION BY project_id, family_slug ORDER BY created_at ASC) as rn
42
+ FROM agent_profiles
43
+ WHERE family_slug IS NOT NULL
44
+ )
45
+ SELECT ranked.id as merge_id, cp.canonical_id
46
+ FROM ranked
47
+ JOIN canonical_profiles cp
48
+ ON ranked.project_id = cp.project_id
49
+ AND ranked.family_slug = cp.family_slug
50
+ WHERE ranked.rn > 1;
51
+
52
+ --> statement-breakpoint
53
+
54
+ -- Step 3: Capture agent→providerId mapping BEFORE profiles are merged/deleted
55
+ -- This preserves which provider each agent was originally using
56
+ CREATE TEMP TABLE agent_original_provider AS
57
+ SELECT a.id as agent_id, ap.provider_id
58
+ FROM agents a
59
+ JOIN agent_profiles ap ON a.profile_id = ap.id
60
+ WHERE a.profile_id IN (SELECT merge_id FROM profiles_to_merge);
61
+
62
+ --> statement-breakpoint
63
+
64
+ -- Step 4: Update provider configs - move from merged profiles to canonical
65
+ UPDATE profile_provider_configs
66
+ SET profile_id = (
67
+ SELECT canonical_id FROM profiles_to_merge ptm
68
+ WHERE ptm.merge_id = profile_provider_configs.profile_id
69
+ )
70
+ WHERE profile_id IN (SELECT merge_id FROM profiles_to_merge);
71
+
72
+ --> statement-breakpoint
73
+
74
+ -- Step 5: Set agents.provider_config_id based on original providerId
75
+ -- Match each agent to the config in their new canonical profile that has the same providerId
76
+ UPDATE agents
77
+ SET provider_config_id = (
78
+ SELECT ppc.id
79
+ FROM agent_original_provider aop
80
+ JOIN profiles_to_merge ptm ON agents.profile_id = ptm.merge_id
81
+ JOIN profile_provider_configs ppc ON ppc.profile_id = ptm.canonical_id
82
+ WHERE aop.agent_id = agents.id
83
+ AND ppc.provider_id = aop.provider_id
84
+ LIMIT 1
85
+ )
86
+ WHERE profile_id IN (SELECT merge_id FROM profiles_to_merge)
87
+ AND provider_config_id IS NULL;
88
+
89
+ --> statement-breakpoint
90
+
91
+ -- Step 6: Update agents - point to canonical profile
92
+ UPDATE agents
93
+ SET profile_id = (
94
+ SELECT canonical_id FROM profiles_to_merge ptm
95
+ WHERE ptm.merge_id = agents.profile_id
96
+ )
97
+ WHERE profile_id IN (SELECT merge_id FROM profiles_to_merge);
98
+
99
+ --> statement-breakpoint
100
+
101
+ -- Step 7: Delete merged profiles
102
+ DELETE FROM agent_profiles
103
+ WHERE id IN (SELECT merge_id FROM profiles_to_merge);
104
+
105
+ --> statement-breakpoint
106
+
107
+ -- Step 8: Cleanup temp tables
108
+ DROP TABLE agent_original_provider;
109
+
110
+ --> statement-breakpoint
111
+
112
+ DROP TABLE profiles_to_merge;
113
+
114
+ --> statement-breakpoint
115
+
116
+ DROP TABLE canonical_profiles;
@@ -0,0 +1,14 @@
1
+ -- Phase 2 Migration: Set agents.providerConfigId
2
+ -- For agents not handled by 0029 (non-merged profiles), set providerConfigId
3
+ -- Match config by profile's provider_id (not by creation order)
4
+
5
+ UPDATE agents
6
+ SET provider_config_id = (
7
+ SELECT ppc.id
8
+ FROM profile_provider_configs ppc
9
+ JOIN agent_profiles ap ON ap.id = agents.profile_id
10
+ WHERE ppc.profile_id = agents.profile_id
11
+ AND ppc.provider_id = ap.provider_id
12
+ LIMIT 1
13
+ )
14
+ WHERE provider_config_id IS NULL;
@@ -0,0 +1,68 @@
1
+ -- Phase 4: Remove legacy providerId and options columns from agent_profiles
2
+ -- Provider configuration now lives in profile_provider_configs table
3
+ -- This migration:
4
+ -- 1. Rebuilds agent_profiles without providerId and options columns
5
+ -- 2. Replaces family_provider_unique index with family_unique index
6
+
7
+ -- Note: foreign_keys are disabled in db.provider.ts before running migrations
8
+ -- (PRAGMA foreign_keys=OFF has no effect inside a transaction)
9
+
10
+ -- Step 1: Create new table without providerId and options columns
11
+ CREATE TABLE `__new_agent_profiles` (
12
+ `id` text PRIMARY KEY NOT NULL,
13
+ `project_id` text,
14
+ `name` text NOT NULL,
15
+ `family_slug` text,
16
+ `system_prompt` text,
17
+ `instructions` text,
18
+ `temperature` integer,
19
+ `max_tokens` integer,
20
+ `created_at` text NOT NULL,
21
+ `updated_at` text NOT NULL,
22
+ FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE cascade
23
+ );
24
+ --> statement-breakpoint
25
+
26
+ -- Step 2: Copy data (excluding providerId and options)
27
+ INSERT INTO `__new_agent_profiles` (
28
+ `id`,
29
+ `project_id`,
30
+ `name`,
31
+ `family_slug`,
32
+ `system_prompt`,
33
+ `instructions`,
34
+ `temperature`,
35
+ `max_tokens`,
36
+ `created_at`,
37
+ `updated_at`
38
+ )
39
+ SELECT
40
+ `id`,
41
+ `project_id`,
42
+ `name`,
43
+ `family_slug`,
44
+ `system_prompt`,
45
+ `instructions`,
46
+ `temperature`,
47
+ `max_tokens`,
48
+ `created_at`,
49
+ `updated_at`
50
+ FROM `agent_profiles`;
51
+ --> statement-breakpoint
52
+
53
+ -- Step 3: Drop old table
54
+ DROP TABLE `agent_profiles`;
55
+ --> statement-breakpoint
56
+
57
+ -- Step 4: Rename new table
58
+ ALTER TABLE `__new_agent_profiles` RENAME TO `agent_profiles`;
59
+ --> statement-breakpoint
60
+
61
+ -- Step 5: Recreate indexes
62
+ -- Keep: unique constraint on (project_id, name)
63
+ CREATE UNIQUE INDEX `agent_profiles_project_name_unique` ON `agent_profiles` (`project_id`, `name`);
64
+ --> statement-breakpoint
65
+
66
+ -- New: unique constraint on (project_id, family_slug) - profiles are now provider-independent
67
+ -- Note: Replaces old agent_profiles_family_provider_unique which included provider_id
68
+ CREATE UNIQUE INDEX `agent_profiles_family_unique` ON `agent_profiles` (`project_id`, `family_slug`) WHERE `family_slug` IS NOT NULL;
@@ -0,0 +1,52 @@
1
+ -- Phase 4: Make agents.providerConfigId NOT NULL
2
+ -- Prerequisites:
3
+ -- - Migration 0030 must have run to set all providerConfigId values
4
+ -- - No agents should have NULL providerConfigId
5
+ -- This migration will FAIL if any agent has NULL providerConfigId
6
+ -- (the INSERT into the NOT NULL column will fail naturally)
7
+
8
+ -- Rebuild agents table with NOT NULL constraint
9
+ -- Note: foreign_keys are disabled in db.provider.ts before running migrations
10
+ -- (PRAGMA foreign_keys=OFF has no effect inside a transaction)
11
+
12
+ CREATE TABLE `__new_agents` (
13
+ `id` text PRIMARY KEY NOT NULL,
14
+ `project_id` text NOT NULL,
15
+ `profile_id` text NOT NULL,
16
+ `provider_config_id` text NOT NULL,
17
+ `name` text NOT NULL,
18
+ `description` text,
19
+ `created_at` text NOT NULL,
20
+ `updated_at` text NOT NULL,
21
+ FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON UPDATE no action ON DELETE cascade,
22
+ FOREIGN KEY (`profile_id`) REFERENCES `agent_profiles`(`id`) ON UPDATE no action ON DELETE no action,
23
+ FOREIGN KEY (`provider_config_id`) REFERENCES `profile_provider_configs`(`id`) ON UPDATE no action ON DELETE restrict
24
+ );
25
+ --> statement-breakpoint
26
+
27
+ INSERT INTO `__new_agents` (
28
+ `id`,
29
+ `project_id`,
30
+ `profile_id`,
31
+ `provider_config_id`,
32
+ `name`,
33
+ `description`,
34
+ `created_at`,
35
+ `updated_at`
36
+ )
37
+ SELECT
38
+ `id`,
39
+ `project_id`,
40
+ `profile_id`,
41
+ `provider_config_id`,
42
+ `name`,
43
+ `description`,
44
+ `created_at`,
45
+ `updated_at`
46
+ FROM `agents`;
47
+ --> statement-breakpoint
48
+
49
+ DROP TABLE `agents`;
50
+ --> statement-breakpoint
51
+
52
+ ALTER TABLE `__new_agents` RENAME TO `agents`;
@@ -0,0 +1,38 @@
1
+ -- Migration: Add name column to profile_provider_configs
2
+ -- This enables multiple configs per provider per profile (GLM use case)
3
+
4
+ -- Step 1: Add name column with default empty string
5
+ ALTER TABLE profile_provider_configs ADD COLUMN name TEXT NOT NULL DEFAULT '';
6
+ --> statement-breakpoint
7
+
8
+ -- Step 2: Backfill existing rows with deterministic names (provider_name + order suffix if needed)
9
+ -- Uses a CTE to assign row numbers per profile, then updates with provider-based names
10
+ WITH ranked_configs AS (
11
+ SELECT
12
+ ppc.id,
13
+ p.name AS provider_name,
14
+ ROW_NUMBER() OVER (
15
+ PARTITION BY ppc.profile_id, ppc.provider_id
16
+ ORDER BY ppc.created_at ASC, ppc.id ASC
17
+ ) AS rn,
18
+ COUNT(*) OVER (
19
+ PARTITION BY ppc.profile_id, ppc.provider_id
20
+ ) AS total_for_provider
21
+ FROM profile_provider_configs ppc
22
+ JOIN providers p ON p.id = ppc.provider_id
23
+ )
24
+ UPDATE profile_provider_configs
25
+ SET name = (
26
+ SELECT
27
+ CASE
28
+ WHEN rc.total_for_provider = 1 THEN rc.provider_name
29
+ ELSE rc.provider_name || '-' || rc.rn
30
+ END
31
+ FROM ranked_configs rc
32
+ WHERE rc.id = profile_provider_configs.id
33
+ )
34
+ WHERE name = '';
35
+ --> statement-breakpoint
36
+
37
+ -- Step 3: Create unique index on (profile_id, name)
38
+ CREATE UNIQUE INDEX profile_provider_configs_profile_name_idx ON profile_provider_configs(profile_id, name);
@@ -0,0 +1,30 @@
1
+ -- Migration 0034: Clean up duplicate sessions and add unique index
2
+ -- This migration ensures only one running session per agent exists before adding the constraint
3
+
4
+ -- Step 1: Clean up any existing duplicate running sessions
5
+ -- For each agent with multiple running sessions, keep exactly one and mark the rest as 'stopped'
6
+ -- Uses window function for deterministic ordering even when started_at timestamps are identical
7
+ UPDATE sessions
8
+ SET status = 'stopped',
9
+ ended_at = datetime('now'),
10
+ updated_at = datetime('now')
11
+ WHERE id IN (
12
+ -- Find all duplicate sessions (rn > 1 means not the first for that agent)
13
+ SELECT id FROM (
14
+ SELECT id,
15
+ ROW_NUMBER() OVER (
16
+ PARTITION BY agent_id
17
+ ORDER BY started_at DESC, created_at DESC, id DESC
18
+ ) as rn
19
+ FROM sessions
20
+ WHERE status = 'running'
21
+ AND agent_id IS NOT NULL
22
+ ) ranked
23
+ WHERE rn > 1
24
+ );
25
+ --> statement-breakpoint
26
+
27
+ -- Step 2: Add partial unique index to prevent future duplicates
28
+ -- This is a safety net for the application-level lock (withAgentLock in SessionsService)
29
+ -- to catch any edge cases where concurrent requests might create duplicate sessions
30
+ CREATE UNIQUE INDEX `idx_sessions_agent_running` ON `sessions` (`agent_id`) WHERE status = 'running' AND agent_id IS NOT NULL;
@@ -0,0 +1,26 @@
1
+ -- Migration: Add position column to profile_provider_configs
2
+ -- This enables drag-and-drop ordering of provider configs within a profile
3
+
4
+ -- Step 1: Add position column with default 0
5
+ ALTER TABLE profile_provider_configs ADD COLUMN position INTEGER NOT NULL DEFAULT 0;
6
+ --> statement-breakpoint
7
+
8
+ -- Step 2: Backfill positions based on createdAt order per profile
9
+ -- Uses CTE with ROW_NUMBER() to assign sequential positions (0, 1, 2, ...)
10
+ WITH ranked AS (
11
+ SELECT
12
+ id,
13
+ ROW_NUMBER() OVER (PARTITION BY profile_id ORDER BY created_at ASC, id ASC) - 1 AS pos
14
+ FROM profile_provider_configs
15
+ )
16
+ UPDATE profile_provider_configs
17
+ SET position = (
18
+ SELECT pos
19
+ FROM ranked
20
+ WHERE ranked.id = profile_provider_configs.id
21
+ );
22
+ --> statement-breakpoint
23
+
24
+ -- Step 3: Create unique index on (profile_id, position)
25
+ CREATE UNIQUE INDEX profile_provider_configs_profile_position_idx
26
+ ON profile_provider_configs(profile_id, position);
@@ -0,0 +1,109 @@
1
+ # Phase 2 Migration Guide: Profile Provider Configs
2
+
3
+ ## Overview
4
+
5
+ Phase 2 migrations populate the `profile_provider_configs` table and merge duplicate profiles.
6
+
7
+ **Migrations:**
8
+ - `0028_populate_provider_configs.sql` - Create configs from existing profiles
9
+ - `0029_merge_profiles_by_family_slug.sql` - Merge profiles with same familySlug
10
+ - `0030_set_agents_provider_config_id.sql` - Set agent config references
11
+
12
+ ## Pre-Migration Checklist
13
+
14
+ ### 1. Stop All Sessions
15
+
16
+ Before running migrations, ensure no active sessions exist:
17
+
18
+ ```bash
19
+ sqlite3 ~/.devchain/devchain.db < apps/local-app/drizzle/migration-guard.sql
20
+ ```
21
+
22
+ If any output is shown, stop the sessions first:
23
+ ```bash
24
+ # List running sessions
25
+ sqlite3 ~/.devchain/devchain.db "SELECT id, agent_id FROM sessions WHERE status='running';"
26
+
27
+ # Stop sessions via UI or terminate tmux sessions
28
+ ```
29
+
30
+ ### 2. Create Backup
31
+
32
+ **CRITICAL:** Always backup before migrating:
33
+
34
+ ```bash
35
+ cp ~/.devchain/devchain.db ~/.devchain/devchain.db.backup-$(date +%Y%m%d-%H%M%S)
36
+ ```
37
+
38
+ ### 3. Verify Backup
39
+
40
+ ```bash
41
+ sqlite3 ~/.devchain/devchain.db.backup-* "SELECT COUNT(*) FROM agent_profiles;"
42
+ ```
43
+
44
+ ## Running Migrations
45
+
46
+ ```bash
47
+ pnpm --filter local-app db:migrate
48
+ ```
49
+
50
+ Or manually:
51
+ ```bash
52
+ sqlite3 ~/.devchain/devchain.db < apps/local-app/drizzle/0028_populate_provider_configs.sql
53
+ sqlite3 ~/.devchain/devchain.db < apps/local-app/drizzle/0029_merge_profiles_by_family_slug.sql
54
+ sqlite3 ~/.devchain/devchain.db < apps/local-app/drizzle/0030_set_agents_provider_config_id.sql
55
+ ```
56
+
57
+ ## Post-Migration Verification
58
+
59
+ ```bash
60
+ # Verify configs created
61
+ sqlite3 ~/.devchain/devchain.db "SELECT COUNT(*) FROM profile_provider_configs;"
62
+
63
+ # Verify no duplicate familySlug groups
64
+ sqlite3 ~/.devchain/devchain.db "SELECT project_id, family_slug, COUNT(*) FROM agent_profiles WHERE family_slug IS NOT NULL GROUP BY project_id, family_slug HAVING COUNT(*) > 1;"
65
+
66
+ # Verify all agents have config
67
+ sqlite3 ~/.devchain/devchain.db "SELECT COUNT(*) FROM agents WHERE provider_config_id IS NULL;"
68
+ ```
69
+
70
+ ## Rollback Procedure
71
+
72
+ ### Recommended: Restore from Backup
73
+
74
+ ```bash
75
+ # Stop the application
76
+ # Restore backup
77
+ cp ~/.devchain/devchain.db.backup-YYYYMMDD-HHMMSS ~/.devchain/devchain.db
78
+
79
+ # Restart the application
80
+ ```
81
+
82
+ ### Alternative: Partial Rollback Script
83
+
84
+ **WARNING:** The partial rollback script cannot restore deleted profiles from the merge step.
85
+
86
+ ```bash
87
+ sqlite3 ~/.devchain/devchain.db < apps/local-app/drizzle/phase2-rollback.sql
88
+ ```
89
+
90
+ This will:
91
+ 1. Clear `agents.provider_config_id` (set to NULL)
92
+ 2. Delete all `profile_provider_configs` records
93
+ 3. Remove migration tracking entries
94
+
95
+ **NOT restored:** Profiles deleted during merge (0029) cannot be restored without backup.
96
+
97
+ ## Troubleshooting
98
+
99
+ ### Migration fails with "database is locked"
100
+ - Stop all sessions and the application
101
+ - Ensure no other process is accessing the database
102
+
103
+ ### Agents have NULL providerConfigId after migration
104
+ - Verify profile has a config: `SELECT * FROM profile_provider_configs WHERE profile_id = '<profile_id>';`
105
+ - Re-run migration 0030
106
+
107
+ ### Profile configs missing
108
+ - Check if profile has providerId: `SELECT provider_id FROM agent_profiles WHERE id = '<profile_id>';`
109
+ - Profiles with NULL providerId don't get configs created