@superblocksteam/vite-plugin-file-sync 2.0.151 → 2.0.152-next.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/dist/ai-service/agent/middleware.d.ts.map +1 -1
- package/dist/ai-service/agent/middleware.js +10 -1
- package/dist/ai-service/agent/middleware.js.map +1 -1
- package/dist/ai-service/agent/prompts/build-base-system-prompt.d.ts.map +1 -1
- package/dist/ai-service/agent/prompts/build-base-system-prompt.js +25 -6
- package/dist/ai-service/agent/prompts/build-base-system-prompt.js.map +1 -1
- package/dist/ai-service/agent/prompts/build-security-scan-prompt.d.ts.map +1 -1
- package/dist/ai-service/agent/prompts/build-security-scan-prompt.js +7 -2
- package/dist/ai-service/agent/prompts/build-security-scan-prompt.js.map +1 -1
- package/dist/ai-service/agent/tools/apis/get-sdk-api-docs.d.ts +47 -1
- package/dist/ai-service/agent/tools/apis/get-sdk-api-docs.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/apis/get-sdk-api-docs.js +129 -15
- package/dist/ai-service/agent/tools/apis/get-sdk-api-docs.js.map +1 -1
- package/dist/ai-service/agent/tools/apis/test-api.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/apis/test-api.js +1 -24
- package/dist/ai-service/agent/tools/apis/test-api.js.map +1 -1
- package/dist/ai-service/agent/tools/databases/dev-database-tasks.d.ts +53 -9
- package/dist/ai-service/agent/tools/databases/dev-database-tasks.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/databases/dev-database-tasks.js +304 -67
- package/dist/ai-service/agent/tools/databases/dev-database-tasks.js.map +1 -1
- package/dist/ai-service/agent/tools/databases/dev-database-tools.d.ts +47 -1
- package/dist/ai-service/agent/tools/databases/dev-database-tools.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/databases/dev-database-tools.js +38 -1
- package/dist/ai-service/agent/tools/databases/dev-database-tools.js.map +1 -1
- package/dist/ai-service/agent/tools/integrations/execute-request.d.ts +391 -0
- package/dist/ai-service/agent/tools/integrations/execute-request.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/integrations/execute-request.js +257 -44
- package/dist/ai-service/agent/tools/integrations/execute-request.js.map +1 -1
- package/dist/ai-service/agent/tools/integrations/index.d.ts +1 -1
- package/dist/ai-service/agent/tools/integrations/index.d.ts.map +1 -1
- package/dist/ai-service/agent/tools/integrations/index.js +1 -1
- package/dist/ai-service/agent/tools/integrations/index.js.map +1 -1
- package/dist/ai-service/agent/tools/integrations/managed-app-database.d.ts +16 -0
- package/dist/ai-service/agent/tools/integrations/managed-app-database.d.ts.map +1 -0
- package/dist/ai-service/agent/tools/integrations/managed-app-database.js +31 -0
- package/dist/ai-service/agent/tools/integrations/managed-app-database.js.map +1 -0
- package/dist/ai-service/agent/tools/integrations/sql-classification.d.ts +44 -0
- package/dist/ai-service/agent/tools/integrations/sql-classification.d.ts.map +1 -0
- package/dist/ai-service/agent/tools/integrations/sql-classification.js +352 -0
- package/dist/ai-service/agent/tools/integrations/sql-classification.js.map +1 -0
- package/dist/ai-service/agent/tools/report-security-findings.d.ts +5 -5
- package/dist/ai-service/agent/tools.d.ts.map +1 -1
- package/dist/ai-service/agent/tools.js +5 -1
- package/dist/ai-service/agent/tools.js.map +1 -1
- package/dist/ai-service/agent/tools2/registry.d.ts.map +1 -1
- package/dist/ai-service/agent/tools2/registry.js +2 -0
- package/dist/ai-service/agent/tools2/registry.js.map +1 -1
- package/dist/ai-service/agent/tools2/tools/check-task.d.ts.map +1 -1
- package/dist/ai-service/agent/tools2/tools/check-task.js +4 -1
- package/dist/ai-service/agent/tools2/tools/check-task.js.map +1 -1
- package/dist/ai-service/agent/tools2/tools/exit-plan-mode.d.ts.map +1 -1
- package/dist/ai-service/agent/tools2/tools/exit-plan-mode.js +12 -1
- package/dist/ai-service/agent/tools2/tools/exit-plan-mode.js.map +1 -1
- package/dist/ai-service/agent/tools2/tools/wait-for-task.d.ts.map +1 -1
- package/dist/ai-service/agent/tools2/tools/wait-for-task.js +5 -1
- package/dist/ai-service/agent/tools2/tools/wait-for-task.js.map +1 -1
- package/dist/ai-service/chat/chat-session-store.d.ts +10 -1
- package/dist/ai-service/chat/chat-session-store.d.ts.map +1 -1
- package/dist/ai-service/chat/chat-session-store.js +65 -15
- package/dist/ai-service/chat/chat-session-store.js.map +1 -1
- package/dist/ai-service/context-archive-paths.d.ts +7 -0
- package/dist/ai-service/context-archive-paths.d.ts.map +1 -1
- package/dist/ai-service/context-archive-paths.js +7 -0
- package/dist/ai-service/context-archive-paths.js.map +1 -1
- package/dist/ai-service/context-download.d.ts.map +1 -1
- package/dist/ai-service/context-download.js +8 -10
- package/dist/ai-service/context-download.js.map +1 -1
- package/dist/ai-service/context-flush.d.ts +36 -0
- package/dist/ai-service/context-flush.d.ts.map +1 -0
- package/dist/ai-service/context-flush.js +90 -0
- package/dist/ai-service/context-flush.js.map +1 -0
- package/dist/ai-service/context-restore.d.ts +64 -0
- package/dist/ai-service/context-restore.d.ts.map +1 -0
- package/dist/ai-service/context-restore.js +136 -0
- package/dist/ai-service/context-restore.js.map +1 -0
- package/dist/ai-service/context-upload.d.ts +13 -4
- package/dist/ai-service/context-upload.d.ts.map +1 -1
- package/dist/ai-service/context-upload.js +28 -18
- package/dist/ai-service/context-upload.js.map +1 -1
- package/dist/ai-service/dev-database-client.d.ts +51 -0
- package/dist/ai-service/dev-database-client.d.ts.map +1 -1
- package/dist/ai-service/dev-database-client.js +3 -0
- package/dist/ai-service/dev-database-client.js.map +1 -1
- package/dist/ai-service/index.d.ts +19 -0
- package/dist/ai-service/index.d.ts.map +1 -1
- package/dist/ai-service/index.js +449 -182
- package/dist/ai-service/index.js.map +1 -1
- package/dist/ai-service/llm/context-v2/adapter.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/adapter.js +1 -0
- package/dist/ai-service/llm/context-v2/adapter.js.map +1 -1
- package/dist/ai-service/llm/context-v2/compaction/client-side.d.ts +30 -0
- package/dist/ai-service/llm/context-v2/compaction/client-side.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/compaction/client-side.js +77 -4
- package/dist/ai-service/llm/context-v2/compaction/client-side.js.map +1 -1
- package/dist/ai-service/llm/context-v2/compaction/compaction-strategy.d.ts +5 -0
- package/dist/ai-service/llm/context-v2/compaction/compaction-strategy.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/compaction/server-side.d.ts +1 -0
- package/dist/ai-service/llm/context-v2/compaction/server-side.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/compaction/server-side.js +3 -0
- package/dist/ai-service/llm/context-v2/compaction/server-side.js.map +1 -1
- package/dist/ai-service/llm/context-v2/compactor.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/compactor.js +57 -6
- package/dist/ai-service/llm/context-v2/compactor.js.map +1 -1
- package/dist/ai-service/llm/context-v2/config.d.ts +13 -1
- package/dist/ai-service/llm/context-v2/config.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/config.js +39 -2
- package/dist/ai-service/llm/context-v2/config.js.map +1 -1
- package/dist/ai-service/llm/context-v2/context-metrics.d.ts +23 -0
- package/dist/ai-service/llm/context-v2/context-metrics.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/context-metrics.js +52 -1
- package/dist/ai-service/llm/context-v2/context-metrics.js.map +1 -1
- package/dist/ai-service/llm/context-v2/context.d.ts +27 -3
- package/dist/ai-service/llm/context-v2/context.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/context.js +107 -10
- package/dist/ai-service/llm/context-v2/context.js.map +1 -1
- package/dist/ai-service/llm/context-v2/manager.d.ts +66 -0
- package/dist/ai-service/llm/context-v2/manager.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/manager.js +194 -2
- package/dist/ai-service/llm/context-v2/manager.js.map +1 -1
- package/dist/ai-service/llm/context-v2/storage/event-store.d.ts +31 -5
- package/dist/ai-service/llm/context-v2/storage/event-store.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/storage/jsonl-event-store.d.ts +30 -9
- package/dist/ai-service/llm/context-v2/storage/jsonl-event-store.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/storage/jsonl-event-store.js +96 -13
- package/dist/ai-service/llm/context-v2/storage/jsonl-event-store.js.map +1 -1
- package/dist/ai-service/llm/context-v2/storage/legacy-context-adoption.d.ts +46 -0
- package/dist/ai-service/llm/context-v2/storage/legacy-context-adoption.d.ts.map +1 -0
- package/dist/ai-service/llm/context-v2/storage/legacy-context-adoption.js +240 -0
- package/dist/ai-service/llm/context-v2/storage/legacy-context-adoption.js.map +1 -0
- package/dist/ai-service/llm/context-v2/storage/local.d.ts +1 -1
- package/dist/ai-service/llm/context-v2/storage/local.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/storage/local.js +3 -2
- package/dist/ai-service/llm/context-v2/storage/local.js.map +1 -1
- package/dist/ai-service/llm/context-v2/types.d.ts +5 -1
- package/dist/ai-service/llm/context-v2/types.d.ts.map +1 -1
- package/dist/ai-service/llm/context-v2/types.js.map +1 -1
- package/dist/ai-service/llm/stream/config.d.ts +4 -3
- package/dist/ai-service/llm/stream/config.d.ts.map +1 -1
- package/dist/ai-service/llm/stream/config.js.map +1 -1
- package/dist/ai-service/llm/stream/idle-monitor.d.ts +37 -4
- package/dist/ai-service/llm/stream/idle-monitor.d.ts.map +1 -1
- package/dist/ai-service/llm/stream/idle-monitor.js +38 -3
- package/dist/ai-service/llm/stream/idle-monitor.js.map +1 -1
- package/dist/ai-service/policy-agent-metrics.d.ts +18 -0
- package/dist/ai-service/policy-agent-metrics.d.ts.map +1 -0
- package/dist/ai-service/policy-agent-metrics.js +99 -0
- package/dist/ai-service/policy-agent-metrics.js.map +1 -0
- package/dist/ai-service/state-machine/clark-fsm.d.ts +9 -0
- package/dist/ai-service/state-machine/clark-fsm.d.ts.map +1 -1
- package/dist/ai-service/state-machine/helpers/prompt-request-metrics.d.ts +9 -0
- package/dist/ai-service/state-machine/helpers/prompt-request-metrics.d.ts.map +1 -1
- package/dist/ai-service/state-machine/helpers/prompt-request-metrics.js +27 -0
- package/dist/ai-service/state-machine/helpers/prompt-request-metrics.js.map +1 -1
- package/dist/ai-service/tasks/factory.d.ts +7 -0
- package/dist/ai-service/tasks/factory.d.ts.map +1 -1
- package/dist/ai-service/tasks/factory.js +5 -0
- package/dist/ai-service/tasks/factory.js.map +1 -1
- package/dist/ai-service/tasks/task-store.d.ts +11 -1
- package/dist/ai-service/tasks/task-store.d.ts.map +1 -1
- package/dist/ai-service/tasks/task-store.js +104 -29
- package/dist/ai-service/tasks/task-store.js.map +1 -1
- package/dist/ai-service/tasks/types.d.ts +9 -1
- package/dist/ai-service/tasks/types.d.ts.map +1 -1
- package/dist/extract-api-integrations.d.ts +76 -0
- package/dist/extract-api-integrations.d.ts.map +1 -1
- package/dist/extract-api-integrations.js +428 -109
- package/dist/extract-api-integrations.js.map +1 -1
- package/dist/file-sync-vite-plugin.d.ts +1 -1
- package/dist/file-sync-vite-plugin.d.ts.map +1 -1
- package/dist/file-sync-vite-plugin.js +42 -79
- package/dist/file-sync-vite-plugin.js.map +1 -1
- package/dist/file-system-helpers.d.ts +1 -1
- package/dist/file-system-helpers.d.ts.map +1 -1
- package/dist/file-system-helpers.js +1 -0
- package/dist/file-system-helpers.js.map +1 -1
- package/dist/git-auth-state.d.ts +4 -0
- package/dist/git-auth-state.d.ts.map +1 -0
- package/dist/git-auth-state.js +25 -0
- package/dist/git-auth-state.js.map +1 -0
- package/dist/git-service/checkpoint-commit-metrics.d.ts +2 -0
- package/dist/git-service/checkpoint-commit-metrics.d.ts.map +1 -0
- package/dist/git-service/checkpoint-commit-metrics.js +15 -0
- package/dist/git-service/checkpoint-commit-metrics.js.map +1 -0
- package/dist/git-service/checkpoint-committer.d.ts +22 -0
- package/dist/git-service/checkpoint-committer.d.ts.map +1 -0
- package/dist/git-service/checkpoint-committer.js +91 -0
- package/dist/git-service/checkpoint-committer.js.map +1 -0
- package/dist/git-service/commit-live-branch.d.ts +17 -0
- package/dist/git-service/commit-live-branch.d.ts.map +1 -0
- package/dist/git-service/commit-live-branch.js +28 -0
- package/dist/git-service/commit-live-branch.js.map +1 -0
- package/dist/git-service/provider-workflow-files.d.ts +12 -0
- package/dist/git-service/provider-workflow-files.d.ts.map +1 -0
- package/dist/git-service/provider-workflow-files.js +36 -0
- package/dist/git-service/provider-workflow-files.js.map +1 -0
- package/dist/plugin-options.d.ts +8 -0
- package/dist/plugin-options.d.ts.map +1 -1
- package/dist/plugin-options.js.map +1 -1
- package/dist/policy-gate-runner.d.ts.map +1 -1
- package/dist/policy-gate-runner.js +7 -9
- package/dist/policy-gate-runner.js.map +1 -1
- package/dist/server-rpc/client.d.ts.map +1 -1
- package/dist/server-rpc/client.js +2 -0
- package/dist/server-rpc/client.js.map +1 -1
- package/dist/server-rpc/index.d.ts +43 -1
- package/dist/server-rpc/index.d.ts.map +1 -1
- package/dist/server-rpc/index.js +143 -5
- package/dist/server-rpc/index.js.map +1 -1
- package/dist/server-rpc/request-abort-context.d.ts +3 -0
- package/dist/server-rpc/request-abort-context.d.ts.map +1 -0
- package/dist/server-rpc/request-abort-context.js +23 -0
- package/dist/server-rpc/request-abort-context.js.map +1 -0
- package/dist/server-rpc/rpc-invalidate-metrics.d.ts +20 -0
- package/dist/server-rpc/rpc-invalidate-metrics.d.ts.map +1 -0
- package/dist/server-rpc/rpc-invalidate-metrics.js +86 -0
- package/dist/server-rpc/rpc-invalidate-metrics.js.map +1 -0
- package/dist/socket-manager.d.ts.map +1 -1
- package/dist/socket-manager.js +21 -60
- package/dist/socket-manager.js.map +1 -1
- package/dist/sync-service/index.d.ts +26 -2
- package/dist/sync-service/index.d.ts.map +1 -1
- package/dist/sync-service/index.js +74 -37
- package/dist/sync-service/index.js.map +1 -1
- 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
|
|
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
|
|
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
|
-
//
|
|
75
|
-
//
|
|
76
|
-
//
|
|
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
|
|
96
|
-
//
|
|
97
|
-
//
|
|
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`:
|
|
100
|
-
//
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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 environment — do 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
|
|
119
|
-
//
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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((
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
626
|
-
|
|
627
|
-
|
|
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
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
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 {
|