@superblocksteam/vite-plugin-file-sync 2.0.151 → 2.0.152-next.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 (224) hide show
  1. package/dist/ai-service/agent/middleware.d.ts.map +1 -1
  2. package/dist/ai-service/agent/middleware.js +10 -1
  3. package/dist/ai-service/agent/middleware.js.map +1 -1
  4. package/dist/ai-service/agent/prompts/build-base-system-prompt.d.ts.map +1 -1
  5. package/dist/ai-service/agent/prompts/build-base-system-prompt.js +25 -6
  6. package/dist/ai-service/agent/prompts/build-base-system-prompt.js.map +1 -1
  7. package/dist/ai-service/agent/prompts/build-security-scan-prompt.d.ts.map +1 -1
  8. package/dist/ai-service/agent/prompts/build-security-scan-prompt.js +7 -2
  9. package/dist/ai-service/agent/prompts/build-security-scan-prompt.js.map +1 -1
  10. package/dist/ai-service/agent/tools/apis/get-sdk-api-docs.d.ts +47 -1
  11. package/dist/ai-service/agent/tools/apis/get-sdk-api-docs.d.ts.map +1 -1
  12. package/dist/ai-service/agent/tools/apis/get-sdk-api-docs.js +129 -15
  13. package/dist/ai-service/agent/tools/apis/get-sdk-api-docs.js.map +1 -1
  14. package/dist/ai-service/agent/tools/apis/test-api.d.ts.map +1 -1
  15. package/dist/ai-service/agent/tools/apis/test-api.js +1 -24
  16. package/dist/ai-service/agent/tools/apis/test-api.js.map +1 -1
  17. package/dist/ai-service/agent/tools/databases/dev-database-tasks.d.ts +53 -9
  18. package/dist/ai-service/agent/tools/databases/dev-database-tasks.d.ts.map +1 -1
  19. package/dist/ai-service/agent/tools/databases/dev-database-tasks.js +304 -67
  20. package/dist/ai-service/agent/tools/databases/dev-database-tasks.js.map +1 -1
  21. package/dist/ai-service/agent/tools/databases/dev-database-tools.d.ts +47 -1
  22. package/dist/ai-service/agent/tools/databases/dev-database-tools.d.ts.map +1 -1
  23. package/dist/ai-service/agent/tools/databases/dev-database-tools.js +38 -1
  24. package/dist/ai-service/agent/tools/databases/dev-database-tools.js.map +1 -1
  25. package/dist/ai-service/agent/tools/integrations/execute-request.d.ts +391 -0
  26. package/dist/ai-service/agent/tools/integrations/execute-request.d.ts.map +1 -1
  27. package/dist/ai-service/agent/tools/integrations/execute-request.js +257 -44
  28. package/dist/ai-service/agent/tools/integrations/execute-request.js.map +1 -1
  29. package/dist/ai-service/agent/tools/integrations/index.d.ts +1 -1
  30. package/dist/ai-service/agent/tools/integrations/index.d.ts.map +1 -1
  31. package/dist/ai-service/agent/tools/integrations/index.js +1 -1
  32. package/dist/ai-service/agent/tools/integrations/index.js.map +1 -1
  33. package/dist/ai-service/agent/tools/integrations/managed-app-database.d.ts +16 -0
  34. package/dist/ai-service/agent/tools/integrations/managed-app-database.d.ts.map +1 -0
  35. package/dist/ai-service/agent/tools/integrations/managed-app-database.js +31 -0
  36. package/dist/ai-service/agent/tools/integrations/managed-app-database.js.map +1 -0
  37. package/dist/ai-service/agent/tools/integrations/sql-classification.d.ts +44 -0
  38. package/dist/ai-service/agent/tools/integrations/sql-classification.d.ts.map +1 -0
  39. package/dist/ai-service/agent/tools/integrations/sql-classification.js +352 -0
  40. package/dist/ai-service/agent/tools/integrations/sql-classification.js.map +1 -0
  41. package/dist/ai-service/agent/tools/report-security-findings.d.ts +5 -5
  42. package/dist/ai-service/agent/tools.d.ts.map +1 -1
  43. package/dist/ai-service/agent/tools.js +5 -1
  44. package/dist/ai-service/agent/tools.js.map +1 -1
  45. package/dist/ai-service/agent/tools2/registry.d.ts.map +1 -1
  46. package/dist/ai-service/agent/tools2/registry.js +2 -0
  47. package/dist/ai-service/agent/tools2/registry.js.map +1 -1
  48. package/dist/ai-service/agent/tools2/tools/check-task.d.ts.map +1 -1
  49. package/dist/ai-service/agent/tools2/tools/check-task.js +4 -1
  50. package/dist/ai-service/agent/tools2/tools/check-task.js.map +1 -1
  51. package/dist/ai-service/agent/tools2/tools/exit-plan-mode.d.ts.map +1 -1
  52. package/dist/ai-service/agent/tools2/tools/exit-plan-mode.js +12 -1
  53. package/dist/ai-service/agent/tools2/tools/exit-plan-mode.js.map +1 -1
  54. package/dist/ai-service/agent/tools2/tools/wait-for-task.d.ts.map +1 -1
  55. package/dist/ai-service/agent/tools2/tools/wait-for-task.js +5 -1
  56. package/dist/ai-service/agent/tools2/tools/wait-for-task.js.map +1 -1
  57. package/dist/ai-service/chat/chat-session-store.d.ts +10 -1
  58. package/dist/ai-service/chat/chat-session-store.d.ts.map +1 -1
  59. package/dist/ai-service/chat/chat-session-store.js +65 -15
  60. package/dist/ai-service/chat/chat-session-store.js.map +1 -1
  61. package/dist/ai-service/context-archive-paths.d.ts +7 -0
  62. package/dist/ai-service/context-archive-paths.d.ts.map +1 -1
  63. package/dist/ai-service/context-archive-paths.js +7 -0
  64. package/dist/ai-service/context-archive-paths.js.map +1 -1
  65. package/dist/ai-service/context-download.d.ts.map +1 -1
  66. package/dist/ai-service/context-download.js +8 -10
  67. package/dist/ai-service/context-download.js.map +1 -1
  68. package/dist/ai-service/context-flush.d.ts +36 -0
  69. package/dist/ai-service/context-flush.d.ts.map +1 -0
  70. package/dist/ai-service/context-flush.js +90 -0
  71. package/dist/ai-service/context-flush.js.map +1 -0
  72. package/dist/ai-service/context-restore.d.ts +64 -0
  73. package/dist/ai-service/context-restore.d.ts.map +1 -0
  74. package/dist/ai-service/context-restore.js +136 -0
  75. package/dist/ai-service/context-restore.js.map +1 -0
  76. package/dist/ai-service/context-upload.d.ts +13 -4
  77. package/dist/ai-service/context-upload.d.ts.map +1 -1
  78. package/dist/ai-service/context-upload.js +28 -18
  79. package/dist/ai-service/context-upload.js.map +1 -1
  80. package/dist/ai-service/dev-database-client.d.ts +51 -0
  81. package/dist/ai-service/dev-database-client.d.ts.map +1 -1
  82. package/dist/ai-service/dev-database-client.js +3 -0
  83. package/dist/ai-service/dev-database-client.js.map +1 -1
  84. package/dist/ai-service/index.d.ts +19 -0
  85. package/dist/ai-service/index.d.ts.map +1 -1
  86. package/dist/ai-service/index.js +449 -182
  87. package/dist/ai-service/index.js.map +1 -1
  88. package/dist/ai-service/llm/context-v2/adapter.d.ts.map +1 -1
  89. package/dist/ai-service/llm/context-v2/adapter.js +1 -0
  90. package/dist/ai-service/llm/context-v2/adapter.js.map +1 -1
  91. package/dist/ai-service/llm/context-v2/compaction/client-side.d.ts +30 -0
  92. package/dist/ai-service/llm/context-v2/compaction/client-side.d.ts.map +1 -1
  93. package/dist/ai-service/llm/context-v2/compaction/client-side.js +77 -4
  94. package/dist/ai-service/llm/context-v2/compaction/client-side.js.map +1 -1
  95. package/dist/ai-service/llm/context-v2/compaction/compaction-strategy.d.ts +5 -0
  96. package/dist/ai-service/llm/context-v2/compaction/compaction-strategy.d.ts.map +1 -1
  97. package/dist/ai-service/llm/context-v2/compaction/server-side.d.ts +1 -0
  98. package/dist/ai-service/llm/context-v2/compaction/server-side.d.ts.map +1 -1
  99. package/dist/ai-service/llm/context-v2/compaction/server-side.js +3 -0
  100. package/dist/ai-service/llm/context-v2/compaction/server-side.js.map +1 -1
  101. package/dist/ai-service/llm/context-v2/compactor.d.ts.map +1 -1
  102. package/dist/ai-service/llm/context-v2/compactor.js +57 -6
  103. package/dist/ai-service/llm/context-v2/compactor.js.map +1 -1
  104. package/dist/ai-service/llm/context-v2/config.d.ts +13 -1
  105. package/dist/ai-service/llm/context-v2/config.d.ts.map +1 -1
  106. package/dist/ai-service/llm/context-v2/config.js +39 -2
  107. package/dist/ai-service/llm/context-v2/config.js.map +1 -1
  108. package/dist/ai-service/llm/context-v2/context-metrics.d.ts +23 -0
  109. package/dist/ai-service/llm/context-v2/context-metrics.d.ts.map +1 -1
  110. package/dist/ai-service/llm/context-v2/context-metrics.js +52 -1
  111. package/dist/ai-service/llm/context-v2/context-metrics.js.map +1 -1
  112. package/dist/ai-service/llm/context-v2/context.d.ts +27 -3
  113. package/dist/ai-service/llm/context-v2/context.d.ts.map +1 -1
  114. package/dist/ai-service/llm/context-v2/context.js +107 -10
  115. package/dist/ai-service/llm/context-v2/context.js.map +1 -1
  116. package/dist/ai-service/llm/context-v2/manager.d.ts +66 -0
  117. package/dist/ai-service/llm/context-v2/manager.d.ts.map +1 -1
  118. package/dist/ai-service/llm/context-v2/manager.js +194 -2
  119. package/dist/ai-service/llm/context-v2/manager.js.map +1 -1
  120. package/dist/ai-service/llm/context-v2/storage/event-store.d.ts +31 -5
  121. package/dist/ai-service/llm/context-v2/storage/event-store.d.ts.map +1 -1
  122. package/dist/ai-service/llm/context-v2/storage/jsonl-event-store.d.ts +30 -9
  123. package/dist/ai-service/llm/context-v2/storage/jsonl-event-store.d.ts.map +1 -1
  124. package/dist/ai-service/llm/context-v2/storage/jsonl-event-store.js +96 -13
  125. package/dist/ai-service/llm/context-v2/storage/jsonl-event-store.js.map +1 -1
  126. package/dist/ai-service/llm/context-v2/storage/legacy-context-adoption.d.ts +46 -0
  127. package/dist/ai-service/llm/context-v2/storage/legacy-context-adoption.d.ts.map +1 -0
  128. package/dist/ai-service/llm/context-v2/storage/legacy-context-adoption.js +240 -0
  129. package/dist/ai-service/llm/context-v2/storage/legacy-context-adoption.js.map +1 -0
  130. package/dist/ai-service/llm/context-v2/storage/local.d.ts +1 -1
  131. package/dist/ai-service/llm/context-v2/storage/local.d.ts.map +1 -1
  132. package/dist/ai-service/llm/context-v2/storage/local.js +3 -2
  133. package/dist/ai-service/llm/context-v2/storage/local.js.map +1 -1
  134. package/dist/ai-service/llm/context-v2/types.d.ts +5 -1
  135. package/dist/ai-service/llm/context-v2/types.d.ts.map +1 -1
  136. package/dist/ai-service/llm/context-v2/types.js.map +1 -1
  137. package/dist/ai-service/llm/stream/config.d.ts +4 -3
  138. package/dist/ai-service/llm/stream/config.d.ts.map +1 -1
  139. package/dist/ai-service/llm/stream/config.js.map +1 -1
  140. package/dist/ai-service/llm/stream/idle-monitor.d.ts +37 -4
  141. package/dist/ai-service/llm/stream/idle-monitor.d.ts.map +1 -1
  142. package/dist/ai-service/llm/stream/idle-monitor.js +38 -3
  143. package/dist/ai-service/llm/stream/idle-monitor.js.map +1 -1
  144. package/dist/ai-service/policy-agent-metrics.d.ts +18 -0
  145. package/dist/ai-service/policy-agent-metrics.d.ts.map +1 -0
  146. package/dist/ai-service/policy-agent-metrics.js +99 -0
  147. package/dist/ai-service/policy-agent-metrics.js.map +1 -0
  148. package/dist/ai-service/state-machine/clark-fsm.d.ts +9 -0
  149. package/dist/ai-service/state-machine/clark-fsm.d.ts.map +1 -1
  150. package/dist/ai-service/state-machine/helpers/prompt-request-metrics.d.ts +9 -0
  151. package/dist/ai-service/state-machine/helpers/prompt-request-metrics.d.ts.map +1 -1
  152. package/dist/ai-service/state-machine/helpers/prompt-request-metrics.js +27 -0
  153. package/dist/ai-service/state-machine/helpers/prompt-request-metrics.js.map +1 -1
  154. package/dist/ai-service/tasks/factory.d.ts +7 -0
  155. package/dist/ai-service/tasks/factory.d.ts.map +1 -1
  156. package/dist/ai-service/tasks/factory.js +5 -0
  157. package/dist/ai-service/tasks/factory.js.map +1 -1
  158. package/dist/ai-service/tasks/task-store.d.ts +11 -1
  159. package/dist/ai-service/tasks/task-store.d.ts.map +1 -1
  160. package/dist/ai-service/tasks/task-store.js +104 -29
  161. package/dist/ai-service/tasks/task-store.js.map +1 -1
  162. package/dist/ai-service/tasks/types.d.ts +9 -1
  163. package/dist/ai-service/tasks/types.d.ts.map +1 -1
  164. package/dist/extract-api-integrations.d.ts +76 -0
  165. package/dist/extract-api-integrations.d.ts.map +1 -1
  166. package/dist/extract-api-integrations.js +428 -109
  167. package/dist/extract-api-integrations.js.map +1 -1
  168. package/dist/file-sync-vite-plugin.d.ts +1 -1
  169. package/dist/file-sync-vite-plugin.d.ts.map +1 -1
  170. package/dist/file-sync-vite-plugin.js +42 -79
  171. package/dist/file-sync-vite-plugin.js.map +1 -1
  172. package/dist/file-system-helpers.d.ts +1 -1
  173. package/dist/file-system-helpers.d.ts.map +1 -1
  174. package/dist/file-system-helpers.js +1 -0
  175. package/dist/file-system-helpers.js.map +1 -1
  176. package/dist/git-auth-state.d.ts +4 -0
  177. package/dist/git-auth-state.d.ts.map +1 -0
  178. package/dist/git-auth-state.js +25 -0
  179. package/dist/git-auth-state.js.map +1 -0
  180. package/dist/git-service/checkpoint-commit-metrics.d.ts +2 -0
  181. package/dist/git-service/checkpoint-commit-metrics.d.ts.map +1 -0
  182. package/dist/git-service/checkpoint-commit-metrics.js +15 -0
  183. package/dist/git-service/checkpoint-commit-metrics.js.map +1 -0
  184. package/dist/git-service/checkpoint-committer.d.ts +22 -0
  185. package/dist/git-service/checkpoint-committer.d.ts.map +1 -0
  186. package/dist/git-service/checkpoint-committer.js +91 -0
  187. package/dist/git-service/checkpoint-committer.js.map +1 -0
  188. package/dist/git-service/commit-live-branch.d.ts +17 -0
  189. package/dist/git-service/commit-live-branch.d.ts.map +1 -0
  190. package/dist/git-service/commit-live-branch.js +28 -0
  191. package/dist/git-service/commit-live-branch.js.map +1 -0
  192. package/dist/git-service/provider-workflow-files.d.ts +12 -0
  193. package/dist/git-service/provider-workflow-files.d.ts.map +1 -0
  194. package/dist/git-service/provider-workflow-files.js +36 -0
  195. package/dist/git-service/provider-workflow-files.js.map +1 -0
  196. package/dist/plugin-options.d.ts +8 -0
  197. package/dist/plugin-options.d.ts.map +1 -1
  198. package/dist/plugin-options.js.map +1 -1
  199. package/dist/policy-gate-runner.d.ts.map +1 -1
  200. package/dist/policy-gate-runner.js +7 -9
  201. package/dist/policy-gate-runner.js.map +1 -1
  202. package/dist/server-rpc/client.d.ts.map +1 -1
  203. package/dist/server-rpc/client.js +2 -0
  204. package/dist/server-rpc/client.js.map +1 -1
  205. package/dist/server-rpc/index.d.ts +43 -1
  206. package/dist/server-rpc/index.d.ts.map +1 -1
  207. package/dist/server-rpc/index.js +143 -5
  208. package/dist/server-rpc/index.js.map +1 -1
  209. package/dist/server-rpc/request-abort-context.d.ts +3 -0
  210. package/dist/server-rpc/request-abort-context.d.ts.map +1 -0
  211. package/dist/server-rpc/request-abort-context.js +23 -0
  212. package/dist/server-rpc/request-abort-context.js.map +1 -0
  213. package/dist/server-rpc/rpc-invalidate-metrics.d.ts +20 -0
  214. package/dist/server-rpc/rpc-invalidate-metrics.d.ts.map +1 -0
  215. package/dist/server-rpc/rpc-invalidate-metrics.js +86 -0
  216. package/dist/server-rpc/rpc-invalidate-metrics.js.map +1 -0
  217. package/dist/socket-manager.d.ts.map +1 -1
  218. package/dist/socket-manager.js +21 -60
  219. package/dist/socket-manager.js.map +1 -1
  220. package/dist/sync-service/index.d.ts +26 -2
  221. package/dist/sync-service/index.d.ts.map +1 -1
  222. package/dist/sync-service/index.js +74 -37
  223. package/dist/sync-service/index.js.map +1 -1
  224. package/package.json +9 -8
@@ -7,7 +7,7 @@ import { PermissionLevel, ToolCategory } from "../../tools2/types.js";
7
7
  export {};
8
8
  // Master parity: the only user-controlled requirement fields are
9
9
  // `logicalName` and (optionally) `migrationDirectory`. Engine, extensions,
10
- // sizing, replicaCount, version, and the dev environment name are
10
+ // sizing, replicaCount, version, and the editor-mode profile are
11
11
  // platform-controlled — the server resolves them from the org's
12
12
  // lifecycle policy, so exposing them on the LLM surface would let Clark
13
13
  // invent values the platform later overrides.
@@ -19,7 +19,7 @@ const databaseRequirementSchema = z.object({
19
19
  migrationDirectory: z
20
20
  .string()
21
21
  .optional()
22
- .describe("Repo-relative path to the directory holding the app's SQL migrations. Set this explicitly when provisionDatabase should enable startApplyDevDatabaseMigrations; `db/migrations/` is the recommended convention. Files inside must match `<digits>_<description>.sql` (e.g. `0001_init.sql`, `0002_add_users.sql`); the leading digits are the sort key and the runner applies them in lexicographic order against the in-DB ledger. Migrations run in edit and deployed — put seed/INSERT rows here only when deployed apps will always need that data; for edit-only test/demo data insert at runtime against the integration instead of a .sql seed migration."),
22
+ .describe("Repo-relative path to the directory holding the app's SQL migrations. Set this explicitly when provisionDatabase should enable startApplyDevDatabaseMigrations; `db/migrations/` is the recommended convention. Files inside must match `<digits>_<description>.sql` (e.g. `0001_init.sql`, `0002_add_users.sql`); the leading digits are the sort key and the runner applies them in lexicographic order against the in-DB ledger. Migrations run in authoring and on production deploys — put seed/INSERT rows here only when deployed apps will always need that data; for authoring-only test/demo data insert at runtime against the integration instead of a .sql seed migration."),
23
23
  });
24
24
  const provisionInputSchema = z.object({
25
25
  requirement: databaseRequirementSchema,
@@ -71,12 +71,20 @@ export const watchDevDatabaseProvisionTaskMetadata = {
71
71
  name: "watchDevDatabaseProvision",
72
72
  pollSchedule: { factor: 1.5, initialMs: 2_000, maxMs: 30_000 },
73
73
  };
74
- // Empty LLM input: Clark must not pin migrate to a provision-primary
75
- // bindingKey. The server fans out migrate_schema across every eligible
76
- // editor Environments datatag (ENG-3469).
77
- const applyMigrationsInputSchema = z.object({});
74
+ // Clark must not pin migrate to a provision-primary bindingKey. The server
75
+ // fans out migrate_schema across every eligible editor Environments datatag
76
+ // (ENG-3469). The only LLM-facing field is acknowledgeDestructive.
77
+ const applyMigrationsInputSchema = z.object({
78
+ acknowledgeDestructive: z
79
+ .boolean()
80
+ .optional()
81
+ .describe("Set true to re-request a destructive apply after a prior call failed with DESTRUCTIVE_MIGRATION_ACK_REQUIRED (DROP TABLE/DATABASE/SCHEMA, TRUNCATE, DROP COLUMN). This raises a permission request the user must approve before the destructive change runs; you do not need a separate chat confirmation first."),
82
+ });
78
83
  const applyMigrationsHandleSchema = z.object({
79
84
  bindingKey: z.string().min(1).optional(),
85
+ // Captured at start() so entityTag can still be built if a later
86
+ // poll/migrated envelope omits logicalName (optional on the response).
87
+ logicalName: z.string().optional(),
80
88
  requestId: z.string().min(1),
81
89
  siblingTaskIds: z.array(z.string().min(1)).optional(),
82
90
  });
@@ -92,18 +100,20 @@ const applyMigrationsHandleSchema = z.object({
92
100
  */
93
101
  export const provisionDevDatabaseTaskMetadata = {
94
102
  category: ToolCategory.DATA,
95
- // ALLOW: createDevDb is idempotent (returns the existing dev binding
96
- // if one exists for this app+env) and scoped to a lifecycle-managed
97
- // dev database invisible to anything but Clark.
103
+ // ALLOW: createDevDb is idempotent when the requested logical database
104
+ // matches the existing app+profile binding. A differently named database
105
+ // is rejected because an application supports only one App Database.
98
106
  defaultPermission: PermissionLevel.ALLOW,
99
- // Substance mirrors master's `createDevDb`: lifecycle-managed binding
100
- // on Amazon RDS, returns lifecycle progress. Engine/extensions and the
101
- // dev environment name are platform-controlled, so they're not in the
102
- // input. BG wrapping (taskId, don't waitForTask, check first) is the
103
- // only behavioral delta from master.
104
- description: "Create or reuse a dev App Database binding for this application on Amazon RDS as a BACKGROUND TASK. Clark provisions the database and returns lifecycle progress. Returns a taskId IMMEDIATELY while RDS provisioning runs in the background; the user already sees live progress in the chat's background-tasks indicator. " +
105
- "DO NOT call waitForTask right after this — that defeats the whole point of a background task and blocks your turn for minutes while RDS spins up. Instead: keep working on whatever does NOT depend on the database (scaffold the UI, draft API code that will use the binding, write SQL migration files, plan next steps). When you reach a step that genuinely cannot proceed without the connection details, FIRST call checkTask({ taskId }) — it returns instantly. Only fall back to waitForTask if checkTask still reports state='running' AND you literally have no other useful work to do. " +
106
- 'On completion the result includes bindingKey, integrationId, lifecycleState, and connectionMetadata. App Database bindings use IAM authentication. Use integrationId only for tool inputs or generated source code such as postgres(...); do not repeat internal UUIDs in user-facing chat. When declaring the integration in an SDK API, derive a valid lowercase snake_case integrations map key from requirement.logicalName (normalize non-alphanumeric to _, prefix db_ if needed, disambiguate collisions; e.g. pokemon_tracker pokemon_tracker: postgres(POKEMON_TRACKER), Orders DB orders_db) so code and TraceView match the database namenever a generic key like db. This task ONLY creates the database server + an integration; it does NOT apply SQL migrations query attempts will fail with `relation "X" does not exist` until you call startApplyDevDatabaseMigrations. Cancellation only stops local polling; the underlying RDS provisioning continues on the server.',
107
+ // Substance mirrors master's `createDevDb`: App Database binding
108
+ // (Aurora or RDS depending on the Superblocks environment — never
109
+ // hard-code either product; APPS-5377), returns lifecycle progress.
110
+ // Engine/extensions and the editor-mode profile are
111
+ // platform-controlled, so they're not in the input. BG wrapping
112
+ // (taskId, don't waitForTask, check first) is the only behavioral
113
+ // delta from master.
114
+ description: "Create the application's dev App Database binding, or reuse it when the same logical database was already provisioned, as a BACKGROUND TASK. Only one App Database per application is supported; never reinterpret a request for another database as a request to add tables or migrations to the existing database. Clark provisions a cloud Postgres database (Amazon Aurora or Amazon RDS depending on the Superblocks environmentdo NOT assume either product) and returns lifecycle progress. Returns a taskId IMMEDIATELY while provisioning runs in the background; the user already sees live progress in the chat's background-tasks indicator. " +
115
+ "DO NOT call waitForTask right after this — that defeats the whole point of a background task and blocks your turn for minutes while the database spins up. Instead: keep working on whatever does NOT depend on the database (scaffold the UI, draft API code that will use the binding, write SQL migration files, plan next steps). When you reach a step that genuinely cannot proceed without the connection details, FIRST call checkTask({ taskId }) — it returns instantly. Only fall back to waitForTask if checkTask still reports state='running' AND you literally have no other useful work to do. " +
116
+ 'On completion the result includes bindingKey, integrationId, entityTag, lifecycleState, connectionMetadata, and providerLabel. App Database bindings use IAM authentication. Use providerLabel when naming the cloud product in user-facing chat (e.g. "Amazon Aurora" or "App Database"); NEVER invent "RDS" or "Aurora" when providerLabel is absent or says "App Database" — Aurora still uses the AWS RDS API and *.rds.amazonaws.com hostnames. Use integrationId only for tool inputs or generated source code such as postgres(...). MUST NEVER paste bare integrationId UUIDs or phrases like "Integration ID is …" in user-facing chat — when referring to this managed database for the user, paste entityTag (e.g. @OrdersDB[id=…]) so the UI shows a name sticker. When declaring the integration in an SDK API, derive a valid lowercase snake_case integrations map key from requirement.logicalName (normalize non-alphanumeric to _, prefix db_ if needed, disambiguate collisions; e.g. pokemon_tracker → pokemon_tracker: postgres(POKEMON_TRACKER), Orders DB → orders_db) so code and TraceView match the database name — never a generic key like db. This task ONLY creates the database server + an integration; it does NOT apply SQL migrations — query attempts will fail with `relation "X" does not exist` until you call startApplyDevDatabaseMigrations. Cancellation only stops local polling; the underlying cloud provisioning continues on the server.',
107
117
  externalHandleSchema: provisionHandleSchema,
108
118
  inputSchema: provisionInputSchema,
109
119
  name: "provisionDatabase",
@@ -115,10 +125,8 @@ export const provisionDevDatabaseTaskMetadata = {
115
125
  */
116
126
  export const applyDevDatabaseMigrationsTaskMetadata = {
117
127
  category: ToolCategory.DATA,
118
- // ALLOW: applies idempotent working-state SQL migrations. The runner
119
- // skips versions already in the ledger; re-firing is safe. Filename
120
- // validation + destructive-op detection on the server side are the
121
- // safety net.
128
+ // ALLOW for additive migrations. Schema-destroying SQL is blocked by the
129
+ // server until acknowledgeDestructive is set after an explicit user confirm.
122
130
  defaultPermission: PermissionLevel.ALLOW,
123
131
  // Substance mirrors master's `applyMigrations`: SQL files come from the
124
132
  // binding's `migrationDirectory` set on provisionDatabase (no implicit
@@ -126,9 +134,10 @@ export const applyDevDatabaseMigrationsTaskMetadata = {
126
134
  // automatically, which is a lie when provisionDatabase didn't set
127
135
  // one). BG wrapping (taskId, don't waitForTask, check first) is the
128
136
  // only behavioral delta from master.
129
- description: "Run the application's working-state SQL migrations against every eligible editor datatag binding for this application's Amazon RDS databases as a BACKGROUND TASK. Returns a taskId IMMEDIATELY; the user already sees live progress in the chat's background-tasks indicator. " +
137
+ description: "Run the application's working-state SQL migrations against every eligible editor datatag binding for this application's App Databases as a BACKGROUND TASK. Returns a taskId IMMEDIATELY; the user already sees live progress in the chat's background-tasks indicator. " +
130
138
  "DO NOT call waitForTask right after this — that defeats the whole point of a background task and blocks your turn while migrations run. Instead: keep working on anything that does not need migrations to have landed yet (UI scaffolding, additional API code, the next migration file). When you reach a step that genuinely needs migrationState='migrated' (for example, executing a SELECT against the new tables), FIRST call checkTask({ taskId }) — it returns instantly. Only fall back to waitForTask if checkTask still reports state='running' AND there is nothing else useful to do. " +
131
- "REQUIRED after provisionDatabase if any application tables need to exist — provisioning only creates an empty RDS database. Reads SQL files from each binding's migrationDirectory set on provisionDatabase and applies any not yet recorded in the in-DB migration ledger; if provisionDatabase did not set a migrationDirectory, no SQL files are loaded. Seed/INSERT rows belong in migrations only when deployed apps will always need that data; for edit-only test/demo data insert at runtime against the integration instead of a .sql seed file. Re-firing is safe; already-applied versions are skipped. The terminal result includes migrationState='migrated' on success.",
139
+ "REQUIRED after provisionDatabase if any application tables need to exist — provisioning only creates an empty App Database. Reads SQL files from each binding's migrationDirectory set on provisionDatabase and applies any not yet recorded in the in-DB migration ledger; if provisionDatabase did not set a migrationDirectory, no SQL files are loaded. Seed/INSERT rows belong in migrations only when deployed apps will always need that data; for authoring-only test/demo data insert at runtime against the integration instead of a .sql seed file. Re-firing is safe; already-applied versions are skipped. The terminal result includes migrationState='migrated' on success. " +
140
+ "If the server returns errorCode DESTRUCTIVE_MIGRATION_ACK_REQUIRED, immediately re-call startApplyDevDatabaseMigrations with acknowledgeDestructive=true. That re-call raises a permission request the user approves or denies in the UI before any destructive change runs - the approval button is the confirmation, so you do not need to ask in chat first. Never run the migration another way (for example raw SQL) or tell the user to drop objects manually.",
132
141
  externalHandleSchema: applyMigrationsHandleSchema,
133
142
  inputSchema: applyMigrationsInputSchema,
134
143
  name: "startApplyDevDatabaseMigrations",
@@ -139,6 +148,74 @@ const TERMINAL_LIFECYCLE_STATES = new Set([
139
148
  "failed",
140
149
  "cancelled",
141
150
  ]);
151
+ /** Max length for code/message/requestId/providerLabel in agent-facing
152
+ * lifecycle fields. Matches `SAFE_INTERP_MAX_LEN` in build-install-packages.ts
153
+ * so a multi-KB provider string cannot crowd out the rest of agent context. */
154
+ const LIFECYCLE_FAILURE_FIELD_MAX_LEN = 100;
155
+ function sanitizeLifecycleFailureField(value) {
156
+ // Defense-in-depth for LLM-facing task fields: server already redacts
157
+ // credential material, but worker messages / labels can still carry
158
+ // newlines / backticks that would otherwise reach agent context verbatim.
159
+ const scrubbed = stripUnsafeChars(scrubSecrets(value)).trim();
160
+ return scrubbed.length > LIFECYCLE_FAILURE_FIELD_MAX_LEN
161
+ ? `${scrubbed.slice(0, LIFECYCLE_FAILURE_FIELD_MAX_LEN)}…`
162
+ : scrubbed;
163
+ }
164
+ const DEFAULT_PROVIDER_LABEL = "App Database";
165
+ /**
166
+ * APPS-5377: Clark historically assumed "Amazon RDS" because tool copy
167
+ * hard-coded it. Aurora Serverless still uses the RDS API and
168
+ * `*.rds.amazonaws.com` hostnames (including Multi-AZ cluster writers
169
+ * that share the `*.cluster-*` DNS pattern with Aurora), so hostname
170
+ * sniffing replaces one confident mislabel with another.
171
+ *
172
+ * Keep the generic `App Database` label unless the backend explicitly
173
+ * supplies `providerLabel` / `provider_label` on the lifecycle response
174
+ * or connectionMetadata.
175
+ */
176
+ export function inferManagedDatabaseProviderLabel(connectionMetadata) {
177
+ // Prefer an explicit backend hint if present (future / passthrough).
178
+ const explicit = connectionMetadata?.providerLabel ?? connectionMetadata?.provider_label;
179
+ if (typeof explicit === "string" && explicit.trim()) {
180
+ return {
181
+ providerLabel: explicit.trim(),
182
+ source: "connectionMetadata",
183
+ };
184
+ }
185
+ return { providerLabel: DEFAULT_PROVIDER_LABEL, source: "default" };
186
+ }
187
+ function sanitizeProviderLabel(value) {
188
+ const scrubbed = sanitizeLifecycleFailureField(value);
189
+ return scrubbed.length > 0 ? scrubbed : DEFAULT_PROVIDER_LABEL;
190
+ }
191
+ function withProviderLabel(response) {
192
+ if (response.providerLabel) {
193
+ const providerLabel = sanitizeProviderLabel(response.providerLabel);
194
+ getLogger().debug({
195
+ bindingKey: response.bindingKey,
196
+ requestId: response.requestId,
197
+ providerLabel,
198
+ source: "server",
199
+ }, "[devDatabase] using server-provided providerLabel");
200
+ return providerLabel === response.providerLabel
201
+ ? response
202
+ : { ...response, providerLabel };
203
+ }
204
+ const { providerLabel: inferredLabel, source } = inferManagedDatabaseProviderLabel(response.connectionMetadata);
205
+ const providerLabel = sanitizeProviderLabel(inferredLabel);
206
+ getLogger().debug({
207
+ bindingKey: response.bindingKey,
208
+ requestId: response.requestId,
209
+ providerLabel,
210
+ source,
211
+ }, source === "connectionMetadata"
212
+ ? "[devDatabase] using providerLabel from connectionMetadata"
213
+ : "[devDatabase] using generic providerLabel (prefer server field when available)");
214
+ return {
215
+ ...response,
216
+ providerLabel,
217
+ };
218
+ }
142
219
  function toToolError(error) {
143
220
  if (error instanceof DevDatabaseClientError) {
144
221
  return new Error(`${error.code}: ${error.message}`);
@@ -193,19 +270,6 @@ const LIFECYCLE_FAILURE_AGENT_GUIDANCE = {
193
270
  unsupported_provider_capability: "The agent does not support this database operation; update OPA lifecycle configuration, then retry.",
194
271
  },
195
272
  };
196
- /** Max length for code/message/requestId in agent-facing lifecycle failures.
197
- * Matches `SAFE_INTERP_MAX_LEN` in build-install-packages.ts so a multi-KB
198
- * provider error cannot crowd out the rest of the agent context. */
199
- const LIFECYCLE_FAILURE_FIELD_MAX_LEN = 100;
200
- function sanitizeLifecycleFailureField(value) {
201
- // Defense-in-depth for LLM-facing task errors: server already redacts
202
- // credential material, but worker messages can still carry newlines /
203
- // backticks that would otherwise reach agent context verbatim.
204
- const scrubbed = stripUnsafeChars(scrubSecrets(value)).trim();
205
- return scrubbed.length > LIFECYCLE_FAILURE_FIELD_MAX_LEN
206
- ? `${scrubbed.slice(0, LIFECYCLE_FAILURE_FIELD_MAX_LEN)}…`
207
- : scrubbed;
208
- }
209
273
  function formatLifecycleTaskFailure(action, response,
210
274
  // Durable id from the persisted task handle when progress omits it
211
275
  // (Scout / Bugbot: ProvisionHandle.requestId must be read, not only written).
@@ -255,6 +319,47 @@ fallbackRequestId) {
255
319
  userDefault;
256
320
  return { error, detail };
257
321
  }
322
+ /**
323
+ * Keep only characters the chat mention parser accepts inside `@Name[id=…]`
324
+ * (letters, numbers, `_` `.` `-` `$`, and interior spaces). Scrub secrets /
325
+ * unsafe chars and cap length like sibling lifecycle fields so a long
326
+ * `logicalName` cannot inflate completed task results fed back to the model.
327
+ * Punctuation such as `()`, `/`, or apostrophes would otherwise prevent a
328
+ * match and leave the raw `[id=<uuid>]` visible in assistant text. Display
329
+ * name still comes from entity lookup by id when the tag resolves.
330
+ */
331
+ export function mentionSafeTagName(name) {
332
+ const scrubbed = stripUnsafeChars(scrubSecrets(name)).trim();
333
+ const sanitized = scrubbed
334
+ .replace(/[^\p{L}\p{N}_.\-$ ]+/gu, "")
335
+ .replace(/\s+/g, " ")
336
+ .trim();
337
+ const capped = sanitized.length > LIFECYCLE_FAILURE_FIELD_MAX_LEN
338
+ ? sanitized.slice(0, LIFECYCLE_FAILURE_FIELD_MAX_LEN).trimEnd()
339
+ : sanitized;
340
+ return capped || "entity";
341
+ }
342
+ /**
343
+ * Attach `@logicalName[id=integrationId]` for user-facing chat stickers.
344
+ * Uses a parser-safe label derived from logicalName. Keeps integrationId
345
+ * available for codegen.
346
+ */
347
+ function withEntityTag(response, fallbackLogicalName) {
348
+ if (response.entityTag) {
349
+ return response;
350
+ }
351
+ const name = response.logicalName ?? fallbackLogicalName;
352
+ if (!response.integrationId || !name) {
353
+ if (response.integrationId && !name) {
354
+ getLogger().warn("[devDatabase] cannot attach entityTag: response missing logicalName and no fallback provided; chat may only have bare integrationId");
355
+ }
356
+ return response;
357
+ }
358
+ return {
359
+ ...response,
360
+ entityTag: `@${mentionSafeTagName(name)}[id=${response.integrationId}]`,
361
+ };
362
+ }
258
363
  /** A completed task hands its whole result to the LLM, so per-datatag error text
259
364
  * needs the same scrubbing a top-level failure gets. Partial success is the
260
365
  * common case here: the task completes while some datatags carry provider
@@ -276,10 +381,40 @@ function withSanitizedTargets(response) {
276
381
  })),
277
382
  };
278
383
  }
279
- function lifecycleStatusFor(response, fallbackRequestId) {
384
+ const REGISTERING_CONNECTION_MESSAGE = "Database provisioned; registering the connection";
385
+ /**
386
+ * When the server says runtime config is not ready yet, keep the task running
387
+ * so Clark does not treat a vacant `{}` projection as executable (ENG-5285).
388
+ * Older servers omit the fields — preserve prior complete-on-ready behavior,
389
+ * except the migrate-then-project case (ready + migration still pending).
390
+ */
391
+ function awaitingRuntimeConfiguration(response) {
392
+ if (response.runtimeConfigurationReady !== false) {
393
+ return false;
394
+ }
395
+ if (typeof response.expectsRuntimeConfiguration === "boolean") {
396
+ return response.expectsRuntimeConfiguration;
397
+ }
398
+ // Compat for servers that only send runtimeConfigurationReady: projection
399
+ // was historically deferred until migrate, so pending migration may complete.
400
+ return response.migrationState !== "pending";
401
+ }
402
+ function enrichCompletedResponse(response, fallbackLogicalName) {
403
+ return withEntityTag(withProviderLabel(withSanitizedTargets(response)), fallbackLogicalName);
404
+ }
405
+ function lifecycleStatusFor(response, fallbackRequestId, fallbackLogicalName) {
280
406
  switch (response.lifecycleState) {
281
407
  case "ready":
282
- return { state: "completed", result: withSanitizedTargets(response) };
408
+ if (awaitingRuntimeConfiguration(response)) {
409
+ return {
410
+ state: "running",
411
+ message: REGISTERING_CONNECTION_MESSAGE,
412
+ };
413
+ }
414
+ return {
415
+ state: "completed",
416
+ result: enrichCompletedResponse(response, fallbackLogicalName),
417
+ };
283
418
  case "failed": {
284
419
  const { error, detail } = formatLifecycleTaskFailure("dev database provisioning", response, fallbackRequestId);
285
420
  return { state: "failed", error, detail };
@@ -291,10 +426,19 @@ function lifecycleStatusFor(response, fallbackRequestId) {
291
426
  return { state: "running", message: response.lifecycleState };
292
427
  }
293
428
  }
294
- function migrationStatusFor(response, fallbackRequestId) {
429
+ function migrationStatusFor(response, fallbackRequestId, fallbackLogicalName) {
295
430
  switch (response.migrationState) {
296
431
  case "migrated":
297
- return { state: "completed", result: withSanitizedTargets(response) };
432
+ if (awaitingRuntimeConfiguration(response)) {
433
+ return {
434
+ state: "running",
435
+ message: REGISTERING_CONNECTION_MESSAGE,
436
+ };
437
+ }
438
+ return {
439
+ state: "completed",
440
+ result: enrichCompletedResponse(response, fallbackLogicalName),
441
+ };
298
442
  case "failed": {
299
443
  const { error, detail } = formatLifecycleTaskFailure("applying migrations", response, fallbackRequestId);
300
444
  return { state: "failed", error, detail };
@@ -309,6 +453,33 @@ function isPreconditionFailure(response) {
309
453
  }
310
454
  const DEFAULT_MIGRATION_TASK_LABEL = "the database schema update";
311
455
  const MAX_MIGRATION_FILENAMES_IN_TASK_LABEL = 3;
456
+ /**
457
+ * Deterministic next step for Clark when the server blocks a destructive
458
+ * migration. This is deliberately kept out of the length-capped `error`
459
+ * field (see the precondition branch below): `error` is clamped to
460
+ * LIFECYCLE_FAILURE_FIELD_MAX_LEN, so a findings list would truncate the
461
+ * instruction and Clark would improvise (for example try to run the DROP as
462
+ * raw SQL, or tell the user to drop the object by hand). `detail` is not
463
+ * clamped, so the whole instruction reaches the model verbatim.
464
+ *
465
+ * Re-calling with `acknowledgeDestructive=true` is what raises the
466
+ * user-facing permission request; the approval button is the confirmation,
467
+ * so Clark must not seek a separate chat confirmation first, and must not try
468
+ * to bypass the block.
469
+ */
470
+ const DESTRUCTIVE_MIGRATION_ACK_AGENT_GUIDANCE = "This apply includes destructive schema changes and needs the user's " +
471
+ "permission. Re-call startApplyDevDatabaseMigrations with " +
472
+ "acknowledgeDestructive=true; that raises a permission request the user " +
473
+ "approves or denies before anything is dropped. Do not run the SQL another " +
474
+ "way and do not ask the user to drop objects manually.";
475
+ const SINGLE_APP_DATABASE_PRECONDITION_AGENT_GUIDANCE = "This application already has an App Database, and only one App Database " +
476
+ "per application is supported. Explain that limit to the user. Do not add " +
477
+ "tables, migrations, or schema to the existing database as a substitute for " +
478
+ "creating another database. Only modify the existing database if the user " +
479
+ "explicitly asks to.";
480
+ function isSingleAppDatabasePreconditionFailure(response) {
481
+ return /already has (?:a )?(?:dev |app )?database|only one (?:dev |app )?database per application/i.test(response.errorMessage ?? "");
482
+ }
312
483
  function migrationTaskLabel(response) {
313
484
  if (response.migrationState === "failed") {
314
485
  return DEFAULT_MIGRATION_TASK_LABEL;
@@ -322,20 +493,37 @@ function migrationTaskLabel(response) {
322
493
  if (migrations.length === 0) {
323
494
  return "No database migration files to apply";
324
495
  }
325
- const filenames = migrations
496
+ // The dispatch always carries every SQL file in the migration directory,
497
+ // because the worker replays them against its own ledger. Name only the ones
498
+ // this run adds, so a row for a one-file change does not read like the whole
499
+ // schema is being rebuilt. Servers that don't report the added files leave us
500
+ // no way to tell them apart, so fall back to naming the whole set.
501
+ const applying = response.newMigrationFilenames ??
502
+ migrations.map(({ filename }) => filename);
503
+ // An empty added-set is an estimate, not the worker's ledger, and this label
504
+ // is set once and never revisited. Naming nothing is honest; asserting the
505
+ // schema is current would stay on screen even if the run then failed.
506
+ if (applying.length === 0) {
507
+ return DEFAULT_MIGRATION_TASK_LABEL;
508
+ }
509
+ const filenames = applying
326
510
  .slice(0, MAX_MIGRATION_FILENAMES_IN_TASK_LABEL)
327
- .map(({ filename }) => sanitizeLifecycleFailureField(filename));
511
+ .map((filename) => sanitizeLifecycleFailureField(filename));
328
512
  if (filenames.some((filename) => filename.length === 0)) {
329
513
  return DEFAULT_MIGRATION_TASK_LABEL;
330
514
  }
331
- const remaining = Math.max(0, migrations.length - MAX_MIGRATION_FILENAMES_IN_TASK_LABEL);
515
+ const remaining = Math.max(0, applying.length - MAX_MIGRATION_FILENAMES_IN_TASK_LABEL);
332
516
  return `Database migrations: ${filenames.join(", ")}${remaining > 0 ? ` (+${remaining} more)` : ""}`;
333
517
  }
334
518
  function updateProvisionHandleFromResponse(handle, response) {
335
519
  handle.engine = response.engine;
336
520
  handle.integrationId = response.integrationId;
337
521
  handle.lifecycleState = response.lifecycleState;
338
- handle.logicalName = response.logicalName;
522
+ // Poll envelopes may omit logicalName; keep the name captured at task /
523
+ // watch start so entityTag can still be attached on completion.
524
+ if (response.logicalName) {
525
+ handle.logicalName = response.logicalName;
526
+ }
339
527
  if (response.requestId) {
340
528
  handle.requestId = response.requestId;
341
529
  }
@@ -367,7 +555,7 @@ function fanOutTargetsFromResponse(response) {
367
555
  },
368
556
  ];
369
557
  }
370
- async function spawnSiblingWatchTasks(deps, { logicalName, primaryBindingKey, targets, toolOpts, watchKind = "provision", }) {
558
+ async function spawnSiblingWatchTasks(deps, { logicalName, primaryBindingKey, signal, targets, toolOpts, watchKind = "provision", }) {
371
559
  const taskStore = deps.getTaskStore?.();
372
560
  if (!taskStore) {
373
561
  return [];
@@ -375,7 +563,19 @@ async function spawnSiblingWatchTasks(deps, { logicalName, primaryBindingKey, ta
375
563
  const startedTaskIds = [];
376
564
  const ownerUserId = deps.getCurrentOwnerId?.();
377
565
  const siblings = targets.filter((target) => target.bindingKey !== primaryBindingKey);
566
+ // Spawning is sequential and each start is awaited, so a cancel can land
567
+ // between siblings or while one is in flight. Stop at the next boundary and
568
+ // retire the watches this kickoff already created: their parent is gone, so
569
+ // nobody would consume them and they would keep polling on their own.
570
+ const stopIfCancelled = async () => {
571
+ if (signal?.aborted !== true) {
572
+ return;
573
+ }
574
+ await Promise.allSettled(startedTaskIds.map((taskId) => taskStore.cancel(taskId)));
575
+ signal.throwIfAborted();
576
+ };
378
577
  for (const target of siblings) {
578
+ await stopIfCancelled();
379
579
  try {
380
580
  const rawInitialError = !target.ok
381
581
  ? (target.errorMessage ??
@@ -406,6 +606,7 @@ async function spawnSiblingWatchTasks(deps, { logicalName, primaryBindingKey, ta
406
606
  // unobserved binding may still be provisioning.
407
607
  throw error;
408
608
  }
609
+ await stopIfCancelled();
409
610
  }
410
611
  return startedTaskIds;
411
612
  }
@@ -486,19 +687,21 @@ export function createDevDatabaseTaskTypes(deps) {
486
687
  throw toToolError(error);
487
688
  }
488
689
  updateProvisionHandleFromResponse(handle, response);
690
+ const fallbackLogicalName = handle.logicalName ?? handle.requirement.logicalName;
489
691
  if (handle.watchKind === "migration") {
490
692
  if (TERMINAL_LIFECYCLE_STATES.has(response.lifecycleState) &&
491
693
  response.lifecycleState !== "ready") {
492
- return lifecycleStatusFor(response, handle.requestId);
694
+ return lifecycleStatusFor(response, handle.requestId, fallbackLogicalName);
493
695
  }
494
- return migrationStatusFor(response, handle.requestId);
696
+ return migrationStatusFor(response, handle.requestId, fallbackLogicalName);
495
697
  }
496
- return lifecycleStatusFor(response, handle.requestId);
698
+ return lifecycleStatusFor(response, handle.requestId, fallbackLogicalName);
497
699
  },
498
700
  };
499
701
  const provisionDevDatabaseTaskType = {
500
702
  ...provisionDevDatabaseTaskMetadata,
501
- start: async (input, toolOpts) => {
703
+ pendingLabel: (input) => provisionLabel(input.requirement.logicalName),
704
+ start: async (input, toolOpts, signal) => {
502
705
  let response;
503
706
  try {
504
707
  // Client never sends a profile — the server resolves editor
@@ -512,6 +715,7 @@ export function createDevDatabaseTaskTypes(deps) {
512
715
  catch (error) {
513
716
  throw toToolError(error);
514
717
  }
718
+ signal?.throwIfAborted();
515
719
  // The application already has a database (typically under a
516
720
  // different name — only one dev database per application is
517
721
  // supported), or another precondition blocks provisioning. Surface
@@ -520,6 +724,9 @@ export function createDevDatabaseTaskTypes(deps) {
520
724
  // opaque "did not include lifecycle state" error. Mirrors the
521
725
  // applyMigrations precondition branch below.
522
726
  if (isPreconditionFailure(response)) {
727
+ const detail = isSingleAppDatabasePreconditionFailure(response)
728
+ ? SINGLE_APP_DATABASE_PRECONDITION_AGENT_GUIDANCE
729
+ : undefined;
523
730
  return {
524
731
  externalHandle: {
525
732
  bindingKey: "precondition-failure",
@@ -530,6 +737,7 @@ export function createDevDatabaseTaskTypes(deps) {
530
737
  error: sanitizeLifecycleFailureField(response.errorMessage ??
531
738
  response.errorCode ??
532
739
  "createDevDb precondition failed"),
740
+ ...(detail ? { detail } : {}),
533
741
  },
534
742
  label: provisionLabel(input.requirement.logicalName),
535
743
  };
@@ -552,6 +760,7 @@ export function createDevDatabaseTaskTypes(deps) {
552
760
  const siblingTaskIds = await spawnSiblingWatchTasks(deps, {
553
761
  logicalName: input.requirement.logicalName,
554
762
  primaryBindingKey: response.bindingKey,
763
+ signal,
555
764
  targets,
556
765
  toolOpts,
557
766
  });
@@ -564,7 +773,7 @@ export function createDevDatabaseTaskTypes(deps) {
564
773
  if (TERMINAL_LIFECYCLE_STATES.has(response.lifecycleState)) {
565
774
  return {
566
775
  externalHandle: handle,
567
- initialStatus: heldForSiblings(deps, lifecycleStatusFor(response), handle.siblingTaskIds),
776
+ initialStatus: heldForSiblings(deps, lifecycleStatusFor(response, undefined, input.requirement.logicalName), handle.siblingTaskIds),
568
777
  label,
569
778
  };
570
779
  }
@@ -590,18 +799,23 @@ export function createDevDatabaseTaskTypes(deps) {
590
799
  throw toToolError(error);
591
800
  }
592
801
  updateProvisionHandleFromResponse(handle, response);
593
- return heldForSiblings(deps, lifecycleStatusFor(response, handle.requestId), handle.siblingTaskIds);
802
+ return heldForSiblings(deps, lifecycleStatusFor(response, handle.requestId, handle.logicalName ?? handle.requirement.logicalName), handle.siblingTaskIds);
594
803
  },
595
804
  };
596
805
  const applyDevDatabaseMigrationsTaskType = {
597
806
  ...applyDevDatabaseMigrationsTaskMetadata,
598
- start: async (_input, toolOpts) => {
807
+ // Kickoff uploads the working directory and then asks the server what
808
+ // needs applying, so there is nothing to name yet. `start` swaps in the
809
+ // filenames once it knows them.
810
+ pendingLabel: () => DEFAULT_MIGRATION_TASK_LABEL,
811
+ start: async (input, toolOpts, signal) => {
599
812
  try {
600
813
  await deps.ensureFilesSynced?.();
601
814
  }
602
815
  catch (error) {
603
816
  throw toToolError(error);
604
817
  }
818
+ signal?.throwIfAborted();
605
819
  let response;
606
820
  try {
607
821
  // Never pass bindingKey — server fans out migrate_schema across
@@ -610,21 +824,43 @@ export function createDevDatabaseTaskTypes(deps) {
610
824
  // projected integration configs (migrations never ran there).
611
825
  response = await deps.getClient().applyMigrations({
612
826
  applicationId: deps.applicationId,
827
+ ...(input.acknowledgeDestructive === true
828
+ ? { acknowledgeDestructive: true }
829
+ : {}),
613
830
  });
614
831
  }
615
832
  catch (error) {
616
833
  throw toToolError(error);
617
834
  }
835
+ signal?.throwIfAborted();
618
836
  if (isPreconditionFailure(response)) {
837
+ const baseError = sanitizeLifecycleFailureField(response.errorMessage ??
838
+ response.errorCode ??
839
+ "applyMigrations precondition failed");
840
+ const isDestructiveAck = response.errorCode === "DESTRUCTIVE_MIGRATION_ACK_REQUIRED";
841
+ const findingsSuffix = isDestructiveAck &&
842
+ Array.isArray(response.findings) &&
843
+ response.findings.length > 0
844
+ ? sanitizeLifecycleFailureField(`Findings: ${response.findings
845
+ .map((finding) => `${sanitizeLifecycleFailureField(finding.fileName)} (${sanitizeLifecycleFailureField(finding.kind)})`)
846
+ .join("; ")}`)
847
+ : "";
848
+ const error = sanitizeLifecycleFailureField(findingsSuffix ? `${baseError} ${findingsSuffix}` : baseError);
619
849
  return {
620
850
  externalHandle: {
621
851
  requestId: "precondition-failure",
622
852
  },
623
853
  initialStatus: {
624
854
  state: "failed",
625
- error: sanitizeLifecycleFailureField(response.errorMessage ??
626
- response.errorCode ??
627
- "applyMigrations precondition failed"),
855
+ error,
856
+ // The re-call instruction goes in `detail`, not `error`: `error`
857
+ // is clamped, so a findings list would truncate the actionable
858
+ // step. `detail` reaches Clark verbatim, so it reliably re-calls
859
+ // with acknowledgeDestructive=true and the user gets a permission
860
+ // request instead of Clark improvising a bypass.
861
+ ...(isDestructiveAck
862
+ ? { detail: DESTRUCTIVE_MIGRATION_ACK_AGENT_GUIDANCE }
863
+ : {}),
628
864
  },
629
865
  label: DEFAULT_MIGRATION_TASK_LABEL,
630
866
  };
@@ -632,6 +868,7 @@ export function createDevDatabaseTaskTypes(deps) {
632
868
  const label = migrationTaskLabel(response);
633
869
  const handle = {
634
870
  bindingKey: response.bindingKey,
871
+ logicalName: response.logicalName,
635
872
  requestId: response.requestId,
636
873
  };
637
874
  const targets = fanOutTargetsFromResponse(response);
@@ -640,6 +877,7 @@ export function createDevDatabaseTaskTypes(deps) {
640
877
  const siblingTaskIds = await spawnSiblingWatchTasks(deps, {
641
878
  logicalName: response.logicalName ?? "database",
642
879
  primaryBindingKey: response.bindingKey,
880
+ signal,
643
881
  targets,
644
882
  toolOpts,
645
883
  watchKind: "migration",
@@ -648,18 +886,14 @@ export function createDevDatabaseTaskTypes(deps) {
648
886
  handle.siblingTaskIds = siblingTaskIds;
649
887
  }
650
888
  }
651
- // Migrations sometimes finish synchronously (no SQL to apply).
652
- if (response.migrationState === "migrated") {
653
- return {
654
- externalHandle: handle,
655
- initialStatus: heldForSiblings(deps, { state: "completed", result: withSanitizedTargets(response) }, handle.siblingTaskIds),
656
- label,
657
- };
658
- }
659
- if (response.migrationState === "failed") {
889
+ // Migrations sometimes finish synchronously (no SQL to apply). Still
890
+ // route through migrationStatusFor so a vacant projection keeps the
891
+ // task running until runtimeConfigurationReady (ENG-5285).
892
+ if (response.migrationState === "migrated" ||
893
+ response.migrationState === "failed") {
660
894
  return {
661
895
  externalHandle: handle,
662
- initialStatus: migrationStatusFor(response, handle.requestId),
896
+ initialStatus: heldForSiblings(deps, migrationStatusFor(response, handle.requestId, handle.logicalName), handle.siblingTaskIds),
663
897
  label,
664
898
  };
665
899
  }
@@ -684,14 +918,17 @@ export function createDevDatabaseTaskTypes(deps) {
684
918
  catch (error) {
685
919
  throw toToolError(error);
686
920
  }
921
+ if (response.logicalName) {
922
+ handle.logicalName = response.logicalName;
923
+ }
687
924
  // If the underlying lifecycle went terminal (failed/cancelled)
688
925
  // while migrations are still pending, surface the lifecycle
689
926
  // failure instead of spinning forever on migrationState.
690
927
  if (TERMINAL_LIFECYCLE_STATES.has(response.lifecycleState) &&
691
928
  response.lifecycleState !== "ready") {
692
- return lifecycleStatusFor(response, handle.requestId);
929
+ return lifecycleStatusFor(response, handle.requestId, handle.logicalName);
693
930
  }
694
- return heldForSiblings(deps, migrationStatusFor(response, handle.requestId), handle.siblingTaskIds);
931
+ return heldForSiblings(deps, migrationStatusFor(response, handle.requestId, handle.logicalName), handle.siblingTaskIds);
695
932
  },
696
933
  };
697
934
  return {