@cortexkit/pi-magic-context 0.32.1 → 0.32.2
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/index.js +3688 -149
- package/dist/subagent-entry.js +3589 -111
- package/package.json +1 -1
package/dist/subagent-entry.js
CHANGED
|
@@ -8018,9 +8018,27 @@ var require_src2 = __commonJS((exports, module) => {
|
|
|
8018
8018
|
|
|
8019
8019
|
// ../plugin/src/features/magic-context/storage-db.ts
|
|
8020
8020
|
import { chmodSync, copyFileSync, cpSync, existsSync as existsSync4, mkdirSync as mkdirSync3, mkdtempSync } from "node:fs";
|
|
8021
|
-
init_data_path();
|
|
8022
8021
|
import { dirname as dirname2, join as join3 } from "node:path";
|
|
8023
8022
|
|
|
8023
|
+
// ../plugin/src/plugin/boot-quiet.ts
|
|
8024
|
+
var BOOT_QUIET_MS = 120000;
|
|
8025
|
+
var bootQuietUntilMs = 0;
|
|
8026
|
+
function beginBootQuietPeriod(now = Date.now()) {
|
|
8027
|
+
if (bootQuietUntilMs === 0)
|
|
8028
|
+
bootQuietUntilMs = now + BOOT_QUIET_MS;
|
|
8029
|
+
}
|
|
8030
|
+
function bootQuietRemainingMs(now = Date.now()) {
|
|
8031
|
+
return Math.max(0, bootQuietUntilMs - now);
|
|
8032
|
+
}
|
|
8033
|
+
function scheduleAfterBootQuiet(task, additionalDelayMs = 0) {
|
|
8034
|
+
const timer = setTimeout(task, bootQuietRemainingMs() + Math.max(0, additionalDelayMs));
|
|
8035
|
+
timer.unref?.();
|
|
8036
|
+
return timer;
|
|
8037
|
+
}
|
|
8038
|
+
|
|
8039
|
+
// ../plugin/src/features/magic-context/storage-db.ts
|
|
8040
|
+
init_data_path();
|
|
8041
|
+
|
|
8024
8042
|
// ../plugin/src/shared/error-message.ts
|
|
8025
8043
|
function getErrorMessage(error) {
|
|
8026
8044
|
return error instanceof Error ? error.message : String(error);
|
|
@@ -8901,6 +8919,22 @@ function bumpEpochsForWorkspaceMemberSet(db, identities, now = Date.now()) {
|
|
|
8901
8919
|
}
|
|
8902
8920
|
|
|
8903
8921
|
// ../plugin/src/features/magic-context/migrations.ts
|
|
8922
|
+
var MIGRATION_LOCK_RETRY_DELAYS_MS = [1000, 2000, 4000, 8000, 15000];
|
|
8923
|
+
|
|
8924
|
+
class MigrationLockBusyError extends Error {
|
|
8925
|
+
constructor(message) {
|
|
8926
|
+
super(message);
|
|
8927
|
+
this.name = "MigrationLockBusyError";
|
|
8928
|
+
}
|
|
8929
|
+
}
|
|
8930
|
+
function isSqliteLockError(error) {
|
|
8931
|
+
if (!error || typeof error !== "object")
|
|
8932
|
+
return false;
|
|
8933
|
+
const candidate = error;
|
|
8934
|
+
if (candidate.code === "SQLITE_BUSY" || candidate.code === "SQLITE_LOCKED")
|
|
8935
|
+
return true;
|
|
8936
|
+
return typeof candidate.message === "string" && /database is locked|sqlite_(busy|locked)/i.test(candidate.message);
|
|
8937
|
+
}
|
|
8904
8938
|
function tableExists2(db, name) {
|
|
8905
8939
|
return Boolean(db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name = ?").get(name));
|
|
8906
8940
|
}
|
|
@@ -10260,6 +10294,80 @@ var MIGRATIONS = [
|
|
|
10260
10294
|
ensureColumn(db, "session_meta", "emergency_recovery_origin", "TEXT DEFAULT ''");
|
|
10261
10295
|
}
|
|
10262
10296
|
}
|
|
10297
|
+
},
|
|
10298
|
+
{
|
|
10299
|
+
version: 53,
|
|
10300
|
+
description: "add Synapse batch, shadow, and measurement storage",
|
|
10301
|
+
up(db) {
|
|
10302
|
+
db.exec(`
|
|
10303
|
+
CREATE TABLE IF NOT EXISTS embedding_registrations (
|
|
10304
|
+
project_path TEXT PRIMARY KEY,
|
|
10305
|
+
provider_identity TEXT NOT NULL DEFAULT '',
|
|
10306
|
+
model_id TEXT NOT NULL DEFAULT '',
|
|
10307
|
+
chunk_model_id TEXT NOT NULL DEFAULT '',
|
|
10308
|
+
fingerprint TEXT NOT NULL DEFAULT '',
|
|
10309
|
+
table_epoch INTEGER NOT NULL DEFAULT 0,
|
|
10310
|
+
dims INTEGER NOT NULL DEFAULT 0,
|
|
10311
|
+
provenance_json TEXT NOT NULL DEFAULT '{}',
|
|
10312
|
+
generation INTEGER NOT NULL DEFAULT 0,
|
|
10313
|
+
updated_at INTEGER NOT NULL DEFAULT 0
|
|
10314
|
+
);
|
|
10315
|
+
CREATE TABLE IF NOT EXISTS synapse_batch_ledger (
|
|
10316
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
10317
|
+
session_id TEXT NOT NULL,
|
|
10318
|
+
project_path TEXT NOT NULL DEFAULT '',
|
|
10319
|
+
scope TEXT NOT NULL DEFAULT '',
|
|
10320
|
+
manifest_json TEXT NOT NULL DEFAULT '{}',
|
|
10321
|
+
request_key TEXT NOT NULL DEFAULT '',
|
|
10322
|
+
job_id TEXT,
|
|
10323
|
+
cursor TEXT,
|
|
10324
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
10325
|
+
created_at INTEGER NOT NULL DEFAULT 0,
|
|
10326
|
+
updated_at INTEGER NOT NULL DEFAULT 0,
|
|
10327
|
+
UNIQUE(session_id, request_key)
|
|
10328
|
+
);
|
|
10329
|
+
CREATE INDEX IF NOT EXISTS idx_synapse_batch_ledger_session
|
|
10330
|
+
ON synapse_batch_ledger(session_id, updated_at);
|
|
10331
|
+
CREATE TABLE IF NOT EXISTS shadow_embedding_registrations (
|
|
10332
|
+
project_path TEXT NOT NULL,
|
|
10333
|
+
scope TEXT NOT NULL CHECK(scope IN ('memory', 'commit', 'chunk')),
|
|
10334
|
+
model_id TEXT NOT NULL,
|
|
10335
|
+
generation INTEGER NOT NULL DEFAULT 0,
|
|
10336
|
+
fingerprint TEXT NOT NULL DEFAULT '',
|
|
10337
|
+
table_epoch INTEGER NOT NULL DEFAULT 0,
|
|
10338
|
+
dims INTEGER NOT NULL DEFAULT 0,
|
|
10339
|
+
provenance_json TEXT NOT NULL DEFAULT '{}',
|
|
10340
|
+
updated_at INTEGER NOT NULL DEFAULT 0,
|
|
10341
|
+
PRIMARY KEY(project_path, scope, model_id)
|
|
10342
|
+
);
|
|
10343
|
+
CREATE TABLE IF NOT EXISTS embedding_measurement_corpus (
|
|
10344
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
10345
|
+
session_id TEXT NOT NULL,
|
|
10346
|
+
project_path TEXT NOT NULL DEFAULT '',
|
|
10347
|
+
dedup_key TEXT NOT NULL DEFAULT '',
|
|
10348
|
+
cohort_key TEXT NOT NULL DEFAULT '',
|
|
10349
|
+
query_text_hash TEXT NOT NULL DEFAULT '',
|
|
10350
|
+
primary_result_ids_json TEXT NOT NULL DEFAULT '[]',
|
|
10351
|
+
shadow_result_ids_json TEXT NOT NULL DEFAULT '[]',
|
|
10352
|
+
primary_latency_ms INTEGER,
|
|
10353
|
+
shadow_latency_ms INTEGER,
|
|
10354
|
+
primary_failed INTEGER NOT NULL DEFAULT 0,
|
|
10355
|
+
shadow_failed INTEGER NOT NULL DEFAULT 0,
|
|
10356
|
+
primary_model_id TEXT NOT NULL DEFAULT '',
|
|
10357
|
+
shadow_model_id TEXT NOT NULL DEFAULT '',
|
|
10358
|
+
primary_fingerprint TEXT NOT NULL DEFAULT '',
|
|
10359
|
+
shadow_fingerprint TEXT NOT NULL DEFAULT '',
|
|
10360
|
+
primary_epoch INTEGER NOT NULL DEFAULT 0,
|
|
10361
|
+
shadow_epoch INTEGER NOT NULL DEFAULT 0,
|
|
10362
|
+
corpus_hash TEXT NOT NULL DEFAULT '',
|
|
10363
|
+
coverage_json TEXT NOT NULL DEFAULT '{}',
|
|
10364
|
+
created_at INTEGER NOT NULL DEFAULT 0,
|
|
10365
|
+
UNIQUE(dedup_key, cohort_key)
|
|
10366
|
+
);
|
|
10367
|
+
CREATE INDEX IF NOT EXISTS idx_embedding_measurement_session
|
|
10368
|
+
ON embedding_measurement_corpus(session_id, created_at);
|
|
10369
|
+
`);
|
|
10370
|
+
}
|
|
10263
10371
|
}
|
|
10264
10372
|
];
|
|
10265
10373
|
var LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max, m) => Math.max(max, m.version), 0);
|
|
@@ -10288,7 +10396,14 @@ function isSiblingMigrationConflict(db, error, version) {
|
|
|
10288
10396
|
return confirmed != null;
|
|
10289
10397
|
}
|
|
10290
10398
|
function runMigrations(db) {
|
|
10291
|
-
|
|
10399
|
+
try {
|
|
10400
|
+
ensureMigrationsTable(db);
|
|
10401
|
+
} catch (error) {
|
|
10402
|
+
if (isSqliteLockError(error)) {
|
|
10403
|
+
throw new MigrationLockBusyError(`failed to prepare migration lock: ${error instanceof Error ? error.message : String(error)}`);
|
|
10404
|
+
}
|
|
10405
|
+
throw error;
|
|
10406
|
+
}
|
|
10292
10407
|
let loggedPlan = false;
|
|
10293
10408
|
while (true) {
|
|
10294
10409
|
let migration;
|
|
@@ -10312,6 +10427,9 @@ function runMigrations(db) {
|
|
|
10312
10427
|
break;
|
|
10313
10428
|
log(`[migrations] applied v${migration.version}: ${migration.description}`);
|
|
10314
10429
|
} catch (error) {
|
|
10430
|
+
if (!migration && isSqliteLockError(error)) {
|
|
10431
|
+
throw new MigrationLockBusyError(`failed to acquire migration write lock: ${error instanceof Error ? error.message : String(error)}`);
|
|
10432
|
+
}
|
|
10315
10433
|
if (migration && isSiblingMigrationConflict(db, error, migration.version)) {
|
|
10316
10434
|
log(`[migrations] v${migration.version} already applied by sibling instance — resuming with re-read version`);
|
|
10317
10435
|
const reReadVersion = getCurrentVersion(db);
|
|
@@ -10329,6 +10447,26 @@ function runMigrations(db) {
|
|
|
10329
10447
|
log(`[migrations] schema version now: ${MIGRATIONS[MIGRATIONS.length - 1].version}`);
|
|
10330
10448
|
}
|
|
10331
10449
|
}
|
|
10450
|
+
async function runMigrationsWithRetry(db, options = {}) {
|
|
10451
|
+
const retryDelaysMs = options.retryDelaysMs ?? MIGRATION_LOCK_RETRY_DELAYS_MS;
|
|
10452
|
+
const sleep = options.sleep ?? ((delayMs) => new Promise((resolve) => setTimeout(resolve, delayMs)));
|
|
10453
|
+
const totalAttempts = retryDelaysMs.length + 1;
|
|
10454
|
+
for (let attempt = 1;attempt <= totalAttempts; attempt += 1) {
|
|
10455
|
+
log(`[migrations] migration lock check attempt ${attempt}/${totalAttempts}`);
|
|
10456
|
+
try {
|
|
10457
|
+
runMigrations(db);
|
|
10458
|
+
return;
|
|
10459
|
+
} catch (error) {
|
|
10460
|
+
if (!(error instanceof MigrationLockBusyError))
|
|
10461
|
+
throw error;
|
|
10462
|
+
const delayMs = retryDelaysMs[attempt - 1];
|
|
10463
|
+
if (delayMs === undefined)
|
|
10464
|
+
throw error;
|
|
10465
|
+
log(`[migrations] migration write lock is busy; retrying attempt ${attempt + 1}/${totalAttempts} in ${delayMs}ms`);
|
|
10466
|
+
await sleep(delayMs);
|
|
10467
|
+
}
|
|
10468
|
+
}
|
|
10469
|
+
}
|
|
10332
10470
|
|
|
10333
10471
|
// ../plugin/src/shared/commit-detection.ts
|
|
10334
10472
|
var HASH_HEX = "[0-9a-f]{7,12}";
|
|
@@ -143242,11 +143380,12 @@ function backfillToolOwnersInChunks(db, result) {
|
|
|
143242
143380
|
|
|
143243
143381
|
// ../plugin/src/features/magic-context/storage-db.ts
|
|
143244
143382
|
var databases = new Map;
|
|
143383
|
+
var pendingAsyncOpens = new Map;
|
|
143245
143384
|
var persistenceByDatabase = new WeakMap;
|
|
143246
143385
|
var persistenceErrorByDatabase = new WeakMap;
|
|
143247
143386
|
var pathByDatabase = new WeakMap;
|
|
143248
143387
|
var lastSchemaFenceRejection = null;
|
|
143249
|
-
var LATEST_SUPPORTED_VERSION =
|
|
143388
|
+
var LATEST_SUPPORTED_VERSION = 53;
|
|
143250
143389
|
var PERMISSIONS_ENFORCEABLE = process.platform !== "win32";
|
|
143251
143390
|
function ensureSecureStorageDir(dir) {
|
|
143252
143391
|
mkdirSync3(dir, { recursive: true, mode: 448 });
|
|
@@ -143376,6 +143515,34 @@ function runSqliteOptimize(db) {
|
|
|
143376
143515
|
db.exec("PRAGMA optimize");
|
|
143377
143516
|
} catch {}
|
|
143378
143517
|
}
|
|
143518
|
+
function finishDatabaseOpen(db, dbPath, explicitDbPath, latestSupportedVersion) {
|
|
143519
|
+
if (!enforceSchemaFence(db, dbPath, latestSupportedVersion)) {
|
|
143520
|
+
closeQuietly(db);
|
|
143521
|
+
return null;
|
|
143522
|
+
}
|
|
143523
|
+
healWedgedChannel2Claims(db);
|
|
143524
|
+
if (!explicitDbPath) {
|
|
143525
|
+
const runBackfill = () => {
|
|
143526
|
+
try {
|
|
143527
|
+
runToolOwnerBackfill(db);
|
|
143528
|
+
} catch (error) {
|
|
143529
|
+
log(`[magic-context] tool-owner backfill failed (continuing with lazy adoption fallback): ${getErrorMessage(error)}`);
|
|
143530
|
+
}
|
|
143531
|
+
};
|
|
143532
|
+
if (bootQuietRemainingMs() > 0)
|
|
143533
|
+
scheduleAfterBootQuiet(runBackfill);
|
|
143534
|
+
else
|
|
143535
|
+
runBackfill();
|
|
143536
|
+
}
|
|
143537
|
+
setDatabase(db);
|
|
143538
|
+
loadToolDefinitionMeasurements(db);
|
|
143539
|
+
restrictDatabaseFilePermissions(dbPath);
|
|
143540
|
+
databases.set(dbPath, db);
|
|
143541
|
+
pathByDatabase.set(db, dbPath);
|
|
143542
|
+
persistenceByDatabase.set(db, true);
|
|
143543
|
+
persistenceErrorByDatabase.delete(db);
|
|
143544
|
+
return db;
|
|
143545
|
+
}
|
|
143379
143546
|
function initializeDatabase(db) {
|
|
143380
143547
|
db.exec("PRAGMA busy_timeout=5000");
|
|
143381
143548
|
db.exec("PRAGMA foreign_keys=ON");
|
|
@@ -143633,6 +143800,76 @@ function initializeDatabase(db) {
|
|
|
143633
143800
|
PRIMARY KEY(project_path, scope, model_id)
|
|
143634
143801
|
);
|
|
143635
143802
|
|
|
143803
|
+
CREATE TABLE IF NOT EXISTS embedding_registrations (
|
|
143804
|
+
project_path TEXT PRIMARY KEY,
|
|
143805
|
+
provider_identity TEXT NOT NULL DEFAULT '',
|
|
143806
|
+
model_id TEXT NOT NULL DEFAULT '',
|
|
143807
|
+
chunk_model_id TEXT NOT NULL DEFAULT '',
|
|
143808
|
+
fingerprint TEXT NOT NULL DEFAULT '',
|
|
143809
|
+
table_epoch INTEGER NOT NULL DEFAULT 0,
|
|
143810
|
+
dims INTEGER NOT NULL DEFAULT 0,
|
|
143811
|
+
provenance_json TEXT NOT NULL DEFAULT '{}',
|
|
143812
|
+
generation INTEGER NOT NULL DEFAULT 0,
|
|
143813
|
+
updated_at INTEGER NOT NULL DEFAULT 0
|
|
143814
|
+
);
|
|
143815
|
+
|
|
143816
|
+
CREATE TABLE IF NOT EXISTS synapse_batch_ledger (
|
|
143817
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
143818
|
+
session_id TEXT NOT NULL,
|
|
143819
|
+
project_path TEXT NOT NULL DEFAULT '',
|
|
143820
|
+
scope TEXT NOT NULL DEFAULT '',
|
|
143821
|
+
manifest_json TEXT NOT NULL DEFAULT '{}',
|
|
143822
|
+
request_key TEXT NOT NULL DEFAULT '',
|
|
143823
|
+
job_id TEXT,
|
|
143824
|
+
cursor TEXT,
|
|
143825
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
143826
|
+
created_at INTEGER NOT NULL DEFAULT 0,
|
|
143827
|
+
updated_at INTEGER NOT NULL DEFAULT 0,
|
|
143828
|
+
UNIQUE(session_id, request_key)
|
|
143829
|
+
);
|
|
143830
|
+
CREATE INDEX IF NOT EXISTS idx_synapse_batch_ledger_session
|
|
143831
|
+
ON synapse_batch_ledger(session_id, updated_at);
|
|
143832
|
+
|
|
143833
|
+
CREATE TABLE IF NOT EXISTS shadow_embedding_registrations (
|
|
143834
|
+
project_path TEXT NOT NULL,
|
|
143835
|
+
scope TEXT NOT NULL CHECK(scope IN ('memory', 'commit', 'chunk')),
|
|
143836
|
+
model_id TEXT NOT NULL,
|
|
143837
|
+
generation INTEGER NOT NULL DEFAULT 0,
|
|
143838
|
+
fingerprint TEXT NOT NULL DEFAULT '',
|
|
143839
|
+
table_epoch INTEGER NOT NULL DEFAULT 0,
|
|
143840
|
+
dims INTEGER NOT NULL DEFAULT 0,
|
|
143841
|
+
provenance_json TEXT NOT NULL DEFAULT '{}',
|
|
143842
|
+
updated_at INTEGER NOT NULL DEFAULT 0,
|
|
143843
|
+
PRIMARY KEY(project_path, scope, model_id)
|
|
143844
|
+
);
|
|
143845
|
+
|
|
143846
|
+
CREATE TABLE IF NOT EXISTS embedding_measurement_corpus (
|
|
143847
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
143848
|
+
session_id TEXT NOT NULL,
|
|
143849
|
+
project_path TEXT NOT NULL DEFAULT '',
|
|
143850
|
+
dedup_key TEXT NOT NULL DEFAULT '',
|
|
143851
|
+
cohort_key TEXT NOT NULL DEFAULT '',
|
|
143852
|
+
query_text_hash TEXT NOT NULL DEFAULT '',
|
|
143853
|
+
primary_result_ids_json TEXT NOT NULL DEFAULT '[]',
|
|
143854
|
+
shadow_result_ids_json TEXT NOT NULL DEFAULT '[]',
|
|
143855
|
+
primary_latency_ms INTEGER,
|
|
143856
|
+
shadow_latency_ms INTEGER,
|
|
143857
|
+
primary_failed INTEGER NOT NULL DEFAULT 0,
|
|
143858
|
+
shadow_failed INTEGER NOT NULL DEFAULT 0,
|
|
143859
|
+
primary_model_id TEXT NOT NULL DEFAULT '',
|
|
143860
|
+
shadow_model_id TEXT NOT NULL DEFAULT '',
|
|
143861
|
+
primary_fingerprint TEXT NOT NULL DEFAULT '',
|
|
143862
|
+
shadow_fingerprint TEXT NOT NULL DEFAULT '',
|
|
143863
|
+
primary_epoch INTEGER NOT NULL DEFAULT 0,
|
|
143864
|
+
shadow_epoch INTEGER NOT NULL DEFAULT 0,
|
|
143865
|
+
corpus_hash TEXT NOT NULL DEFAULT '',
|
|
143866
|
+
coverage_json TEXT NOT NULL DEFAULT '{}',
|
|
143867
|
+
created_at INTEGER NOT NULL DEFAULT 0,
|
|
143868
|
+
UNIQUE(dedup_key, cohort_key)
|
|
143869
|
+
);
|
|
143870
|
+
CREATE INDEX IF NOT EXISTS idx_embedding_measurement_session
|
|
143871
|
+
ON embedding_measurement_corpus(session_id, created_at);
|
|
143872
|
+
|
|
143636
143873
|
CREATE TABLE IF NOT EXISTS memory_verifications (
|
|
143637
143874
|
memory_id INTEGER NOT NULL REFERENCES memories(id) ON DELETE CASCADE,
|
|
143638
143875
|
file_path TEXT NOT NULL,
|
|
@@ -144373,32 +144610,60 @@ function openDatabase(dbPathOrOptions) {
|
|
|
144373
144610
|
}
|
|
144374
144611
|
initializeDatabase(db);
|
|
144375
144612
|
runMigrations(db);
|
|
144376
|
-
|
|
144377
|
-
closeQuietly(db);
|
|
144378
|
-
return null;
|
|
144379
|
-
}
|
|
144380
|
-
healWedgedChannel2Claims(db);
|
|
144381
|
-
if (!explicitDbPath) {
|
|
144382
|
-
try {
|
|
144383
|
-
runToolOwnerBackfill(db);
|
|
144384
|
-
} catch (error) {
|
|
144385
|
-
log(`[magic-context] tool-owner backfill failed (continuing with lazy adoption fallback): ${getErrorMessage(error)}`);
|
|
144386
|
-
}
|
|
144387
|
-
}
|
|
144388
|
-
setDatabase(db);
|
|
144389
|
-
loadToolDefinitionMeasurements(db);
|
|
144390
|
-
restrictDatabaseFilePermissions(dbPath);
|
|
144391
|
-
databases.set(dbPath, db);
|
|
144392
|
-
pathByDatabase.set(db, dbPath);
|
|
144393
|
-
persistenceByDatabase.set(db, true);
|
|
144394
|
-
persistenceErrorByDatabase.delete(db);
|
|
144395
|
-
return db;
|
|
144613
|
+
return finishDatabaseOpen(db, dbPath, explicitDbPath, latestSupportedVersion);
|
|
144396
144614
|
} catch (error) {
|
|
144397
144615
|
const detail = getErrorMessage(error);
|
|
144398
144616
|
log(`[magic-context] storage fatal: failed to open ${dbPath}: ${detail}`);
|
|
144399
144617
|
throw new Error(`[magic-context] storage unavailable: ${detail}. Magic Context is disabled for this run; check log for details.`);
|
|
144400
144618
|
}
|
|
144401
144619
|
}
|
|
144620
|
+
async function openDatabaseAsync(dbPathOrOptions) {
|
|
144621
|
+
const options = typeof dbPathOrOptions === "string" ? { dbPath: dbPathOrOptions } : dbPathOrOptions;
|
|
144622
|
+
const explicitDbPath = options?.dbPath !== undefined;
|
|
144623
|
+
const { dbDir, dbPath } = resolveDatabasePath(options?.dbPath);
|
|
144624
|
+
const latestSupportedVersion = getRuntimeLatestSupportedVersion(options);
|
|
144625
|
+
const existing = databases.get(dbPath);
|
|
144626
|
+
if (existing) {
|
|
144627
|
+
if (!enforceSchemaFence(existing, dbPath, latestSupportedVersion))
|
|
144628
|
+
return null;
|
|
144629
|
+
if (!persistenceByDatabase.has(existing))
|
|
144630
|
+
persistenceByDatabase.set(existing, true);
|
|
144631
|
+
healWedgedChannel2Claims(existing);
|
|
144632
|
+
return existing;
|
|
144633
|
+
}
|
|
144634
|
+
const pending = pendingAsyncOpens.get(dbPath);
|
|
144635
|
+
if (pending)
|
|
144636
|
+
return pending;
|
|
144637
|
+
const opening = (async () => {
|
|
144638
|
+
let db;
|
|
144639
|
+
try {
|
|
144640
|
+
if (!explicitDbPath)
|
|
144641
|
+
migrateLegacyStorageIfNeeded(dbPath, dbDir);
|
|
144642
|
+
ensureSecureStorageDir(dbDir);
|
|
144643
|
+
db = new Database(dbPath);
|
|
144644
|
+
if (!enforceSchemaFence(db, dbPath, latestSupportedVersion)) {
|
|
144645
|
+
closeQuietly(db);
|
|
144646
|
+
return null;
|
|
144647
|
+
}
|
|
144648
|
+
initializeDatabase(db);
|
|
144649
|
+
await runMigrationsWithRetry(db);
|
|
144650
|
+
return finishDatabaseOpen(db, dbPath, explicitDbPath, latestSupportedVersion);
|
|
144651
|
+
} catch (error) {
|
|
144652
|
+
if (db)
|
|
144653
|
+
closeQuietly(db);
|
|
144654
|
+
const detail = getErrorMessage(error);
|
|
144655
|
+
log(`[magic-context] storage fatal: failed to open ${dbPath}: ${detail}`);
|
|
144656
|
+
throw new Error(`[magic-context] storage unavailable: ${detail}. Magic Context is disabled for this run; check log for details.`);
|
|
144657
|
+
}
|
|
144658
|
+
})();
|
|
144659
|
+
pendingAsyncOpens.set(dbPath, opening);
|
|
144660
|
+
try {
|
|
144661
|
+
return await opening;
|
|
144662
|
+
} finally {
|
|
144663
|
+
if (pendingAsyncOpens.get(dbPath) === opening)
|
|
144664
|
+
pendingAsyncOpens.delete(dbPath);
|
|
144665
|
+
}
|
|
144666
|
+
}
|
|
144402
144667
|
|
|
144403
144668
|
// src/subagent-entry.ts
|
|
144404
144669
|
init_logger();
|
|
@@ -145159,6 +145424,9 @@ function migrateLegacyExperimental(rawConfig, warnings) {
|
|
|
145159
145424
|
return patched;
|
|
145160
145425
|
}
|
|
145161
145426
|
|
|
145427
|
+
// ../plugin/src/config/schema/magic-context.ts
|
|
145428
|
+
import { homedir as homedir3 } from "node:os";
|
|
145429
|
+
|
|
145162
145430
|
// ../../node_modules/.bun/zod@4.4.3/node_modules/zod/v4/classic/external.js
|
|
145163
145431
|
var exports_external = {};
|
|
145164
145432
|
__export(exports_external, {
|
|
@@ -159825,8 +160093,18 @@ var HistorianConfigSchema = AgentOverrideConfigSchema.extend({
|
|
|
159825
160093
|
thinking_level: PiThinkingLevelSchema.describe("Pi only: explicit thinking level passed as --thinking <level> to Pi historian subagent invocations. Required when using reasoning models (e.g. github-copilot/gpt-5.4) because Pi's default thinking-level resolution can pick a value the provider rejects. OpenCode users set variant instead. Valid: off | minimal | low | medium | high | xhigh"),
|
|
159826
160094
|
disallowed_tools: exports_external.array(exports_external.enum(["*", "read", "aft_outline", "aft_zoom", "aft_search"])).default([]).describe(`OpenCode only. Tools to REMOVE from the historian's default allow-list [read, aft_outline, aft_zoom, aft_search]. Applies to both historian and historian-editor agents. Use ["*"] to strip all tool definitions from the model request — this prevents weak instruction-following models (e.g. mistral-small-latest) from entering tool-calling loops. Individual tool names remove just that tool. Note: a user-supplied historian.permission override can re-allow a tool that disallowed_tools removed — disallowed_tools sets the baseline, permission overrides take precedence. (default: [])`)
|
|
159827
160095
|
}).optional();
|
|
160096
|
+
var EmbeddingFallbackProviderSchema = exports_external.enum(["local", "openai-compatible", "off"]);
|
|
160097
|
+
function expandConfigPath(value) {
|
|
160098
|
+
const trimmed = value.trim();
|
|
160099
|
+
if (trimmed === "~")
|
|
160100
|
+
return homedir3();
|
|
160101
|
+
if (trimmed.startsWith("~/"))
|
|
160102
|
+
return `${homedir3()}/${trimmed.slice(2)}`;
|
|
160103
|
+
return trimmed;
|
|
160104
|
+
}
|
|
159828
160105
|
var BaseEmbeddingConfigSchema = exports_external.object({
|
|
159829
|
-
provider: exports_external.enum(["local", "openai-compatible", "off"]).default("local").describe("Embedding provider. 'local' uses Xenova/all-MiniLM-L6-v2, 'openai-compatible' requires endpoint and model, 'off' disables embeddings."),
|
|
160106
|
+
provider: exports_external.enum(["local", "openai-compatible", "off", "synapse"]).default("local").describe("Embedding provider. 'local' uses Xenova/all-MiniLM-L6-v2, 'openai-compatible' requires endpoint and model, 'synapse' uses the certified local Synapse lane with an explicit fallback provider, and 'off' disables embeddings."),
|
|
160107
|
+
fallback_provider: EmbeddingFallbackProviderSchema.optional().describe("Fallback provider for the Synapse lane. Required when provider is 'synapse'; local, openai-compatible, and off are valid."),
|
|
159830
160108
|
model: exports_external.string().optional().describe("Embedding model name. Required for openai-compatible, ignored for local."),
|
|
159831
160109
|
endpoint: exports_external.string().optional().describe("API endpoint URL. Required when provider is openai-compatible."),
|
|
159832
160110
|
api_key: exports_external.string().optional().describe("API key for remote embedding provider (optional)"),
|
|
@@ -159835,14 +160113,15 @@ var BaseEmbeddingConfigSchema = exports_external.object({
|
|
|
159835
160113
|
truncate: exports_external.string().optional().describe("Optional truncate mode sent in the embedding request body (e.g. NVIDIA NIM accepts 'NONE' | 'START' | 'END'). Omitted from the request when unset."),
|
|
159836
160114
|
max_input_tokens: exports_external.number().int().positive().optional().describe("Optional maximum input tokens for chunk embeddings. Defaults conservatively to 512 when omitted.")
|
|
159837
160115
|
}).superRefine((data, ctx) => {
|
|
159838
|
-
|
|
160116
|
+
const validationProvider = data.provider === "synapse" ? data.fallback_provider : data.provider;
|
|
160117
|
+
if (validationProvider === "openai-compatible" && !data.endpoint?.trim()) {
|
|
159839
160118
|
ctx.addIssue({
|
|
159840
160119
|
code: "custom",
|
|
159841
160120
|
path: ["endpoint"],
|
|
159842
160121
|
message: "endpoint is required when embedding.provider is openai-compatible"
|
|
159843
160122
|
});
|
|
159844
160123
|
}
|
|
159845
|
-
if (
|
|
160124
|
+
if (validationProvider === "openai-compatible" && !data.model?.trim()) {
|
|
159846
160125
|
ctx.addIssue({
|
|
159847
160126
|
code: "custom",
|
|
159848
160127
|
path: ["model"],
|
|
@@ -159851,6 +160130,25 @@ var BaseEmbeddingConfigSchema = exports_external.object({
|
|
|
159851
160130
|
}
|
|
159852
160131
|
});
|
|
159853
160132
|
var EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data) => {
|
|
160133
|
+
if (data.provider === "synapse") {
|
|
160134
|
+
const model = data.model?.trim();
|
|
160135
|
+
const endpoint = data.endpoint?.trim();
|
|
160136
|
+
const apiKey = data.api_key?.trim();
|
|
160137
|
+
const inputType = data.input_type?.trim();
|
|
160138
|
+
const queryInputType = data.query_input_type?.trim();
|
|
160139
|
+
const truncate = data.truncate?.trim();
|
|
160140
|
+
return {
|
|
160141
|
+
provider: "synapse",
|
|
160142
|
+
...data.fallback_provider ? { fallback_provider: data.fallback_provider } : {},
|
|
160143
|
+
...model ? { model } : {},
|
|
160144
|
+
...endpoint ? { endpoint } : {},
|
|
160145
|
+
...apiKey ? { api_key: apiKey } : {},
|
|
160146
|
+
...inputType ? { input_type: inputType } : {},
|
|
160147
|
+
...queryInputType ? { query_input_type: queryInputType } : {},
|
|
160148
|
+
...truncate ? { truncate } : {},
|
|
160149
|
+
...data.max_input_tokens ? { max_input_tokens: data.max_input_tokens } : {}
|
|
160150
|
+
};
|
|
160151
|
+
}
|
|
159854
160152
|
if (data.provider === "local") {
|
|
159855
160153
|
return {
|
|
159856
160154
|
provider: "local",
|
|
@@ -159878,6 +160176,7 @@ var EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data) => {
|
|
|
159878
160176
|
});
|
|
159879
160177
|
var MagicContextConfigSchema = exports_external.object({
|
|
159880
160178
|
enabled: exports_external.boolean().default(true).describe("Enable magic context (default: true)"),
|
|
160179
|
+
transform_mode: exports_external.enum(["ts", "rust"]).default("ts").describe('Experimental: routes the entire Magic Context runtime for the project through the ck-mc Rust module over subc (requires user-level `subc` config); "ts" is the current TypeScript pipeline.'),
|
|
159881
160180
|
auto_update: exports_external.boolean().optional().describe("Enable automatic npm self-update checks for the OpenCode plugin. Security: USER-only in config loader, so hostile project configs cannot suppress updates."),
|
|
159882
160181
|
language: exports_external.string().trim().toLowerCase().refine((s) => isValidLanguageCode(s), 'language must be a 2-letter ISO 639-1 code (e.g. "tr", "es", "de")').optional().describe("Output language for Magic Context's generated content and guidance, as a " + '2-letter ISO 639-1 code (e.g. "tr", "es", "de", "ja", "pt"). When set, the ' + "historian, dreamer, sidekick, and the agent-guidance block instruct the model to " + "write its PROSE in this language while keeping all structural tokens (XML tags, " + "the five memory category names, code identifiers, file paths) in English. " + "USER-LEVEL ONLY (ignored in project config for security). Unset = today's " + "behavior (model mirrors the conversation; English scaffolding). Changing it " + "triggers one cache re-materialization; existing compartments/memories keep their " + "original language until naturally rewritten."),
|
|
159883
160182
|
historian: HistorianConfigSchema.describe("Historian agent configuration (model, fallback_models, variant, temperature, maxTokens, permission, two_pass, etc.)"),
|
|
@@ -159914,6 +160213,12 @@ var MagicContextConfigSchema = exports_external.object({
|
|
|
159914
160213
|
provider: "local",
|
|
159915
160214
|
model: DEFAULT_LOCAL_EMBEDDING_MODEL
|
|
159916
160215
|
}).describe("Embedding provider configuration"),
|
|
160216
|
+
subc: exports_external.object({
|
|
160217
|
+
connection_file: exports_external.string().trim().min(1).transform(expandConfigPath).describe("Path to the owner-only subc connection file.")
|
|
160218
|
+
}).optional().describe("User-only Synapse daemon connection settings."),
|
|
160219
|
+
shadow_embedding: exports_external.object({
|
|
160220
|
+
enabled: exports_external.boolean().default(false).describe("Developer-only Synapse shadow embedding lane switch.")
|
|
160221
|
+
}).default({ enabled: false }).describe("Developer-only Synapse shadow embedding lane."),
|
|
159917
160222
|
temporal_awareness: exports_external.boolean().default(true).describe('Inject wall-clock gap markers (<!-- +Xm -->) between user messages where > 5 min elapsed since the previous message, and add compact date ranges to compartment headings. Gives the agent a sense of session pacing and "how long ago" across multi-day sessions. Graduated from experimental.temporal_awareness; default: true (set false to opt out).'),
|
|
159918
160223
|
keep_subagents: exports_external.boolean().default(false).describe("Debug: keep the child sessions Magic Context spawns for its own subagents (historian, dreamer, sidekick, memory-migration) instead of deleting them on success. Useful for short-term inspection/data collection — their full transcript (prompt, tool calls, token usage, output) stays in the host session store. Kept sessions accumulate until manually cleared; leave false for normal use. Requires a restart to take effect."),
|
|
159919
160224
|
shadow_transform: exports_external.object({
|
|
@@ -159963,7 +160268,7 @@ var MagicContextConfigSchema = exports_external.object({
|
|
|
159963
160268
|
var HIDDEN_AGENT_KEYS = ["historian", "dreamer", "sidekick"];
|
|
159964
160269
|
var HISTORIAN_USER_ONLY_FIELDS = ["model", "fallback_models"];
|
|
159965
160270
|
var AGENT_ESCALATION_FIELDS = ["prompt", "permission", "tools", "system_prompt"];
|
|
159966
|
-
var EMBEDDING_DESTINATION_FIELDS = ["endpoint", "provider"];
|
|
160271
|
+
var EMBEDDING_DESTINATION_FIELDS = ["endpoint", "provider", "fallback_provider"];
|
|
159967
160272
|
var PERCENTAGE_THRESHOLD_REASON = "security: a repository may only raise compaction thresholds above the user's effective value; it cannot force earlier historian work or cloned-repo cost escalation.";
|
|
159968
160273
|
var TOKEN_THRESHOLD_REASON = "security: a repository may only raise execute_threshold_tokens above the user's trusted token threshold; it cannot force earlier historian work or cloned-repo cost escalation.";
|
|
159969
160274
|
var TOKEN_THRESHOLD_INTRODUCTION_REASON = "security: a repository cannot introduce a new execute_threshold_tokens override when the user has no trusted token threshold for that key; that could force earlier historian work or cloned-repo cost escalation.";
|
|
@@ -160079,6 +160384,12 @@ function stripUnsafeProjectConfigFields(projectRaw) {
|
|
|
160079
160384
|
delete projectRaw.shadow_transform;
|
|
160080
160385
|
warnings.push("Ignoring shadow_transform from project config (security: this developer-only mirror lane is user-level only).");
|
|
160081
160386
|
}
|
|
160387
|
+
for (const field of ["subc", "shadow_embedding"]) {
|
|
160388
|
+
if (field in projectRaw) {
|
|
160389
|
+
delete projectRaw[field];
|
|
160390
|
+
warnings.push(`Ignoring ${field} from project config (security: daemon routing and developer-only embedding traffic are user-level settings).`);
|
|
160391
|
+
}
|
|
160392
|
+
}
|
|
160082
160393
|
const embedding = projectRaw.embedding;
|
|
160083
160394
|
if (isPlainObject3(embedding)) {
|
|
160084
160395
|
const removed = [];
|
|
@@ -160254,7 +160565,7 @@ function dropInheritedEmbeddingKeyOnRedirect(projectRaw, mergedRaw, userRaw) {
|
|
|
160254
160565
|
|
|
160255
160566
|
// ../plugin/src/config/variable.ts
|
|
160256
160567
|
import { existsSync as existsSync6, readFileSync as readFileSync3 } from "node:fs";
|
|
160257
|
-
import { homedir as
|
|
160568
|
+
import { homedir as homedir4 } from "node:os";
|
|
160258
160569
|
import { dirname as dirname4, isAbsolute as isAbsolute2, resolve } from "node:path";
|
|
160259
160570
|
|
|
160260
160571
|
// ../plugin/src/shared/jsonc-parser.ts
|
|
@@ -160317,7 +160628,7 @@ function stripJsonComments(content) {
|
|
|
160317
160628
|
var ENV_PATTERN = /\{env:([^}]+)\}/g;
|
|
160318
160629
|
var FILE_PATTERN = /\{file:([^}]+)\}/g;
|
|
160319
160630
|
function sensitiveFilePathReason(resolvedPath) {
|
|
160320
|
-
const home =
|
|
160631
|
+
const home = homedir4();
|
|
160321
160632
|
const sensitiveDirs = [
|
|
160322
160633
|
{ dir: resolve(home, ".ssh"), label: "SSH keys" },
|
|
160323
160634
|
{ dir: resolve(home, ".aws"), label: "AWS credentials" },
|
|
@@ -160380,7 +160691,7 @@ function substituteConfigVariables(input) {
|
|
|
160380
160691
|
}
|
|
160381
160692
|
let filePath = rawPath.trim();
|
|
160382
160693
|
if (filePath.startsWith("~/")) {
|
|
160383
|
-
filePath = resolve(
|
|
160694
|
+
filePath = resolve(homedir4(), filePath.slice(2));
|
|
160384
160695
|
} else if (!isAbsolute2(filePath)) {
|
|
160385
160696
|
filePath = resolve(configDir, filePath);
|
|
160386
160697
|
}
|
|
@@ -161362,14 +161673,2634 @@ function cosineSimilarity(a, b) {
|
|
|
161362
161673
|
return denominator === 0 ? 0 : dotProduct / denominator;
|
|
161363
161674
|
}
|
|
161364
161675
|
|
|
161365
|
-
// ../plugin/src/features/magic-context/memory/
|
|
161676
|
+
// ../plugin/src/features/magic-context/memory/embedding-synapse.ts
|
|
161366
161677
|
import { createHash as createHash4 } from "node:crypto";
|
|
161678
|
+
|
|
161679
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.4.1/node_modules/@cortexkit/subc-client/dist/client.js
|
|
161680
|
+
import { promises as fs3 } from "node:fs";
|
|
161681
|
+
import { debuglog } from "node:util";
|
|
161682
|
+
|
|
161683
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.4.1/node_modules/@cortexkit/subc-client/dist/auth.js
|
|
161684
|
+
import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
|
161685
|
+
var NONCE_LEN = 32;
|
|
161686
|
+
var MAX_AUTH_MESSAGE_LEN = 4096;
|
|
161687
|
+
var SERVER_PROOF_DOMAIN = "subc-server-v1";
|
|
161688
|
+
var CLIENT_AUTH_DOMAIN = "subc-client-v1";
|
|
161689
|
+
var DEFAULT_CLIENT_ROLE = "client";
|
|
161690
|
+
|
|
161691
|
+
class AuthError extends Error {
|
|
161692
|
+
}
|
|
161693
|
+
function computeProof(key, domain2, clientNonce, serverNonce, daemonId) {
|
|
161694
|
+
const mac3 = createHmac("sha256", Buffer.from(key));
|
|
161695
|
+
mac3.update(Buffer.from(domain2, "utf8"));
|
|
161696
|
+
mac3.update(Buffer.from(clientNonce));
|
|
161697
|
+
mac3.update(Buffer.from(serverNonce));
|
|
161698
|
+
mac3.update(Buffer.from(daemonId));
|
|
161699
|
+
return new Uint8Array(mac3.digest());
|
|
161700
|
+
}
|
|
161701
|
+
function constantTimeEq(a, b) {
|
|
161702
|
+
if (a.length !== b.length)
|
|
161703
|
+
return false;
|
|
161704
|
+
return timingSafeEqual(Buffer.from(a), Buffer.from(b));
|
|
161705
|
+
}
|
|
161706
|
+
async function writeMessage(sock, value, deadlineMs) {
|
|
161707
|
+
const json2 = Buffer.from(JSON.stringify(value), "utf8");
|
|
161708
|
+
if (json2.length > MAX_AUTH_MESSAGE_LEN) {
|
|
161709
|
+
throw new AuthError(`auth message too large: ${json2.length} > ${MAX_AUTH_MESSAGE_LEN}`);
|
|
161710
|
+
}
|
|
161711
|
+
const lenPrefix = new Uint8Array(4);
|
|
161712
|
+
new DataView(lenPrefix.buffer).setUint32(0, json2.length, true);
|
|
161713
|
+
await sock.write(lenPrefix, deadlineMs);
|
|
161714
|
+
await sock.write(json2, deadlineMs);
|
|
161715
|
+
}
|
|
161716
|
+
async function readMessage(sock, deadlineMs) {
|
|
161717
|
+
const lenBytes = await sock.readExact(4, deadlineMs);
|
|
161718
|
+
const len = new DataView(lenBytes.buffer, lenBytes.byteOffset, 4).getUint32(0, true);
|
|
161719
|
+
if (len > MAX_AUTH_MESSAGE_LEN) {
|
|
161720
|
+
throw new AuthError(`auth message too large: ${len} > ${MAX_AUTH_MESSAGE_LEN}`);
|
|
161721
|
+
}
|
|
161722
|
+
const body = len === 0 ? new Uint8Array(0) : await sock.readExact(len, deadlineMs);
|
|
161723
|
+
try {
|
|
161724
|
+
return JSON.parse(Buffer.from(body).toString("utf8"));
|
|
161725
|
+
} catch (err) {
|
|
161726
|
+
throw new AuthError(`auth message JSON decode failed: ${String(err)}`);
|
|
161727
|
+
}
|
|
161728
|
+
}
|
|
161729
|
+
async function authenticateClient(sock, conn, deadlineMs) {
|
|
161730
|
+
const clientNonce = new Uint8Array(randomBytes(NONCE_LEN));
|
|
161731
|
+
await writeMessage(sock, { client_nonce: Array.from(clientNonce), role: DEFAULT_CLIENT_ROLE }, deadlineMs);
|
|
161732
|
+
const proof = await readMessage(sock, deadlineMs);
|
|
161733
|
+
const serverNonce = Uint8Array.from(proof.server_nonce);
|
|
161734
|
+
const daemonId = Uint8Array.from(proof.daemon_id);
|
|
161735
|
+
const serverProof = Uint8Array.from(proof.server_proof);
|
|
161736
|
+
const expected = computeProof(conn.key, SERVER_PROOF_DOMAIN, clientNonce, serverNonce, daemonId);
|
|
161737
|
+
if (!constantTimeEq(expected, serverProof)) {
|
|
161738
|
+
throw new AuthError("server proof mismatch — wrong key or impostor daemon");
|
|
161739
|
+
}
|
|
161740
|
+
if (!constantTimeEq(daemonId, conn.daemonId)) {
|
|
161741
|
+
throw new AuthError("daemon id mismatch — connection file points at a different daemon");
|
|
161742
|
+
}
|
|
161743
|
+
const clientAuth = computeProof(conn.key, CLIENT_AUTH_DOMAIN, clientNonce, serverNonce, daemonId);
|
|
161744
|
+
await writeMessage(sock, { client_auth: Array.from(clientAuth) }, deadlineMs);
|
|
161745
|
+
}
|
|
161746
|
+
|
|
161747
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.4.1/node_modules/@cortexkit/subc-client/dist/connection-file.js
|
|
161748
|
+
import { promises as fs2 } from "node:fs";
|
|
161749
|
+
var SCHEMA_VERSION = 1;
|
|
161750
|
+
var MIN_KEY_LEN = 32;
|
|
161751
|
+
var DAEMON_ID_LEN = 16;
|
|
161752
|
+
|
|
161753
|
+
class ConnectionFileError extends Error {
|
|
161754
|
+
}
|
|
161755
|
+
function toBytes(value, field) {
|
|
161756
|
+
if (!Array.isArray(value) || value.some((n) => typeof n !== "number")) {
|
|
161757
|
+
throw new ConnectionFileError(`connection file field '${field}' must be a JSON array of bytes`);
|
|
161758
|
+
}
|
|
161759
|
+
return Uint8Array.from(value);
|
|
161760
|
+
}
|
|
161761
|
+
function validate(info) {
|
|
161762
|
+
if (info.schema !== SCHEMA_VERSION) {
|
|
161763
|
+
throw new ConnectionFileError(`unsupported connection file schema ${info.schema}; expected ${SCHEMA_VERSION}`);
|
|
161764
|
+
}
|
|
161765
|
+
if (info.endpoints.length === 0) {
|
|
161766
|
+
throw new ConnectionFileError("connection file must include at least one endpoint");
|
|
161767
|
+
}
|
|
161768
|
+
if (info.key.length < MIN_KEY_LEN) {
|
|
161769
|
+
throw new ConnectionFileError(`connection file key is too short: ${info.key.length} bytes, need at least ${MIN_KEY_LEN}`);
|
|
161770
|
+
}
|
|
161771
|
+
if (info.daemonId.length !== DAEMON_ID_LEN) {
|
|
161772
|
+
throw new ConnectionFileError(`connection file daemon_id must be ${DAEMON_ID_LEN} bytes, got ${info.daemonId.length}`);
|
|
161773
|
+
}
|
|
161774
|
+
}
|
|
161775
|
+
async function verifyOwnerOnly(path4) {
|
|
161776
|
+
if (process.platform === "win32")
|
|
161777
|
+
return;
|
|
161778
|
+
const stat = await fs2.stat(path4);
|
|
161779
|
+
const mode = stat.mode & 511;
|
|
161780
|
+
if ((mode & 63) !== 0) {
|
|
161781
|
+
throw new ConnectionFileError(`connection file ${path4} has insecure permissions 0o${mode.toString(8)}; expected owner-only 0600`);
|
|
161782
|
+
}
|
|
161783
|
+
}
|
|
161784
|
+
async function readConnectionFile(path4) {
|
|
161785
|
+
await verifyOwnerOnly(path4);
|
|
161786
|
+
const raw = await fs2.readFile(path4, "utf8");
|
|
161787
|
+
let parsed;
|
|
161788
|
+
try {
|
|
161789
|
+
parsed = JSON.parse(raw);
|
|
161790
|
+
} catch (err) {
|
|
161791
|
+
throw new ConnectionFileError(`connection file JSON read failed for ${path4}: ${String(err)}`);
|
|
161792
|
+
}
|
|
161793
|
+
const endpointsRaw = parsed.endpoints;
|
|
161794
|
+
if (!Array.isArray(endpointsRaw)) {
|
|
161795
|
+
throw new ConnectionFileError("connection file 'endpoints' must be an array");
|
|
161796
|
+
}
|
|
161797
|
+
const endpoints = endpointsRaw.map((e) => {
|
|
161798
|
+
const ep = e;
|
|
161799
|
+
if (typeof ep.host !== "string" || typeof ep.port !== "number") {
|
|
161800
|
+
throw new ConnectionFileError("connection file endpoint must be { host: string, port: number }");
|
|
161801
|
+
}
|
|
161802
|
+
return { host: ep.host, port: ep.port };
|
|
161803
|
+
});
|
|
161804
|
+
const info = {
|
|
161805
|
+
schema: parsed.schema,
|
|
161806
|
+
endpoints,
|
|
161807
|
+
key: toBytes(parsed.key, "key"),
|
|
161808
|
+
daemonId: toBytes(parsed.daemon_id, "daemon_id"),
|
|
161809
|
+
pid: parsed.pid,
|
|
161810
|
+
daemonVer: parsed.daemon_ver ?? ""
|
|
161811
|
+
};
|
|
161812
|
+
validate(info);
|
|
161813
|
+
return info;
|
|
161814
|
+
}
|
|
161815
|
+
|
|
161816
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.4.1/node_modules/@cortexkit/subc-client/dist/envelope.js
|
|
161817
|
+
var PROTOCOL_VERSION = 2;
|
|
161818
|
+
var HEADER_LEN = 21;
|
|
161819
|
+
var FROZEN_PREFIX_LEN = 5;
|
|
161820
|
+
var MAX_FRAME_BODY_LEN = 64 * 1024 * 1024;
|
|
161821
|
+
var FrameType;
|
|
161822
|
+
(function(FrameType2) {
|
|
161823
|
+
FrameType2[FrameType2["Request"] = 0] = "Request";
|
|
161824
|
+
FrameType2[FrameType2["Response"] = 1] = "Response";
|
|
161825
|
+
FrameType2[FrameType2["Push"] = 2] = "Push";
|
|
161826
|
+
FrameType2[FrameType2["StreamData"] = 3] = "StreamData";
|
|
161827
|
+
FrameType2[FrameType2["StreamEnd"] = 4] = "StreamEnd";
|
|
161828
|
+
FrameType2[FrameType2["Error"] = 5] = "Error";
|
|
161829
|
+
FrameType2[FrameType2["Cancel"] = 6] = "Cancel";
|
|
161830
|
+
FrameType2[FrameType2["Ping"] = 7] = "Ping";
|
|
161831
|
+
FrameType2[FrameType2["Pong"] = 8] = "Pong";
|
|
161832
|
+
FrameType2[FrameType2["Hello"] = 9] = "Hello";
|
|
161833
|
+
FrameType2[FrameType2["HelloAck"] = 10] = "HelloAck";
|
|
161834
|
+
FrameType2[FrameType2["Goodbye"] = 11] = "Goodbye";
|
|
161835
|
+
})(FrameType || (FrameType = {}));
|
|
161836
|
+
var FRAME_TYPE_MAX = FrameType.Goodbye;
|
|
161837
|
+
function isPureHeader(ty) {
|
|
161838
|
+
return ty === FrameType.Cancel || ty === FrameType.Ping || ty === FrameType.Pong || ty === FrameType.Goodbye;
|
|
161839
|
+
}
|
|
161840
|
+
var Priority;
|
|
161841
|
+
(function(Priority2) {
|
|
161842
|
+
Priority2[Priority2["Passive"] = 0] = "Passive";
|
|
161843
|
+
Priority2[Priority2["Interactive"] = 1] = "Interactive";
|
|
161844
|
+
Priority2[Priority2["Background"] = 2] = "Background";
|
|
161845
|
+
})(Priority || (Priority = {}));
|
|
161846
|
+
var AdmissionClass;
|
|
161847
|
+
(function(AdmissionClass2) {
|
|
161848
|
+
AdmissionClass2[AdmissionClass2["Normal"] = 0] = "Normal";
|
|
161849
|
+
AdmissionClass2[AdmissionClass2["Expedite"] = 1] = "Expedite";
|
|
161850
|
+
AdmissionClass2[AdmissionClass2["Sheddable"] = 2] = "Sheddable";
|
|
161851
|
+
})(AdmissionClass || (AdmissionClass = {}));
|
|
161852
|
+
var FLAG_BINARY = 1;
|
|
161853
|
+
var FLAG_PRIORITY_MASK = 6;
|
|
161854
|
+
var FLAG_PRIORITY_SHIFT = 1;
|
|
161855
|
+
var FLAG_LAST = 8;
|
|
161856
|
+
var FLAG_ADMISSION_MASK = 48;
|
|
161857
|
+
var FLAG_ADMISSION_SHIFT = 4;
|
|
161858
|
+
var FLAG_RESERVED_MASK = 192;
|
|
161859
|
+
function buildFlags(binary, priority, last, admissionClass = AdmissionClass.Normal) {
|
|
161860
|
+
let flags = 0;
|
|
161861
|
+
if (binary)
|
|
161862
|
+
flags |= FLAG_BINARY;
|
|
161863
|
+
flags |= priority << FLAG_PRIORITY_SHIFT;
|
|
161864
|
+
if (last)
|
|
161865
|
+
flags |= FLAG_LAST;
|
|
161866
|
+
flags |= admissionClass << FLAG_ADMISSION_SHIFT;
|
|
161867
|
+
return flags;
|
|
161868
|
+
}
|
|
161869
|
+
function encodeHeader(header) {
|
|
161870
|
+
const buffer2 = new Uint8Array(HEADER_LEN);
|
|
161871
|
+
const view = new DataView(buffer2.buffer);
|
|
161872
|
+
view.setUint32(0, header.len, true);
|
|
161873
|
+
buffer2[4] = header.ver;
|
|
161874
|
+
buffer2[5] = header.ty;
|
|
161875
|
+
buffer2[6] = header.flags;
|
|
161876
|
+
view.setUint16(7, header.channel, true);
|
|
161877
|
+
view.setUint32(9, header.epoch, true);
|
|
161878
|
+
view.setBigUint64(13, header.corr, true);
|
|
161879
|
+
return buffer2;
|
|
161880
|
+
}
|
|
161881
|
+
|
|
161882
|
+
class DecodeError extends Error {
|
|
161883
|
+
code;
|
|
161884
|
+
constructor(message, code) {
|
|
161885
|
+
super(message);
|
|
161886
|
+
this.code = code;
|
|
161887
|
+
this.name = "DecodeError";
|
|
161888
|
+
}
|
|
161889
|
+
}
|
|
161890
|
+
function decodeHeader(bytes) {
|
|
161891
|
+
if (bytes.length < FROZEN_PREFIX_LEN) {
|
|
161892
|
+
throw new DecodeError(`header shorter than frozen prefix: have ${bytes.length} bytes`, "too_short_for_prefix");
|
|
161893
|
+
}
|
|
161894
|
+
const ver = bytes[4];
|
|
161895
|
+
if (ver !== PROTOCOL_VERSION)
|
|
161896
|
+
throw new DecodeError(`unsupported envelope version ${ver}`, "unsupported_version");
|
|
161897
|
+
if (bytes.length < HEADER_LEN) {
|
|
161898
|
+
throw new DecodeError(`header too short for version: have ${bytes.length} bytes, need ${HEADER_LEN}`, "too_short_for_header");
|
|
161899
|
+
}
|
|
161900
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
161901
|
+
const len = view.getUint32(0, true);
|
|
161902
|
+
const typeByte = bytes[5];
|
|
161903
|
+
if (typeByte > FRAME_TYPE_MAX)
|
|
161904
|
+
throw new DecodeError(`unknown frame type byte ${typeByte}`, "unknown_frame_type");
|
|
161905
|
+
const ty = typeByte;
|
|
161906
|
+
const flags = bytes[6];
|
|
161907
|
+
if ((flags & FLAG_RESERVED_MASK) !== 0) {
|
|
161908
|
+
throw new DecodeError(`reserved flag bits set in flags 0b${flags.toString(2).padStart(8, "0")}`, "reserved_flag_bits");
|
|
161909
|
+
}
|
|
161910
|
+
if ((flags & FLAG_PRIORITY_MASK) >> FLAG_PRIORITY_SHIFT === 3) {
|
|
161911
|
+
throw new DecodeError(`reserved priority bits set in flags 0b${flags.toString(2).padStart(8, "0")}`, "reserved_priority_bits");
|
|
161912
|
+
}
|
|
161913
|
+
const admission = (flags & FLAG_ADMISSION_MASK) >> FLAG_ADMISSION_SHIFT;
|
|
161914
|
+
if (admission === 3) {
|
|
161915
|
+
throw new DecodeError(`reserved admission class set in flags 0b${flags.toString(2).padStart(8, "0")}`, "reserved_admission_class");
|
|
161916
|
+
}
|
|
161917
|
+
if (admission === AdmissionClass.Sheddable && ty !== FrameType.Push && ty !== FrameType.StreamData) {
|
|
161918
|
+
throw new DecodeError(`SHEDDABLE admission class is illegal on ${FrameType[ty]} in flags 0b${flags.toString(2).padStart(8, "0")}`, "sheddable_illegal_frame_type");
|
|
161919
|
+
}
|
|
161920
|
+
const channel = view.getUint16(7, true);
|
|
161921
|
+
const epoch = view.getUint32(9, true);
|
|
161922
|
+
if (channel === 0 && epoch !== 0) {
|
|
161923
|
+
throw new DecodeError(`control channel carried nonzero epoch ${epoch}`, "nonzero_epoch_on_control_channel");
|
|
161924
|
+
}
|
|
161925
|
+
if (isPureHeader(ty) && len !== 0) {
|
|
161926
|
+
throw new DecodeError(`pure-header frame ${FrameType[ty]} declared non-zero body length ${len}`, "pure_header_frame_with_body");
|
|
161927
|
+
}
|
|
161928
|
+
return { len, ver, ty, flags, channel, epoch, corr: view.getBigUint64(13, true) };
|
|
161929
|
+
}
|
|
161930
|
+
function buildFrame(ty, flags, channel, epoch, corr, body) {
|
|
161931
|
+
return buildFrameWithVersion(PROTOCOL_VERSION, ty, flags, channel, epoch, corr, body);
|
|
161932
|
+
}
|
|
161933
|
+
function buildFrameWithVersion(ver, ty, flags, channel, epoch, corr, body) {
|
|
161934
|
+
if (body.length > MAX_FRAME_BODY_LEN) {
|
|
161935
|
+
throw new DecodeError(`frame body ${body.length} exceeds max ${MAX_FRAME_BODY_LEN}`, "frame_body_too_large");
|
|
161936
|
+
}
|
|
161937
|
+
const header = { len: body.length, ver, ty, flags, channel, epoch, corr };
|
|
161938
|
+
decodeHeader(encodeHeader(header));
|
|
161939
|
+
return { header, body };
|
|
161940
|
+
}
|
|
161941
|
+
function encodeFrame(frame) {
|
|
161942
|
+
if (frame.header.len !== frame.body.length) {
|
|
161943
|
+
throw new DecodeError(`frame header length ${frame.header.len} does not match body length ${frame.body.length}`, "frame_length_mismatch");
|
|
161944
|
+
}
|
|
161945
|
+
const header = encodeHeader(frame.header);
|
|
161946
|
+
const output = new Uint8Array(header.length + frame.body.length);
|
|
161947
|
+
output.set(header, 0);
|
|
161948
|
+
output.set(frame.body, header.length);
|
|
161949
|
+
return output;
|
|
161950
|
+
}
|
|
161951
|
+
|
|
161952
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.4.1/node_modules/@cortexkit/subc-client/dist/route-handle.js
|
|
161953
|
+
var connectionToken = new WeakMap;
|
|
161954
|
+
|
|
161955
|
+
class RouteHandle {
|
|
161956
|
+
channel;
|
|
161957
|
+
epoch;
|
|
161958
|
+
constructor(channel, epoch, token) {
|
|
161959
|
+
if (!Number.isInteger(channel) || channel <= 0 || channel > 65535) {
|
|
161960
|
+
throw new RangeError(`route channel must be an integer in 1..65535, got ${channel}`);
|
|
161961
|
+
}
|
|
161962
|
+
if (!Number.isInteger(epoch) || epoch <= 0 || epoch > 4294967295) {
|
|
161963
|
+
throw new RangeError(`route epoch must be an integer in 1..4294967295, got ${epoch}`);
|
|
161964
|
+
}
|
|
161965
|
+
this.channel = channel;
|
|
161966
|
+
this.epoch = epoch;
|
|
161967
|
+
connectionToken.set(this, token);
|
|
161968
|
+
Object.freeze(this);
|
|
161969
|
+
}
|
|
161970
|
+
static create(channel, epoch, token) {
|
|
161971
|
+
return new RouteHandle(channel, epoch, token);
|
|
161972
|
+
}
|
|
161973
|
+
}
|
|
161974
|
+
function createRouteHandle(channel, epoch, token) {
|
|
161975
|
+
const factory = RouteHandle;
|
|
161976
|
+
return factory.create(channel, epoch, token);
|
|
161977
|
+
}
|
|
161978
|
+
|
|
161979
|
+
class StaleRouteHandleError extends Error {
|
|
161980
|
+
handle;
|
|
161981
|
+
code = "stale_route_handle";
|
|
161982
|
+
constructor(handle) {
|
|
161983
|
+
super(`route handle (${handle.channel}, ${handle.epoch}) is not live on the current connection`);
|
|
161984
|
+
this.handle = handle;
|
|
161985
|
+
this.name = "StaleRouteHandleError";
|
|
161986
|
+
}
|
|
161987
|
+
}
|
|
161988
|
+
function newConnectionToken() {
|
|
161989
|
+
return Object.freeze({});
|
|
161990
|
+
}
|
|
161991
|
+
function belongsToConnection(handle, token) {
|
|
161992
|
+
return connectionToken.get(handle) === token;
|
|
161993
|
+
}
|
|
161994
|
+
function sameRouteHandle(left, right) {
|
|
161995
|
+
return left === right;
|
|
161996
|
+
}
|
|
161997
|
+
|
|
161998
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.4.1/node_modules/@cortexkit/subc-client/dist/socket.js
|
|
161999
|
+
import net from "node:net";
|
|
162000
|
+
class SocketClosedError extends Error {
|
|
162001
|
+
}
|
|
162002
|
+
|
|
162003
|
+
class SocketTimeoutError extends Error {
|
|
162004
|
+
}
|
|
162005
|
+
|
|
162006
|
+
class SocketWriteNotQueuedError extends Error {
|
|
162007
|
+
cause;
|
|
162008
|
+
constructor(message, cause) {
|
|
162009
|
+
super(message);
|
|
162010
|
+
this.cause = cause;
|
|
162011
|
+
}
|
|
162012
|
+
}
|
|
162013
|
+
|
|
162014
|
+
class SocketWriteQueuedError extends Error {
|
|
162015
|
+
cause;
|
|
162016
|
+
constructor(message, cause) {
|
|
162017
|
+
super(message);
|
|
162018
|
+
this.cause = cause;
|
|
162019
|
+
}
|
|
162020
|
+
}
|
|
162021
|
+
|
|
162022
|
+
class SubcSocket {
|
|
162023
|
+
sock;
|
|
162024
|
+
chunks = [];
|
|
162025
|
+
buffered = 0;
|
|
162026
|
+
waiter = null;
|
|
162027
|
+
closedErr = null;
|
|
162028
|
+
bufferedBytes() {
|
|
162029
|
+
return this.buffered;
|
|
162030
|
+
}
|
|
162031
|
+
constructor(sock) {
|
|
162032
|
+
this.sock = sock;
|
|
162033
|
+
sock.on("data", (chunk) => {
|
|
162034
|
+
this.chunks.push(chunk);
|
|
162035
|
+
this.buffered += chunk.length;
|
|
162036
|
+
this.tryServe();
|
|
162037
|
+
});
|
|
162038
|
+
const fail = (err) => {
|
|
162039
|
+
if (!this.closedErr)
|
|
162040
|
+
this.closedErr = err;
|
|
162041
|
+
this.tryServe();
|
|
162042
|
+
};
|
|
162043
|
+
sock.on("error", (err) => fail(err instanceof Error ? err : new Error(String(err))));
|
|
162044
|
+
sock.on("end", () => fail(new SocketClosedError("subc closed the connection")));
|
|
162045
|
+
sock.on("close", () => fail(new SocketClosedError("subc connection closed")));
|
|
162046
|
+
}
|
|
162047
|
+
localPort() {
|
|
162048
|
+
return this.sock.localPort ?? null;
|
|
162049
|
+
}
|
|
162050
|
+
static connect(host, port, deadlineMs) {
|
|
162051
|
+
return new Promise((resolve2, reject) => {
|
|
162052
|
+
const sock = net.connect({ host, port });
|
|
162053
|
+
sock.setNoDelay(true);
|
|
162054
|
+
const timer = setTimeout(() => {
|
|
162055
|
+
sock.destroy();
|
|
162056
|
+
reject(new SocketTimeoutError(`timed out connecting to ${host}:${port}`));
|
|
162057
|
+
}, Math.max(0, deadlineMs - Date.now()));
|
|
162058
|
+
sock.once("connect", () => {
|
|
162059
|
+
clearTimeout(timer);
|
|
162060
|
+
resolve2(new SubcSocket(sock));
|
|
162061
|
+
});
|
|
162062
|
+
sock.once("error", (err) => {
|
|
162063
|
+
clearTimeout(timer);
|
|
162064
|
+
reject(err);
|
|
162065
|
+
});
|
|
162066
|
+
});
|
|
162067
|
+
}
|
|
162068
|
+
async readFrame(headerDeadlineMs, bodyDeadline, onHeader) {
|
|
162069
|
+
const prefix = await this.readExact(FROZEN_PREFIX_LEN, headerDeadlineMs);
|
|
162070
|
+
const version2 = prefix[4];
|
|
162071
|
+
if (version2 !== PROTOCOL_VERSION)
|
|
162072
|
+
throw new DecodeError(`unsupported envelope version ${version2}`, "unsupported_version");
|
|
162073
|
+
const remainder = await this.readExact(HEADER_LEN - FROZEN_PREFIX_LEN, headerDeadlineMs);
|
|
162074
|
+
const headerBytes = new Uint8Array(HEADER_LEN);
|
|
162075
|
+
headerBytes.set(prefix);
|
|
162076
|
+
headerBytes.set(remainder, FROZEN_PREFIX_LEN);
|
|
162077
|
+
const header = decodeHeader(headerBytes);
|
|
162078
|
+
if (header.len > MAX_FRAME_BODY_LEN) {
|
|
162079
|
+
throw new DecodeError(`frame body ${header.len} exceeds max ${MAX_FRAME_BODY_LEN}`, "frame_body_too_large");
|
|
162080
|
+
}
|
|
162081
|
+
onHeader?.();
|
|
162082
|
+
const bodyDeadlineMs = typeof bodyDeadline === "number" ? bodyDeadline : Date.now() + bodyDeadline.afterHeaderMs;
|
|
162083
|
+
const body = header.len === 0 ? new Uint8Array(0) : await this.readExact(header.len, bodyDeadlineMs);
|
|
162084
|
+
return { header, body };
|
|
162085
|
+
}
|
|
162086
|
+
readExact(n, deadlineMs) {
|
|
162087
|
+
if (this.waiter) {
|
|
162088
|
+
return Promise.reject(new Error("concurrent readExact is not supported"));
|
|
162089
|
+
}
|
|
162090
|
+
if (n === 0)
|
|
162091
|
+
return Promise.resolve(new Uint8Array(0));
|
|
162092
|
+
return new Promise((resolve2, reject) => {
|
|
162093
|
+
let timer = null;
|
|
162094
|
+
if (Number.isFinite(deadlineMs)) {
|
|
162095
|
+
const remaining = deadlineMs - Date.now();
|
|
162096
|
+
if (remaining <= 0) {
|
|
162097
|
+
reject(new SocketTimeoutError(`timed out waiting for ${n} bytes`));
|
|
162098
|
+
return;
|
|
162099
|
+
}
|
|
162100
|
+
timer = setTimeout(() => {
|
|
162101
|
+
this.waiter = null;
|
|
162102
|
+
reject(new SocketTimeoutError(`timed out waiting for ${n} bytes`));
|
|
162103
|
+
}, remaining);
|
|
162104
|
+
}
|
|
162105
|
+
this.waiter = { need: n, resolve: resolve2, reject, timer };
|
|
162106
|
+
this.tryServe();
|
|
162107
|
+
});
|
|
162108
|
+
}
|
|
162109
|
+
async write(bytes, deadlineMs) {
|
|
162110
|
+
try {
|
|
162111
|
+
await this.writeTracked(bytes, deadlineMs).completed;
|
|
162112
|
+
} catch (err) {
|
|
162113
|
+
if (err instanceof SocketWriteNotQueuedError || err instanceof SocketWriteQueuedError) {
|
|
162114
|
+
throw err.cause ?? err;
|
|
162115
|
+
}
|
|
162116
|
+
throw err;
|
|
162117
|
+
}
|
|
162118
|
+
}
|
|
162119
|
+
writeTracked(bytes, deadlineMs) {
|
|
162120
|
+
if (this.closedErr) {
|
|
162121
|
+
return {
|
|
162122
|
+
queued: false,
|
|
162123
|
+
completed: Promise.reject(new SocketWriteNotQueuedError("subc socket was closed before bytes could be queued", this.closedErr))
|
|
162124
|
+
};
|
|
162125
|
+
}
|
|
162126
|
+
let queued = false;
|
|
162127
|
+
let settled = false;
|
|
162128
|
+
let timer = null;
|
|
162129
|
+
const completed = new Promise((resolve2, reject) => {
|
|
162130
|
+
const settle = (run) => {
|
|
162131
|
+
if (settled)
|
|
162132
|
+
return;
|
|
162133
|
+
settled = true;
|
|
162134
|
+
if (timer)
|
|
162135
|
+
clearTimeout(timer);
|
|
162136
|
+
run();
|
|
162137
|
+
};
|
|
162138
|
+
const remaining = deadlineMs - Date.now();
|
|
162139
|
+
if (remaining <= 0) {
|
|
162140
|
+
settle(() => reject(new SocketWriteNotQueuedError("timed out before bytes could be queued to subc", new SocketTimeoutError("timed out writing to subc"))));
|
|
162141
|
+
return;
|
|
162142
|
+
}
|
|
162143
|
+
timer = setTimeout(() => {
|
|
162144
|
+
const timeout = new SocketTimeoutError("timed out writing to subc");
|
|
162145
|
+
settle(() => reject(queued ? new SocketWriteQueuedError("timed out after bytes were handed to the subc socket", timeout) : new SocketWriteNotQueuedError("timed out before bytes could be queued to subc", timeout)));
|
|
162146
|
+
}, remaining);
|
|
162147
|
+
try {
|
|
162148
|
+
this.sock.write(Buffer.from(bytes), (err) => {
|
|
162149
|
+
settle(() => {
|
|
162150
|
+
if (err) {
|
|
162151
|
+
reject(new SocketWriteQueuedError("subc socket reported a write error after bytes were handed to the socket", err instanceof Error ? err : new Error(String(err))));
|
|
162152
|
+
} else {
|
|
162153
|
+
resolve2();
|
|
162154
|
+
}
|
|
162155
|
+
});
|
|
162156
|
+
});
|
|
162157
|
+
queued = true;
|
|
162158
|
+
} catch (err) {
|
|
162159
|
+
settle(() => reject(new SocketWriteNotQueuedError("subc socket write threw before bytes could be queued", err instanceof Error ? err : new Error(String(err)))));
|
|
162160
|
+
}
|
|
162161
|
+
});
|
|
162162
|
+
return { queued, completed };
|
|
162163
|
+
}
|
|
162164
|
+
close() {
|
|
162165
|
+
this.sock.destroy();
|
|
162166
|
+
}
|
|
162167
|
+
tryServe() {
|
|
162168
|
+
const w = this.waiter;
|
|
162169
|
+
if (!w)
|
|
162170
|
+
return;
|
|
162171
|
+
if (this.buffered >= w.need) {
|
|
162172
|
+
const out = this.take(w.need);
|
|
162173
|
+
this.waiter = null;
|
|
162174
|
+
if (w.timer)
|
|
162175
|
+
clearTimeout(w.timer);
|
|
162176
|
+
w.resolve(out);
|
|
162177
|
+
return;
|
|
162178
|
+
}
|
|
162179
|
+
if (this.closedErr) {
|
|
162180
|
+
this.waiter = null;
|
|
162181
|
+
if (w.timer)
|
|
162182
|
+
clearTimeout(w.timer);
|
|
162183
|
+
w.reject(this.closedErr);
|
|
162184
|
+
}
|
|
162185
|
+
}
|
|
162186
|
+
take(n) {
|
|
162187
|
+
const out = Buffer.allocUnsafe(n);
|
|
162188
|
+
let off = 0;
|
|
162189
|
+
while (off < n) {
|
|
162190
|
+
const head = this.chunks[0];
|
|
162191
|
+
const want = n - off;
|
|
162192
|
+
if (head.length <= want) {
|
|
162193
|
+
head.copy(out, off);
|
|
162194
|
+
off += head.length;
|
|
162195
|
+
this.chunks.shift();
|
|
162196
|
+
} else {
|
|
162197
|
+
head.copy(out, off, 0, want);
|
|
162198
|
+
this.chunks[0] = head.subarray(want);
|
|
162199
|
+
off += want;
|
|
162200
|
+
}
|
|
162201
|
+
}
|
|
162202
|
+
this.buffered -= n;
|
|
162203
|
+
return out;
|
|
162204
|
+
}
|
|
162205
|
+
}
|
|
162206
|
+
|
|
162207
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.4.1/node_modules/@cortexkit/subc-client/dist/client.js
|
|
162208
|
+
var debug = debuglog("subc-client");
|
|
162209
|
+
var DEFAULT_HANDSHAKE_TIMEOUT_MS = 1e4;
|
|
162210
|
+
var DEFAULT_REQUEST_TIMEOUT_MS = 30000;
|
|
162211
|
+
var TIMEOUT_ARBITRATION_GRACE_MS = 50;
|
|
162212
|
+
var REQUEST_DEADLINE_MARKER = "request_deadline";
|
|
162213
|
+
var DEADLINE_NO_DROP_CODE = "deadline_exceeded_no_drop_observed";
|
|
162214
|
+
var ROUTE_OPEN_RETRY_DEADLINE_MS = 30000;
|
|
162215
|
+
var BODY_READ_TIMEOUT_MS = 30000;
|
|
162216
|
+
var EMPTY_BODY = new Uint8Array(0);
|
|
162217
|
+
var DEFAULT_MANAGED_TARGET_KIND = "management_surface";
|
|
162218
|
+
var SUBC_MODULE_ID_ENV = "SUBC_MODULE_ID";
|
|
162219
|
+
var SUBC_LAUNCH_NONCE_ENV = "SUBC_LAUNCH_NONCE";
|
|
162220
|
+
var DEFAULT_RECONNECT_BACKOFF = {
|
|
162221
|
+
baseMs: 100,
|
|
162222
|
+
capMs: 2000,
|
|
162223
|
+
maxAttempts: 6
|
|
162224
|
+
};
|
|
162225
|
+
|
|
162226
|
+
class SubcCallError extends Error {
|
|
162227
|
+
kind;
|
|
162228
|
+
code;
|
|
162229
|
+
cause;
|
|
162230
|
+
constructor(kind, message, code, cause) {
|
|
162231
|
+
super(message);
|
|
162232
|
+
this.kind = kind;
|
|
162233
|
+
this.code = code;
|
|
162234
|
+
this.cause = cause;
|
|
162235
|
+
this.name = "SubcCallError";
|
|
162236
|
+
}
|
|
162237
|
+
}
|
|
162238
|
+
|
|
162239
|
+
class SubcError extends Error {
|
|
162240
|
+
code;
|
|
162241
|
+
constructor(message, code) {
|
|
162242
|
+
super(message);
|
|
162243
|
+
this.code = code;
|
|
162244
|
+
}
|
|
162245
|
+
}
|
|
162246
|
+
|
|
162247
|
+
class SubcClient {
|
|
162248
|
+
sock;
|
|
162249
|
+
currentConn;
|
|
162250
|
+
opts;
|
|
162251
|
+
nextCorr = 1n;
|
|
162252
|
+
pending = new Map;
|
|
162253
|
+
lateResponses = new Map;
|
|
162254
|
+
routes = new Map;
|
|
162255
|
+
liveRoutes = new Map;
|
|
162256
|
+
connectionToken = newConnectionToken();
|
|
162257
|
+
ingressEpochDropCount = 0;
|
|
162258
|
+
closedErr = null;
|
|
162259
|
+
closeStarted = false;
|
|
162260
|
+
reconnecting = null;
|
|
162261
|
+
generation = 1;
|
|
162262
|
+
readerActive = false;
|
|
162263
|
+
constructor(sock, currentConn, opts) {
|
|
162264
|
+
this.sock = sock;
|
|
162265
|
+
this.currentConn = currentConn;
|
|
162266
|
+
this.opts = opts;
|
|
162267
|
+
this.readLoop(sock, this.generation);
|
|
162268
|
+
}
|
|
162269
|
+
get conn() {
|
|
162270
|
+
return this.currentConn;
|
|
162271
|
+
}
|
|
162272
|
+
static async connect(opts) {
|
|
162273
|
+
const normalized = normalizeConnectOptions(opts);
|
|
162274
|
+
const opened = await SubcClient.openConnection(normalized);
|
|
162275
|
+
return new SubcClient(opened.sock, opened.conn, normalized);
|
|
162276
|
+
}
|
|
162277
|
+
async catalogList(moduleId) {
|
|
162278
|
+
const body = this.encode(moduleId === undefined ? { op: "catalog.list" } : { op: "catalog.list", module_id: moduleId });
|
|
162279
|
+
const reply = await this.controlRpc(body);
|
|
162280
|
+
const parsed = this.parseJson(reply);
|
|
162281
|
+
return parsed.modules ?? [];
|
|
162282
|
+
}
|
|
162283
|
+
async routeOpen(target, identity, opts = {}) {
|
|
162284
|
+
const consumerIdentity = routeOpenConsumerIdentity(opts);
|
|
162285
|
+
const consumerCapabilities = opts.consumerCapabilities;
|
|
162286
|
+
const body = this.encode({
|
|
162287
|
+
op: "route.open",
|
|
162288
|
+
target,
|
|
162289
|
+
identity,
|
|
162290
|
+
...consumerIdentity ? { consumer_identity: consumerIdentity } : {},
|
|
162291
|
+
...consumerCapabilities !== undefined ? { consumer_capabilities: consumerCapabilities } : {}
|
|
162292
|
+
});
|
|
162293
|
+
let installed = null;
|
|
162294
|
+
const install = (frame) => {
|
|
162295
|
+
if (frame.header.ty !== FrameType.Response)
|
|
162296
|
+
return true;
|
|
162297
|
+
const parsed = this.parseJson(frame);
|
|
162298
|
+
if (typeof parsed.route_channel !== "number" || typeof parsed.route_epoch !== "number") {
|
|
162299
|
+
throw new SubcError(`route.open returned no route handle: ${JSON.stringify(parsed)}`);
|
|
162300
|
+
}
|
|
162301
|
+
installed = this.installRoute(parsed.route_channel, parsed.route_epoch);
|
|
162302
|
+
return true;
|
|
162303
|
+
};
|
|
162304
|
+
const closeLateRoute = (frame) => {
|
|
162305
|
+
if (frame.header.ty !== FrameType.Response)
|
|
162306
|
+
return;
|
|
162307
|
+
try {
|
|
162308
|
+
const parsed = this.parseJson(frame);
|
|
162309
|
+
if (typeof parsed.route_channel !== "number" || typeof parsed.route_epoch !== "number")
|
|
162310
|
+
return;
|
|
162311
|
+
const lateHandle = this.installRoute(parsed.route_channel, parsed.route_epoch);
|
|
162312
|
+
this.failHandle(lateHandle, new SubcError("late route.open was closed", "route_closed"));
|
|
162313
|
+
this.liveRoutes.delete(lateHandle.channel);
|
|
162314
|
+
this.sendRouteGoodbye(lateHandle, true);
|
|
162315
|
+
} catch {
|
|
162316
|
+
this.closeConnectionAfterCleanupFailure();
|
|
162317
|
+
}
|
|
162318
|
+
};
|
|
162319
|
+
await this.controlRpc(body, install, closeLateRoute);
|
|
162320
|
+
if (!installed)
|
|
162321
|
+
throw new SubcError("route.open response was not installed");
|
|
162322
|
+
return installed;
|
|
162323
|
+
}
|
|
162324
|
+
async request(handle, body, opts = {}) {
|
|
162325
|
+
this.assertLiveHandle(handle);
|
|
162326
|
+
const bytes = body instanceof Uint8Array ? body : this.encode(body);
|
|
162327
|
+
const priority = opts.priority ?? Priority.Interactive;
|
|
162328
|
+
const admission = opts.admissionClass ?? AdmissionClass.Normal;
|
|
162329
|
+
const reply = await this.send(handle, bytes, priority, admission, opts.timeoutMs, opts.onProgress);
|
|
162330
|
+
return this.parseJson(reply);
|
|
162331
|
+
}
|
|
162332
|
+
async call(moduleId, method, params, opts = {}) {
|
|
162333
|
+
const body = params === undefined ? { method } : { method, params };
|
|
162334
|
+
let retriedUnknownChannel = false;
|
|
162335
|
+
for (;; ) {
|
|
162336
|
+
const routeHandle = await this.cachedRouteHandle(moduleId, opts);
|
|
162337
|
+
try {
|
|
162338
|
+
return await this.managedRequest(routeHandle, body, opts);
|
|
162339
|
+
} catch (err) {
|
|
162340
|
+
if (!(err instanceof SubcCallError))
|
|
162341
|
+
throw this.terminalCallError("managed call failed", err);
|
|
162342
|
+
if (err.code === "unknown_channel" && !retriedUnknownChannel && !this.closeStarted) {
|
|
162343
|
+
retriedUnknownChannel = true;
|
|
162344
|
+
this.evictRouteHandle(routeHandle);
|
|
162345
|
+
continue;
|
|
162346
|
+
}
|
|
162347
|
+
if (err.kind === "not_sent") {
|
|
162348
|
+
try {
|
|
162349
|
+
await this.reconnectAfterDrop(err);
|
|
162350
|
+
} catch (reconnectErr) {
|
|
162351
|
+
throw this.notSentRecoveryError("managed call was not sent", reconnectErr);
|
|
162352
|
+
}
|
|
162353
|
+
continue;
|
|
162354
|
+
}
|
|
162355
|
+
if (err.kind === "outcome_unknown" && err.code !== DEADLINE_NO_DROP_CODE) {
|
|
162356
|
+
this.scheduleReconnectAfterDrop(err);
|
|
162357
|
+
}
|
|
162358
|
+
throw err;
|
|
162359
|
+
}
|
|
162360
|
+
}
|
|
162361
|
+
}
|
|
162362
|
+
subscribe(handle, body, onEvent, opts = {}) {
|
|
162363
|
+
this.assertLiveHandle(handle);
|
|
162364
|
+
const bytes = body instanceof Uint8Array ? body : this.encode(body);
|
|
162365
|
+
const priority = opts.priority ?? Priority.Interactive;
|
|
162366
|
+
const admission = opts.admissionClass ?? AdmissionClass.Normal;
|
|
162367
|
+
const corr = this.allocateCorr();
|
|
162368
|
+
const key = pendingKey(handle, corr);
|
|
162369
|
+
const closed = new Promise((resolve2, reject) => {
|
|
162370
|
+
if (this.closedErr) {
|
|
162371
|
+
reject(this.closedErr);
|
|
162372
|
+
return;
|
|
162373
|
+
}
|
|
162374
|
+
this.pending.set(key, {
|
|
162375
|
+
handle,
|
|
162376
|
+
resolve: () => resolve2(),
|
|
162377
|
+
reject,
|
|
162378
|
+
onProgress: onEvent,
|
|
162379
|
+
timer: null,
|
|
162380
|
+
subscription: true
|
|
162381
|
+
});
|
|
162382
|
+
const frame = buildFrame(FrameType.Request, buildFlags(false, priority, false, admission), handle.channel, handle.epoch, corr, bytes);
|
|
162383
|
+
this.sock.write(encodeFrame(frame), Date.now() + DEFAULT_REQUEST_TIMEOUT_MS).catch((err) => {
|
|
162384
|
+
const pending = this.pending.get(key);
|
|
162385
|
+
if (pending)
|
|
162386
|
+
this.rejectPending(key, pending, err instanceof Error ? err : new SubcError(String(err)));
|
|
162387
|
+
});
|
|
162388
|
+
});
|
|
162389
|
+
let cancelled = false;
|
|
162390
|
+
const unsubscribe = () => {
|
|
162391
|
+
if (cancelled)
|
|
162392
|
+
return;
|
|
162393
|
+
cancelled = true;
|
|
162394
|
+
this.cancel(handle, corr, priority);
|
|
162395
|
+
};
|
|
162396
|
+
return { unsubscribe, closed };
|
|
162397
|
+
}
|
|
162398
|
+
cancel(handle, corr, priority = Priority.Interactive) {
|
|
162399
|
+
this.assertLiveHandle(handle);
|
|
162400
|
+
const cancel = buildFrame(FrameType.Cancel, buildFlags(false, priority, false), handle.channel, handle.epoch, corr, EMPTY_BODY);
|
|
162401
|
+
this.sock.write(encodeFrame(cancel), Date.now() + DEFAULT_REQUEST_TIMEOUT_MS).catch(() => {
|
|
162402
|
+
return;
|
|
162403
|
+
});
|
|
162404
|
+
}
|
|
162405
|
+
async routePoll(handle, kind) {
|
|
162406
|
+
this.assertLiveHandle(handle);
|
|
162407
|
+
const body = this.encode({
|
|
162408
|
+
op: "route.poll",
|
|
162409
|
+
route_channel: handle.channel,
|
|
162410
|
+
route_epoch: handle.epoch,
|
|
162411
|
+
kind
|
|
162412
|
+
});
|
|
162413
|
+
const reply = await this.controlRpc(body, (frame) => {
|
|
162414
|
+
if (frame.header.ty !== FrameType.Response)
|
|
162415
|
+
return true;
|
|
162416
|
+
const parsed = this.parseJson(frame);
|
|
162417
|
+
return parsed.route_channel === handle.channel && parsed.route_epoch === handle.epoch;
|
|
162418
|
+
});
|
|
162419
|
+
return this.parseJson(reply);
|
|
162420
|
+
}
|
|
162421
|
+
async closeRoute(handle, opts = {}) {
|
|
162422
|
+
this.assertLiveHandle(handle);
|
|
162423
|
+
for (const [key, cached2] of this.routes) {
|
|
162424
|
+
if (cached2.handle && sameRouteHandle(cached2.handle, handle)) {
|
|
162425
|
+
cached2.closed = true;
|
|
162426
|
+
cached2.handle = null;
|
|
162427
|
+
this.routes.delete(key);
|
|
162428
|
+
}
|
|
162429
|
+
}
|
|
162430
|
+
if (opts.drain)
|
|
162431
|
+
await this.drainUnaryOnHandle(handle);
|
|
162432
|
+
this.failHandle(handle, new SubcError("route closed by closeRoute", "route_closed"));
|
|
162433
|
+
if (this.liveRoutes.get(handle.channel) === handle)
|
|
162434
|
+
this.liveRoutes.delete(handle.channel);
|
|
162435
|
+
this.sendRouteGoodbye(handle);
|
|
162436
|
+
}
|
|
162437
|
+
async closeManagedRoute(target, identity, opts = {}) {
|
|
162438
|
+
const key = routeCacheKey(target, identity, routeOpenConsumerIdentity(opts));
|
|
162439
|
+
const cached2 = this.routes.get(key);
|
|
162440
|
+
if (!cached2)
|
|
162441
|
+
return;
|
|
162442
|
+
cached2.closed = true;
|
|
162443
|
+
this.routes.delete(key);
|
|
162444
|
+
const handle = cached2.handle;
|
|
162445
|
+
cached2.handle = null;
|
|
162446
|
+
if (handle)
|
|
162447
|
+
await this.closeRoute(handle, opts);
|
|
162448
|
+
}
|
|
162449
|
+
async closeRouteChannel(handle, opts = {}) {
|
|
162450
|
+
await this.closeRoute(handle, opts);
|
|
162451
|
+
}
|
|
162452
|
+
close() {
|
|
162453
|
+
this.closeStarted = true;
|
|
162454
|
+
this.fail(new SubcError("client closed"));
|
|
162455
|
+
this.sock.close();
|
|
162456
|
+
}
|
|
162457
|
+
drainUnaryOnHandle(handle) {
|
|
162458
|
+
const waiters = [];
|
|
162459
|
+
for (const pending of this.pending.values()) {
|
|
162460
|
+
if (pending.handle === handle && !pending.subscription) {
|
|
162461
|
+
waiters.push(new Promise((resolve2) => {
|
|
162462
|
+
const previous = pending.onSettle;
|
|
162463
|
+
pending.onSettle = () => {
|
|
162464
|
+
previous?.();
|
|
162465
|
+
resolve2();
|
|
162466
|
+
};
|
|
162467
|
+
}));
|
|
162468
|
+
}
|
|
162469
|
+
}
|
|
162470
|
+
return Promise.all(waiters).then(() => {
|
|
162471
|
+
return;
|
|
162472
|
+
});
|
|
162473
|
+
}
|
|
162474
|
+
sendRouteGoodbye(handle, closeOnQueueFailure = false) {
|
|
162475
|
+
this.assertLiveConnection(handle);
|
|
162476
|
+
if (this.closedErr) {
|
|
162477
|
+
if (closeOnQueueFailure)
|
|
162478
|
+
this.closeConnectionAfterCleanupFailure();
|
|
162479
|
+
return;
|
|
162480
|
+
}
|
|
162481
|
+
const goodbye = buildFrame(FrameType.Goodbye, buildFlags(false, Priority.Interactive, false), handle.channel, handle.epoch, 0n, EMPTY_BODY);
|
|
162482
|
+
const write = this.sock.writeTracked(encodeFrame(goodbye), Date.now() + DEFAULT_REQUEST_TIMEOUT_MS);
|
|
162483
|
+
if (!write.queued && closeOnQueueFailure)
|
|
162484
|
+
this.closeConnectionAfterCleanupFailure();
|
|
162485
|
+
write.completed.catch(() => {
|
|
162486
|
+
if (closeOnQueueFailure && !write.queued)
|
|
162487
|
+
this.closeConnectionAfterCleanupFailure();
|
|
162488
|
+
});
|
|
162489
|
+
}
|
|
162490
|
+
static async openConnection(opts) {
|
|
162491
|
+
const conn = await readConnectionFile(opts.connectionFile);
|
|
162492
|
+
const deadline = Date.now() + (opts.handshakeTimeoutMs ?? DEFAULT_HANDSHAKE_TIMEOUT_MS);
|
|
162493
|
+
const endpoint = conn.endpoints[0];
|
|
162494
|
+
const sock = await SubcSocket.connect(endpoint.host, endpoint.port, deadline);
|
|
162495
|
+
try {
|
|
162496
|
+
await authenticateClient(sock, conn, deadline);
|
|
162497
|
+
} catch (err) {
|
|
162498
|
+
sock.close();
|
|
162499
|
+
throw err;
|
|
162500
|
+
}
|
|
162501
|
+
return { sock, conn };
|
|
162502
|
+
}
|
|
162503
|
+
async controlRpc(body, acceptFrame, onLateResponse) {
|
|
162504
|
+
return this.send(null, body, Priority.Interactive, AdmissionClass.Normal, undefined, undefined, acceptFrame, onLateResponse);
|
|
162505
|
+
}
|
|
162506
|
+
send(handle, body, priority, admission, timeoutMs, onProgress, acceptFrame, onLateResponse) {
|
|
162507
|
+
if (handle)
|
|
162508
|
+
this.assertLiveHandle(handle);
|
|
162509
|
+
if (this.closedErr)
|
|
162510
|
+
return Promise.reject(this.closedErr);
|
|
162511
|
+
let corr;
|
|
162512
|
+
try {
|
|
162513
|
+
corr = this.allocateCorr();
|
|
162514
|
+
} catch (error51) {
|
|
162515
|
+
return Promise.reject(error51);
|
|
162516
|
+
}
|
|
162517
|
+
const key = pendingKey(handle, corr);
|
|
162518
|
+
const channel = handle?.channel ?? 0;
|
|
162519
|
+
const epoch = handle?.epoch ?? 0;
|
|
162520
|
+
const frame = buildFrame(FrameType.Request, buildFlags(false, priority, false, admission), channel, epoch, corr, body);
|
|
162521
|
+
return new Promise((resolve2, reject) => {
|
|
162522
|
+
const ms = timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
162523
|
+
const pending = {
|
|
162524
|
+
handle,
|
|
162525
|
+
resolve: resolve2,
|
|
162526
|
+
reject,
|
|
162527
|
+
onProgress,
|
|
162528
|
+
timer: null,
|
|
162529
|
+
acceptFrame,
|
|
162530
|
+
onLateResponse
|
|
162531
|
+
};
|
|
162532
|
+
pending.timer = setTimeout(() => this.arbitrateTimeout(key, pending, channel, corr, ms), ms);
|
|
162533
|
+
this.pending.set(key, pending);
|
|
162534
|
+
this.sock.write(encodeFrame(frame), Date.now() + ms).catch((error51) => {
|
|
162535
|
+
const current = this.pending.get(key);
|
|
162536
|
+
if (current)
|
|
162537
|
+
this.rejectPending(key, current, error51 instanceof Error ? error51 : new SubcError(String(error51)));
|
|
162538
|
+
});
|
|
162539
|
+
});
|
|
162540
|
+
}
|
|
162541
|
+
arbitrateTimeout(key, pending, channel, corr, ms) {
|
|
162542
|
+
const settleAsTimeout = () => {
|
|
162543
|
+
if (pending.onLateResponse)
|
|
162544
|
+
this.lateResponses.set(key, pending.onLateResponse);
|
|
162545
|
+
this.rejectPending(key, pending, new SubcError(this.timeoutMessage(channel, corr, ms), REQUEST_DEADLINE_MARKER));
|
|
162546
|
+
};
|
|
162547
|
+
const graceDeadline = Date.now() + this.opts.timeoutArbitrationGraceMs;
|
|
162548
|
+
const arbitrate = () => {
|
|
162549
|
+
if (this.pending.get(key) !== pending)
|
|
162550
|
+
return;
|
|
162551
|
+
const readerDraining = this.readerActive || this.sock.bufferedBytes() > 0;
|
|
162552
|
+
if (readerDraining && Date.now() < graceDeadline) {
|
|
162553
|
+
setImmediate(arbitrate);
|
|
162554
|
+
return;
|
|
162555
|
+
}
|
|
162556
|
+
settleAsTimeout();
|
|
162557
|
+
};
|
|
162558
|
+
setImmediate(arbitrate);
|
|
162559
|
+
}
|
|
162560
|
+
async managedRequest(handle, body, opts) {
|
|
162561
|
+
const bytes = body instanceof Uint8Array ? body : this.encode(body);
|
|
162562
|
+
const priority = opts.priority ?? Priority.Interactive;
|
|
162563
|
+
const admission = opts.admissionClass ?? AdmissionClass.Normal;
|
|
162564
|
+
try {
|
|
162565
|
+
const reply = await this.sendManaged(handle, bytes, priority, admission, opts.timeoutMs, opts.onProgress);
|
|
162566
|
+
return this.parseJson(reply);
|
|
162567
|
+
} catch (error51) {
|
|
162568
|
+
if (error51 instanceof SubcCallError)
|
|
162569
|
+
throw error51;
|
|
162570
|
+
throw this.terminalCallError("managed call failed", error51);
|
|
162571
|
+
}
|
|
162572
|
+
}
|
|
162573
|
+
sendManaged(handle, body, priority, admission, timeoutMs, onProgress) {
|
|
162574
|
+
try {
|
|
162575
|
+
this.assertLiveHandle(handle);
|
|
162576
|
+
} catch (error51) {
|
|
162577
|
+
return Promise.reject(this.notSentCallError("request used a stale route handle", error51));
|
|
162578
|
+
}
|
|
162579
|
+
if (this.closedErr) {
|
|
162580
|
+
return Promise.reject(this.notSentCallError("request was not sent because the subc connection was already closed", this.closedErr));
|
|
162581
|
+
}
|
|
162582
|
+
let corr;
|
|
162583
|
+
try {
|
|
162584
|
+
corr = this.allocateCorr();
|
|
162585
|
+
} catch (error51) {
|
|
162586
|
+
return Promise.reject(this.notSentCallError("request correlation allocator was exhausted", error51));
|
|
162587
|
+
}
|
|
162588
|
+
const key = pendingKey(handle, corr);
|
|
162589
|
+
const frame = buildFrame(FrameType.Request, buildFlags(false, priority, false, admission), handle.channel, handle.epoch, corr, body);
|
|
162590
|
+
let handedToSocket = false;
|
|
162591
|
+
const classifyFailure = (error51) => {
|
|
162592
|
+
if (!handedToSocket)
|
|
162593
|
+
return this.notSentCallError("request bytes were not queued to the subc socket", error51);
|
|
162594
|
+
if (error51 instanceof SubcError && error51.code === REQUEST_DEADLINE_MARKER) {
|
|
162595
|
+
return new SubcCallError("outcome_unknown", `managed call deadline exceeded after request bytes were queued to the local socket; no terminal response was observed; outcome unknown${causeMessage(error51)}`, DEADLINE_NO_DROP_CODE, error51);
|
|
162596
|
+
}
|
|
162597
|
+
return this.outcomeUnknownCallError("connection dropped before the managed call returned a response", error51);
|
|
162598
|
+
};
|
|
162599
|
+
return new Promise((resolve2, reject) => {
|
|
162600
|
+
const ms = timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
162601
|
+
const pending = {
|
|
162602
|
+
handle,
|
|
162603
|
+
resolve: resolve2,
|
|
162604
|
+
reject,
|
|
162605
|
+
onProgress,
|
|
162606
|
+
timer: null,
|
|
162607
|
+
classifyFailure
|
|
162608
|
+
};
|
|
162609
|
+
pending.timer = setTimeout(() => this.arbitrateTimeout(key, pending, handle.channel, corr, ms), ms);
|
|
162610
|
+
this.pending.set(key, pending);
|
|
162611
|
+
const write = this.sock.writeTracked(encodeFrame(frame), Date.now() + ms);
|
|
162612
|
+
handedToSocket = write.queued;
|
|
162613
|
+
write.completed.catch((error51) => {
|
|
162614
|
+
const current = this.pending.get(key);
|
|
162615
|
+
if (current)
|
|
162616
|
+
this.rejectPending(key, current, error51 instanceof Error ? error51 : new SubcError(String(error51)));
|
|
162617
|
+
});
|
|
162618
|
+
});
|
|
162619
|
+
}
|
|
162620
|
+
async cachedRouteHandle(moduleId, opts) {
|
|
162621
|
+
const identity = opts.identity ?? this.opts.identity;
|
|
162622
|
+
if (!identity) {
|
|
162623
|
+
throw new SubcCallError("terminal", "managed call requires a BindIdentity in SubcClient.connect({ identity }) or call(..., { identity })", "missing_identity");
|
|
162624
|
+
}
|
|
162625
|
+
const target = { kind: opts.targetKind ?? this.opts.targetKind, module_id: moduleId };
|
|
162626
|
+
const consumerIdentity = routeOpenConsumerIdentity(opts);
|
|
162627
|
+
const key = routeCacheKey(target, identity, consumerIdentity);
|
|
162628
|
+
let cached2 = this.routes.get(key);
|
|
162629
|
+
if (!cached2) {
|
|
162630
|
+
cached2 = {
|
|
162631
|
+
key,
|
|
162632
|
+
moduleId,
|
|
162633
|
+
target,
|
|
162634
|
+
identity,
|
|
162635
|
+
consumerIdentity,
|
|
162636
|
+
handle: null,
|
|
162637
|
+
opening: null
|
|
162638
|
+
};
|
|
162639
|
+
this.routes.set(key, cached2);
|
|
162640
|
+
}
|
|
162641
|
+
if (cached2.handle && this.isLiveHandle(cached2.handle))
|
|
162642
|
+
return cached2.handle;
|
|
162643
|
+
if (!cached2.opening) {
|
|
162644
|
+
cached2.opening = this.openCachedRoute(cached2).finally(() => {
|
|
162645
|
+
cached2.opening = null;
|
|
162646
|
+
});
|
|
162647
|
+
}
|
|
162648
|
+
return cached2.opening;
|
|
162649
|
+
}
|
|
162650
|
+
async openCachedRoute(cached2) {
|
|
162651
|
+
const routeRetryDeadline = Date.now() + ROUTE_OPEN_RETRY_DEADLINE_MS;
|
|
162652
|
+
let routeRetryDelay = this.opts.reconnectBackoff.baseMs;
|
|
162653
|
+
let routeRetryAttempt = 0;
|
|
162654
|
+
for (;; ) {
|
|
162655
|
+
if (cached2.closed)
|
|
162656
|
+
throw this.routeClosedDuringOpen();
|
|
162657
|
+
try {
|
|
162658
|
+
await this.ensureConnectedForManaged();
|
|
162659
|
+
} catch (error51) {
|
|
162660
|
+
throw this.notSentRecoveryError("route.open could not run because reconnect failed", error51);
|
|
162661
|
+
}
|
|
162662
|
+
if (cached2.handle && this.isLiveHandle(cached2.handle))
|
|
162663
|
+
return cached2.handle;
|
|
162664
|
+
try {
|
|
162665
|
+
const handle = await this.routeOpen(cached2.target, cached2.identity, {
|
|
162666
|
+
consumerIdentity: cached2.consumerIdentity ?? null
|
|
162667
|
+
});
|
|
162668
|
+
if (cached2.closed) {
|
|
162669
|
+
this.liveRoutes.delete(handle.channel);
|
|
162670
|
+
this.sendRouteGoodbye(handle);
|
|
162671
|
+
throw this.routeClosedDuringOpen();
|
|
162672
|
+
}
|
|
162673
|
+
cached2.handle = handle;
|
|
162674
|
+
return handle;
|
|
162675
|
+
} catch (error51) {
|
|
162676
|
+
if (error51 instanceof SubcCallError && error51.code === "route_closed")
|
|
162677
|
+
throw error51;
|
|
162678
|
+
if (!this.closeStarted && isConsumerReconnectTransient(error51)) {
|
|
162679
|
+
try {
|
|
162680
|
+
await this.reconnectAfterDrop(error51);
|
|
162681
|
+
} catch (reconnectError) {
|
|
162682
|
+
throw this.notSentRecoveryError("route.open was not sent and reconnect failed", reconnectError);
|
|
162683
|
+
}
|
|
162684
|
+
continue;
|
|
162685
|
+
}
|
|
162686
|
+
if (!this.closeStarted && error51 instanceof SubcError && isRetryableRouteOpenCode(error51.code)) {
|
|
162687
|
+
routeRetryAttempt += 1;
|
|
162688
|
+
if (routeRetryAttempt < this.opts.reconnectBackoff.maxAttempts && Date.now() < routeRetryDeadline) {
|
|
162689
|
+
await this.opts.sleep(routeRetryDelay);
|
|
162690
|
+
routeRetryDelay = Math.min(routeRetryDelay * 2, this.opts.reconnectBackoff.capMs);
|
|
162691
|
+
continue;
|
|
162692
|
+
}
|
|
162693
|
+
throw this.notSentCallError(`route.open failed for module ${cached2.moduleId}: ${error51.code} (retry budget exhausted)`, error51);
|
|
162694
|
+
}
|
|
162695
|
+
throw this.terminalCallError(`route.open failed for module ${cached2.moduleId}`, error51);
|
|
162696
|
+
}
|
|
162697
|
+
}
|
|
162698
|
+
}
|
|
162699
|
+
async ensureConnectedForManaged() {
|
|
162700
|
+
if (this.closeStarted)
|
|
162701
|
+
throw new SubcError("client closed");
|
|
162702
|
+
if (this.reconnecting)
|
|
162703
|
+
await this.reconnecting;
|
|
162704
|
+
if (this.closedErr)
|
|
162705
|
+
await this.reconnectAfterDrop(this.closedErr);
|
|
162706
|
+
}
|
|
162707
|
+
scheduleReconnectAfterDrop(err) {
|
|
162708
|
+
if (this.closeStarted || this.reconnecting)
|
|
162709
|
+
return;
|
|
162710
|
+
this.reconnectAfterDrop(err).catch(() => {});
|
|
162711
|
+
}
|
|
162712
|
+
reconnectAfterDrop(trigger) {
|
|
162713
|
+
if (this.closeStarted)
|
|
162714
|
+
return Promise.reject(new SubcError("client closed"));
|
|
162715
|
+
if (this.reconnecting)
|
|
162716
|
+
return this.reconnecting;
|
|
162717
|
+
const promise2 = this.reconnectWithRetry(trigger).finally(() => {
|
|
162718
|
+
if (this.reconnecting === promise2)
|
|
162719
|
+
this.reconnecting = null;
|
|
162720
|
+
});
|
|
162721
|
+
this.reconnecting = promise2;
|
|
162722
|
+
return promise2;
|
|
162723
|
+
}
|
|
162724
|
+
async reconnectWithRetry(_trigger) {
|
|
162725
|
+
let attempt = 0;
|
|
162726
|
+
let delay = this.opts.reconnectBackoff.baseMs;
|
|
162727
|
+
for (;; ) {
|
|
162728
|
+
if (this.closeStarted)
|
|
162729
|
+
throw new SubcError("client closed");
|
|
162730
|
+
attempt += 1;
|
|
162731
|
+
try {
|
|
162732
|
+
const opened = await SubcClient.openConnection(this.opts);
|
|
162733
|
+
if (this.closeStarted) {
|
|
162734
|
+
opened.sock.close();
|
|
162735
|
+
throw new SubcError("client closed");
|
|
162736
|
+
}
|
|
162737
|
+
this.replaceConnection(opened);
|
|
162738
|
+
await this.reopenCachedRoutes();
|
|
162739
|
+
return;
|
|
162740
|
+
} catch (err) {
|
|
162741
|
+
if (!isConsumerReconnectTransient(err) || attempt >= this.opts.reconnectBackoff.maxAttempts) {
|
|
162742
|
+
if (err instanceof AuthError && attempt > 1) {
|
|
162743
|
+
throw new AuthError(`reconnect gave up after ${attempt} attempts: ${err.message} — the connection file and the daemon's key disagree persistently ` + `(daemon restarting in a loop, split connection-file paths, or a genuinely foreign daemon on this port); ` + `check the daemon, then restart this host app`);
|
|
162744
|
+
}
|
|
162745
|
+
throw err;
|
|
162746
|
+
}
|
|
162747
|
+
await this.opts.sleep(delay);
|
|
162748
|
+
delay = Math.min(delay * 2, this.opts.reconnectBackoff.capMs);
|
|
162749
|
+
}
|
|
162750
|
+
}
|
|
162751
|
+
}
|
|
162752
|
+
replaceConnection(opened) {
|
|
162753
|
+
this.sock.close();
|
|
162754
|
+
this.sock = opened.sock;
|
|
162755
|
+
this.currentConn = opened.conn;
|
|
162756
|
+
this.closedErr = null;
|
|
162757
|
+
this.generation += 1;
|
|
162758
|
+
this.connectionToken = newConnectionToken();
|
|
162759
|
+
this.liveRoutes.clear();
|
|
162760
|
+
this.lateResponses.clear();
|
|
162761
|
+
this.nextCorr = 1n;
|
|
162762
|
+
this.readLoop(opened.sock, this.generation);
|
|
162763
|
+
}
|
|
162764
|
+
async reopenCachedRoutes() {
|
|
162765
|
+
for (const cached2 of this.routes.values())
|
|
162766
|
+
cached2.handle = null;
|
|
162767
|
+
for (const cached2 of this.routes.values()) {
|
|
162768
|
+
if (cached2.closed)
|
|
162769
|
+
continue;
|
|
162770
|
+
const handle = await this.routeOpen(cached2.target, cached2.identity, {
|
|
162771
|
+
consumerIdentity: cached2.consumerIdentity ?? null
|
|
162772
|
+
});
|
|
162773
|
+
if (cached2.closed) {
|
|
162774
|
+
this.liveRoutes.delete(handle.channel);
|
|
162775
|
+
this.sendRouteGoodbye(handle);
|
|
162776
|
+
continue;
|
|
162777
|
+
}
|
|
162778
|
+
cached2.handle = handle;
|
|
162779
|
+
}
|
|
162780
|
+
}
|
|
162781
|
+
timeoutMessage(channel, corr, ms) {
|
|
162782
|
+
const port = this.sock.localPort();
|
|
162783
|
+
const where = port === null ? "channel" : `local_port=${port} channel`;
|
|
162784
|
+
return `request on ${where} ${channel} corr ${corr} timed out after ${ms}ms`;
|
|
162785
|
+
}
|
|
162786
|
+
routeClosedDuringOpen() {
|
|
162787
|
+
return new SubcCallError("not_sent", "route was closed before route.open completed", "route_closed");
|
|
162788
|
+
}
|
|
162789
|
+
async readLoop(sock, generation) {
|
|
162790
|
+
try {
|
|
162791
|
+
for (;; ) {
|
|
162792
|
+
this.readerActive = false;
|
|
162793
|
+
const frame = await sock.readFrame(Number.POSITIVE_INFINITY, { afterHeaderMs: BODY_READ_TIMEOUT_MS }, () => {
|
|
162794
|
+
this.readerActive = true;
|
|
162795
|
+
});
|
|
162796
|
+
try {
|
|
162797
|
+
if (this.sock === sock && this.generation === generation)
|
|
162798
|
+
this.dispatch(frame);
|
|
162799
|
+
} finally {
|
|
162800
|
+
this.readerActive = false;
|
|
162801
|
+
}
|
|
162802
|
+
}
|
|
162803
|
+
} catch (error51) {
|
|
162804
|
+
if (this.sock === sock && this.generation === generation) {
|
|
162805
|
+
this.fail(error51 instanceof Error ? error51 : new SubcError(String(error51)));
|
|
162806
|
+
}
|
|
162807
|
+
}
|
|
162808
|
+
}
|
|
162809
|
+
dispatch(frame) {
|
|
162810
|
+
let handle = null;
|
|
162811
|
+
if (frame.header.channel !== 0) {
|
|
162812
|
+
handle = this.liveRoutes.get(frame.header.channel) ?? null;
|
|
162813
|
+
if (!handle || handle.epoch !== frame.header.epoch) {
|
|
162814
|
+
this.ingressEpochDropCount += 1;
|
|
162815
|
+
return;
|
|
162816
|
+
}
|
|
162817
|
+
}
|
|
162818
|
+
const key = pendingKey(handle, frame.header.corr);
|
|
162819
|
+
const pending = this.pending.get(key);
|
|
162820
|
+
if (pending) {
|
|
162821
|
+
if (pending.acceptFrame && !pending.acceptFrame(frame))
|
|
162822
|
+
return;
|
|
162823
|
+
switch (frame.header.ty) {
|
|
162824
|
+
case FrameType.Push:
|
|
162825
|
+
case FrameType.StreamData:
|
|
162826
|
+
pending.onProgress?.(frame.body);
|
|
162827
|
+
return;
|
|
162828
|
+
case FrameType.Response:
|
|
162829
|
+
case FrameType.StreamEnd:
|
|
162830
|
+
this.settle(key, pending, () => pending.resolve(frame));
|
|
162831
|
+
return;
|
|
162832
|
+
case FrameType.Error:
|
|
162833
|
+
this.settle(key, pending, () => pending.reject(this.errorFromFrame(frame)));
|
|
162834
|
+
return;
|
|
162835
|
+
default:
|
|
162836
|
+
return;
|
|
162837
|
+
}
|
|
162838
|
+
}
|
|
162839
|
+
const late = this.lateResponses.get(key);
|
|
162840
|
+
if (late && (frame.header.ty === FrameType.Response || frame.header.ty === FrameType.Error)) {
|
|
162841
|
+
this.lateResponses.delete(key);
|
|
162842
|
+
late(frame);
|
|
162843
|
+
return;
|
|
162844
|
+
}
|
|
162845
|
+
if (frame.header.ty === FrameType.Goodbye && handle) {
|
|
162846
|
+
this.failHandle(handle, new SubcError("route closed by subc (GOODBYE)"));
|
|
162847
|
+
if (this.liveRoutes.get(handle.channel) === handle)
|
|
162848
|
+
this.liveRoutes.delete(handle.channel);
|
|
162849
|
+
this.evictRouteHandle(handle);
|
|
162850
|
+
return;
|
|
162851
|
+
}
|
|
162852
|
+
if (frame.header.ty === FrameType.Response || frame.header.ty === FrameType.Error || frame.header.ty === FrameType.StreamEnd) {
|
|
162853
|
+
debug("dropped terminal frame with no waiter: type=%d channel=%d epoch=%d corr=%s port=%s", frame.header.ty, frame.header.channel, frame.header.epoch, frame.header.corr, this.sock.localPort() ?? "?");
|
|
162854
|
+
}
|
|
162855
|
+
}
|
|
162856
|
+
settle(key, pending, run) {
|
|
162857
|
+
if (this.pending.get(key) !== pending)
|
|
162858
|
+
return false;
|
|
162859
|
+
this.pending.delete(key);
|
|
162860
|
+
if (pending.timer)
|
|
162861
|
+
clearTimeout(pending.timer);
|
|
162862
|
+
run();
|
|
162863
|
+
pending.onSettle?.();
|
|
162864
|
+
return true;
|
|
162865
|
+
}
|
|
162866
|
+
rejectPending(key, pending, err) {
|
|
162867
|
+
this.settle(key, pending, () => pending.reject(pending.classifyFailure?.(err) ?? err));
|
|
162868
|
+
}
|
|
162869
|
+
errorFromFrame(frame) {
|
|
162870
|
+
try {
|
|
162871
|
+
const parsed = JSON.parse(Buffer.from(frame.body).toString("utf8"));
|
|
162872
|
+
return new SubcError(parsed.message ?? "subc error", parsed.code);
|
|
162873
|
+
} catch {
|
|
162874
|
+
return new SubcError(Buffer.from(frame.body).toString("utf8") || "subc error");
|
|
162875
|
+
}
|
|
162876
|
+
}
|
|
162877
|
+
evictRouteHandle(handle) {
|
|
162878
|
+
for (const cached2 of this.routes.values()) {
|
|
162879
|
+
if (cached2.handle && sameRouteHandle(cached2.handle, handle))
|
|
162880
|
+
cached2.handle = null;
|
|
162881
|
+
}
|
|
162882
|
+
}
|
|
162883
|
+
failHandle(handle, error51) {
|
|
162884
|
+
for (const [key, pending] of this.pending) {
|
|
162885
|
+
if (pending.handle && sameRouteHandle(pending.handle, handle))
|
|
162886
|
+
this.rejectPending(key, pending, error51);
|
|
162887
|
+
}
|
|
162888
|
+
}
|
|
162889
|
+
fail(err) {
|
|
162890
|
+
if (!this.closedErr)
|
|
162891
|
+
this.closedErr = err;
|
|
162892
|
+
for (const [key, pending] of this.pending) {
|
|
162893
|
+
this.rejectPending(key, pending, err);
|
|
162894
|
+
}
|
|
162895
|
+
}
|
|
162896
|
+
notSentCallError(message, cause) {
|
|
162897
|
+
return new SubcCallError("not_sent", `${message}${causeMessage(cause)}`, errorCode(cause), cause);
|
|
162898
|
+
}
|
|
162899
|
+
outcomeUnknownCallError(message, cause) {
|
|
162900
|
+
return new SubcCallError("outcome_unknown", `${message}${causeMessage(cause)}`, errorCode(cause), cause);
|
|
162901
|
+
}
|
|
162902
|
+
terminalCallError(message, cause) {
|
|
162903
|
+
if (cause instanceof SubcCallError)
|
|
162904
|
+
return cause;
|
|
162905
|
+
return new SubcCallError("terminal", `${message}${causeMessage(cause)}`, errorCode(cause), cause);
|
|
162906
|
+
}
|
|
162907
|
+
notSentRecoveryError(message, cause) {
|
|
162908
|
+
if (cause instanceof SubcCallError)
|
|
162909
|
+
return cause;
|
|
162910
|
+
if (isConsumerReconnectTransient(cause))
|
|
162911
|
+
return this.notSentCallError(message, cause);
|
|
162912
|
+
return this.terminalCallError(message, cause);
|
|
162913
|
+
}
|
|
162914
|
+
get droppedIngressFrames() {
|
|
162915
|
+
return this.ingressEpochDropCount;
|
|
162916
|
+
}
|
|
162917
|
+
installRoute(channel, epoch) {
|
|
162918
|
+
const handle = createRouteHandle(channel, epoch, this.connectionToken);
|
|
162919
|
+
this.liveRoutes.set(channel, handle);
|
|
162920
|
+
return handle;
|
|
162921
|
+
}
|
|
162922
|
+
isLiveHandle(handle) {
|
|
162923
|
+
return belongsToConnection(handle, this.connectionToken) && this.liveRoutes.get(handle.channel) === handle;
|
|
162924
|
+
}
|
|
162925
|
+
assertLiveConnection(handle) {
|
|
162926
|
+
if (!belongsToConnection(handle, this.connectionToken))
|
|
162927
|
+
throw new StaleRouteHandleError(handle);
|
|
162928
|
+
}
|
|
162929
|
+
assertLiveHandle(handle) {
|
|
162930
|
+
if (!this.isLiveHandle(handle))
|
|
162931
|
+
throw new StaleRouteHandleError(handle);
|
|
162932
|
+
}
|
|
162933
|
+
allocateCorr() {
|
|
162934
|
+
const maximum = 0xffffffffffffffffn;
|
|
162935
|
+
if (this.nextCorr > maximum) {
|
|
162936
|
+
const error51 = new SubcError("channel-0 correlation id allocator exhausted", "corr_exhausted");
|
|
162937
|
+
this.fail(error51);
|
|
162938
|
+
this.sock.close();
|
|
162939
|
+
this.scheduleReconnectAfterDrop(error51);
|
|
162940
|
+
throw error51;
|
|
162941
|
+
}
|
|
162942
|
+
const corr = this.nextCorr;
|
|
162943
|
+
this.nextCorr += 1n;
|
|
162944
|
+
return corr;
|
|
162945
|
+
}
|
|
162946
|
+
closeConnectionAfterCleanupFailure() {
|
|
162947
|
+
const error51 = new SubcError("late route cleanup could not be queued", "late_route_cleanup_failed");
|
|
162948
|
+
this.fail(error51);
|
|
162949
|
+
this.sock.close();
|
|
162950
|
+
this.scheduleReconnectAfterDrop(error51);
|
|
162951
|
+
}
|
|
162952
|
+
encode(value) {
|
|
162953
|
+
return new Uint8Array(Buffer.from(JSON.stringify(value), "utf8"));
|
|
162954
|
+
}
|
|
162955
|
+
parseJson(frame) {
|
|
162956
|
+
return JSON.parse(Buffer.from(frame.body).toString("utf8"));
|
|
162957
|
+
}
|
|
162958
|
+
}
|
|
162959
|
+
function isConsumerReconnectTransient(err) {
|
|
162960
|
+
if (err instanceof SocketClosedError || err instanceof SocketTimeoutError)
|
|
162961
|
+
return true;
|
|
162962
|
+
if (err instanceof SocketWriteNotQueuedError || err instanceof SocketWriteQueuedError)
|
|
162963
|
+
return true;
|
|
162964
|
+
if (err instanceof SubcCallError)
|
|
162965
|
+
return err.kind === "not_sent" || err.kind === "outcome_unknown";
|
|
162966
|
+
if (err instanceof AuthError)
|
|
162967
|
+
return true;
|
|
162968
|
+
if (err instanceof SubcError || err instanceof ConnectionFileError)
|
|
162969
|
+
return false;
|
|
162970
|
+
const code = errorCode(err);
|
|
162971
|
+
return code === "ECONNREFUSED" || code === "ECONNRESET" || code === "EPIPE" || code === "ETIMEDOUT" || code === "ENOENT";
|
|
162972
|
+
}
|
|
162973
|
+
function isRetryableRouteOpenCode(code) {
|
|
162974
|
+
return code === "unknown_module" || code === "module_reloading" || code === "target_unavailable" || code === "module_timeout";
|
|
162975
|
+
}
|
|
162976
|
+
async function connectionFileExists(path4) {
|
|
162977
|
+
try {
|
|
162978
|
+
await fs3.access(path4);
|
|
162979
|
+
return true;
|
|
162980
|
+
} catch {
|
|
162981
|
+
return false;
|
|
162982
|
+
}
|
|
162983
|
+
}
|
|
162984
|
+
function normalizeConnectOptions(opts) {
|
|
162985
|
+
return {
|
|
162986
|
+
connectionFile: opts.connectionFile,
|
|
162987
|
+
handshakeTimeoutMs: opts.handshakeTimeoutMs,
|
|
162988
|
+
identity: opts.identity,
|
|
162989
|
+
targetKind: opts.targetKind ?? DEFAULT_MANAGED_TARGET_KIND,
|
|
162990
|
+
reconnectBackoff: opts.reconnectBackoff ?? DEFAULT_RECONNECT_BACKOFF,
|
|
162991
|
+
sleep: opts.sleep ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms))),
|
|
162992
|
+
timeoutArbitrationGraceMs: opts.timeoutArbitrationGraceMs ?? TIMEOUT_ARBITRATION_GRACE_MS
|
|
162993
|
+
};
|
|
162994
|
+
}
|
|
162995
|
+
function routeCacheKey(target, identity, consumerIdentity) {
|
|
162996
|
+
const consumerPart = consumerIdentity ? `${consumerIdentity.module_id}\x00${consumerIdentity.launch_nonce}` : "";
|
|
162997
|
+
return `${target.kind}\x00${target.module_id}\x00${identity.project_root}\x00${identity.harness}\x00${identity.session}\x00${consumerPart}`;
|
|
162998
|
+
}
|
|
162999
|
+
function routeOpenConsumerIdentity(opts = {}) {
|
|
163000
|
+
if (opts.consumerIdentity !== undefined)
|
|
163001
|
+
return opts.consumerIdentity ?? undefined;
|
|
163002
|
+
const moduleId = process.env[SUBC_MODULE_ID_ENV];
|
|
163003
|
+
const launchNonce = process.env[SUBC_LAUNCH_NONCE_ENV];
|
|
163004
|
+
if (!moduleId || !launchNonce)
|
|
163005
|
+
return;
|
|
163006
|
+
return { module_id: moduleId, launch_nonce: launchNonce };
|
|
163007
|
+
}
|
|
163008
|
+
function errorCode(err) {
|
|
163009
|
+
if (typeof err === "object" && err !== null && "code" in err) {
|
|
163010
|
+
const code = err.code;
|
|
163011
|
+
if (typeof code === "string")
|
|
163012
|
+
return code;
|
|
163013
|
+
}
|
|
163014
|
+
return;
|
|
163015
|
+
}
|
|
163016
|
+
function causeMessage(cause) {
|
|
163017
|
+
if (cause === undefined)
|
|
163018
|
+
return "";
|
|
163019
|
+
return `: ${cause instanceof Error ? cause.message : String(cause)}`;
|
|
163020
|
+
}
|
|
163021
|
+
function pendingKey(handle, corr) {
|
|
163022
|
+
return handle ? `${handle.channel}:${handle.epoch}:${corr}` : `0:0:${corr}`;
|
|
163023
|
+
}
|
|
163024
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.4.1/node_modules/@cortexkit/subc-client/dist/provider.js
|
|
163025
|
+
import { Buffer as Buffer2 } from "node:buffer";
|
|
163026
|
+
var DEFAULT_HANDSHAKE_TIMEOUT_MS2 = 1e4;
|
|
163027
|
+
var BODY_READ_TIMEOUT_MS2 = 30000;
|
|
163028
|
+
var WRITE_TIMEOUT_MS = 30000;
|
|
163029
|
+
var DEFAULT_RESTORED_DEBOUNCE_MS = 250;
|
|
163030
|
+
var DEFAULT_PROVIDER_HANDLER_CAPACITY = 64;
|
|
163031
|
+
var HEALTH_CHECK_OP = "health.check";
|
|
163032
|
+
var HELLO_CORR = 1n;
|
|
163033
|
+
|
|
163034
|
+
class AsyncPermitPool {
|
|
163035
|
+
available;
|
|
163036
|
+
waiters = [];
|
|
163037
|
+
constructor(capacity) {
|
|
163038
|
+
if (!Number.isInteger(capacity) || capacity <= 0) {
|
|
163039
|
+
throw new SubcProviderError("provider handler capacity must be a positive integer", "invalid_handler_capacity");
|
|
163040
|
+
}
|
|
163041
|
+
this.available = capacity;
|
|
163042
|
+
}
|
|
163043
|
+
async acquire() {
|
|
163044
|
+
if (this.available > 0) {
|
|
163045
|
+
this.available -= 1;
|
|
163046
|
+
return this.releaseOnce();
|
|
163047
|
+
}
|
|
163048
|
+
await new Promise((resolve2) => {
|
|
163049
|
+
this.waiters.push(resolve2);
|
|
163050
|
+
});
|
|
163051
|
+
return this.releaseOnce();
|
|
163052
|
+
}
|
|
163053
|
+
releaseOnce() {
|
|
163054
|
+
let released = false;
|
|
163055
|
+
return () => {
|
|
163056
|
+
if (released)
|
|
163057
|
+
return;
|
|
163058
|
+
released = true;
|
|
163059
|
+
const next = this.waiters.shift();
|
|
163060
|
+
if (next) {
|
|
163061
|
+
next();
|
|
163062
|
+
} else {
|
|
163063
|
+
this.available += 1;
|
|
163064
|
+
}
|
|
163065
|
+
};
|
|
163066
|
+
}
|
|
163067
|
+
}
|
|
163068
|
+
|
|
163069
|
+
class SubcProviderError extends Error {
|
|
163070
|
+
code;
|
|
163071
|
+
constructor(message, code) {
|
|
163072
|
+
super(message);
|
|
163073
|
+
this.code = code;
|
|
163074
|
+
}
|
|
163075
|
+
}
|
|
163076
|
+
class SubcProvider {
|
|
163077
|
+
sock;
|
|
163078
|
+
currentConn;
|
|
163079
|
+
opts;
|
|
163080
|
+
closed;
|
|
163081
|
+
resolveClosed = () => {
|
|
163082
|
+
return;
|
|
163083
|
+
};
|
|
163084
|
+
closeStarted = false;
|
|
163085
|
+
closedErr = null;
|
|
163086
|
+
inflight = new Map;
|
|
163087
|
+
pending = new Map;
|
|
163088
|
+
liveRoutes = new Map;
|
|
163089
|
+
connectionToken = newConnectionToken();
|
|
163090
|
+
nextCorr = 1n;
|
|
163091
|
+
ingressEpochDropCount = 0;
|
|
163092
|
+
requestGate = new AsyncPermitPool(DEFAULT_PROVIDER_HANDLER_CAPACITY);
|
|
163093
|
+
reconnecting = null;
|
|
163094
|
+
generation = 1;
|
|
163095
|
+
connectionEpoch = 1;
|
|
163096
|
+
stateQueue = [];
|
|
163097
|
+
drainingStateQueue = false;
|
|
163098
|
+
restoredDebounceToken = 0;
|
|
163099
|
+
storage;
|
|
163100
|
+
constructor(sock, currentConn, opts, storage) {
|
|
163101
|
+
this.sock = sock;
|
|
163102
|
+
this.currentConn = currentConn;
|
|
163103
|
+
this.opts = opts;
|
|
163104
|
+
this.storage = storage;
|
|
163105
|
+
this.closed = new Promise((resolve2) => {
|
|
163106
|
+
this.resolveClosed = resolve2;
|
|
163107
|
+
});
|
|
163108
|
+
this.readLoop(sock, this.generation);
|
|
163109
|
+
this.enqueueConnectionState({ state: "connected", epoch: this.connectionEpoch });
|
|
163110
|
+
}
|
|
163111
|
+
get droppedIngressFrames() {
|
|
163112
|
+
return this.ingressEpochDropCount;
|
|
163113
|
+
}
|
|
163114
|
+
get conn() {
|
|
163115
|
+
return this.currentConn;
|
|
163116
|
+
}
|
|
163117
|
+
currentEpoch() {
|
|
163118
|
+
return this.connectionEpoch;
|
|
163119
|
+
}
|
|
163120
|
+
async request(handle, body, opts = {}) {
|
|
163121
|
+
this.assertLiveHandle(handle);
|
|
163122
|
+
const corr = this.allocateCorr();
|
|
163123
|
+
const key = routeKey(handle, corr);
|
|
163124
|
+
const timeoutMs = opts.timeoutMs ?? WRITE_TIMEOUT_MS;
|
|
163125
|
+
const frame = buildFrame(FrameType.Request, buildFlags(false, opts.priority ?? Priority.Interactive, false, opts.admissionClass ?? AdmissionClass.Normal), handle.channel, handle.epoch, corr, body);
|
|
163126
|
+
return await new Promise((resolve2, reject) => {
|
|
163127
|
+
const timer = setTimeout(() => {
|
|
163128
|
+
if (this.pending.delete(key))
|
|
163129
|
+
reject(new SubcProviderError("reverse request timed out", "request_timeout"));
|
|
163130
|
+
}, timeoutMs);
|
|
163131
|
+
this.pending.set(key, {
|
|
163132
|
+
resolve: (response) => resolve2(response.body),
|
|
163133
|
+
reject,
|
|
163134
|
+
timer
|
|
163135
|
+
});
|
|
163136
|
+
this.sendOn(this.sock, this.generation, frame).catch((error51) => {
|
|
163137
|
+
const pending = this.pending.get(key);
|
|
163138
|
+
if (!pending)
|
|
163139
|
+
return;
|
|
163140
|
+
this.pending.delete(key);
|
|
163141
|
+
clearTimeout(pending.timer);
|
|
163142
|
+
reject(error51 instanceof Error ? error51 : new SubcProviderError(String(error51)));
|
|
163143
|
+
});
|
|
163144
|
+
});
|
|
163145
|
+
}
|
|
163146
|
+
async push(handle, body, opts = {}) {
|
|
163147
|
+
this.assertLiveHandle(handle);
|
|
163148
|
+
await this.sendOn(this.sock, this.generation, buildFrame(FrameType.Push, buildFlags(false, opts.priority ?? Priority.Interactive, false, opts.admissionClass ?? AdmissionClass.Normal), handle.channel, handle.epoch, 0n, body));
|
|
163149
|
+
}
|
|
163150
|
+
cancel(handle, corr) {
|
|
163151
|
+
this.assertLiveHandle(handle);
|
|
163152
|
+
this.sendOn(this.sock, this.generation, buildFrame(FrameType.Cancel, controlFlags(), handle.channel, handle.epoch, corr, new Uint8Array(0)));
|
|
163153
|
+
}
|
|
163154
|
+
closeRoute(handle) {
|
|
163155
|
+
this.assertLiveHandle(handle);
|
|
163156
|
+
this.liveRoutes.delete(handle.channel);
|
|
163157
|
+
this.abortHandle(handle);
|
|
163158
|
+
this.sendOn(this.sock, this.generation, buildFrame(FrameType.Goodbye, controlFlags(), handle.channel, handle.epoch, 0n, new Uint8Array(0)));
|
|
163159
|
+
}
|
|
163160
|
+
static async connect(opts) {
|
|
163161
|
+
if (opts.manifest.protocol_ver !== PROTOCOL_VERSION) {
|
|
163162
|
+
throw new SubcProviderError(`manifest protocol_ver ${opts.manifest.protocol_ver} does not match client protocol ${PROTOCOL_VERSION}`, "invalid_manifest");
|
|
163163
|
+
}
|
|
163164
|
+
const normalized = normalizeProviderConnectOptions(opts);
|
|
163165
|
+
const opened = await SubcProvider.openConnection(normalized);
|
|
163166
|
+
return new SubcProvider(opened.sock, opened.conn, normalized, opened.ack.storage);
|
|
163167
|
+
}
|
|
163168
|
+
async close() {
|
|
163169
|
+
if (!this.closeStarted) {
|
|
163170
|
+
this.closeStarted = true;
|
|
163171
|
+
this.cancelRestoredDebounce();
|
|
163172
|
+
const sock = this.sock;
|
|
163173
|
+
try {
|
|
163174
|
+
await sendFrame(sock, buildFrame(FrameType.Goodbye, controlFlags(), 0, 0, 0n, new Uint8Array(0)));
|
|
163175
|
+
} catch {} finally {
|
|
163176
|
+
sock.close();
|
|
163177
|
+
this.finishClosed();
|
|
163178
|
+
}
|
|
163179
|
+
}
|
|
163180
|
+
await this.closed;
|
|
163181
|
+
}
|
|
163182
|
+
static async openConnection(opts) {
|
|
163183
|
+
const conn = await readConnectionFile(opts.connectionFile);
|
|
163184
|
+
const deadline = Date.now() + (opts.handshakeTimeoutMs ?? DEFAULT_HANDSHAKE_TIMEOUT_MS2);
|
|
163185
|
+
const endpoint = conn.endpoints[0];
|
|
163186
|
+
const sock = await SubcSocket.connect(endpoint.host, endpoint.port, deadline);
|
|
163187
|
+
try {
|
|
163188
|
+
await authenticateClient(sock, conn, deadline);
|
|
163189
|
+
await sendFrame(sock, buildHelloFrame(opts));
|
|
163190
|
+
const ack = await expectHelloAck(sock, deadline);
|
|
163191
|
+
return { sock, conn, ack };
|
|
163192
|
+
} catch (err) {
|
|
163193
|
+
sock.close();
|
|
163194
|
+
throw err;
|
|
163195
|
+
}
|
|
163196
|
+
}
|
|
163197
|
+
async readLoop(sock, generation) {
|
|
163198
|
+
try {
|
|
163199
|
+
for (;; ) {
|
|
163200
|
+
const frame = await sock.readFrame(Number.POSITIVE_INFINITY, { afterHeaderMs: BODY_READ_TIMEOUT_MS2 });
|
|
163201
|
+
const keepGoing = await this.dispatch(frame, sock, generation);
|
|
163202
|
+
if (!keepGoing) {
|
|
163203
|
+
if (this.sock === sock && this.generation === generation)
|
|
163204
|
+
this.closeStarted = true;
|
|
163205
|
+
break;
|
|
163206
|
+
}
|
|
163207
|
+
}
|
|
163208
|
+
} catch (error51) {
|
|
163209
|
+
if (this.sock === sock && this.generation === generation && !this.closeStarted) {
|
|
163210
|
+
this.handleUnexpectedDrop(sock, generation, error51 instanceof Error ? error51 : new SubcProviderError(String(error51)));
|
|
163211
|
+
return;
|
|
163212
|
+
}
|
|
163213
|
+
} finally {
|
|
163214
|
+
if (this.sock === sock && this.generation === generation) {
|
|
163215
|
+
sock.close();
|
|
163216
|
+
if (this.closeStarted)
|
|
163217
|
+
this.finishClosed();
|
|
163218
|
+
}
|
|
163219
|
+
}
|
|
163220
|
+
}
|
|
163221
|
+
async dispatch(frame, sock, generation) {
|
|
163222
|
+
let handle = null;
|
|
163223
|
+
if (frame.header.channel !== 0) {
|
|
163224
|
+
handle = this.liveRoutes.get(frame.header.channel) ?? null;
|
|
163225
|
+
if (!handle || handle.epoch !== frame.header.epoch) {
|
|
163226
|
+
this.ingressEpochDropCount += 1;
|
|
163227
|
+
return true;
|
|
163228
|
+
}
|
|
163229
|
+
}
|
|
163230
|
+
if (handle) {
|
|
163231
|
+
const pendingKey2 = routeKey(handle, frame.header.corr);
|
|
163232
|
+
const pending = this.pending.get(pendingKey2);
|
|
163233
|
+
if (pending) {
|
|
163234
|
+
if (frame.header.ty === FrameType.Push || frame.header.ty === FrameType.StreamData)
|
|
163235
|
+
return true;
|
|
163236
|
+
if (frame.header.ty === FrameType.Response || frame.header.ty === FrameType.StreamEnd) {
|
|
163237
|
+
this.pending.delete(pendingKey2);
|
|
163238
|
+
clearTimeout(pending.timer);
|
|
163239
|
+
pending.resolve(frame);
|
|
163240
|
+
return true;
|
|
163241
|
+
}
|
|
163242
|
+
if (frame.header.ty === FrameType.Error) {
|
|
163243
|
+
this.pending.delete(pendingKey2);
|
|
163244
|
+
clearTimeout(pending.timer);
|
|
163245
|
+
pending.reject(providerErrorFromFrame(frame));
|
|
163246
|
+
return true;
|
|
163247
|
+
}
|
|
163248
|
+
}
|
|
163249
|
+
}
|
|
163250
|
+
switch (frame.header.ty) {
|
|
163251
|
+
case FrameType.Ping:
|
|
163252
|
+
if (frame.header.channel === 0) {
|
|
163253
|
+
await this.sendOn(sock, generation, buildFrameWithVersion(frame.header.ver, FrameType.Pong, frame.header.flags, 0, 0, frame.header.corr, new Uint8Array(0)));
|
|
163254
|
+
}
|
|
163255
|
+
return true;
|
|
163256
|
+
case FrameType.Goodbye:
|
|
163257
|
+
if (!handle)
|
|
163258
|
+
return false;
|
|
163259
|
+
this.liveRoutes.delete(handle.channel);
|
|
163260
|
+
this.abortHandle(handle);
|
|
163261
|
+
await this.opts.onRouteGone?.(handle);
|
|
163262
|
+
return true;
|
|
163263
|
+
case FrameType.Cancel:
|
|
163264
|
+
if (handle)
|
|
163265
|
+
this.inflight.get(routeKey(handle, frame.header.corr))?.abort();
|
|
163266
|
+
return true;
|
|
163267
|
+
case FrameType.Request:
|
|
163268
|
+
if (frame.header.channel === 0) {
|
|
163269
|
+
await this.handleControlRequest(frame, sock, generation);
|
|
163270
|
+
} else if (handle) {
|
|
163271
|
+
this.handleDataRequest(frame, handle, sock, generation).catch((error51) => {
|
|
163272
|
+
if (!this.closeStarted && this.sock === sock && this.generation === generation) {
|
|
163273
|
+
console.warn("SubcProvider handler failed after its request was dispatched", error51);
|
|
163274
|
+
}
|
|
163275
|
+
});
|
|
163276
|
+
}
|
|
163277
|
+
return true;
|
|
163278
|
+
default:
|
|
163279
|
+
return true;
|
|
163280
|
+
}
|
|
163281
|
+
}
|
|
163282
|
+
abortHandle(handle) {
|
|
163283
|
+
const prefix = `${handle.channel}:${handle.epoch}:`;
|
|
163284
|
+
for (const [key, controller] of this.inflight) {
|
|
163285
|
+
if (key.startsWith(prefix))
|
|
163286
|
+
controller.abort();
|
|
163287
|
+
}
|
|
163288
|
+
for (const [key, pending] of this.pending) {
|
|
163289
|
+
if (!key.startsWith(prefix))
|
|
163290
|
+
continue;
|
|
163291
|
+
this.pending.delete(key);
|
|
163292
|
+
clearTimeout(pending.timer);
|
|
163293
|
+
pending.reject(new StaleRouteHandleError(handle));
|
|
163294
|
+
}
|
|
163295
|
+
}
|
|
163296
|
+
abortGeneration(_generation) {
|
|
163297
|
+
this.abortAllInflight();
|
|
163298
|
+
for (const [key, pending] of this.pending) {
|
|
163299
|
+
this.pending.delete(key);
|
|
163300
|
+
clearTimeout(pending.timer);
|
|
163301
|
+
pending.reject(new SubcProviderError("provider connection dropped", "connection_dropped"));
|
|
163302
|
+
}
|
|
163303
|
+
this.liveRoutes.clear();
|
|
163304
|
+
}
|
|
163305
|
+
abortAllInflight() {
|
|
163306
|
+
for (const controller of this.inflight.values())
|
|
163307
|
+
controller.abort();
|
|
163308
|
+
}
|
|
163309
|
+
async handleControlRequest(frame, sock, generation) {
|
|
163310
|
+
const request = parseJson(frame.body);
|
|
163311
|
+
if (request.op === HEALTH_CHECK_OP) {
|
|
163312
|
+
this.handleHealthRequest(frame, sock, generation).catch((error51) => {
|
|
163313
|
+
if (!this.closeStarted && this.sock === sock && this.generation === generation) {
|
|
163314
|
+
console.warn("SubcProvider health handler failed after its request was dispatched", error51);
|
|
163315
|
+
}
|
|
163316
|
+
});
|
|
163317
|
+
return;
|
|
163318
|
+
}
|
|
163319
|
+
if (request.op !== "route.bind") {
|
|
163320
|
+
throw new SubcProviderError(`unsupported module control request ${request.op ?? "<missing op>"}`);
|
|
163321
|
+
}
|
|
163322
|
+
const tentative = createRouteHandle(numberField(request.route_channel, "route_channel"), numberField(request.epoch, "epoch"), this.connectionToken);
|
|
163323
|
+
const bindRequest = {
|
|
163324
|
+
handle: tentative,
|
|
163325
|
+
target: request.target,
|
|
163326
|
+
identity: request.identity,
|
|
163327
|
+
principal: request.principal,
|
|
163328
|
+
consumer_capabilities: request.consumer_capabilities
|
|
163329
|
+
};
|
|
163330
|
+
let decision;
|
|
163331
|
+
try {
|
|
163332
|
+
decision = await this.opts.onBind?.(bindRequest);
|
|
163333
|
+
} catch (error51) {
|
|
163334
|
+
try {
|
|
163335
|
+
await this.sendError(frame, "route_rejected", error51 instanceof Error ? error51.message : String(error51), controlFlags(), sock, generation);
|
|
163336
|
+
} finally {
|
|
163337
|
+
await this.opts.onRouteGone?.(tentative);
|
|
163338
|
+
}
|
|
163339
|
+
return;
|
|
163340
|
+
}
|
|
163341
|
+
const rejection = bindRejection(decision);
|
|
163342
|
+
if (rejection) {
|
|
163343
|
+
try {
|
|
163344
|
+
await this.sendError(frame, rejection.code, rejection.message, controlFlags(), sock, generation);
|
|
163345
|
+
} finally {
|
|
163346
|
+
await this.opts.onRouteGone?.(tentative);
|
|
163347
|
+
}
|
|
163348
|
+
return;
|
|
163349
|
+
}
|
|
163350
|
+
try {
|
|
163351
|
+
await this.sendOn(sock, generation, buildFrameWithVersion(frame.header.ver, FrameType.Response, controlFlags(), 0, 0, frame.header.corr, encodeJson({ op: "route.bind" })));
|
|
163352
|
+
} catch (error51) {
|
|
163353
|
+
await this.opts.onRouteGone?.(tentative);
|
|
163354
|
+
throw error51;
|
|
163355
|
+
}
|
|
163356
|
+
if (this.sock !== sock || this.generation !== generation || this.closeStarted || this.closedErr) {
|
|
163357
|
+
await this.opts.onRouteGone?.(tentative);
|
|
163358
|
+
return;
|
|
163359
|
+
}
|
|
163360
|
+
this.liveRoutes.set(tentative.channel, tentative);
|
|
163361
|
+
await this.opts.onBound?.(tentative);
|
|
163362
|
+
}
|
|
163363
|
+
async handleDataRequest(frame, handle, sock, generation) {
|
|
163364
|
+
const { corr, ver } = frame.header;
|
|
163365
|
+
const key = routeKey(handle, corr);
|
|
163366
|
+
const controller = new AbortController;
|
|
163367
|
+
this.inflight.set(key, controller);
|
|
163368
|
+
const context = {
|
|
163369
|
+
handle,
|
|
163370
|
+
signal: controller.signal,
|
|
163371
|
+
currentEpoch: () => this.connectionEpoch,
|
|
163372
|
+
emit: async (eventBody, options = {}) => {
|
|
163373
|
+
this.assertLiveHandle(handle);
|
|
163374
|
+
if (controller.signal.aborted)
|
|
163375
|
+
return;
|
|
163376
|
+
await this.sendOn(sock, generation, buildFrameWithVersion(ver, FrameType.StreamData, buildFlags(false, options.priority ?? Priority.Interactive, false, options.admissionClass ?? AdmissionClass.Normal), handle.channel, handle.epoch, corr, eventBody));
|
|
163377
|
+
}
|
|
163378
|
+
};
|
|
163379
|
+
const releasePermit = await (this.requestGate ?? new AsyncPermitPool(DEFAULT_PROVIDER_HANDLER_CAPACITY)).acquire();
|
|
163380
|
+
const dataFlags = buildFlags(false, Priority.Interactive, false);
|
|
163381
|
+
try {
|
|
163382
|
+
const body = await this.opts.handler(handle, frame.body, context);
|
|
163383
|
+
if (controller.signal.aborted)
|
|
163384
|
+
return;
|
|
163385
|
+
this.assertLiveHandle(handle);
|
|
163386
|
+
if (body === undefined) {
|
|
163387
|
+
await this.sendOn(sock, generation, buildFrameWithVersion(ver, FrameType.StreamEnd, dataFlags, handle.channel, handle.epoch, corr, new Uint8Array(0)));
|
|
163388
|
+
} else if (body instanceof Uint8Array) {
|
|
163389
|
+
await this.sendOn(sock, generation, buildFrameWithVersion(ver, FrameType.Response, dataFlags, handle.channel, handle.epoch, corr, body));
|
|
163390
|
+
} else {
|
|
163391
|
+
throw new SubcProviderError("provider handler must return a Uint8Array or void", "invalid_handler_response");
|
|
163392
|
+
}
|
|
163393
|
+
} catch (error51) {
|
|
163394
|
+
if (error51 instanceof StaleRouteHandleError || controller.signal.aborted)
|
|
163395
|
+
return;
|
|
163396
|
+
await this.sendError(frame, error51 instanceof SubcProviderError && error51.code ? error51.code : "handler_error", error51 instanceof Error ? error51.message : String(error51), dataFlags, sock, generation);
|
|
163397
|
+
} finally {
|
|
163398
|
+
releasePermit();
|
|
163399
|
+
if (this.inflight.get(key) === controller)
|
|
163400
|
+
this.inflight.delete(key);
|
|
163401
|
+
}
|
|
163402
|
+
}
|
|
163403
|
+
async handleHealthRequest(frame, sock, generation) {
|
|
163404
|
+
const { corr, ver } = frame.header;
|
|
163405
|
+
const key = `control:${generation}:${corr}`;
|
|
163406
|
+
const controller = new AbortController;
|
|
163407
|
+
this.inflight.set(key, controller);
|
|
163408
|
+
const releasePermit = await (this.requestGate ?? new AsyncPermitPool(DEFAULT_PROVIDER_HANDLER_CAPACITY)).acquire();
|
|
163409
|
+
try {
|
|
163410
|
+
if (controller.signal.aborted)
|
|
163411
|
+
return;
|
|
163412
|
+
const report = await this.opts.health();
|
|
163413
|
+
await this.sendOn(sock, generation, buildFrameWithVersion(ver, FrameType.Response, controlFlags(), 0, 0, corr, encodeJson({
|
|
163414
|
+
op: HEALTH_CHECK_OP,
|
|
163415
|
+
status: report.status,
|
|
163416
|
+
...report.detail === undefined ? {} : { detail: report.detail },
|
|
163417
|
+
...report.metrics === undefined ? {} : { metrics: report.metrics }
|
|
163418
|
+
})));
|
|
163419
|
+
} catch (error51) {
|
|
163420
|
+
await this.sendError(frame, error51 instanceof SubcProviderError && error51.code ? error51.code : "health_error", error51 instanceof Error ? error51.message : String(error51), controlFlags(), sock, generation);
|
|
163421
|
+
} finally {
|
|
163422
|
+
releasePermit();
|
|
163423
|
+
if (this.inflight.get(key) === controller)
|
|
163424
|
+
this.inflight.delete(key);
|
|
163425
|
+
}
|
|
163426
|
+
}
|
|
163427
|
+
async sendError(frame, code, message, flags, sock, generation) {
|
|
163428
|
+
await this.sendOn(sock, generation, buildFrameWithVersion(frame.header.ver, FrameType.Error, flags, frame.header.channel, frame.header.epoch, frame.header.corr, encodeJson({ code, message })));
|
|
163429
|
+
}
|
|
163430
|
+
async sendOn(sock, generation, frame) {
|
|
163431
|
+
if (this.sock !== sock || this.generation !== generation || this.closeStarted || this.closedErr)
|
|
163432
|
+
return;
|
|
163433
|
+
await sendFrame(sock, frame);
|
|
163434
|
+
}
|
|
163435
|
+
handleUnexpectedDrop(sock, generation, cause) {
|
|
163436
|
+
this.cancelRestoredDebounce();
|
|
163437
|
+
this.abortGeneration(generation);
|
|
163438
|
+
this.generation += 1;
|
|
163439
|
+
sock.close();
|
|
163440
|
+
this.enqueueConnectionState({ state: "down", cause });
|
|
163441
|
+
this.scheduleReconnectAfterDrop(cause);
|
|
163442
|
+
}
|
|
163443
|
+
scheduleReconnectAfterDrop(trigger) {
|
|
163444
|
+
if (this.closeStarted || this.reconnecting)
|
|
163445
|
+
return;
|
|
163446
|
+
const promise2 = this.reconnectWithRetry(trigger).catch((err) => {
|
|
163447
|
+
if (!this.closeStarted)
|
|
163448
|
+
this.failFatal(err instanceof Error ? err : new SubcProviderError(String(err)));
|
|
163449
|
+
}).finally(() => {
|
|
163450
|
+
if (this.reconnecting === promise2)
|
|
163451
|
+
this.reconnecting = null;
|
|
163452
|
+
});
|
|
163453
|
+
this.reconnecting = promise2;
|
|
163454
|
+
}
|
|
163455
|
+
async reconnectWithRetry(_trigger) {
|
|
163456
|
+
let attempt = 0;
|
|
163457
|
+
let delay = this.opts.reconnectBackoff.baseMs;
|
|
163458
|
+
for (;; ) {
|
|
163459
|
+
if (this.closeStarted)
|
|
163460
|
+
throw new SubcProviderError("provider closed");
|
|
163461
|
+
attempt += 1;
|
|
163462
|
+
this.enqueueConnectionState({ state: "reconnecting", attempt });
|
|
163463
|
+
try {
|
|
163464
|
+
const opened = await SubcProvider.openConnection(this.opts);
|
|
163465
|
+
if (this.closeStarted) {
|
|
163466
|
+
opened.sock.close();
|
|
163467
|
+
throw new SubcProviderError("provider closed");
|
|
163468
|
+
}
|
|
163469
|
+
this.replaceConnection(opened);
|
|
163470
|
+
return;
|
|
163471
|
+
} catch (err) {
|
|
163472
|
+
if (this.closeStarted)
|
|
163473
|
+
throw err;
|
|
163474
|
+
if (!isProviderReconnectTransient(err))
|
|
163475
|
+
throw err;
|
|
163476
|
+
await this.opts.sleep(delay);
|
|
163477
|
+
delay = Math.min(delay * 2, this.opts.reconnectBackoff.capMs);
|
|
163478
|
+
}
|
|
163479
|
+
}
|
|
163480
|
+
}
|
|
163481
|
+
replaceConnection(opened) {
|
|
163482
|
+
this.sock.close();
|
|
163483
|
+
this.sock = opened.sock;
|
|
163484
|
+
this.currentConn = opened.conn;
|
|
163485
|
+
this.storage = opened.ack.storage;
|
|
163486
|
+
this.closedErr = null;
|
|
163487
|
+
this.connectionEpoch += 1;
|
|
163488
|
+
this.connectionToken = newConnectionToken();
|
|
163489
|
+
this.liveRoutes.clear();
|
|
163490
|
+
this.nextCorr = 1n;
|
|
163491
|
+
const generation = this.generation;
|
|
163492
|
+
this.readLoop(opened.sock, generation);
|
|
163493
|
+
this.scheduleRestored(generation, this.connectionEpoch);
|
|
163494
|
+
}
|
|
163495
|
+
scheduleRestored(generation, epoch) {
|
|
163496
|
+
if (!this.opts.onConnectionState)
|
|
163497
|
+
return;
|
|
163498
|
+
const token = ++this.restoredDebounceToken;
|
|
163499
|
+
this.opts.sleep(this.opts.restoredDebounceMs).then(() => {
|
|
163500
|
+
if (token === this.restoredDebounceToken && !this.closeStarted && this.sock && this.generation === generation && this.connectionEpoch === epoch) {
|
|
163501
|
+
this.enqueueConnectionState({ state: "restored", epoch });
|
|
163502
|
+
}
|
|
163503
|
+
}).catch((err) => {
|
|
163504
|
+
if (token === this.restoredDebounceToken && !this.closeStarted) {
|
|
163505
|
+
console.warn("SubcProvider restored debounce timer failed", err);
|
|
163506
|
+
}
|
|
163507
|
+
});
|
|
163508
|
+
}
|
|
163509
|
+
cancelRestoredDebounce() {
|
|
163510
|
+
this.restoredDebounceToken += 1;
|
|
163511
|
+
}
|
|
163512
|
+
enqueueConnectionState(event) {
|
|
163513
|
+
if (!this.opts.onConnectionState)
|
|
163514
|
+
return;
|
|
163515
|
+
this.stateQueue.push(event);
|
|
163516
|
+
if (!this.drainingStateQueue)
|
|
163517
|
+
this.drainConnectionStateQueue();
|
|
163518
|
+
}
|
|
163519
|
+
async drainConnectionStateQueue() {
|
|
163520
|
+
if (this.drainingStateQueue)
|
|
163521
|
+
return;
|
|
163522
|
+
this.drainingStateQueue = true;
|
|
163523
|
+
try {
|
|
163524
|
+
while (this.stateQueue.length > 0) {
|
|
163525
|
+
const event = this.stateQueue[0];
|
|
163526
|
+
try {
|
|
163527
|
+
await this.opts.onConnectionState?.(event);
|
|
163528
|
+
this.stateQueue.shift();
|
|
163529
|
+
} catch (err) {
|
|
163530
|
+
if (event.state === "restored") {
|
|
163531
|
+
console.warn("SubcProvider restored callback failed; retrying delivery", err);
|
|
163532
|
+
await pauseBeforeStateRetry();
|
|
163533
|
+
continue;
|
|
163534
|
+
}
|
|
163535
|
+
console.warn("SubcProvider connection-state callback failed", err);
|
|
163536
|
+
this.stateQueue.shift();
|
|
163537
|
+
}
|
|
163538
|
+
}
|
|
163539
|
+
} finally {
|
|
163540
|
+
this.drainingStateQueue = false;
|
|
163541
|
+
if (this.stateQueue.length > 0)
|
|
163542
|
+
this.drainConnectionStateQueue();
|
|
163543
|
+
}
|
|
163544
|
+
}
|
|
163545
|
+
isLiveHandle(handle) {
|
|
163546
|
+
return belongsToConnection(handle, this.connectionToken) && this.liveRoutes.get(handle.channel) === handle;
|
|
163547
|
+
}
|
|
163548
|
+
assertLiveHandle(handle) {
|
|
163549
|
+
if (!this.isLiveHandle(handle))
|
|
163550
|
+
throw new StaleRouteHandleError(handle);
|
|
163551
|
+
}
|
|
163552
|
+
allocateCorr() {
|
|
163553
|
+
const maximum = 0xffffffffffffffffn;
|
|
163554
|
+
if (this.nextCorr > maximum) {
|
|
163555
|
+
const error51 = new SubcProviderError("channel-0 correlation id allocator exhausted", "corr_exhausted");
|
|
163556
|
+
this.handleUnexpectedDrop(this.sock, this.generation, error51);
|
|
163557
|
+
throw error51;
|
|
163558
|
+
}
|
|
163559
|
+
const corr = this.nextCorr;
|
|
163560
|
+
this.nextCorr += 1n;
|
|
163561
|
+
return corr;
|
|
163562
|
+
}
|
|
163563
|
+
failFatal(err) {
|
|
163564
|
+
if (!this.closedErr)
|
|
163565
|
+
this.closedErr = err;
|
|
163566
|
+
this.closeStarted = true;
|
|
163567
|
+
this.cancelRestoredDebounce();
|
|
163568
|
+
this.abortAllInflight();
|
|
163569
|
+
this.sock.close();
|
|
163570
|
+
this.finishClosed();
|
|
163571
|
+
}
|
|
163572
|
+
finishClosed() {
|
|
163573
|
+
this.resolveClosed();
|
|
163574
|
+
}
|
|
163575
|
+
}
|
|
163576
|
+
function routeKey(handle, corr) {
|
|
163577
|
+
return `${handle.channel}:${handle.epoch}:${corr}`;
|
|
163578
|
+
}
|
|
163579
|
+
function providerErrorFromFrame(frame) {
|
|
163580
|
+
try {
|
|
163581
|
+
const body = parseJson(frame.body);
|
|
163582
|
+
return new SubcProviderError(body.message ?? "subc error", body.code);
|
|
163583
|
+
} catch {
|
|
163584
|
+
return new SubcProviderError(Buffer2.from(frame.body).toString("utf8") || "subc error");
|
|
163585
|
+
}
|
|
163586
|
+
}
|
|
163587
|
+
function launchNonce(opts) {
|
|
163588
|
+
const nonce = opts.launchNonce ?? process.env[SUBC_LAUNCH_NONCE_ENV2];
|
|
163589
|
+
return nonce && nonce.length > 0 ? nonce : undefined;
|
|
163590
|
+
}
|
|
163591
|
+
var SUBC_LAUNCH_NONCE_ENV2 = "SUBC_LAUNCH_NONCE";
|
|
163592
|
+
function normalizeProviderConnectOptions(opts) {
|
|
163593
|
+
return {
|
|
163594
|
+
connectionFile: opts.connectionFile,
|
|
163595
|
+
manifest: opts.manifest,
|
|
163596
|
+
handler: opts.handler,
|
|
163597
|
+
health: opts.health ?? (() => ({ status: "ok" })),
|
|
163598
|
+
handshakeTimeoutMs: opts.handshakeTimeoutMs,
|
|
163599
|
+
controlOps: opts.controlOps,
|
|
163600
|
+
onBind: opts.onBind,
|
|
163601
|
+
onBound: opts.onBound,
|
|
163602
|
+
onRouteGone: opts.onRouteGone,
|
|
163603
|
+
reconnectBackoff: opts.reconnectBackoff ?? DEFAULT_RECONNECT_BACKOFF,
|
|
163604
|
+
sleep: opts.sleep ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms))),
|
|
163605
|
+
restoredDebounceMs: opts.restoredDebounceMs ?? DEFAULT_RESTORED_DEBOUNCE_MS,
|
|
163606
|
+
onConnectionState: opts.onConnectionState,
|
|
163607
|
+
launchNonce: opts.launchNonce
|
|
163608
|
+
};
|
|
163609
|
+
}
|
|
163610
|
+
function normalizedControlOps(controlOps) {
|
|
163611
|
+
if (controlOps === null)
|
|
163612
|
+
return null;
|
|
163613
|
+
const merged = new Set(controlOps ?? []);
|
|
163614
|
+
merged.add(HEALTH_CHECK_OP);
|
|
163615
|
+
return [...merged];
|
|
163616
|
+
}
|
|
163617
|
+
function buildHelloFrame(opts) {
|
|
163618
|
+
const nonce = launchNonce(opts);
|
|
163619
|
+
return buildFrame(FrameType.Hello, controlFlags(), 0, 0, HELLO_CORR, encodeJson({
|
|
163620
|
+
manifest: normalizeManifest(opts.manifest),
|
|
163621
|
+
protocol_ver: PROTOCOL_VERSION,
|
|
163622
|
+
control_ops: normalizedControlOps(opts.controlOps),
|
|
163623
|
+
...nonce ? { launch_nonce: nonce } : {}
|
|
163624
|
+
}));
|
|
163625
|
+
}
|
|
163626
|
+
function isProviderReconnectTransient(err) {
|
|
163627
|
+
if (err instanceof SubcProviderError)
|
|
163628
|
+
return err.code === "duplicate_module_id";
|
|
163629
|
+
if (err instanceof SocketClosedError || err instanceof SocketTimeoutError)
|
|
163630
|
+
return true;
|
|
163631
|
+
if (err instanceof SocketWriteNotQueuedError || err instanceof SocketWriteQueuedError)
|
|
163632
|
+
return true;
|
|
163633
|
+
if (err instanceof AuthError)
|
|
163634
|
+
return true;
|
|
163635
|
+
if (err instanceof ConnectionFileError)
|
|
163636
|
+
return false;
|
|
163637
|
+
const code = errorCode2(err);
|
|
163638
|
+
return code === "ECONNREFUSED" || code === "ECONNRESET" || code === "EPIPE" || code === "ETIMEDOUT" || code === "ENOENT";
|
|
163639
|
+
}
|
|
163640
|
+
function errorCode2(err) {
|
|
163641
|
+
if (typeof err === "object" && err !== null && "code" in err) {
|
|
163642
|
+
const code = err.code;
|
|
163643
|
+
if (typeof code === "string")
|
|
163644
|
+
return code;
|
|
163645
|
+
}
|
|
163646
|
+
return;
|
|
163647
|
+
}
|
|
163648
|
+
async function pauseBeforeStateRetry() {
|
|
163649
|
+
await new Promise((resolve2) => setTimeout(resolve2, 0));
|
|
163650
|
+
}
|
|
163651
|
+
function controlFlags() {
|
|
163652
|
+
return buildFlags(false, Priority.Passive, false);
|
|
163653
|
+
}
|
|
163654
|
+
async function sendFrame(sock, frame) {
|
|
163655
|
+
await sock.write(encodeFrame(frame), Date.now() + WRITE_TIMEOUT_MS);
|
|
163656
|
+
}
|
|
163657
|
+
async function expectHelloAck(sock, deadline) {
|
|
163658
|
+
const frame = await sock.readFrame(deadline, deadline);
|
|
163659
|
+
switch (frame.header.ty) {
|
|
163660
|
+
case FrameType.HelloAck: {
|
|
163661
|
+
const ack = parseJson(frame.body);
|
|
163662
|
+
if (ack.negotiated_ver !== PROTOCOL_VERSION) {
|
|
163663
|
+
throw new SubcProviderError(`subc negotiated protocol ${ack.negotiated_ver}; expected exactly ${PROTOCOL_VERSION}`, "unsupported_version");
|
|
163664
|
+
}
|
|
163665
|
+
return ack;
|
|
163666
|
+
}
|
|
163667
|
+
case FrameType.Error: {
|
|
163668
|
+
const error51 = parseJson(frame.body);
|
|
163669
|
+
throw new SubcProviderError(`subc rejected HELLO: ${error51.code ?? "unknown"} — ${error51.message ?? "subc error"}`, error51.code);
|
|
163670
|
+
}
|
|
163671
|
+
default:
|
|
163672
|
+
throw new SubcProviderError(`unexpected frame ${FrameType[frame.header.ty]} awaiting HELLO_ACK`);
|
|
163673
|
+
}
|
|
163674
|
+
}
|
|
163675
|
+
function encodeJson(value) {
|
|
163676
|
+
return new Uint8Array(Buffer2.from(JSON.stringify(value), "utf8"));
|
|
163677
|
+
}
|
|
163678
|
+
function parseJson(bytes) {
|
|
163679
|
+
return JSON.parse(Buffer2.from(bytes).toString("utf8"));
|
|
163680
|
+
}
|
|
163681
|
+
function numberField(value, field) {
|
|
163682
|
+
if (typeof value !== "number" || !Number.isInteger(value)) {
|
|
163683
|
+
throw new SubcProviderError(`route.bind ${field} must be an integer`);
|
|
163684
|
+
}
|
|
163685
|
+
return value;
|
|
163686
|
+
}
|
|
163687
|
+
function bindRejection(decision) {
|
|
163688
|
+
if (decision === undefined || decision === true)
|
|
163689
|
+
return null;
|
|
163690
|
+
if (decision === false) {
|
|
163691
|
+
return { code: "route_rejected", message: "route.bind rejected by provider" };
|
|
163692
|
+
}
|
|
163693
|
+
if (decision.accept)
|
|
163694
|
+
return null;
|
|
163695
|
+
return {
|
|
163696
|
+
code: decision.code ?? "route_rejected",
|
|
163697
|
+
message: decision.message ?? "route.bind rejected by provider"
|
|
163698
|
+
};
|
|
163699
|
+
}
|
|
163700
|
+
function normalizeManifest(manifest) {
|
|
163701
|
+
return {
|
|
163702
|
+
module_id: manifest.module_id,
|
|
163703
|
+
module_version: manifest.module_version,
|
|
163704
|
+
protocol_ver: manifest.protocol_ver,
|
|
163705
|
+
trust_tier: manifest.trust_tier,
|
|
163706
|
+
provides: manifest.provides.map(normalizeProviderRole),
|
|
163707
|
+
consumes: manifest.consumes.map(normalizeConsumerRole),
|
|
163708
|
+
scheduled_tasks: manifest.scheduled_tasks.map(normalizeScheduledTask),
|
|
163709
|
+
bindings: {
|
|
163710
|
+
storage: {
|
|
163711
|
+
kind: manifest.bindings.storage.kind,
|
|
163712
|
+
scope: manifest.bindings.storage.scope,
|
|
163713
|
+
owns_schema: manifest.bindings.storage.owns_schema
|
|
163714
|
+
},
|
|
163715
|
+
vault_grants: manifest.bindings.vault_grants.map((grant) => ({
|
|
163716
|
+
secret: grant.secret,
|
|
163717
|
+
reason: grant.reason
|
|
163718
|
+
})),
|
|
163719
|
+
identity: {
|
|
163720
|
+
requires: [...manifest.bindings.identity.requires],
|
|
163721
|
+
optional: [...manifest.bindings.identity.optional]
|
|
163722
|
+
}
|
|
163723
|
+
}
|
|
163724
|
+
};
|
|
163725
|
+
}
|
|
163726
|
+
function normalizeProviderRole(role) {
|
|
163727
|
+
switch (role.role) {
|
|
163728
|
+
case "tool_provider":
|
|
163729
|
+
return {
|
|
163730
|
+
role: "tool_provider",
|
|
163731
|
+
tools: role.tools.map((tool) => ({
|
|
163732
|
+
name: tool.name,
|
|
163733
|
+
...tool.description === undefined ? {} : { description: tool.description },
|
|
163734
|
+
execution_mode: tool.execution_mode,
|
|
163735
|
+
schema: tool.schema
|
|
163736
|
+
})),
|
|
163737
|
+
identity_scope: [...role.identity_scope],
|
|
163738
|
+
concurrency: role.concurrency,
|
|
163739
|
+
emits_push: role.emits_push,
|
|
163740
|
+
sub_supervises: role.sub_supervises
|
|
163741
|
+
};
|
|
163742
|
+
case "pipeline_stage":
|
|
163743
|
+
return {
|
|
163744
|
+
role: "pipeline_stage",
|
|
163745
|
+
stage: role.stage,
|
|
163746
|
+
applies_to: {
|
|
163747
|
+
provider: role.applies_to.provider,
|
|
163748
|
+
model: role.applies_to.model
|
|
163749
|
+
},
|
|
163750
|
+
interface: role.interface,
|
|
163751
|
+
declares_frozen_floor: role.declares_frozen_floor,
|
|
163752
|
+
needs_signals: [...role.needs_signals],
|
|
163753
|
+
conformance_class: role.conformance_class
|
|
163754
|
+
};
|
|
163755
|
+
case "management_surface":
|
|
163756
|
+
return {
|
|
163757
|
+
role: "management_surface",
|
|
163758
|
+
operations: role.operations.map((operation) => ({
|
|
163759
|
+
name: operation.name,
|
|
163760
|
+
kind: operation.kind
|
|
163761
|
+
})),
|
|
163762
|
+
config_schema: role.config_schema,
|
|
163763
|
+
observability: role.observability.map((surface) => ({
|
|
163764
|
+
name: surface.name,
|
|
163765
|
+
kind: surface.kind
|
|
163766
|
+
})),
|
|
163767
|
+
identity_scope: [...role.identity_scope]
|
|
163768
|
+
};
|
|
163769
|
+
case "internal_service":
|
|
163770
|
+
return {
|
|
163771
|
+
role: "internal_service",
|
|
163772
|
+
service_id: role.service_id,
|
|
163773
|
+
transport: role.transport,
|
|
163774
|
+
agent_facing: role.agent_facing,
|
|
163775
|
+
operations: [...role.operations]
|
|
163776
|
+
};
|
|
163777
|
+
}
|
|
163778
|
+
}
|
|
163779
|
+
function normalizeConsumerRole(role) {
|
|
163780
|
+
switch (role.role) {
|
|
163781
|
+
case "tool_client":
|
|
163782
|
+
return { role: "tool_client", of: [...role.of] };
|
|
163783
|
+
case "llm_client":
|
|
163784
|
+
return { role: "llm_client", via: role.via, auth: role.auth };
|
|
163785
|
+
case "service_client":
|
|
163786
|
+
return { role: "service_client", of: [...role.of] };
|
|
163787
|
+
}
|
|
163788
|
+
}
|
|
163789
|
+
function normalizeScheduledTask(task) {
|
|
163790
|
+
return {
|
|
163791
|
+
task_id: task.task_id,
|
|
163792
|
+
eligibility: {
|
|
163793
|
+
cooldown: task.eligibility.cooldown,
|
|
163794
|
+
window: task.eligibility.window
|
|
163795
|
+
},
|
|
163796
|
+
lease_scope: task.lease_scope,
|
|
163797
|
+
renews_during_calls: task.renews_during_calls,
|
|
163798
|
+
toolset: [...task.toolset],
|
|
163799
|
+
model_policy: {
|
|
163800
|
+
tier: task.model_policy.tier,
|
|
163801
|
+
fallback_chain: [...task.model_policy.fallback_chain]
|
|
163802
|
+
},
|
|
163803
|
+
step_cap: task.step_cap,
|
|
163804
|
+
circuit_breaker: {
|
|
163805
|
+
identical_failures: task.circuit_breaker.identical_failures
|
|
163806
|
+
}
|
|
163807
|
+
};
|
|
163808
|
+
}
|
|
163809
|
+
// ../plugin/src/features/magic-context/memory/embedding-synapse.ts
|
|
163810
|
+
init_logger();
|
|
163811
|
+
var SYNAPSE_DEFAULT_MODEL = "gte-modernbert-base-f16";
|
|
163812
|
+
var SYNAPSE_MAX_INPUT_TOKENS = 8192;
|
|
163813
|
+
var SYNAPSE_DEFAULT_QUERY_TIMEOUT_MS = 3000;
|
|
163814
|
+
var SYNAPSE_DEFAULT_BATCH_TIMEOUT_MS = 120000;
|
|
163815
|
+
|
|
163816
|
+
class SynapseEmbeddingError extends Error {
|
|
163817
|
+
code;
|
|
163818
|
+
retryAfterMs;
|
|
163819
|
+
permanent;
|
|
163820
|
+
constructor(code, message, options) {
|
|
163821
|
+
super(message, options?.cause === undefined ? undefined : { cause: options.cause });
|
|
163822
|
+
this.name = "SynapseEmbeddingError";
|
|
163823
|
+
this.code = code;
|
|
163824
|
+
this.permanent = options?.permanent ?? isPermanentSynapseCode(code);
|
|
163825
|
+
this.retryAfterMs = options?.retryAfterMs ?? (this.permanent ? undefined : 100);
|
|
163826
|
+
}
|
|
163827
|
+
}
|
|
163828
|
+
function isPermanentSynapseCode(code) {
|
|
163829
|
+
return code === "artifact_invalid" || code === "substitution_rejected" || code === "not_certified" || code === "probe_required" || code === "idempotency_conflict" || code === "schema_violation";
|
|
163830
|
+
}
|
|
163831
|
+
function asRecord(value) {
|
|
163832
|
+
return value !== null && typeof value === "object" ? value : null;
|
|
163833
|
+
}
|
|
163834
|
+
function responseBody(value) {
|
|
163835
|
+
const record2 = asRecord(value);
|
|
163836
|
+
const result = asRecord(record2?.result);
|
|
163837
|
+
return result ? { ...record2, ...result } : record2 ?? {};
|
|
163838
|
+
}
|
|
163839
|
+
function readRetryAfter(value) {
|
|
163840
|
+
const record2 = asRecord(value);
|
|
163841
|
+
const candidate = record2?.retry_after_ms ?? record2?.retryAfterMs;
|
|
163842
|
+
if (typeof candidate !== "number" || !Number.isFinite(candidate) || candidate < 0)
|
|
163843
|
+
return;
|
|
163844
|
+
return Math.ceil(candidate);
|
|
163845
|
+
}
|
|
163846
|
+
function readErrorCode(value) {
|
|
163847
|
+
const record2 = asRecord(value);
|
|
163848
|
+
if (typeof record2?.code === "string")
|
|
163849
|
+
return record2.code;
|
|
163850
|
+
if (value instanceof Error && "code" in value && typeof value.code === "string") {
|
|
163851
|
+
return value.code;
|
|
163852
|
+
}
|
|
163853
|
+
return;
|
|
163854
|
+
}
|
|
163855
|
+
function classifyError(value) {
|
|
163856
|
+
if (value instanceof SynapseEmbeddingError)
|
|
163857
|
+
return value;
|
|
163858
|
+
const code = readErrorCode(value) ?? (value instanceof Error ? value.name : "transport");
|
|
163859
|
+
const normalized = code.toLowerCase();
|
|
163860
|
+
let mapped = "transport";
|
|
163861
|
+
if (normalized.includes("queue_full"))
|
|
163862
|
+
mapped = "queue_full";
|
|
163863
|
+
else if (normalized.includes("model_loading"))
|
|
163864
|
+
mapped = "model_loading";
|
|
163865
|
+
else if (normalized.includes("timeout") || normalized.includes("deadline"))
|
|
163866
|
+
mapped = "timeout";
|
|
163867
|
+
else if (normalized.includes("artifact_invalid"))
|
|
163868
|
+
mapped = "artifact_invalid";
|
|
163869
|
+
else if (normalized.includes("substitution"))
|
|
163870
|
+
mapped = "substitution_rejected";
|
|
163871
|
+
else if (normalized.includes("not_certified"))
|
|
163872
|
+
mapped = "not_certified";
|
|
163873
|
+
else if (normalized.includes("probe_required"))
|
|
163874
|
+
mapped = "probe_required";
|
|
163875
|
+
else if (normalized.includes("idempotency_conflict"))
|
|
163876
|
+
mapped = "idempotency_conflict";
|
|
163877
|
+
else if (normalized.includes("schema"))
|
|
163878
|
+
mapped = "schema_violation";
|
|
163879
|
+
else if (normalized.includes("module_restarted") || normalized.includes("module restarted"))
|
|
163880
|
+
mapped = "module_restarted";
|
|
163881
|
+
const message = value instanceof Error ? value.message : String(value);
|
|
163882
|
+
return new SynapseEmbeddingError(mapped, message, {
|
|
163883
|
+
retryAfterMs: readRetryAfter(value) ?? (isPermanentSynapseCode(mapped) ? undefined : 100),
|
|
163884
|
+
cause: value
|
|
163885
|
+
});
|
|
163886
|
+
}
|
|
163887
|
+
function wait(ms) {
|
|
163888
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
163889
|
+
}
|
|
163890
|
+
function sha256(value) {
|
|
163891
|
+
return createHash4("sha256").update(value).digest("hex");
|
|
163892
|
+
}
|
|
163893
|
+
function stableJson(value) {
|
|
163894
|
+
if (Array.isArray(value))
|
|
163895
|
+
return `[${value.map(stableJson).join(",")}]`;
|
|
163896
|
+
if (value && typeof value === "object") {
|
|
163897
|
+
return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => `${JSON.stringify(key)}:${stableJson(entry)}`).join(",")}}`;
|
|
163898
|
+
}
|
|
163899
|
+
return JSON.stringify(value);
|
|
163900
|
+
}
|
|
163901
|
+
function getSynapseLaneIdentity(model, fingerprint) {
|
|
163902
|
+
return `synapse:v1:${sha256(stableJson({ model, fingerprint }))}`;
|
|
163903
|
+
}
|
|
163904
|
+
function getSynapseBatchRequestKey(args) {
|
|
163905
|
+
return sha256(stableJson({
|
|
163906
|
+
op: "embed.batch",
|
|
163907
|
+
model: args.model,
|
|
163908
|
+
required_fingerprint: args.fingerprint,
|
|
163909
|
+
required_epoch: args.tableEpoch,
|
|
163910
|
+
allow_equivalent: false,
|
|
163911
|
+
accept_declared: false,
|
|
163912
|
+
ids: args.items.map((item) => item.id),
|
|
163913
|
+
content_sha256: args.items.map((item) => item.contentSha256)
|
|
163914
|
+
}));
|
|
163915
|
+
}
|
|
163916
|
+
function hashContent(text) {
|
|
163917
|
+
return sha256(text);
|
|
163918
|
+
}
|
|
163919
|
+
function extractCatalogEntries(value) {
|
|
163920
|
+
const body = responseBody(value);
|
|
163921
|
+
const raw = Array.isArray(body.models) ? body.models : Array.isArray(body.entries) ? body.entries : Array.isArray(value) ? value : [];
|
|
163922
|
+
const envelopeEpoch = typeof body.table_epoch === "number" && Number.isInteger(body.table_epoch) ? body.table_epoch : undefined;
|
|
163923
|
+
return raw.flatMap((entry) => {
|
|
163924
|
+
const record2 = asRecord(entry);
|
|
163925
|
+
if (!record2)
|
|
163926
|
+
return [];
|
|
163927
|
+
const model = typeof record2.model === "string" && record2.model.length > 0 ? record2.model : typeof record2.model_id === "string" ? record2.model_id : "";
|
|
163928
|
+
const fingerprint = typeof record2.fingerprint === "string" && record2.fingerprint.length > 0 ? record2.fingerprint : Array.isArray(record2.fingerprints) && typeof record2.fingerprints[0] === "string" ? record2.fingerprints[0] : "";
|
|
163929
|
+
const entryEpoch = record2.table_epoch ?? record2.tableEpoch;
|
|
163930
|
+
const tableEpoch = typeof entryEpoch === "number" && Number.isInteger(entryEpoch) ? entryEpoch : envelopeEpoch;
|
|
163931
|
+
const dims = record2.dims ?? record2.dimensions;
|
|
163932
|
+
if (model.length === 0 || fingerprint.length === 0 || typeof tableEpoch !== "number" || !Number.isInteger(tableEpoch)) {
|
|
163933
|
+
return [];
|
|
163934
|
+
}
|
|
163935
|
+
const recommendedBatch = record2.recommended_batch ?? record2.recommendedBatch;
|
|
163936
|
+
const state = typeof record2.state === "string" ? record2.state : undefined;
|
|
163937
|
+
return [
|
|
163938
|
+
{
|
|
163939
|
+
model,
|
|
163940
|
+
fingerprint,
|
|
163941
|
+
table_epoch: tableEpoch,
|
|
163942
|
+
...typeof dims === "number" && Number.isInteger(dims) && dims > 0 ? { dims } : {},
|
|
163943
|
+
...typeof recommendedBatch === "number" && recommendedBatch > 0 ? { recommended_batch: Math.floor(recommendedBatch) } : {},
|
|
163944
|
+
...record2.provenance !== undefined ? { provenance: record2.provenance } : {},
|
|
163945
|
+
...typeof record2.certified === "boolean" ? { certified: record2.certified } : {},
|
|
163946
|
+
...typeof record2.status === "string" ? { status: record2.status } : state ? { status: state } : {}
|
|
163947
|
+
}
|
|
163948
|
+
];
|
|
163949
|
+
});
|
|
163950
|
+
}
|
|
163951
|
+
function extractVector(value) {
|
|
163952
|
+
const body = responseBody(value);
|
|
163953
|
+
const fromVectors = Array.isArray(body.vectors) ? asRecord(body.vectors[0])?.vector : undefined;
|
|
163954
|
+
const raw = fromVectors ?? body.vector ?? body.embedding;
|
|
163955
|
+
if (!Array.isArray(raw) || raw.some((item) => typeof item !== "number" || !Number.isFinite(item))) {
|
|
163956
|
+
return null;
|
|
163957
|
+
}
|
|
163958
|
+
return { vector: Float32Array.from(raw), metadata: body };
|
|
163959
|
+
}
|
|
163960
|
+
function extractBatchItems(value) {
|
|
163961
|
+
const body = responseBody(value);
|
|
163962
|
+
const raw = Array.isArray(body.vectors) ? body.vectors : Array.isArray(body.items) ? body.items : Array.isArray(body.results) ? body.results : [];
|
|
163963
|
+
return raw.flatMap((item) => {
|
|
163964
|
+
const record2 = asRecord(item);
|
|
163965
|
+
return record2 ? [record2] : [];
|
|
163966
|
+
});
|
|
163967
|
+
}
|
|
163968
|
+
var sharedClient = null;
|
|
163969
|
+
var sharedClientFile = null;
|
|
163970
|
+
var sharedClientPromise = null;
|
|
163971
|
+
var factoryClients = new WeakMap;
|
|
163972
|
+
async function getSharedClient(options) {
|
|
163973
|
+
if (options.clientFactory) {
|
|
163974
|
+
let promise2 = factoryClients.get(options.clientFactory);
|
|
163975
|
+
if (!promise2) {
|
|
163976
|
+
promise2 = options.clientFactory();
|
|
163977
|
+
factoryClients.set(options.clientFactory, promise2);
|
|
163978
|
+
}
|
|
163979
|
+
return promise2;
|
|
163980
|
+
}
|
|
163981
|
+
if (sharedClient && sharedClientFile === options.connectionFile)
|
|
163982
|
+
return sharedClient;
|
|
163983
|
+
if (sharedClientPromise && sharedClientFile === options.connectionFile)
|
|
163984
|
+
return sharedClientPromise;
|
|
163985
|
+
sharedClientFile = options.connectionFile;
|
|
163986
|
+
sharedClientPromise = SubcClient.connect({ connectionFile: options.connectionFile }).then((client) => {
|
|
163987
|
+
sharedClient = client;
|
|
163988
|
+
return client;
|
|
163989
|
+
});
|
|
163990
|
+
return sharedClientPromise;
|
|
163991
|
+
}
|
|
163992
|
+
|
|
163993
|
+
class SynapseEmbeddingProvider {
|
|
163994
|
+
modelId;
|
|
163995
|
+
maxInputTokens = SYNAPSE_MAX_INPUT_TOKENS;
|
|
163996
|
+
metadata;
|
|
163997
|
+
options;
|
|
163998
|
+
client = null;
|
|
163999
|
+
initialized = false;
|
|
164000
|
+
initializing = null;
|
|
164001
|
+
permanentFailure = false;
|
|
164002
|
+
batchLimit = 16;
|
|
164003
|
+
constructor(options) {
|
|
164004
|
+
this.options = options;
|
|
164005
|
+
const model = options.model || SYNAPSE_DEFAULT_MODEL;
|
|
164006
|
+
const fingerprint = options.fingerprint ?? "";
|
|
164007
|
+
this.metadata = fingerprint && Number.isInteger(options.tableEpoch) && Number.isInteger(options.dims) && (options.dims ?? 0) > 0 ? {
|
|
164008
|
+
model,
|
|
164009
|
+
fingerprint,
|
|
164010
|
+
table_epoch: options.tableEpoch,
|
|
164011
|
+
dims: options.dims,
|
|
164012
|
+
...options.recommendedBatch ? { recommended_batch: Math.max(1, Math.floor(options.recommendedBatch)) } : {},
|
|
164013
|
+
...options.provenance !== undefined ? { provenance: options.provenance } : {},
|
|
164014
|
+
laneIdentity: getSynapseLaneIdentity(model, fingerprint)
|
|
164015
|
+
} : null;
|
|
164016
|
+
this.modelId = this.metadata?.laneIdentity ?? "synapse:v1:pending";
|
|
164017
|
+
this.batchLimit = this.metadata?.recommended_batch ?? 16;
|
|
164018
|
+
}
|
|
164019
|
+
static async discover(options) {
|
|
164020
|
+
const provider = new SynapseEmbeddingProvider(options);
|
|
164021
|
+
if (!await provider.initialize() || !provider.metadata) {
|
|
164022
|
+
throw new SynapseEmbeddingError("not_certified", "Synapse lane is not ready");
|
|
164023
|
+
}
|
|
164024
|
+
return provider.metadata;
|
|
164025
|
+
}
|
|
164026
|
+
async initialize() {
|
|
164027
|
+
if (this.initialized)
|
|
164028
|
+
return true;
|
|
164029
|
+
if (this.permanentFailure)
|
|
164030
|
+
return false;
|
|
164031
|
+
if (this.initializing)
|
|
164032
|
+
return this.initializing;
|
|
164033
|
+
this.initializing = (async () => {
|
|
164034
|
+
try {
|
|
164035
|
+
if (!this.options.clientFactory && !await connectionFileExists(this.options.connectionFile)) {
|
|
164036
|
+
throw new SynapseEmbeddingError("transport", `Synapse connection file is unavailable: ${this.options.connectionFile}`);
|
|
164037
|
+
}
|
|
164038
|
+
this.client = await getSharedClient(this.options);
|
|
164039
|
+
if (!this.metadata) {
|
|
164040
|
+
const discovered = await this.callWithRetry("models.list", {}, this.options.queryTimeoutMs ?? SYNAPSE_DEFAULT_QUERY_TIMEOUT_MS, false);
|
|
164041
|
+
const entries = extractCatalogEntries(discovered);
|
|
164042
|
+
const requested = this.options.model?.trim() || SYNAPSE_DEFAULT_MODEL;
|
|
164043
|
+
const entry = entries.find((candidate) => candidate.model === requested);
|
|
164044
|
+
if (!entry) {
|
|
164045
|
+
throw new SynapseEmbeddingError("artifact_invalid", `Synapse models.list did not return requested model ${requested}`);
|
|
164046
|
+
}
|
|
164047
|
+
if (entry.certified === false || entry.status === "not_certified") {
|
|
164048
|
+
throw new SynapseEmbeddingError("not_certified", `Synapse model ${entry.model} is not certified`);
|
|
164049
|
+
}
|
|
164050
|
+
const metadata = {
|
|
164051
|
+
...entry,
|
|
164052
|
+
laneIdentity: getSynapseLaneIdentity(entry.model, entry.fingerprint)
|
|
164053
|
+
};
|
|
164054
|
+
this.metadata = metadata;
|
|
164055
|
+
this.modelId = metadata.laneIdentity;
|
|
164056
|
+
this.batchLimit = metadata.recommended_batch ?? this.batchLimit;
|
|
164057
|
+
}
|
|
164058
|
+
this.initialized = true;
|
|
164059
|
+
return true;
|
|
164060
|
+
} catch (error51) {
|
|
164061
|
+
const classified = classifyError(error51);
|
|
164062
|
+
if (classified.permanent) {
|
|
164063
|
+
this.permanentFailure = true;
|
|
164064
|
+
log(`[magic-context] Synapse lane disabled: ${classified.code}: ${classified.message}`);
|
|
164065
|
+
} else {
|
|
164066
|
+
log(`[magic-context] Synapse lane unavailable: ${classified.message}`);
|
|
164067
|
+
}
|
|
164068
|
+
this.initialized = false;
|
|
164069
|
+
return false;
|
|
164070
|
+
} finally {
|
|
164071
|
+
this.initializing = null;
|
|
164072
|
+
}
|
|
164073
|
+
})();
|
|
164074
|
+
return this.initializing;
|
|
164075
|
+
}
|
|
164076
|
+
async embed(text, signal) {
|
|
164077
|
+
if (!await this.initialize() || signal?.aborted || !this.metadata)
|
|
164078
|
+
return null;
|
|
164079
|
+
try {
|
|
164080
|
+
const value = await this.callWithRetry("embed.query", this.requestConstraints({
|
|
164081
|
+
text,
|
|
164082
|
+
deadline_ms: this.options.queryTimeoutMs ?? SYNAPSE_DEFAULT_QUERY_TIMEOUT_MS
|
|
164083
|
+
}), this.options.queryTimeoutMs ?? SYNAPSE_DEFAULT_QUERY_TIMEOUT_MS, true, signal);
|
|
164084
|
+
const extracted = extractVector(value);
|
|
164085
|
+
if (!extracted)
|
|
164086
|
+
throw new SynapseEmbeddingError("schema_violation", "Synapse query returned no vector");
|
|
164087
|
+
this.validateResponse(extracted.metadata, extracted.vector.length);
|
|
164088
|
+
return extracted.vector;
|
|
164089
|
+
} catch (error51) {
|
|
164090
|
+
this.logCallFailure(error51, "embed.query");
|
|
164091
|
+
return null;
|
|
164092
|
+
}
|
|
164093
|
+
}
|
|
164094
|
+
async embedBatch(texts, signal) {
|
|
164095
|
+
if (texts.length === 0)
|
|
164096
|
+
return [];
|
|
164097
|
+
const items = texts.map((text, index) => ({
|
|
164098
|
+
id: `item:${index}`,
|
|
164099
|
+
text,
|
|
164100
|
+
contentSha256: hashContent(text)
|
|
164101
|
+
}));
|
|
164102
|
+
const map2 = await this.embedItems(items, signal);
|
|
164103
|
+
return items.map((item) => map2.get(item.id) ?? null);
|
|
164104
|
+
}
|
|
164105
|
+
async embedItems(items, signal) {
|
|
164106
|
+
const output = new Map;
|
|
164107
|
+
if (items.length === 0 || !await this.initialize() || !this.metadata || signal?.aborted) {
|
|
164108
|
+
return output;
|
|
164109
|
+
}
|
|
164110
|
+
for (let start = 0;start < items.length; start += this.batchLimit) {
|
|
164111
|
+
if (signal?.aborted || this.permanentFailure)
|
|
164112
|
+
break;
|
|
164113
|
+
const page = items.slice(start, start + this.batchLimit);
|
|
164114
|
+
try {
|
|
164115
|
+
const requestKey = this.requestKey(page);
|
|
164116
|
+
let body = {};
|
|
164117
|
+
let restarted = false;
|
|
164118
|
+
for (;; ) {
|
|
164119
|
+
try {
|
|
164120
|
+
body = await this.callWithRetry("embed.batch", this.batchRequest(page, requestKey), this.options.batchTimeoutMs ?? SYNAPSE_DEFAULT_BATCH_TIMEOUT_MS, true, signal);
|
|
164121
|
+
const first = responseBody(body);
|
|
164122
|
+
const jobId = typeof first.job_id === "string" ? first.job_id : null;
|
|
164123
|
+
if (jobId)
|
|
164124
|
+
body = await this.pollBatch(jobId, requestKey, signal);
|
|
164125
|
+
break;
|
|
164126
|
+
} catch (error51) {
|
|
164127
|
+
const classified = classifyError(error51);
|
|
164128
|
+
if (classified.code !== "module_restarted" || restarted)
|
|
164129
|
+
throw classified;
|
|
164130
|
+
restarted = true;
|
|
164131
|
+
}
|
|
164132
|
+
}
|
|
164133
|
+
const batchEnvelope = responseBody(body);
|
|
164134
|
+
for (const item of extractBatchItems(body)) {
|
|
164135
|
+
const id = typeof item.id === "string" ? item.id : "";
|
|
164136
|
+
const vector = item.vector ?? item.embedding;
|
|
164137
|
+
if (!id || !Array.isArray(vector)) {
|
|
164138
|
+
throw new SynapseEmbeddingError("schema_violation", "Synapse batch item is malformed");
|
|
164139
|
+
}
|
|
164140
|
+
const expected = page.find((candidate) => candidate.id === id);
|
|
164141
|
+
if (!expected) {
|
|
164142
|
+
throw new SynapseEmbeddingError("schema_violation", `Synapse returned unknown item ${id}`);
|
|
164143
|
+
}
|
|
164144
|
+
if (typeof item.content_sha256 === "string" && item.content_sha256 !== expected.contentSha256) {
|
|
164145
|
+
throw new SynapseEmbeddingError("artifact_invalid", `Synapse content hash mismatch for item ${id}`);
|
|
164146
|
+
}
|
|
164147
|
+
const vectorArray = Float32Array.from(vector);
|
|
164148
|
+
this.validateResponse({ ...batchEnvelope, ...item }, vectorArray.length);
|
|
164149
|
+
output.set(id, vectorArray);
|
|
164150
|
+
}
|
|
164151
|
+
} catch (error51) {
|
|
164152
|
+
const classified = classifyError(error51);
|
|
164153
|
+
this.logCallFailure(classified, "embed.batch");
|
|
164154
|
+
if (classified.code === "idempotency_conflict")
|
|
164155
|
+
throw classified;
|
|
164156
|
+
if (classified.permanent) {
|
|
164157
|
+
this.permanentFailure = true;
|
|
164158
|
+
this.initialized = false;
|
|
164159
|
+
break;
|
|
164160
|
+
}
|
|
164161
|
+
}
|
|
164162
|
+
}
|
|
164163
|
+
return output;
|
|
164164
|
+
}
|
|
164165
|
+
async dispose() {
|
|
164166
|
+
this.initialized = false;
|
|
164167
|
+
this.client = null;
|
|
164168
|
+
}
|
|
164169
|
+
isLoaded() {
|
|
164170
|
+
return this.initialized;
|
|
164171
|
+
}
|
|
164172
|
+
requestConstraints(extra) {
|
|
164173
|
+
const metadata = this.metadata;
|
|
164174
|
+
if (!metadata)
|
|
164175
|
+
return extra;
|
|
164176
|
+
return {
|
|
164177
|
+
...extra,
|
|
164178
|
+
model: metadata.model,
|
|
164179
|
+
required_fingerprint: metadata.fingerprint,
|
|
164180
|
+
required_epoch: metadata.table_epoch,
|
|
164181
|
+
allow_equivalent: false,
|
|
164182
|
+
accept_declared: false
|
|
164183
|
+
};
|
|
164184
|
+
}
|
|
164185
|
+
batchRequest(items, requestKey) {
|
|
164186
|
+
return this.requestConstraints({
|
|
164187
|
+
items: items.map((item) => ({
|
|
164188
|
+
id: item.id,
|
|
164189
|
+
text: item.text,
|
|
164190
|
+
content_sha256: item.contentSha256
|
|
164191
|
+
})),
|
|
164192
|
+
request_key: requestKey
|
|
164193
|
+
});
|
|
164194
|
+
}
|
|
164195
|
+
requestKey(items) {
|
|
164196
|
+
if (!this.metadata)
|
|
164197
|
+
throw new SynapseEmbeddingError("transport", "Synapse metadata is unavailable");
|
|
164198
|
+
return getSynapseBatchRequestKey({
|
|
164199
|
+
model: this.metadata.model,
|
|
164200
|
+
fingerprint: this.metadata.fingerprint,
|
|
164201
|
+
tableEpoch: this.metadata.table_epoch,
|
|
164202
|
+
items
|
|
164203
|
+
});
|
|
164204
|
+
}
|
|
164205
|
+
async pollBatch(jobId, requestKey, signal) {
|
|
164206
|
+
let cursor = null;
|
|
164207
|
+
const allItems = [];
|
|
164208
|
+
for (;; ) {
|
|
164209
|
+
if (signal?.aborted)
|
|
164210
|
+
return {};
|
|
164211
|
+
const body = await this.callWithRetry("embed.result", this.requestConstraints({
|
|
164212
|
+
job_id: jobId,
|
|
164213
|
+
cursor,
|
|
164214
|
+
request_key: requestKey
|
|
164215
|
+
}), this.options.batchTimeoutMs ?? SYNAPSE_DEFAULT_BATCH_TIMEOUT_MS, true, signal);
|
|
164216
|
+
const parsed = responseBody(body);
|
|
164217
|
+
const items = extractBatchItems(body);
|
|
164218
|
+
allItems.push(...items);
|
|
164219
|
+
const nextCursor = parsed.next_cursor ?? parsed.cursor;
|
|
164220
|
+
const done = parsed.done === true || parsed.complete === true || nextCursor === undefined || nextCursor === null;
|
|
164221
|
+
if (done)
|
|
164222
|
+
return { ...parsed, items: allItems };
|
|
164223
|
+
cursor = nextCursor;
|
|
164224
|
+
}
|
|
164225
|
+
}
|
|
164226
|
+
async callWithRetry(method, params, timeoutMs, retryEmbeddings, signal) {
|
|
164227
|
+
let attempt = 0;
|
|
164228
|
+
for (;; ) {
|
|
164229
|
+
if (signal?.aborted)
|
|
164230
|
+
throw new SynapseEmbeddingError("transport", "Synapse request aborted");
|
|
164231
|
+
try {
|
|
164232
|
+
if (!this.client)
|
|
164233
|
+
throw new SynapseEmbeddingError("transport", "Synapse client is unavailable");
|
|
164234
|
+
return await this.client.call(this.options.moduleId ?? "synapse", method, params, {
|
|
164235
|
+
timeoutMs,
|
|
164236
|
+
targetKind: "management_surface",
|
|
164237
|
+
identity: {
|
|
164238
|
+
project_root: this.options.projectRoot,
|
|
164239
|
+
harness: getHarness(),
|
|
164240
|
+
session: this.options.session
|
|
164241
|
+
}
|
|
164242
|
+
});
|
|
164243
|
+
} catch (error51) {
|
|
164244
|
+
const classified = classifyError(error51);
|
|
164245
|
+
if (classified.code === "idempotency_conflict")
|
|
164246
|
+
throw classified;
|
|
164247
|
+
const outcomeUnknown = error51 instanceof SubcCallError && error51.kind === "outcome_unknown";
|
|
164248
|
+
const retryable = !classified.permanent && (retryEmbeddings || !outcomeUnknown);
|
|
164249
|
+
if (!retryable || attempt >= 3)
|
|
164250
|
+
throw classified;
|
|
164251
|
+
const delay = classified.retryAfterMs ?? Math.min(2000, 100 * 2 ** attempt);
|
|
164252
|
+
attempt += 1;
|
|
164253
|
+
await wait(delay);
|
|
164254
|
+
}
|
|
164255
|
+
}
|
|
164256
|
+
}
|
|
164257
|
+
validateResponse(body, dims) {
|
|
164258
|
+
const metadata = this.metadata;
|
|
164259
|
+
if (!metadata) {
|
|
164260
|
+
throw new SynapseEmbeddingError("artifact_invalid", "Synapse lane metadata missing");
|
|
164261
|
+
}
|
|
164262
|
+
if (metadata.dims === undefined) {
|
|
164263
|
+
const envelopeDims = body.dims;
|
|
164264
|
+
if (typeof envelopeDims === "number" && envelopeDims !== dims) {
|
|
164265
|
+
throw new SynapseEmbeddingError("artifact_invalid", `Synapse envelope declares ${envelopeDims} dimensions but the vector has ${dims}`);
|
|
164266
|
+
}
|
|
164267
|
+
metadata.dims = dims;
|
|
164268
|
+
}
|
|
164269
|
+
if (dims !== metadata.dims) {
|
|
164270
|
+
throw new SynapseEmbeddingError("artifact_invalid", `Synapse returned ${dims} dimensions, expected ${metadata.dims}`);
|
|
164271
|
+
}
|
|
164272
|
+
const fingerprint = body.fingerprint ?? body.served_fingerprint;
|
|
164273
|
+
if (typeof fingerprint !== "string") {
|
|
164274
|
+
throw new SynapseEmbeddingError("artifact_invalid", "Synapse response omitted the served fingerprint");
|
|
164275
|
+
}
|
|
164276
|
+
if (fingerprint !== metadata.fingerprint) {
|
|
164277
|
+
throw new SynapseEmbeddingError("substitution_rejected", `Synapse fingerprint changed from ${metadata.fingerprint} to ${fingerprint}`);
|
|
164278
|
+
}
|
|
164279
|
+
const epoch = body.table_epoch ?? body.tableEpoch;
|
|
164280
|
+
if (typeof epoch !== "number") {
|
|
164281
|
+
throw new SynapseEmbeddingError("artifact_invalid", "Synapse response omitted the served table epoch");
|
|
164282
|
+
}
|
|
164283
|
+
if (epoch !== metadata.table_epoch) {
|
|
164284
|
+
throw new SynapseEmbeddingError("substitution_rejected", `Synapse table epoch changed from ${metadata.table_epoch} to ${epoch}`);
|
|
164285
|
+
}
|
|
164286
|
+
}
|
|
164287
|
+
logCallFailure(error51, operation) {
|
|
164288
|
+
const classified = classifyError(error51);
|
|
164289
|
+
if (classified.permanent)
|
|
164290
|
+
this.permanentFailure = true;
|
|
164291
|
+
const suffix = classified.retryAfterMs === undefined ? "" : ` retry_after_ms=${classified.retryAfterMs}`;
|
|
164292
|
+
log(`[magic-context] Synapse ${operation} failed: ${classified.code}${suffix}: ${classified.message}`);
|
|
164293
|
+
}
|
|
164294
|
+
}
|
|
164295
|
+
|
|
164296
|
+
// ../plugin/src/features/magic-context/memory/normalize-hash.ts
|
|
164297
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
161367
164298
|
function normalizeMemoryContent(content) {
|
|
161368
164299
|
return content.toLowerCase().replace(/\s+/g, " ").trim();
|
|
161369
164300
|
}
|
|
161370
164301
|
function computeNormalizedHash(content) {
|
|
161371
164302
|
const normalized = normalizeMemoryContent(content);
|
|
161372
|
-
return
|
|
164303
|
+
return createHash5("md5").update(normalized).digest("hex");
|
|
161373
164304
|
}
|
|
161374
164305
|
|
|
161375
164306
|
// ../plugin/src/features/magic-context/memory/embedding-identity.ts
|
|
@@ -161380,6 +164311,15 @@ function getEmbeddingProviderIdentity(config2) {
|
|
|
161380
164311
|
if (config2.provider === "off") {
|
|
161381
164312
|
return "embedding-provider:off";
|
|
161382
164313
|
}
|
|
164314
|
+
if (config2.provider === "synapse") {
|
|
164315
|
+
const resolved = config2;
|
|
164316
|
+
if (!resolved.model || !resolved.synapse_fingerprint)
|
|
164317
|
+
return "synapse:v1:pending";
|
|
164318
|
+
return getSynapseLaneIdentity(resolved.model, resolved.synapse_fingerprint);
|
|
164319
|
+
}
|
|
164320
|
+
if (config2.provider !== "local" && config2.provider !== "openai-compatible") {
|
|
164321
|
+
throw new Error("Unknown embedding provider");
|
|
164322
|
+
}
|
|
161383
164323
|
const truncate = config2.provider === "openai-compatible" ? config2.truncate?.trim() : undefined;
|
|
161384
164324
|
const identityInput = config2.provider === "openai-compatible" ? {
|
|
161385
164325
|
provider: "openai-compatible",
|
|
@@ -162169,7 +165109,7 @@ function getMemoryEmbedCoverage(db, projectPath, modelId) {
|
|
|
162169
165109
|
}
|
|
162170
165110
|
|
|
162171
165111
|
// ../plugin/src/features/magic-context/project-embedding-registry.ts
|
|
162172
|
-
import { createHash as
|
|
165112
|
+
import { createHash as createHash7, randomUUID } from "node:crypto";
|
|
162173
165113
|
init_logger();
|
|
162174
165114
|
|
|
162175
165115
|
// ../plugin/src/features/magic-context/git-commits/storage-git-commit-embeddings.ts
|
|
@@ -162622,6 +165562,37 @@ function repairMisScopedCompartmentChunkEmbeddingsForProject(db, projectPath) {
|
|
|
162622
165562
|
return getRepairProjectChunkProjectStatement(db).run(projectPath, projectPath).changes;
|
|
162623
165563
|
}
|
|
162624
165564
|
|
|
165565
|
+
// ../plugin/src/features/magic-context/storage-embedding-measurements.ts
|
|
165566
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
165567
|
+
function normalizedQueryHash(query) {
|
|
165568
|
+
const normalized = query.trim().replace(/\s+/g, " ").toLowerCase();
|
|
165569
|
+
return createHash6("sha256").update(normalized).digest("hex");
|
|
165570
|
+
}
|
|
165571
|
+
function recordEmbeddingMeasurement(db, input) {
|
|
165572
|
+
const queryTextHash = normalizedQueryHash(input.queryText);
|
|
165573
|
+
const dedupKey = queryTextHash;
|
|
165574
|
+
const result = db.prepare(`INSERT OR IGNORE INTO embedding_measurement_corpus
|
|
165575
|
+
(session_id, project_path, dedup_key, cohort_key, query_text_hash,
|
|
165576
|
+
primary_result_ids_json, shadow_result_ids_json, primary_latency_ms, shadow_latency_ms,
|
|
165577
|
+
primary_failed, shadow_failed, primary_model_id, shadow_model_id,
|
|
165578
|
+
primary_fingerprint, shadow_fingerprint, primary_epoch, shadow_epoch,
|
|
165579
|
+
corpus_hash, coverage_json, created_at)
|
|
165580
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(input.sessionId, input.projectPath, dedupKey, input.cohortKey, queryTextHash, JSON.stringify(input.primaryResultIds.slice(0, 10)), JSON.stringify(input.shadowResultIds.slice(0, 10)), input.primaryLatencyMs, input.shadowLatencyMs, input.primaryFailed ? 1 : 0, input.shadowFailed ? 1 : 0, input.primaryModelId, input.shadowModelId, input.primaryFingerprint, input.shadowFingerprint, input.primaryEpoch, input.shadowEpoch, input.corpusHash, JSON.stringify(input.coverage), Date.now());
|
|
165581
|
+
return result.changes > 0;
|
|
165582
|
+
}
|
|
165583
|
+
function beginSynapseBatchLedger(db, input) {
|
|
165584
|
+
const now = Date.now();
|
|
165585
|
+
db.prepare(`INSERT INTO synapse_batch_ledger
|
|
165586
|
+
(session_id, project_path, scope, manifest_json, request_key, status, created_at, updated_at)
|
|
165587
|
+
VALUES (?, ?, ?, ?, ?, 'pending', ?, ?)
|
|
165588
|
+
ON CONFLICT(session_id, request_key) DO UPDATE SET
|
|
165589
|
+
manifest_json = excluded.manifest_json,
|
|
165590
|
+
updated_at = excluded.updated_at`).run(input.sessionId, input.projectPath, input.scope, JSON.stringify(input.manifest), input.requestKey, now, now);
|
|
165591
|
+
}
|
|
165592
|
+
function finishSynapseBatchLedger(db, sessionId, requestKey, status) {
|
|
165593
|
+
db.prepare("UPDATE synapse_batch_ledger SET status = ?, updated_at = ? WHERE session_id = ? AND request_key = ?").run(status, Date.now(), sessionId, requestKey);
|
|
165594
|
+
}
|
|
165595
|
+
|
|
162625
165596
|
// ../plugin/src/features/magic-context/project-embedding-registry.ts
|
|
162626
165597
|
var OFF_PROVIDER_IDENTITY = "embedding-provider:off";
|
|
162627
165598
|
var SWEEP_MAX_WALL_CLOCK_MS = 10 * 60 * 1000;
|
|
@@ -162637,6 +165608,12 @@ var EMBED_SLICE_RETRY_BASE_MS = 250;
|
|
|
162637
165608
|
var EMBED_SLOW_FAILURE_NO_RETRY_MS = 1e4;
|
|
162638
165609
|
var MAX_CONSECUTIVE_FAILED_BATCHES = 3;
|
|
162639
165610
|
var projectRegistrations = new Map;
|
|
165611
|
+
var shadowRegistrations = new Map;
|
|
165612
|
+
var shadowQueue = [];
|
|
165613
|
+
var shadowWorker = null;
|
|
165614
|
+
var SHADOW_MAX_ITEMS_PER_TICK = 64;
|
|
165615
|
+
var SHADOW_MAX_BYTES_PER_TICK = 512 * 1024;
|
|
165616
|
+
var SHADOW_MAX_WALL_CLOCK_MS = 2000;
|
|
162640
165617
|
var loadUnembeddedMemoriesStatements = new WeakMap;
|
|
162641
165618
|
var upsertActiveIdentityStatements = new WeakMap;
|
|
162642
165619
|
var backfillActiveIdentityStatements = new Map;
|
|
@@ -162648,6 +165625,35 @@ function markProjectLoadUntrusted(projectIdentity) {
|
|
|
162648
165625
|
untrustedLoadProjects.add(projectIdentity);
|
|
162649
165626
|
}
|
|
162650
165627
|
var testProviderFactory = null;
|
|
165628
|
+
function synapseConfigFields(config2) {
|
|
165629
|
+
const raw = config2;
|
|
165630
|
+
return {
|
|
165631
|
+
...typeof raw.model === "string" ? { model: raw.model } : {},
|
|
165632
|
+
...typeof raw.synapse_fingerprint === "string" ? { fingerprint: raw.synapse_fingerprint } : {},
|
|
165633
|
+
...typeof raw.synapse_table_epoch === "number" ? { tableEpoch: raw.synapse_table_epoch } : {},
|
|
165634
|
+
...typeof raw.synapse_dims === "number" ? { dims: raw.synapse_dims } : {},
|
|
165635
|
+
...raw.synapse_provenance !== undefined ? { provenance: raw.synapse_provenance } : {}
|
|
165636
|
+
};
|
|
165637
|
+
}
|
|
165638
|
+
function persistPrimaryDescriptor(db, registration) {
|
|
165639
|
+
const descriptorTable = db.prepare("SELECT 1 AS present FROM sqlite_master WHERE type = 'table' AND name = 'embedding_registrations'").get();
|
|
165640
|
+
if (!descriptorTable)
|
|
165641
|
+
return;
|
|
165642
|
+
const fields = synapseConfigFields(registration.config);
|
|
165643
|
+
db.prepare(`INSERT INTO embedding_registrations
|
|
165644
|
+
(project_path, provider_identity, model_id, chunk_model_id, fingerprint, table_epoch, dims, provenance_json, generation, updated_at)
|
|
165645
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
165646
|
+
ON CONFLICT(project_path) DO UPDATE SET
|
|
165647
|
+
provider_identity = excluded.provider_identity,
|
|
165648
|
+
model_id = excluded.model_id,
|
|
165649
|
+
chunk_model_id = excluded.chunk_model_id,
|
|
165650
|
+
fingerprint = excluded.fingerprint,
|
|
165651
|
+
table_epoch = excluded.table_epoch,
|
|
165652
|
+
dims = excluded.dims,
|
|
165653
|
+
provenance_json = excluded.provenance_json,
|
|
165654
|
+
generation = excluded.generation,
|
|
165655
|
+
updated_at = excluded.updated_at`).run(registration.projectIdentity, registration.providerIdentity, registration.modelId, registration.chunkModelId, fields.fingerprint ?? "", fields.tableEpoch ?? 0, fields.dims ?? 0, JSON.stringify(fields.provenance ?? {}), registration.generation, Date.now());
|
|
165656
|
+
}
|
|
162651
165657
|
function resolveEmbeddingConfig(config2) {
|
|
162652
165658
|
if (!config2 || config2.provider === "local") {
|
|
162653
165659
|
return {
|
|
@@ -162676,9 +165682,26 @@ function resolveEmbeddingConfig(config2) {
|
|
|
162676
165682
|
} : {}
|
|
162677
165683
|
};
|
|
162678
165684
|
}
|
|
162679
|
-
|
|
165685
|
+
if (config2.provider === "off") {
|
|
165686
|
+
return { provider: "off" };
|
|
165687
|
+
}
|
|
165688
|
+
if (config2.provider === "synapse") {
|
|
165689
|
+
const synapse = config2;
|
|
165690
|
+
return {
|
|
165691
|
+
provider: "synapse",
|
|
165692
|
+
model: synapse.model?.trim() || "gte-modernbert-base-f16",
|
|
165693
|
+
max_input_tokens: SYNAPSE_MAX_INPUT_TOKENS,
|
|
165694
|
+
...synapse.synapse_connection_file ? { synapse_connection_file: synapse.synapse_connection_file } : {},
|
|
165695
|
+
...synapse.synapse_fingerprint ? { synapse_fingerprint: synapse.synapse_fingerprint } : {},
|
|
165696
|
+
...typeof synapse.synapse_table_epoch === "number" ? { synapse_table_epoch: synapse.synapse_table_epoch } : {},
|
|
165697
|
+
...typeof synapse.synapse_dims === "number" ? { synapse_dims: synapse.synapse_dims } : {},
|
|
165698
|
+
...typeof synapse.synapse_recommended_batch === "number" ? { synapse_recommended_batch: synapse.synapse_recommended_batch } : {},
|
|
165699
|
+
...synapse.synapse_provenance !== undefined ? { synapse_provenance: synapse.synapse_provenance } : {}
|
|
165700
|
+
};
|
|
165701
|
+
}
|
|
165702
|
+
throw new Error("Unknown embedding provider");
|
|
162680
165703
|
}
|
|
162681
|
-
function createProvider(config2) {
|
|
165704
|
+
function createProvider(config2, context) {
|
|
162682
165705
|
if (testProviderFactory) {
|
|
162683
165706
|
return testProviderFactory(config2);
|
|
162684
165707
|
}
|
|
@@ -162696,7 +165719,24 @@ function createProvider(config2) {
|
|
|
162696
165719
|
maxInputTokens: config2.max_input_tokens
|
|
162697
165720
|
});
|
|
162698
165721
|
}
|
|
162699
|
-
|
|
165722
|
+
if (config2.provider === "local") {
|
|
165723
|
+
return new LocalEmbeddingProvider(config2.model, config2.max_input_tokens);
|
|
165724
|
+
}
|
|
165725
|
+
if (config2.provider === "synapse") {
|
|
165726
|
+
const synapse = config2;
|
|
165727
|
+
return new SynapseEmbeddingProvider({
|
|
165728
|
+
connectionFile: synapse.synapse_connection_file ?? "",
|
|
165729
|
+
projectRoot: context?.projectRoot ?? "",
|
|
165730
|
+
session: context?.session ?? "embedding",
|
|
165731
|
+
model: synapse.model,
|
|
165732
|
+
fingerprint: synapse.synapse_fingerprint,
|
|
165733
|
+
tableEpoch: synapse.synapse_table_epoch,
|
|
165734
|
+
dims: synapse.synapse_dims,
|
|
165735
|
+
recommendedBatch: synapse.synapse_recommended_batch,
|
|
165736
|
+
provenance: synapse.synapse_provenance
|
|
165737
|
+
});
|
|
165738
|
+
}
|
|
165739
|
+
throw new Error("Unknown embedding provider");
|
|
162700
165740
|
}
|
|
162701
165741
|
function stableStringify2(value) {
|
|
162702
165742
|
if (Array.isArray(value)) {
|
|
@@ -162709,7 +165749,10 @@ function stableStringify2(value) {
|
|
|
162709
165749
|
return JSON.stringify(value);
|
|
162710
165750
|
}
|
|
162711
165751
|
function sha256Prefix(value, length = 16) {
|
|
162712
|
-
return
|
|
165752
|
+
return createHash7("sha256").update(value).digest("hex").slice(0, length);
|
|
165753
|
+
}
|
|
165754
|
+
function contentSha256(value) {
|
|
165755
|
+
return createHash7("sha256").update(value).digest("hex");
|
|
162713
165756
|
}
|
|
162714
165757
|
function getRuntimeFingerprint(config2) {
|
|
162715
165758
|
if (config2.provider === "off") {
|
|
@@ -162736,6 +165779,7 @@ function snapshotFor(registration) {
|
|
|
162736
165779
|
const providerIsOn = registration.providerIdentity !== OFF_PROVIDER_IDENTITY;
|
|
162737
165780
|
const enabled = !registration.observationMode && providerIsOn && registration.features.memoryEnabled;
|
|
162738
165781
|
const gitCommitEnabled = !registration.observationMode && providerIsOn && registration.features.gitCommitEnabled;
|
|
165782
|
+
const configuredModel = "model" in registration.config && typeof registration.config.model === "string" ? registration.config.model.trim() : "";
|
|
162739
165783
|
return {
|
|
162740
165784
|
projectIdentity: registration.projectIdentity,
|
|
162741
165785
|
sourceDirectory: registration.sourceDirectory,
|
|
@@ -162747,7 +165791,7 @@ function snapshotFor(registration) {
|
|
|
162747
165791
|
gitCommitEnabled,
|
|
162748
165792
|
modelId: registration.observationMode || !providerIsOn ? "off" : registration.modelId,
|
|
162749
165793
|
chunkModelId: registration.observationMode || !providerIsOn ? "off" : registration.chunkModelId,
|
|
162750
|
-
model: registration.observationMode || !providerIsOn ? "off" :
|
|
165794
|
+
model: registration.observationMode || !providerIsOn ? "off" : configuredModel ? configuredModel : registration.modelId,
|
|
162751
165795
|
provider: registration.observationMode || !providerIsOn ? "off" : registration.config.provider ?? "local"
|
|
162752
165796
|
};
|
|
162753
165797
|
}
|
|
@@ -162854,9 +165898,9 @@ function getDeleteActiveIdentityStatement(db) {
|
|
|
162854
165898
|
}
|
|
162855
165899
|
return stmt;
|
|
162856
165900
|
}
|
|
162857
|
-
function staleModelsForScope(db, projectIdentity, scope, currentModelId, cutoff) {
|
|
165901
|
+
function staleModelsForScope(db, projectIdentity, scope, currentModelId, cutoff, protectedModelIds = new Set([currentModelId])) {
|
|
162858
165902
|
const rows = getStaleIdentityStatement(db, scope).all(projectIdentity, scope, currentModelId, cutoff);
|
|
162859
|
-
return rows.map((row) => row.modelId).filter((modelId) => typeof modelId === "string");
|
|
165903
|
+
return rows.map((row) => row.modelId).filter((modelId) => typeof modelId === "string" && !protectedModelIds.has(modelId));
|
|
162860
165904
|
}
|
|
162861
165905
|
function deleteStaleEmbeddingBatch(db, scope, projectIdentity, modelId, limit) {
|
|
162862
165906
|
if (scope === "memory") {
|
|
@@ -162921,6 +165965,12 @@ function sweepStaleEmbeddingIdentitiesForProject(db, projectIdentity, now = Date
|
|
|
162921
165965
|
return result;
|
|
162922
165966
|
const cutoff = now - EMBEDDING_IDENTITY_GC_GRACE_MS;
|
|
162923
165967
|
const deleteTracking = getDeleteActiveIdentityStatement(db);
|
|
165968
|
+
const shadow = shadowRegistrations.get(projectIdentity);
|
|
165969
|
+
const protectedModels = {
|
|
165970
|
+
memory: new Set([snapshot.modelId, ...shadow ? [shadow.modelId] : []]),
|
|
165971
|
+
commit: new Set([snapshot.modelId, ...shadow ? [shadow.modelId] : []]),
|
|
165972
|
+
chunk: new Set([snapshot.chunkModelId, ...shadow ? [shadow.chunkModelId] : []])
|
|
165973
|
+
};
|
|
162924
165974
|
const scopes = [
|
|
162925
165975
|
{
|
|
162926
165976
|
scope: "memory",
|
|
@@ -162944,7 +165994,7 @@ function sweepStaleEmbeddingIdentitiesForProject(db, projectIdentity, now = Date
|
|
|
162944
165994
|
for (const { scope, enabled, currentModelId } of scopes) {
|
|
162945
165995
|
if (!enabled || remainingBudget === 0)
|
|
162946
165996
|
continue;
|
|
162947
|
-
for (const modelId of staleModelsForScope(db, projectIdentity, scope, currentModelId, cutoff)) {
|
|
165997
|
+
for (const modelId of staleModelsForScope(db, projectIdentity, scope, currentModelId, cutoff, protectedModels[scope])) {
|
|
162948
165998
|
if (remainingBudget === 0)
|
|
162949
165999
|
break;
|
|
162950
166000
|
const deleted = deleteStaleEmbeddingBatch(db, scope, projectIdentity, modelId, remainingBudget);
|
|
@@ -162998,11 +166048,206 @@ function registerProjectEmbedding(db, projectIdentity, config2, features, source
|
|
|
162998
166048
|
observationMode: false
|
|
162999
166049
|
};
|
|
163000
166050
|
projectRegistrations.set(projectIdentity, registration);
|
|
166051
|
+
persistPrimaryDescriptor(db, registration);
|
|
163001
166052
|
if (!canReuseProvider) {
|
|
163002
166053
|
disposeProvider(prior?.provider ?? null);
|
|
163003
166054
|
}
|
|
163004
166055
|
return snapshotFor(registration);
|
|
163005
166056
|
}
|
|
166057
|
+
function startShadowWorker() {
|
|
166058
|
+
if (shadowWorker)
|
|
166059
|
+
return;
|
|
166060
|
+
shadowWorker = runShadowWorker().finally(() => {
|
|
166061
|
+
shadowWorker = null;
|
|
166062
|
+
if (shadowQueue.length > 0)
|
|
166063
|
+
startShadowWorker();
|
|
166064
|
+
});
|
|
166065
|
+
}
|
|
166066
|
+
function getShadowEmbeddingMeasurementCohort(projectIdentity) {
|
|
166067
|
+
const registration = shadowRegistrations.get(projectIdentity);
|
|
166068
|
+
if (!registration)
|
|
166069
|
+
return null;
|
|
166070
|
+
const fields = synapseConfigFields(registration.config);
|
|
166071
|
+
return {
|
|
166072
|
+
modelId: registration.modelId,
|
|
166073
|
+
chunkModelId: registration.chunkModelId,
|
|
166074
|
+
fingerprint: fields.fingerprint ?? "",
|
|
166075
|
+
epoch: fields.tableEpoch ?? 0,
|
|
166076
|
+
dims: fields.dims ?? 0
|
|
166077
|
+
};
|
|
166078
|
+
}
|
|
166079
|
+
function getPrimaryEmbeddingMeasurementCohort(projectIdentity) {
|
|
166080
|
+
const registration = projectRegistrations.get(projectIdentity);
|
|
166081
|
+
if (!registration)
|
|
166082
|
+
return null;
|
|
166083
|
+
const fields = synapseConfigFields(registration.config);
|
|
166084
|
+
return {
|
|
166085
|
+
modelId: registration.modelId,
|
|
166086
|
+
chunkModelId: registration.chunkModelId,
|
|
166087
|
+
fingerprint: fields.fingerprint ?? "",
|
|
166088
|
+
epoch: fields.tableEpoch ?? 0,
|
|
166089
|
+
dims: fields.dims ?? 0
|
|
166090
|
+
};
|
|
166091
|
+
}
|
|
166092
|
+
async function embedShadowTextForProject(projectIdentity, text, signal) {
|
|
166093
|
+
const registration = shadowRegistrations.get(projectIdentity);
|
|
166094
|
+
if (!registration)
|
|
166095
|
+
return null;
|
|
166096
|
+
try {
|
|
166097
|
+
return await registration.provider.embed(text, signal, "query");
|
|
166098
|
+
} catch (error51) {
|
|
166099
|
+
log("[magic-context] Synapse shadow query failed:", error51);
|
|
166100
|
+
return null;
|
|
166101
|
+
}
|
|
166102
|
+
}
|
|
166103
|
+
function enqueueShadowEmbeddingItems(projectIdentity, scope, ids) {
|
|
166104
|
+
if (ids.length === 0 || !shadowRegistrations.has(projectIdentity))
|
|
166105
|
+
return;
|
|
166106
|
+
shadowQueue.push({ projectIdentity, scope, ids: [...ids] });
|
|
166107
|
+
startShadowWorker();
|
|
166108
|
+
}
|
|
166109
|
+
async function embedShadowItems(registration, items, db, scope) {
|
|
166110
|
+
const raw = registration.config;
|
|
166111
|
+
const fingerprint = typeof raw.synapse_fingerprint === "string" ? raw.synapse_fingerprint : "";
|
|
166112
|
+
const tableEpoch = typeof raw.synapse_table_epoch === "number" ? raw.synapse_table_epoch : 0;
|
|
166113
|
+
const requestKey = getSynapseBatchRequestKey({
|
|
166114
|
+
model: typeof raw.model === "string" ? raw.model : SYNAPSE_DEFAULT_MODEL,
|
|
166115
|
+
fingerprint,
|
|
166116
|
+
tableEpoch,
|
|
166117
|
+
items
|
|
166118
|
+
});
|
|
166119
|
+
beginSynapseBatchLedger(db, {
|
|
166120
|
+
sessionId: `shadow:${registration.projectIdentity}`,
|
|
166121
|
+
projectPath: registration.projectIdentity,
|
|
166122
|
+
scope,
|
|
166123
|
+
manifest: items.map(({ id, contentSha256: contentSha2562 }) => ({ id, contentSha256: contentSha2562 })),
|
|
166124
|
+
requestKey
|
|
166125
|
+
});
|
|
166126
|
+
try {
|
|
166127
|
+
if (registration.provider.embedItems) {
|
|
166128
|
+
const vectors2 = await registration.provider.embedItems(items);
|
|
166129
|
+
finishSynapseBatchLedger(db, `shadow:${registration.projectIdentity}`, requestKey, vectors2.size === items.length ? "complete" : "partial");
|
|
166130
|
+
return vectors2;
|
|
166131
|
+
}
|
|
166132
|
+
const positional = await registration.provider.embedBatch(items.map((item) => item.text));
|
|
166133
|
+
const vectors = new Map(items.flatMap((item, index) => {
|
|
166134
|
+
const vector = positional[index];
|
|
166135
|
+
return vector ? [[item.id, vector]] : [];
|
|
166136
|
+
}));
|
|
166137
|
+
finishSynapseBatchLedger(db, `shadow:${registration.projectIdentity}`, requestKey, vectors.size === items.length ? "complete" : "partial");
|
|
166138
|
+
return vectors;
|
|
166139
|
+
} catch (error51) {
|
|
166140
|
+
finishSynapseBatchLedger(db, `shadow:${registration.projectIdentity}`, requestKey, "failed");
|
|
166141
|
+
throw error51;
|
|
166142
|
+
}
|
|
166143
|
+
}
|
|
166144
|
+
async function processShadowQueueItem(item) {
|
|
166145
|
+
const registration = shadowRegistrations.get(item.projectIdentity);
|
|
166146
|
+
if (!registration)
|
|
166147
|
+
return;
|
|
166148
|
+
const boundedIds = item.ids.slice(0, SHADOW_MAX_ITEMS_PER_TICK);
|
|
166149
|
+
if (item.scope === "memory") {
|
|
166150
|
+
const db2 = dbForShadowQueue.get(item.projectIdentity);
|
|
166151
|
+
if (!db2)
|
|
166152
|
+
return;
|
|
166153
|
+
const placeholders3 = boundedIds.map(() => "?").join(",");
|
|
166154
|
+
const rows = db2.prepare(`SELECT id, content, normalized_hash FROM memories
|
|
166155
|
+
WHERE project_path = ? AND id IN (${placeholders3}) AND status = 'active'`).all(item.projectIdentity, ...boundedIds.map((id) => Number(id)));
|
|
166156
|
+
const vectors2 = await embedShadowItems(registration, rows.map((row) => ({
|
|
166157
|
+
id: `memory:${row.id}`,
|
|
166158
|
+
text: row.content,
|
|
166159
|
+
contentSha256: contentSha256(row.content)
|
|
166160
|
+
})), db2, "memory");
|
|
166161
|
+
db2.transaction(() => {
|
|
166162
|
+
for (const row of rows) {
|
|
166163
|
+
const vector = vectors2.get(`memory:${row.id}`);
|
|
166164
|
+
if (vector)
|
|
166165
|
+
saveEmbeddingIfHashMatches(db2, row.id, vector, registration.modelId, row.normalized_hash);
|
|
166166
|
+
}
|
|
166167
|
+
})();
|
|
166168
|
+
return;
|
|
166169
|
+
}
|
|
166170
|
+
if (item.scope === "commit") {
|
|
166171
|
+
const db2 = dbForShadowQueue.get(item.projectIdentity);
|
|
166172
|
+
if (!db2)
|
|
166173
|
+
return;
|
|
166174
|
+
const placeholders3 = boundedIds.map(() => "?").join(",");
|
|
166175
|
+
const rows = db2.prepare(`SELECT sha, message FROM git_commits WHERE project_path = ? AND sha IN (${placeholders3})`).all(item.projectIdentity, ...boundedIds);
|
|
166176
|
+
const vectors2 = await embedShadowItems(registration, rows.map((row) => ({
|
|
166177
|
+
id: `commit:${row.sha}`,
|
|
166178
|
+
text: row.message,
|
|
166179
|
+
contentSha256: contentSha256(row.message)
|
|
166180
|
+
})), db2, "commit");
|
|
166181
|
+
db2.transaction(() => {
|
|
166182
|
+
for (const row of rows) {
|
|
166183
|
+
const vector = vectors2.get(`commit:${row.sha}`);
|
|
166184
|
+
if (vector)
|
|
166185
|
+
saveCommitEmbedding(db2, row.sha, vector, registration.modelId);
|
|
166186
|
+
}
|
|
166187
|
+
})();
|
|
166188
|
+
return;
|
|
166189
|
+
}
|
|
166190
|
+
const db = dbForShadowQueue.get(item.projectIdentity);
|
|
166191
|
+
if (!db)
|
|
166192
|
+
return;
|
|
166193
|
+
const placeholders2 = boundedIds.map(() => "?").join(",");
|
|
166194
|
+
const candidates = db.prepare(`SELECT id, session_id, start_message, end_message
|
|
166195
|
+
FROM compartments WHERE id IN (${placeholders2})`).all(...boundedIds.map((id) => Number(id)));
|
|
166196
|
+
const prepared = candidates.flatMap((candidate) => {
|
|
166197
|
+
const text = buildCanonicalChunkTextFromFts(db, candidate.session_id, candidate.start_message, candidate.end_message) || buildCompartmentSummaryFallbackText(db, candidate.id);
|
|
166198
|
+
if (!text)
|
|
166199
|
+
return [];
|
|
166200
|
+
const windows = chunkCanonicalText(text, candidate.start_message, candidate.end_message, SYNAPSE_MAX_INPUT_TOKENS);
|
|
166201
|
+
return windows.length > 0 ? [{ candidate, windows }] : [];
|
|
166202
|
+
});
|
|
166203
|
+
const items = prepared.flatMap((item2) => item2.windows.map((window) => ({
|
|
166204
|
+
id: `chunk:${item2.candidate.id}:${window.windowIndex}`,
|
|
166205
|
+
text: window.text,
|
|
166206
|
+
contentSha256: contentSha256(window.text)
|
|
166207
|
+
})));
|
|
166208
|
+
const vectors = await embedShadowItems(registration, items, db, "chunk");
|
|
166209
|
+
for (const item2 of prepared) {
|
|
166210
|
+
const rows = item2.windows.flatMap((window) => {
|
|
166211
|
+
const vector = vectors.get(`chunk:${item2.candidate.id}:${window.windowIndex}`);
|
|
166212
|
+
return vector ? [
|
|
166213
|
+
{
|
|
166214
|
+
compartmentId: item2.candidate.id,
|
|
166215
|
+
sessionId: item2.candidate.session_id,
|
|
166216
|
+
projectPath: registration.projectIdentity,
|
|
166217
|
+
window,
|
|
166218
|
+
modelId: registration.chunkModelId,
|
|
166219
|
+
vector
|
|
166220
|
+
}
|
|
166221
|
+
] : [];
|
|
166222
|
+
});
|
|
166223
|
+
if (rows.length === item2.windows.length)
|
|
166224
|
+
replaceCompartmentChunkEmbeddings(db, rows);
|
|
166225
|
+
}
|
|
166226
|
+
}
|
|
166227
|
+
var dbForShadowQueue = new Map;
|
|
166228
|
+
async function runShadowWorker() {
|
|
166229
|
+
const startedAt = Date.now();
|
|
166230
|
+
let processed = 0;
|
|
166231
|
+
let processedBytes = 0;
|
|
166232
|
+
while (shadowQueue.length > 0 && processed < SHADOW_MAX_ITEMS_PER_TICK && Date.now() - startedAt < SHADOW_MAX_WALL_CLOCK_MS) {
|
|
166233
|
+
const item = shadowQueue.shift();
|
|
166234
|
+
if (!item)
|
|
166235
|
+
break;
|
|
166236
|
+
const itemBytes = item.ids.reduce((total, id) => total + id.length, 0);
|
|
166237
|
+
if (processed > 0 && processedBytes + itemBytes > SHADOW_MAX_BYTES_PER_TICK) {
|
|
166238
|
+
shadowQueue.unshift(item);
|
|
166239
|
+
break;
|
|
166240
|
+
}
|
|
166241
|
+
try {
|
|
166242
|
+
await processShadowQueueItem(item);
|
|
166243
|
+
} catch (error51) {
|
|
166244
|
+
log("[magic-context] Synapse shadow write failed:", error51);
|
|
166245
|
+
}
|
|
166246
|
+
processed += item.ids.length;
|
|
166247
|
+
processedBytes += itemBytes;
|
|
166248
|
+
await new Promise((resolve2) => setTimeout(resolve2, 0));
|
|
166249
|
+
}
|
|
166250
|
+
}
|
|
163006
166251
|
function registerProjectInObservationMode(db, projectIdentity, sourceDirectory, failedConfig, failureSummary) {
|
|
163007
166252
|
const prior = projectRegistrations.get(projectIdentity);
|
|
163008
166253
|
const runtimeFingerprint = `observation:${sha256Prefix(failureSummary)}`;
|
|
@@ -163044,7 +166289,10 @@ function getOrCreateProjectProvider(registration) {
|
|
|
163044
166289
|
if (registration.provider) {
|
|
163045
166290
|
return registration.provider;
|
|
163046
166291
|
}
|
|
163047
|
-
const provider = createProvider(registration.config
|
|
166292
|
+
const provider = createProvider(registration.config, {
|
|
166293
|
+
projectRoot: registration.sourceDirectory,
|
|
166294
|
+
session: `project:${registration.projectIdentity}`
|
|
166295
|
+
});
|
|
163048
166296
|
registration.provider = provider;
|
|
163049
166297
|
return provider;
|
|
163050
166298
|
}
|
|
@@ -163064,7 +166312,7 @@ async function embedTextForProject(projectIdentity, text, signal, purpose = "pas
|
|
|
163064
166312
|
if (!current || current.generation !== generation || current.runtimeFingerprint !== registration.runtimeFingerprint) {
|
|
163065
166313
|
return null;
|
|
163066
166314
|
}
|
|
163067
|
-
return { vector, modelId, generation };
|
|
166315
|
+
return { vector, modelId, chunkModelId: registration.chunkModelId, generation };
|
|
163068
166316
|
}
|
|
163069
166317
|
async function embedBatchForProject(projectIdentity, texts, signal, purpose = "passage") {
|
|
163070
166318
|
if (texts.length === 0) {
|
|
@@ -163089,31 +166337,81 @@ async function embedBatchForProject(projectIdentity, texts, signal, purpose = "p
|
|
|
163089
166337
|
}
|
|
163090
166338
|
return { vectors, modelId, generation };
|
|
163091
166339
|
}
|
|
163092
|
-
async function
|
|
163093
|
-
|
|
163094
|
-
|
|
166340
|
+
async function embedItemsForProject(projectIdentity, items, signal, db, sessionId = projectIdentity) {
|
|
166341
|
+
const registration = projectRegistrations.get(projectIdentity);
|
|
166342
|
+
if (!registration || registration.observationMode || items.length === 0)
|
|
166343
|
+
return null;
|
|
166344
|
+
const generation = registration.generation;
|
|
166345
|
+
const modelId = registration.modelId;
|
|
166346
|
+
const runtimeFingerprint = registration.runtimeFingerprint;
|
|
166347
|
+
const provider = getOrCreateProjectProvider(registration);
|
|
166348
|
+
if (!provider)
|
|
166349
|
+
return null;
|
|
166350
|
+
const rawConfig = registration.config;
|
|
166351
|
+
const fingerprint = typeof rawConfig.synapse_fingerprint === "string" ? rawConfig.synapse_fingerprint : "";
|
|
166352
|
+
const tableEpoch = typeof rawConfig.synapse_table_epoch === "number" ? rawConfig.synapse_table_epoch : undefined;
|
|
166353
|
+
const isSynapse = registration.providerIdentity.startsWith("synapse:v1:") && fingerprint && tableEpoch !== undefined;
|
|
166354
|
+
const ledgerKey = isSynapse ? getSynapseBatchRequestKey({
|
|
166355
|
+
model: typeof rawConfig.model === "string" ? rawConfig.model : registration.modelId,
|
|
166356
|
+
fingerprint,
|
|
166357
|
+
tableEpoch,
|
|
166358
|
+
items
|
|
166359
|
+
}) : null;
|
|
166360
|
+
if (db && ledgerKey) {
|
|
166361
|
+
beginSynapseBatchLedger(db, {
|
|
166362
|
+
sessionId,
|
|
166363
|
+
projectPath: projectIdentity,
|
|
166364
|
+
scope: items[0].id.split(":", 1)[0],
|
|
166365
|
+
manifest: items.map(({ id, contentSha256: contentSha2562 }) => ({ id, contentSha256: contentSha2562 })),
|
|
166366
|
+
requestKey: ledgerKey
|
|
166367
|
+
});
|
|
163095
166368
|
}
|
|
163096
|
-
|
|
166369
|
+
let vectors;
|
|
166370
|
+
try {
|
|
166371
|
+
if (provider.embedItems) {
|
|
166372
|
+
vectors = await provider.embedItems(items, signal);
|
|
166373
|
+
} else {
|
|
166374
|
+
const positional = await provider.embedBatch(items.map((item) => item.text), signal, "passage");
|
|
166375
|
+
vectors = new Map(items.flatMap((item, index) => {
|
|
166376
|
+
const vector = positional[index];
|
|
166377
|
+
return vector ? [[item.id, vector]] : [];
|
|
166378
|
+
}));
|
|
166379
|
+
}
|
|
166380
|
+
} catch (error51) {
|
|
166381
|
+
if (db && ledgerKey)
|
|
166382
|
+
finishSynapseBatchLedger(db, sessionId, ledgerKey, "failed");
|
|
166383
|
+
throw error51;
|
|
166384
|
+
}
|
|
166385
|
+
if (db && ledgerKey) {
|
|
166386
|
+
finishSynapseBatchLedger(db, sessionId, ledgerKey, vectors.size === items.length ? "complete" : "partial");
|
|
166387
|
+
}
|
|
166388
|
+
const current = projectRegistrations.get(projectIdentity);
|
|
166389
|
+
if (!current || current.generation !== generation || current.runtimeFingerprint !== runtimeFingerprint) {
|
|
166390
|
+
return null;
|
|
166391
|
+
}
|
|
166392
|
+
return { vectors, modelId, generation };
|
|
166393
|
+
}
|
|
166394
|
+
async function embedItemsWindowBounded(projectIdentity, items, signal, db) {
|
|
166395
|
+
if (items.length <= MAX_WINDOWS_PER_EMBED_CALL) {
|
|
166396
|
+
return embedItemsForProject(projectIdentity, items, signal, db, projectIdentity);
|
|
166397
|
+
}
|
|
166398
|
+
const vectors = new Map;
|
|
163097
166399
|
let modelId = null;
|
|
163098
166400
|
let generation = null;
|
|
163099
|
-
for (let start = 0;start <
|
|
163100
|
-
|
|
163101
|
-
return null;
|
|
163102
|
-
const sub = texts.slice(start, start + MAX_WINDOWS_PER_EMBED_CALL);
|
|
163103
|
-
const result = await embedBatchForProject(projectIdentity, sub, signal);
|
|
166401
|
+
for (let start = 0;start < items.length; start += MAX_WINDOWS_PER_EMBED_CALL) {
|
|
166402
|
+
const result = await embedItemsForProject(projectIdentity, items.slice(start, start + MAX_WINDOWS_PER_EMBED_CALL), signal, db, projectIdentity);
|
|
163104
166403
|
if (!result)
|
|
163105
166404
|
return null;
|
|
163106
166405
|
if (modelId === null) {
|
|
163107
166406
|
modelId = result.modelId;
|
|
163108
166407
|
generation = result.generation;
|
|
163109
|
-
} else if (
|
|
166408
|
+
} else if (modelId !== result.modelId || generation !== result.generation) {
|
|
163110
166409
|
return null;
|
|
163111
166410
|
}
|
|
163112
|
-
|
|
166411
|
+
for (const [id, vector] of result.vectors)
|
|
166412
|
+
vectors.set(id, vector);
|
|
163113
166413
|
}
|
|
163114
|
-
|
|
163115
|
-
return null;
|
|
163116
|
-
return { vectors, modelId, generation };
|
|
166414
|
+
return modelId === null || generation === null ? null : { vectors, modelId, generation };
|
|
163117
166415
|
}
|
|
163118
166416
|
function isUnembeddedMemoryRow(row) {
|
|
163119
166417
|
if (row === null || typeof row !== "object")
|
|
@@ -163138,13 +166436,17 @@ async function embedUnembeddedMemoriesForProject(db, projectIdentity, batchSize
|
|
|
163138
166436
|
if (memories.length === 0)
|
|
163139
166437
|
return 0;
|
|
163140
166438
|
try {
|
|
163141
|
-
const result = await
|
|
166439
|
+
const result = await embedItemsForProject(projectIdentity, memories.map((memory) => ({
|
|
166440
|
+
id: `memory:${memory.id}`,
|
|
166441
|
+
text: memory.content,
|
|
166442
|
+
contentSha256: contentSha256(memory.content)
|
|
166443
|
+
})), undefined, db, projectIdentity);
|
|
163142
166444
|
if (!result)
|
|
163143
166445
|
return 0;
|
|
163144
166446
|
let embeddedCount = 0;
|
|
163145
166447
|
db.transaction(() => {
|
|
163146
|
-
for (const
|
|
163147
|
-
const embedding = result.vectors
|
|
166448
|
+
for (const memory of memories) {
|
|
166449
|
+
const embedding = result.vectors.get(`memory:${memory.id}`);
|
|
163148
166450
|
if (!embedding)
|
|
163149
166451
|
continue;
|
|
163150
166452
|
if (saveEmbeddingIfHashMatches(db, memory.id, embedding, result.modelId, memory.normalized_hash)) {
|
|
@@ -163152,6 +166454,7 @@ async function embedUnembeddedMemoriesForProject(db, projectIdentity, batchSize
|
|
|
163152
166454
|
}
|
|
163153
166455
|
}
|
|
163154
166456
|
})();
|
|
166457
|
+
enqueueShadowEmbeddingItems(projectIdentity, "memory", memories.filter((memory) => result.vectors.has(`memory:${memory.id}`)).map((memory) => String(memory.id)));
|
|
163155
166458
|
return embeddedCount;
|
|
163156
166459
|
} catch (error51) {
|
|
163157
166460
|
log("[magic-context] failed to proactively embed missing memories:", error51);
|
|
@@ -163165,19 +166468,24 @@ async function embedCommitBatch(db, projectIdentity, batchSize) {
|
|
|
163165
166468
|
const commits = loadUnembeddedCommits(db, projectIdentity, snapshot.modelId, Math.max(1, Math.floor(batchSize)));
|
|
163166
166469
|
if (commits.length === 0)
|
|
163167
166470
|
return 0;
|
|
163168
|
-
const result = await
|
|
166471
|
+
const result = await embedItemsForProject(projectIdentity, commits.map((commit) => ({
|
|
166472
|
+
id: `commit:${commit.sha}`,
|
|
166473
|
+
text: commit.message,
|
|
166474
|
+
contentSha256: contentSha256(commit.message)
|
|
166475
|
+
})), undefined, db, projectIdentity);
|
|
163169
166476
|
if (!result)
|
|
163170
166477
|
return 0;
|
|
163171
166478
|
let embeddedCount = 0;
|
|
163172
166479
|
db.transaction(() => {
|
|
163173
|
-
for (const
|
|
163174
|
-
const embedding = result.vectors
|
|
166480
|
+
for (const commit of commits) {
|
|
166481
|
+
const embedding = result.vectors.get(`commit:${commit.sha}`);
|
|
163175
166482
|
if (!embedding)
|
|
163176
166483
|
continue;
|
|
163177
166484
|
saveCommitEmbedding(db, commit.sha, embedding, result.modelId);
|
|
163178
166485
|
embeddedCount += 1;
|
|
163179
166486
|
}
|
|
163180
166487
|
})();
|
|
166488
|
+
enqueueShadowEmbeddingItems(projectIdentity, "commit", commits.filter((commit) => result.vectors.has(`commit:${commit.sha}`)).map((commit) => commit.sha));
|
|
163181
166489
|
return embeddedCount;
|
|
163182
166490
|
}
|
|
163183
166491
|
async function drainCommitBacklogForProject(db, projectIdentity, deadline) {
|
|
@@ -163250,9 +166558,11 @@ async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates
|
|
|
163250
166558
|
windowCount += item.windows.length;
|
|
163251
166559
|
i += 1;
|
|
163252
166560
|
} while (i < prepared.length && windowCount + prepared[i].windows.length <= MAX_WINDOWS_PER_EMBED_CALL);
|
|
163253
|
-
const
|
|
163254
|
-
|
|
163255
|
-
|
|
166561
|
+
const items = slice.flatMap((item) => item.windows.map((window) => ({
|
|
166562
|
+
id: `chunk:${item.candidate.id}:${window.windowIndex}`,
|
|
166563
|
+
text: window.text,
|
|
166564
|
+
contentSha256: contentSha256(window.text)
|
|
166565
|
+
})));
|
|
163256
166566
|
const persistedIds = new Set;
|
|
163257
166567
|
for (let attempt = 0;attempt < EMBED_SLICE_RETRY_ATTEMPTS; attempt++) {
|
|
163258
166568
|
if (signal?.aborted)
|
|
@@ -163260,19 +166570,17 @@ async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates
|
|
|
163260
166570
|
let result = null;
|
|
163261
166571
|
const attemptStart = Date.now();
|
|
163262
166572
|
try {
|
|
163263
|
-
result = await
|
|
166573
|
+
result = await embedItemsWindowBounded(projectIdentity, items, signal, db);
|
|
163264
166574
|
} catch (error51) {
|
|
163265
166575
|
log("[magic-context] failed to proactively embed compartment chunks:", error51);
|
|
163266
166576
|
}
|
|
163267
166577
|
if (signal?.aborted)
|
|
163268
166578
|
break;
|
|
163269
166579
|
if (result) {
|
|
163270
|
-
let offset = 0;
|
|
163271
166580
|
for (const item of slice) {
|
|
163272
|
-
const vectors = result.vectors.slice(offset, offset + item.windows.length);
|
|
163273
|
-
offset += item.windows.length;
|
|
163274
166581
|
if (persistedIds.has(item.candidate.id))
|
|
163275
166582
|
continue;
|
|
166583
|
+
const vectors = item.windows.map((window) => result.vectors.get(`chunk:${item.candidate.id}:${window.windowIndex}`));
|
|
163276
166584
|
if (vectors.length !== item.windows.length || vectors.some((v) => !v)) {
|
|
163277
166585
|
continue;
|
|
163278
166586
|
}
|
|
@@ -163286,6 +166594,9 @@ async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates
|
|
|
163286
166594
|
}));
|
|
163287
166595
|
replaceCompartmentChunkEmbeddings(db, rows);
|
|
163288
166596
|
persistedIds.add(item.candidate.id);
|
|
166597
|
+
enqueueShadowEmbeddingItems(projectIdentity, "chunk", [
|
|
166598
|
+
String(item.candidate.id)
|
|
166599
|
+
]);
|
|
163289
166600
|
}
|
|
163290
166601
|
}
|
|
163291
166602
|
if (persistedIds.size === slice.length)
|
|
@@ -163454,7 +166765,24 @@ function createProvider2(config2) {
|
|
|
163454
166765
|
maxInputTokens: config2.max_input_tokens
|
|
163455
166766
|
});
|
|
163456
166767
|
}
|
|
163457
|
-
|
|
166768
|
+
if (config2.provider === "local") {
|
|
166769
|
+
return new LocalEmbeddingProvider(config2.model, config2.max_input_tokens);
|
|
166770
|
+
}
|
|
166771
|
+
if (config2.provider === "synapse") {
|
|
166772
|
+
const synapse = config2;
|
|
166773
|
+
return new SynapseEmbeddingProvider({
|
|
166774
|
+
connectionFile: synapse.synapse_connection_file ?? "",
|
|
166775
|
+
projectRoot: "",
|
|
166776
|
+
session: "embedding",
|
|
166777
|
+
model: synapse.model,
|
|
166778
|
+
fingerprint: synapse.synapse_fingerprint,
|
|
166779
|
+
tableEpoch: synapse.synapse_table_epoch,
|
|
166780
|
+
dims: synapse.synapse_dims,
|
|
166781
|
+
recommendedBatch: synapse.synapse_recommended_batch,
|
|
166782
|
+
provenance: synapse.synapse_provenance
|
|
166783
|
+
});
|
|
166784
|
+
}
|
|
166785
|
+
throw new Error("Unknown embedding provider");
|
|
163458
166786
|
}
|
|
163459
166787
|
function getOrCreateProvider() {
|
|
163460
166788
|
if (provider) {
|
|
@@ -163479,7 +166807,7 @@ async function embedText(text, signal) {
|
|
|
163479
166807
|
var SWEEP_MAX_WALL_CLOCK_MS2 = 10 * 60 * 1000;
|
|
163480
166808
|
|
|
163481
166809
|
// ../plugin/src/plugin/embedding-bootstrap-helpers.ts
|
|
163482
|
-
import { createHash as
|
|
166810
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
163483
166811
|
init_logger();
|
|
163484
166812
|
var EMBEDDING_AFFECTING_KEYS = new Set([
|
|
163485
166813
|
"embedding.api_key",
|
|
@@ -163489,7 +166817,11 @@ var EMBEDDING_AFFECTING_KEYS = new Set([
|
|
|
163489
166817
|
"embedding.input_type",
|
|
163490
166818
|
"embedding.truncate",
|
|
163491
166819
|
"embedding.max_input_tokens",
|
|
163492
|
-
"embedding.query_input_type"
|
|
166820
|
+
"embedding.query_input_type",
|
|
166821
|
+
"embedding.fallback_provider",
|
|
166822
|
+
"subc",
|
|
166823
|
+
"subc.connection_file",
|
|
166824
|
+
"shadow_embedding"
|
|
163493
166825
|
]);
|
|
163494
166826
|
var LITERAL_CONFIG_TOKEN_RE = /\{(?:env|file):[^}]+\}/;
|
|
163495
166827
|
function embeddingConfigHasLiteralTokens(embedding) {
|
|
@@ -163502,7 +166834,13 @@ function embeddingConfigHasLiteralTokens(embedding) {
|
|
|
163502
166834
|
}
|
|
163503
166835
|
return false;
|
|
163504
166836
|
}
|
|
163505
|
-
var EMBEDDING_AFFECTING_TOP_LEVEL_KEYS = new Set([
|
|
166837
|
+
var EMBEDDING_AFFECTING_TOP_LEVEL_KEYS = new Set([
|
|
166838
|
+
"embedding",
|
|
166839
|
+
"memory",
|
|
166840
|
+
"experimental",
|
|
166841
|
+
"subc",
|
|
166842
|
+
"shadow_embedding"
|
|
166843
|
+
]);
|
|
163506
166844
|
var EMBEDDING_WARNING_TERMS = [
|
|
163507
166845
|
"api_key",
|
|
163508
166846
|
"endpoint",
|
|
@@ -163510,11 +166848,13 @@ var EMBEDDING_WARNING_TERMS = [
|
|
|
163510
166848
|
"provider",
|
|
163511
166849
|
"embedding",
|
|
163512
166850
|
"input_type",
|
|
163513
|
-
"truncate"
|
|
166851
|
+
"truncate",
|
|
166852
|
+
"subc",
|
|
166853
|
+
"shadow_embedding"
|
|
163514
166854
|
];
|
|
163515
166855
|
var loggedFailureSignatures = new Map;
|
|
163516
166856
|
function sha256Prefix2(value, length = 16) {
|
|
163517
|
-
return
|
|
166857
|
+
return createHash8("sha256").update(value).digest("hex").slice(0, length);
|
|
163518
166858
|
}
|
|
163519
166859
|
function warningLooksEmbeddingRelated(message) {
|
|
163520
166860
|
const lower = message.toLowerCase();
|
|
@@ -163689,7 +167029,7 @@ function clearCompressionDepthRange(db, sessionId, startOrdinal, endOrdinal) {
|
|
|
163689
167029
|
}
|
|
163690
167030
|
|
|
163691
167031
|
// ../plugin/src/features/magic-context/storage-meta-shared.ts
|
|
163692
|
-
import { Buffer as
|
|
167032
|
+
import { Buffer as Buffer3 } from "node:buffer";
|
|
163693
167033
|
var SESSION_META_SELECT_COLUMNS = [
|
|
163694
167034
|
"session_id",
|
|
163695
167035
|
"last_response_time",
|
|
@@ -163822,14 +167162,14 @@ function isNumberOrNull(value) {
|
|
|
163822
167162
|
return value === null || typeof value === "number";
|
|
163823
167163
|
}
|
|
163824
167164
|
function isBlobOrNull(value) {
|
|
163825
|
-
return value === null ||
|
|
167165
|
+
return value === null || Buffer3.isBuffer(value) || value instanceof Uint8Array;
|
|
163826
167166
|
}
|
|
163827
167167
|
function toBufferOrNull(value) {
|
|
163828
167168
|
if (value === null)
|
|
163829
167169
|
return null;
|
|
163830
|
-
if (
|
|
167170
|
+
if (Buffer3.isBuffer(value))
|
|
163831
167171
|
return value;
|
|
163832
|
-
return
|
|
167172
|
+
return Buffer3.from(value.buffer, value.byteOffset, value.byteLength);
|
|
163833
167173
|
}
|
|
163834
167174
|
function isSessionMetaRow(row) {
|
|
163835
167175
|
if (row === null || typeof row !== "object")
|
|
@@ -163974,7 +167314,7 @@ function persistCachedM0(db, sessionId, payload) {
|
|
|
163974
167314
|
cached_m0_system_hash = ?,
|
|
163975
167315
|
cached_m0_model_key = ?,
|
|
163976
167316
|
cached_m0_project_identity = ?
|
|
163977
|
-
WHERE session_id = ?`).run(
|
|
167317
|
+
WHERE session_id = ?`).run(Buffer3.from(payload.m0Bytes), payload.projectMemoryEpoch, payload.workspaceFingerprint ?? null, payload.projectUserProfileVersion, payload.maxCompartmentSeq, payload.maxMemoryId, payload.maxMutationId, payload.maxMemoryMutationId ?? null, payload.m1Bytes ? Buffer3.from(payload.m1Bytes) : null, payload.projectDocsHash, payload.materializedAt, payload.sessionFactsVersion, payload.upgradeState, payload.systemHash ?? "", payload.modelKey ?? "", payload.projectIdentity ?? null, sessionId);
|
|
163978
167318
|
}
|
|
163979
167319
|
function clearCachedM0M1(db, sessionId) {
|
|
163980
167320
|
ensureSessionMetaRow(db, sessionId);
|
|
@@ -171029,7 +174369,7 @@ function indexMessagesAfterOrdinal(db, sessionId, messages, _lastIndexedOrdinal,
|
|
|
171029
174369
|
return inserted;
|
|
171030
174370
|
}
|
|
171031
174371
|
// ../plugin/src/features/magic-context/project-docs-hash.ts
|
|
171032
|
-
import { createHash as
|
|
174372
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
171033
174373
|
import { lstatSync, readFileSync as readFileSync5, statSync as statSync5 } from "node:fs";
|
|
171034
174374
|
import path5 from "node:path";
|
|
171035
174375
|
var PROJECT_DOC_FILES = ["ARCHITECTURE.md", "STRUCTURE.md"];
|
|
@@ -171127,7 +174467,7 @@ function hashCanonicalPieces(hashPieces) {
|
|
|
171127
174467
|
if (hashPieces.length === 0) {
|
|
171128
174468
|
return "";
|
|
171129
174469
|
}
|
|
171130
|
-
return
|
|
174470
|
+
return createHash9("sha256").update(hashPieces.join(PROJECT_DOCS_DELIMITER), "utf8").digest("hex");
|
|
171131
174471
|
}
|
|
171132
174472
|
function readProjectDocsCanonical(projectDirectory) {
|
|
171133
174473
|
const canonicalDirectory = path5.resolve(projectDirectory);
|
|
@@ -171172,14 +174512,19 @@ function countRows(db, table, sessionId) {
|
|
|
171172
174512
|
const row = db.prepare(`SELECT COUNT(*) AS count FROM ${table} WHERE session_id = ?`).get(sessionId);
|
|
171173
174513
|
return typeof row?.count === "number" ? row.count : 0;
|
|
171174
174514
|
}
|
|
171175
|
-
function
|
|
174515
|
+
function mapMessageId(filter, messageId) {
|
|
174516
|
+
if (messageId === null)
|
|
174517
|
+
return null;
|
|
174518
|
+
return filter.mapMessageId?.(messageId) ?? messageId;
|
|
174519
|
+
}
|
|
174520
|
+
function filterIdBlob(raw, filter) {
|
|
171176
174521
|
if (!raw)
|
|
171177
174522
|
return "";
|
|
171178
174523
|
try {
|
|
171179
174524
|
const value = JSON.parse(raw);
|
|
171180
174525
|
if (!Array.isArray(value))
|
|
171181
174526
|
return "";
|
|
171182
|
-
const filtered = value.filter((id) => typeof id === "string" && includeMessageId(id));
|
|
174527
|
+
const filtered = value.filter((id) => typeof id === "string" && filter.includeMessageId(id)).map((id) => mapMessageId(filter, id));
|
|
171183
174528
|
return filtered.length > 0 ? JSON.stringify(filtered) : "";
|
|
171184
174529
|
} catch {
|
|
171185
174530
|
return "";
|
|
@@ -171216,16 +174561,16 @@ function copySessionStateForClone(db, sourceSessionId, destinationSessionId, fil
|
|
|
171216
174561
|
const endMessage = filter.resolveBoundaryOrdinal(row.end_message_id);
|
|
171217
174562
|
if (startMessage === undefined || endMessage === undefined)
|
|
171218
174563
|
continue;
|
|
171219
|
-
insertCompartment.run(destinationSessionId, row.sequence, startMessage, endMessage, row.start_message_id, row.end_message_id, row.title, row.content, row.p1, row.p2, row.p3, row.p4, row.importance, row.episode_type, row.legacy, row.created_at, row.harness);
|
|
174564
|
+
insertCompartment.run(destinationSessionId, row.sequence, startMessage, endMessage, mapMessageId(filter, row.start_message_id), mapMessageId(filter, row.end_message_id), row.title, row.content, row.p1, row.p2, row.p3, row.p4, row.importance, row.episode_type, row.legacy, row.created_at, row.harness);
|
|
171220
174565
|
copiedCompartments.push({
|
|
171221
174566
|
sequence: row.sequence,
|
|
171222
174567
|
startMessage,
|
|
171223
174568
|
endMessage,
|
|
171224
|
-
startMessageId: row.start_message_id,
|
|
171225
|
-
endMessageId: row.end_message_id
|
|
174569
|
+
startMessageId: mapMessageId(filter, row.start_message_id) ?? row.start_message_id,
|
|
174570
|
+
endMessageId: mapMessageId(filter, row.end_message_id) ?? row.end_message_id
|
|
171226
174571
|
});
|
|
171227
174572
|
}
|
|
171228
|
-
const sourceTags = db.prepare(`SELECT message_id, type, status, byte_size, tag_number, harness,
|
|
174573
|
+
const sourceTags = db.prepare(`SELECT id, message_id, type, status, byte_size, tag_number, harness,
|
|
171229
174574
|
entry_fingerprint, token_count, input_token_count,
|
|
171230
174575
|
reasoning_token_count, reasoning_byte_size, drop_mode, tool_name,
|
|
171231
174576
|
input_byte_size, caveman_depth, tool_owner_message_id
|
|
@@ -171237,6 +174582,7 @@ function copySessionStateForClone(db, sourceSessionId, destinationSessionId, fil
|
|
|
171237
174582
|
caveman_depth, tool_owner_message_id)
|
|
171238
174583
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
171239
174584
|
const copiedTagNumbers = [];
|
|
174585
|
+
const copiedTagIds = new Map;
|
|
171240
174586
|
for (const row of sourceTags) {
|
|
171241
174587
|
if (!filter.includeTag({
|
|
171242
174588
|
messageId: row.message_id,
|
|
@@ -171246,19 +174592,39 @@ function copySessionStateForClone(db, sourceSessionId, destinationSessionId, fil
|
|
|
171246
174592
|
})) {
|
|
171247
174593
|
continue;
|
|
171248
174594
|
}
|
|
171249
|
-
insertTag.run(destinationSessionId, row.message_id, row.type, row.status, row.byte_size, row.tag_number, row.harness, row.entry_fingerprint, row.token_count, row.input_token_count, row.reasoning_token_count, row.reasoning_byte_size, row.drop_mode, row.tool_name, row.input_byte_size, row.caveman_depth, row.tool_owner_message_id);
|
|
174595
|
+
const insertResult = insertTag.run(destinationSessionId, mapMessageId(filter, row.message_id), row.type, row.status, row.byte_size, row.tag_number, row.harness, row.entry_fingerprint, row.token_count, row.input_token_count, row.reasoning_token_count, row.reasoning_byte_size, row.drop_mode, row.tool_name, row.input_byte_size, row.caveman_depth, mapMessageId(filter, row.tool_owner_message_id));
|
|
174596
|
+
const insertedTagId = Number(insertResult.lastInsertRowid);
|
|
174597
|
+
if (!Number.isSafeInteger(insertedTagId) || insertedTagId <= 0) {
|
|
174598
|
+
throw new Error(`failed to obtain destination tag id for tag ${row.tag_number}`);
|
|
174599
|
+
}
|
|
174600
|
+
const sourceTagId = filter.mapTagId ? row.id : row.tag_number;
|
|
174601
|
+
const destinationTagId = filter.mapTagId ? filter.mapTagId(sourceTagId, insertedTagId) : sourceTagId;
|
|
174602
|
+
copiedTagIds.set(sourceTagId, destinationTagId);
|
|
171250
174603
|
copiedTagNumbers.push(row.tag_number);
|
|
171251
174604
|
}
|
|
171252
174605
|
if (copiedTagNumbers.length > 0) {
|
|
171253
|
-
const
|
|
171254
|
-
|
|
171255
|
-
|
|
171256
|
-
|
|
171257
|
-
|
|
171258
|
-
db.prepare(
|
|
171259
|
-
|
|
171260
|
-
|
|
171261
|
-
|
|
174606
|
+
const sourceTagIds = [...copiedTagIds.keys()];
|
|
174607
|
+
const placeholders3 = sourceTagIds.map(() => "?").join(", ");
|
|
174608
|
+
const sourceContents = db.prepare(`SELECT tag_id, content, created_at, harness
|
|
174609
|
+
FROM source_contents
|
|
174610
|
+
WHERE session_id = ? AND tag_id IN (${placeholders3})`).all(sourceSessionId, ...sourceTagIds);
|
|
174611
|
+
const insertSourceContent = db.prepare("INSERT INTO source_contents (tag_id, session_id, content, created_at, harness) VALUES (?, ?, ?, ?, ?)");
|
|
174612
|
+
for (const row of sourceContents) {
|
|
174613
|
+
const destinationTagId = copiedTagIds.get(row.tag_id);
|
|
174614
|
+
if (destinationTagId === undefined)
|
|
174615
|
+
continue;
|
|
174616
|
+
insertSourceContent.run(destinationTagId, destinationSessionId, row.content, row.created_at, row.harness);
|
|
174617
|
+
}
|
|
174618
|
+
const pendingOps = db.prepare(`SELECT tag_id, operation, queued_at, harness
|
|
174619
|
+
FROM pending_ops
|
|
174620
|
+
WHERE session_id = ? AND tag_id IN (${placeholders3})`).all(sourceSessionId, ...sourceTagIds);
|
|
174621
|
+
const insertPendingOp = db.prepare("INSERT INTO pending_ops (session_id, tag_id, operation, queued_at, harness) VALUES (?, ?, ?, ?, ?)");
|
|
174622
|
+
for (const row of pendingOps) {
|
|
174623
|
+
const destinationTagId = copiedTagIds.get(row.tag_id);
|
|
174624
|
+
if (destinationTagId === undefined)
|
|
174625
|
+
continue;
|
|
174626
|
+
insertPendingOp.run(destinationSessionId, destinationTagId, row.operation, row.queued_at, row.harness);
|
|
174627
|
+
}
|
|
171262
174628
|
}
|
|
171263
174629
|
const meta3 = db.prepare(`SELECT cleared_reasoning_through_tag, tool_reclaim_watermark,
|
|
171264
174630
|
pi_stable_id_scheme, stripped_placeholder_ids,
|
|
@@ -171294,7 +174660,7 @@ function copySessionStateForClone(db, sourceSessionId, destinationSessionId, fil
|
|
|
171294
174660
|
todo_synthetic_anchor_message_id = excluded.todo_synthetic_anchor_message_id,
|
|
171295
174661
|
todo_synthetic_state_json = excluded.todo_synthetic_state_json,
|
|
171296
174662
|
cached_m0_bytes = NULL,
|
|
171297
|
-
cached_m1_bytes = NULL`).run(destinationSessionId, getHarness(), maxCopiedTag, clampWatermark(meta3?.cleared_reasoning_through_tag ?? null, maxCopiedTag), clampWatermark(meta3?.tool_reclaim_watermark ?? null, maxCopiedTag), typeof meta3?.pi_stable_id_scheme === "number" && Number.isFinite(meta3.pi_stable_id_scheme) ? meta3.pi_stable_id_scheme : null, filterIdBlob(meta3?.stripped_placeholder_ids ?? null, filter
|
|
174663
|
+
cached_m1_bytes = NULL`).run(destinationSessionId, getHarness(), maxCopiedTag, clampWatermark(meta3?.cleared_reasoning_through_tag ?? null, maxCopiedTag), clampWatermark(meta3?.tool_reclaim_watermark ?? null, maxCopiedTag), typeof meta3?.pi_stable_id_scheme === "number" && Number.isFinite(meta3.pi_stable_id_scheme) ? meta3.pi_stable_id_scheme : null, filterIdBlob(meta3?.stripped_placeholder_ids ?? null, filter), filterIdBlob(meta3?.stale_reduce_stripped_ids ?? null, filter), filterIdBlob(meta3?.processed_image_stripped_ids ?? null, filter), pendingMarker, migrateTodo ? meta3?.last_todo_state ?? "" : "", migrateTodo ? meta3?.todo_synthetic_call_id ?? "" : "", migrateTodo ? mapMessageId(filter, todoAnchor) ?? "" : "", migrateTodo ? meta3?.todo_synthetic_state_json ?? "" : "");
|
|
171298
174664
|
const pendingOpsRow = db.prepare("SELECT COUNT(*) AS count FROM pending_ops WHERE session_id = ?").get(destinationSessionId);
|
|
171299
174665
|
return {
|
|
171300
174666
|
kind: "migrated",
|
|
@@ -171464,6 +174830,7 @@ function getMaxMemoryMutationIdForProjects(db, projectPaths) {
|
|
|
171464
174830
|
}
|
|
171465
174831
|
// ../plugin/src/features/magic-context/storage-meta-persisted.ts
|
|
171466
174832
|
init_logger();
|
|
174833
|
+
var emergencyRecoveryArmedSessions = new Set;
|
|
171467
174834
|
var CAS_RETRY_LIMIT = 5;
|
|
171468
174835
|
var AUTO_SEARCH_NO_HINT_REASONS = new Set([
|
|
171469
174836
|
"below-threshold",
|
|
@@ -172185,6 +175552,7 @@ function getOverflowState(db, sessionId, modelKey) {
|
|
|
172185
175552
|
};
|
|
172186
175553
|
}
|
|
172187
175554
|
function recordOverflowDetected(db, sessionId, reportedLimit, modelKey, origin = "provider_overflow") {
|
|
175555
|
+
emergencyRecoveryArmedSessions.add(sessionId);
|
|
172188
175556
|
db.transaction(() => {
|
|
172189
175557
|
ensureSessionMetaRow(db, sessionId);
|
|
172190
175558
|
if (typeof reportedLimit === "number" && reportedLimit > 0) {
|
|
@@ -172203,6 +175571,7 @@ function clearEmergencyRecovery(db, sessionId) {
|
|
|
172203
175571
|
db.prepare("UPDATE session_meta SET needs_emergency_recovery = 0, emergency_recovery_origin = '' WHERE session_id = ?").run(sessionId);
|
|
172204
175572
|
}
|
|
172205
175573
|
})();
|
|
175574
|
+
emergencyRecoveryArmedSessions.delete(sessionId);
|
|
172206
175575
|
}
|
|
172207
175576
|
function clearDetectedContextLimit(db, sessionId) {
|
|
172208
175577
|
db.transaction(() => {
|
|
@@ -172402,7 +175771,7 @@ function getSessionWorkMetrics(db, sessionId) {
|
|
|
172402
175771
|
};
|
|
172403
175772
|
}
|
|
172404
175773
|
// ../plugin/src/features/magic-context/storage-meta-session.ts
|
|
172405
|
-
import { Buffer as
|
|
175774
|
+
import { Buffer as Buffer4 } from "node:buffer";
|
|
172406
175775
|
|
|
172407
175776
|
// ../plugin/src/features/magic-context/resolve-subagent-fallback.ts
|
|
172408
175777
|
init_logger();
|
|
@@ -172489,7 +175858,7 @@ function updateSessionMeta(db, sessionId, updates) {
|
|
|
172489
175858
|
values.push(NULL_BIND_META_KEYS.has(key) ? null : "");
|
|
172490
175859
|
} else if ((key === "cachedM0Bytes" || key === "cachedM1Bytes") && value instanceof Uint8Array) {
|
|
172491
175860
|
setClauses.push(`${column} = ?`);
|
|
172492
|
-
values.push(
|
|
175861
|
+
values.push(Buffer4.from(value.buffer, value.byteOffset, value.byteLength));
|
|
172493
175862
|
} else if (BOOLEAN_META_KEYS.has(key)) {
|
|
172494
175863
|
setClauses.push(`${column} = ?`);
|
|
172495
175864
|
values.push(value ? 1 : 0);
|
|
@@ -174743,18 +178112,23 @@ async function embedUnembeddedCommits(db, projectPath) {
|
|
|
174743
178112
|
break;
|
|
174744
178113
|
let embeddedThisBatch = 0;
|
|
174745
178114
|
try {
|
|
174746
|
-
const result = await
|
|
178115
|
+
const result = await embedItemsForProject(projectPath, rows.map((row) => ({
|
|
178116
|
+
id: `commit:${row.sha}`,
|
|
178117
|
+
text: row.message,
|
|
178118
|
+
contentSha256: contentSha256(row.message)
|
|
178119
|
+
})), undefined, db, projectPath);
|
|
174747
178120
|
if (!result)
|
|
174748
178121
|
break;
|
|
174749
178122
|
db.transaction(() => {
|
|
174750
|
-
for (const
|
|
174751
|
-
const embedding2 = result.vectors
|
|
178123
|
+
for (const row of rows) {
|
|
178124
|
+
const embedding2 = result.vectors.get(`commit:${row.sha}`);
|
|
174752
178125
|
if (!embedding2)
|
|
174753
178126
|
continue;
|
|
174754
178127
|
saveCommitEmbedding(db, row.sha, embedding2, result.modelId);
|
|
174755
178128
|
embeddedThisBatch += 1;
|
|
174756
178129
|
}
|
|
174757
178130
|
})();
|
|
178131
|
+
enqueueShadowEmbeddingItems(projectPath, "commit", rows.filter((row) => result.vectors.has(`commit:${row.sha}`)).map((row) => row.sha));
|
|
174758
178132
|
} catch (error51) {
|
|
174759
178133
|
log(`[git-commits] embed batch failed for ${projectPath}: ${error51 instanceof Error ? error51.message : String(error51)}`);
|
|
174760
178134
|
break;
|
|
@@ -174962,6 +178336,93 @@ function containsProbeVerbatim(text, probes) {
|
|
|
174962
178336
|
return probes.some((probe) => haystack.includes(probe.toLowerCase()));
|
|
174963
178337
|
}
|
|
174964
178338
|
|
|
178339
|
+
// ../plugin/src/features/magic-context/search-measurement.ts
|
|
178340
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
178341
|
+
function resultId(result) {
|
|
178342
|
+
switch (result.source) {
|
|
178343
|
+
case "memory":
|
|
178344
|
+
return `memory:${result.memoryId}`;
|
|
178345
|
+
case "message":
|
|
178346
|
+
return `message:${result.messageId}`;
|
|
178347
|
+
case "compartment":
|
|
178348
|
+
return `chunk:${result.compartmentId}`;
|
|
178349
|
+
case "git_commit":
|
|
178350
|
+
return `commit:${result.sha}`;
|
|
178351
|
+
case "primer":
|
|
178352
|
+
return `primer:${result.primerId}`;
|
|
178353
|
+
case "note":
|
|
178354
|
+
return `note:${result.noteId}`;
|
|
178355
|
+
}
|
|
178356
|
+
}
|
|
178357
|
+
async function recordShadowMeasurement(args) {
|
|
178358
|
+
const shadowCohort = getShadowEmbeddingMeasurementCohort(args.projectPath);
|
|
178359
|
+
if (!shadowCohort)
|
|
178360
|
+
return;
|
|
178361
|
+
const primaryCohort = getPrimaryEmbeddingMeasurementCohort(args.projectPath);
|
|
178362
|
+
const shadowStartedAt = Date.now();
|
|
178363
|
+
let shadowFailed = false;
|
|
178364
|
+
let shadowResults = [];
|
|
178365
|
+
try {
|
|
178366
|
+
const vector = await embedShadowTextForProject(args.projectPath, args.query);
|
|
178367
|
+
if (!vector) {
|
|
178368
|
+
shadowFailed = true;
|
|
178369
|
+
} else {
|
|
178370
|
+
shadowResults = await args.search(args.db, args.sessionId, args.projectPath, args.query, {
|
|
178371
|
+
...args.options,
|
|
178372
|
+
embedQuery: async () => vector,
|
|
178373
|
+
isEmbeddingRuntimeEnabled: () => true,
|
|
178374
|
+
embeddingEnabled: true,
|
|
178375
|
+
embeddingModelIdOverride: shadowCohort.modelId,
|
|
178376
|
+
chunkModelIdOverride: shadowCohort.chunkModelId,
|
|
178377
|
+
measurementDisabled: true,
|
|
178378
|
+
countRetrievals: false
|
|
178379
|
+
});
|
|
178380
|
+
}
|
|
178381
|
+
} catch {
|
|
178382
|
+
shadowFailed = true;
|
|
178383
|
+
}
|
|
178384
|
+
const primaryModelId = args.primaryQuery?.modelId ?? primaryCohort?.modelId ?? "";
|
|
178385
|
+
const primaryFingerprint = primaryCohort?.fingerprint ?? "";
|
|
178386
|
+
const primaryEpoch = primaryCohort?.epoch ?? 0;
|
|
178387
|
+
const cohortKey = JSON.stringify({
|
|
178388
|
+
primaryFingerprint,
|
|
178389
|
+
primaryEpoch,
|
|
178390
|
+
shadowFingerprint: shadowCohort.fingerprint,
|
|
178391
|
+
shadowEpoch: shadowCohort.epoch
|
|
178392
|
+
});
|
|
178393
|
+
const primaryIds = args.primaryResults.map(resultId);
|
|
178394
|
+
const shadowIds = shadowResults.map(resultId);
|
|
178395
|
+
const corpusHash = sha2562(JSON.stringify({ query: args.query, primaryIds, shadowIds }));
|
|
178396
|
+
recordEmbeddingMeasurement(args.db, {
|
|
178397
|
+
sessionId: args.sessionId,
|
|
178398
|
+
projectPath: args.projectPath,
|
|
178399
|
+
queryText: args.query,
|
|
178400
|
+
cohortKey,
|
|
178401
|
+
primaryResultIds: primaryIds,
|
|
178402
|
+
shadowResultIds: shadowIds,
|
|
178403
|
+
primaryLatencyMs: args.primaryLatencyMs,
|
|
178404
|
+
shadowLatencyMs: Date.now() - shadowStartedAt,
|
|
178405
|
+
primaryFailed: args.primaryQuery === null,
|
|
178406
|
+
shadowFailed,
|
|
178407
|
+
primaryModelId,
|
|
178408
|
+
shadowModelId: shadowCohort.modelId,
|
|
178409
|
+
primaryFingerprint,
|
|
178410
|
+
shadowFingerprint: shadowCohort.fingerprint,
|
|
178411
|
+
primaryEpoch,
|
|
178412
|
+
shadowEpoch: shadowCohort.epoch,
|
|
178413
|
+
corpusHash,
|
|
178414
|
+
coverage: {
|
|
178415
|
+
primaryResultCount: primaryIds.length,
|
|
178416
|
+
shadowResultCount: shadowIds.length,
|
|
178417
|
+
primaryAnswered: args.primaryQuery !== null,
|
|
178418
|
+
shadowAnswered: !shadowFailed
|
|
178419
|
+
}
|
|
178420
|
+
});
|
|
178421
|
+
}
|
|
178422
|
+
function sha2562(value) {
|
|
178423
|
+
return createHash10("sha256").update(value).digest("hex");
|
|
178424
|
+
}
|
|
178425
|
+
|
|
174965
178426
|
// ../plugin/src/features/magic-context/search.ts
|
|
174966
178427
|
var DEFAULT_UNIFIED_SEARCH_LIMIT = 10;
|
|
174967
178428
|
var FTS_SEMANTIC_CANDIDATE_LIMIT = 50;
|
|
@@ -175719,6 +179180,7 @@ function resolveSources(sources) {
|
|
|
175719
179180
|
}
|
|
175720
179181
|
async function unifiedSearch(db, sessionId, projectPath, query, options = {}) {
|
|
175721
179182
|
const trimmedQuery = query.trim();
|
|
179183
|
+
const measurementStartedAt = Date.now();
|
|
175722
179184
|
if (trimmedQuery.length === 0) {
|
|
175723
179185
|
return [];
|
|
175724
179186
|
}
|
|
@@ -175751,11 +179213,14 @@ async function unifiedSearch(db, sessionId, projectPath, query, options = {}) {
|
|
|
175751
179213
|
maxOrdinal: options.maxMessageOrdinal,
|
|
175752
179214
|
probes: messageProbes
|
|
175753
179215
|
}) : [];
|
|
175754
|
-
const
|
|
175755
|
-
const workspace = resolveSearchWorkspaceContext(db, projectPath);
|
|
179216
|
+
const capturedQuery = await queryEmbeddingPromise;
|
|
175756
179217
|
const embeddingSnapshot = getProjectEmbeddingSnapshot(projectPath);
|
|
175757
|
-
const
|
|
175758
|
-
const
|
|
179218
|
+
const queryContract = capturedQuery instanceof Float32Array || capturedQuery === null ? null : capturedQuery;
|
|
179219
|
+
const generationIsCurrent = queryContract === null || embeddingSnapshot !== null && embeddingSnapshot.generation === queryContract.generation;
|
|
179220
|
+
const queryEmbedding = generationIsCurrent ? queryContract?.vector ?? (capturedQuery instanceof Float32Array ? capturedQuery : null) : null;
|
|
179221
|
+
const workspace = resolveSearchWorkspaceContext(db, projectPath);
|
|
179222
|
+
const embeddingModelId = queryContract?.modelId ?? options.embeddingModelIdOverride ?? embeddingSnapshot?.modelId;
|
|
179223
|
+
const chunkModelId = queryContract?.chunkModelId ?? options.chunkModelIdOverride ?? embeddingSnapshot?.chunkModelId;
|
|
175759
179224
|
const compartmentResults = runCompartmentChunks ? searchCompartmentChunks({
|
|
175760
179225
|
db,
|
|
175761
179226
|
sessionId,
|
|
@@ -175814,6 +179279,19 @@ async function unifiedSearch(db, sessionId, projectPath, query, options = {}) {
|
|
|
175814
179279
|
...gitCommitResults,
|
|
175815
179280
|
...noteResults
|
|
175816
179281
|
].sort(compareUnifiedResults).slice(0, limit);
|
|
179282
|
+
if (!options.measurementDisabled) {
|
|
179283
|
+
recordShadowMeasurement({
|
|
179284
|
+
db,
|
|
179285
|
+
sessionId,
|
|
179286
|
+
projectPath,
|
|
179287
|
+
query: trimmedQuery,
|
|
179288
|
+
options,
|
|
179289
|
+
primaryResults: results,
|
|
179290
|
+
primaryQuery: queryContract,
|
|
179291
|
+
primaryLatencyMs: Date.now() - measurementStartedAt,
|
|
179292
|
+
search: unifiedSearch
|
|
179293
|
+
});
|
|
179294
|
+
}
|
|
175817
179295
|
const countRetrievals = options.countRetrievals ?? true;
|
|
175818
179296
|
if (countRetrievals) {
|
|
175819
179297
|
const memoryIds = results.filter((result) => result.source === "memory").map((result) => result.memoryId);
|
|
@@ -176579,7 +180057,7 @@ import {
|
|
|
176579
180057
|
} from "@earendil-works/pi-tui";
|
|
176580
180058
|
|
|
176581
180059
|
// ../plugin/src/hooks/magic-context/todo-view.ts
|
|
176582
|
-
import { createHash as
|
|
180060
|
+
import { createHash as createHash11 } from "node:crypto";
|
|
176583
180061
|
var TODO_STATUS_PENDING = "pending";
|
|
176584
180062
|
var TODO_STATUS_IN_PROGRESS = "in_progress";
|
|
176585
180063
|
var TODO_STATUS_COMPLETED = "completed";
|
|
@@ -176647,7 +180125,7 @@ function buildSyntheticTodoPart(stateJson) {
|
|
|
176647
180125
|
};
|
|
176648
180126
|
}
|
|
176649
180127
|
function computeSyntheticCallId(stateJson) {
|
|
176650
|
-
const hash2 =
|
|
180128
|
+
const hash2 = createHash11("sha256").update(stateJson).digest("hex").slice(0, 16);
|
|
176651
180129
|
return `${SYNTHETIC_CALL_ID_PREFIX}${hash2}`;
|
|
176652
180130
|
}
|
|
176653
180131
|
function parseTodoState(stateJson) {
|