Compare commits

..

2 Commits

Author SHA1 Message Date
centdix
71e08c239b chore: add deliberate type error to fail frontend CI
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 20:27:21 +01:00
centdix
ee206b8594 chore: add test.txt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 20:24:31 +01:00
25 changed files with 276 additions and 271 deletions

View File

@@ -20,8 +20,7 @@
"resource",
"variable",
"ducklake",
"datatable",
"volume"
"datatable"
]
}
}

View File

@@ -46,11 +46,11 @@
]
},
"nullable": [
false,
false,
false,
false,
false,
true,
true,
true,
true,
true,
true,
true
]

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "SELECT email FROM password WHERE ($2::text = '*' OR email LIKE CONCAT('%@', $2::text)) AND NOT EXISTS (\n SELECT 1 FROM usr WHERE workspace_id = $1::text AND email = password.email\n )",
"query": "SELECT email FROM password WHERE ($2::text = '*' OR email LIKE CONCAT('%', $2::text)) AND NOT EXISTS (\n SELECT 1 FROM usr WHERE workspace_id = $1::text AND email = password.email\n )",
"describe": {
"columns": [
{
@@ -19,5 +19,5 @@
false
]
},
"hash": "886a921adc115f0a9c6f3a68381bd8f5a16866135120175d9073b9b2c41bbd51"
"hash": "0ef37117c369f03236e18f9dbb1f3d52776c8cb73f2507199c6ca16d4d2405ba"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO workspace_invite\n (workspace_id, email, is_admin, operator)\n SELECT $1::text, email, false, $3 FROM password WHERE ($2::text = '*' OR email LIKE CONCAT('%@', $2::text)) AND NOT EXISTS (\n SELECT 1 FROM usr WHERE workspace_id = $1::text AND email = password.email\n )\n ON CONFLICT DO NOTHING",
"query": "INSERT INTO workspace_invite\n (workspace_id, email, is_admin, operator)\n SELECT $1::text, email, false, $3 FROM password WHERE ($2::text = '*' OR email LIKE CONCAT('%', $2::text)) AND NOT EXISTS (\n SELECT 1 FROM usr WHERE workspace_id = $1::text AND email = password.email\n )\n ON CONFLICT DO NOTHING",
"describe": {
"columns": [],
"parameters": {
@@ -12,5 +12,5 @@
},
"nullable": []
},
"hash": "c0fad64e5d707ffa29d236f558e23b608168dc3a1b3857d2ad33ec20627acbff"
"hash": "2e1d1c59bfc53d58962251822c85cf9a26e3b2888702e5e9d5fc1b082901df09"
}

View File

@@ -0,0 +1,19 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO token (token, email, label, expiration, scopes, workspace_id)\n VALUES ($1, $2, $3, now() + ($4 || ' seconds')::interval, $5, $6)",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Varchar",
"Varchar",
"Text",
"TextArray",
"Varchar"
]
},
"nullable": []
},
"hash": "7e4aa6b19b110bca423b3a3f428826d92b9808c64ef989fef2142bc8e02d6630"
}

View File

@@ -16,8 +16,7 @@
"resource",
"variable",
"ducklake",
"datatable",
"volume"
"datatable"
]
}
}

View File

@@ -0,0 +1,20 @@
{
"db_name": "PostgreSQL",
"query": "INSERT INTO token\n (token, email, label, expiration, super_admin, scopes, workspace_id)\n VALUES ($1, $2, $3, $4, $5, $6, $7)",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Varchar",
"Varchar",
"Varchar",
"Timestamptz",
"Bool",
"TextArray",
"Varchar"
]
},
"nullable": []
},
"hash": "c624f15f3e321b1eecf123da9bf0b18e8c1d16ef25ffb9d04e5447d0d583d55c"
}

View File

@@ -1 +1 @@
2fb7884849a563bae023574baa2d55fa1fab1176
a4546264d41ce7122dfd127f4b17f724eb63c40a

View File

@@ -1,5 +0,0 @@
DROP INDEX IF EXISTS idx_asset_ws_path_kind_recent;
-- Restore the dropped indexes
CREATE INDEX IF NOT EXISTS idx_asset_workspace_created_id ON asset (workspace_id, created_at DESC, id DESC);
CREATE INDEX IF NOT EXISTS idx_asset_kind_path ON asset (workspace_id, kind, path);

View File

@@ -1,11 +0,0 @@
-- Covering index for the list_assets CTE: GROUP BY (path, kind) + MAX(created_at, id) + ORDER BY
-- Includes usage_kind and usage_path to allow full index-only scan (avoiding heap lookups for filter conditions)
CREATE INDEX IF NOT EXISTS idx_asset_ws_path_kind_recent
ON asset (workspace_id, path, kind, created_at DESC, id DESC)
INCLUDE (usage_kind, usage_path);
-- Drop indexes now subsumed by idx_asset_ws_path_kind_recent:
-- idx_asset_workspace_created_id (workspace_id, created_at DESC, id DESC) - only used by list_assets CTE
-- idx_asset_kind_path (workspace_id, kind, path) - only used by list_assets CTE/outer join, covered by new index + PK
DROP INDEX IF EXISTS idx_asset_workspace_created_id;
DROP INDEX IF EXISTS idx_asset_kind_path;

View File

@@ -144,7 +144,7 @@ async fn list_assets(
format!(
r#"FROM asset
LEFT JOIN v2_job job_cte ON asset.usage_kind = 'job'
AND job_cte.id = CASE WHEN asset.usage_kind = 'job' THEN asset.usage_path::uuid END
AND asset.usage_path = job_cte.id::text
AND job_cte.workspace_id = $1"#
)
} else {
@@ -209,7 +209,7 @@ async fn list_assets(
) = resource.path
AND resource.workspace_id = $1
LEFT JOIN v2_job job ON asset.usage_kind = 'job'
AND job.id = CASE WHEN asset.usage_kind = 'job' THEN asset.usage_path::uuid END
AND asset.usage_path = job.id::text
AND job.workspace_id = $1
WHERE asset.workspace_id = $1
AND (asset.kind <> 'resource' OR resource.path IS NOT NULL)

View File

@@ -663,30 +663,12 @@ async fn global_proxy(
let base_url = provider.get_base_url(None, &db).await?;
let is_anthropic = provider.is_anthropic();
let is_anthropic_sdk = headers.get("X-Anthropic-SDK").is_some();
let url = if is_anthropic_sdk {
let truncated_base_url = base_url.trim_end_matches("/v1");
format!("{}/{}", truncated_base_url, ai_path)
} else {
format!("{}/{}", base_url, ai_path)
};
let url = format!("{}/{}", base_url, ai_path);
let mut request = HTTP_CLIENT
.request(method, url)
.header("content-type", "application/json")
.header("Authorization", format!("Bearer {}", &api_key));
if is_anthropic {
request = request.header("X-API-Key", &api_key);
}
for (header_name, header_value) in headers.iter() {
if header_name.to_string().starts_with("anthropic-") {
request = request.header(header_name, header_value);
}
}
.header("Authorization", format!("Bearer {}", api_key));
// Apply custom headers from AI_HTTP_HEADERS environment variable
for (header_name, header_value) in AI_HTTP_HEADERS.iter() {

View File

@@ -78,9 +78,6 @@ lazy_static::lazy_static! {
(20260207000004, include_str!(
"../../migrations/20260207000004_concurrent_indexes_other.up.sql"
).replace("CREATE INDEX", "CREATE INDEX CONCURRENTLY").replace("DROP INDEX", "DROP INDEX CONCURRENTLY")),
(20260225100000, include_str!(
"../../migrations/20260225100000_asset_covering_index.up.sql"
).replace("CREATE INDEX", "CREATE INDEX CONCURRENTLY").replace("DROP INDEX", "DROP INDEX CONCURRENTLY")),
].into_iter().collect();
}

View File

@@ -274,19 +274,16 @@ pub async fn eval_timeout_quickjs(
let expr_clone = expr.clone();
// Run the QuickJS evaluation with a timeout.
// Use the current runtime handle rather than creating an independent
// current-thread runtime so that HTTP connections opened by the global
// reqwest client (HTTP_CLIENT) are managed by the main runtime.
// Creating a child runtime caused connection-pool dispatch tasks to be
// dropped when the child runtime exited, poisoning pooled connections
// and producing spurious "DispatchGone" / "runtime dropped the dispatch
// task" errors on subsequent requests from the main runtime.
let handle = tokio::runtime::Handle::current();
// Run the QuickJS evaluation with a timeout
tokio::time::timeout(
std::time::Duration::from_millis(EVAL_TIMEOUT_MS),
tokio::task::spawn_blocking(move || {
handle.block_on(async move {
// Create a new tokio runtime for async operations within the blocking context
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()?;
rt.block_on(async move {
eval_quickjs_inner(
&expr_clone,
filtered_context,
@@ -303,9 +300,7 @@ pub async fn eval_timeout_quickjs(
)
.await
.map_err(|_| {
anyhow::anyhow!(
"The expression evaluation `{expr}` took too long to execute (>{EVAL_TIMEOUT_MS}ms)"
)
anyhow::anyhow!("The expression evaluation `{expr}` took too long to execute (>{EVAL_TIMEOUT_MS}ms)")
})??
}
@@ -792,11 +787,13 @@ pub async fn eval_simple_js(
expr: String,
globals: HashMap<String, serde_json::Value>,
) -> anyhow::Result<Box<RawValue>> {
let handle = tokio::runtime::Handle::current();
tokio::time::timeout(
std::time::Duration::from_millis(EVAL_TIMEOUT_MS),
tokio::task::spawn_blocking(move || {
handle.block_on(async move {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()?;
rt.block_on(async move {
let runtime = AsyncRuntime::new()?;
runtime.set_memory_limit(QUICKJS_MEMORY_LIMIT).await;
let context = AsyncContext::full(&runtime).await?;

View File

@@ -753,7 +753,8 @@ mod tests {
buf.extend_from_slice(b"users\0"); // name
buf.push(REPLICA_IDENTITY_DEFAULT_BYTE as u8); // replica identity
buf.extend_from_slice(&1i16.to_be_bytes()); // num columns
// column: flags=0, name="id", type_oid=23 (INT4), type_modifier=-1
buf.extend_from_slice(&1i16.to_be_bytes()); // num columns
// column: flags=0, name="id", type_oid=23 (INT4), type_modifier=-1
buf.push(0); // flags
buf.extend_from_slice(b"id\0"); // name
buf.extend_from_slice(&23u32.to_be_bytes()); // type_oid (INT4)

View File

@@ -1099,23 +1099,21 @@ pub async fn update_flow_status_after_job_completion_internal(
|| (flow_jobs.is_some() && (skip_loop_failures || skip_seq_branch_failure)))
&& !(stop_early && stop_early_err_msg.is_some() && !skip_if_stop_early)
{
let is_skipped_via_skip_if =
if current_module.as_ref().is_some_and(|m| m.skip_if.is_some()) {
sqlx::query_scalar!(
"SELECT kind = 'identity' FROM v2_job WHERE id = $1",
job_id_for_status
)
.fetch_optional(db)
.await
.map_err(|e| {
Error::internal_err(format!("error during skip check: {e:#}"))
})?
.flatten()
.unwrap_or(false)
} else {
false
};
let is_skipped = is_skipped_via_skip_if || (stop_early && skip_if_stop_early);
let is_skipped = if current_module.as_ref().is_some_and(|m| m.skip_if.is_some())
{
sqlx::query_scalar!(
"SELECT kind = 'identity' FROM v2_job WHERE id = $1",
job_id_for_status
)
.fetch_one(db)
.await
.map_err(|e| {
Error::internal_err(format!("error during skip check: {e:#}"))
})?
.unwrap_or(false)
} else {
stop_early && skip_if_stop_early // Mark as skipped when stop_after_if with skip_if_stopped=true
};
success = true;
(
true,

View File

@@ -102,9 +102,9 @@
"@melt-ui/svelte": "^0.86.2",
"@playwright/test": "^1.57.0",
"@sveltejs/adapter-static": "^3.0.6",
"@sveltejs/kit": "^2.53.2",
"@sveltejs/package": "^2.5.7",
"@sveltejs/vite-plugin-svelte": "^6.2.4",
"@sveltejs/kit": "^2.49.2",
"@sveltejs/package": "^2.3.7",
"@sveltejs/vite-plugin-svelte": "^6.2.1",
"@tailwindcss/forms": "^0.5.3",
"@tailwindcss/typography": "^0.5.8",
"@types/d3": "^7.4.0",
@@ -131,9 +131,9 @@
"prettier-plugin-svelte": "^3.3.3",
"style-to-object": "^0.4.1",
"stylelint-config-recommended": "^13.0.0",
"svelte": "^5.53.5",
"svelte": "^5.38.0",
"svelte-awesome-color-picker": "^3.0.4",
"svelte-check": "^4.4.3",
"svelte-check": "^4.0.0",
"svelte-fast-check": "^0.4.5",
"svelte-floating-ui": "^1.5.8",
"svelte-highlight": "^7.6.0",
@@ -835,6 +835,7 @@
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.1.tgz",
"integrity": "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -846,6 +847,7 @@
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz",
"integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -856,6 +858,7 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz",
"integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -1345,6 +1348,7 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.0.tgz",
"integrity": "sha512-Fq6DJW+Bb5jaWE69/qOE0D1TUN9+6uWhCeZpdnSBk14pjLcCWR7Q8n49PTSPHazM37JqrsdpEthXy2xn6jWWiA==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -1499,6 +1503,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1515,6 +1520,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1531,6 +1537,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1547,6 +1554,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1563,6 +1571,7 @@
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1579,6 +1588,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1595,6 +1605,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1611,6 +1622,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1627,6 +1639,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1643,6 +1656,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1659,6 +1673,7 @@
"cpu": [
"wasm32"
],
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -1675,6 +1690,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1691,6 +1707,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2085,9 +2102,9 @@
}
},
"node_modules/@sveltejs/kit": {
"version": "2.53.2",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.53.2.tgz",
"integrity": "sha512-M+MqAvFve12T1HWws/2npP/s3hFtyjw3GB/OXW/8a1jZBk48qnvPJrtgE+VOMc3RnjUMxc4mv/vQ73nvj2uNMg==",
"version": "2.49.2",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.49.2.tgz",
"integrity": "sha512-Vp3zX/qlwerQmHMP6x0Ry1oY7eKKRcOWGc2P59srOp4zcqyn+etJyQpELgOi4+ZSUgteX8Y387NuwruLgGXLUQ==",
"devOptional": true,
"license": "MIT",
"dependencies": {
@@ -2096,12 +2113,13 @@
"@types/cookie": "^0.6.0",
"acorn": "^8.14.1",
"cookie": "^0.6.0",
"devalue": "^5.6.3",
"devalue": "^5.3.2",
"esm-env": "^1.2.2",
"kleur": "^4.1.5",
"magic-string": "^0.30.5",
"mrmime": "^2.0.0",
"set-cookie-parser": "^3.0.0",
"sade": "^1.8.1",
"set-cookie-parser": "^2.6.0",
"sirv": "^3.0.0"
},
"bin": {
@@ -2112,28 +2130,24 @@
},
"peerDependencies": {
"@opentelemetry/api": "^1.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0",
"svelte": "^4.0.0 || ^5.0.0-next.0",
"typescript": "^5.3.3",
"vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0"
"vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0"
},
"peerDependenciesMeta": {
"@opentelemetry/api": {
"optional": true
},
"typescript": {
"optional": true
}
}
},
"node_modules/@sveltejs/package": {
"version": "2.5.7",
"resolved": "https://registry.npmjs.org/@sveltejs/package/-/package-2.5.7.tgz",
"integrity": "sha512-qqD9xa9H7TDiGFrF6rz7AirOR8k15qDK/9i4MIE8te4vWsv5GEogPks61rrZcLy+yWph+aI6pIj2MdoK3YI8AQ==",
"version": "2.5.4",
"resolved": "https://registry.npmjs.org/@sveltejs/package/-/package-2.5.4.tgz",
"integrity": "sha512-8+1hccAt0M3PPkHVPKH54Wc+cc1PNxRqCrICZiv/hEEto8KwbQVRghxNgTB4htIPyle+4CIB8RayTQH5zRQh9A==",
"dev": true,
"license": "MIT",
"dependencies": {
"chokidar": "^5.0.0",
"chokidar": "^4.0.3",
"kleur": "^4.1.5",
"sade": "^1.8.1",
"semver": "^7.5.4",
@@ -2149,47 +2163,17 @@
"svelte": "^3.44.0 || ^4.0.0 || ^5.0.0-next.1"
}
},
"node_modules/@sveltejs/package/node_modules/chokidar": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz",
"integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==",
"dev": true,
"license": "MIT",
"dependencies": {
"readdirp": "^5.0.0"
},
"engines": {
"node": ">= 20.19.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@sveltejs/package/node_modules/readdirp": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz",
"integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 20.19.0"
},
"funding": {
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@sveltejs/vite-plugin-svelte": {
"version": "6.2.4",
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-6.2.4.tgz",
"integrity": "sha512-ou/d51QSdTyN26D7h6dSpusAKaZkAiGM55/AKYi+9AGZw7q85hElbjK3kEyzXHhLSnRISHOYzVge6x0jRZ7DXA==",
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-6.2.1.tgz",
"integrity": "sha512-YZs/OSKOQAQCnJvM/P+F1URotNnYNeU3P2s4oIpzm1uFaqUEqRxUB0g5ejMjEb5Gjb9/PiBI5Ktrq4rUUF8UVQ==",
"devOptional": true,
"license": "MIT",
"dependencies": {
"@sveltejs/vite-plugin-svelte-inspector": "^5.0.0",
"debug": "^4.4.1",
"deepmerge": "^4.3.1",
"magic-string": "^0.30.21",
"obug": "^2.1.0",
"magic-string": "^0.30.17",
"vitefu": "^1.1.1"
},
"engines": {
@@ -2329,6 +2313,7 @@
"version": "0.10.1",
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
"integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
@@ -2738,7 +2723,8 @@
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
"license": "MIT"
"license": "MIT",
"optional": true
},
"node_modules/@types/unist": {
"version": "3.0.3",
@@ -3456,9 +3442,9 @@
"license": "Python-2.0"
},
"node_modules/aria-query": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.1.tgz",
"integrity": "sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==",
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz",
"integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==",
"license": "Apache-2.0",
"engines": {
"node": ">= 0.4"
@@ -4985,9 +4971,10 @@
}
},
"node_modules/devalue": {
"version": "5.6.3",
"resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.3.tgz",
"integrity": "sha512-nc7XjUU/2Lb+SvEFVGcWLiKkzfw8+qHI7zn8WYXKkLMgfGSHbgCEaR6bJpev8Cm6Rmrb19Gfd/tZvGqx9is3wg==",
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/devalue/-/devalue-5.3.2.tgz",
"integrity": "sha512-UDsjUbpQn9kvm68slnrs+mfxwFkIflOhkanmyabZ8zOYk8SMEIbJ3TK+88g70hSIeytu4y18f0z/hYHMTrXIWw==",
"devOptional": true,
"license": "MIT"
},
"node_modules/devlop": {
@@ -5718,9 +5705,9 @@
}
},
"node_modules/esrap": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/esrap/-/esrap-2.2.3.tgz",
"integrity": "sha512-8fOS+GIGCQZl/ZIlhl59htOlms6U8NvX6ZYgYHpRU/b6tVSh3uHkOHZikl3D4cMbYM0JlpBe+p/BkZEi8J9XIQ==",
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/esrap/-/esrap-2.1.0.tgz",
"integrity": "sha512-yzmPNpl7TBbMRC5Lj2JlJZNPml0tzqoqP5B1JXycNUwtqma9AKCO0M2wHrdgsHcy1WRW7S9rJknAMtByg3usgA==",
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.15"
@@ -7206,7 +7193,7 @@
"version": "1.21.7",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz",
"integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
"devOptional": true,
"dev": true,
"license": "MIT",
"bin": {
"jiti": "bin/jiti.js"
@@ -7705,6 +7692,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -7725,6 +7713,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -7745,6 +7734,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -7765,6 +7755,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -7785,6 +7776,7 @@
"cpu": [
"arm"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -7805,6 +7797,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -7825,6 +7818,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -7845,6 +7839,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -7865,6 +7860,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -7885,6 +7881,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -7905,6 +7902,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MPL-2.0",
"optional": true,
"os": [
@@ -9236,7 +9234,7 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
"integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==",
"dev": true,
"devOptional": true,
"license": "MIT",
"engines": {
"node": ">=4"
@@ -9543,7 +9541,7 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz",
"integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==",
"devOptional": true,
"dev": true,
"funding": [
"https://github.com/sponsors/sxzz",
"https://opencollective.com/debug"
@@ -11568,7 +11566,7 @@
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz",
"integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==",
"dev": true,
"devOptional": true,
"license": "MIT",
"dependencies": {
"mri": "^1.1.0"
@@ -11617,9 +11615,9 @@
}
},
"node_modules/set-cookie-parser": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.0.1.tgz",
"integrity": "sha512-n7Z7dXZhJbwuAHhNzkTti6Aw9QDDjZtm3JTpTGATIdNzdQz5GuFs22w90BcvF4INfnrL5xrX3oGsuqO5Dx3A1Q==",
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz",
"integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==",
"devOptional": true,
"license": "MIT"
},
@@ -12432,23 +12430,21 @@
}
},
"node_modules/svelte": {
"version": "5.53.5",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.53.5.tgz",
"integrity": "sha512-YkqERnF05g8KLdDZwZrF8/i1eSbj6Eoat8Jjr2IfruZz9StLuBqo8sfCSzjosNKd+ZrQ8DkKZDjpO5y3ht1Pow==",
"version": "5.39.12",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.39.12.tgz",
"integrity": "sha512-CEzwxFuEycokU8K8CE/OuwVbmei+ivu2HvBGYIdASfMa1hCRSNr4RRkzNSvbAvu6h+BOig2CsZTAEY+WKvwZpA==",
"license": "MIT",
"dependencies": {
"@jridgewell/remapping": "^2.3.4",
"@jridgewell/sourcemap-codec": "^1.5.0",
"@sveltejs/acorn-typescript": "^1.0.5",
"@types/estree": "^1.0.5",
"@types/trusted-types": "^2.0.7",
"acorn": "^8.12.1",
"aria-query": "5.3.1",
"aria-query": "^5.3.1",
"axobject-query": "^4.1.0",
"clsx": "^2.1.1",
"devalue": "^5.6.3",
"esm-env": "^1.2.1",
"esrap": "^2.2.2",
"esrap": "^2.1.0",
"is-reference": "^3.0.3",
"locate-character": "^3.0.0",
"magic-string": "^0.30.11",
@@ -12492,9 +12488,9 @@
}
},
"node_modules/svelte-check": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.4.3.tgz",
"integrity": "sha512-4HtdEv2hOoLCEsSXI+RDELk9okP/4sImWa7X02OjMFFOWeSdFF3NFy3vqpw0z+eH9C88J9vxZfUXz/Uv2A1ANw==",
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.3.3.tgz",
"integrity": "sha512-RYP0bEwenDXzfv0P1sKAwjZSlaRyqBn0Fz1TVni58lqyEiqgwztTpmodJrGzP6ZT2aHl4MbTvWP6gbmQ3FOnBg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -12533,6 +12529,21 @@
}
}
},
"node_modules/svelte-check/node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/svelte-eslint-parser": {
"version": "0.43.0",
"resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-0.43.0.tgz",
@@ -13263,7 +13274,7 @@
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"devOptional": true,
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",

View File

@@ -27,9 +27,9 @@
"@melt-ui/svelte": "^0.86.2",
"@playwright/test": "^1.57.0",
"@sveltejs/adapter-static": "^3.0.6",
"@sveltejs/kit": "^2.53.2",
"@sveltejs/package": "^2.5.7",
"@sveltejs/vite-plugin-svelte": "^6.2.4",
"@sveltejs/kit": "^2.49.2",
"@sveltejs/package": "^2.3.7",
"@sveltejs/vite-plugin-svelte": "^6.2.1",
"@tailwindcss/forms": "^0.5.3",
"@tailwindcss/typography": "^0.5.8",
"@types/d3": "^7.4.0",
@@ -56,9 +56,9 @@
"prettier-plugin-svelte": "^3.3.3",
"style-to-object": "^0.4.1",
"stylelint-config-recommended": "^13.0.0",
"svelte": "^5.53.5",
"svelte": "^5.38.0",
"svelte-awesome-color-picker": "^3.0.4",
"svelte-check": "^4.4.3",
"svelte-check": "^4.0.0",
"svelte-fast-check": "^0.4.5",
"svelte-floating-ui": "^1.5.8",
"svelte-highlight": "^7.6.0",

View File

@@ -9,116 +9,113 @@ const USER_CUSTOM_PROMPTS_KEY = 'userCustomAIPrompts'
const sessionModel = getLocalSetting(COPILOT_SESSION_MODEL_SETTING_NAME)
const sessionProvider = getLocalSetting(COPILOT_SESSION_PROVIDER_SETTING_NAME)
export const copilotSessionModel = writable<AIProviderModel | undefined>(
sessionModel && sessionProvider
? {
model: sessionModel,
provider: sessionProvider as AIProvider
}
: undefined
sessionModel && sessionProvider
? {
model: sessionModel,
provider: sessionProvider as AIProvider
}
: undefined
)
export const copilotInfo = writable<{
enabled: boolean
codeCompletionModel?: AIProviderModel
defaultModel?: AIProviderModel
aiModels: AIProviderModel[]
customPrompts?: Record<string, string>
maxTokensPerModel?: Record<string, number>
enabled: boolean
codeCompletionModel?: AIProviderModel
defaultModel?: AIProviderModel
aiModels: AIProviderModel[]
customPrompts?: Record<string, string>
maxTokensPerModel?: Record<string, number>
}>({
enabled: false,
codeCompletionModel: undefined,
defaultModel: undefined,
aiModels: [],
customPrompts: {},
maxTokensPerModel: {}
enabled: false,
codeCompletionModel: undefined,
defaultModel: undefined,
aiModels: [],
customPrompts: {},
maxTokensPerModel: {}
})
export async function loadCopilot(workspace: string) {
workspaceAIClients.init(workspace)
try {
const info = await WorkspaceService.getCopilotInfo({ workspace })
setCopilotInfo(info)
} catch (err) {
setCopilotInfo({})
console.error('Could not get copilot info', err)
}
workspaceAIClients.init(workspace)
try {
const info = await WorkspaceService.getCopilotInfo({ workspace })
setCopilotInfo(info)
} catch (err) {
setCopilotInfo({})
console.error('Could not get copilot info', err)
}
}
export function setCopilotInfo(aiConfig: AIConfig) {
if (Object.keys(aiConfig.providers ?? {}).length > 0) {
const aiModels = Object.entries(aiConfig.providers ?? {}).flatMap(
([provider, providerConfig]) =>
providerConfig.models.map((m) => ({ model: m, provider: provider as AIProvider }))
)
if (Object.keys(aiConfig.providers ?? {}).length > 0) {
const aiModels = Object.entries(aiConfig.providers ?? {}).flatMap(
([provider, providerConfig]) =>
providerConfig.models.map((m) => ({ model: m, provider: provider as AIProvider }))
)
copilotSessionModel.update((model) => {
if (
model &&
!aiModels.some((m) => m.model === model.model && m.provider === model.provider)
) {
return undefined
}
return model
})
copilotSessionModel.update((model) => {
if (
model &&
!aiModels.some((m) => m.model === model.model && m.provider === model.provider)
) {
return undefined
}
return model
})
copilotInfo.set({
enabled: true,
codeCompletionModel: aiConfig.code_completion_model,
defaultModel: aiConfig.default_model,
aiModels: aiModels,
customPrompts: aiConfig.custom_prompts ?? {},
maxTokensPerModel: aiConfig.max_tokens_per_model ?? {}
})
} else {
copilotSessionModel.set(undefined)
copilotInfo.set({
enabled: true,
codeCompletionModel: aiConfig.code_completion_model,
defaultModel: aiConfig.default_model,
aiModels: aiModels,
customPrompts: aiConfig.custom_prompts ?? {},
maxTokensPerModel: aiConfig.max_tokens_per_model ?? {}
})
} else {
copilotSessionModel.set(undefined)
copilotInfo.set({
enabled: false,
codeCompletionModel: undefined,
defaultModel: undefined,
aiModels: [],
customPrompts: {},
maxTokensPerModel: {}
})
}
copilotInfo.set({
enabled: false,
codeCompletionModel: undefined,
defaultModel: undefined,
aiModels: [],
customPrompts: {},
maxTokensPerModel: {}
})
}
}
export function getCurrentModel(): AIProviderModel {
const model =
get(copilotSessionModel) ?? get(copilotInfo).defaultModel ?? get(copilotInfo).aiModels[0]
if (!model) {
throw new Error('No model selected')
}
return model
}
export function tryGetCurrentModel(): AIProviderModel | undefined {
return get(copilotSessionModel) ?? get(copilotInfo).defaultModel ?? get(copilotInfo).aiModels[0]
export function getCurrentModel() {
const model =
get(copilotSessionModel) ?? get(copilotInfo).defaultModel ?? get(copilotInfo).aiModels[0]
if (!model) {
throw new Error('No model selected')
}
return model
}
export function getUserCustomPrompts(): Record<string, string> {
const stored = getLocalSetting(USER_CUSTOM_PROMPTS_KEY)
if (stored) {
try {
return JSON.parse(stored)
} catch (e) {
console.error('Failed to parse user custom prompts', e)
return {}
}
}
return {}
const stored = getLocalSetting(USER_CUSTOM_PROMPTS_KEY)
if (stored) {
try {
return JSON.parse(stored)
} catch (e) {
console.error('Failed to parse user custom prompts', e)
return {}
}
}
return {}
}
export function getCombinedCustomPrompt(mode: string): string | undefined {
const workspacePrompt = get(copilotInfo).customPrompts?.[mode]
const userPrompts = getUserCustomPrompts()
const userPrompt = userPrompts[mode]
const workspacePrompt = get(copilotInfo).customPrompts?.[mode]
const userPrompts = getUserCustomPrompts()
const userPrompt = userPrompts[mode]
const prompts = [workspacePrompt, userPrompt].filter((p) => p?.trim())
const prompts = [workspacePrompt, userPrompt].filter((p) => p?.trim())
if (prompts.length === 0) {
return undefined
}
if (prompts.length === 0) {
return undefined
}
return prompts.join('\n\n')
return prompts.join('\n\n')
}

View File

@@ -57,7 +57,7 @@ import { prepareApiSystemMessage, prepareApiUserMessage } from './api/core'
import { getAnthropicCompletion, parseAnthropicCompletion } from './anthropic'
import { getOpenAIResponsesCompletion, parseOpenAIResponsesCompletion } from './openai-responses'
import type { ReviewChangesOpts } from './monaco-adapter'
import { getCurrentModel, tryGetCurrentModel, getCombinedCustomPrompt } from '$lib/aiStore'
import { getCurrentModel, getCombinedCustomPrompt } from '$lib/aiStore'
// If the estimated token usage is greater than the model context window - the threshold, we delete the oldest message
const MAX_TOKENS_THRESHOLD_PERCENTAGE = 0.05
@@ -229,12 +229,11 @@ class AIChatManager {
closeScriptSettings?: boolean
}
) {
if (mode === AIMode.SCRIPT && !tryGetCurrentModel()) return
this.mode = mode
this.pendingPrompt = pendingPrompt ?? ''
if (mode === AIMode.SCRIPT) {
const currentModel = getCurrentModel()
const customPrompt = getCombinedCustomPrompt(mode)
const currentModel = getCurrentModel()
const lang = this.scriptEditorOptions?.lang ?? 'bun'
const context = this.contextManager.getSelectedContext()
this.systemMessage = prepareScriptSystemMessage(currentModel, lang, {}, customPrompt)

View File

@@ -55,7 +55,7 @@ export const AI_PROVIDERS: Record<AIProvider, AIProviderDetails> = {
},
anthropic: {
label: 'Anthropic',
defaultModels: ['claude-sonnet-4-6', 'claude-sonnet-4-6/thinking', 'claude-3-5-haiku-latest']
defaultModels: ['claude-sonnet-4-0', 'claude-sonnet-4-0/thinking', 'claude-3-5-haiku-latest']
},
mistral: {
label: 'Mistral',

View File

@@ -51,7 +51,7 @@
}
</script>
<script lang="ts">
<script>
import { Plus, SettingsIcon } from 'lucide-svelte'
import Button from '../common/button/Button.svelte'

View File

@@ -6,6 +6,9 @@
// import { page } from '$app/stores'
// import { get } from 'svelte/store'
// CI test: deliberate type error
const _ciTest: number = 'this is not a number'
import { deepEqual } from 'fast-equals'
import YAML from 'yaml'
import { type UserExt } from './stores'

View File

@@ -88,11 +88,10 @@ const config = {
plugins: [sveltekit(), ...(process.env.HTTPS === 'true' ? [mkcert()] : []), plugin],
define: { __pkg__: version },
optimizeDeps: {
include: ['highlight.js', 'highlight.js/lib/core', 'monaco-vim'],
include: ['highlight.js', 'highlight.js/lib/core', 'monaco-vim', 'monaco-editor-wrapper'],
exclude: [
'@codingame/monaco-vscode-standalone-typescript-language-features',
'@codingame/monaco-vscode-standalone-languages',
'windmill-client'
]
},
worker: { format: 'es' },

0
test.txt Normal file
View File