@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/index.js
CHANGED
|
@@ -351,7 +351,7 @@ var require_dist2 = __commonJS((exports, module) => {
|
|
|
351
351
|
throw c;
|
|
352
352
|
};
|
|
353
353
|
if (typeof __filename < "u" ? _scriptName = __filename : f && (_scriptName = self.location.href), ba) {
|
|
354
|
-
var
|
|
354
|
+
var fs5 = __require("fs");
|
|
355
355
|
1 < process.argv.length && (t = process.argv[1].replace(/\\/g, "/")), process.argv.slice(2), w = (b, c) => {
|
|
356
356
|
throw process.exitCode = b, c;
|
|
357
357
|
};
|
|
@@ -16055,6 +16055,22 @@ function storedPathBelongsToIdentity(storedProjectPath, projectIdentity) {
|
|
|
16055
16055
|
return storedProjectPath === projectIdentity || normalizeStoredProjectPath(storedProjectPath) === projectIdentity;
|
|
16056
16056
|
}
|
|
16057
16057
|
|
|
16058
|
+
// ../plugin/src/plugin/boot-quiet.ts
|
|
16059
|
+
var BOOT_QUIET_MS = 120000;
|
|
16060
|
+
var bootQuietUntilMs = 0;
|
|
16061
|
+
function beginBootQuietPeriod(now = Date.now()) {
|
|
16062
|
+
if (bootQuietUntilMs === 0)
|
|
16063
|
+
bootQuietUntilMs = now + BOOT_QUIET_MS;
|
|
16064
|
+
}
|
|
16065
|
+
function bootQuietRemainingMs(now = Date.now()) {
|
|
16066
|
+
return Math.max(0, bootQuietUntilMs - now);
|
|
16067
|
+
}
|
|
16068
|
+
function scheduleAfterBootQuiet(task, additionalDelayMs = 0) {
|
|
16069
|
+
const timer = setTimeout(task, bootQuietRemainingMs() + Math.max(0, additionalDelayMs));
|
|
16070
|
+
timer.unref?.();
|
|
16071
|
+
return timer;
|
|
16072
|
+
}
|
|
16073
|
+
|
|
16058
16074
|
// ../plugin/src/features/magic-context/message-index-async.ts
|
|
16059
16075
|
init_logger();
|
|
16060
16076
|
|
|
@@ -149799,11 +149815,13 @@ function scheduleReconciliation(db, sessionId, readMessages) {
|
|
|
149799
149815
|
return;
|
|
149800
149816
|
}
|
|
149801
149817
|
reconciliationScheduledSessions.add(sessionId);
|
|
149802
|
-
|
|
149803
|
-
|
|
149804
|
-
|
|
149805
|
-
|
|
149806
|
-
|
|
149818
|
+
scheduleAfterBootQuiet(() => {
|
|
149819
|
+
defer(() => {
|
|
149820
|
+
reconcileSessionIndex(db, sessionId, readMessages).catch((error) => {
|
|
149821
|
+
logIndexingError(sessionId, "reconciliation", error);
|
|
149822
|
+
}).finally(() => {
|
|
149823
|
+
reconciliationScheduledSessions.delete(sessionId);
|
|
149824
|
+
});
|
|
149807
149825
|
});
|
|
149808
149826
|
});
|
|
149809
149827
|
}
|
|
@@ -151242,6 +151260,22 @@ function bumpEpochsForWorkspaceMemberSet(db, identities, now = Date.now()) {
|
|
|
151242
151260
|
}
|
|
151243
151261
|
|
|
151244
151262
|
// ../plugin/src/features/magic-context/migrations.ts
|
|
151263
|
+
var MIGRATION_LOCK_RETRY_DELAYS_MS = [1000, 2000, 4000, 8000, 15000];
|
|
151264
|
+
|
|
151265
|
+
class MigrationLockBusyError extends Error {
|
|
151266
|
+
constructor(message) {
|
|
151267
|
+
super(message);
|
|
151268
|
+
this.name = "MigrationLockBusyError";
|
|
151269
|
+
}
|
|
151270
|
+
}
|
|
151271
|
+
function isSqliteLockError(error) {
|
|
151272
|
+
if (!error || typeof error !== "object")
|
|
151273
|
+
return false;
|
|
151274
|
+
const candidate = error;
|
|
151275
|
+
if (candidate.code === "SQLITE_BUSY" || candidate.code === "SQLITE_LOCKED")
|
|
151276
|
+
return true;
|
|
151277
|
+
return typeof candidate.message === "string" && /database is locked|sqlite_(busy|locked)/i.test(candidate.message);
|
|
151278
|
+
}
|
|
151245
151279
|
function tableExists2(db, name2) {
|
|
151246
151280
|
return Boolean(db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name = ?").get(name2));
|
|
151247
151281
|
}
|
|
@@ -152601,6 +152635,80 @@ var MIGRATIONS = [
|
|
|
152601
152635
|
ensureColumn(db, "session_meta", "emergency_recovery_origin", "TEXT DEFAULT ''");
|
|
152602
152636
|
}
|
|
152603
152637
|
}
|
|
152638
|
+
},
|
|
152639
|
+
{
|
|
152640
|
+
version: 53,
|
|
152641
|
+
description: "add Synapse batch, shadow, and measurement storage",
|
|
152642
|
+
up(db) {
|
|
152643
|
+
db.exec(`
|
|
152644
|
+
CREATE TABLE IF NOT EXISTS embedding_registrations (
|
|
152645
|
+
project_path TEXT PRIMARY KEY,
|
|
152646
|
+
provider_identity TEXT NOT NULL DEFAULT '',
|
|
152647
|
+
model_id TEXT NOT NULL DEFAULT '',
|
|
152648
|
+
chunk_model_id TEXT NOT NULL DEFAULT '',
|
|
152649
|
+
fingerprint TEXT NOT NULL DEFAULT '',
|
|
152650
|
+
table_epoch INTEGER NOT NULL DEFAULT 0,
|
|
152651
|
+
dims INTEGER NOT NULL DEFAULT 0,
|
|
152652
|
+
provenance_json TEXT NOT NULL DEFAULT '{}',
|
|
152653
|
+
generation INTEGER NOT NULL DEFAULT 0,
|
|
152654
|
+
updated_at INTEGER NOT NULL DEFAULT 0
|
|
152655
|
+
);
|
|
152656
|
+
CREATE TABLE IF NOT EXISTS synapse_batch_ledger (
|
|
152657
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
152658
|
+
session_id TEXT NOT NULL,
|
|
152659
|
+
project_path TEXT NOT NULL DEFAULT '',
|
|
152660
|
+
scope TEXT NOT NULL DEFAULT '',
|
|
152661
|
+
manifest_json TEXT NOT NULL DEFAULT '{}',
|
|
152662
|
+
request_key TEXT NOT NULL DEFAULT '',
|
|
152663
|
+
job_id TEXT,
|
|
152664
|
+
cursor TEXT,
|
|
152665
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
152666
|
+
created_at INTEGER NOT NULL DEFAULT 0,
|
|
152667
|
+
updated_at INTEGER NOT NULL DEFAULT 0,
|
|
152668
|
+
UNIQUE(session_id, request_key)
|
|
152669
|
+
);
|
|
152670
|
+
CREATE INDEX IF NOT EXISTS idx_synapse_batch_ledger_session
|
|
152671
|
+
ON synapse_batch_ledger(session_id, updated_at);
|
|
152672
|
+
CREATE TABLE IF NOT EXISTS shadow_embedding_registrations (
|
|
152673
|
+
project_path TEXT NOT NULL,
|
|
152674
|
+
scope TEXT NOT NULL CHECK(scope IN ('memory', 'commit', 'chunk')),
|
|
152675
|
+
model_id TEXT NOT NULL,
|
|
152676
|
+
generation INTEGER NOT NULL DEFAULT 0,
|
|
152677
|
+
fingerprint TEXT NOT NULL DEFAULT '',
|
|
152678
|
+
table_epoch INTEGER NOT NULL DEFAULT 0,
|
|
152679
|
+
dims INTEGER NOT NULL DEFAULT 0,
|
|
152680
|
+
provenance_json TEXT NOT NULL DEFAULT '{}',
|
|
152681
|
+
updated_at INTEGER NOT NULL DEFAULT 0,
|
|
152682
|
+
PRIMARY KEY(project_path, scope, model_id)
|
|
152683
|
+
);
|
|
152684
|
+
CREATE TABLE IF NOT EXISTS embedding_measurement_corpus (
|
|
152685
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
152686
|
+
session_id TEXT NOT NULL,
|
|
152687
|
+
project_path TEXT NOT NULL DEFAULT '',
|
|
152688
|
+
dedup_key TEXT NOT NULL DEFAULT '',
|
|
152689
|
+
cohort_key TEXT NOT NULL DEFAULT '',
|
|
152690
|
+
query_text_hash TEXT NOT NULL DEFAULT '',
|
|
152691
|
+
primary_result_ids_json TEXT NOT NULL DEFAULT '[]',
|
|
152692
|
+
shadow_result_ids_json TEXT NOT NULL DEFAULT '[]',
|
|
152693
|
+
primary_latency_ms INTEGER,
|
|
152694
|
+
shadow_latency_ms INTEGER,
|
|
152695
|
+
primary_failed INTEGER NOT NULL DEFAULT 0,
|
|
152696
|
+
shadow_failed INTEGER NOT NULL DEFAULT 0,
|
|
152697
|
+
primary_model_id TEXT NOT NULL DEFAULT '',
|
|
152698
|
+
shadow_model_id TEXT NOT NULL DEFAULT '',
|
|
152699
|
+
primary_fingerprint TEXT NOT NULL DEFAULT '',
|
|
152700
|
+
shadow_fingerprint TEXT NOT NULL DEFAULT '',
|
|
152701
|
+
primary_epoch INTEGER NOT NULL DEFAULT 0,
|
|
152702
|
+
shadow_epoch INTEGER NOT NULL DEFAULT 0,
|
|
152703
|
+
corpus_hash TEXT NOT NULL DEFAULT '',
|
|
152704
|
+
coverage_json TEXT NOT NULL DEFAULT '{}',
|
|
152705
|
+
created_at INTEGER NOT NULL DEFAULT 0,
|
|
152706
|
+
UNIQUE(dedup_key, cohort_key)
|
|
152707
|
+
);
|
|
152708
|
+
CREATE INDEX IF NOT EXISTS idx_embedding_measurement_session
|
|
152709
|
+
ON embedding_measurement_corpus(session_id, created_at);
|
|
152710
|
+
`);
|
|
152711
|
+
}
|
|
152604
152712
|
}
|
|
152605
152713
|
];
|
|
152606
152714
|
var LATEST_MIGRATION_VERSION = MIGRATIONS.reduce((max, m) => Math.max(max, m.version), 0);
|
|
@@ -152629,7 +152737,14 @@ function isSiblingMigrationConflict(db, error, version) {
|
|
|
152629
152737
|
return confirmed != null;
|
|
152630
152738
|
}
|
|
152631
152739
|
function runMigrations(db) {
|
|
152632
|
-
|
|
152740
|
+
try {
|
|
152741
|
+
ensureMigrationsTable(db);
|
|
152742
|
+
} catch (error) {
|
|
152743
|
+
if (isSqliteLockError(error)) {
|
|
152744
|
+
throw new MigrationLockBusyError(`failed to prepare migration lock: ${error instanceof Error ? error.message : String(error)}`);
|
|
152745
|
+
}
|
|
152746
|
+
throw error;
|
|
152747
|
+
}
|
|
152633
152748
|
let loggedPlan = false;
|
|
152634
152749
|
while (true) {
|
|
152635
152750
|
let migration;
|
|
@@ -152653,6 +152768,9 @@ function runMigrations(db) {
|
|
|
152653
152768
|
break;
|
|
152654
152769
|
log(`[migrations] applied v${migration.version}: ${migration.description}`);
|
|
152655
152770
|
} catch (error) {
|
|
152771
|
+
if (!migration && isSqliteLockError(error)) {
|
|
152772
|
+
throw new MigrationLockBusyError(`failed to acquire migration write lock: ${error instanceof Error ? error.message : String(error)}`);
|
|
152773
|
+
}
|
|
152656
152774
|
if (migration && isSiblingMigrationConflict(db, error, migration.version)) {
|
|
152657
152775
|
log(`[migrations] v${migration.version} already applied by sibling instance — resuming with re-read version`);
|
|
152658
152776
|
const reReadVersion = getCurrentVersion(db);
|
|
@@ -152670,6 +152788,26 @@ function runMigrations(db) {
|
|
|
152670
152788
|
log(`[migrations] schema version now: ${MIGRATIONS[MIGRATIONS.length - 1].version}`);
|
|
152671
152789
|
}
|
|
152672
152790
|
}
|
|
152791
|
+
async function runMigrationsWithRetry(db, options = {}) {
|
|
152792
|
+
const retryDelaysMs = options.retryDelaysMs ?? MIGRATION_LOCK_RETRY_DELAYS_MS;
|
|
152793
|
+
const sleep = options.sleep ?? ((delayMs) => new Promise((resolve) => setTimeout(resolve, delayMs)));
|
|
152794
|
+
const totalAttempts = retryDelaysMs.length + 1;
|
|
152795
|
+
for (let attempt = 1;attempt <= totalAttempts; attempt += 1) {
|
|
152796
|
+
log(`[migrations] migration lock check attempt ${attempt}/${totalAttempts}`);
|
|
152797
|
+
try {
|
|
152798
|
+
runMigrations(db);
|
|
152799
|
+
return;
|
|
152800
|
+
} catch (error) {
|
|
152801
|
+
if (!(error instanceof MigrationLockBusyError))
|
|
152802
|
+
throw error;
|
|
152803
|
+
const delayMs = retryDelaysMs[attempt - 1];
|
|
152804
|
+
if (delayMs === undefined)
|
|
152805
|
+
throw error;
|
|
152806
|
+
log(`[migrations] migration write lock is busy; retrying attempt ${attempt + 1}/${totalAttempts} in ${delayMs}ms`);
|
|
152807
|
+
await sleep(delayMs);
|
|
152808
|
+
}
|
|
152809
|
+
}
|
|
152810
|
+
}
|
|
152673
152811
|
// ../plugin/src/features/magic-context/project-docs-hash.ts
|
|
152674
152812
|
import { createHash as createHash3 } from "node:crypto";
|
|
152675
152813
|
import { lstatSync, readFileSync as readFileSync3, statSync as statSync3 } from "node:fs";
|
|
@@ -152814,14 +152952,19 @@ function countRows(db, table, sessionId) {
|
|
|
152814
152952
|
const row = db.prepare(`SELECT COUNT(*) AS count FROM ${table} WHERE session_id = ?`).get(sessionId);
|
|
152815
152953
|
return typeof row?.count === "number" ? row.count : 0;
|
|
152816
152954
|
}
|
|
152817
|
-
function
|
|
152955
|
+
function mapMessageId(filter, messageId) {
|
|
152956
|
+
if (messageId === null)
|
|
152957
|
+
return null;
|
|
152958
|
+
return filter.mapMessageId?.(messageId) ?? messageId;
|
|
152959
|
+
}
|
|
152960
|
+
function filterIdBlob(raw, filter) {
|
|
152818
152961
|
if (!raw)
|
|
152819
152962
|
return "";
|
|
152820
152963
|
try {
|
|
152821
152964
|
const value = JSON.parse(raw);
|
|
152822
152965
|
if (!Array.isArray(value))
|
|
152823
152966
|
return "";
|
|
152824
|
-
const filtered = value.filter((id) => typeof id === "string" && includeMessageId(id));
|
|
152967
|
+
const filtered = value.filter((id) => typeof id === "string" && filter.includeMessageId(id)).map((id) => mapMessageId(filter, id));
|
|
152825
152968
|
return filtered.length > 0 ? JSON.stringify(filtered) : "";
|
|
152826
152969
|
} catch {
|
|
152827
152970
|
return "";
|
|
@@ -152858,16 +153001,16 @@ function copySessionStateForClone(db, sourceSessionId, destinationSessionId, fil
|
|
|
152858
153001
|
const endMessage = filter.resolveBoundaryOrdinal(row.end_message_id);
|
|
152859
153002
|
if (startMessage === undefined || endMessage === undefined)
|
|
152860
153003
|
continue;
|
|
152861
|
-
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);
|
|
153004
|
+
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);
|
|
152862
153005
|
copiedCompartments.push({
|
|
152863
153006
|
sequence: row.sequence,
|
|
152864
153007
|
startMessage,
|
|
152865
153008
|
endMessage,
|
|
152866
|
-
startMessageId: row.start_message_id,
|
|
152867
|
-
endMessageId: row.end_message_id
|
|
153009
|
+
startMessageId: mapMessageId(filter, row.start_message_id) ?? row.start_message_id,
|
|
153010
|
+
endMessageId: mapMessageId(filter, row.end_message_id) ?? row.end_message_id
|
|
152868
153011
|
});
|
|
152869
153012
|
}
|
|
152870
|
-
const sourceTags = db.prepare(`SELECT message_id, type, status, byte_size, tag_number, harness,
|
|
153013
|
+
const sourceTags = db.prepare(`SELECT id, message_id, type, status, byte_size, tag_number, harness,
|
|
152871
153014
|
entry_fingerprint, token_count, input_token_count,
|
|
152872
153015
|
reasoning_token_count, reasoning_byte_size, drop_mode, tool_name,
|
|
152873
153016
|
input_byte_size, caveman_depth, tool_owner_message_id
|
|
@@ -152879,6 +153022,7 @@ function copySessionStateForClone(db, sourceSessionId, destinationSessionId, fil
|
|
|
152879
153022
|
caveman_depth, tool_owner_message_id)
|
|
152880
153023
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
152881
153024
|
const copiedTagNumbers = [];
|
|
153025
|
+
const copiedTagIds = new Map;
|
|
152882
153026
|
for (const row of sourceTags) {
|
|
152883
153027
|
if (!filter.includeTag({
|
|
152884
153028
|
messageId: row.message_id,
|
|
@@ -152888,19 +153032,39 @@ function copySessionStateForClone(db, sourceSessionId, destinationSessionId, fil
|
|
|
152888
153032
|
})) {
|
|
152889
153033
|
continue;
|
|
152890
153034
|
}
|
|
152891
|
-
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);
|
|
153035
|
+
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));
|
|
153036
|
+
const insertedTagId = Number(insertResult.lastInsertRowid);
|
|
153037
|
+
if (!Number.isSafeInteger(insertedTagId) || insertedTagId <= 0) {
|
|
153038
|
+
throw new Error(`failed to obtain destination tag id for tag ${row.tag_number}`);
|
|
153039
|
+
}
|
|
153040
|
+
const sourceTagId = filter.mapTagId ? row.id : row.tag_number;
|
|
153041
|
+
const destinationTagId = filter.mapTagId ? filter.mapTagId(sourceTagId, insertedTagId) : sourceTagId;
|
|
153042
|
+
copiedTagIds.set(sourceTagId, destinationTagId);
|
|
152892
153043
|
copiedTagNumbers.push(row.tag_number);
|
|
152893
153044
|
}
|
|
152894
153045
|
if (copiedTagNumbers.length > 0) {
|
|
152895
|
-
const
|
|
152896
|
-
|
|
152897
|
-
|
|
152898
|
-
|
|
152899
|
-
|
|
152900
|
-
db.prepare(
|
|
152901
|
-
|
|
152902
|
-
|
|
152903
|
-
|
|
153046
|
+
const sourceTagIds = [...copiedTagIds.keys()];
|
|
153047
|
+
const placeholders2 = sourceTagIds.map(() => "?").join(", ");
|
|
153048
|
+
const sourceContents = db.prepare(`SELECT tag_id, content, created_at, harness
|
|
153049
|
+
FROM source_contents
|
|
153050
|
+
WHERE session_id = ? AND tag_id IN (${placeholders2})`).all(sourceSessionId, ...sourceTagIds);
|
|
153051
|
+
const insertSourceContent = db.prepare("INSERT INTO source_contents (tag_id, session_id, content, created_at, harness) VALUES (?, ?, ?, ?, ?)");
|
|
153052
|
+
for (const row of sourceContents) {
|
|
153053
|
+
const destinationTagId = copiedTagIds.get(row.tag_id);
|
|
153054
|
+
if (destinationTagId === undefined)
|
|
153055
|
+
continue;
|
|
153056
|
+
insertSourceContent.run(destinationTagId, destinationSessionId, row.content, row.created_at, row.harness);
|
|
153057
|
+
}
|
|
153058
|
+
const pendingOps = db.prepare(`SELECT tag_id, operation, queued_at, harness
|
|
153059
|
+
FROM pending_ops
|
|
153060
|
+
WHERE session_id = ? AND tag_id IN (${placeholders2})`).all(sourceSessionId, ...sourceTagIds);
|
|
153061
|
+
const insertPendingOp = db.prepare("INSERT INTO pending_ops (session_id, tag_id, operation, queued_at, harness) VALUES (?, ?, ?, ?, ?)");
|
|
153062
|
+
for (const row of pendingOps) {
|
|
153063
|
+
const destinationTagId = copiedTagIds.get(row.tag_id);
|
|
153064
|
+
if (destinationTagId === undefined)
|
|
153065
|
+
continue;
|
|
153066
|
+
insertPendingOp.run(destinationSessionId, destinationTagId, row.operation, row.queued_at, row.harness);
|
|
153067
|
+
}
|
|
152904
153068
|
}
|
|
152905
153069
|
const meta = db.prepare(`SELECT cleared_reasoning_through_tag, tool_reclaim_watermark,
|
|
152906
153070
|
pi_stable_id_scheme, stripped_placeholder_ids,
|
|
@@ -152936,7 +153100,7 @@ function copySessionStateForClone(db, sourceSessionId, destinationSessionId, fil
|
|
|
152936
153100
|
todo_synthetic_anchor_message_id = excluded.todo_synthetic_anchor_message_id,
|
|
152937
153101
|
todo_synthetic_state_json = excluded.todo_synthetic_state_json,
|
|
152938
153102
|
cached_m0_bytes = NULL,
|
|
152939
|
-
cached_m1_bytes = NULL`).run(destinationSessionId, getHarness(), maxCopiedTag, clampWatermark(meta?.cleared_reasoning_through_tag ?? null, maxCopiedTag), clampWatermark(meta?.tool_reclaim_watermark ?? null, maxCopiedTag), typeof meta?.pi_stable_id_scheme === "number" && Number.isFinite(meta.pi_stable_id_scheme) ? meta.pi_stable_id_scheme : null, filterIdBlob(meta?.stripped_placeholder_ids ?? null, filter
|
|
153103
|
+
cached_m1_bytes = NULL`).run(destinationSessionId, getHarness(), maxCopiedTag, clampWatermark(meta?.cleared_reasoning_through_tag ?? null, maxCopiedTag), clampWatermark(meta?.tool_reclaim_watermark ?? null, maxCopiedTag), typeof meta?.pi_stable_id_scheme === "number" && Number.isFinite(meta.pi_stable_id_scheme) ? meta.pi_stable_id_scheme : null, filterIdBlob(meta?.stripped_placeholder_ids ?? null, filter), filterIdBlob(meta?.stale_reduce_stripped_ids ?? null, filter), filterIdBlob(meta?.processed_image_stripped_ids ?? null, filter), pendingMarker, migrateTodo ? meta?.last_todo_state ?? "" : "", migrateTodo ? meta?.todo_synthetic_call_id ?? "" : "", migrateTodo ? mapMessageId(filter, todoAnchor) ?? "" : "", migrateTodo ? meta?.todo_synthetic_state_json ?? "" : "");
|
|
152940
153104
|
const pendingOpsRow = db.prepare("SELECT COUNT(*) AS count FROM pending_ops WHERE session_id = ?").get(destinationSessionId);
|
|
152941
153105
|
return {
|
|
152942
153106
|
kind: "migrated",
|
|
@@ -152949,8 +153113,8 @@ function copySessionStateForClone(db, sourceSessionId, destinationSessionId, fil
|
|
|
152949
153113
|
}
|
|
152950
153114
|
// ../plugin/src/features/magic-context/storage-db.ts
|
|
152951
153115
|
import { chmodSync, copyFileSync, cpSync, existsSync as existsSync7, mkdirSync as mkdirSync4, mkdtempSync } from "node:fs";
|
|
152952
|
-
init_data_path();
|
|
152953
153116
|
import { dirname as dirname3, join as join5 } from "node:path";
|
|
153117
|
+
init_data_path();
|
|
152954
153118
|
|
|
152955
153119
|
// ../plugin/src/shared/error-message.ts
|
|
152956
153120
|
function getErrorMessage(error) {
|
|
@@ -153342,11 +153506,12 @@ function backfillToolOwnersInChunks(db, result) {
|
|
|
153342
153506
|
|
|
153343
153507
|
// ../plugin/src/features/magic-context/storage-db.ts
|
|
153344
153508
|
var databases = new Map;
|
|
153509
|
+
var pendingAsyncOpens = new Map;
|
|
153345
153510
|
var persistenceByDatabase = new WeakMap;
|
|
153346
153511
|
var persistenceErrorByDatabase = new WeakMap;
|
|
153347
153512
|
var pathByDatabase = new WeakMap;
|
|
153348
153513
|
var lastSchemaFenceRejection = null;
|
|
153349
|
-
var LATEST_SUPPORTED_VERSION =
|
|
153514
|
+
var LATEST_SUPPORTED_VERSION = 53;
|
|
153350
153515
|
var PERMISSIONS_ENFORCEABLE = process.platform !== "win32";
|
|
153351
153516
|
function ensureSecureStorageDir(dir) {
|
|
153352
153517
|
mkdirSync4(dir, { recursive: true, mode: 448 });
|
|
@@ -153476,6 +153641,34 @@ function runSqliteOptimize(db) {
|
|
|
153476
153641
|
db.exec("PRAGMA optimize");
|
|
153477
153642
|
} catch {}
|
|
153478
153643
|
}
|
|
153644
|
+
function finishDatabaseOpen(db, dbPath, explicitDbPath, latestSupportedVersion) {
|
|
153645
|
+
if (!enforceSchemaFence(db, dbPath, latestSupportedVersion)) {
|
|
153646
|
+
closeQuietly(db);
|
|
153647
|
+
return null;
|
|
153648
|
+
}
|
|
153649
|
+
healWedgedChannel2Claims(db);
|
|
153650
|
+
if (!explicitDbPath) {
|
|
153651
|
+
const runBackfill = () => {
|
|
153652
|
+
try {
|
|
153653
|
+
runToolOwnerBackfill(db);
|
|
153654
|
+
} catch (error) {
|
|
153655
|
+
log(`[magic-context] tool-owner backfill failed (continuing with lazy adoption fallback): ${getErrorMessage(error)}`);
|
|
153656
|
+
}
|
|
153657
|
+
};
|
|
153658
|
+
if (bootQuietRemainingMs() > 0)
|
|
153659
|
+
scheduleAfterBootQuiet(runBackfill);
|
|
153660
|
+
else
|
|
153661
|
+
runBackfill();
|
|
153662
|
+
}
|
|
153663
|
+
setDatabase(db);
|
|
153664
|
+
loadToolDefinitionMeasurements(db);
|
|
153665
|
+
restrictDatabaseFilePermissions(dbPath);
|
|
153666
|
+
databases.set(dbPath, db);
|
|
153667
|
+
pathByDatabase.set(db, dbPath);
|
|
153668
|
+
persistenceByDatabase.set(db, true);
|
|
153669
|
+
persistenceErrorByDatabase.delete(db);
|
|
153670
|
+
return db;
|
|
153671
|
+
}
|
|
153479
153672
|
function initializeDatabase(db) {
|
|
153480
153673
|
db.exec("PRAGMA busy_timeout=5000");
|
|
153481
153674
|
db.exec("PRAGMA foreign_keys=ON");
|
|
@@ -153733,6 +153926,76 @@ function initializeDatabase(db) {
|
|
|
153733
153926
|
PRIMARY KEY(project_path, scope, model_id)
|
|
153734
153927
|
);
|
|
153735
153928
|
|
|
153929
|
+
CREATE TABLE IF NOT EXISTS embedding_registrations (
|
|
153930
|
+
project_path TEXT PRIMARY KEY,
|
|
153931
|
+
provider_identity TEXT NOT NULL DEFAULT '',
|
|
153932
|
+
model_id TEXT NOT NULL DEFAULT '',
|
|
153933
|
+
chunk_model_id TEXT NOT NULL DEFAULT '',
|
|
153934
|
+
fingerprint TEXT NOT NULL DEFAULT '',
|
|
153935
|
+
table_epoch INTEGER NOT NULL DEFAULT 0,
|
|
153936
|
+
dims INTEGER NOT NULL DEFAULT 0,
|
|
153937
|
+
provenance_json TEXT NOT NULL DEFAULT '{}',
|
|
153938
|
+
generation INTEGER NOT NULL DEFAULT 0,
|
|
153939
|
+
updated_at INTEGER NOT NULL DEFAULT 0
|
|
153940
|
+
);
|
|
153941
|
+
|
|
153942
|
+
CREATE TABLE IF NOT EXISTS synapse_batch_ledger (
|
|
153943
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
153944
|
+
session_id TEXT NOT NULL,
|
|
153945
|
+
project_path TEXT NOT NULL DEFAULT '',
|
|
153946
|
+
scope TEXT NOT NULL DEFAULT '',
|
|
153947
|
+
manifest_json TEXT NOT NULL DEFAULT '{}',
|
|
153948
|
+
request_key TEXT NOT NULL DEFAULT '',
|
|
153949
|
+
job_id TEXT,
|
|
153950
|
+
cursor TEXT,
|
|
153951
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
153952
|
+
created_at INTEGER NOT NULL DEFAULT 0,
|
|
153953
|
+
updated_at INTEGER NOT NULL DEFAULT 0,
|
|
153954
|
+
UNIQUE(session_id, request_key)
|
|
153955
|
+
);
|
|
153956
|
+
CREATE INDEX IF NOT EXISTS idx_synapse_batch_ledger_session
|
|
153957
|
+
ON synapse_batch_ledger(session_id, updated_at);
|
|
153958
|
+
|
|
153959
|
+
CREATE TABLE IF NOT EXISTS shadow_embedding_registrations (
|
|
153960
|
+
project_path TEXT NOT NULL,
|
|
153961
|
+
scope TEXT NOT NULL CHECK(scope IN ('memory', 'commit', 'chunk')),
|
|
153962
|
+
model_id TEXT NOT NULL,
|
|
153963
|
+
generation INTEGER NOT NULL DEFAULT 0,
|
|
153964
|
+
fingerprint TEXT NOT NULL DEFAULT '',
|
|
153965
|
+
table_epoch INTEGER NOT NULL DEFAULT 0,
|
|
153966
|
+
dims INTEGER NOT NULL DEFAULT 0,
|
|
153967
|
+
provenance_json TEXT NOT NULL DEFAULT '{}',
|
|
153968
|
+
updated_at INTEGER NOT NULL DEFAULT 0,
|
|
153969
|
+
PRIMARY KEY(project_path, scope, model_id)
|
|
153970
|
+
);
|
|
153971
|
+
|
|
153972
|
+
CREATE TABLE IF NOT EXISTS embedding_measurement_corpus (
|
|
153973
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
153974
|
+
session_id TEXT NOT NULL,
|
|
153975
|
+
project_path TEXT NOT NULL DEFAULT '',
|
|
153976
|
+
dedup_key TEXT NOT NULL DEFAULT '',
|
|
153977
|
+
cohort_key TEXT NOT NULL DEFAULT '',
|
|
153978
|
+
query_text_hash TEXT NOT NULL DEFAULT '',
|
|
153979
|
+
primary_result_ids_json TEXT NOT NULL DEFAULT '[]',
|
|
153980
|
+
shadow_result_ids_json TEXT NOT NULL DEFAULT '[]',
|
|
153981
|
+
primary_latency_ms INTEGER,
|
|
153982
|
+
shadow_latency_ms INTEGER,
|
|
153983
|
+
primary_failed INTEGER NOT NULL DEFAULT 0,
|
|
153984
|
+
shadow_failed INTEGER NOT NULL DEFAULT 0,
|
|
153985
|
+
primary_model_id TEXT NOT NULL DEFAULT '',
|
|
153986
|
+
shadow_model_id TEXT NOT NULL DEFAULT '',
|
|
153987
|
+
primary_fingerprint TEXT NOT NULL DEFAULT '',
|
|
153988
|
+
shadow_fingerprint TEXT NOT NULL DEFAULT '',
|
|
153989
|
+
primary_epoch INTEGER NOT NULL DEFAULT 0,
|
|
153990
|
+
shadow_epoch INTEGER NOT NULL DEFAULT 0,
|
|
153991
|
+
corpus_hash TEXT NOT NULL DEFAULT '',
|
|
153992
|
+
coverage_json TEXT NOT NULL DEFAULT '{}',
|
|
153993
|
+
created_at INTEGER NOT NULL DEFAULT 0,
|
|
153994
|
+
UNIQUE(dedup_key, cohort_key)
|
|
153995
|
+
);
|
|
153996
|
+
CREATE INDEX IF NOT EXISTS idx_embedding_measurement_session
|
|
153997
|
+
ON embedding_measurement_corpus(session_id, created_at);
|
|
153998
|
+
|
|
153736
153999
|
CREATE TABLE IF NOT EXISTS memory_verifications (
|
|
153737
154000
|
memory_id INTEGER NOT NULL REFERENCES memories(id) ON DELETE CASCADE,
|
|
153738
154001
|
file_path TEXT NOT NULL,
|
|
@@ -154473,32 +154736,90 @@ function openDatabase(dbPathOrOptions) {
|
|
|
154473
154736
|
}
|
|
154474
154737
|
initializeDatabase(db);
|
|
154475
154738
|
runMigrations(db);
|
|
154476
|
-
|
|
154477
|
-
closeQuietly(db);
|
|
154478
|
-
return null;
|
|
154479
|
-
}
|
|
154480
|
-
healWedgedChannel2Claims(db);
|
|
154481
|
-
if (!explicitDbPath) {
|
|
154482
|
-
try {
|
|
154483
|
-
runToolOwnerBackfill(db);
|
|
154484
|
-
} catch (error) {
|
|
154485
|
-
log(`[magic-context] tool-owner backfill failed (continuing with lazy adoption fallback): ${getErrorMessage(error)}`);
|
|
154486
|
-
}
|
|
154487
|
-
}
|
|
154488
|
-
setDatabase(db);
|
|
154489
|
-
loadToolDefinitionMeasurements(db);
|
|
154490
|
-
restrictDatabaseFilePermissions(dbPath);
|
|
154491
|
-
databases.set(dbPath, db);
|
|
154492
|
-
pathByDatabase.set(db, dbPath);
|
|
154493
|
-
persistenceByDatabase.set(db, true);
|
|
154494
|
-
persistenceErrorByDatabase.delete(db);
|
|
154495
|
-
return db;
|
|
154739
|
+
return finishDatabaseOpen(db, dbPath, explicitDbPath, latestSupportedVersion);
|
|
154496
154740
|
} catch (error) {
|
|
154497
154741
|
const detail = getErrorMessage(error);
|
|
154498
154742
|
log(`[magic-context] storage fatal: failed to open ${dbPath}: ${detail}`);
|
|
154499
154743
|
throw new Error(`[magic-context] storage unavailable: ${detail}. Magic Context is disabled for this run; check log for details.`);
|
|
154500
154744
|
}
|
|
154501
154745
|
}
|
|
154746
|
+
async function openDatabaseAsync(dbPathOrOptions) {
|
|
154747
|
+
const options = typeof dbPathOrOptions === "string" ? { dbPath: dbPathOrOptions } : dbPathOrOptions;
|
|
154748
|
+
const explicitDbPath = options?.dbPath !== undefined;
|
|
154749
|
+
const { dbDir, dbPath } = resolveDatabasePath(options?.dbPath);
|
|
154750
|
+
const latestSupportedVersion = getRuntimeLatestSupportedVersion(options);
|
|
154751
|
+
const existing = databases.get(dbPath);
|
|
154752
|
+
if (existing) {
|
|
154753
|
+
if (!enforceSchemaFence(existing, dbPath, latestSupportedVersion))
|
|
154754
|
+
return null;
|
|
154755
|
+
if (!persistenceByDatabase.has(existing))
|
|
154756
|
+
persistenceByDatabase.set(existing, true);
|
|
154757
|
+
healWedgedChannel2Claims(existing);
|
|
154758
|
+
return existing;
|
|
154759
|
+
}
|
|
154760
|
+
const pending = pendingAsyncOpens.get(dbPath);
|
|
154761
|
+
if (pending)
|
|
154762
|
+
return pending;
|
|
154763
|
+
const opening = (async () => {
|
|
154764
|
+
let db;
|
|
154765
|
+
try {
|
|
154766
|
+
if (!explicitDbPath)
|
|
154767
|
+
migrateLegacyStorageIfNeeded(dbPath, dbDir);
|
|
154768
|
+
ensureSecureStorageDir(dbDir);
|
|
154769
|
+
db = new Database(dbPath);
|
|
154770
|
+
if (!enforceSchemaFence(db, dbPath, latestSupportedVersion)) {
|
|
154771
|
+
closeQuietly(db);
|
|
154772
|
+
return null;
|
|
154773
|
+
}
|
|
154774
|
+
initializeDatabase(db);
|
|
154775
|
+
await runMigrationsWithRetry(db);
|
|
154776
|
+
return finishDatabaseOpen(db, dbPath, explicitDbPath, latestSupportedVersion);
|
|
154777
|
+
} catch (error) {
|
|
154778
|
+
if (db)
|
|
154779
|
+
closeQuietly(db);
|
|
154780
|
+
const detail = getErrorMessage(error);
|
|
154781
|
+
log(`[magic-context] storage fatal: failed to open ${dbPath}: ${detail}`);
|
|
154782
|
+
throw new Error(`[magic-context] storage unavailable: ${detail}. Magic Context is disabled for this run; check log for details.`);
|
|
154783
|
+
}
|
|
154784
|
+
})();
|
|
154785
|
+
pendingAsyncOpens.set(dbPath, opening);
|
|
154786
|
+
try {
|
|
154787
|
+
return await opening;
|
|
154788
|
+
} finally {
|
|
154789
|
+
if (pendingAsyncOpens.get(dbPath) === opening)
|
|
154790
|
+
pendingAsyncOpens.delete(dbPath);
|
|
154791
|
+
}
|
|
154792
|
+
}
|
|
154793
|
+
// ../plugin/src/features/magic-context/storage-embedding-measurements.ts
|
|
154794
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
154795
|
+
function normalizedQueryHash(query) {
|
|
154796
|
+
const normalized = query.trim().replace(/\s+/g, " ").toLowerCase();
|
|
154797
|
+
return createHash4("sha256").update(normalized).digest("hex");
|
|
154798
|
+
}
|
|
154799
|
+
function recordEmbeddingMeasurement(db, input) {
|
|
154800
|
+
const queryTextHash = normalizedQueryHash(input.queryText);
|
|
154801
|
+
const dedupKey = queryTextHash;
|
|
154802
|
+
const result = db.prepare(`INSERT OR IGNORE INTO embedding_measurement_corpus
|
|
154803
|
+
(session_id, project_path, dedup_key, cohort_key, query_text_hash,
|
|
154804
|
+
primary_result_ids_json, shadow_result_ids_json, primary_latency_ms, shadow_latency_ms,
|
|
154805
|
+
primary_failed, shadow_failed, primary_model_id, shadow_model_id,
|
|
154806
|
+
primary_fingerprint, shadow_fingerprint, primary_epoch, shadow_epoch,
|
|
154807
|
+
corpus_hash, coverage_json, created_at)
|
|
154808
|
+
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());
|
|
154809
|
+
return result.changes > 0;
|
|
154810
|
+
}
|
|
154811
|
+
function beginSynapseBatchLedger(db, input) {
|
|
154812
|
+
const now = Date.now();
|
|
154813
|
+
db.prepare(`INSERT INTO synapse_batch_ledger
|
|
154814
|
+
(session_id, project_path, scope, manifest_json, request_key, status, created_at, updated_at)
|
|
154815
|
+
VALUES (?, ?, ?, ?, ?, 'pending', ?, ?)
|
|
154816
|
+
ON CONFLICT(session_id, request_key) DO UPDATE SET
|
|
154817
|
+
manifest_json = excluded.manifest_json,
|
|
154818
|
+
updated_at = excluded.updated_at`).run(input.sessionId, input.projectPath, input.scope, JSON.stringify(input.manifest), input.requestKey, now, now);
|
|
154819
|
+
}
|
|
154820
|
+
function finishSynapseBatchLedger(db, sessionId, requestKey, status) {
|
|
154821
|
+
db.prepare("UPDATE synapse_batch_ledger SET status = ?, updated_at = ? WHERE session_id = ? AND request_key = ?").run(status, Date.now(), sessionId, requestKey);
|
|
154822
|
+
}
|
|
154502
154823
|
// ../plugin/src/features/magic-context/storage-m0-mutation-log.ts
|
|
154503
154824
|
var M0_MUTATION_TYPES = new Set([
|
|
154504
154825
|
"compartment_delete",
|
|
@@ -154658,6 +154979,7 @@ function getMaxMemoryMutationIdForProjects(db, projectPaths) {
|
|
|
154658
154979
|
}
|
|
154659
154980
|
// ../plugin/src/features/magic-context/storage-meta-persisted.ts
|
|
154660
154981
|
init_logger();
|
|
154982
|
+
var emergencyRecoveryArmedSessions = new Set;
|
|
154661
154983
|
var CAS_RETRY_LIMIT = 5;
|
|
154662
154984
|
var AUTO_SEARCH_NO_HINT_REASONS = new Set([
|
|
154663
154985
|
"below-threshold",
|
|
@@ -155379,6 +155701,7 @@ function getOverflowState(db, sessionId, modelKey) {
|
|
|
155379
155701
|
};
|
|
155380
155702
|
}
|
|
155381
155703
|
function recordOverflowDetected(db, sessionId, reportedLimit, modelKey, origin = "provider_overflow") {
|
|
155704
|
+
emergencyRecoveryArmedSessions.add(sessionId);
|
|
155382
155705
|
db.transaction(() => {
|
|
155383
155706
|
ensureSessionMetaRow(db, sessionId);
|
|
155384
155707
|
if (typeof reportedLimit === "number" && reportedLimit > 0) {
|
|
@@ -155397,6 +155720,7 @@ function clearEmergencyRecovery(db, sessionId) {
|
|
|
155397
155720
|
db.prepare("UPDATE session_meta SET needs_emergency_recovery = 0, emergency_recovery_origin = '' WHERE session_id = ?").run(sessionId);
|
|
155398
155721
|
}
|
|
155399
155722
|
})();
|
|
155723
|
+
emergencyRecoveryArmedSessions.delete(sessionId);
|
|
155400
155724
|
}
|
|
155401
155725
|
function clearDetectedContextLimit(db, sessionId) {
|
|
155402
155726
|
db.transaction(() => {
|
|
@@ -156902,7 +157226,7 @@ var ERROR_CLASSES = new Set([
|
|
|
156902
157226
|
]);
|
|
156903
157227
|
// ../plugin/src/features/magic-context/v22-deferred-backfill.ts
|
|
156904
157228
|
init_logger();
|
|
156905
|
-
import { createHash as
|
|
157229
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
156906
157230
|
import { realpathSync as realpathSync2 } from "node:fs";
|
|
156907
157231
|
import path5 from "node:path";
|
|
156908
157232
|
|
|
@@ -156964,7 +157288,7 @@ function computeLegacyRustDirIdentity(rawProjectPath) {
|
|
|
156964
157288
|
} catch {
|
|
156965
157289
|
canonical = path5.isAbsolute(rawProjectPath) ? rawProjectPath : path5.join(process.cwd(), rawProjectPath);
|
|
156966
157290
|
}
|
|
156967
|
-
return `dir:${
|
|
157291
|
+
return `dir:${createHash5("sha256").update(canonical, "utf8").digest("hex")}`;
|
|
156968
157292
|
}
|
|
156969
157293
|
function upsertRekeyMap(db, oldProjectPath, newProjectPath, rekeyedAt) {
|
|
156970
157294
|
db.prepare(`INSERT INTO v22_identity_rekey_map (old_project_path, new_project_path, rekeyed_at)
|
|
@@ -157456,7 +157780,7 @@ function clearNoteNudgeTriggerOnly(db, sessionId) {
|
|
|
157456
157780
|
}
|
|
157457
157781
|
|
|
157458
157782
|
// ../plugin/src/hooks/magic-context/todo-view.ts
|
|
157459
|
-
import { createHash as
|
|
157783
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
157460
157784
|
var TODO_STATUS_PENDING = "pending";
|
|
157461
157785
|
var TODO_STATUS_IN_PROGRESS = "in_progress";
|
|
157462
157786
|
var TODO_STATUS_COMPLETED = "completed";
|
|
@@ -157524,7 +157848,7 @@ function buildSyntheticTodoPart(stateJson) {
|
|
|
157524
157848
|
};
|
|
157525
157849
|
}
|
|
157526
157850
|
function computeSyntheticCallId(stateJson) {
|
|
157527
|
-
const hash =
|
|
157851
|
+
const hash = createHash6("sha256").update(stateJson).digest("hex").slice(0, 16);
|
|
157528
157852
|
return `${SYNTHETIC_CALL_ID_PREFIX}${hash}`;
|
|
157529
157853
|
}
|
|
157530
157854
|
function parseTodoState(stateJson) {
|
|
@@ -159428,7 +159752,7 @@ function openOpenCodeDb() {
|
|
|
159428
159752
|
}
|
|
159429
159753
|
|
|
159430
159754
|
// ../plugin/src/features/magic-context/dreamer/task-executor.ts
|
|
159431
|
-
import { createHash as
|
|
159755
|
+
import { createHash as createHash12 } from "node:crypto";
|
|
159432
159756
|
import { existsSync as existsSync15 } from "node:fs";
|
|
159433
159757
|
|
|
159434
159758
|
// ../plugin/src/agents/dreamer.ts
|
|
@@ -159819,6 +160143,9 @@ function parseFields(json) {
|
|
|
159819
160143
|
} catch {}
|
|
159820
160144
|
return {};
|
|
159821
160145
|
}
|
|
160146
|
+
// ../plugin/src/config/schema/magic-context.ts
|
|
160147
|
+
import { homedir as homedir3 } from "node:os";
|
|
160148
|
+
|
|
159822
160149
|
// ../../node_modules/.bun/zod@4.4.3/node_modules/zod/v4/classic/external.js
|
|
159823
160150
|
var exports_external = {};
|
|
159824
160151
|
__export(exports_external, {
|
|
@@ -174342,8 +174669,18 @@ var HistorianConfigSchema = AgentOverrideConfigSchema.extend({
|
|
|
174342
174669
|
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"),
|
|
174343
174670
|
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: [])`)
|
|
174344
174671
|
}).optional();
|
|
174672
|
+
var EmbeddingFallbackProviderSchema = exports_external.enum(["local", "openai-compatible", "off"]);
|
|
174673
|
+
function expandConfigPath(value) {
|
|
174674
|
+
const trimmed = value.trim();
|
|
174675
|
+
if (trimmed === "~")
|
|
174676
|
+
return homedir3();
|
|
174677
|
+
if (trimmed.startsWith("~/"))
|
|
174678
|
+
return `${homedir3()}/${trimmed.slice(2)}`;
|
|
174679
|
+
return trimmed;
|
|
174680
|
+
}
|
|
174345
174681
|
var BaseEmbeddingConfigSchema = exports_external.object({
|
|
174346
|
-
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."),
|
|
174682
|
+
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."),
|
|
174683
|
+
fallback_provider: EmbeddingFallbackProviderSchema.optional().describe("Fallback provider for the Synapse lane. Required when provider is 'synapse'; local, openai-compatible, and off are valid."),
|
|
174347
174684
|
model: exports_external.string().optional().describe("Embedding model name. Required for openai-compatible, ignored for local."),
|
|
174348
174685
|
endpoint: exports_external.string().optional().describe("API endpoint URL. Required when provider is openai-compatible."),
|
|
174349
174686
|
api_key: exports_external.string().optional().describe("API key for remote embedding provider (optional)"),
|
|
@@ -174352,14 +174689,15 @@ var BaseEmbeddingConfigSchema = exports_external.object({
|
|
|
174352
174689
|
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."),
|
|
174353
174690
|
max_input_tokens: exports_external.number().int().positive().optional().describe("Optional maximum input tokens for chunk embeddings. Defaults conservatively to 512 when omitted.")
|
|
174354
174691
|
}).superRefine((data, ctx) => {
|
|
174355
|
-
|
|
174692
|
+
const validationProvider = data.provider === "synapse" ? data.fallback_provider : data.provider;
|
|
174693
|
+
if (validationProvider === "openai-compatible" && !data.endpoint?.trim()) {
|
|
174356
174694
|
ctx.addIssue({
|
|
174357
174695
|
code: "custom",
|
|
174358
174696
|
path: ["endpoint"],
|
|
174359
174697
|
message: "endpoint is required when embedding.provider is openai-compatible"
|
|
174360
174698
|
});
|
|
174361
174699
|
}
|
|
174362
|
-
if (
|
|
174700
|
+
if (validationProvider === "openai-compatible" && !data.model?.trim()) {
|
|
174363
174701
|
ctx.addIssue({
|
|
174364
174702
|
code: "custom",
|
|
174365
174703
|
path: ["model"],
|
|
@@ -174368,6 +174706,25 @@ var BaseEmbeddingConfigSchema = exports_external.object({
|
|
|
174368
174706
|
}
|
|
174369
174707
|
});
|
|
174370
174708
|
var EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data) => {
|
|
174709
|
+
if (data.provider === "synapse") {
|
|
174710
|
+
const model = data.model?.trim();
|
|
174711
|
+
const endpoint = data.endpoint?.trim();
|
|
174712
|
+
const apiKey = data.api_key?.trim();
|
|
174713
|
+
const inputType = data.input_type?.trim();
|
|
174714
|
+
const queryInputType = data.query_input_type?.trim();
|
|
174715
|
+
const truncate = data.truncate?.trim();
|
|
174716
|
+
return {
|
|
174717
|
+
provider: "synapse",
|
|
174718
|
+
...data.fallback_provider ? { fallback_provider: data.fallback_provider } : {},
|
|
174719
|
+
...model ? { model } : {},
|
|
174720
|
+
...endpoint ? { endpoint } : {},
|
|
174721
|
+
...apiKey ? { api_key: apiKey } : {},
|
|
174722
|
+
...inputType ? { input_type: inputType } : {},
|
|
174723
|
+
...queryInputType ? { query_input_type: queryInputType } : {},
|
|
174724
|
+
...truncate ? { truncate } : {},
|
|
174725
|
+
...data.max_input_tokens ? { max_input_tokens: data.max_input_tokens } : {}
|
|
174726
|
+
};
|
|
174727
|
+
}
|
|
174371
174728
|
if (data.provider === "local") {
|
|
174372
174729
|
return {
|
|
174373
174730
|
provider: "local",
|
|
@@ -174395,6 +174752,7 @@ var EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data) => {
|
|
|
174395
174752
|
});
|
|
174396
174753
|
var MagicContextConfigSchema = exports_external.object({
|
|
174397
174754
|
enabled: exports_external.boolean().default(true).describe("Enable magic context (default: true)"),
|
|
174755
|
+
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.'),
|
|
174398
174756
|
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."),
|
|
174399
174757
|
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."),
|
|
174400
174758
|
historian: HistorianConfigSchema.describe("Historian agent configuration (model, fallback_models, variant, temperature, maxTokens, permission, two_pass, etc.)"),
|
|
@@ -174431,6 +174789,12 @@ var MagicContextConfigSchema = exports_external.object({
|
|
|
174431
174789
|
provider: "local",
|
|
174432
174790
|
model: DEFAULT_LOCAL_EMBEDDING_MODEL
|
|
174433
174791
|
}).describe("Embedding provider configuration"),
|
|
174792
|
+
subc: exports_external.object({
|
|
174793
|
+
connection_file: exports_external.string().trim().min(1).transform(expandConfigPath).describe("Path to the owner-only subc connection file.")
|
|
174794
|
+
}).optional().describe("User-only Synapse daemon connection settings."),
|
|
174795
|
+
shadow_embedding: exports_external.object({
|
|
174796
|
+
enabled: exports_external.boolean().default(false).describe("Developer-only Synapse shadow embedding lane switch.")
|
|
174797
|
+
}).default({ enabled: false }).describe("Developer-only Synapse shadow embedding lane."),
|
|
174434
174798
|
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).'),
|
|
174435
174799
|
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."),
|
|
174436
174800
|
shadow_transform: exports_external.object({
|
|
@@ -174480,7 +174844,7 @@ var MagicContextConfigSchema = exports_external.object({
|
|
|
174480
174844
|
init_logger();
|
|
174481
174845
|
|
|
174482
174846
|
// ../plugin/src/features/magic-context/compartment-chunk-embedding.ts
|
|
174483
|
-
import { createHash as
|
|
174847
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
174484
174848
|
init_logger();
|
|
174485
174849
|
|
|
174486
174850
|
// ../plugin/src/features/magic-context/recursive-text-splitter.ts
|
|
@@ -174692,7 +175056,7 @@ function parseCanonicalLineRange(line) {
|
|
|
174692
175056
|
return { start, end };
|
|
174693
175057
|
}
|
|
174694
175058
|
function hashChunkText(text) {
|
|
174695
|
-
return
|
|
175059
|
+
return createHash7("sha256").update(text).digest("hex");
|
|
174696
175060
|
}
|
|
174697
175061
|
function vectorBlob2(vector) {
|
|
174698
175062
|
return new Uint8Array(vector.buffer, vector.byteOffset, vector.byteLength);
|
|
@@ -175084,14 +175448,2634 @@ function cosineSimilarity(a, b) {
|
|
|
175084
175448
|
return denominator === 0 ? 0 : dotProduct / denominator;
|
|
175085
175449
|
}
|
|
175086
175450
|
|
|
175451
|
+
// ../plugin/src/features/magic-context/memory/embedding-synapse.ts
|
|
175452
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
175453
|
+
|
|
175454
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.4.1/node_modules/@cortexkit/subc-client/dist/client.js
|
|
175455
|
+
import { promises as fs4 } from "node:fs";
|
|
175456
|
+
import { debuglog } from "node:util";
|
|
175457
|
+
|
|
175458
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.4.1/node_modules/@cortexkit/subc-client/dist/auth.js
|
|
175459
|
+
import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
|
175460
|
+
var NONCE_LEN = 32;
|
|
175461
|
+
var MAX_AUTH_MESSAGE_LEN = 4096;
|
|
175462
|
+
var SERVER_PROOF_DOMAIN = "subc-server-v1";
|
|
175463
|
+
var CLIENT_AUTH_DOMAIN = "subc-client-v1";
|
|
175464
|
+
var DEFAULT_CLIENT_ROLE = "client";
|
|
175465
|
+
|
|
175466
|
+
class AuthError extends Error {
|
|
175467
|
+
}
|
|
175468
|
+
function computeProof(key, domain2, clientNonce, serverNonce, daemonId) {
|
|
175469
|
+
const mac3 = createHmac("sha256", Buffer.from(key));
|
|
175470
|
+
mac3.update(Buffer.from(domain2, "utf8"));
|
|
175471
|
+
mac3.update(Buffer.from(clientNonce));
|
|
175472
|
+
mac3.update(Buffer.from(serverNonce));
|
|
175473
|
+
mac3.update(Buffer.from(daemonId));
|
|
175474
|
+
return new Uint8Array(mac3.digest());
|
|
175475
|
+
}
|
|
175476
|
+
function constantTimeEq(a, b) {
|
|
175477
|
+
if (a.length !== b.length)
|
|
175478
|
+
return false;
|
|
175479
|
+
return timingSafeEqual(Buffer.from(a), Buffer.from(b));
|
|
175480
|
+
}
|
|
175481
|
+
async function writeMessage(sock, value, deadlineMs) {
|
|
175482
|
+
const json2 = Buffer.from(JSON.stringify(value), "utf8");
|
|
175483
|
+
if (json2.length > MAX_AUTH_MESSAGE_LEN) {
|
|
175484
|
+
throw new AuthError(`auth message too large: ${json2.length} > ${MAX_AUTH_MESSAGE_LEN}`);
|
|
175485
|
+
}
|
|
175486
|
+
const lenPrefix = new Uint8Array(4);
|
|
175487
|
+
new DataView(lenPrefix.buffer).setUint32(0, json2.length, true);
|
|
175488
|
+
await sock.write(lenPrefix, deadlineMs);
|
|
175489
|
+
await sock.write(json2, deadlineMs);
|
|
175490
|
+
}
|
|
175491
|
+
async function readMessage(sock, deadlineMs) {
|
|
175492
|
+
const lenBytes = await sock.readExact(4, deadlineMs);
|
|
175493
|
+
const len = new DataView(lenBytes.buffer, lenBytes.byteOffset, 4).getUint32(0, true);
|
|
175494
|
+
if (len > MAX_AUTH_MESSAGE_LEN) {
|
|
175495
|
+
throw new AuthError(`auth message too large: ${len} > ${MAX_AUTH_MESSAGE_LEN}`);
|
|
175496
|
+
}
|
|
175497
|
+
const body = len === 0 ? new Uint8Array(0) : await sock.readExact(len, deadlineMs);
|
|
175498
|
+
try {
|
|
175499
|
+
return JSON.parse(Buffer.from(body).toString("utf8"));
|
|
175500
|
+
} catch (err) {
|
|
175501
|
+
throw new AuthError(`auth message JSON decode failed: ${String(err)}`);
|
|
175502
|
+
}
|
|
175503
|
+
}
|
|
175504
|
+
async function authenticateClient(sock, conn, deadlineMs) {
|
|
175505
|
+
const clientNonce = new Uint8Array(randomBytes(NONCE_LEN));
|
|
175506
|
+
await writeMessage(sock, { client_nonce: Array.from(clientNonce), role: DEFAULT_CLIENT_ROLE }, deadlineMs);
|
|
175507
|
+
const proof = await readMessage(sock, deadlineMs);
|
|
175508
|
+
const serverNonce = Uint8Array.from(proof.server_nonce);
|
|
175509
|
+
const daemonId = Uint8Array.from(proof.daemon_id);
|
|
175510
|
+
const serverProof = Uint8Array.from(proof.server_proof);
|
|
175511
|
+
const expected = computeProof(conn.key, SERVER_PROOF_DOMAIN, clientNonce, serverNonce, daemonId);
|
|
175512
|
+
if (!constantTimeEq(expected, serverProof)) {
|
|
175513
|
+
throw new AuthError("server proof mismatch — wrong key or impostor daemon");
|
|
175514
|
+
}
|
|
175515
|
+
if (!constantTimeEq(daemonId, conn.daemonId)) {
|
|
175516
|
+
throw new AuthError("daemon id mismatch — connection file points at a different daemon");
|
|
175517
|
+
}
|
|
175518
|
+
const clientAuth = computeProof(conn.key, CLIENT_AUTH_DOMAIN, clientNonce, serverNonce, daemonId);
|
|
175519
|
+
await writeMessage(sock, { client_auth: Array.from(clientAuth) }, deadlineMs);
|
|
175520
|
+
}
|
|
175521
|
+
|
|
175522
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.4.1/node_modules/@cortexkit/subc-client/dist/connection-file.js
|
|
175523
|
+
import { promises as fs3 } from "node:fs";
|
|
175524
|
+
var SCHEMA_VERSION = 1;
|
|
175525
|
+
var MIN_KEY_LEN = 32;
|
|
175526
|
+
var DAEMON_ID_LEN = 16;
|
|
175527
|
+
|
|
175528
|
+
class ConnectionFileError extends Error {
|
|
175529
|
+
}
|
|
175530
|
+
function toBytes(value, field) {
|
|
175531
|
+
if (!Array.isArray(value) || value.some((n) => typeof n !== "number")) {
|
|
175532
|
+
throw new ConnectionFileError(`connection file field '${field}' must be a JSON array of bytes`);
|
|
175533
|
+
}
|
|
175534
|
+
return Uint8Array.from(value);
|
|
175535
|
+
}
|
|
175536
|
+
function validate(info) {
|
|
175537
|
+
if (info.schema !== SCHEMA_VERSION) {
|
|
175538
|
+
throw new ConnectionFileError(`unsupported connection file schema ${info.schema}; expected ${SCHEMA_VERSION}`);
|
|
175539
|
+
}
|
|
175540
|
+
if (info.endpoints.length === 0) {
|
|
175541
|
+
throw new ConnectionFileError("connection file must include at least one endpoint");
|
|
175542
|
+
}
|
|
175543
|
+
if (info.key.length < MIN_KEY_LEN) {
|
|
175544
|
+
throw new ConnectionFileError(`connection file key is too short: ${info.key.length} bytes, need at least ${MIN_KEY_LEN}`);
|
|
175545
|
+
}
|
|
175546
|
+
if (info.daemonId.length !== DAEMON_ID_LEN) {
|
|
175547
|
+
throw new ConnectionFileError(`connection file daemon_id must be ${DAEMON_ID_LEN} bytes, got ${info.daemonId.length}`);
|
|
175548
|
+
}
|
|
175549
|
+
}
|
|
175550
|
+
async function verifyOwnerOnly(path7) {
|
|
175551
|
+
if (process.platform === "win32")
|
|
175552
|
+
return;
|
|
175553
|
+
const stat = await fs3.stat(path7);
|
|
175554
|
+
const mode = stat.mode & 511;
|
|
175555
|
+
if ((mode & 63) !== 0) {
|
|
175556
|
+
throw new ConnectionFileError(`connection file ${path7} has insecure permissions 0o${mode.toString(8)}; expected owner-only 0600`);
|
|
175557
|
+
}
|
|
175558
|
+
}
|
|
175559
|
+
async function readConnectionFile(path7) {
|
|
175560
|
+
await verifyOwnerOnly(path7);
|
|
175561
|
+
const raw = await fs3.readFile(path7, "utf8");
|
|
175562
|
+
let parsed;
|
|
175563
|
+
try {
|
|
175564
|
+
parsed = JSON.parse(raw);
|
|
175565
|
+
} catch (err) {
|
|
175566
|
+
throw new ConnectionFileError(`connection file JSON read failed for ${path7}: ${String(err)}`);
|
|
175567
|
+
}
|
|
175568
|
+
const endpointsRaw = parsed.endpoints;
|
|
175569
|
+
if (!Array.isArray(endpointsRaw)) {
|
|
175570
|
+
throw new ConnectionFileError("connection file 'endpoints' must be an array");
|
|
175571
|
+
}
|
|
175572
|
+
const endpoints = endpointsRaw.map((e) => {
|
|
175573
|
+
const ep = e;
|
|
175574
|
+
if (typeof ep.host !== "string" || typeof ep.port !== "number") {
|
|
175575
|
+
throw new ConnectionFileError("connection file endpoint must be { host: string, port: number }");
|
|
175576
|
+
}
|
|
175577
|
+
return { host: ep.host, port: ep.port };
|
|
175578
|
+
});
|
|
175579
|
+
const info = {
|
|
175580
|
+
schema: parsed.schema,
|
|
175581
|
+
endpoints,
|
|
175582
|
+
key: toBytes(parsed.key, "key"),
|
|
175583
|
+
daemonId: toBytes(parsed.daemon_id, "daemon_id"),
|
|
175584
|
+
pid: parsed.pid,
|
|
175585
|
+
daemonVer: parsed.daemon_ver ?? ""
|
|
175586
|
+
};
|
|
175587
|
+
validate(info);
|
|
175588
|
+
return info;
|
|
175589
|
+
}
|
|
175590
|
+
|
|
175591
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.4.1/node_modules/@cortexkit/subc-client/dist/envelope.js
|
|
175592
|
+
var PROTOCOL_VERSION = 2;
|
|
175593
|
+
var HEADER_LEN = 21;
|
|
175594
|
+
var FROZEN_PREFIX_LEN = 5;
|
|
175595
|
+
var MAX_FRAME_BODY_LEN = 64 * 1024 * 1024;
|
|
175596
|
+
var FrameType;
|
|
175597
|
+
(function(FrameType2) {
|
|
175598
|
+
FrameType2[FrameType2["Request"] = 0] = "Request";
|
|
175599
|
+
FrameType2[FrameType2["Response"] = 1] = "Response";
|
|
175600
|
+
FrameType2[FrameType2["Push"] = 2] = "Push";
|
|
175601
|
+
FrameType2[FrameType2["StreamData"] = 3] = "StreamData";
|
|
175602
|
+
FrameType2[FrameType2["StreamEnd"] = 4] = "StreamEnd";
|
|
175603
|
+
FrameType2[FrameType2["Error"] = 5] = "Error";
|
|
175604
|
+
FrameType2[FrameType2["Cancel"] = 6] = "Cancel";
|
|
175605
|
+
FrameType2[FrameType2["Ping"] = 7] = "Ping";
|
|
175606
|
+
FrameType2[FrameType2["Pong"] = 8] = "Pong";
|
|
175607
|
+
FrameType2[FrameType2["Hello"] = 9] = "Hello";
|
|
175608
|
+
FrameType2[FrameType2["HelloAck"] = 10] = "HelloAck";
|
|
175609
|
+
FrameType2[FrameType2["Goodbye"] = 11] = "Goodbye";
|
|
175610
|
+
})(FrameType || (FrameType = {}));
|
|
175611
|
+
var FRAME_TYPE_MAX = FrameType.Goodbye;
|
|
175612
|
+
function isPureHeader(ty) {
|
|
175613
|
+
return ty === FrameType.Cancel || ty === FrameType.Ping || ty === FrameType.Pong || ty === FrameType.Goodbye;
|
|
175614
|
+
}
|
|
175615
|
+
var Priority;
|
|
175616
|
+
(function(Priority2) {
|
|
175617
|
+
Priority2[Priority2["Passive"] = 0] = "Passive";
|
|
175618
|
+
Priority2[Priority2["Interactive"] = 1] = "Interactive";
|
|
175619
|
+
Priority2[Priority2["Background"] = 2] = "Background";
|
|
175620
|
+
})(Priority || (Priority = {}));
|
|
175621
|
+
var AdmissionClass;
|
|
175622
|
+
(function(AdmissionClass2) {
|
|
175623
|
+
AdmissionClass2[AdmissionClass2["Normal"] = 0] = "Normal";
|
|
175624
|
+
AdmissionClass2[AdmissionClass2["Expedite"] = 1] = "Expedite";
|
|
175625
|
+
AdmissionClass2[AdmissionClass2["Sheddable"] = 2] = "Sheddable";
|
|
175626
|
+
})(AdmissionClass || (AdmissionClass = {}));
|
|
175627
|
+
var FLAG_BINARY = 1;
|
|
175628
|
+
var FLAG_PRIORITY_MASK = 6;
|
|
175629
|
+
var FLAG_PRIORITY_SHIFT = 1;
|
|
175630
|
+
var FLAG_LAST = 8;
|
|
175631
|
+
var FLAG_ADMISSION_MASK = 48;
|
|
175632
|
+
var FLAG_ADMISSION_SHIFT = 4;
|
|
175633
|
+
var FLAG_RESERVED_MASK = 192;
|
|
175634
|
+
function buildFlags(binary, priority, last, admissionClass = AdmissionClass.Normal) {
|
|
175635
|
+
let flags = 0;
|
|
175636
|
+
if (binary)
|
|
175637
|
+
flags |= FLAG_BINARY;
|
|
175638
|
+
flags |= priority << FLAG_PRIORITY_SHIFT;
|
|
175639
|
+
if (last)
|
|
175640
|
+
flags |= FLAG_LAST;
|
|
175641
|
+
flags |= admissionClass << FLAG_ADMISSION_SHIFT;
|
|
175642
|
+
return flags;
|
|
175643
|
+
}
|
|
175644
|
+
function encodeHeader(header) {
|
|
175645
|
+
const buffer2 = new Uint8Array(HEADER_LEN);
|
|
175646
|
+
const view = new DataView(buffer2.buffer);
|
|
175647
|
+
view.setUint32(0, header.len, true);
|
|
175648
|
+
buffer2[4] = header.ver;
|
|
175649
|
+
buffer2[5] = header.ty;
|
|
175650
|
+
buffer2[6] = header.flags;
|
|
175651
|
+
view.setUint16(7, header.channel, true);
|
|
175652
|
+
view.setUint32(9, header.epoch, true);
|
|
175653
|
+
view.setBigUint64(13, header.corr, true);
|
|
175654
|
+
return buffer2;
|
|
175655
|
+
}
|
|
175656
|
+
|
|
175657
|
+
class DecodeError extends Error {
|
|
175658
|
+
code;
|
|
175659
|
+
constructor(message, code) {
|
|
175660
|
+
super(message);
|
|
175661
|
+
this.code = code;
|
|
175662
|
+
this.name = "DecodeError";
|
|
175663
|
+
}
|
|
175664
|
+
}
|
|
175665
|
+
function decodeHeader(bytes) {
|
|
175666
|
+
if (bytes.length < FROZEN_PREFIX_LEN) {
|
|
175667
|
+
throw new DecodeError(`header shorter than frozen prefix: have ${bytes.length} bytes`, "too_short_for_prefix");
|
|
175668
|
+
}
|
|
175669
|
+
const ver = bytes[4];
|
|
175670
|
+
if (ver !== PROTOCOL_VERSION)
|
|
175671
|
+
throw new DecodeError(`unsupported envelope version ${ver}`, "unsupported_version");
|
|
175672
|
+
if (bytes.length < HEADER_LEN) {
|
|
175673
|
+
throw new DecodeError(`header too short for version: have ${bytes.length} bytes, need ${HEADER_LEN}`, "too_short_for_header");
|
|
175674
|
+
}
|
|
175675
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
175676
|
+
const len = view.getUint32(0, true);
|
|
175677
|
+
const typeByte = bytes[5];
|
|
175678
|
+
if (typeByte > FRAME_TYPE_MAX)
|
|
175679
|
+
throw new DecodeError(`unknown frame type byte ${typeByte}`, "unknown_frame_type");
|
|
175680
|
+
const ty = typeByte;
|
|
175681
|
+
const flags = bytes[6];
|
|
175682
|
+
if ((flags & FLAG_RESERVED_MASK) !== 0) {
|
|
175683
|
+
throw new DecodeError(`reserved flag bits set in flags 0b${flags.toString(2).padStart(8, "0")}`, "reserved_flag_bits");
|
|
175684
|
+
}
|
|
175685
|
+
if ((flags & FLAG_PRIORITY_MASK) >> FLAG_PRIORITY_SHIFT === 3) {
|
|
175686
|
+
throw new DecodeError(`reserved priority bits set in flags 0b${flags.toString(2).padStart(8, "0")}`, "reserved_priority_bits");
|
|
175687
|
+
}
|
|
175688
|
+
const admission = (flags & FLAG_ADMISSION_MASK) >> FLAG_ADMISSION_SHIFT;
|
|
175689
|
+
if (admission === 3) {
|
|
175690
|
+
throw new DecodeError(`reserved admission class set in flags 0b${flags.toString(2).padStart(8, "0")}`, "reserved_admission_class");
|
|
175691
|
+
}
|
|
175692
|
+
if (admission === AdmissionClass.Sheddable && ty !== FrameType.Push && ty !== FrameType.StreamData) {
|
|
175693
|
+
throw new DecodeError(`SHEDDABLE admission class is illegal on ${FrameType[ty]} in flags 0b${flags.toString(2).padStart(8, "0")}`, "sheddable_illegal_frame_type");
|
|
175694
|
+
}
|
|
175695
|
+
const channel = view.getUint16(7, true);
|
|
175696
|
+
const epoch = view.getUint32(9, true);
|
|
175697
|
+
if (channel === 0 && epoch !== 0) {
|
|
175698
|
+
throw new DecodeError(`control channel carried nonzero epoch ${epoch}`, "nonzero_epoch_on_control_channel");
|
|
175699
|
+
}
|
|
175700
|
+
if (isPureHeader(ty) && len !== 0) {
|
|
175701
|
+
throw new DecodeError(`pure-header frame ${FrameType[ty]} declared non-zero body length ${len}`, "pure_header_frame_with_body");
|
|
175702
|
+
}
|
|
175703
|
+
return { len, ver, ty, flags, channel, epoch, corr: view.getBigUint64(13, true) };
|
|
175704
|
+
}
|
|
175705
|
+
function buildFrame(ty, flags, channel, epoch, corr, body) {
|
|
175706
|
+
return buildFrameWithVersion(PROTOCOL_VERSION, ty, flags, channel, epoch, corr, body);
|
|
175707
|
+
}
|
|
175708
|
+
function buildFrameWithVersion(ver, ty, flags, channel, epoch, corr, body) {
|
|
175709
|
+
if (body.length > MAX_FRAME_BODY_LEN) {
|
|
175710
|
+
throw new DecodeError(`frame body ${body.length} exceeds max ${MAX_FRAME_BODY_LEN}`, "frame_body_too_large");
|
|
175711
|
+
}
|
|
175712
|
+
const header = { len: body.length, ver, ty, flags, channel, epoch, corr };
|
|
175713
|
+
decodeHeader(encodeHeader(header));
|
|
175714
|
+
return { header, body };
|
|
175715
|
+
}
|
|
175716
|
+
function encodeFrame(frame) {
|
|
175717
|
+
if (frame.header.len !== frame.body.length) {
|
|
175718
|
+
throw new DecodeError(`frame header length ${frame.header.len} does not match body length ${frame.body.length}`, "frame_length_mismatch");
|
|
175719
|
+
}
|
|
175720
|
+
const header = encodeHeader(frame.header);
|
|
175721
|
+
const output = new Uint8Array(header.length + frame.body.length);
|
|
175722
|
+
output.set(header, 0);
|
|
175723
|
+
output.set(frame.body, header.length);
|
|
175724
|
+
return output;
|
|
175725
|
+
}
|
|
175726
|
+
|
|
175727
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.4.1/node_modules/@cortexkit/subc-client/dist/route-handle.js
|
|
175728
|
+
var connectionToken = new WeakMap;
|
|
175729
|
+
|
|
175730
|
+
class RouteHandle {
|
|
175731
|
+
channel;
|
|
175732
|
+
epoch;
|
|
175733
|
+
constructor(channel, epoch, token) {
|
|
175734
|
+
if (!Number.isInteger(channel) || channel <= 0 || channel > 65535) {
|
|
175735
|
+
throw new RangeError(`route channel must be an integer in 1..65535, got ${channel}`);
|
|
175736
|
+
}
|
|
175737
|
+
if (!Number.isInteger(epoch) || epoch <= 0 || epoch > 4294967295) {
|
|
175738
|
+
throw new RangeError(`route epoch must be an integer in 1..4294967295, got ${epoch}`);
|
|
175739
|
+
}
|
|
175740
|
+
this.channel = channel;
|
|
175741
|
+
this.epoch = epoch;
|
|
175742
|
+
connectionToken.set(this, token);
|
|
175743
|
+
Object.freeze(this);
|
|
175744
|
+
}
|
|
175745
|
+
static create(channel, epoch, token) {
|
|
175746
|
+
return new RouteHandle(channel, epoch, token);
|
|
175747
|
+
}
|
|
175748
|
+
}
|
|
175749
|
+
function createRouteHandle(channel, epoch, token) {
|
|
175750
|
+
const factory = RouteHandle;
|
|
175751
|
+
return factory.create(channel, epoch, token);
|
|
175752
|
+
}
|
|
175753
|
+
|
|
175754
|
+
class StaleRouteHandleError extends Error {
|
|
175755
|
+
handle;
|
|
175756
|
+
code = "stale_route_handle";
|
|
175757
|
+
constructor(handle) {
|
|
175758
|
+
super(`route handle (${handle.channel}, ${handle.epoch}) is not live on the current connection`);
|
|
175759
|
+
this.handle = handle;
|
|
175760
|
+
this.name = "StaleRouteHandleError";
|
|
175761
|
+
}
|
|
175762
|
+
}
|
|
175763
|
+
function newConnectionToken() {
|
|
175764
|
+
return Object.freeze({});
|
|
175765
|
+
}
|
|
175766
|
+
function belongsToConnection(handle, token) {
|
|
175767
|
+
return connectionToken.get(handle) === token;
|
|
175768
|
+
}
|
|
175769
|
+
function sameRouteHandle(left, right) {
|
|
175770
|
+
return left === right;
|
|
175771
|
+
}
|
|
175772
|
+
|
|
175773
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.4.1/node_modules/@cortexkit/subc-client/dist/socket.js
|
|
175774
|
+
import net from "node:net";
|
|
175775
|
+
class SocketClosedError extends Error {
|
|
175776
|
+
}
|
|
175777
|
+
|
|
175778
|
+
class SocketTimeoutError extends Error {
|
|
175779
|
+
}
|
|
175780
|
+
|
|
175781
|
+
class SocketWriteNotQueuedError extends Error {
|
|
175782
|
+
cause;
|
|
175783
|
+
constructor(message, cause) {
|
|
175784
|
+
super(message);
|
|
175785
|
+
this.cause = cause;
|
|
175786
|
+
}
|
|
175787
|
+
}
|
|
175788
|
+
|
|
175789
|
+
class SocketWriteQueuedError extends Error {
|
|
175790
|
+
cause;
|
|
175791
|
+
constructor(message, cause) {
|
|
175792
|
+
super(message);
|
|
175793
|
+
this.cause = cause;
|
|
175794
|
+
}
|
|
175795
|
+
}
|
|
175796
|
+
|
|
175797
|
+
class SubcSocket {
|
|
175798
|
+
sock;
|
|
175799
|
+
chunks = [];
|
|
175800
|
+
buffered = 0;
|
|
175801
|
+
waiter = null;
|
|
175802
|
+
closedErr = null;
|
|
175803
|
+
bufferedBytes() {
|
|
175804
|
+
return this.buffered;
|
|
175805
|
+
}
|
|
175806
|
+
constructor(sock) {
|
|
175807
|
+
this.sock = sock;
|
|
175808
|
+
sock.on("data", (chunk) => {
|
|
175809
|
+
this.chunks.push(chunk);
|
|
175810
|
+
this.buffered += chunk.length;
|
|
175811
|
+
this.tryServe();
|
|
175812
|
+
});
|
|
175813
|
+
const fail = (err) => {
|
|
175814
|
+
if (!this.closedErr)
|
|
175815
|
+
this.closedErr = err;
|
|
175816
|
+
this.tryServe();
|
|
175817
|
+
};
|
|
175818
|
+
sock.on("error", (err) => fail(err instanceof Error ? err : new Error(String(err))));
|
|
175819
|
+
sock.on("end", () => fail(new SocketClosedError("subc closed the connection")));
|
|
175820
|
+
sock.on("close", () => fail(new SocketClosedError("subc connection closed")));
|
|
175821
|
+
}
|
|
175822
|
+
localPort() {
|
|
175823
|
+
return this.sock.localPort ?? null;
|
|
175824
|
+
}
|
|
175825
|
+
static connect(host, port, deadlineMs) {
|
|
175826
|
+
return new Promise((resolve, reject) => {
|
|
175827
|
+
const sock = net.connect({ host, port });
|
|
175828
|
+
sock.setNoDelay(true);
|
|
175829
|
+
const timer = setTimeout(() => {
|
|
175830
|
+
sock.destroy();
|
|
175831
|
+
reject(new SocketTimeoutError(`timed out connecting to ${host}:${port}`));
|
|
175832
|
+
}, Math.max(0, deadlineMs - Date.now()));
|
|
175833
|
+
sock.once("connect", () => {
|
|
175834
|
+
clearTimeout(timer);
|
|
175835
|
+
resolve(new SubcSocket(sock));
|
|
175836
|
+
});
|
|
175837
|
+
sock.once("error", (err) => {
|
|
175838
|
+
clearTimeout(timer);
|
|
175839
|
+
reject(err);
|
|
175840
|
+
});
|
|
175841
|
+
});
|
|
175842
|
+
}
|
|
175843
|
+
async readFrame(headerDeadlineMs, bodyDeadline, onHeader) {
|
|
175844
|
+
const prefix = await this.readExact(FROZEN_PREFIX_LEN, headerDeadlineMs);
|
|
175845
|
+
const version2 = prefix[4];
|
|
175846
|
+
if (version2 !== PROTOCOL_VERSION)
|
|
175847
|
+
throw new DecodeError(`unsupported envelope version ${version2}`, "unsupported_version");
|
|
175848
|
+
const remainder = await this.readExact(HEADER_LEN - FROZEN_PREFIX_LEN, headerDeadlineMs);
|
|
175849
|
+
const headerBytes = new Uint8Array(HEADER_LEN);
|
|
175850
|
+
headerBytes.set(prefix);
|
|
175851
|
+
headerBytes.set(remainder, FROZEN_PREFIX_LEN);
|
|
175852
|
+
const header = decodeHeader(headerBytes);
|
|
175853
|
+
if (header.len > MAX_FRAME_BODY_LEN) {
|
|
175854
|
+
throw new DecodeError(`frame body ${header.len} exceeds max ${MAX_FRAME_BODY_LEN}`, "frame_body_too_large");
|
|
175855
|
+
}
|
|
175856
|
+
onHeader?.();
|
|
175857
|
+
const bodyDeadlineMs = typeof bodyDeadline === "number" ? bodyDeadline : Date.now() + bodyDeadline.afterHeaderMs;
|
|
175858
|
+
const body = header.len === 0 ? new Uint8Array(0) : await this.readExact(header.len, bodyDeadlineMs);
|
|
175859
|
+
return { header, body };
|
|
175860
|
+
}
|
|
175861
|
+
readExact(n, deadlineMs) {
|
|
175862
|
+
if (this.waiter) {
|
|
175863
|
+
return Promise.reject(new Error("concurrent readExact is not supported"));
|
|
175864
|
+
}
|
|
175865
|
+
if (n === 0)
|
|
175866
|
+
return Promise.resolve(new Uint8Array(0));
|
|
175867
|
+
return new Promise((resolve, reject) => {
|
|
175868
|
+
let timer = null;
|
|
175869
|
+
if (Number.isFinite(deadlineMs)) {
|
|
175870
|
+
const remaining = deadlineMs - Date.now();
|
|
175871
|
+
if (remaining <= 0) {
|
|
175872
|
+
reject(new SocketTimeoutError(`timed out waiting for ${n} bytes`));
|
|
175873
|
+
return;
|
|
175874
|
+
}
|
|
175875
|
+
timer = setTimeout(() => {
|
|
175876
|
+
this.waiter = null;
|
|
175877
|
+
reject(new SocketTimeoutError(`timed out waiting for ${n} bytes`));
|
|
175878
|
+
}, remaining);
|
|
175879
|
+
}
|
|
175880
|
+
this.waiter = { need: n, resolve, reject, timer };
|
|
175881
|
+
this.tryServe();
|
|
175882
|
+
});
|
|
175883
|
+
}
|
|
175884
|
+
async write(bytes, deadlineMs) {
|
|
175885
|
+
try {
|
|
175886
|
+
await this.writeTracked(bytes, deadlineMs).completed;
|
|
175887
|
+
} catch (err) {
|
|
175888
|
+
if (err instanceof SocketWriteNotQueuedError || err instanceof SocketWriteQueuedError) {
|
|
175889
|
+
throw err.cause ?? err;
|
|
175890
|
+
}
|
|
175891
|
+
throw err;
|
|
175892
|
+
}
|
|
175893
|
+
}
|
|
175894
|
+
writeTracked(bytes, deadlineMs) {
|
|
175895
|
+
if (this.closedErr) {
|
|
175896
|
+
return {
|
|
175897
|
+
queued: false,
|
|
175898
|
+
completed: Promise.reject(new SocketWriteNotQueuedError("subc socket was closed before bytes could be queued", this.closedErr))
|
|
175899
|
+
};
|
|
175900
|
+
}
|
|
175901
|
+
let queued = false;
|
|
175902
|
+
let settled = false;
|
|
175903
|
+
let timer = null;
|
|
175904
|
+
const completed = new Promise((resolve, reject) => {
|
|
175905
|
+
const settle = (run) => {
|
|
175906
|
+
if (settled)
|
|
175907
|
+
return;
|
|
175908
|
+
settled = true;
|
|
175909
|
+
if (timer)
|
|
175910
|
+
clearTimeout(timer);
|
|
175911
|
+
run();
|
|
175912
|
+
};
|
|
175913
|
+
const remaining = deadlineMs - Date.now();
|
|
175914
|
+
if (remaining <= 0) {
|
|
175915
|
+
settle(() => reject(new SocketWriteNotQueuedError("timed out before bytes could be queued to subc", new SocketTimeoutError("timed out writing to subc"))));
|
|
175916
|
+
return;
|
|
175917
|
+
}
|
|
175918
|
+
timer = setTimeout(() => {
|
|
175919
|
+
const timeout = new SocketTimeoutError("timed out writing to subc");
|
|
175920
|
+
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)));
|
|
175921
|
+
}, remaining);
|
|
175922
|
+
try {
|
|
175923
|
+
this.sock.write(Buffer.from(bytes), (err) => {
|
|
175924
|
+
settle(() => {
|
|
175925
|
+
if (err) {
|
|
175926
|
+
reject(new SocketWriteQueuedError("subc socket reported a write error after bytes were handed to the socket", err instanceof Error ? err : new Error(String(err))));
|
|
175927
|
+
} else {
|
|
175928
|
+
resolve();
|
|
175929
|
+
}
|
|
175930
|
+
});
|
|
175931
|
+
});
|
|
175932
|
+
queued = true;
|
|
175933
|
+
} catch (err) {
|
|
175934
|
+
settle(() => reject(new SocketWriteNotQueuedError("subc socket write threw before bytes could be queued", err instanceof Error ? err : new Error(String(err)))));
|
|
175935
|
+
}
|
|
175936
|
+
});
|
|
175937
|
+
return { queued, completed };
|
|
175938
|
+
}
|
|
175939
|
+
close() {
|
|
175940
|
+
this.sock.destroy();
|
|
175941
|
+
}
|
|
175942
|
+
tryServe() {
|
|
175943
|
+
const w = this.waiter;
|
|
175944
|
+
if (!w)
|
|
175945
|
+
return;
|
|
175946
|
+
if (this.buffered >= w.need) {
|
|
175947
|
+
const out = this.take(w.need);
|
|
175948
|
+
this.waiter = null;
|
|
175949
|
+
if (w.timer)
|
|
175950
|
+
clearTimeout(w.timer);
|
|
175951
|
+
w.resolve(out);
|
|
175952
|
+
return;
|
|
175953
|
+
}
|
|
175954
|
+
if (this.closedErr) {
|
|
175955
|
+
this.waiter = null;
|
|
175956
|
+
if (w.timer)
|
|
175957
|
+
clearTimeout(w.timer);
|
|
175958
|
+
w.reject(this.closedErr);
|
|
175959
|
+
}
|
|
175960
|
+
}
|
|
175961
|
+
take(n) {
|
|
175962
|
+
const out = Buffer.allocUnsafe(n);
|
|
175963
|
+
let off = 0;
|
|
175964
|
+
while (off < n) {
|
|
175965
|
+
const head = this.chunks[0];
|
|
175966
|
+
const want = n - off;
|
|
175967
|
+
if (head.length <= want) {
|
|
175968
|
+
head.copy(out, off);
|
|
175969
|
+
off += head.length;
|
|
175970
|
+
this.chunks.shift();
|
|
175971
|
+
} else {
|
|
175972
|
+
head.copy(out, off, 0, want);
|
|
175973
|
+
this.chunks[0] = head.subarray(want);
|
|
175974
|
+
off += want;
|
|
175975
|
+
}
|
|
175976
|
+
}
|
|
175977
|
+
this.buffered -= n;
|
|
175978
|
+
return out;
|
|
175979
|
+
}
|
|
175980
|
+
}
|
|
175981
|
+
|
|
175982
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.4.1/node_modules/@cortexkit/subc-client/dist/client.js
|
|
175983
|
+
var debug = debuglog("subc-client");
|
|
175984
|
+
var DEFAULT_HANDSHAKE_TIMEOUT_MS = 1e4;
|
|
175985
|
+
var DEFAULT_REQUEST_TIMEOUT_MS = 30000;
|
|
175986
|
+
var TIMEOUT_ARBITRATION_GRACE_MS = 50;
|
|
175987
|
+
var REQUEST_DEADLINE_MARKER = "request_deadline";
|
|
175988
|
+
var DEADLINE_NO_DROP_CODE = "deadline_exceeded_no_drop_observed";
|
|
175989
|
+
var ROUTE_OPEN_RETRY_DEADLINE_MS = 30000;
|
|
175990
|
+
var BODY_READ_TIMEOUT_MS = 30000;
|
|
175991
|
+
var EMPTY_BODY = new Uint8Array(0);
|
|
175992
|
+
var DEFAULT_MANAGED_TARGET_KIND = "management_surface";
|
|
175993
|
+
var SUBC_MODULE_ID_ENV = "SUBC_MODULE_ID";
|
|
175994
|
+
var SUBC_LAUNCH_NONCE_ENV = "SUBC_LAUNCH_NONCE";
|
|
175995
|
+
var DEFAULT_RECONNECT_BACKOFF = {
|
|
175996
|
+
baseMs: 100,
|
|
175997
|
+
capMs: 2000,
|
|
175998
|
+
maxAttempts: 6
|
|
175999
|
+
};
|
|
176000
|
+
|
|
176001
|
+
class SubcCallError extends Error {
|
|
176002
|
+
kind;
|
|
176003
|
+
code;
|
|
176004
|
+
cause;
|
|
176005
|
+
constructor(kind, message, code, cause) {
|
|
176006
|
+
super(message);
|
|
176007
|
+
this.kind = kind;
|
|
176008
|
+
this.code = code;
|
|
176009
|
+
this.cause = cause;
|
|
176010
|
+
this.name = "SubcCallError";
|
|
176011
|
+
}
|
|
176012
|
+
}
|
|
176013
|
+
|
|
176014
|
+
class SubcError extends Error {
|
|
176015
|
+
code;
|
|
176016
|
+
constructor(message, code) {
|
|
176017
|
+
super(message);
|
|
176018
|
+
this.code = code;
|
|
176019
|
+
}
|
|
176020
|
+
}
|
|
176021
|
+
|
|
176022
|
+
class SubcClient {
|
|
176023
|
+
sock;
|
|
176024
|
+
currentConn;
|
|
176025
|
+
opts;
|
|
176026
|
+
nextCorr = 1n;
|
|
176027
|
+
pending = new Map;
|
|
176028
|
+
lateResponses = new Map;
|
|
176029
|
+
routes = new Map;
|
|
176030
|
+
liveRoutes = new Map;
|
|
176031
|
+
connectionToken = newConnectionToken();
|
|
176032
|
+
ingressEpochDropCount = 0;
|
|
176033
|
+
closedErr = null;
|
|
176034
|
+
closeStarted = false;
|
|
176035
|
+
reconnecting = null;
|
|
176036
|
+
generation = 1;
|
|
176037
|
+
readerActive = false;
|
|
176038
|
+
constructor(sock, currentConn, opts) {
|
|
176039
|
+
this.sock = sock;
|
|
176040
|
+
this.currentConn = currentConn;
|
|
176041
|
+
this.opts = opts;
|
|
176042
|
+
this.readLoop(sock, this.generation);
|
|
176043
|
+
}
|
|
176044
|
+
get conn() {
|
|
176045
|
+
return this.currentConn;
|
|
176046
|
+
}
|
|
176047
|
+
static async connect(opts) {
|
|
176048
|
+
const normalized = normalizeConnectOptions(opts);
|
|
176049
|
+
const opened = await SubcClient.openConnection(normalized);
|
|
176050
|
+
return new SubcClient(opened.sock, opened.conn, normalized);
|
|
176051
|
+
}
|
|
176052
|
+
async catalogList(moduleId) {
|
|
176053
|
+
const body = this.encode(moduleId === undefined ? { op: "catalog.list" } : { op: "catalog.list", module_id: moduleId });
|
|
176054
|
+
const reply = await this.controlRpc(body);
|
|
176055
|
+
const parsed = this.parseJson(reply);
|
|
176056
|
+
return parsed.modules ?? [];
|
|
176057
|
+
}
|
|
176058
|
+
async routeOpen(target, identity, opts = {}) {
|
|
176059
|
+
const consumerIdentity = routeOpenConsumerIdentity(opts);
|
|
176060
|
+
const consumerCapabilities = opts.consumerCapabilities;
|
|
176061
|
+
const body = this.encode({
|
|
176062
|
+
op: "route.open",
|
|
176063
|
+
target,
|
|
176064
|
+
identity,
|
|
176065
|
+
...consumerIdentity ? { consumer_identity: consumerIdentity } : {},
|
|
176066
|
+
...consumerCapabilities !== undefined ? { consumer_capabilities: consumerCapabilities } : {}
|
|
176067
|
+
});
|
|
176068
|
+
let installed = null;
|
|
176069
|
+
const install = (frame) => {
|
|
176070
|
+
if (frame.header.ty !== FrameType.Response)
|
|
176071
|
+
return true;
|
|
176072
|
+
const parsed = this.parseJson(frame);
|
|
176073
|
+
if (typeof parsed.route_channel !== "number" || typeof parsed.route_epoch !== "number") {
|
|
176074
|
+
throw new SubcError(`route.open returned no route handle: ${JSON.stringify(parsed)}`);
|
|
176075
|
+
}
|
|
176076
|
+
installed = this.installRoute(parsed.route_channel, parsed.route_epoch);
|
|
176077
|
+
return true;
|
|
176078
|
+
};
|
|
176079
|
+
const closeLateRoute = (frame) => {
|
|
176080
|
+
if (frame.header.ty !== FrameType.Response)
|
|
176081
|
+
return;
|
|
176082
|
+
try {
|
|
176083
|
+
const parsed = this.parseJson(frame);
|
|
176084
|
+
if (typeof parsed.route_channel !== "number" || typeof parsed.route_epoch !== "number")
|
|
176085
|
+
return;
|
|
176086
|
+
const lateHandle = this.installRoute(parsed.route_channel, parsed.route_epoch);
|
|
176087
|
+
this.failHandle(lateHandle, new SubcError("late route.open was closed", "route_closed"));
|
|
176088
|
+
this.liveRoutes.delete(lateHandle.channel);
|
|
176089
|
+
this.sendRouteGoodbye(lateHandle, true);
|
|
176090
|
+
} catch {
|
|
176091
|
+
this.closeConnectionAfterCleanupFailure();
|
|
176092
|
+
}
|
|
176093
|
+
};
|
|
176094
|
+
await this.controlRpc(body, install, closeLateRoute);
|
|
176095
|
+
if (!installed)
|
|
176096
|
+
throw new SubcError("route.open response was not installed");
|
|
176097
|
+
return installed;
|
|
176098
|
+
}
|
|
176099
|
+
async request(handle, body, opts = {}) {
|
|
176100
|
+
this.assertLiveHandle(handle);
|
|
176101
|
+
const bytes = body instanceof Uint8Array ? body : this.encode(body);
|
|
176102
|
+
const priority = opts.priority ?? Priority.Interactive;
|
|
176103
|
+
const admission = opts.admissionClass ?? AdmissionClass.Normal;
|
|
176104
|
+
const reply = await this.send(handle, bytes, priority, admission, opts.timeoutMs, opts.onProgress);
|
|
176105
|
+
return this.parseJson(reply);
|
|
176106
|
+
}
|
|
176107
|
+
async call(moduleId, method, params, opts = {}) {
|
|
176108
|
+
const body = params === undefined ? { method } : { method, params };
|
|
176109
|
+
let retriedUnknownChannel = false;
|
|
176110
|
+
for (;; ) {
|
|
176111
|
+
const routeHandle = await this.cachedRouteHandle(moduleId, opts);
|
|
176112
|
+
try {
|
|
176113
|
+
return await this.managedRequest(routeHandle, body, opts);
|
|
176114
|
+
} catch (err) {
|
|
176115
|
+
if (!(err instanceof SubcCallError))
|
|
176116
|
+
throw this.terminalCallError("managed call failed", err);
|
|
176117
|
+
if (err.code === "unknown_channel" && !retriedUnknownChannel && !this.closeStarted) {
|
|
176118
|
+
retriedUnknownChannel = true;
|
|
176119
|
+
this.evictRouteHandle(routeHandle);
|
|
176120
|
+
continue;
|
|
176121
|
+
}
|
|
176122
|
+
if (err.kind === "not_sent") {
|
|
176123
|
+
try {
|
|
176124
|
+
await this.reconnectAfterDrop(err);
|
|
176125
|
+
} catch (reconnectErr) {
|
|
176126
|
+
throw this.notSentRecoveryError("managed call was not sent", reconnectErr);
|
|
176127
|
+
}
|
|
176128
|
+
continue;
|
|
176129
|
+
}
|
|
176130
|
+
if (err.kind === "outcome_unknown" && err.code !== DEADLINE_NO_DROP_CODE) {
|
|
176131
|
+
this.scheduleReconnectAfterDrop(err);
|
|
176132
|
+
}
|
|
176133
|
+
throw err;
|
|
176134
|
+
}
|
|
176135
|
+
}
|
|
176136
|
+
}
|
|
176137
|
+
subscribe(handle, body, onEvent, opts = {}) {
|
|
176138
|
+
this.assertLiveHandle(handle);
|
|
176139
|
+
const bytes = body instanceof Uint8Array ? body : this.encode(body);
|
|
176140
|
+
const priority = opts.priority ?? Priority.Interactive;
|
|
176141
|
+
const admission = opts.admissionClass ?? AdmissionClass.Normal;
|
|
176142
|
+
const corr = this.allocateCorr();
|
|
176143
|
+
const key = pendingKey(handle, corr);
|
|
176144
|
+
const closed = new Promise((resolve, reject) => {
|
|
176145
|
+
if (this.closedErr) {
|
|
176146
|
+
reject(this.closedErr);
|
|
176147
|
+
return;
|
|
176148
|
+
}
|
|
176149
|
+
this.pending.set(key, {
|
|
176150
|
+
handle,
|
|
176151
|
+
resolve: () => resolve(),
|
|
176152
|
+
reject,
|
|
176153
|
+
onProgress: onEvent,
|
|
176154
|
+
timer: null,
|
|
176155
|
+
subscription: true
|
|
176156
|
+
});
|
|
176157
|
+
const frame = buildFrame(FrameType.Request, buildFlags(false, priority, false, admission), handle.channel, handle.epoch, corr, bytes);
|
|
176158
|
+
this.sock.write(encodeFrame(frame), Date.now() + DEFAULT_REQUEST_TIMEOUT_MS).catch((err) => {
|
|
176159
|
+
const pending = this.pending.get(key);
|
|
176160
|
+
if (pending)
|
|
176161
|
+
this.rejectPending(key, pending, err instanceof Error ? err : new SubcError(String(err)));
|
|
176162
|
+
});
|
|
176163
|
+
});
|
|
176164
|
+
let cancelled = false;
|
|
176165
|
+
const unsubscribe = () => {
|
|
176166
|
+
if (cancelled)
|
|
176167
|
+
return;
|
|
176168
|
+
cancelled = true;
|
|
176169
|
+
this.cancel(handle, corr, priority);
|
|
176170
|
+
};
|
|
176171
|
+
return { unsubscribe, closed };
|
|
176172
|
+
}
|
|
176173
|
+
cancel(handle, corr, priority = Priority.Interactive) {
|
|
176174
|
+
this.assertLiveHandle(handle);
|
|
176175
|
+
const cancel = buildFrame(FrameType.Cancel, buildFlags(false, priority, false), handle.channel, handle.epoch, corr, EMPTY_BODY);
|
|
176176
|
+
this.sock.write(encodeFrame(cancel), Date.now() + DEFAULT_REQUEST_TIMEOUT_MS).catch(() => {
|
|
176177
|
+
return;
|
|
176178
|
+
});
|
|
176179
|
+
}
|
|
176180
|
+
async routePoll(handle, kind) {
|
|
176181
|
+
this.assertLiveHandle(handle);
|
|
176182
|
+
const body = this.encode({
|
|
176183
|
+
op: "route.poll",
|
|
176184
|
+
route_channel: handle.channel,
|
|
176185
|
+
route_epoch: handle.epoch,
|
|
176186
|
+
kind
|
|
176187
|
+
});
|
|
176188
|
+
const reply = await this.controlRpc(body, (frame) => {
|
|
176189
|
+
if (frame.header.ty !== FrameType.Response)
|
|
176190
|
+
return true;
|
|
176191
|
+
const parsed = this.parseJson(frame);
|
|
176192
|
+
return parsed.route_channel === handle.channel && parsed.route_epoch === handle.epoch;
|
|
176193
|
+
});
|
|
176194
|
+
return this.parseJson(reply);
|
|
176195
|
+
}
|
|
176196
|
+
async closeRoute(handle, opts = {}) {
|
|
176197
|
+
this.assertLiveHandle(handle);
|
|
176198
|
+
for (const [key, cached2] of this.routes) {
|
|
176199
|
+
if (cached2.handle && sameRouteHandle(cached2.handle, handle)) {
|
|
176200
|
+
cached2.closed = true;
|
|
176201
|
+
cached2.handle = null;
|
|
176202
|
+
this.routes.delete(key);
|
|
176203
|
+
}
|
|
176204
|
+
}
|
|
176205
|
+
if (opts.drain)
|
|
176206
|
+
await this.drainUnaryOnHandle(handle);
|
|
176207
|
+
this.failHandle(handle, new SubcError("route closed by closeRoute", "route_closed"));
|
|
176208
|
+
if (this.liveRoutes.get(handle.channel) === handle)
|
|
176209
|
+
this.liveRoutes.delete(handle.channel);
|
|
176210
|
+
this.sendRouteGoodbye(handle);
|
|
176211
|
+
}
|
|
176212
|
+
async closeManagedRoute(target, identity, opts = {}) {
|
|
176213
|
+
const key = routeCacheKey(target, identity, routeOpenConsumerIdentity(opts));
|
|
176214
|
+
const cached2 = this.routes.get(key);
|
|
176215
|
+
if (!cached2)
|
|
176216
|
+
return;
|
|
176217
|
+
cached2.closed = true;
|
|
176218
|
+
this.routes.delete(key);
|
|
176219
|
+
const handle = cached2.handle;
|
|
176220
|
+
cached2.handle = null;
|
|
176221
|
+
if (handle)
|
|
176222
|
+
await this.closeRoute(handle, opts);
|
|
176223
|
+
}
|
|
176224
|
+
async closeRouteChannel(handle, opts = {}) {
|
|
176225
|
+
await this.closeRoute(handle, opts);
|
|
176226
|
+
}
|
|
176227
|
+
close() {
|
|
176228
|
+
this.closeStarted = true;
|
|
176229
|
+
this.fail(new SubcError("client closed"));
|
|
176230
|
+
this.sock.close();
|
|
176231
|
+
}
|
|
176232
|
+
drainUnaryOnHandle(handle) {
|
|
176233
|
+
const waiters = [];
|
|
176234
|
+
for (const pending of this.pending.values()) {
|
|
176235
|
+
if (pending.handle === handle && !pending.subscription) {
|
|
176236
|
+
waiters.push(new Promise((resolve) => {
|
|
176237
|
+
const previous = pending.onSettle;
|
|
176238
|
+
pending.onSettle = () => {
|
|
176239
|
+
previous?.();
|
|
176240
|
+
resolve();
|
|
176241
|
+
};
|
|
176242
|
+
}));
|
|
176243
|
+
}
|
|
176244
|
+
}
|
|
176245
|
+
return Promise.all(waiters).then(() => {
|
|
176246
|
+
return;
|
|
176247
|
+
});
|
|
176248
|
+
}
|
|
176249
|
+
sendRouteGoodbye(handle, closeOnQueueFailure = false) {
|
|
176250
|
+
this.assertLiveConnection(handle);
|
|
176251
|
+
if (this.closedErr) {
|
|
176252
|
+
if (closeOnQueueFailure)
|
|
176253
|
+
this.closeConnectionAfterCleanupFailure();
|
|
176254
|
+
return;
|
|
176255
|
+
}
|
|
176256
|
+
const goodbye = buildFrame(FrameType.Goodbye, buildFlags(false, Priority.Interactive, false), handle.channel, handle.epoch, 0n, EMPTY_BODY);
|
|
176257
|
+
const write = this.sock.writeTracked(encodeFrame(goodbye), Date.now() + DEFAULT_REQUEST_TIMEOUT_MS);
|
|
176258
|
+
if (!write.queued && closeOnQueueFailure)
|
|
176259
|
+
this.closeConnectionAfterCleanupFailure();
|
|
176260
|
+
write.completed.catch(() => {
|
|
176261
|
+
if (closeOnQueueFailure && !write.queued)
|
|
176262
|
+
this.closeConnectionAfterCleanupFailure();
|
|
176263
|
+
});
|
|
176264
|
+
}
|
|
176265
|
+
static async openConnection(opts) {
|
|
176266
|
+
const conn = await readConnectionFile(opts.connectionFile);
|
|
176267
|
+
const deadline = Date.now() + (opts.handshakeTimeoutMs ?? DEFAULT_HANDSHAKE_TIMEOUT_MS);
|
|
176268
|
+
const endpoint = conn.endpoints[0];
|
|
176269
|
+
const sock = await SubcSocket.connect(endpoint.host, endpoint.port, deadline);
|
|
176270
|
+
try {
|
|
176271
|
+
await authenticateClient(sock, conn, deadline);
|
|
176272
|
+
} catch (err) {
|
|
176273
|
+
sock.close();
|
|
176274
|
+
throw err;
|
|
176275
|
+
}
|
|
176276
|
+
return { sock, conn };
|
|
176277
|
+
}
|
|
176278
|
+
async controlRpc(body, acceptFrame, onLateResponse) {
|
|
176279
|
+
return this.send(null, body, Priority.Interactive, AdmissionClass.Normal, undefined, undefined, acceptFrame, onLateResponse);
|
|
176280
|
+
}
|
|
176281
|
+
send(handle, body, priority, admission, timeoutMs, onProgress, acceptFrame, onLateResponse) {
|
|
176282
|
+
if (handle)
|
|
176283
|
+
this.assertLiveHandle(handle);
|
|
176284
|
+
if (this.closedErr)
|
|
176285
|
+
return Promise.reject(this.closedErr);
|
|
176286
|
+
let corr;
|
|
176287
|
+
try {
|
|
176288
|
+
corr = this.allocateCorr();
|
|
176289
|
+
} catch (error51) {
|
|
176290
|
+
return Promise.reject(error51);
|
|
176291
|
+
}
|
|
176292
|
+
const key = pendingKey(handle, corr);
|
|
176293
|
+
const channel = handle?.channel ?? 0;
|
|
176294
|
+
const epoch = handle?.epoch ?? 0;
|
|
176295
|
+
const frame = buildFrame(FrameType.Request, buildFlags(false, priority, false, admission), channel, epoch, corr, body);
|
|
176296
|
+
return new Promise((resolve, reject) => {
|
|
176297
|
+
const ms = timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
176298
|
+
const pending = {
|
|
176299
|
+
handle,
|
|
176300
|
+
resolve,
|
|
176301
|
+
reject,
|
|
176302
|
+
onProgress,
|
|
176303
|
+
timer: null,
|
|
176304
|
+
acceptFrame,
|
|
176305
|
+
onLateResponse
|
|
176306
|
+
};
|
|
176307
|
+
pending.timer = setTimeout(() => this.arbitrateTimeout(key, pending, channel, corr, ms), ms);
|
|
176308
|
+
this.pending.set(key, pending);
|
|
176309
|
+
this.sock.write(encodeFrame(frame), Date.now() + ms).catch((error51) => {
|
|
176310
|
+
const current = this.pending.get(key);
|
|
176311
|
+
if (current)
|
|
176312
|
+
this.rejectPending(key, current, error51 instanceof Error ? error51 : new SubcError(String(error51)));
|
|
176313
|
+
});
|
|
176314
|
+
});
|
|
176315
|
+
}
|
|
176316
|
+
arbitrateTimeout(key, pending, channel, corr, ms) {
|
|
176317
|
+
const settleAsTimeout = () => {
|
|
176318
|
+
if (pending.onLateResponse)
|
|
176319
|
+
this.lateResponses.set(key, pending.onLateResponse);
|
|
176320
|
+
this.rejectPending(key, pending, new SubcError(this.timeoutMessage(channel, corr, ms), REQUEST_DEADLINE_MARKER));
|
|
176321
|
+
};
|
|
176322
|
+
const graceDeadline = Date.now() + this.opts.timeoutArbitrationGraceMs;
|
|
176323
|
+
const arbitrate = () => {
|
|
176324
|
+
if (this.pending.get(key) !== pending)
|
|
176325
|
+
return;
|
|
176326
|
+
const readerDraining = this.readerActive || this.sock.bufferedBytes() > 0;
|
|
176327
|
+
if (readerDraining && Date.now() < graceDeadline) {
|
|
176328
|
+
setImmediate(arbitrate);
|
|
176329
|
+
return;
|
|
176330
|
+
}
|
|
176331
|
+
settleAsTimeout();
|
|
176332
|
+
};
|
|
176333
|
+
setImmediate(arbitrate);
|
|
176334
|
+
}
|
|
176335
|
+
async managedRequest(handle, body, opts) {
|
|
176336
|
+
const bytes = body instanceof Uint8Array ? body : this.encode(body);
|
|
176337
|
+
const priority = opts.priority ?? Priority.Interactive;
|
|
176338
|
+
const admission = opts.admissionClass ?? AdmissionClass.Normal;
|
|
176339
|
+
try {
|
|
176340
|
+
const reply = await this.sendManaged(handle, bytes, priority, admission, opts.timeoutMs, opts.onProgress);
|
|
176341
|
+
return this.parseJson(reply);
|
|
176342
|
+
} catch (error51) {
|
|
176343
|
+
if (error51 instanceof SubcCallError)
|
|
176344
|
+
throw error51;
|
|
176345
|
+
throw this.terminalCallError("managed call failed", error51);
|
|
176346
|
+
}
|
|
176347
|
+
}
|
|
176348
|
+
sendManaged(handle, body, priority, admission, timeoutMs, onProgress) {
|
|
176349
|
+
try {
|
|
176350
|
+
this.assertLiveHandle(handle);
|
|
176351
|
+
} catch (error51) {
|
|
176352
|
+
return Promise.reject(this.notSentCallError("request used a stale route handle", error51));
|
|
176353
|
+
}
|
|
176354
|
+
if (this.closedErr) {
|
|
176355
|
+
return Promise.reject(this.notSentCallError("request was not sent because the subc connection was already closed", this.closedErr));
|
|
176356
|
+
}
|
|
176357
|
+
let corr;
|
|
176358
|
+
try {
|
|
176359
|
+
corr = this.allocateCorr();
|
|
176360
|
+
} catch (error51) {
|
|
176361
|
+
return Promise.reject(this.notSentCallError("request correlation allocator was exhausted", error51));
|
|
176362
|
+
}
|
|
176363
|
+
const key = pendingKey(handle, corr);
|
|
176364
|
+
const frame = buildFrame(FrameType.Request, buildFlags(false, priority, false, admission), handle.channel, handle.epoch, corr, body);
|
|
176365
|
+
let handedToSocket = false;
|
|
176366
|
+
const classifyFailure = (error51) => {
|
|
176367
|
+
if (!handedToSocket)
|
|
176368
|
+
return this.notSentCallError("request bytes were not queued to the subc socket", error51);
|
|
176369
|
+
if (error51 instanceof SubcError && error51.code === REQUEST_DEADLINE_MARKER) {
|
|
176370
|
+
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);
|
|
176371
|
+
}
|
|
176372
|
+
return this.outcomeUnknownCallError("connection dropped before the managed call returned a response", error51);
|
|
176373
|
+
};
|
|
176374
|
+
return new Promise((resolve, reject) => {
|
|
176375
|
+
const ms = timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
176376
|
+
const pending = {
|
|
176377
|
+
handle,
|
|
176378
|
+
resolve,
|
|
176379
|
+
reject,
|
|
176380
|
+
onProgress,
|
|
176381
|
+
timer: null,
|
|
176382
|
+
classifyFailure
|
|
176383
|
+
};
|
|
176384
|
+
pending.timer = setTimeout(() => this.arbitrateTimeout(key, pending, handle.channel, corr, ms), ms);
|
|
176385
|
+
this.pending.set(key, pending);
|
|
176386
|
+
const write = this.sock.writeTracked(encodeFrame(frame), Date.now() + ms);
|
|
176387
|
+
handedToSocket = write.queued;
|
|
176388
|
+
write.completed.catch((error51) => {
|
|
176389
|
+
const current = this.pending.get(key);
|
|
176390
|
+
if (current)
|
|
176391
|
+
this.rejectPending(key, current, error51 instanceof Error ? error51 : new SubcError(String(error51)));
|
|
176392
|
+
});
|
|
176393
|
+
});
|
|
176394
|
+
}
|
|
176395
|
+
async cachedRouteHandle(moduleId, opts) {
|
|
176396
|
+
const identity = opts.identity ?? this.opts.identity;
|
|
176397
|
+
if (!identity) {
|
|
176398
|
+
throw new SubcCallError("terminal", "managed call requires a BindIdentity in SubcClient.connect({ identity }) or call(..., { identity })", "missing_identity");
|
|
176399
|
+
}
|
|
176400
|
+
const target = { kind: opts.targetKind ?? this.opts.targetKind, module_id: moduleId };
|
|
176401
|
+
const consumerIdentity = routeOpenConsumerIdentity(opts);
|
|
176402
|
+
const key = routeCacheKey(target, identity, consumerIdentity);
|
|
176403
|
+
let cached2 = this.routes.get(key);
|
|
176404
|
+
if (!cached2) {
|
|
176405
|
+
cached2 = {
|
|
176406
|
+
key,
|
|
176407
|
+
moduleId,
|
|
176408
|
+
target,
|
|
176409
|
+
identity,
|
|
176410
|
+
consumerIdentity,
|
|
176411
|
+
handle: null,
|
|
176412
|
+
opening: null
|
|
176413
|
+
};
|
|
176414
|
+
this.routes.set(key, cached2);
|
|
176415
|
+
}
|
|
176416
|
+
if (cached2.handle && this.isLiveHandle(cached2.handle))
|
|
176417
|
+
return cached2.handle;
|
|
176418
|
+
if (!cached2.opening) {
|
|
176419
|
+
cached2.opening = this.openCachedRoute(cached2).finally(() => {
|
|
176420
|
+
cached2.opening = null;
|
|
176421
|
+
});
|
|
176422
|
+
}
|
|
176423
|
+
return cached2.opening;
|
|
176424
|
+
}
|
|
176425
|
+
async openCachedRoute(cached2) {
|
|
176426
|
+
const routeRetryDeadline = Date.now() + ROUTE_OPEN_RETRY_DEADLINE_MS;
|
|
176427
|
+
let routeRetryDelay = this.opts.reconnectBackoff.baseMs;
|
|
176428
|
+
let routeRetryAttempt = 0;
|
|
176429
|
+
for (;; ) {
|
|
176430
|
+
if (cached2.closed)
|
|
176431
|
+
throw this.routeClosedDuringOpen();
|
|
176432
|
+
try {
|
|
176433
|
+
await this.ensureConnectedForManaged();
|
|
176434
|
+
} catch (error51) {
|
|
176435
|
+
throw this.notSentRecoveryError("route.open could not run because reconnect failed", error51);
|
|
176436
|
+
}
|
|
176437
|
+
if (cached2.handle && this.isLiveHandle(cached2.handle))
|
|
176438
|
+
return cached2.handle;
|
|
176439
|
+
try {
|
|
176440
|
+
const handle = await this.routeOpen(cached2.target, cached2.identity, {
|
|
176441
|
+
consumerIdentity: cached2.consumerIdentity ?? null
|
|
176442
|
+
});
|
|
176443
|
+
if (cached2.closed) {
|
|
176444
|
+
this.liveRoutes.delete(handle.channel);
|
|
176445
|
+
this.sendRouteGoodbye(handle);
|
|
176446
|
+
throw this.routeClosedDuringOpen();
|
|
176447
|
+
}
|
|
176448
|
+
cached2.handle = handle;
|
|
176449
|
+
return handle;
|
|
176450
|
+
} catch (error51) {
|
|
176451
|
+
if (error51 instanceof SubcCallError && error51.code === "route_closed")
|
|
176452
|
+
throw error51;
|
|
176453
|
+
if (!this.closeStarted && isConsumerReconnectTransient(error51)) {
|
|
176454
|
+
try {
|
|
176455
|
+
await this.reconnectAfterDrop(error51);
|
|
176456
|
+
} catch (reconnectError) {
|
|
176457
|
+
throw this.notSentRecoveryError("route.open was not sent and reconnect failed", reconnectError);
|
|
176458
|
+
}
|
|
176459
|
+
continue;
|
|
176460
|
+
}
|
|
176461
|
+
if (!this.closeStarted && error51 instanceof SubcError && isRetryableRouteOpenCode(error51.code)) {
|
|
176462
|
+
routeRetryAttempt += 1;
|
|
176463
|
+
if (routeRetryAttempt < this.opts.reconnectBackoff.maxAttempts && Date.now() < routeRetryDeadline) {
|
|
176464
|
+
await this.opts.sleep(routeRetryDelay);
|
|
176465
|
+
routeRetryDelay = Math.min(routeRetryDelay * 2, this.opts.reconnectBackoff.capMs);
|
|
176466
|
+
continue;
|
|
176467
|
+
}
|
|
176468
|
+
throw this.notSentCallError(`route.open failed for module ${cached2.moduleId}: ${error51.code} (retry budget exhausted)`, error51);
|
|
176469
|
+
}
|
|
176470
|
+
throw this.terminalCallError(`route.open failed for module ${cached2.moduleId}`, error51);
|
|
176471
|
+
}
|
|
176472
|
+
}
|
|
176473
|
+
}
|
|
176474
|
+
async ensureConnectedForManaged() {
|
|
176475
|
+
if (this.closeStarted)
|
|
176476
|
+
throw new SubcError("client closed");
|
|
176477
|
+
if (this.reconnecting)
|
|
176478
|
+
await this.reconnecting;
|
|
176479
|
+
if (this.closedErr)
|
|
176480
|
+
await this.reconnectAfterDrop(this.closedErr);
|
|
176481
|
+
}
|
|
176482
|
+
scheduleReconnectAfterDrop(err) {
|
|
176483
|
+
if (this.closeStarted || this.reconnecting)
|
|
176484
|
+
return;
|
|
176485
|
+
this.reconnectAfterDrop(err).catch(() => {});
|
|
176486
|
+
}
|
|
176487
|
+
reconnectAfterDrop(trigger) {
|
|
176488
|
+
if (this.closeStarted)
|
|
176489
|
+
return Promise.reject(new SubcError("client closed"));
|
|
176490
|
+
if (this.reconnecting)
|
|
176491
|
+
return this.reconnecting;
|
|
176492
|
+
const promise2 = this.reconnectWithRetry(trigger).finally(() => {
|
|
176493
|
+
if (this.reconnecting === promise2)
|
|
176494
|
+
this.reconnecting = null;
|
|
176495
|
+
});
|
|
176496
|
+
this.reconnecting = promise2;
|
|
176497
|
+
return promise2;
|
|
176498
|
+
}
|
|
176499
|
+
async reconnectWithRetry(_trigger) {
|
|
176500
|
+
let attempt = 0;
|
|
176501
|
+
let delay = this.opts.reconnectBackoff.baseMs;
|
|
176502
|
+
for (;; ) {
|
|
176503
|
+
if (this.closeStarted)
|
|
176504
|
+
throw new SubcError("client closed");
|
|
176505
|
+
attempt += 1;
|
|
176506
|
+
try {
|
|
176507
|
+
const opened = await SubcClient.openConnection(this.opts);
|
|
176508
|
+
if (this.closeStarted) {
|
|
176509
|
+
opened.sock.close();
|
|
176510
|
+
throw new SubcError("client closed");
|
|
176511
|
+
}
|
|
176512
|
+
this.replaceConnection(opened);
|
|
176513
|
+
await this.reopenCachedRoutes();
|
|
176514
|
+
return;
|
|
176515
|
+
} catch (err) {
|
|
176516
|
+
if (!isConsumerReconnectTransient(err) || attempt >= this.opts.reconnectBackoff.maxAttempts) {
|
|
176517
|
+
if (err instanceof AuthError && attempt > 1) {
|
|
176518
|
+
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`);
|
|
176519
|
+
}
|
|
176520
|
+
throw err;
|
|
176521
|
+
}
|
|
176522
|
+
await this.opts.sleep(delay);
|
|
176523
|
+
delay = Math.min(delay * 2, this.opts.reconnectBackoff.capMs);
|
|
176524
|
+
}
|
|
176525
|
+
}
|
|
176526
|
+
}
|
|
176527
|
+
replaceConnection(opened) {
|
|
176528
|
+
this.sock.close();
|
|
176529
|
+
this.sock = opened.sock;
|
|
176530
|
+
this.currentConn = opened.conn;
|
|
176531
|
+
this.closedErr = null;
|
|
176532
|
+
this.generation += 1;
|
|
176533
|
+
this.connectionToken = newConnectionToken();
|
|
176534
|
+
this.liveRoutes.clear();
|
|
176535
|
+
this.lateResponses.clear();
|
|
176536
|
+
this.nextCorr = 1n;
|
|
176537
|
+
this.readLoop(opened.sock, this.generation);
|
|
176538
|
+
}
|
|
176539
|
+
async reopenCachedRoutes() {
|
|
176540
|
+
for (const cached2 of this.routes.values())
|
|
176541
|
+
cached2.handle = null;
|
|
176542
|
+
for (const cached2 of this.routes.values()) {
|
|
176543
|
+
if (cached2.closed)
|
|
176544
|
+
continue;
|
|
176545
|
+
const handle = await this.routeOpen(cached2.target, cached2.identity, {
|
|
176546
|
+
consumerIdentity: cached2.consumerIdentity ?? null
|
|
176547
|
+
});
|
|
176548
|
+
if (cached2.closed) {
|
|
176549
|
+
this.liveRoutes.delete(handle.channel);
|
|
176550
|
+
this.sendRouteGoodbye(handle);
|
|
176551
|
+
continue;
|
|
176552
|
+
}
|
|
176553
|
+
cached2.handle = handle;
|
|
176554
|
+
}
|
|
176555
|
+
}
|
|
176556
|
+
timeoutMessage(channel, corr, ms) {
|
|
176557
|
+
const port = this.sock.localPort();
|
|
176558
|
+
const where = port === null ? "channel" : `local_port=${port} channel`;
|
|
176559
|
+
return `request on ${where} ${channel} corr ${corr} timed out after ${ms}ms`;
|
|
176560
|
+
}
|
|
176561
|
+
routeClosedDuringOpen() {
|
|
176562
|
+
return new SubcCallError("not_sent", "route was closed before route.open completed", "route_closed");
|
|
176563
|
+
}
|
|
176564
|
+
async readLoop(sock, generation) {
|
|
176565
|
+
try {
|
|
176566
|
+
for (;; ) {
|
|
176567
|
+
this.readerActive = false;
|
|
176568
|
+
const frame = await sock.readFrame(Number.POSITIVE_INFINITY, { afterHeaderMs: BODY_READ_TIMEOUT_MS }, () => {
|
|
176569
|
+
this.readerActive = true;
|
|
176570
|
+
});
|
|
176571
|
+
try {
|
|
176572
|
+
if (this.sock === sock && this.generation === generation)
|
|
176573
|
+
this.dispatch(frame);
|
|
176574
|
+
} finally {
|
|
176575
|
+
this.readerActive = false;
|
|
176576
|
+
}
|
|
176577
|
+
}
|
|
176578
|
+
} catch (error51) {
|
|
176579
|
+
if (this.sock === sock && this.generation === generation) {
|
|
176580
|
+
this.fail(error51 instanceof Error ? error51 : new SubcError(String(error51)));
|
|
176581
|
+
}
|
|
176582
|
+
}
|
|
176583
|
+
}
|
|
176584
|
+
dispatch(frame) {
|
|
176585
|
+
let handle = null;
|
|
176586
|
+
if (frame.header.channel !== 0) {
|
|
176587
|
+
handle = this.liveRoutes.get(frame.header.channel) ?? null;
|
|
176588
|
+
if (!handle || handle.epoch !== frame.header.epoch) {
|
|
176589
|
+
this.ingressEpochDropCount += 1;
|
|
176590
|
+
return;
|
|
176591
|
+
}
|
|
176592
|
+
}
|
|
176593
|
+
const key = pendingKey(handle, frame.header.corr);
|
|
176594
|
+
const pending = this.pending.get(key);
|
|
176595
|
+
if (pending) {
|
|
176596
|
+
if (pending.acceptFrame && !pending.acceptFrame(frame))
|
|
176597
|
+
return;
|
|
176598
|
+
switch (frame.header.ty) {
|
|
176599
|
+
case FrameType.Push:
|
|
176600
|
+
case FrameType.StreamData:
|
|
176601
|
+
pending.onProgress?.(frame.body);
|
|
176602
|
+
return;
|
|
176603
|
+
case FrameType.Response:
|
|
176604
|
+
case FrameType.StreamEnd:
|
|
176605
|
+
this.settle(key, pending, () => pending.resolve(frame));
|
|
176606
|
+
return;
|
|
176607
|
+
case FrameType.Error:
|
|
176608
|
+
this.settle(key, pending, () => pending.reject(this.errorFromFrame(frame)));
|
|
176609
|
+
return;
|
|
176610
|
+
default:
|
|
176611
|
+
return;
|
|
176612
|
+
}
|
|
176613
|
+
}
|
|
176614
|
+
const late = this.lateResponses.get(key);
|
|
176615
|
+
if (late && (frame.header.ty === FrameType.Response || frame.header.ty === FrameType.Error)) {
|
|
176616
|
+
this.lateResponses.delete(key);
|
|
176617
|
+
late(frame);
|
|
176618
|
+
return;
|
|
176619
|
+
}
|
|
176620
|
+
if (frame.header.ty === FrameType.Goodbye && handle) {
|
|
176621
|
+
this.failHandle(handle, new SubcError("route closed by subc (GOODBYE)"));
|
|
176622
|
+
if (this.liveRoutes.get(handle.channel) === handle)
|
|
176623
|
+
this.liveRoutes.delete(handle.channel);
|
|
176624
|
+
this.evictRouteHandle(handle);
|
|
176625
|
+
return;
|
|
176626
|
+
}
|
|
176627
|
+
if (frame.header.ty === FrameType.Response || frame.header.ty === FrameType.Error || frame.header.ty === FrameType.StreamEnd) {
|
|
176628
|
+
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() ?? "?");
|
|
176629
|
+
}
|
|
176630
|
+
}
|
|
176631
|
+
settle(key, pending, run) {
|
|
176632
|
+
if (this.pending.get(key) !== pending)
|
|
176633
|
+
return false;
|
|
176634
|
+
this.pending.delete(key);
|
|
176635
|
+
if (pending.timer)
|
|
176636
|
+
clearTimeout(pending.timer);
|
|
176637
|
+
run();
|
|
176638
|
+
pending.onSettle?.();
|
|
176639
|
+
return true;
|
|
176640
|
+
}
|
|
176641
|
+
rejectPending(key, pending, err) {
|
|
176642
|
+
this.settle(key, pending, () => pending.reject(pending.classifyFailure?.(err) ?? err));
|
|
176643
|
+
}
|
|
176644
|
+
errorFromFrame(frame) {
|
|
176645
|
+
try {
|
|
176646
|
+
const parsed = JSON.parse(Buffer.from(frame.body).toString("utf8"));
|
|
176647
|
+
return new SubcError(parsed.message ?? "subc error", parsed.code);
|
|
176648
|
+
} catch {
|
|
176649
|
+
return new SubcError(Buffer.from(frame.body).toString("utf8") || "subc error");
|
|
176650
|
+
}
|
|
176651
|
+
}
|
|
176652
|
+
evictRouteHandle(handle) {
|
|
176653
|
+
for (const cached2 of this.routes.values()) {
|
|
176654
|
+
if (cached2.handle && sameRouteHandle(cached2.handle, handle))
|
|
176655
|
+
cached2.handle = null;
|
|
176656
|
+
}
|
|
176657
|
+
}
|
|
176658
|
+
failHandle(handle, error51) {
|
|
176659
|
+
for (const [key, pending] of this.pending) {
|
|
176660
|
+
if (pending.handle && sameRouteHandle(pending.handle, handle))
|
|
176661
|
+
this.rejectPending(key, pending, error51);
|
|
176662
|
+
}
|
|
176663
|
+
}
|
|
176664
|
+
fail(err) {
|
|
176665
|
+
if (!this.closedErr)
|
|
176666
|
+
this.closedErr = err;
|
|
176667
|
+
for (const [key, pending] of this.pending) {
|
|
176668
|
+
this.rejectPending(key, pending, err);
|
|
176669
|
+
}
|
|
176670
|
+
}
|
|
176671
|
+
notSentCallError(message, cause) {
|
|
176672
|
+
return new SubcCallError("not_sent", `${message}${causeMessage(cause)}`, errorCode(cause), cause);
|
|
176673
|
+
}
|
|
176674
|
+
outcomeUnknownCallError(message, cause) {
|
|
176675
|
+
return new SubcCallError("outcome_unknown", `${message}${causeMessage(cause)}`, errorCode(cause), cause);
|
|
176676
|
+
}
|
|
176677
|
+
terminalCallError(message, cause) {
|
|
176678
|
+
if (cause instanceof SubcCallError)
|
|
176679
|
+
return cause;
|
|
176680
|
+
return new SubcCallError("terminal", `${message}${causeMessage(cause)}`, errorCode(cause), cause);
|
|
176681
|
+
}
|
|
176682
|
+
notSentRecoveryError(message, cause) {
|
|
176683
|
+
if (cause instanceof SubcCallError)
|
|
176684
|
+
return cause;
|
|
176685
|
+
if (isConsumerReconnectTransient(cause))
|
|
176686
|
+
return this.notSentCallError(message, cause);
|
|
176687
|
+
return this.terminalCallError(message, cause);
|
|
176688
|
+
}
|
|
176689
|
+
get droppedIngressFrames() {
|
|
176690
|
+
return this.ingressEpochDropCount;
|
|
176691
|
+
}
|
|
176692
|
+
installRoute(channel, epoch) {
|
|
176693
|
+
const handle = createRouteHandle(channel, epoch, this.connectionToken);
|
|
176694
|
+
this.liveRoutes.set(channel, handle);
|
|
176695
|
+
return handle;
|
|
176696
|
+
}
|
|
176697
|
+
isLiveHandle(handle) {
|
|
176698
|
+
return belongsToConnection(handle, this.connectionToken) && this.liveRoutes.get(handle.channel) === handle;
|
|
176699
|
+
}
|
|
176700
|
+
assertLiveConnection(handle) {
|
|
176701
|
+
if (!belongsToConnection(handle, this.connectionToken))
|
|
176702
|
+
throw new StaleRouteHandleError(handle);
|
|
176703
|
+
}
|
|
176704
|
+
assertLiveHandle(handle) {
|
|
176705
|
+
if (!this.isLiveHandle(handle))
|
|
176706
|
+
throw new StaleRouteHandleError(handle);
|
|
176707
|
+
}
|
|
176708
|
+
allocateCorr() {
|
|
176709
|
+
const maximum = 0xffffffffffffffffn;
|
|
176710
|
+
if (this.nextCorr > maximum) {
|
|
176711
|
+
const error51 = new SubcError("channel-0 correlation id allocator exhausted", "corr_exhausted");
|
|
176712
|
+
this.fail(error51);
|
|
176713
|
+
this.sock.close();
|
|
176714
|
+
this.scheduleReconnectAfterDrop(error51);
|
|
176715
|
+
throw error51;
|
|
176716
|
+
}
|
|
176717
|
+
const corr = this.nextCorr;
|
|
176718
|
+
this.nextCorr += 1n;
|
|
176719
|
+
return corr;
|
|
176720
|
+
}
|
|
176721
|
+
closeConnectionAfterCleanupFailure() {
|
|
176722
|
+
const error51 = new SubcError("late route cleanup could not be queued", "late_route_cleanup_failed");
|
|
176723
|
+
this.fail(error51);
|
|
176724
|
+
this.sock.close();
|
|
176725
|
+
this.scheduleReconnectAfterDrop(error51);
|
|
176726
|
+
}
|
|
176727
|
+
encode(value) {
|
|
176728
|
+
return new Uint8Array(Buffer.from(JSON.stringify(value), "utf8"));
|
|
176729
|
+
}
|
|
176730
|
+
parseJson(frame) {
|
|
176731
|
+
return JSON.parse(Buffer.from(frame.body).toString("utf8"));
|
|
176732
|
+
}
|
|
176733
|
+
}
|
|
176734
|
+
function isConsumerReconnectTransient(err) {
|
|
176735
|
+
if (err instanceof SocketClosedError || err instanceof SocketTimeoutError)
|
|
176736
|
+
return true;
|
|
176737
|
+
if (err instanceof SocketWriteNotQueuedError || err instanceof SocketWriteQueuedError)
|
|
176738
|
+
return true;
|
|
176739
|
+
if (err instanceof SubcCallError)
|
|
176740
|
+
return err.kind === "not_sent" || err.kind === "outcome_unknown";
|
|
176741
|
+
if (err instanceof AuthError)
|
|
176742
|
+
return true;
|
|
176743
|
+
if (err instanceof SubcError || err instanceof ConnectionFileError)
|
|
176744
|
+
return false;
|
|
176745
|
+
const code = errorCode(err);
|
|
176746
|
+
return code === "ECONNREFUSED" || code === "ECONNRESET" || code === "EPIPE" || code === "ETIMEDOUT" || code === "ENOENT";
|
|
176747
|
+
}
|
|
176748
|
+
function isRetryableRouteOpenCode(code) {
|
|
176749
|
+
return code === "unknown_module" || code === "module_reloading" || code === "target_unavailable" || code === "module_timeout";
|
|
176750
|
+
}
|
|
176751
|
+
async function connectionFileExists(path7) {
|
|
176752
|
+
try {
|
|
176753
|
+
await fs4.access(path7);
|
|
176754
|
+
return true;
|
|
176755
|
+
} catch {
|
|
176756
|
+
return false;
|
|
176757
|
+
}
|
|
176758
|
+
}
|
|
176759
|
+
function normalizeConnectOptions(opts) {
|
|
176760
|
+
return {
|
|
176761
|
+
connectionFile: opts.connectionFile,
|
|
176762
|
+
handshakeTimeoutMs: opts.handshakeTimeoutMs,
|
|
176763
|
+
identity: opts.identity,
|
|
176764
|
+
targetKind: opts.targetKind ?? DEFAULT_MANAGED_TARGET_KIND,
|
|
176765
|
+
reconnectBackoff: opts.reconnectBackoff ?? DEFAULT_RECONNECT_BACKOFF,
|
|
176766
|
+
sleep: opts.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms))),
|
|
176767
|
+
timeoutArbitrationGraceMs: opts.timeoutArbitrationGraceMs ?? TIMEOUT_ARBITRATION_GRACE_MS
|
|
176768
|
+
};
|
|
176769
|
+
}
|
|
176770
|
+
function routeCacheKey(target, identity, consumerIdentity) {
|
|
176771
|
+
const consumerPart = consumerIdentity ? `${consumerIdentity.module_id}\x00${consumerIdentity.launch_nonce}` : "";
|
|
176772
|
+
return `${target.kind}\x00${target.module_id}\x00${identity.project_root}\x00${identity.harness}\x00${identity.session}\x00${consumerPart}`;
|
|
176773
|
+
}
|
|
176774
|
+
function routeOpenConsumerIdentity(opts = {}) {
|
|
176775
|
+
if (opts.consumerIdentity !== undefined)
|
|
176776
|
+
return opts.consumerIdentity ?? undefined;
|
|
176777
|
+
const moduleId = process.env[SUBC_MODULE_ID_ENV];
|
|
176778
|
+
const launchNonce = process.env[SUBC_LAUNCH_NONCE_ENV];
|
|
176779
|
+
if (!moduleId || !launchNonce)
|
|
176780
|
+
return;
|
|
176781
|
+
return { module_id: moduleId, launch_nonce: launchNonce };
|
|
176782
|
+
}
|
|
176783
|
+
function errorCode(err) {
|
|
176784
|
+
if (typeof err === "object" && err !== null && "code" in err) {
|
|
176785
|
+
const code = err.code;
|
|
176786
|
+
if (typeof code === "string")
|
|
176787
|
+
return code;
|
|
176788
|
+
}
|
|
176789
|
+
return;
|
|
176790
|
+
}
|
|
176791
|
+
function causeMessage(cause) {
|
|
176792
|
+
if (cause === undefined)
|
|
176793
|
+
return "";
|
|
176794
|
+
return `: ${cause instanceof Error ? cause.message : String(cause)}`;
|
|
176795
|
+
}
|
|
176796
|
+
function pendingKey(handle, corr) {
|
|
176797
|
+
return handle ? `${handle.channel}:${handle.epoch}:${corr}` : `0:0:${corr}`;
|
|
176798
|
+
}
|
|
176799
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.4.1/node_modules/@cortexkit/subc-client/dist/provider.js
|
|
176800
|
+
import { Buffer as Buffer4 } from "node:buffer";
|
|
176801
|
+
var DEFAULT_HANDSHAKE_TIMEOUT_MS2 = 1e4;
|
|
176802
|
+
var BODY_READ_TIMEOUT_MS2 = 30000;
|
|
176803
|
+
var WRITE_TIMEOUT_MS = 30000;
|
|
176804
|
+
var DEFAULT_RESTORED_DEBOUNCE_MS = 250;
|
|
176805
|
+
var DEFAULT_PROVIDER_HANDLER_CAPACITY = 64;
|
|
176806
|
+
var HEALTH_CHECK_OP = "health.check";
|
|
176807
|
+
var HELLO_CORR = 1n;
|
|
176808
|
+
|
|
176809
|
+
class AsyncPermitPool {
|
|
176810
|
+
available;
|
|
176811
|
+
waiters = [];
|
|
176812
|
+
constructor(capacity) {
|
|
176813
|
+
if (!Number.isInteger(capacity) || capacity <= 0) {
|
|
176814
|
+
throw new SubcProviderError("provider handler capacity must be a positive integer", "invalid_handler_capacity");
|
|
176815
|
+
}
|
|
176816
|
+
this.available = capacity;
|
|
176817
|
+
}
|
|
176818
|
+
async acquire() {
|
|
176819
|
+
if (this.available > 0) {
|
|
176820
|
+
this.available -= 1;
|
|
176821
|
+
return this.releaseOnce();
|
|
176822
|
+
}
|
|
176823
|
+
await new Promise((resolve) => {
|
|
176824
|
+
this.waiters.push(resolve);
|
|
176825
|
+
});
|
|
176826
|
+
return this.releaseOnce();
|
|
176827
|
+
}
|
|
176828
|
+
releaseOnce() {
|
|
176829
|
+
let released = false;
|
|
176830
|
+
return () => {
|
|
176831
|
+
if (released)
|
|
176832
|
+
return;
|
|
176833
|
+
released = true;
|
|
176834
|
+
const next = this.waiters.shift();
|
|
176835
|
+
if (next) {
|
|
176836
|
+
next();
|
|
176837
|
+
} else {
|
|
176838
|
+
this.available += 1;
|
|
176839
|
+
}
|
|
176840
|
+
};
|
|
176841
|
+
}
|
|
176842
|
+
}
|
|
176843
|
+
|
|
176844
|
+
class SubcProviderError extends Error {
|
|
176845
|
+
code;
|
|
176846
|
+
constructor(message, code) {
|
|
176847
|
+
super(message);
|
|
176848
|
+
this.code = code;
|
|
176849
|
+
}
|
|
176850
|
+
}
|
|
176851
|
+
class SubcProvider {
|
|
176852
|
+
sock;
|
|
176853
|
+
currentConn;
|
|
176854
|
+
opts;
|
|
176855
|
+
closed;
|
|
176856
|
+
resolveClosed = () => {
|
|
176857
|
+
return;
|
|
176858
|
+
};
|
|
176859
|
+
closeStarted = false;
|
|
176860
|
+
closedErr = null;
|
|
176861
|
+
inflight = new Map;
|
|
176862
|
+
pending = new Map;
|
|
176863
|
+
liveRoutes = new Map;
|
|
176864
|
+
connectionToken = newConnectionToken();
|
|
176865
|
+
nextCorr = 1n;
|
|
176866
|
+
ingressEpochDropCount = 0;
|
|
176867
|
+
requestGate = new AsyncPermitPool(DEFAULT_PROVIDER_HANDLER_CAPACITY);
|
|
176868
|
+
reconnecting = null;
|
|
176869
|
+
generation = 1;
|
|
176870
|
+
connectionEpoch = 1;
|
|
176871
|
+
stateQueue = [];
|
|
176872
|
+
drainingStateQueue = false;
|
|
176873
|
+
restoredDebounceToken = 0;
|
|
176874
|
+
storage;
|
|
176875
|
+
constructor(sock, currentConn, opts, storage) {
|
|
176876
|
+
this.sock = sock;
|
|
176877
|
+
this.currentConn = currentConn;
|
|
176878
|
+
this.opts = opts;
|
|
176879
|
+
this.storage = storage;
|
|
176880
|
+
this.closed = new Promise((resolve) => {
|
|
176881
|
+
this.resolveClosed = resolve;
|
|
176882
|
+
});
|
|
176883
|
+
this.readLoop(sock, this.generation);
|
|
176884
|
+
this.enqueueConnectionState({ state: "connected", epoch: this.connectionEpoch });
|
|
176885
|
+
}
|
|
176886
|
+
get droppedIngressFrames() {
|
|
176887
|
+
return this.ingressEpochDropCount;
|
|
176888
|
+
}
|
|
176889
|
+
get conn() {
|
|
176890
|
+
return this.currentConn;
|
|
176891
|
+
}
|
|
176892
|
+
currentEpoch() {
|
|
176893
|
+
return this.connectionEpoch;
|
|
176894
|
+
}
|
|
176895
|
+
async request(handle, body, opts = {}) {
|
|
176896
|
+
this.assertLiveHandle(handle);
|
|
176897
|
+
const corr = this.allocateCorr();
|
|
176898
|
+
const key = routeKey(handle, corr);
|
|
176899
|
+
const timeoutMs = opts.timeoutMs ?? WRITE_TIMEOUT_MS;
|
|
176900
|
+
const frame = buildFrame(FrameType.Request, buildFlags(false, opts.priority ?? Priority.Interactive, false, opts.admissionClass ?? AdmissionClass.Normal), handle.channel, handle.epoch, corr, body);
|
|
176901
|
+
return await new Promise((resolve, reject) => {
|
|
176902
|
+
const timer = setTimeout(() => {
|
|
176903
|
+
if (this.pending.delete(key))
|
|
176904
|
+
reject(new SubcProviderError("reverse request timed out", "request_timeout"));
|
|
176905
|
+
}, timeoutMs);
|
|
176906
|
+
this.pending.set(key, {
|
|
176907
|
+
resolve: (response) => resolve(response.body),
|
|
176908
|
+
reject,
|
|
176909
|
+
timer
|
|
176910
|
+
});
|
|
176911
|
+
this.sendOn(this.sock, this.generation, frame).catch((error51) => {
|
|
176912
|
+
const pending = this.pending.get(key);
|
|
176913
|
+
if (!pending)
|
|
176914
|
+
return;
|
|
176915
|
+
this.pending.delete(key);
|
|
176916
|
+
clearTimeout(pending.timer);
|
|
176917
|
+
reject(error51 instanceof Error ? error51 : new SubcProviderError(String(error51)));
|
|
176918
|
+
});
|
|
176919
|
+
});
|
|
176920
|
+
}
|
|
176921
|
+
async push(handle, body, opts = {}) {
|
|
176922
|
+
this.assertLiveHandle(handle);
|
|
176923
|
+
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));
|
|
176924
|
+
}
|
|
176925
|
+
cancel(handle, corr) {
|
|
176926
|
+
this.assertLiveHandle(handle);
|
|
176927
|
+
this.sendOn(this.sock, this.generation, buildFrame(FrameType.Cancel, controlFlags(), handle.channel, handle.epoch, corr, new Uint8Array(0)));
|
|
176928
|
+
}
|
|
176929
|
+
closeRoute(handle) {
|
|
176930
|
+
this.assertLiveHandle(handle);
|
|
176931
|
+
this.liveRoutes.delete(handle.channel);
|
|
176932
|
+
this.abortHandle(handle);
|
|
176933
|
+
this.sendOn(this.sock, this.generation, buildFrame(FrameType.Goodbye, controlFlags(), handle.channel, handle.epoch, 0n, new Uint8Array(0)));
|
|
176934
|
+
}
|
|
176935
|
+
static async connect(opts) {
|
|
176936
|
+
if (opts.manifest.protocol_ver !== PROTOCOL_VERSION) {
|
|
176937
|
+
throw new SubcProviderError(`manifest protocol_ver ${opts.manifest.protocol_ver} does not match client protocol ${PROTOCOL_VERSION}`, "invalid_manifest");
|
|
176938
|
+
}
|
|
176939
|
+
const normalized = normalizeProviderConnectOptions(opts);
|
|
176940
|
+
const opened = await SubcProvider.openConnection(normalized);
|
|
176941
|
+
return new SubcProvider(opened.sock, opened.conn, normalized, opened.ack.storage);
|
|
176942
|
+
}
|
|
176943
|
+
async close() {
|
|
176944
|
+
if (!this.closeStarted) {
|
|
176945
|
+
this.closeStarted = true;
|
|
176946
|
+
this.cancelRestoredDebounce();
|
|
176947
|
+
const sock = this.sock;
|
|
176948
|
+
try {
|
|
176949
|
+
await sendFrame(sock, buildFrame(FrameType.Goodbye, controlFlags(), 0, 0, 0n, new Uint8Array(0)));
|
|
176950
|
+
} catch {} finally {
|
|
176951
|
+
sock.close();
|
|
176952
|
+
this.finishClosed();
|
|
176953
|
+
}
|
|
176954
|
+
}
|
|
176955
|
+
await this.closed;
|
|
176956
|
+
}
|
|
176957
|
+
static async openConnection(opts) {
|
|
176958
|
+
const conn = await readConnectionFile(opts.connectionFile);
|
|
176959
|
+
const deadline = Date.now() + (opts.handshakeTimeoutMs ?? DEFAULT_HANDSHAKE_TIMEOUT_MS2);
|
|
176960
|
+
const endpoint = conn.endpoints[0];
|
|
176961
|
+
const sock = await SubcSocket.connect(endpoint.host, endpoint.port, deadline);
|
|
176962
|
+
try {
|
|
176963
|
+
await authenticateClient(sock, conn, deadline);
|
|
176964
|
+
await sendFrame(sock, buildHelloFrame(opts));
|
|
176965
|
+
const ack = await expectHelloAck(sock, deadline);
|
|
176966
|
+
return { sock, conn, ack };
|
|
176967
|
+
} catch (err) {
|
|
176968
|
+
sock.close();
|
|
176969
|
+
throw err;
|
|
176970
|
+
}
|
|
176971
|
+
}
|
|
176972
|
+
async readLoop(sock, generation) {
|
|
176973
|
+
try {
|
|
176974
|
+
for (;; ) {
|
|
176975
|
+
const frame = await sock.readFrame(Number.POSITIVE_INFINITY, { afterHeaderMs: BODY_READ_TIMEOUT_MS2 });
|
|
176976
|
+
const keepGoing = await this.dispatch(frame, sock, generation);
|
|
176977
|
+
if (!keepGoing) {
|
|
176978
|
+
if (this.sock === sock && this.generation === generation)
|
|
176979
|
+
this.closeStarted = true;
|
|
176980
|
+
break;
|
|
176981
|
+
}
|
|
176982
|
+
}
|
|
176983
|
+
} catch (error51) {
|
|
176984
|
+
if (this.sock === sock && this.generation === generation && !this.closeStarted) {
|
|
176985
|
+
this.handleUnexpectedDrop(sock, generation, error51 instanceof Error ? error51 : new SubcProviderError(String(error51)));
|
|
176986
|
+
return;
|
|
176987
|
+
}
|
|
176988
|
+
} finally {
|
|
176989
|
+
if (this.sock === sock && this.generation === generation) {
|
|
176990
|
+
sock.close();
|
|
176991
|
+
if (this.closeStarted)
|
|
176992
|
+
this.finishClosed();
|
|
176993
|
+
}
|
|
176994
|
+
}
|
|
176995
|
+
}
|
|
176996
|
+
async dispatch(frame, sock, generation) {
|
|
176997
|
+
let handle = null;
|
|
176998
|
+
if (frame.header.channel !== 0) {
|
|
176999
|
+
handle = this.liveRoutes.get(frame.header.channel) ?? null;
|
|
177000
|
+
if (!handle || handle.epoch !== frame.header.epoch) {
|
|
177001
|
+
this.ingressEpochDropCount += 1;
|
|
177002
|
+
return true;
|
|
177003
|
+
}
|
|
177004
|
+
}
|
|
177005
|
+
if (handle) {
|
|
177006
|
+
const pendingKey2 = routeKey(handle, frame.header.corr);
|
|
177007
|
+
const pending = this.pending.get(pendingKey2);
|
|
177008
|
+
if (pending) {
|
|
177009
|
+
if (frame.header.ty === FrameType.Push || frame.header.ty === FrameType.StreamData)
|
|
177010
|
+
return true;
|
|
177011
|
+
if (frame.header.ty === FrameType.Response || frame.header.ty === FrameType.StreamEnd) {
|
|
177012
|
+
this.pending.delete(pendingKey2);
|
|
177013
|
+
clearTimeout(pending.timer);
|
|
177014
|
+
pending.resolve(frame);
|
|
177015
|
+
return true;
|
|
177016
|
+
}
|
|
177017
|
+
if (frame.header.ty === FrameType.Error) {
|
|
177018
|
+
this.pending.delete(pendingKey2);
|
|
177019
|
+
clearTimeout(pending.timer);
|
|
177020
|
+
pending.reject(providerErrorFromFrame(frame));
|
|
177021
|
+
return true;
|
|
177022
|
+
}
|
|
177023
|
+
}
|
|
177024
|
+
}
|
|
177025
|
+
switch (frame.header.ty) {
|
|
177026
|
+
case FrameType.Ping:
|
|
177027
|
+
if (frame.header.channel === 0) {
|
|
177028
|
+
await this.sendOn(sock, generation, buildFrameWithVersion(frame.header.ver, FrameType.Pong, frame.header.flags, 0, 0, frame.header.corr, new Uint8Array(0)));
|
|
177029
|
+
}
|
|
177030
|
+
return true;
|
|
177031
|
+
case FrameType.Goodbye:
|
|
177032
|
+
if (!handle)
|
|
177033
|
+
return false;
|
|
177034
|
+
this.liveRoutes.delete(handle.channel);
|
|
177035
|
+
this.abortHandle(handle);
|
|
177036
|
+
await this.opts.onRouteGone?.(handle);
|
|
177037
|
+
return true;
|
|
177038
|
+
case FrameType.Cancel:
|
|
177039
|
+
if (handle)
|
|
177040
|
+
this.inflight.get(routeKey(handle, frame.header.corr))?.abort();
|
|
177041
|
+
return true;
|
|
177042
|
+
case FrameType.Request:
|
|
177043
|
+
if (frame.header.channel === 0) {
|
|
177044
|
+
await this.handleControlRequest(frame, sock, generation);
|
|
177045
|
+
} else if (handle) {
|
|
177046
|
+
this.handleDataRequest(frame, handle, sock, generation).catch((error51) => {
|
|
177047
|
+
if (!this.closeStarted && this.sock === sock && this.generation === generation) {
|
|
177048
|
+
console.warn("SubcProvider handler failed after its request was dispatched", error51);
|
|
177049
|
+
}
|
|
177050
|
+
});
|
|
177051
|
+
}
|
|
177052
|
+
return true;
|
|
177053
|
+
default:
|
|
177054
|
+
return true;
|
|
177055
|
+
}
|
|
177056
|
+
}
|
|
177057
|
+
abortHandle(handle) {
|
|
177058
|
+
const prefix = `${handle.channel}:${handle.epoch}:`;
|
|
177059
|
+
for (const [key, controller] of this.inflight) {
|
|
177060
|
+
if (key.startsWith(prefix))
|
|
177061
|
+
controller.abort();
|
|
177062
|
+
}
|
|
177063
|
+
for (const [key, pending] of this.pending) {
|
|
177064
|
+
if (!key.startsWith(prefix))
|
|
177065
|
+
continue;
|
|
177066
|
+
this.pending.delete(key);
|
|
177067
|
+
clearTimeout(pending.timer);
|
|
177068
|
+
pending.reject(new StaleRouteHandleError(handle));
|
|
177069
|
+
}
|
|
177070
|
+
}
|
|
177071
|
+
abortGeneration(_generation) {
|
|
177072
|
+
this.abortAllInflight();
|
|
177073
|
+
for (const [key, pending] of this.pending) {
|
|
177074
|
+
this.pending.delete(key);
|
|
177075
|
+
clearTimeout(pending.timer);
|
|
177076
|
+
pending.reject(new SubcProviderError("provider connection dropped", "connection_dropped"));
|
|
177077
|
+
}
|
|
177078
|
+
this.liveRoutes.clear();
|
|
177079
|
+
}
|
|
177080
|
+
abortAllInflight() {
|
|
177081
|
+
for (const controller of this.inflight.values())
|
|
177082
|
+
controller.abort();
|
|
177083
|
+
}
|
|
177084
|
+
async handleControlRequest(frame, sock, generation) {
|
|
177085
|
+
const request = parseJson(frame.body);
|
|
177086
|
+
if (request.op === HEALTH_CHECK_OP) {
|
|
177087
|
+
this.handleHealthRequest(frame, sock, generation).catch((error51) => {
|
|
177088
|
+
if (!this.closeStarted && this.sock === sock && this.generation === generation) {
|
|
177089
|
+
console.warn("SubcProvider health handler failed after its request was dispatched", error51);
|
|
177090
|
+
}
|
|
177091
|
+
});
|
|
177092
|
+
return;
|
|
177093
|
+
}
|
|
177094
|
+
if (request.op !== "route.bind") {
|
|
177095
|
+
throw new SubcProviderError(`unsupported module control request ${request.op ?? "<missing op>"}`);
|
|
177096
|
+
}
|
|
177097
|
+
const tentative = createRouteHandle(numberField(request.route_channel, "route_channel"), numberField(request.epoch, "epoch"), this.connectionToken);
|
|
177098
|
+
const bindRequest = {
|
|
177099
|
+
handle: tentative,
|
|
177100
|
+
target: request.target,
|
|
177101
|
+
identity: request.identity,
|
|
177102
|
+
principal: request.principal,
|
|
177103
|
+
consumer_capabilities: request.consumer_capabilities
|
|
177104
|
+
};
|
|
177105
|
+
let decision;
|
|
177106
|
+
try {
|
|
177107
|
+
decision = await this.opts.onBind?.(bindRequest);
|
|
177108
|
+
} catch (error51) {
|
|
177109
|
+
try {
|
|
177110
|
+
await this.sendError(frame, "route_rejected", error51 instanceof Error ? error51.message : String(error51), controlFlags(), sock, generation);
|
|
177111
|
+
} finally {
|
|
177112
|
+
await this.opts.onRouteGone?.(tentative);
|
|
177113
|
+
}
|
|
177114
|
+
return;
|
|
177115
|
+
}
|
|
177116
|
+
const rejection = bindRejection(decision);
|
|
177117
|
+
if (rejection) {
|
|
177118
|
+
try {
|
|
177119
|
+
await this.sendError(frame, rejection.code, rejection.message, controlFlags(), sock, generation);
|
|
177120
|
+
} finally {
|
|
177121
|
+
await this.opts.onRouteGone?.(tentative);
|
|
177122
|
+
}
|
|
177123
|
+
return;
|
|
177124
|
+
}
|
|
177125
|
+
try {
|
|
177126
|
+
await this.sendOn(sock, generation, buildFrameWithVersion(frame.header.ver, FrameType.Response, controlFlags(), 0, 0, frame.header.corr, encodeJson({ op: "route.bind" })));
|
|
177127
|
+
} catch (error51) {
|
|
177128
|
+
await this.opts.onRouteGone?.(tentative);
|
|
177129
|
+
throw error51;
|
|
177130
|
+
}
|
|
177131
|
+
if (this.sock !== sock || this.generation !== generation || this.closeStarted || this.closedErr) {
|
|
177132
|
+
await this.opts.onRouteGone?.(tentative);
|
|
177133
|
+
return;
|
|
177134
|
+
}
|
|
177135
|
+
this.liveRoutes.set(tentative.channel, tentative);
|
|
177136
|
+
await this.opts.onBound?.(tentative);
|
|
177137
|
+
}
|
|
177138
|
+
async handleDataRequest(frame, handle, sock, generation) {
|
|
177139
|
+
const { corr, ver } = frame.header;
|
|
177140
|
+
const key = routeKey(handle, corr);
|
|
177141
|
+
const controller = new AbortController;
|
|
177142
|
+
this.inflight.set(key, controller);
|
|
177143
|
+
const context = {
|
|
177144
|
+
handle,
|
|
177145
|
+
signal: controller.signal,
|
|
177146
|
+
currentEpoch: () => this.connectionEpoch,
|
|
177147
|
+
emit: async (eventBody, options = {}) => {
|
|
177148
|
+
this.assertLiveHandle(handle);
|
|
177149
|
+
if (controller.signal.aborted)
|
|
177150
|
+
return;
|
|
177151
|
+
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));
|
|
177152
|
+
}
|
|
177153
|
+
};
|
|
177154
|
+
const releasePermit = await (this.requestGate ?? new AsyncPermitPool(DEFAULT_PROVIDER_HANDLER_CAPACITY)).acquire();
|
|
177155
|
+
const dataFlags = buildFlags(false, Priority.Interactive, false);
|
|
177156
|
+
try {
|
|
177157
|
+
const body = await this.opts.handler(handle, frame.body, context);
|
|
177158
|
+
if (controller.signal.aborted)
|
|
177159
|
+
return;
|
|
177160
|
+
this.assertLiveHandle(handle);
|
|
177161
|
+
if (body === undefined) {
|
|
177162
|
+
await this.sendOn(sock, generation, buildFrameWithVersion(ver, FrameType.StreamEnd, dataFlags, handle.channel, handle.epoch, corr, new Uint8Array(0)));
|
|
177163
|
+
} else if (body instanceof Uint8Array) {
|
|
177164
|
+
await this.sendOn(sock, generation, buildFrameWithVersion(ver, FrameType.Response, dataFlags, handle.channel, handle.epoch, corr, body));
|
|
177165
|
+
} else {
|
|
177166
|
+
throw new SubcProviderError("provider handler must return a Uint8Array or void", "invalid_handler_response");
|
|
177167
|
+
}
|
|
177168
|
+
} catch (error51) {
|
|
177169
|
+
if (error51 instanceof StaleRouteHandleError || controller.signal.aborted)
|
|
177170
|
+
return;
|
|
177171
|
+
await this.sendError(frame, error51 instanceof SubcProviderError && error51.code ? error51.code : "handler_error", error51 instanceof Error ? error51.message : String(error51), dataFlags, sock, generation);
|
|
177172
|
+
} finally {
|
|
177173
|
+
releasePermit();
|
|
177174
|
+
if (this.inflight.get(key) === controller)
|
|
177175
|
+
this.inflight.delete(key);
|
|
177176
|
+
}
|
|
177177
|
+
}
|
|
177178
|
+
async handleHealthRequest(frame, sock, generation) {
|
|
177179
|
+
const { corr, ver } = frame.header;
|
|
177180
|
+
const key = `control:${generation}:${corr}`;
|
|
177181
|
+
const controller = new AbortController;
|
|
177182
|
+
this.inflight.set(key, controller);
|
|
177183
|
+
const releasePermit = await (this.requestGate ?? new AsyncPermitPool(DEFAULT_PROVIDER_HANDLER_CAPACITY)).acquire();
|
|
177184
|
+
try {
|
|
177185
|
+
if (controller.signal.aborted)
|
|
177186
|
+
return;
|
|
177187
|
+
const report = await this.opts.health();
|
|
177188
|
+
await this.sendOn(sock, generation, buildFrameWithVersion(ver, FrameType.Response, controlFlags(), 0, 0, corr, encodeJson({
|
|
177189
|
+
op: HEALTH_CHECK_OP,
|
|
177190
|
+
status: report.status,
|
|
177191
|
+
...report.detail === undefined ? {} : { detail: report.detail },
|
|
177192
|
+
...report.metrics === undefined ? {} : { metrics: report.metrics }
|
|
177193
|
+
})));
|
|
177194
|
+
} catch (error51) {
|
|
177195
|
+
await this.sendError(frame, error51 instanceof SubcProviderError && error51.code ? error51.code : "health_error", error51 instanceof Error ? error51.message : String(error51), controlFlags(), sock, generation);
|
|
177196
|
+
} finally {
|
|
177197
|
+
releasePermit();
|
|
177198
|
+
if (this.inflight.get(key) === controller)
|
|
177199
|
+
this.inflight.delete(key);
|
|
177200
|
+
}
|
|
177201
|
+
}
|
|
177202
|
+
async sendError(frame, code, message, flags, sock, generation) {
|
|
177203
|
+
await this.sendOn(sock, generation, buildFrameWithVersion(frame.header.ver, FrameType.Error, flags, frame.header.channel, frame.header.epoch, frame.header.corr, encodeJson({ code, message })));
|
|
177204
|
+
}
|
|
177205
|
+
async sendOn(sock, generation, frame) {
|
|
177206
|
+
if (this.sock !== sock || this.generation !== generation || this.closeStarted || this.closedErr)
|
|
177207
|
+
return;
|
|
177208
|
+
await sendFrame(sock, frame);
|
|
177209
|
+
}
|
|
177210
|
+
handleUnexpectedDrop(sock, generation, cause) {
|
|
177211
|
+
this.cancelRestoredDebounce();
|
|
177212
|
+
this.abortGeneration(generation);
|
|
177213
|
+
this.generation += 1;
|
|
177214
|
+
sock.close();
|
|
177215
|
+
this.enqueueConnectionState({ state: "down", cause });
|
|
177216
|
+
this.scheduleReconnectAfterDrop(cause);
|
|
177217
|
+
}
|
|
177218
|
+
scheduleReconnectAfterDrop(trigger) {
|
|
177219
|
+
if (this.closeStarted || this.reconnecting)
|
|
177220
|
+
return;
|
|
177221
|
+
const promise2 = this.reconnectWithRetry(trigger).catch((err) => {
|
|
177222
|
+
if (!this.closeStarted)
|
|
177223
|
+
this.failFatal(err instanceof Error ? err : new SubcProviderError(String(err)));
|
|
177224
|
+
}).finally(() => {
|
|
177225
|
+
if (this.reconnecting === promise2)
|
|
177226
|
+
this.reconnecting = null;
|
|
177227
|
+
});
|
|
177228
|
+
this.reconnecting = promise2;
|
|
177229
|
+
}
|
|
177230
|
+
async reconnectWithRetry(_trigger) {
|
|
177231
|
+
let attempt = 0;
|
|
177232
|
+
let delay = this.opts.reconnectBackoff.baseMs;
|
|
177233
|
+
for (;; ) {
|
|
177234
|
+
if (this.closeStarted)
|
|
177235
|
+
throw new SubcProviderError("provider closed");
|
|
177236
|
+
attempt += 1;
|
|
177237
|
+
this.enqueueConnectionState({ state: "reconnecting", attempt });
|
|
177238
|
+
try {
|
|
177239
|
+
const opened = await SubcProvider.openConnection(this.opts);
|
|
177240
|
+
if (this.closeStarted) {
|
|
177241
|
+
opened.sock.close();
|
|
177242
|
+
throw new SubcProviderError("provider closed");
|
|
177243
|
+
}
|
|
177244
|
+
this.replaceConnection(opened);
|
|
177245
|
+
return;
|
|
177246
|
+
} catch (err) {
|
|
177247
|
+
if (this.closeStarted)
|
|
177248
|
+
throw err;
|
|
177249
|
+
if (!isProviderReconnectTransient(err))
|
|
177250
|
+
throw err;
|
|
177251
|
+
await this.opts.sleep(delay);
|
|
177252
|
+
delay = Math.min(delay * 2, this.opts.reconnectBackoff.capMs);
|
|
177253
|
+
}
|
|
177254
|
+
}
|
|
177255
|
+
}
|
|
177256
|
+
replaceConnection(opened) {
|
|
177257
|
+
this.sock.close();
|
|
177258
|
+
this.sock = opened.sock;
|
|
177259
|
+
this.currentConn = opened.conn;
|
|
177260
|
+
this.storage = opened.ack.storage;
|
|
177261
|
+
this.closedErr = null;
|
|
177262
|
+
this.connectionEpoch += 1;
|
|
177263
|
+
this.connectionToken = newConnectionToken();
|
|
177264
|
+
this.liveRoutes.clear();
|
|
177265
|
+
this.nextCorr = 1n;
|
|
177266
|
+
const generation = this.generation;
|
|
177267
|
+
this.readLoop(opened.sock, generation);
|
|
177268
|
+
this.scheduleRestored(generation, this.connectionEpoch);
|
|
177269
|
+
}
|
|
177270
|
+
scheduleRestored(generation, epoch) {
|
|
177271
|
+
if (!this.opts.onConnectionState)
|
|
177272
|
+
return;
|
|
177273
|
+
const token = ++this.restoredDebounceToken;
|
|
177274
|
+
this.opts.sleep(this.opts.restoredDebounceMs).then(() => {
|
|
177275
|
+
if (token === this.restoredDebounceToken && !this.closeStarted && this.sock && this.generation === generation && this.connectionEpoch === epoch) {
|
|
177276
|
+
this.enqueueConnectionState({ state: "restored", epoch });
|
|
177277
|
+
}
|
|
177278
|
+
}).catch((err) => {
|
|
177279
|
+
if (token === this.restoredDebounceToken && !this.closeStarted) {
|
|
177280
|
+
console.warn("SubcProvider restored debounce timer failed", err);
|
|
177281
|
+
}
|
|
177282
|
+
});
|
|
177283
|
+
}
|
|
177284
|
+
cancelRestoredDebounce() {
|
|
177285
|
+
this.restoredDebounceToken += 1;
|
|
177286
|
+
}
|
|
177287
|
+
enqueueConnectionState(event) {
|
|
177288
|
+
if (!this.opts.onConnectionState)
|
|
177289
|
+
return;
|
|
177290
|
+
this.stateQueue.push(event);
|
|
177291
|
+
if (!this.drainingStateQueue)
|
|
177292
|
+
this.drainConnectionStateQueue();
|
|
177293
|
+
}
|
|
177294
|
+
async drainConnectionStateQueue() {
|
|
177295
|
+
if (this.drainingStateQueue)
|
|
177296
|
+
return;
|
|
177297
|
+
this.drainingStateQueue = true;
|
|
177298
|
+
try {
|
|
177299
|
+
while (this.stateQueue.length > 0) {
|
|
177300
|
+
const event = this.stateQueue[0];
|
|
177301
|
+
try {
|
|
177302
|
+
await this.opts.onConnectionState?.(event);
|
|
177303
|
+
this.stateQueue.shift();
|
|
177304
|
+
} catch (err) {
|
|
177305
|
+
if (event.state === "restored") {
|
|
177306
|
+
console.warn("SubcProvider restored callback failed; retrying delivery", err);
|
|
177307
|
+
await pauseBeforeStateRetry();
|
|
177308
|
+
continue;
|
|
177309
|
+
}
|
|
177310
|
+
console.warn("SubcProvider connection-state callback failed", err);
|
|
177311
|
+
this.stateQueue.shift();
|
|
177312
|
+
}
|
|
177313
|
+
}
|
|
177314
|
+
} finally {
|
|
177315
|
+
this.drainingStateQueue = false;
|
|
177316
|
+
if (this.stateQueue.length > 0)
|
|
177317
|
+
this.drainConnectionStateQueue();
|
|
177318
|
+
}
|
|
177319
|
+
}
|
|
177320
|
+
isLiveHandle(handle) {
|
|
177321
|
+
return belongsToConnection(handle, this.connectionToken) && this.liveRoutes.get(handle.channel) === handle;
|
|
177322
|
+
}
|
|
177323
|
+
assertLiveHandle(handle) {
|
|
177324
|
+
if (!this.isLiveHandle(handle))
|
|
177325
|
+
throw new StaleRouteHandleError(handle);
|
|
177326
|
+
}
|
|
177327
|
+
allocateCorr() {
|
|
177328
|
+
const maximum = 0xffffffffffffffffn;
|
|
177329
|
+
if (this.nextCorr > maximum) {
|
|
177330
|
+
const error51 = new SubcProviderError("channel-0 correlation id allocator exhausted", "corr_exhausted");
|
|
177331
|
+
this.handleUnexpectedDrop(this.sock, this.generation, error51);
|
|
177332
|
+
throw error51;
|
|
177333
|
+
}
|
|
177334
|
+
const corr = this.nextCorr;
|
|
177335
|
+
this.nextCorr += 1n;
|
|
177336
|
+
return corr;
|
|
177337
|
+
}
|
|
177338
|
+
failFatal(err) {
|
|
177339
|
+
if (!this.closedErr)
|
|
177340
|
+
this.closedErr = err;
|
|
177341
|
+
this.closeStarted = true;
|
|
177342
|
+
this.cancelRestoredDebounce();
|
|
177343
|
+
this.abortAllInflight();
|
|
177344
|
+
this.sock.close();
|
|
177345
|
+
this.finishClosed();
|
|
177346
|
+
}
|
|
177347
|
+
finishClosed() {
|
|
177348
|
+
this.resolveClosed();
|
|
177349
|
+
}
|
|
177350
|
+
}
|
|
177351
|
+
function routeKey(handle, corr) {
|
|
177352
|
+
return `${handle.channel}:${handle.epoch}:${corr}`;
|
|
177353
|
+
}
|
|
177354
|
+
function providerErrorFromFrame(frame) {
|
|
177355
|
+
try {
|
|
177356
|
+
const body = parseJson(frame.body);
|
|
177357
|
+
return new SubcProviderError(body.message ?? "subc error", body.code);
|
|
177358
|
+
} catch {
|
|
177359
|
+
return new SubcProviderError(Buffer4.from(frame.body).toString("utf8") || "subc error");
|
|
177360
|
+
}
|
|
177361
|
+
}
|
|
177362
|
+
function launchNonce(opts) {
|
|
177363
|
+
const nonce = opts.launchNonce ?? process.env[SUBC_LAUNCH_NONCE_ENV2];
|
|
177364
|
+
return nonce && nonce.length > 0 ? nonce : undefined;
|
|
177365
|
+
}
|
|
177366
|
+
var SUBC_LAUNCH_NONCE_ENV2 = "SUBC_LAUNCH_NONCE";
|
|
177367
|
+
function normalizeProviderConnectOptions(opts) {
|
|
177368
|
+
return {
|
|
177369
|
+
connectionFile: opts.connectionFile,
|
|
177370
|
+
manifest: opts.manifest,
|
|
177371
|
+
handler: opts.handler,
|
|
177372
|
+
health: opts.health ?? (() => ({ status: "ok" })),
|
|
177373
|
+
handshakeTimeoutMs: opts.handshakeTimeoutMs,
|
|
177374
|
+
controlOps: opts.controlOps,
|
|
177375
|
+
onBind: opts.onBind,
|
|
177376
|
+
onBound: opts.onBound,
|
|
177377
|
+
onRouteGone: opts.onRouteGone,
|
|
177378
|
+
reconnectBackoff: opts.reconnectBackoff ?? DEFAULT_RECONNECT_BACKOFF,
|
|
177379
|
+
sleep: opts.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms))),
|
|
177380
|
+
restoredDebounceMs: opts.restoredDebounceMs ?? DEFAULT_RESTORED_DEBOUNCE_MS,
|
|
177381
|
+
onConnectionState: opts.onConnectionState,
|
|
177382
|
+
launchNonce: opts.launchNonce
|
|
177383
|
+
};
|
|
177384
|
+
}
|
|
177385
|
+
function normalizedControlOps(controlOps) {
|
|
177386
|
+
if (controlOps === null)
|
|
177387
|
+
return null;
|
|
177388
|
+
const merged = new Set(controlOps ?? []);
|
|
177389
|
+
merged.add(HEALTH_CHECK_OP);
|
|
177390
|
+
return [...merged];
|
|
177391
|
+
}
|
|
177392
|
+
function buildHelloFrame(opts) {
|
|
177393
|
+
const nonce = launchNonce(opts);
|
|
177394
|
+
return buildFrame(FrameType.Hello, controlFlags(), 0, 0, HELLO_CORR, encodeJson({
|
|
177395
|
+
manifest: normalizeManifest(opts.manifest),
|
|
177396
|
+
protocol_ver: PROTOCOL_VERSION,
|
|
177397
|
+
control_ops: normalizedControlOps(opts.controlOps),
|
|
177398
|
+
...nonce ? { launch_nonce: nonce } : {}
|
|
177399
|
+
}));
|
|
177400
|
+
}
|
|
177401
|
+
function isProviderReconnectTransient(err) {
|
|
177402
|
+
if (err instanceof SubcProviderError)
|
|
177403
|
+
return err.code === "duplicate_module_id";
|
|
177404
|
+
if (err instanceof SocketClosedError || err instanceof SocketTimeoutError)
|
|
177405
|
+
return true;
|
|
177406
|
+
if (err instanceof SocketWriteNotQueuedError || err instanceof SocketWriteQueuedError)
|
|
177407
|
+
return true;
|
|
177408
|
+
if (err instanceof AuthError)
|
|
177409
|
+
return true;
|
|
177410
|
+
if (err instanceof ConnectionFileError)
|
|
177411
|
+
return false;
|
|
177412
|
+
const code = errorCode2(err);
|
|
177413
|
+
return code === "ECONNREFUSED" || code === "ECONNRESET" || code === "EPIPE" || code === "ETIMEDOUT" || code === "ENOENT";
|
|
177414
|
+
}
|
|
177415
|
+
function errorCode2(err) {
|
|
177416
|
+
if (typeof err === "object" && err !== null && "code" in err) {
|
|
177417
|
+
const code = err.code;
|
|
177418
|
+
if (typeof code === "string")
|
|
177419
|
+
return code;
|
|
177420
|
+
}
|
|
177421
|
+
return;
|
|
177422
|
+
}
|
|
177423
|
+
async function pauseBeforeStateRetry() {
|
|
177424
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
177425
|
+
}
|
|
177426
|
+
function controlFlags() {
|
|
177427
|
+
return buildFlags(false, Priority.Passive, false);
|
|
177428
|
+
}
|
|
177429
|
+
async function sendFrame(sock, frame) {
|
|
177430
|
+
await sock.write(encodeFrame(frame), Date.now() + WRITE_TIMEOUT_MS);
|
|
177431
|
+
}
|
|
177432
|
+
async function expectHelloAck(sock, deadline) {
|
|
177433
|
+
const frame = await sock.readFrame(deadline, deadline);
|
|
177434
|
+
switch (frame.header.ty) {
|
|
177435
|
+
case FrameType.HelloAck: {
|
|
177436
|
+
const ack = parseJson(frame.body);
|
|
177437
|
+
if (ack.negotiated_ver !== PROTOCOL_VERSION) {
|
|
177438
|
+
throw new SubcProviderError(`subc negotiated protocol ${ack.negotiated_ver}; expected exactly ${PROTOCOL_VERSION}`, "unsupported_version");
|
|
177439
|
+
}
|
|
177440
|
+
return ack;
|
|
177441
|
+
}
|
|
177442
|
+
case FrameType.Error: {
|
|
177443
|
+
const error51 = parseJson(frame.body);
|
|
177444
|
+
throw new SubcProviderError(`subc rejected HELLO: ${error51.code ?? "unknown"} — ${error51.message ?? "subc error"}`, error51.code);
|
|
177445
|
+
}
|
|
177446
|
+
default:
|
|
177447
|
+
throw new SubcProviderError(`unexpected frame ${FrameType[frame.header.ty]} awaiting HELLO_ACK`);
|
|
177448
|
+
}
|
|
177449
|
+
}
|
|
177450
|
+
function encodeJson(value) {
|
|
177451
|
+
return new Uint8Array(Buffer4.from(JSON.stringify(value), "utf8"));
|
|
177452
|
+
}
|
|
177453
|
+
function parseJson(bytes) {
|
|
177454
|
+
return JSON.parse(Buffer4.from(bytes).toString("utf8"));
|
|
177455
|
+
}
|
|
177456
|
+
function numberField(value, field) {
|
|
177457
|
+
if (typeof value !== "number" || !Number.isInteger(value)) {
|
|
177458
|
+
throw new SubcProviderError(`route.bind ${field} must be an integer`);
|
|
177459
|
+
}
|
|
177460
|
+
return value;
|
|
177461
|
+
}
|
|
177462
|
+
function bindRejection(decision) {
|
|
177463
|
+
if (decision === undefined || decision === true)
|
|
177464
|
+
return null;
|
|
177465
|
+
if (decision === false) {
|
|
177466
|
+
return { code: "route_rejected", message: "route.bind rejected by provider" };
|
|
177467
|
+
}
|
|
177468
|
+
if (decision.accept)
|
|
177469
|
+
return null;
|
|
177470
|
+
return {
|
|
177471
|
+
code: decision.code ?? "route_rejected",
|
|
177472
|
+
message: decision.message ?? "route.bind rejected by provider"
|
|
177473
|
+
};
|
|
177474
|
+
}
|
|
177475
|
+
function normalizeManifest(manifest) {
|
|
177476
|
+
return {
|
|
177477
|
+
module_id: manifest.module_id,
|
|
177478
|
+
module_version: manifest.module_version,
|
|
177479
|
+
protocol_ver: manifest.protocol_ver,
|
|
177480
|
+
trust_tier: manifest.trust_tier,
|
|
177481
|
+
provides: manifest.provides.map(normalizeProviderRole),
|
|
177482
|
+
consumes: manifest.consumes.map(normalizeConsumerRole),
|
|
177483
|
+
scheduled_tasks: manifest.scheduled_tasks.map(normalizeScheduledTask),
|
|
177484
|
+
bindings: {
|
|
177485
|
+
storage: {
|
|
177486
|
+
kind: manifest.bindings.storage.kind,
|
|
177487
|
+
scope: manifest.bindings.storage.scope,
|
|
177488
|
+
owns_schema: manifest.bindings.storage.owns_schema
|
|
177489
|
+
},
|
|
177490
|
+
vault_grants: manifest.bindings.vault_grants.map((grant) => ({
|
|
177491
|
+
secret: grant.secret,
|
|
177492
|
+
reason: grant.reason
|
|
177493
|
+
})),
|
|
177494
|
+
identity: {
|
|
177495
|
+
requires: [...manifest.bindings.identity.requires],
|
|
177496
|
+
optional: [...manifest.bindings.identity.optional]
|
|
177497
|
+
}
|
|
177498
|
+
}
|
|
177499
|
+
};
|
|
177500
|
+
}
|
|
177501
|
+
function normalizeProviderRole(role) {
|
|
177502
|
+
switch (role.role) {
|
|
177503
|
+
case "tool_provider":
|
|
177504
|
+
return {
|
|
177505
|
+
role: "tool_provider",
|
|
177506
|
+
tools: role.tools.map((tool) => ({
|
|
177507
|
+
name: tool.name,
|
|
177508
|
+
...tool.description === undefined ? {} : { description: tool.description },
|
|
177509
|
+
execution_mode: tool.execution_mode,
|
|
177510
|
+
schema: tool.schema
|
|
177511
|
+
})),
|
|
177512
|
+
identity_scope: [...role.identity_scope],
|
|
177513
|
+
concurrency: role.concurrency,
|
|
177514
|
+
emits_push: role.emits_push,
|
|
177515
|
+
sub_supervises: role.sub_supervises
|
|
177516
|
+
};
|
|
177517
|
+
case "pipeline_stage":
|
|
177518
|
+
return {
|
|
177519
|
+
role: "pipeline_stage",
|
|
177520
|
+
stage: role.stage,
|
|
177521
|
+
applies_to: {
|
|
177522
|
+
provider: role.applies_to.provider,
|
|
177523
|
+
model: role.applies_to.model
|
|
177524
|
+
},
|
|
177525
|
+
interface: role.interface,
|
|
177526
|
+
declares_frozen_floor: role.declares_frozen_floor,
|
|
177527
|
+
needs_signals: [...role.needs_signals],
|
|
177528
|
+
conformance_class: role.conformance_class
|
|
177529
|
+
};
|
|
177530
|
+
case "management_surface":
|
|
177531
|
+
return {
|
|
177532
|
+
role: "management_surface",
|
|
177533
|
+
operations: role.operations.map((operation) => ({
|
|
177534
|
+
name: operation.name,
|
|
177535
|
+
kind: operation.kind
|
|
177536
|
+
})),
|
|
177537
|
+
config_schema: role.config_schema,
|
|
177538
|
+
observability: role.observability.map((surface) => ({
|
|
177539
|
+
name: surface.name,
|
|
177540
|
+
kind: surface.kind
|
|
177541
|
+
})),
|
|
177542
|
+
identity_scope: [...role.identity_scope]
|
|
177543
|
+
};
|
|
177544
|
+
case "internal_service":
|
|
177545
|
+
return {
|
|
177546
|
+
role: "internal_service",
|
|
177547
|
+
service_id: role.service_id,
|
|
177548
|
+
transport: role.transport,
|
|
177549
|
+
agent_facing: role.agent_facing,
|
|
177550
|
+
operations: [...role.operations]
|
|
177551
|
+
};
|
|
177552
|
+
}
|
|
177553
|
+
}
|
|
177554
|
+
function normalizeConsumerRole(role) {
|
|
177555
|
+
switch (role.role) {
|
|
177556
|
+
case "tool_client":
|
|
177557
|
+
return { role: "tool_client", of: [...role.of] };
|
|
177558
|
+
case "llm_client":
|
|
177559
|
+
return { role: "llm_client", via: role.via, auth: role.auth };
|
|
177560
|
+
case "service_client":
|
|
177561
|
+
return { role: "service_client", of: [...role.of] };
|
|
177562
|
+
}
|
|
177563
|
+
}
|
|
177564
|
+
function normalizeScheduledTask(task) {
|
|
177565
|
+
return {
|
|
177566
|
+
task_id: task.task_id,
|
|
177567
|
+
eligibility: {
|
|
177568
|
+
cooldown: task.eligibility.cooldown,
|
|
177569
|
+
window: task.eligibility.window
|
|
177570
|
+
},
|
|
177571
|
+
lease_scope: task.lease_scope,
|
|
177572
|
+
renews_during_calls: task.renews_during_calls,
|
|
177573
|
+
toolset: [...task.toolset],
|
|
177574
|
+
model_policy: {
|
|
177575
|
+
tier: task.model_policy.tier,
|
|
177576
|
+
fallback_chain: [...task.model_policy.fallback_chain]
|
|
177577
|
+
},
|
|
177578
|
+
step_cap: task.step_cap,
|
|
177579
|
+
circuit_breaker: {
|
|
177580
|
+
identical_failures: task.circuit_breaker.identical_failures
|
|
177581
|
+
}
|
|
177582
|
+
};
|
|
177583
|
+
}
|
|
177584
|
+
// ../plugin/src/features/magic-context/memory/embedding-synapse.ts
|
|
177585
|
+
init_logger();
|
|
177586
|
+
var SYNAPSE_DEFAULT_MODEL = "gte-modernbert-base-f16";
|
|
177587
|
+
var SYNAPSE_MAX_INPUT_TOKENS = 8192;
|
|
177588
|
+
var SYNAPSE_DEFAULT_QUERY_TIMEOUT_MS = 3000;
|
|
177589
|
+
var SYNAPSE_DEFAULT_BATCH_TIMEOUT_MS = 120000;
|
|
177590
|
+
|
|
177591
|
+
class SynapseEmbeddingError extends Error {
|
|
177592
|
+
code;
|
|
177593
|
+
retryAfterMs;
|
|
177594
|
+
permanent;
|
|
177595
|
+
constructor(code, message, options) {
|
|
177596
|
+
super(message, options?.cause === undefined ? undefined : { cause: options.cause });
|
|
177597
|
+
this.name = "SynapseEmbeddingError";
|
|
177598
|
+
this.code = code;
|
|
177599
|
+
this.permanent = options?.permanent ?? isPermanentSynapseCode(code);
|
|
177600
|
+
this.retryAfterMs = options?.retryAfterMs ?? (this.permanent ? undefined : 100);
|
|
177601
|
+
}
|
|
177602
|
+
}
|
|
177603
|
+
function isPermanentSynapseCode(code) {
|
|
177604
|
+
return code === "artifact_invalid" || code === "substitution_rejected" || code === "not_certified" || code === "probe_required" || code === "idempotency_conflict" || code === "schema_violation";
|
|
177605
|
+
}
|
|
177606
|
+
function asRecord(value) {
|
|
177607
|
+
return value !== null && typeof value === "object" ? value : null;
|
|
177608
|
+
}
|
|
177609
|
+
function responseBody(value) {
|
|
177610
|
+
const record2 = asRecord(value);
|
|
177611
|
+
const result = asRecord(record2?.result);
|
|
177612
|
+
return result ? { ...record2, ...result } : record2 ?? {};
|
|
177613
|
+
}
|
|
177614
|
+
function readRetryAfter(value) {
|
|
177615
|
+
const record2 = asRecord(value);
|
|
177616
|
+
const candidate = record2?.retry_after_ms ?? record2?.retryAfterMs;
|
|
177617
|
+
if (typeof candidate !== "number" || !Number.isFinite(candidate) || candidate < 0)
|
|
177618
|
+
return;
|
|
177619
|
+
return Math.ceil(candidate);
|
|
177620
|
+
}
|
|
177621
|
+
function readErrorCode(value) {
|
|
177622
|
+
const record2 = asRecord(value);
|
|
177623
|
+
if (typeof record2?.code === "string")
|
|
177624
|
+
return record2.code;
|
|
177625
|
+
if (value instanceof Error && "code" in value && typeof value.code === "string") {
|
|
177626
|
+
return value.code;
|
|
177627
|
+
}
|
|
177628
|
+
return;
|
|
177629
|
+
}
|
|
177630
|
+
function classifyError(value) {
|
|
177631
|
+
if (value instanceof SynapseEmbeddingError)
|
|
177632
|
+
return value;
|
|
177633
|
+
const code = readErrorCode(value) ?? (value instanceof Error ? value.name : "transport");
|
|
177634
|
+
const normalized = code.toLowerCase();
|
|
177635
|
+
let mapped = "transport";
|
|
177636
|
+
if (normalized.includes("queue_full"))
|
|
177637
|
+
mapped = "queue_full";
|
|
177638
|
+
else if (normalized.includes("model_loading"))
|
|
177639
|
+
mapped = "model_loading";
|
|
177640
|
+
else if (normalized.includes("timeout") || normalized.includes("deadline"))
|
|
177641
|
+
mapped = "timeout";
|
|
177642
|
+
else if (normalized.includes("artifact_invalid"))
|
|
177643
|
+
mapped = "artifact_invalid";
|
|
177644
|
+
else if (normalized.includes("substitution"))
|
|
177645
|
+
mapped = "substitution_rejected";
|
|
177646
|
+
else if (normalized.includes("not_certified"))
|
|
177647
|
+
mapped = "not_certified";
|
|
177648
|
+
else if (normalized.includes("probe_required"))
|
|
177649
|
+
mapped = "probe_required";
|
|
177650
|
+
else if (normalized.includes("idempotency_conflict"))
|
|
177651
|
+
mapped = "idempotency_conflict";
|
|
177652
|
+
else if (normalized.includes("schema"))
|
|
177653
|
+
mapped = "schema_violation";
|
|
177654
|
+
else if (normalized.includes("module_restarted") || normalized.includes("module restarted"))
|
|
177655
|
+
mapped = "module_restarted";
|
|
177656
|
+
const message = value instanceof Error ? value.message : String(value);
|
|
177657
|
+
return new SynapseEmbeddingError(mapped, message, {
|
|
177658
|
+
retryAfterMs: readRetryAfter(value) ?? (isPermanentSynapseCode(mapped) ? undefined : 100),
|
|
177659
|
+
cause: value
|
|
177660
|
+
});
|
|
177661
|
+
}
|
|
177662
|
+
function wait(ms) {
|
|
177663
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
177664
|
+
}
|
|
177665
|
+
function sha256(value) {
|
|
177666
|
+
return createHash8("sha256").update(value).digest("hex");
|
|
177667
|
+
}
|
|
177668
|
+
function stableJson(value) {
|
|
177669
|
+
if (Array.isArray(value))
|
|
177670
|
+
return `[${value.map(stableJson).join(",")}]`;
|
|
177671
|
+
if (value && typeof value === "object") {
|
|
177672
|
+
return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => `${JSON.stringify(key)}:${stableJson(entry)}`).join(",")}}`;
|
|
177673
|
+
}
|
|
177674
|
+
return JSON.stringify(value);
|
|
177675
|
+
}
|
|
177676
|
+
function getSynapseLaneIdentity(model, fingerprint) {
|
|
177677
|
+
return `synapse:v1:${sha256(stableJson({ model, fingerprint }))}`;
|
|
177678
|
+
}
|
|
177679
|
+
function getSynapseBatchRequestKey(args) {
|
|
177680
|
+
return sha256(stableJson({
|
|
177681
|
+
op: "embed.batch",
|
|
177682
|
+
model: args.model,
|
|
177683
|
+
required_fingerprint: args.fingerprint,
|
|
177684
|
+
required_epoch: args.tableEpoch,
|
|
177685
|
+
allow_equivalent: false,
|
|
177686
|
+
accept_declared: false,
|
|
177687
|
+
ids: args.items.map((item) => item.id),
|
|
177688
|
+
content_sha256: args.items.map((item) => item.contentSha256)
|
|
177689
|
+
}));
|
|
177690
|
+
}
|
|
177691
|
+
function hashContent(text) {
|
|
177692
|
+
return sha256(text);
|
|
177693
|
+
}
|
|
177694
|
+
function extractCatalogEntries(value) {
|
|
177695
|
+
const body = responseBody(value);
|
|
177696
|
+
const raw = Array.isArray(body.models) ? body.models : Array.isArray(body.entries) ? body.entries : Array.isArray(value) ? value : [];
|
|
177697
|
+
const envelopeEpoch = typeof body.table_epoch === "number" && Number.isInteger(body.table_epoch) ? body.table_epoch : undefined;
|
|
177698
|
+
return raw.flatMap((entry) => {
|
|
177699
|
+
const record2 = asRecord(entry);
|
|
177700
|
+
if (!record2)
|
|
177701
|
+
return [];
|
|
177702
|
+
const model = typeof record2.model === "string" && record2.model.length > 0 ? record2.model : typeof record2.model_id === "string" ? record2.model_id : "";
|
|
177703
|
+
const fingerprint = typeof record2.fingerprint === "string" && record2.fingerprint.length > 0 ? record2.fingerprint : Array.isArray(record2.fingerprints) && typeof record2.fingerprints[0] === "string" ? record2.fingerprints[0] : "";
|
|
177704
|
+
const entryEpoch = record2.table_epoch ?? record2.tableEpoch;
|
|
177705
|
+
const tableEpoch = typeof entryEpoch === "number" && Number.isInteger(entryEpoch) ? entryEpoch : envelopeEpoch;
|
|
177706
|
+
const dims = record2.dims ?? record2.dimensions;
|
|
177707
|
+
if (model.length === 0 || fingerprint.length === 0 || typeof tableEpoch !== "number" || !Number.isInteger(tableEpoch)) {
|
|
177708
|
+
return [];
|
|
177709
|
+
}
|
|
177710
|
+
const recommendedBatch = record2.recommended_batch ?? record2.recommendedBatch;
|
|
177711
|
+
const state = typeof record2.state === "string" ? record2.state : undefined;
|
|
177712
|
+
return [
|
|
177713
|
+
{
|
|
177714
|
+
model,
|
|
177715
|
+
fingerprint,
|
|
177716
|
+
table_epoch: tableEpoch,
|
|
177717
|
+
...typeof dims === "number" && Number.isInteger(dims) && dims > 0 ? { dims } : {},
|
|
177718
|
+
...typeof recommendedBatch === "number" && recommendedBatch > 0 ? { recommended_batch: Math.floor(recommendedBatch) } : {},
|
|
177719
|
+
...record2.provenance !== undefined ? { provenance: record2.provenance } : {},
|
|
177720
|
+
...typeof record2.certified === "boolean" ? { certified: record2.certified } : {},
|
|
177721
|
+
...typeof record2.status === "string" ? { status: record2.status } : state ? { status: state } : {}
|
|
177722
|
+
}
|
|
177723
|
+
];
|
|
177724
|
+
});
|
|
177725
|
+
}
|
|
177726
|
+
function extractVector(value) {
|
|
177727
|
+
const body = responseBody(value);
|
|
177728
|
+
const fromVectors = Array.isArray(body.vectors) ? asRecord(body.vectors[0])?.vector : undefined;
|
|
177729
|
+
const raw = fromVectors ?? body.vector ?? body.embedding;
|
|
177730
|
+
if (!Array.isArray(raw) || raw.some((item) => typeof item !== "number" || !Number.isFinite(item))) {
|
|
177731
|
+
return null;
|
|
177732
|
+
}
|
|
177733
|
+
return { vector: Float32Array.from(raw), metadata: body };
|
|
177734
|
+
}
|
|
177735
|
+
function extractBatchItems(value) {
|
|
177736
|
+
const body = responseBody(value);
|
|
177737
|
+
const raw = Array.isArray(body.vectors) ? body.vectors : Array.isArray(body.items) ? body.items : Array.isArray(body.results) ? body.results : [];
|
|
177738
|
+
return raw.flatMap((item) => {
|
|
177739
|
+
const record2 = asRecord(item);
|
|
177740
|
+
return record2 ? [record2] : [];
|
|
177741
|
+
});
|
|
177742
|
+
}
|
|
177743
|
+
var sharedClient = null;
|
|
177744
|
+
var sharedClientFile = null;
|
|
177745
|
+
var sharedClientPromise = null;
|
|
177746
|
+
var factoryClients = new WeakMap;
|
|
177747
|
+
async function getSharedClient(options) {
|
|
177748
|
+
if (options.clientFactory) {
|
|
177749
|
+
let promise2 = factoryClients.get(options.clientFactory);
|
|
177750
|
+
if (!promise2) {
|
|
177751
|
+
promise2 = options.clientFactory();
|
|
177752
|
+
factoryClients.set(options.clientFactory, promise2);
|
|
177753
|
+
}
|
|
177754
|
+
return promise2;
|
|
177755
|
+
}
|
|
177756
|
+
if (sharedClient && sharedClientFile === options.connectionFile)
|
|
177757
|
+
return sharedClient;
|
|
177758
|
+
if (sharedClientPromise && sharedClientFile === options.connectionFile)
|
|
177759
|
+
return sharedClientPromise;
|
|
177760
|
+
sharedClientFile = options.connectionFile;
|
|
177761
|
+
sharedClientPromise = SubcClient.connect({ connectionFile: options.connectionFile }).then((client) => {
|
|
177762
|
+
sharedClient = client;
|
|
177763
|
+
return client;
|
|
177764
|
+
});
|
|
177765
|
+
return sharedClientPromise;
|
|
177766
|
+
}
|
|
177767
|
+
|
|
177768
|
+
class SynapseEmbeddingProvider {
|
|
177769
|
+
modelId;
|
|
177770
|
+
maxInputTokens = SYNAPSE_MAX_INPUT_TOKENS;
|
|
177771
|
+
metadata;
|
|
177772
|
+
options;
|
|
177773
|
+
client = null;
|
|
177774
|
+
initialized = false;
|
|
177775
|
+
initializing = null;
|
|
177776
|
+
permanentFailure = false;
|
|
177777
|
+
batchLimit = 16;
|
|
177778
|
+
constructor(options) {
|
|
177779
|
+
this.options = options;
|
|
177780
|
+
const model = options.model || SYNAPSE_DEFAULT_MODEL;
|
|
177781
|
+
const fingerprint = options.fingerprint ?? "";
|
|
177782
|
+
this.metadata = fingerprint && Number.isInteger(options.tableEpoch) && Number.isInteger(options.dims) && (options.dims ?? 0) > 0 ? {
|
|
177783
|
+
model,
|
|
177784
|
+
fingerprint,
|
|
177785
|
+
table_epoch: options.tableEpoch,
|
|
177786
|
+
dims: options.dims,
|
|
177787
|
+
...options.recommendedBatch ? { recommended_batch: Math.max(1, Math.floor(options.recommendedBatch)) } : {},
|
|
177788
|
+
...options.provenance !== undefined ? { provenance: options.provenance } : {},
|
|
177789
|
+
laneIdentity: getSynapseLaneIdentity(model, fingerprint)
|
|
177790
|
+
} : null;
|
|
177791
|
+
this.modelId = this.metadata?.laneIdentity ?? "synapse:v1:pending";
|
|
177792
|
+
this.batchLimit = this.metadata?.recommended_batch ?? 16;
|
|
177793
|
+
}
|
|
177794
|
+
static async discover(options) {
|
|
177795
|
+
const provider = new SynapseEmbeddingProvider(options);
|
|
177796
|
+
if (!await provider.initialize() || !provider.metadata) {
|
|
177797
|
+
throw new SynapseEmbeddingError("not_certified", "Synapse lane is not ready");
|
|
177798
|
+
}
|
|
177799
|
+
return provider.metadata;
|
|
177800
|
+
}
|
|
177801
|
+
async initialize() {
|
|
177802
|
+
if (this.initialized)
|
|
177803
|
+
return true;
|
|
177804
|
+
if (this.permanentFailure)
|
|
177805
|
+
return false;
|
|
177806
|
+
if (this.initializing)
|
|
177807
|
+
return this.initializing;
|
|
177808
|
+
this.initializing = (async () => {
|
|
177809
|
+
try {
|
|
177810
|
+
if (!this.options.clientFactory && !await connectionFileExists(this.options.connectionFile)) {
|
|
177811
|
+
throw new SynapseEmbeddingError("transport", `Synapse connection file is unavailable: ${this.options.connectionFile}`);
|
|
177812
|
+
}
|
|
177813
|
+
this.client = await getSharedClient(this.options);
|
|
177814
|
+
if (!this.metadata) {
|
|
177815
|
+
const discovered = await this.callWithRetry("models.list", {}, this.options.queryTimeoutMs ?? SYNAPSE_DEFAULT_QUERY_TIMEOUT_MS, false);
|
|
177816
|
+
const entries = extractCatalogEntries(discovered);
|
|
177817
|
+
const requested = this.options.model?.trim() || SYNAPSE_DEFAULT_MODEL;
|
|
177818
|
+
const entry = entries.find((candidate) => candidate.model === requested);
|
|
177819
|
+
if (!entry) {
|
|
177820
|
+
throw new SynapseEmbeddingError("artifact_invalid", `Synapse models.list did not return requested model ${requested}`);
|
|
177821
|
+
}
|
|
177822
|
+
if (entry.certified === false || entry.status === "not_certified") {
|
|
177823
|
+
throw new SynapseEmbeddingError("not_certified", `Synapse model ${entry.model} is not certified`);
|
|
177824
|
+
}
|
|
177825
|
+
const metadata = {
|
|
177826
|
+
...entry,
|
|
177827
|
+
laneIdentity: getSynapseLaneIdentity(entry.model, entry.fingerprint)
|
|
177828
|
+
};
|
|
177829
|
+
this.metadata = metadata;
|
|
177830
|
+
this.modelId = metadata.laneIdentity;
|
|
177831
|
+
this.batchLimit = metadata.recommended_batch ?? this.batchLimit;
|
|
177832
|
+
}
|
|
177833
|
+
this.initialized = true;
|
|
177834
|
+
return true;
|
|
177835
|
+
} catch (error51) {
|
|
177836
|
+
const classified = classifyError(error51);
|
|
177837
|
+
if (classified.permanent) {
|
|
177838
|
+
this.permanentFailure = true;
|
|
177839
|
+
log(`[magic-context] Synapse lane disabled: ${classified.code}: ${classified.message}`);
|
|
177840
|
+
} else {
|
|
177841
|
+
log(`[magic-context] Synapse lane unavailable: ${classified.message}`);
|
|
177842
|
+
}
|
|
177843
|
+
this.initialized = false;
|
|
177844
|
+
return false;
|
|
177845
|
+
} finally {
|
|
177846
|
+
this.initializing = null;
|
|
177847
|
+
}
|
|
177848
|
+
})();
|
|
177849
|
+
return this.initializing;
|
|
177850
|
+
}
|
|
177851
|
+
async embed(text, signal) {
|
|
177852
|
+
if (!await this.initialize() || signal?.aborted || !this.metadata)
|
|
177853
|
+
return null;
|
|
177854
|
+
try {
|
|
177855
|
+
const value = await this.callWithRetry("embed.query", this.requestConstraints({
|
|
177856
|
+
text,
|
|
177857
|
+
deadline_ms: this.options.queryTimeoutMs ?? SYNAPSE_DEFAULT_QUERY_TIMEOUT_MS
|
|
177858
|
+
}), this.options.queryTimeoutMs ?? SYNAPSE_DEFAULT_QUERY_TIMEOUT_MS, true, signal);
|
|
177859
|
+
const extracted = extractVector(value);
|
|
177860
|
+
if (!extracted)
|
|
177861
|
+
throw new SynapseEmbeddingError("schema_violation", "Synapse query returned no vector");
|
|
177862
|
+
this.validateResponse(extracted.metadata, extracted.vector.length);
|
|
177863
|
+
return extracted.vector;
|
|
177864
|
+
} catch (error51) {
|
|
177865
|
+
this.logCallFailure(error51, "embed.query");
|
|
177866
|
+
return null;
|
|
177867
|
+
}
|
|
177868
|
+
}
|
|
177869
|
+
async embedBatch(texts, signal) {
|
|
177870
|
+
if (texts.length === 0)
|
|
177871
|
+
return [];
|
|
177872
|
+
const items = texts.map((text, index) => ({
|
|
177873
|
+
id: `item:${index}`,
|
|
177874
|
+
text,
|
|
177875
|
+
contentSha256: hashContent(text)
|
|
177876
|
+
}));
|
|
177877
|
+
const map2 = await this.embedItems(items, signal);
|
|
177878
|
+
return items.map((item) => map2.get(item.id) ?? null);
|
|
177879
|
+
}
|
|
177880
|
+
async embedItems(items, signal) {
|
|
177881
|
+
const output = new Map;
|
|
177882
|
+
if (items.length === 0 || !await this.initialize() || !this.metadata || signal?.aborted) {
|
|
177883
|
+
return output;
|
|
177884
|
+
}
|
|
177885
|
+
for (let start = 0;start < items.length; start += this.batchLimit) {
|
|
177886
|
+
if (signal?.aborted || this.permanentFailure)
|
|
177887
|
+
break;
|
|
177888
|
+
const page = items.slice(start, start + this.batchLimit);
|
|
177889
|
+
try {
|
|
177890
|
+
const requestKey = this.requestKey(page);
|
|
177891
|
+
let body = {};
|
|
177892
|
+
let restarted = false;
|
|
177893
|
+
for (;; ) {
|
|
177894
|
+
try {
|
|
177895
|
+
body = await this.callWithRetry("embed.batch", this.batchRequest(page, requestKey), this.options.batchTimeoutMs ?? SYNAPSE_DEFAULT_BATCH_TIMEOUT_MS, true, signal);
|
|
177896
|
+
const first = responseBody(body);
|
|
177897
|
+
const jobId = typeof first.job_id === "string" ? first.job_id : null;
|
|
177898
|
+
if (jobId)
|
|
177899
|
+
body = await this.pollBatch(jobId, requestKey, signal);
|
|
177900
|
+
break;
|
|
177901
|
+
} catch (error51) {
|
|
177902
|
+
const classified = classifyError(error51);
|
|
177903
|
+
if (classified.code !== "module_restarted" || restarted)
|
|
177904
|
+
throw classified;
|
|
177905
|
+
restarted = true;
|
|
177906
|
+
}
|
|
177907
|
+
}
|
|
177908
|
+
const batchEnvelope = responseBody(body);
|
|
177909
|
+
for (const item of extractBatchItems(body)) {
|
|
177910
|
+
const id = typeof item.id === "string" ? item.id : "";
|
|
177911
|
+
const vector = item.vector ?? item.embedding;
|
|
177912
|
+
if (!id || !Array.isArray(vector)) {
|
|
177913
|
+
throw new SynapseEmbeddingError("schema_violation", "Synapse batch item is malformed");
|
|
177914
|
+
}
|
|
177915
|
+
const expected = page.find((candidate) => candidate.id === id);
|
|
177916
|
+
if (!expected) {
|
|
177917
|
+
throw new SynapseEmbeddingError("schema_violation", `Synapse returned unknown item ${id}`);
|
|
177918
|
+
}
|
|
177919
|
+
if (typeof item.content_sha256 === "string" && item.content_sha256 !== expected.contentSha256) {
|
|
177920
|
+
throw new SynapseEmbeddingError("artifact_invalid", `Synapse content hash mismatch for item ${id}`);
|
|
177921
|
+
}
|
|
177922
|
+
const vectorArray = Float32Array.from(vector);
|
|
177923
|
+
this.validateResponse({ ...batchEnvelope, ...item }, vectorArray.length);
|
|
177924
|
+
output.set(id, vectorArray);
|
|
177925
|
+
}
|
|
177926
|
+
} catch (error51) {
|
|
177927
|
+
const classified = classifyError(error51);
|
|
177928
|
+
this.logCallFailure(classified, "embed.batch");
|
|
177929
|
+
if (classified.code === "idempotency_conflict")
|
|
177930
|
+
throw classified;
|
|
177931
|
+
if (classified.permanent) {
|
|
177932
|
+
this.permanentFailure = true;
|
|
177933
|
+
this.initialized = false;
|
|
177934
|
+
break;
|
|
177935
|
+
}
|
|
177936
|
+
}
|
|
177937
|
+
}
|
|
177938
|
+
return output;
|
|
177939
|
+
}
|
|
177940
|
+
async dispose() {
|
|
177941
|
+
this.initialized = false;
|
|
177942
|
+
this.client = null;
|
|
177943
|
+
}
|
|
177944
|
+
isLoaded() {
|
|
177945
|
+
return this.initialized;
|
|
177946
|
+
}
|
|
177947
|
+
requestConstraints(extra) {
|
|
177948
|
+
const metadata = this.metadata;
|
|
177949
|
+
if (!metadata)
|
|
177950
|
+
return extra;
|
|
177951
|
+
return {
|
|
177952
|
+
...extra,
|
|
177953
|
+
model: metadata.model,
|
|
177954
|
+
required_fingerprint: metadata.fingerprint,
|
|
177955
|
+
required_epoch: metadata.table_epoch,
|
|
177956
|
+
allow_equivalent: false,
|
|
177957
|
+
accept_declared: false
|
|
177958
|
+
};
|
|
177959
|
+
}
|
|
177960
|
+
batchRequest(items, requestKey) {
|
|
177961
|
+
return this.requestConstraints({
|
|
177962
|
+
items: items.map((item) => ({
|
|
177963
|
+
id: item.id,
|
|
177964
|
+
text: item.text,
|
|
177965
|
+
content_sha256: item.contentSha256
|
|
177966
|
+
})),
|
|
177967
|
+
request_key: requestKey
|
|
177968
|
+
});
|
|
177969
|
+
}
|
|
177970
|
+
requestKey(items) {
|
|
177971
|
+
if (!this.metadata)
|
|
177972
|
+
throw new SynapseEmbeddingError("transport", "Synapse metadata is unavailable");
|
|
177973
|
+
return getSynapseBatchRequestKey({
|
|
177974
|
+
model: this.metadata.model,
|
|
177975
|
+
fingerprint: this.metadata.fingerprint,
|
|
177976
|
+
tableEpoch: this.metadata.table_epoch,
|
|
177977
|
+
items
|
|
177978
|
+
});
|
|
177979
|
+
}
|
|
177980
|
+
async pollBatch(jobId, requestKey, signal) {
|
|
177981
|
+
let cursor = null;
|
|
177982
|
+
const allItems = [];
|
|
177983
|
+
for (;; ) {
|
|
177984
|
+
if (signal?.aborted)
|
|
177985
|
+
return {};
|
|
177986
|
+
const body = await this.callWithRetry("embed.result", this.requestConstraints({
|
|
177987
|
+
job_id: jobId,
|
|
177988
|
+
cursor,
|
|
177989
|
+
request_key: requestKey
|
|
177990
|
+
}), this.options.batchTimeoutMs ?? SYNAPSE_DEFAULT_BATCH_TIMEOUT_MS, true, signal);
|
|
177991
|
+
const parsed = responseBody(body);
|
|
177992
|
+
const items = extractBatchItems(body);
|
|
177993
|
+
allItems.push(...items);
|
|
177994
|
+
const nextCursor = parsed.next_cursor ?? parsed.cursor;
|
|
177995
|
+
const done = parsed.done === true || parsed.complete === true || nextCursor === undefined || nextCursor === null;
|
|
177996
|
+
if (done)
|
|
177997
|
+
return { ...parsed, items: allItems };
|
|
177998
|
+
cursor = nextCursor;
|
|
177999
|
+
}
|
|
178000
|
+
}
|
|
178001
|
+
async callWithRetry(method, params, timeoutMs, retryEmbeddings, signal) {
|
|
178002
|
+
let attempt = 0;
|
|
178003
|
+
for (;; ) {
|
|
178004
|
+
if (signal?.aborted)
|
|
178005
|
+
throw new SynapseEmbeddingError("transport", "Synapse request aborted");
|
|
178006
|
+
try {
|
|
178007
|
+
if (!this.client)
|
|
178008
|
+
throw new SynapseEmbeddingError("transport", "Synapse client is unavailable");
|
|
178009
|
+
return await this.client.call(this.options.moduleId ?? "synapse", method, params, {
|
|
178010
|
+
timeoutMs,
|
|
178011
|
+
targetKind: "management_surface",
|
|
178012
|
+
identity: {
|
|
178013
|
+
project_root: this.options.projectRoot,
|
|
178014
|
+
harness: getHarness(),
|
|
178015
|
+
session: this.options.session
|
|
178016
|
+
}
|
|
178017
|
+
});
|
|
178018
|
+
} catch (error51) {
|
|
178019
|
+
const classified = classifyError(error51);
|
|
178020
|
+
if (classified.code === "idempotency_conflict")
|
|
178021
|
+
throw classified;
|
|
178022
|
+
const outcomeUnknown = error51 instanceof SubcCallError && error51.kind === "outcome_unknown";
|
|
178023
|
+
const retryable = !classified.permanent && (retryEmbeddings || !outcomeUnknown);
|
|
178024
|
+
if (!retryable || attempt >= 3)
|
|
178025
|
+
throw classified;
|
|
178026
|
+
const delay = classified.retryAfterMs ?? Math.min(2000, 100 * 2 ** attempt);
|
|
178027
|
+
attempt += 1;
|
|
178028
|
+
await wait(delay);
|
|
178029
|
+
}
|
|
178030
|
+
}
|
|
178031
|
+
}
|
|
178032
|
+
validateResponse(body, dims) {
|
|
178033
|
+
const metadata = this.metadata;
|
|
178034
|
+
if (!metadata) {
|
|
178035
|
+
throw new SynapseEmbeddingError("artifact_invalid", "Synapse lane metadata missing");
|
|
178036
|
+
}
|
|
178037
|
+
if (metadata.dims === undefined) {
|
|
178038
|
+
const envelopeDims = body.dims;
|
|
178039
|
+
if (typeof envelopeDims === "number" && envelopeDims !== dims) {
|
|
178040
|
+
throw new SynapseEmbeddingError("artifact_invalid", `Synapse envelope declares ${envelopeDims} dimensions but the vector has ${dims}`);
|
|
178041
|
+
}
|
|
178042
|
+
metadata.dims = dims;
|
|
178043
|
+
}
|
|
178044
|
+
if (dims !== metadata.dims) {
|
|
178045
|
+
throw new SynapseEmbeddingError("artifact_invalid", `Synapse returned ${dims} dimensions, expected ${metadata.dims}`);
|
|
178046
|
+
}
|
|
178047
|
+
const fingerprint = body.fingerprint ?? body.served_fingerprint;
|
|
178048
|
+
if (typeof fingerprint !== "string") {
|
|
178049
|
+
throw new SynapseEmbeddingError("artifact_invalid", "Synapse response omitted the served fingerprint");
|
|
178050
|
+
}
|
|
178051
|
+
if (fingerprint !== metadata.fingerprint) {
|
|
178052
|
+
throw new SynapseEmbeddingError("substitution_rejected", `Synapse fingerprint changed from ${metadata.fingerprint} to ${fingerprint}`);
|
|
178053
|
+
}
|
|
178054
|
+
const epoch = body.table_epoch ?? body.tableEpoch;
|
|
178055
|
+
if (typeof epoch !== "number") {
|
|
178056
|
+
throw new SynapseEmbeddingError("artifact_invalid", "Synapse response omitted the served table epoch");
|
|
178057
|
+
}
|
|
178058
|
+
if (epoch !== metadata.table_epoch) {
|
|
178059
|
+
throw new SynapseEmbeddingError("substitution_rejected", `Synapse table epoch changed from ${metadata.table_epoch} to ${epoch}`);
|
|
178060
|
+
}
|
|
178061
|
+
}
|
|
178062
|
+
logCallFailure(error51, operation) {
|
|
178063
|
+
const classified = classifyError(error51);
|
|
178064
|
+
if (classified.permanent)
|
|
178065
|
+
this.permanentFailure = true;
|
|
178066
|
+
const suffix = classified.retryAfterMs === undefined ? "" : ` retry_after_ms=${classified.retryAfterMs}`;
|
|
178067
|
+
log(`[magic-context] Synapse ${operation} failed: ${classified.code}${suffix}: ${classified.message}`);
|
|
178068
|
+
}
|
|
178069
|
+
}
|
|
178070
|
+
|
|
175087
178071
|
// ../plugin/src/features/magic-context/memory/normalize-hash.ts
|
|
175088
|
-
import { createHash as
|
|
178072
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
175089
178073
|
function normalizeMemoryContent(content) {
|
|
175090
178074
|
return content.toLowerCase().replace(/\s+/g, " ").trim();
|
|
175091
178075
|
}
|
|
175092
178076
|
function computeNormalizedHash(content) {
|
|
175093
178077
|
const normalized = normalizeMemoryContent(content);
|
|
175094
|
-
return
|
|
178078
|
+
return createHash9("md5").update(normalized).digest("hex");
|
|
175095
178079
|
}
|
|
175096
178080
|
|
|
175097
178081
|
// ../plugin/src/features/magic-context/memory/embedding-identity.ts
|
|
@@ -175102,6 +178086,15 @@ function getEmbeddingProviderIdentity(config2) {
|
|
|
175102
178086
|
if (config2.provider === "off") {
|
|
175103
178087
|
return "embedding-provider:off";
|
|
175104
178088
|
}
|
|
178089
|
+
if (config2.provider === "synapse") {
|
|
178090
|
+
const resolved = config2;
|
|
178091
|
+
if (!resolved.model || !resolved.synapse_fingerprint)
|
|
178092
|
+
return "synapse:v1:pending";
|
|
178093
|
+
return getSynapseLaneIdentity(resolved.model, resolved.synapse_fingerprint);
|
|
178094
|
+
}
|
|
178095
|
+
if (config2.provider !== "local" && config2.provider !== "openai-compatible") {
|
|
178096
|
+
throw new Error("Unknown embedding provider");
|
|
178097
|
+
}
|
|
175105
178098
|
const truncate = config2.provider === "openai-compatible" ? config2.truncate?.trim() : undefined;
|
|
175106
178099
|
const identityInput = config2.provider === "openai-compatible" ? {
|
|
175107
178100
|
provider: "openai-compatible",
|
|
@@ -175891,7 +178884,7 @@ function getMemoryEmbedCoverage(db, projectPath, modelId) {
|
|
|
175891
178884
|
}
|
|
175892
178885
|
|
|
175893
178886
|
// ../plugin/src/features/magic-context/project-embedding-registry.ts
|
|
175894
|
-
import { createHash as
|
|
178887
|
+
import { createHash as createHash10, randomUUID as randomUUID2 } from "node:crypto";
|
|
175895
178888
|
init_logger();
|
|
175896
178889
|
|
|
175897
178890
|
// ../plugin/src/features/magic-context/git-commits/storage-git-commit-embeddings.ts
|
|
@@ -176282,6 +179275,12 @@ var EMBED_SLICE_RETRY_BASE_MS = 250;
|
|
|
176282
179275
|
var EMBED_SLOW_FAILURE_NO_RETRY_MS = 1e4;
|
|
176283
179276
|
var MAX_CONSECUTIVE_FAILED_BATCHES = 3;
|
|
176284
179277
|
var projectRegistrations = new Map;
|
|
179278
|
+
var shadowRegistrations = new Map;
|
|
179279
|
+
var shadowQueue = [];
|
|
179280
|
+
var shadowWorker = null;
|
|
179281
|
+
var SHADOW_MAX_ITEMS_PER_TICK = 64;
|
|
179282
|
+
var SHADOW_MAX_BYTES_PER_TICK = 512 * 1024;
|
|
179283
|
+
var SHADOW_MAX_WALL_CLOCK_MS = 2000;
|
|
176285
179284
|
var loadUnembeddedMemoriesStatements = new WeakMap;
|
|
176286
179285
|
var upsertActiveIdentityStatements = new WeakMap;
|
|
176287
179286
|
var backfillActiveIdentityStatements = new Map;
|
|
@@ -176293,6 +179292,35 @@ function markProjectLoadUntrusted(projectIdentity) {
|
|
|
176293
179292
|
untrustedLoadProjects.add(projectIdentity);
|
|
176294
179293
|
}
|
|
176295
179294
|
var testProviderFactory = null;
|
|
179295
|
+
function synapseConfigFields(config2) {
|
|
179296
|
+
const raw = config2;
|
|
179297
|
+
return {
|
|
179298
|
+
...typeof raw.model === "string" ? { model: raw.model } : {},
|
|
179299
|
+
...typeof raw.synapse_fingerprint === "string" ? { fingerprint: raw.synapse_fingerprint } : {},
|
|
179300
|
+
...typeof raw.synapse_table_epoch === "number" ? { tableEpoch: raw.synapse_table_epoch } : {},
|
|
179301
|
+
...typeof raw.synapse_dims === "number" ? { dims: raw.synapse_dims } : {},
|
|
179302
|
+
...raw.synapse_provenance !== undefined ? { provenance: raw.synapse_provenance } : {}
|
|
179303
|
+
};
|
|
179304
|
+
}
|
|
179305
|
+
function persistPrimaryDescriptor(db, registration) {
|
|
179306
|
+
const descriptorTable = db.prepare("SELECT 1 AS present FROM sqlite_master WHERE type = 'table' AND name = 'embedding_registrations'").get();
|
|
179307
|
+
if (!descriptorTable)
|
|
179308
|
+
return;
|
|
179309
|
+
const fields = synapseConfigFields(registration.config);
|
|
179310
|
+
db.prepare(`INSERT INTO embedding_registrations
|
|
179311
|
+
(project_path, provider_identity, model_id, chunk_model_id, fingerprint, table_epoch, dims, provenance_json, generation, updated_at)
|
|
179312
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
179313
|
+
ON CONFLICT(project_path) DO UPDATE SET
|
|
179314
|
+
provider_identity = excluded.provider_identity,
|
|
179315
|
+
model_id = excluded.model_id,
|
|
179316
|
+
chunk_model_id = excluded.chunk_model_id,
|
|
179317
|
+
fingerprint = excluded.fingerprint,
|
|
179318
|
+
table_epoch = excluded.table_epoch,
|
|
179319
|
+
dims = excluded.dims,
|
|
179320
|
+
provenance_json = excluded.provenance_json,
|
|
179321
|
+
generation = excluded.generation,
|
|
179322
|
+
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());
|
|
179323
|
+
}
|
|
176296
179324
|
function resolveEmbeddingConfig(config2) {
|
|
176297
179325
|
if (!config2 || config2.provider === "local") {
|
|
176298
179326
|
return {
|
|
@@ -176321,9 +179349,26 @@ function resolveEmbeddingConfig(config2) {
|
|
|
176321
179349
|
} : {}
|
|
176322
179350
|
};
|
|
176323
179351
|
}
|
|
176324
|
-
|
|
179352
|
+
if (config2.provider === "off") {
|
|
179353
|
+
return { provider: "off" };
|
|
179354
|
+
}
|
|
179355
|
+
if (config2.provider === "synapse") {
|
|
179356
|
+
const synapse = config2;
|
|
179357
|
+
return {
|
|
179358
|
+
provider: "synapse",
|
|
179359
|
+
model: synapse.model?.trim() || "gte-modernbert-base-f16",
|
|
179360
|
+
max_input_tokens: SYNAPSE_MAX_INPUT_TOKENS,
|
|
179361
|
+
...synapse.synapse_connection_file ? { synapse_connection_file: synapse.synapse_connection_file } : {},
|
|
179362
|
+
...synapse.synapse_fingerprint ? { synapse_fingerprint: synapse.synapse_fingerprint } : {},
|
|
179363
|
+
...typeof synapse.synapse_table_epoch === "number" ? { synapse_table_epoch: synapse.synapse_table_epoch } : {},
|
|
179364
|
+
...typeof synapse.synapse_dims === "number" ? { synapse_dims: synapse.synapse_dims } : {},
|
|
179365
|
+
...typeof synapse.synapse_recommended_batch === "number" ? { synapse_recommended_batch: synapse.synapse_recommended_batch } : {},
|
|
179366
|
+
...synapse.synapse_provenance !== undefined ? { synapse_provenance: synapse.synapse_provenance } : {}
|
|
179367
|
+
};
|
|
179368
|
+
}
|
|
179369
|
+
throw new Error("Unknown embedding provider");
|
|
176325
179370
|
}
|
|
176326
|
-
function createProvider(config2) {
|
|
179371
|
+
function createProvider(config2, context) {
|
|
176327
179372
|
if (testProviderFactory) {
|
|
176328
179373
|
return testProviderFactory(config2);
|
|
176329
179374
|
}
|
|
@@ -176341,7 +179386,24 @@ function createProvider(config2) {
|
|
|
176341
179386
|
maxInputTokens: config2.max_input_tokens
|
|
176342
179387
|
});
|
|
176343
179388
|
}
|
|
176344
|
-
|
|
179389
|
+
if (config2.provider === "local") {
|
|
179390
|
+
return new LocalEmbeddingProvider(config2.model, config2.max_input_tokens);
|
|
179391
|
+
}
|
|
179392
|
+
if (config2.provider === "synapse") {
|
|
179393
|
+
const synapse = config2;
|
|
179394
|
+
return new SynapseEmbeddingProvider({
|
|
179395
|
+
connectionFile: synapse.synapse_connection_file ?? "",
|
|
179396
|
+
projectRoot: context?.projectRoot ?? "",
|
|
179397
|
+
session: context?.session ?? "embedding",
|
|
179398
|
+
model: synapse.model,
|
|
179399
|
+
fingerprint: synapse.synapse_fingerprint,
|
|
179400
|
+
tableEpoch: synapse.synapse_table_epoch,
|
|
179401
|
+
dims: synapse.synapse_dims,
|
|
179402
|
+
recommendedBatch: synapse.synapse_recommended_batch,
|
|
179403
|
+
provenance: synapse.synapse_provenance
|
|
179404
|
+
});
|
|
179405
|
+
}
|
|
179406
|
+
throw new Error("Unknown embedding provider");
|
|
176345
179407
|
}
|
|
176346
179408
|
function stableStringify2(value) {
|
|
176347
179409
|
if (Array.isArray(value)) {
|
|
@@ -176354,7 +179416,10 @@ function stableStringify2(value) {
|
|
|
176354
179416
|
return JSON.stringify(value);
|
|
176355
179417
|
}
|
|
176356
179418
|
function sha256Prefix(value, length = 16) {
|
|
176357
|
-
return
|
|
179419
|
+
return createHash10("sha256").update(value).digest("hex").slice(0, length);
|
|
179420
|
+
}
|
|
179421
|
+
function contentSha256(value) {
|
|
179422
|
+
return createHash10("sha256").update(value).digest("hex");
|
|
176358
179423
|
}
|
|
176359
179424
|
function getRuntimeFingerprint(config2) {
|
|
176360
179425
|
if (config2.provider === "off") {
|
|
@@ -176381,6 +179446,7 @@ function snapshotFor(registration) {
|
|
|
176381
179446
|
const providerIsOn = registration.providerIdentity !== OFF_PROVIDER_IDENTITY;
|
|
176382
179447
|
const enabled = !registration.observationMode && providerIsOn && registration.features.memoryEnabled;
|
|
176383
179448
|
const gitCommitEnabled = !registration.observationMode && providerIsOn && registration.features.gitCommitEnabled;
|
|
179449
|
+
const configuredModel = "model" in registration.config && typeof registration.config.model === "string" ? registration.config.model.trim() : "";
|
|
176384
179450
|
return {
|
|
176385
179451
|
projectIdentity: registration.projectIdentity,
|
|
176386
179452
|
sourceDirectory: registration.sourceDirectory,
|
|
@@ -176392,7 +179458,7 @@ function snapshotFor(registration) {
|
|
|
176392
179458
|
gitCommitEnabled,
|
|
176393
179459
|
modelId: registration.observationMode || !providerIsOn ? "off" : registration.modelId,
|
|
176394
179460
|
chunkModelId: registration.observationMode || !providerIsOn ? "off" : registration.chunkModelId,
|
|
176395
|
-
model: registration.observationMode || !providerIsOn ? "off" :
|
|
179461
|
+
model: registration.observationMode || !providerIsOn ? "off" : configuredModel ? configuredModel : registration.modelId,
|
|
176396
179462
|
provider: registration.observationMode || !providerIsOn ? "off" : registration.config.provider ?? "local"
|
|
176397
179463
|
};
|
|
176398
179464
|
}
|
|
@@ -176499,9 +179565,9 @@ function getDeleteActiveIdentityStatement(db) {
|
|
|
176499
179565
|
}
|
|
176500
179566
|
return stmt;
|
|
176501
179567
|
}
|
|
176502
|
-
function staleModelsForScope(db, projectIdentity, scope, currentModelId, cutoff) {
|
|
179568
|
+
function staleModelsForScope(db, projectIdentity, scope, currentModelId, cutoff, protectedModelIds = new Set([currentModelId])) {
|
|
176503
179569
|
const rows = getStaleIdentityStatement(db, scope).all(projectIdentity, scope, currentModelId, cutoff);
|
|
176504
|
-
return rows.map((row) => row.modelId).filter((modelId) => typeof modelId === "string");
|
|
179570
|
+
return rows.map((row) => row.modelId).filter((modelId) => typeof modelId === "string" && !protectedModelIds.has(modelId));
|
|
176505
179571
|
}
|
|
176506
179572
|
function deleteStaleEmbeddingBatch(db, scope, projectIdentity, modelId, limit) {
|
|
176507
179573
|
if (scope === "memory") {
|
|
@@ -176566,6 +179632,12 @@ function sweepStaleEmbeddingIdentitiesForProject(db, projectIdentity, now = Date
|
|
|
176566
179632
|
return result;
|
|
176567
179633
|
const cutoff = now - EMBEDDING_IDENTITY_GC_GRACE_MS;
|
|
176568
179634
|
const deleteTracking = getDeleteActiveIdentityStatement(db);
|
|
179635
|
+
const shadow = shadowRegistrations.get(projectIdentity);
|
|
179636
|
+
const protectedModels = {
|
|
179637
|
+
memory: new Set([snapshot.modelId, ...shadow ? [shadow.modelId] : []]),
|
|
179638
|
+
commit: new Set([snapshot.modelId, ...shadow ? [shadow.modelId] : []]),
|
|
179639
|
+
chunk: new Set([snapshot.chunkModelId, ...shadow ? [shadow.chunkModelId] : []])
|
|
179640
|
+
};
|
|
176569
179641
|
const scopes = [
|
|
176570
179642
|
{
|
|
176571
179643
|
scope: "memory",
|
|
@@ -176589,7 +179661,7 @@ function sweepStaleEmbeddingIdentitiesForProject(db, projectIdentity, now = Date
|
|
|
176589
179661
|
for (const { scope, enabled, currentModelId } of scopes) {
|
|
176590
179662
|
if (!enabled || remainingBudget === 0)
|
|
176591
179663
|
continue;
|
|
176592
|
-
for (const modelId of staleModelsForScope(db, projectIdentity, scope, currentModelId, cutoff)) {
|
|
179664
|
+
for (const modelId of staleModelsForScope(db, projectIdentity, scope, currentModelId, cutoff, protectedModels[scope])) {
|
|
176593
179665
|
if (remainingBudget === 0)
|
|
176594
179666
|
break;
|
|
176595
179667
|
const deleted = deleteStaleEmbeddingBatch(db, scope, projectIdentity, modelId, remainingBudget);
|
|
@@ -176643,11 +179715,206 @@ function registerProjectEmbedding(db, projectIdentity, config2, features, source
|
|
|
176643
179715
|
observationMode: false
|
|
176644
179716
|
};
|
|
176645
179717
|
projectRegistrations.set(projectIdentity, registration);
|
|
179718
|
+
persistPrimaryDescriptor(db, registration);
|
|
176646
179719
|
if (!canReuseProvider) {
|
|
176647
179720
|
disposeProvider(prior?.provider ?? null);
|
|
176648
179721
|
}
|
|
176649
179722
|
return snapshotFor(registration);
|
|
176650
179723
|
}
|
|
179724
|
+
function startShadowWorker() {
|
|
179725
|
+
if (shadowWorker)
|
|
179726
|
+
return;
|
|
179727
|
+
shadowWorker = runShadowWorker().finally(() => {
|
|
179728
|
+
shadowWorker = null;
|
|
179729
|
+
if (shadowQueue.length > 0)
|
|
179730
|
+
startShadowWorker();
|
|
179731
|
+
});
|
|
179732
|
+
}
|
|
179733
|
+
function getShadowEmbeddingMeasurementCohort(projectIdentity) {
|
|
179734
|
+
const registration = shadowRegistrations.get(projectIdentity);
|
|
179735
|
+
if (!registration)
|
|
179736
|
+
return null;
|
|
179737
|
+
const fields = synapseConfigFields(registration.config);
|
|
179738
|
+
return {
|
|
179739
|
+
modelId: registration.modelId,
|
|
179740
|
+
chunkModelId: registration.chunkModelId,
|
|
179741
|
+
fingerprint: fields.fingerprint ?? "",
|
|
179742
|
+
epoch: fields.tableEpoch ?? 0,
|
|
179743
|
+
dims: fields.dims ?? 0
|
|
179744
|
+
};
|
|
179745
|
+
}
|
|
179746
|
+
function getPrimaryEmbeddingMeasurementCohort(projectIdentity) {
|
|
179747
|
+
const registration = projectRegistrations.get(projectIdentity);
|
|
179748
|
+
if (!registration)
|
|
179749
|
+
return null;
|
|
179750
|
+
const fields = synapseConfigFields(registration.config);
|
|
179751
|
+
return {
|
|
179752
|
+
modelId: registration.modelId,
|
|
179753
|
+
chunkModelId: registration.chunkModelId,
|
|
179754
|
+
fingerprint: fields.fingerprint ?? "",
|
|
179755
|
+
epoch: fields.tableEpoch ?? 0,
|
|
179756
|
+
dims: fields.dims ?? 0
|
|
179757
|
+
};
|
|
179758
|
+
}
|
|
179759
|
+
async function embedShadowTextForProject(projectIdentity, text, signal) {
|
|
179760
|
+
const registration = shadowRegistrations.get(projectIdentity);
|
|
179761
|
+
if (!registration)
|
|
179762
|
+
return null;
|
|
179763
|
+
try {
|
|
179764
|
+
return await registration.provider.embed(text, signal, "query");
|
|
179765
|
+
} catch (error51) {
|
|
179766
|
+
log("[magic-context] Synapse shadow query failed:", error51);
|
|
179767
|
+
return null;
|
|
179768
|
+
}
|
|
179769
|
+
}
|
|
179770
|
+
function enqueueShadowEmbeddingItems(projectIdentity, scope, ids) {
|
|
179771
|
+
if (ids.length === 0 || !shadowRegistrations.has(projectIdentity))
|
|
179772
|
+
return;
|
|
179773
|
+
shadowQueue.push({ projectIdentity, scope, ids: [...ids] });
|
|
179774
|
+
startShadowWorker();
|
|
179775
|
+
}
|
|
179776
|
+
async function embedShadowItems(registration, items, db, scope) {
|
|
179777
|
+
const raw = registration.config;
|
|
179778
|
+
const fingerprint = typeof raw.synapse_fingerprint === "string" ? raw.synapse_fingerprint : "";
|
|
179779
|
+
const tableEpoch = typeof raw.synapse_table_epoch === "number" ? raw.synapse_table_epoch : 0;
|
|
179780
|
+
const requestKey = getSynapseBatchRequestKey({
|
|
179781
|
+
model: typeof raw.model === "string" ? raw.model : SYNAPSE_DEFAULT_MODEL,
|
|
179782
|
+
fingerprint,
|
|
179783
|
+
tableEpoch,
|
|
179784
|
+
items
|
|
179785
|
+
});
|
|
179786
|
+
beginSynapseBatchLedger(db, {
|
|
179787
|
+
sessionId: `shadow:${registration.projectIdentity}`,
|
|
179788
|
+
projectPath: registration.projectIdentity,
|
|
179789
|
+
scope,
|
|
179790
|
+
manifest: items.map(({ id, contentSha256: contentSha2562 }) => ({ id, contentSha256: contentSha2562 })),
|
|
179791
|
+
requestKey
|
|
179792
|
+
});
|
|
179793
|
+
try {
|
|
179794
|
+
if (registration.provider.embedItems) {
|
|
179795
|
+
const vectors2 = await registration.provider.embedItems(items);
|
|
179796
|
+
finishSynapseBatchLedger(db, `shadow:${registration.projectIdentity}`, requestKey, vectors2.size === items.length ? "complete" : "partial");
|
|
179797
|
+
return vectors2;
|
|
179798
|
+
}
|
|
179799
|
+
const positional = await registration.provider.embedBatch(items.map((item) => item.text));
|
|
179800
|
+
const vectors = new Map(items.flatMap((item, index) => {
|
|
179801
|
+
const vector = positional[index];
|
|
179802
|
+
return vector ? [[item.id, vector]] : [];
|
|
179803
|
+
}));
|
|
179804
|
+
finishSynapseBatchLedger(db, `shadow:${registration.projectIdentity}`, requestKey, vectors.size === items.length ? "complete" : "partial");
|
|
179805
|
+
return vectors;
|
|
179806
|
+
} catch (error51) {
|
|
179807
|
+
finishSynapseBatchLedger(db, `shadow:${registration.projectIdentity}`, requestKey, "failed");
|
|
179808
|
+
throw error51;
|
|
179809
|
+
}
|
|
179810
|
+
}
|
|
179811
|
+
async function processShadowQueueItem(item) {
|
|
179812
|
+
const registration = shadowRegistrations.get(item.projectIdentity);
|
|
179813
|
+
if (!registration)
|
|
179814
|
+
return;
|
|
179815
|
+
const boundedIds = item.ids.slice(0, SHADOW_MAX_ITEMS_PER_TICK);
|
|
179816
|
+
if (item.scope === "memory") {
|
|
179817
|
+
const db2 = dbForShadowQueue.get(item.projectIdentity);
|
|
179818
|
+
if (!db2)
|
|
179819
|
+
return;
|
|
179820
|
+
const placeholders4 = boundedIds.map(() => "?").join(",");
|
|
179821
|
+
const rows = db2.prepare(`SELECT id, content, normalized_hash FROM memories
|
|
179822
|
+
WHERE project_path = ? AND id IN (${placeholders4}) AND status = 'active'`).all(item.projectIdentity, ...boundedIds.map((id) => Number(id)));
|
|
179823
|
+
const vectors2 = await embedShadowItems(registration, rows.map((row) => ({
|
|
179824
|
+
id: `memory:${row.id}`,
|
|
179825
|
+
text: row.content,
|
|
179826
|
+
contentSha256: contentSha256(row.content)
|
|
179827
|
+
})), db2, "memory");
|
|
179828
|
+
db2.transaction(() => {
|
|
179829
|
+
for (const row of rows) {
|
|
179830
|
+
const vector = vectors2.get(`memory:${row.id}`);
|
|
179831
|
+
if (vector)
|
|
179832
|
+
saveEmbeddingIfHashMatches(db2, row.id, vector, registration.modelId, row.normalized_hash);
|
|
179833
|
+
}
|
|
179834
|
+
})();
|
|
179835
|
+
return;
|
|
179836
|
+
}
|
|
179837
|
+
if (item.scope === "commit") {
|
|
179838
|
+
const db2 = dbForShadowQueue.get(item.projectIdentity);
|
|
179839
|
+
if (!db2)
|
|
179840
|
+
return;
|
|
179841
|
+
const placeholders4 = boundedIds.map(() => "?").join(",");
|
|
179842
|
+
const rows = db2.prepare(`SELECT sha, message FROM git_commits WHERE project_path = ? AND sha IN (${placeholders4})`).all(item.projectIdentity, ...boundedIds);
|
|
179843
|
+
const vectors2 = await embedShadowItems(registration, rows.map((row) => ({
|
|
179844
|
+
id: `commit:${row.sha}`,
|
|
179845
|
+
text: row.message,
|
|
179846
|
+
contentSha256: contentSha256(row.message)
|
|
179847
|
+
})), db2, "commit");
|
|
179848
|
+
db2.transaction(() => {
|
|
179849
|
+
for (const row of rows) {
|
|
179850
|
+
const vector = vectors2.get(`commit:${row.sha}`);
|
|
179851
|
+
if (vector)
|
|
179852
|
+
saveCommitEmbedding(db2, row.sha, vector, registration.modelId);
|
|
179853
|
+
}
|
|
179854
|
+
})();
|
|
179855
|
+
return;
|
|
179856
|
+
}
|
|
179857
|
+
const db = dbForShadowQueue.get(item.projectIdentity);
|
|
179858
|
+
if (!db)
|
|
179859
|
+
return;
|
|
179860
|
+
const placeholders3 = boundedIds.map(() => "?").join(",");
|
|
179861
|
+
const candidates = db.prepare(`SELECT id, session_id, start_message, end_message
|
|
179862
|
+
FROM compartments WHERE id IN (${placeholders3})`).all(...boundedIds.map((id) => Number(id)));
|
|
179863
|
+
const prepared = candidates.flatMap((candidate) => {
|
|
179864
|
+
const text = buildCanonicalChunkTextFromFts(db, candidate.session_id, candidate.start_message, candidate.end_message) || buildCompartmentSummaryFallbackText(db, candidate.id);
|
|
179865
|
+
if (!text)
|
|
179866
|
+
return [];
|
|
179867
|
+
const windows = chunkCanonicalText(text, candidate.start_message, candidate.end_message, SYNAPSE_MAX_INPUT_TOKENS);
|
|
179868
|
+
return windows.length > 0 ? [{ candidate, windows }] : [];
|
|
179869
|
+
});
|
|
179870
|
+
const items = prepared.flatMap((item2) => item2.windows.map((window) => ({
|
|
179871
|
+
id: `chunk:${item2.candidate.id}:${window.windowIndex}`,
|
|
179872
|
+
text: window.text,
|
|
179873
|
+
contentSha256: contentSha256(window.text)
|
|
179874
|
+
})));
|
|
179875
|
+
const vectors = await embedShadowItems(registration, items, db, "chunk");
|
|
179876
|
+
for (const item2 of prepared) {
|
|
179877
|
+
const rows = item2.windows.flatMap((window) => {
|
|
179878
|
+
const vector = vectors.get(`chunk:${item2.candidate.id}:${window.windowIndex}`);
|
|
179879
|
+
return vector ? [
|
|
179880
|
+
{
|
|
179881
|
+
compartmentId: item2.candidate.id,
|
|
179882
|
+
sessionId: item2.candidate.session_id,
|
|
179883
|
+
projectPath: registration.projectIdentity,
|
|
179884
|
+
window,
|
|
179885
|
+
modelId: registration.chunkModelId,
|
|
179886
|
+
vector
|
|
179887
|
+
}
|
|
179888
|
+
] : [];
|
|
179889
|
+
});
|
|
179890
|
+
if (rows.length === item2.windows.length)
|
|
179891
|
+
replaceCompartmentChunkEmbeddings(db, rows);
|
|
179892
|
+
}
|
|
179893
|
+
}
|
|
179894
|
+
var dbForShadowQueue = new Map;
|
|
179895
|
+
async function runShadowWorker() {
|
|
179896
|
+
const startedAt = Date.now();
|
|
179897
|
+
let processed = 0;
|
|
179898
|
+
let processedBytes = 0;
|
|
179899
|
+
while (shadowQueue.length > 0 && processed < SHADOW_MAX_ITEMS_PER_TICK && Date.now() - startedAt < SHADOW_MAX_WALL_CLOCK_MS) {
|
|
179900
|
+
const item = shadowQueue.shift();
|
|
179901
|
+
if (!item)
|
|
179902
|
+
break;
|
|
179903
|
+
const itemBytes = item.ids.reduce((total, id) => total + id.length, 0);
|
|
179904
|
+
if (processed > 0 && processedBytes + itemBytes > SHADOW_MAX_BYTES_PER_TICK) {
|
|
179905
|
+
shadowQueue.unshift(item);
|
|
179906
|
+
break;
|
|
179907
|
+
}
|
|
179908
|
+
try {
|
|
179909
|
+
await processShadowQueueItem(item);
|
|
179910
|
+
} catch (error51) {
|
|
179911
|
+
log("[magic-context] Synapse shadow write failed:", error51);
|
|
179912
|
+
}
|
|
179913
|
+
processed += item.ids.length;
|
|
179914
|
+
processedBytes += itemBytes;
|
|
179915
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
179916
|
+
}
|
|
179917
|
+
}
|
|
176651
179918
|
function registerProjectInObservationMode(db, projectIdentity, sourceDirectory, failedConfig, failureSummary) {
|
|
176652
179919
|
const prior = projectRegistrations.get(projectIdentity);
|
|
176653
179920
|
const runtimeFingerprint = `observation:${sha256Prefix(failureSummary)}`;
|
|
@@ -176689,7 +179956,10 @@ function getOrCreateProjectProvider(registration) {
|
|
|
176689
179956
|
if (registration.provider) {
|
|
176690
179957
|
return registration.provider;
|
|
176691
179958
|
}
|
|
176692
|
-
const provider = createProvider(registration.config
|
|
179959
|
+
const provider = createProvider(registration.config, {
|
|
179960
|
+
projectRoot: registration.sourceDirectory,
|
|
179961
|
+
session: `project:${registration.projectIdentity}`
|
|
179962
|
+
});
|
|
176693
179963
|
registration.provider = provider;
|
|
176694
179964
|
return provider;
|
|
176695
179965
|
}
|
|
@@ -176709,7 +179979,7 @@ async function embedTextForProject(projectIdentity, text, signal, purpose = "pas
|
|
|
176709
179979
|
if (!current || current.generation !== generation || current.runtimeFingerprint !== registration.runtimeFingerprint) {
|
|
176710
179980
|
return null;
|
|
176711
179981
|
}
|
|
176712
|
-
return { vector, modelId, generation };
|
|
179982
|
+
return { vector, modelId, chunkModelId: registration.chunkModelId, generation };
|
|
176713
179983
|
}
|
|
176714
179984
|
async function embedBatchForProject(projectIdentity, texts, signal, purpose = "passage") {
|
|
176715
179985
|
if (texts.length === 0) {
|
|
@@ -176734,31 +180004,81 @@ async function embedBatchForProject(projectIdentity, texts, signal, purpose = "p
|
|
|
176734
180004
|
}
|
|
176735
180005
|
return { vectors, modelId, generation };
|
|
176736
180006
|
}
|
|
176737
|
-
async function
|
|
176738
|
-
|
|
176739
|
-
|
|
180007
|
+
async function embedItemsForProject(projectIdentity, items, signal, db, sessionId = projectIdentity) {
|
|
180008
|
+
const registration = projectRegistrations.get(projectIdentity);
|
|
180009
|
+
if (!registration || registration.observationMode || items.length === 0)
|
|
180010
|
+
return null;
|
|
180011
|
+
const generation = registration.generation;
|
|
180012
|
+
const modelId = registration.modelId;
|
|
180013
|
+
const runtimeFingerprint = registration.runtimeFingerprint;
|
|
180014
|
+
const provider = getOrCreateProjectProvider(registration);
|
|
180015
|
+
if (!provider)
|
|
180016
|
+
return null;
|
|
180017
|
+
const rawConfig = registration.config;
|
|
180018
|
+
const fingerprint = typeof rawConfig.synapse_fingerprint === "string" ? rawConfig.synapse_fingerprint : "";
|
|
180019
|
+
const tableEpoch = typeof rawConfig.synapse_table_epoch === "number" ? rawConfig.synapse_table_epoch : undefined;
|
|
180020
|
+
const isSynapse = registration.providerIdentity.startsWith("synapse:v1:") && fingerprint && tableEpoch !== undefined;
|
|
180021
|
+
const ledgerKey = isSynapse ? getSynapseBatchRequestKey({
|
|
180022
|
+
model: typeof rawConfig.model === "string" ? rawConfig.model : registration.modelId,
|
|
180023
|
+
fingerprint,
|
|
180024
|
+
tableEpoch,
|
|
180025
|
+
items
|
|
180026
|
+
}) : null;
|
|
180027
|
+
if (db && ledgerKey) {
|
|
180028
|
+
beginSynapseBatchLedger(db, {
|
|
180029
|
+
sessionId,
|
|
180030
|
+
projectPath: projectIdentity,
|
|
180031
|
+
scope: items[0].id.split(":", 1)[0],
|
|
180032
|
+
manifest: items.map(({ id, contentSha256: contentSha2562 }) => ({ id, contentSha256: contentSha2562 })),
|
|
180033
|
+
requestKey: ledgerKey
|
|
180034
|
+
});
|
|
180035
|
+
}
|
|
180036
|
+
let vectors;
|
|
180037
|
+
try {
|
|
180038
|
+
if (provider.embedItems) {
|
|
180039
|
+
vectors = await provider.embedItems(items, signal);
|
|
180040
|
+
} else {
|
|
180041
|
+
const positional = await provider.embedBatch(items.map((item) => item.text), signal, "passage");
|
|
180042
|
+
vectors = new Map(items.flatMap((item, index) => {
|
|
180043
|
+
const vector = positional[index];
|
|
180044
|
+
return vector ? [[item.id, vector]] : [];
|
|
180045
|
+
}));
|
|
180046
|
+
}
|
|
180047
|
+
} catch (error51) {
|
|
180048
|
+
if (db && ledgerKey)
|
|
180049
|
+
finishSynapseBatchLedger(db, sessionId, ledgerKey, "failed");
|
|
180050
|
+
throw error51;
|
|
176740
180051
|
}
|
|
176741
|
-
|
|
180052
|
+
if (db && ledgerKey) {
|
|
180053
|
+
finishSynapseBatchLedger(db, sessionId, ledgerKey, vectors.size === items.length ? "complete" : "partial");
|
|
180054
|
+
}
|
|
180055
|
+
const current = projectRegistrations.get(projectIdentity);
|
|
180056
|
+
if (!current || current.generation !== generation || current.runtimeFingerprint !== runtimeFingerprint) {
|
|
180057
|
+
return null;
|
|
180058
|
+
}
|
|
180059
|
+
return { vectors, modelId, generation };
|
|
180060
|
+
}
|
|
180061
|
+
async function embedItemsWindowBounded(projectIdentity, items, signal, db) {
|
|
180062
|
+
if (items.length <= MAX_WINDOWS_PER_EMBED_CALL) {
|
|
180063
|
+
return embedItemsForProject(projectIdentity, items, signal, db, projectIdentity);
|
|
180064
|
+
}
|
|
180065
|
+
const vectors = new Map;
|
|
176742
180066
|
let modelId = null;
|
|
176743
180067
|
let generation = null;
|
|
176744
|
-
for (let start = 0;start <
|
|
176745
|
-
|
|
176746
|
-
return null;
|
|
176747
|
-
const sub = texts.slice(start, start + MAX_WINDOWS_PER_EMBED_CALL);
|
|
176748
|
-
const result = await embedBatchForProject(projectIdentity, sub, signal);
|
|
180068
|
+
for (let start = 0;start < items.length; start += MAX_WINDOWS_PER_EMBED_CALL) {
|
|
180069
|
+
const result = await embedItemsForProject(projectIdentity, items.slice(start, start + MAX_WINDOWS_PER_EMBED_CALL), signal, db, projectIdentity);
|
|
176749
180070
|
if (!result)
|
|
176750
180071
|
return null;
|
|
176751
180072
|
if (modelId === null) {
|
|
176752
180073
|
modelId = result.modelId;
|
|
176753
180074
|
generation = result.generation;
|
|
176754
|
-
} else if (
|
|
180075
|
+
} else if (modelId !== result.modelId || generation !== result.generation) {
|
|
176755
180076
|
return null;
|
|
176756
180077
|
}
|
|
176757
|
-
|
|
180078
|
+
for (const [id, vector] of result.vectors)
|
|
180079
|
+
vectors.set(id, vector);
|
|
176758
180080
|
}
|
|
176759
|
-
|
|
176760
|
-
return null;
|
|
176761
|
-
return { vectors, modelId, generation };
|
|
180081
|
+
return modelId === null || generation === null ? null : { vectors, modelId, generation };
|
|
176762
180082
|
}
|
|
176763
180083
|
function isUnembeddedMemoryRow(row) {
|
|
176764
180084
|
if (row === null || typeof row !== "object")
|
|
@@ -176783,13 +180103,17 @@ async function embedUnembeddedMemoriesForProject(db, projectIdentity, batchSize
|
|
|
176783
180103
|
if (memories.length === 0)
|
|
176784
180104
|
return 0;
|
|
176785
180105
|
try {
|
|
176786
|
-
const result = await
|
|
180106
|
+
const result = await embedItemsForProject(projectIdentity, memories.map((memory) => ({
|
|
180107
|
+
id: `memory:${memory.id}`,
|
|
180108
|
+
text: memory.content,
|
|
180109
|
+
contentSha256: contentSha256(memory.content)
|
|
180110
|
+
})), undefined, db, projectIdentity);
|
|
176787
180111
|
if (!result)
|
|
176788
180112
|
return 0;
|
|
176789
180113
|
let embeddedCount = 0;
|
|
176790
180114
|
db.transaction(() => {
|
|
176791
|
-
for (const
|
|
176792
|
-
const embedding = result.vectors
|
|
180115
|
+
for (const memory of memories) {
|
|
180116
|
+
const embedding = result.vectors.get(`memory:${memory.id}`);
|
|
176793
180117
|
if (!embedding)
|
|
176794
180118
|
continue;
|
|
176795
180119
|
if (saveEmbeddingIfHashMatches(db, memory.id, embedding, result.modelId, memory.normalized_hash)) {
|
|
@@ -176797,6 +180121,7 @@ async function embedUnembeddedMemoriesForProject(db, projectIdentity, batchSize
|
|
|
176797
180121
|
}
|
|
176798
180122
|
}
|
|
176799
180123
|
})();
|
|
180124
|
+
enqueueShadowEmbeddingItems(projectIdentity, "memory", memories.filter((memory) => result.vectors.has(`memory:${memory.id}`)).map((memory) => String(memory.id)));
|
|
176800
180125
|
return embeddedCount;
|
|
176801
180126
|
} catch (error51) {
|
|
176802
180127
|
log("[magic-context] failed to proactively embed missing memories:", error51);
|
|
@@ -176810,19 +180135,24 @@ async function embedCommitBatch(db, projectIdentity, batchSize) {
|
|
|
176810
180135
|
const commits = loadUnembeddedCommits(db, projectIdentity, snapshot.modelId, Math.max(1, Math.floor(batchSize)));
|
|
176811
180136
|
if (commits.length === 0)
|
|
176812
180137
|
return 0;
|
|
176813
|
-
const result = await
|
|
180138
|
+
const result = await embedItemsForProject(projectIdentity, commits.map((commit) => ({
|
|
180139
|
+
id: `commit:${commit.sha}`,
|
|
180140
|
+
text: commit.message,
|
|
180141
|
+
contentSha256: contentSha256(commit.message)
|
|
180142
|
+
})), undefined, db, projectIdentity);
|
|
176814
180143
|
if (!result)
|
|
176815
180144
|
return 0;
|
|
176816
180145
|
let embeddedCount = 0;
|
|
176817
180146
|
db.transaction(() => {
|
|
176818
|
-
for (const
|
|
176819
|
-
const embedding = result.vectors
|
|
180147
|
+
for (const commit of commits) {
|
|
180148
|
+
const embedding = result.vectors.get(`commit:${commit.sha}`);
|
|
176820
180149
|
if (!embedding)
|
|
176821
180150
|
continue;
|
|
176822
180151
|
saveCommitEmbedding(db, commit.sha, embedding, result.modelId);
|
|
176823
180152
|
embeddedCount += 1;
|
|
176824
180153
|
}
|
|
176825
180154
|
})();
|
|
180155
|
+
enqueueShadowEmbeddingItems(projectIdentity, "commit", commits.filter((commit) => result.vectors.has(`commit:${commit.sha}`)).map((commit) => commit.sha));
|
|
176826
180156
|
return embeddedCount;
|
|
176827
180157
|
}
|
|
176828
180158
|
async function drainCommitBacklogForProject(db, projectIdentity, deadline) {
|
|
@@ -176895,9 +180225,11 @@ async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates
|
|
|
176895
180225
|
windowCount += item.windows.length;
|
|
176896
180226
|
i += 1;
|
|
176897
180227
|
} while (i < prepared.length && windowCount + prepared[i].windows.length <= MAX_WINDOWS_PER_EMBED_CALL);
|
|
176898
|
-
const
|
|
176899
|
-
|
|
176900
|
-
|
|
180228
|
+
const items = slice.flatMap((item) => item.windows.map((window) => ({
|
|
180229
|
+
id: `chunk:${item.candidate.id}:${window.windowIndex}`,
|
|
180230
|
+
text: window.text,
|
|
180231
|
+
contentSha256: contentSha256(window.text)
|
|
180232
|
+
})));
|
|
176901
180233
|
const persistedIds = new Set;
|
|
176902
180234
|
for (let attempt = 0;attempt < EMBED_SLICE_RETRY_ATTEMPTS; attempt++) {
|
|
176903
180235
|
if (signal?.aborted)
|
|
@@ -176905,19 +180237,17 @@ async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates
|
|
|
176905
180237
|
let result = null;
|
|
176906
180238
|
const attemptStart = Date.now();
|
|
176907
180239
|
try {
|
|
176908
|
-
result = await
|
|
180240
|
+
result = await embedItemsWindowBounded(projectIdentity, items, signal, db);
|
|
176909
180241
|
} catch (error51) {
|
|
176910
180242
|
log("[magic-context] failed to proactively embed compartment chunks:", error51);
|
|
176911
180243
|
}
|
|
176912
180244
|
if (signal?.aborted)
|
|
176913
180245
|
break;
|
|
176914
180246
|
if (result) {
|
|
176915
|
-
let offset = 0;
|
|
176916
180247
|
for (const item of slice) {
|
|
176917
|
-
const vectors = result.vectors.slice(offset, offset + item.windows.length);
|
|
176918
|
-
offset += item.windows.length;
|
|
176919
180248
|
if (persistedIds.has(item.candidate.id))
|
|
176920
180249
|
continue;
|
|
180250
|
+
const vectors = item.windows.map((window) => result.vectors.get(`chunk:${item.candidate.id}:${window.windowIndex}`));
|
|
176921
180251
|
if (vectors.length !== item.windows.length || vectors.some((v) => !v)) {
|
|
176922
180252
|
continue;
|
|
176923
180253
|
}
|
|
@@ -176931,6 +180261,9 @@ async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates
|
|
|
176931
180261
|
}));
|
|
176932
180262
|
replaceCompartmentChunkEmbeddings(db, rows);
|
|
176933
180263
|
persistedIds.add(item.candidate.id);
|
|
180264
|
+
enqueueShadowEmbeddingItems(projectIdentity, "chunk", [
|
|
180265
|
+
String(item.candidate.id)
|
|
180266
|
+
]);
|
|
176934
180267
|
}
|
|
176935
180268
|
}
|
|
176936
180269
|
if (persistedIds.size === slice.length)
|
|
@@ -177099,7 +180432,24 @@ function createProvider2(config2) {
|
|
|
177099
180432
|
maxInputTokens: config2.max_input_tokens
|
|
177100
180433
|
});
|
|
177101
180434
|
}
|
|
177102
|
-
|
|
180435
|
+
if (config2.provider === "local") {
|
|
180436
|
+
return new LocalEmbeddingProvider(config2.model, config2.max_input_tokens);
|
|
180437
|
+
}
|
|
180438
|
+
if (config2.provider === "synapse") {
|
|
180439
|
+
const synapse = config2;
|
|
180440
|
+
return new SynapseEmbeddingProvider({
|
|
180441
|
+
connectionFile: synapse.synapse_connection_file ?? "",
|
|
180442
|
+
projectRoot: "",
|
|
180443
|
+
session: "embedding",
|
|
180444
|
+
model: synapse.model,
|
|
180445
|
+
fingerprint: synapse.synapse_fingerprint,
|
|
180446
|
+
tableEpoch: synapse.synapse_table_epoch,
|
|
180447
|
+
dims: synapse.synapse_dims,
|
|
180448
|
+
recommendedBatch: synapse.synapse_recommended_batch,
|
|
180449
|
+
provenance: synapse.synapse_provenance
|
|
180450
|
+
});
|
|
180451
|
+
}
|
|
180452
|
+
throw new Error("Unknown embedding provider");
|
|
177103
180453
|
}
|
|
177104
180454
|
function getOrCreateProvider() {
|
|
177105
180455
|
if (provider) {
|
|
@@ -178991,7 +182341,7 @@ If no promotions are warranted, return empty arrays. Always consume reviewed can
|
|
|
178991
182341
|
init_logger();
|
|
178992
182342
|
|
|
178993
182343
|
// ../plugin/src/shared/redaction.ts
|
|
178994
|
-
import { homedir as
|
|
182344
|
+
import { homedir as homedir4, userInfo } from "node:os";
|
|
178995
182345
|
function escapeRegex3(value) {
|
|
178996
182346
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
178997
182347
|
}
|
|
@@ -179040,7 +182390,7 @@ var SECRET_QUALIFIERS = new Set([
|
|
|
179040
182390
|
"azure"
|
|
179041
182391
|
]);
|
|
179042
182392
|
function sanitizePathString(value) {
|
|
179043
|
-
const home =
|
|
182393
|
+
const home = homedir4();
|
|
179044
182394
|
const username = userInfo().username;
|
|
179045
182395
|
let sanitized = value;
|
|
179046
182396
|
if (home) {
|
|
@@ -180036,7 +183386,7 @@ function throwIfAborted2(signal) {
|
|
|
180036
183386
|
}
|
|
180037
183387
|
|
|
180038
183388
|
// ../plugin/src/features/magic-context/smart-notes/compiler.ts
|
|
180039
|
-
import { createHash as
|
|
183389
|
+
import { createHash as createHash11 } from "node:crypto";
|
|
180040
183390
|
init_logger();
|
|
180041
183391
|
|
|
180042
183392
|
// ../plugin/src/features/magic-context/smart-notes/compiler-prompt.ts
|
|
@@ -180347,7 +183697,7 @@ Remember: output only the JSON object described by the system prompt.`;
|
|
|
180347
183697
|
});
|
|
180348
183698
|
const response = run.validated;
|
|
180349
183699
|
const compiledCheck = normalizeCompiledCheck(response.compiled_check);
|
|
180350
|
-
const manifest =
|
|
183700
|
+
const manifest = normalizeManifest2(response.manifest);
|
|
180351
183701
|
const checkCron = normalizeCron(response.check_cron);
|
|
180352
183702
|
for (const warning of manifestAdvisoryWarnings(compiledCheck, manifest)) {
|
|
180353
183703
|
log(`[dreamer] smart note #${args.note.id}: manifest advisory — ${warning}`);
|
|
@@ -180426,7 +183776,7 @@ function normalizeCompiledCheck(source) {
|
|
|
180426
183776
|
}
|
|
180427
183777
|
return code;
|
|
180428
183778
|
}
|
|
180429
|
-
function
|
|
183779
|
+
function normalizeManifest2(manifest) {
|
|
180430
183780
|
const capabilities = Array.isArray(manifest.capabilities) ? unique(manifest.capabilities.slice(0, MAX_MANIFEST_ENTRIES).filter((cap) => ["readFile", "gitHeadSha", "gitTag", "gitLog", "httpGet"].includes(String(cap)))) : [];
|
|
180431
183781
|
return {
|
|
180432
183782
|
capabilities,
|
|
@@ -180470,7 +183820,7 @@ function manifestAdvisoryWarnings(code, manifest) {
|
|
|
180470
183820
|
return warnings;
|
|
180471
183821
|
}
|
|
180472
183822
|
function hashCheck(surfaceCondition, compiledCheck, manifest, checkCron) {
|
|
180473
|
-
return
|
|
183823
|
+
return createHash11("sha256").update(surfaceCondition ?? "").update("\x00").update(compiledCheck).update("\x00").update(JSON.stringify(manifest)).update("\x00").update(checkCron).digest("hex");
|
|
180474
183824
|
}
|
|
180475
183825
|
function extractJsonObject(output) {
|
|
180476
183826
|
const fenced = output.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
@@ -182405,7 +185755,7 @@ function normalizeOpenCodeRows(db, sessionId, rows) {
|
|
|
182405
185755
|
const partsByMessageId = new Map;
|
|
182406
185756
|
for (const row of partRows) {
|
|
182407
185757
|
const parts = partsByMessageId.get(row.message_id) ?? [];
|
|
182408
|
-
const parsed =
|
|
185758
|
+
const parsed = parseJson2(row.data);
|
|
182409
185759
|
if (parsed !== null)
|
|
182410
185760
|
parts.push(parsed);
|
|
182411
185761
|
partsByMessageId.set(row.message_id, parts);
|
|
@@ -182516,7 +185866,7 @@ function stringifyToolOutput(value) {
|
|
|
182516
185866
|
return String(value);
|
|
182517
185867
|
}
|
|
182518
185868
|
}
|
|
182519
|
-
function
|
|
185869
|
+
function parseJson2(value) {
|
|
182520
185870
|
try {
|
|
182521
185871
|
return JSON.parse(value);
|
|
182522
185872
|
} catch {
|
|
@@ -182524,7 +185874,7 @@ function parseJson(value) {
|
|
|
182524
185874
|
}
|
|
182525
185875
|
}
|
|
182526
185876
|
function parseJsonRecord2(value) {
|
|
182527
|
-
const parsed =
|
|
185877
|
+
const parsed = parseJson2(value);
|
|
182528
185878
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
|
|
182529
185879
|
return null;
|
|
182530
185880
|
return parsed;
|
|
@@ -183282,7 +186632,7 @@ function parseFrictionGateVerdict(verdict) {
|
|
|
183282
186632
|
}
|
|
183283
186633
|
function computeRetrospectiveWindowKey(flagged) {
|
|
183284
186634
|
const anchors = flagged.map((message) => `${message.sessionId}:${message.ts}`).sort().join("|");
|
|
183285
|
-
return
|
|
186635
|
+
return createHash12("sha256").update(anchors).digest("hex").slice(0, 32);
|
|
183286
186636
|
}
|
|
183287
186637
|
function renderFrictionWindow(messages, flaggedOrdinals, radius = 2) {
|
|
183288
186638
|
const flagged = new Set(flaggedOrdinals);
|
|
@@ -184136,18 +187486,23 @@ async function embedUnembeddedCommits(db, projectPath) {
|
|
|
184136
187486
|
break;
|
|
184137
187487
|
let embeddedThisBatch = 0;
|
|
184138
187488
|
try {
|
|
184139
|
-
const result = await
|
|
187489
|
+
const result = await embedItemsForProject(projectPath, rows.map((row) => ({
|
|
187490
|
+
id: `commit:${row.sha}`,
|
|
187491
|
+
text: row.message,
|
|
187492
|
+
contentSha256: contentSha256(row.message)
|
|
187493
|
+
})), undefined, db, projectPath);
|
|
184140
187494
|
if (!result)
|
|
184141
187495
|
break;
|
|
184142
187496
|
db.transaction(() => {
|
|
184143
|
-
for (const
|
|
184144
|
-
const embedding2 = result.vectors
|
|
187497
|
+
for (const row of rows) {
|
|
187498
|
+
const embedding2 = result.vectors.get(`commit:${row.sha}`);
|
|
184145
187499
|
if (!embedding2)
|
|
184146
187500
|
continue;
|
|
184147
187501
|
saveCommitEmbedding(db, row.sha, embedding2, result.modelId);
|
|
184148
187502
|
embeddedThisBatch += 1;
|
|
184149
187503
|
}
|
|
184150
187504
|
})();
|
|
187505
|
+
enqueueShadowEmbeddingItems(projectPath, "commit", rows.filter((row) => result.vectors.has(`commit:${row.sha}`)).map((row) => row.sha));
|
|
184151
187506
|
} catch (error51) {
|
|
184152
187507
|
log(`[git-commits] embed batch failed for ${projectPath}: ${error51 instanceof Error ? error51.message : String(error51)}`);
|
|
184153
187508
|
break;
|
|
@@ -184303,7 +187658,11 @@ function searchGitCommitsSync(db, projectPath, query, options) {
|
|
|
184303
187658
|
init_logger();
|
|
184304
187659
|
var DREAM_TIMER_INTERVAL_MS = 15 * 60 * 1000;
|
|
184305
187660
|
var GIT_COMMIT_BACKLOG_DRAIN_MAX_MS = 5 * 60 * 1000;
|
|
187661
|
+
var BOOT_PROJECT_JITTER_SLOT_MS = 1000;
|
|
184306
187662
|
var activeTimer = null;
|
|
187663
|
+
var startupTimers = new Map;
|
|
187664
|
+
var startupJitters = new Map;
|
|
187665
|
+
var nextStartupJitterSlot = 0;
|
|
184307
187666
|
function directoryStillExists(directory) {
|
|
184308
187667
|
try {
|
|
184309
187668
|
return statSync6(directory).isDirectory();
|
|
@@ -184327,39 +187686,53 @@ function openTimerDatabaseOrNull(context) {
|
|
|
184327
187686
|
}
|
|
184328
187687
|
var registeredProjects = new Map;
|
|
184329
187688
|
async function startDreamScheduleTimer(args) {
|
|
187689
|
+
beginBootQuietPeriod();
|
|
184330
187690
|
const db = openTimerDatabaseOrNull("schedule timer registration");
|
|
184331
187691
|
if (!db)
|
|
184332
187692
|
return;
|
|
184333
|
-
await args.ensureRegistered(args.directory, db);
|
|
184334
|
-
const snapshot = getProjectEmbeddingSnapshot(args.projectIdentity);
|
|
184335
187693
|
const dreamingEnabled = Boolean(args.dreamerConfig && args.dreamerConfig.disable !== true);
|
|
184336
|
-
const embeddingSweepEnabled =
|
|
184337
|
-
const commitIndexingEnabled =
|
|
187694
|
+
const embeddingSweepEnabled = args.memoryEnabled === true;
|
|
187695
|
+
const commitIndexingEnabled = args.gitCommitIndexing?.enabled === true;
|
|
184338
187696
|
if (!dreamingEnabled && !embeddingSweepEnabled && !commitIndexingEnabled) {
|
|
184339
187697
|
return;
|
|
184340
187698
|
}
|
|
184341
|
-
const
|
|
187699
|
+
const previousRegistration = registeredProjects.get(args.directory);
|
|
187700
|
+
const isNewRegistration = previousRegistration === undefined;
|
|
187701
|
+
if (previousRegistration && previousRegistration !== args) {
|
|
187702
|
+
const pendingStartup = startupTimers.get(args.directory);
|
|
187703
|
+
if (pendingStartup) {
|
|
187704
|
+
clearTimeout(pendingStartup);
|
|
187705
|
+
startupTimers.delete(args.directory);
|
|
187706
|
+
}
|
|
187707
|
+
}
|
|
184342
187708
|
registeredProjects.set(args.directory, args);
|
|
184343
187709
|
if (isNewRegistration) {
|
|
184344
187710
|
log(`[dreamer] registered project ${args.projectIdentity} (dreaming=${dreamingEnabled} embeddings=${embeddingSweepEnabled} commits=${commitIndexingEnabled}; total=${registeredProjects.size})`);
|
|
184345
187711
|
}
|
|
184346
187712
|
if (!activeTimer) {
|
|
184347
187713
|
log(`[dreamer] started independent schedule timer (every ${DREAM_TIMER_INTERVAL_MS / 60000}m)`);
|
|
184348
|
-
runTick("startup");
|
|
187714
|
+
scheduleAfterBootQuiet(() => runTick("startup"));
|
|
184349
187715
|
const timer = setInterval(() => runTick("interval"), DREAM_TIMER_INTERVAL_MS);
|
|
184350
187716
|
if (typeof timer === "object" && "unref" in timer) {
|
|
184351
187717
|
timer.unref();
|
|
184352
187718
|
}
|
|
184353
187719
|
activeTimer = timer;
|
|
184354
|
-
} else if (isNewRegistration) {
|
|
184355
|
-
|
|
187720
|
+
} else if (isNewRegistration || previousRegistration !== args) {
|
|
187721
|
+
scheduleInitialProjectRun(args, db);
|
|
184356
187722
|
}
|
|
184357
187723
|
return () => {
|
|
184358
187724
|
registeredProjects.delete(args.directory);
|
|
187725
|
+
const startupTimer = startupTimers.get(args.directory);
|
|
187726
|
+
if (startupTimer) {
|
|
187727
|
+
clearTimeout(startupTimer);
|
|
187728
|
+
startupTimers.delete(args.directory);
|
|
187729
|
+
}
|
|
184359
187730
|
log(`[dreamer] unregistered project ${args.projectIdentity} (remaining=${registeredProjects.size})`);
|
|
184360
187731
|
if (registeredProjects.size === 0 && activeTimer) {
|
|
184361
187732
|
clearInterval(activeTimer);
|
|
184362
187733
|
activeTimer = null;
|
|
187734
|
+
startupJitters.clear();
|
|
187735
|
+
nextStartupJitterSlot = 0;
|
|
184363
187736
|
log("[dreamer] stopped dream schedule timer (no projects left)");
|
|
184364
187737
|
}
|
|
184365
187738
|
};
|
|
@@ -184372,22 +187745,53 @@ function runTick(origin) {
|
|
|
184372
187745
|
if (!db)
|
|
184373
187746
|
return;
|
|
184374
187747
|
for (const reg of registeredProjects.values()) {
|
|
184375
|
-
|
|
184376
|
-
|
|
184377
|
-
|
|
184378
|
-
|
|
184379
|
-
if (embeddedCount > 0) {
|
|
184380
|
-
log(`[magic-context] proactively embedded ${embeddedCount} ${embeddedCount === 1 ? "memory" : "memories"} for project ${reg.projectIdentity}`);
|
|
184381
|
-
}
|
|
187748
|
+
if (origin === "startup") {
|
|
187749
|
+
scheduleInitialProjectRun(reg, db);
|
|
187750
|
+
} else {
|
|
187751
|
+
await runProjectMaintenance(reg, origin, db);
|
|
184382
187752
|
}
|
|
184383
|
-
await sweepProject(reg, origin, db);
|
|
184384
187753
|
}
|
|
187754
|
+
if (origin === "startup")
|
|
187755
|
+
return;
|
|
184385
187756
|
runSqliteOptimize(db);
|
|
184386
187757
|
} catch (error51) {
|
|
184387
187758
|
log("[magic-context] timer-triggered maintenance check failed:", error51);
|
|
184388
187759
|
}
|
|
184389
187760
|
})();
|
|
184390
187761
|
}
|
|
187762
|
+
function startupJitterMs(directory) {
|
|
187763
|
+
const existing = startupJitters.get(directory);
|
|
187764
|
+
if (existing !== undefined)
|
|
187765
|
+
return existing;
|
|
187766
|
+
const slot = nextStartupJitterSlot;
|
|
187767
|
+
nextStartupJitterSlot += 1;
|
|
187768
|
+
const hash2 = [...directory].reduce((value, character) => value * 33 + character.charCodeAt(0) >>> 0, 5381);
|
|
187769
|
+
const jitter = slot * BOOT_PROJECT_JITTER_SLOT_MS + hash2 % BOOT_PROJECT_JITTER_SLOT_MS;
|
|
187770
|
+
startupJitters.set(directory, jitter);
|
|
187771
|
+
return jitter;
|
|
187772
|
+
}
|
|
187773
|
+
function scheduleInitialProjectRun(reg, db) {
|
|
187774
|
+
if (startupTimers.has(reg.directory))
|
|
187775
|
+
return;
|
|
187776
|
+
const timer = scheduleAfterBootQuiet(() => {
|
|
187777
|
+
startupTimers.delete(reg.directory);
|
|
187778
|
+
if (registeredProjects.get(reg.directory) !== reg)
|
|
187779
|
+
return;
|
|
187780
|
+
runProjectMaintenance(reg, "startup", db);
|
|
187781
|
+
}, startupJitterMs(reg.directory));
|
|
187782
|
+
startupTimers.set(reg.directory, timer);
|
|
187783
|
+
}
|
|
187784
|
+
async function runProjectMaintenance(reg, origin, db) {
|
|
187785
|
+
await reg.ensureRegistered(reg.directory, db);
|
|
187786
|
+
const memorySnapshot = getProjectEmbeddingSnapshot(reg.projectIdentity);
|
|
187787
|
+
if (memorySnapshot?.enabled) {
|
|
187788
|
+
const embeddedCount = await embedUnembeddedMemoriesForProject(db, reg.projectIdentity);
|
|
187789
|
+
if (embeddedCount > 0) {
|
|
187790
|
+
log(`[magic-context] proactively embedded ${embeddedCount} ${embeddedCount === 1 ? "memory" : "memories"} for project ${reg.projectIdentity}`);
|
|
187791
|
+
}
|
|
187792
|
+
}
|
|
187793
|
+
await sweepProject(reg, origin, db);
|
|
187794
|
+
}
|
|
184391
187795
|
async function sweepProject(reg, origin, db, gitCommitEnabled) {
|
|
184392
187796
|
if (!directoryStillExists(reg.directory)) {
|
|
184393
187797
|
log(`[dreamer] project directory no longer exists (${reg.projectIdentity}); skipping + unregistering`);
|
|
@@ -184555,7 +187959,7 @@ async function sweepGitCommits(args) {
|
|
|
184555
187959
|
import { statSync as statSync7 } from "node:fs";
|
|
184556
187960
|
|
|
184557
187961
|
// ../plugin/src/plugin/embedding-bootstrap-helpers.ts
|
|
184558
|
-
import { createHash as
|
|
187962
|
+
import { createHash as createHash13 } from "node:crypto";
|
|
184559
187963
|
init_logger();
|
|
184560
187964
|
var EMBEDDING_AFFECTING_KEYS = new Set([
|
|
184561
187965
|
"embedding.api_key",
|
|
@@ -184565,7 +187969,11 @@ var EMBEDDING_AFFECTING_KEYS = new Set([
|
|
|
184565
187969
|
"embedding.input_type",
|
|
184566
187970
|
"embedding.truncate",
|
|
184567
187971
|
"embedding.max_input_tokens",
|
|
184568
|
-
"embedding.query_input_type"
|
|
187972
|
+
"embedding.query_input_type",
|
|
187973
|
+
"embedding.fallback_provider",
|
|
187974
|
+
"subc",
|
|
187975
|
+
"subc.connection_file",
|
|
187976
|
+
"shadow_embedding"
|
|
184569
187977
|
]);
|
|
184570
187978
|
var LITERAL_CONFIG_TOKEN_RE = /\{(?:env|file):[^}]+\}/;
|
|
184571
187979
|
function embeddingConfigHasLiteralTokens(embedding2) {
|
|
@@ -184578,7 +187986,13 @@ function embeddingConfigHasLiteralTokens(embedding2) {
|
|
|
184578
187986
|
}
|
|
184579
187987
|
return false;
|
|
184580
187988
|
}
|
|
184581
|
-
var EMBEDDING_AFFECTING_TOP_LEVEL_KEYS = new Set([
|
|
187989
|
+
var EMBEDDING_AFFECTING_TOP_LEVEL_KEYS = new Set([
|
|
187990
|
+
"embedding",
|
|
187991
|
+
"memory",
|
|
187992
|
+
"experimental",
|
|
187993
|
+
"subc",
|
|
187994
|
+
"shadow_embedding"
|
|
187995
|
+
]);
|
|
184582
187996
|
var EMBEDDING_WARNING_TERMS = [
|
|
184583
187997
|
"api_key",
|
|
184584
187998
|
"endpoint",
|
|
@@ -184586,11 +188000,13 @@ var EMBEDDING_WARNING_TERMS = [
|
|
|
184586
188000
|
"provider",
|
|
184587
188001
|
"embedding",
|
|
184588
188002
|
"input_type",
|
|
184589
|
-
"truncate"
|
|
188003
|
+
"truncate",
|
|
188004
|
+
"subc",
|
|
188005
|
+
"shadow_embedding"
|
|
184590
188006
|
];
|
|
184591
188007
|
var loggedFailureSignatures = new Map;
|
|
184592
188008
|
function sha256Prefix2(value, length = 16) {
|
|
184593
|
-
return
|
|
188009
|
+
return createHash13("sha256").update(value).digest("hex").slice(0, length);
|
|
184594
188010
|
}
|
|
184595
188011
|
function warningLooksEmbeddingRelated(message) {
|
|
184596
188012
|
const lower = message.toLowerCase();
|
|
@@ -184989,7 +188405,7 @@ function migrateLegacyExperimental(rawConfig, warnings) {
|
|
|
184989
188405
|
var HIDDEN_AGENT_KEYS = ["historian", "dreamer", "sidekick"];
|
|
184990
188406
|
var HISTORIAN_USER_ONLY_FIELDS = ["model", "fallback_models"];
|
|
184991
188407
|
var AGENT_ESCALATION_FIELDS = ["prompt", "permission", "tools", "system_prompt"];
|
|
184992
|
-
var EMBEDDING_DESTINATION_FIELDS = ["endpoint", "provider"];
|
|
188408
|
+
var EMBEDDING_DESTINATION_FIELDS = ["endpoint", "provider", "fallback_provider"];
|
|
184993
188409
|
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.";
|
|
184994
188410
|
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.";
|
|
184995
188411
|
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.";
|
|
@@ -185105,6 +188521,12 @@ function stripUnsafeProjectConfigFields(projectRaw) {
|
|
|
185105
188521
|
delete projectRaw.shadow_transform;
|
|
185106
188522
|
warnings.push("Ignoring shadow_transform from project config (security: this developer-only mirror lane is user-level only).");
|
|
185107
188523
|
}
|
|
188524
|
+
for (const field of ["subc", "shadow_embedding"]) {
|
|
188525
|
+
if (field in projectRaw) {
|
|
188526
|
+
delete projectRaw[field];
|
|
188527
|
+
warnings.push(`Ignoring ${field} from project config (security: daemon routing and developer-only embedding traffic are user-level settings).`);
|
|
188528
|
+
}
|
|
188529
|
+
}
|
|
185108
188530
|
const embedding2 = projectRaw.embedding;
|
|
185109
188531
|
if (isPlainObject3(embedding2)) {
|
|
185110
188532
|
const removed = [];
|
|
@@ -185280,7 +188702,7 @@ function dropInheritedEmbeddingKeyOnRedirect(projectRaw, mergedRaw, userRaw) {
|
|
|
185280
188702
|
|
|
185281
188703
|
// ../plugin/src/config/variable.ts
|
|
185282
188704
|
import { existsSync as existsSync16, readFileSync as readFileSync7 } from "node:fs";
|
|
185283
|
-
import { homedir as
|
|
188705
|
+
import { homedir as homedir5 } from "node:os";
|
|
185284
188706
|
import { dirname as dirname6, isAbsolute as isAbsolute2, resolve } from "node:path";
|
|
185285
188707
|
|
|
185286
188708
|
// ../plugin/src/shared/jsonc-parser.ts
|
|
@@ -185343,7 +188765,7 @@ function stripJsonComments(content) {
|
|
|
185343
188765
|
var ENV_PATTERN = /\{env:([^}]+)\}/g;
|
|
185344
188766
|
var FILE_PATTERN = /\{file:([^}]+)\}/g;
|
|
185345
188767
|
function sensitiveFilePathReason(resolvedPath) {
|
|
185346
|
-
const home =
|
|
188768
|
+
const home = homedir5();
|
|
185347
188769
|
const sensitiveDirs = [
|
|
185348
188770
|
{ dir: resolve(home, ".ssh"), label: "SSH keys" },
|
|
185349
188771
|
{ dir: resolve(home, ".aws"), label: "AWS credentials" },
|
|
@@ -185406,7 +188828,7 @@ function substituteConfigVariables(input) {
|
|
|
185406
188828
|
}
|
|
185407
188829
|
let filePath = rawPath.trim();
|
|
185408
188830
|
if (filePath.startsWith("~/")) {
|
|
185409
|
-
filePath = resolve(
|
|
188831
|
+
filePath = resolve(homedir5(), filePath.slice(2));
|
|
185410
188832
|
} else if (!isAbsolute2(filePath)) {
|
|
185411
188833
|
filePath = resolve(configDir, filePath);
|
|
185412
188834
|
}
|
|
@@ -190767,6 +194189,93 @@ function containsProbeVerbatim(text, probes) {
|
|
|
190767
194189
|
return probes.some((probe) => haystack.includes(probe.toLowerCase()));
|
|
190768
194190
|
}
|
|
190769
194191
|
|
|
194192
|
+
// ../plugin/src/features/magic-context/search-measurement.ts
|
|
194193
|
+
import { createHash as createHash14 } from "node:crypto";
|
|
194194
|
+
function resultId(result) {
|
|
194195
|
+
switch (result.source) {
|
|
194196
|
+
case "memory":
|
|
194197
|
+
return `memory:${result.memoryId}`;
|
|
194198
|
+
case "message":
|
|
194199
|
+
return `message:${result.messageId}`;
|
|
194200
|
+
case "compartment":
|
|
194201
|
+
return `chunk:${result.compartmentId}`;
|
|
194202
|
+
case "git_commit":
|
|
194203
|
+
return `commit:${result.sha}`;
|
|
194204
|
+
case "primer":
|
|
194205
|
+
return `primer:${result.primerId}`;
|
|
194206
|
+
case "note":
|
|
194207
|
+
return `note:${result.noteId}`;
|
|
194208
|
+
}
|
|
194209
|
+
}
|
|
194210
|
+
async function recordShadowMeasurement(args) {
|
|
194211
|
+
const shadowCohort = getShadowEmbeddingMeasurementCohort(args.projectPath);
|
|
194212
|
+
if (!shadowCohort)
|
|
194213
|
+
return;
|
|
194214
|
+
const primaryCohort = getPrimaryEmbeddingMeasurementCohort(args.projectPath);
|
|
194215
|
+
const shadowStartedAt = Date.now();
|
|
194216
|
+
let shadowFailed = false;
|
|
194217
|
+
let shadowResults = [];
|
|
194218
|
+
try {
|
|
194219
|
+
const vector = await embedShadowTextForProject(args.projectPath, args.query);
|
|
194220
|
+
if (!vector) {
|
|
194221
|
+
shadowFailed = true;
|
|
194222
|
+
} else {
|
|
194223
|
+
shadowResults = await args.search(args.db, args.sessionId, args.projectPath, args.query, {
|
|
194224
|
+
...args.options,
|
|
194225
|
+
embedQuery: async () => vector,
|
|
194226
|
+
isEmbeddingRuntimeEnabled: () => true,
|
|
194227
|
+
embeddingEnabled: true,
|
|
194228
|
+
embeddingModelIdOverride: shadowCohort.modelId,
|
|
194229
|
+
chunkModelIdOverride: shadowCohort.chunkModelId,
|
|
194230
|
+
measurementDisabled: true,
|
|
194231
|
+
countRetrievals: false
|
|
194232
|
+
});
|
|
194233
|
+
}
|
|
194234
|
+
} catch {
|
|
194235
|
+
shadowFailed = true;
|
|
194236
|
+
}
|
|
194237
|
+
const primaryModelId = args.primaryQuery?.modelId ?? primaryCohort?.modelId ?? "";
|
|
194238
|
+
const primaryFingerprint = primaryCohort?.fingerprint ?? "";
|
|
194239
|
+
const primaryEpoch = primaryCohort?.epoch ?? 0;
|
|
194240
|
+
const cohortKey = JSON.stringify({
|
|
194241
|
+
primaryFingerprint,
|
|
194242
|
+
primaryEpoch,
|
|
194243
|
+
shadowFingerprint: shadowCohort.fingerprint,
|
|
194244
|
+
shadowEpoch: shadowCohort.epoch
|
|
194245
|
+
});
|
|
194246
|
+
const primaryIds = args.primaryResults.map(resultId);
|
|
194247
|
+
const shadowIds = shadowResults.map(resultId);
|
|
194248
|
+
const corpusHash = sha2562(JSON.stringify({ query: args.query, primaryIds, shadowIds }));
|
|
194249
|
+
recordEmbeddingMeasurement(args.db, {
|
|
194250
|
+
sessionId: args.sessionId,
|
|
194251
|
+
projectPath: args.projectPath,
|
|
194252
|
+
queryText: args.query,
|
|
194253
|
+
cohortKey,
|
|
194254
|
+
primaryResultIds: primaryIds,
|
|
194255
|
+
shadowResultIds: shadowIds,
|
|
194256
|
+
primaryLatencyMs: args.primaryLatencyMs,
|
|
194257
|
+
shadowLatencyMs: Date.now() - shadowStartedAt,
|
|
194258
|
+
primaryFailed: args.primaryQuery === null,
|
|
194259
|
+
shadowFailed,
|
|
194260
|
+
primaryModelId,
|
|
194261
|
+
shadowModelId: shadowCohort.modelId,
|
|
194262
|
+
primaryFingerprint,
|
|
194263
|
+
shadowFingerprint: shadowCohort.fingerprint,
|
|
194264
|
+
primaryEpoch,
|
|
194265
|
+
shadowEpoch: shadowCohort.epoch,
|
|
194266
|
+
corpusHash,
|
|
194267
|
+
coverage: {
|
|
194268
|
+
primaryResultCount: primaryIds.length,
|
|
194269
|
+
shadowResultCount: shadowIds.length,
|
|
194270
|
+
primaryAnswered: args.primaryQuery !== null,
|
|
194271
|
+
shadowAnswered: !shadowFailed
|
|
194272
|
+
}
|
|
194273
|
+
});
|
|
194274
|
+
}
|
|
194275
|
+
function sha2562(value) {
|
|
194276
|
+
return createHash14("sha256").update(value).digest("hex");
|
|
194277
|
+
}
|
|
194278
|
+
|
|
190770
194279
|
// ../plugin/src/features/magic-context/search.ts
|
|
190771
194280
|
var DEFAULT_UNIFIED_SEARCH_LIMIT = 10;
|
|
190772
194281
|
var FTS_SEMANTIC_CANDIDATE_LIMIT = 50;
|
|
@@ -191524,6 +195033,7 @@ function resolveSources(sources) {
|
|
|
191524
195033
|
}
|
|
191525
195034
|
async function unifiedSearch(db, sessionId, projectPath, query, options = {}) {
|
|
191526
195035
|
const trimmedQuery = query.trim();
|
|
195036
|
+
const measurementStartedAt = Date.now();
|
|
191527
195037
|
if (trimmedQuery.length === 0) {
|
|
191528
195038
|
return [];
|
|
191529
195039
|
}
|
|
@@ -191556,11 +195066,14 @@ async function unifiedSearch(db, sessionId, projectPath, query, options = {}) {
|
|
|
191556
195066
|
maxOrdinal: options.maxMessageOrdinal,
|
|
191557
195067
|
probes: messageProbes
|
|
191558
195068
|
}) : [];
|
|
191559
|
-
const
|
|
191560
|
-
const workspace = resolveSearchWorkspaceContext(db, projectPath);
|
|
195069
|
+
const capturedQuery = await queryEmbeddingPromise;
|
|
191561
195070
|
const embeddingSnapshot = getProjectEmbeddingSnapshot(projectPath);
|
|
191562
|
-
const
|
|
191563
|
-
const
|
|
195071
|
+
const queryContract = capturedQuery instanceof Float32Array || capturedQuery === null ? null : capturedQuery;
|
|
195072
|
+
const generationIsCurrent = queryContract === null || embeddingSnapshot !== null && embeddingSnapshot.generation === queryContract.generation;
|
|
195073
|
+
const queryEmbedding = generationIsCurrent ? queryContract?.vector ?? (capturedQuery instanceof Float32Array ? capturedQuery : null) : null;
|
|
195074
|
+
const workspace = resolveSearchWorkspaceContext(db, projectPath);
|
|
195075
|
+
const embeddingModelId = queryContract?.modelId ?? options.embeddingModelIdOverride ?? embeddingSnapshot?.modelId;
|
|
195076
|
+
const chunkModelId = queryContract?.chunkModelId ?? options.chunkModelIdOverride ?? embeddingSnapshot?.chunkModelId;
|
|
191564
195077
|
const compartmentResults = runCompartmentChunks ? searchCompartmentChunks({
|
|
191565
195078
|
db,
|
|
191566
195079
|
sessionId,
|
|
@@ -191619,6 +195132,19 @@ async function unifiedSearch(db, sessionId, projectPath, query, options = {}) {
|
|
|
191619
195132
|
...gitCommitResults,
|
|
191620
195133
|
...noteResults
|
|
191621
195134
|
].sort(compareUnifiedResults).slice(0, limit);
|
|
195135
|
+
if (!options.measurementDisabled) {
|
|
195136
|
+
recordShadowMeasurement({
|
|
195137
|
+
db,
|
|
195138
|
+
sessionId,
|
|
195139
|
+
projectPath,
|
|
195140
|
+
query: trimmedQuery,
|
|
195141
|
+
options,
|
|
195142
|
+
primaryResults: results,
|
|
195143
|
+
primaryQuery: queryContract,
|
|
195144
|
+
primaryLatencyMs: Date.now() - measurementStartedAt,
|
|
195145
|
+
search: unifiedSearch
|
|
195146
|
+
});
|
|
195147
|
+
}
|
|
191622
195148
|
const countRetrievals = options.countRetrievals ?? true;
|
|
191623
195149
|
if (countRetrievals) {
|
|
191624
195150
|
const memoryIds = results.filter((result) => result.source === "memory").map((result) => result.memoryId);
|
|
@@ -193774,15 +197300,19 @@ async function embedAndStoreCompartmentChunks(db, sessionId, projectPath, compar
|
|
|
193774
197300
|
if (currentModelId !== "off" && chunkEmbeddingWindowsAreCurrent(db, compartment.id, currentModelId, windows, projectPath)) {
|
|
193775
197301
|
continue;
|
|
193776
197302
|
}
|
|
193777
|
-
const result = await
|
|
197303
|
+
const result = await embedItemsForProject(projectPath, windows.map((window) => ({
|
|
197304
|
+
id: `chunk:${compartment.id}:${window.windowIndex}`,
|
|
197305
|
+
text: window.text,
|
|
197306
|
+
contentSha256: contentSha256(window.text)
|
|
197307
|
+
})), undefined, db, sessionId);
|
|
193778
197308
|
if (!result)
|
|
193779
197309
|
continue;
|
|
193780
197310
|
if (chunkEmbeddingWindowsAreCurrent(db, compartment.id, currentModelId, windows, projectPath)) {
|
|
193781
197311
|
continue;
|
|
193782
197312
|
}
|
|
193783
197313
|
const rows = [];
|
|
193784
|
-
for (const
|
|
193785
|
-
const vector = result.vectors
|
|
197314
|
+
for (const window of windows) {
|
|
197315
|
+
const vector = result.vectors.get(`chunk:${compartment.id}:${window.windowIndex}`);
|
|
193786
197316
|
if (!vector)
|
|
193787
197317
|
continue;
|
|
193788
197318
|
rows.push({
|
|
@@ -193796,6 +197326,7 @@ async function embedAndStoreCompartmentChunks(db, sessionId, projectPath, compar
|
|
|
193796
197326
|
}
|
|
193797
197327
|
if (rows.length === windows.length) {
|
|
193798
197328
|
replaceCompartmentChunkEmbeddings(db, rows);
|
|
197329
|
+
enqueueShadowEmbeddingItems(projectPath, "chunk", [String(compartment.id)]);
|
|
193799
197330
|
}
|
|
193800
197331
|
} catch (error51) {
|
|
193801
197332
|
sessionLog(sessionId, `compartment chunk embedding failed for compartment ${compartment.id}:`, error51);
|
|
@@ -198230,7 +201761,7 @@ function stripPiProcessedImages(args) {
|
|
|
198230
201761
|
}
|
|
198231
201762
|
|
|
198232
201763
|
// src/system-prompt.ts
|
|
198233
|
-
import { createHash as
|
|
201764
|
+
import { createHash as createHash15 } from "node:crypto";
|
|
198234
201765
|
|
|
198235
201766
|
// ../plugin/src/agents/magic-context-prompt.ts
|
|
198236
201767
|
var LONG_TERM_PARTNER_FRAME = `### You are the user's long-term partner on this project — not a one-off hire
|
|
@@ -198383,7 +201914,7 @@ function processSystemPromptForCache(args) {
|
|
|
198383
201914
|
sessionLog(sessionId, `system prompt date frozen: real=${liveDate}, using=${stickyDate} (cache-stable pass)`);
|
|
198384
201915
|
}
|
|
198385
201916
|
}
|
|
198386
|
-
const currentHash =
|
|
201917
|
+
const currentHash = createHash15("md5").update(frozenPrompt).digest("hex");
|
|
198387
201918
|
const hashChanged = !isFirstHash && currentHash !== previousHash;
|
|
198388
201919
|
if (hashChanged) {
|
|
198389
201920
|
sessionLog(sessionId, `system prompt hash changed: ${previousHash} → ${currentHash} (len=${frozenPrompt.length})`);
|
|
@@ -203487,6 +207018,11 @@ function applyMemoryMigration(db, projectPath, result) {
|
|
|
203487
207018
|
}
|
|
203488
207019
|
var MIGRATION_SYSTEM_PROMPT = "You re-organize a software project's long-term memory for the magic-context system into a stricter taxonomy. " + "Follow the user instructions exactly. Output ONLY the requested XML blocks, nothing else.";
|
|
203489
207020
|
|
|
207021
|
+
// ../plugin/src/hooks/magic-context/lkg-slot.ts
|
|
207022
|
+
var LKG_TOTAL_BYTES = 64 * 1024 * 1024;
|
|
207023
|
+
var LKG_SINGLE_SLOT_BYTES = 24 * 1024 * 1024;
|
|
207024
|
+
var slots = new Map;
|
|
207025
|
+
|
|
203490
207026
|
// ../plugin/src/hooks/magic-context/recomp-orchestrator.ts
|
|
203491
207027
|
function isRecompFailure(message) {
|
|
203492
207028
|
return /—\s*(Failed|Skipped)/.test(message);
|
|
@@ -203976,7 +207512,7 @@ function formatThresholdPercent(value) {
|
|
|
203976
207512
|
// package.json
|
|
203977
207513
|
var package_default = {
|
|
203978
207514
|
name: "@cortexkit/pi-magic-context",
|
|
203979
|
-
version: "0.32.
|
|
207515
|
+
version: "0.32.2",
|
|
203980
207516
|
type: "module",
|
|
203981
207517
|
description: "Pi coding agent extension for Magic Context — cross-session memory and context management",
|
|
203982
207518
|
main: "dist/index.js",
|
|
@@ -211274,11 +214810,12 @@ async function src_default2(pi) {
|
|
|
211274
214810
|
log(`${PREFIX} subagent child detected (${MAGIC_CONTEXT_PI_SUBAGENT_ENV}=1); skipping full extension registration`);
|
|
211275
214811
|
return;
|
|
211276
214812
|
}
|
|
214813
|
+
beginBootQuietPeriod();
|
|
211277
214814
|
const storageDir = getMagicContextStorageDir();
|
|
211278
214815
|
const dbPath = join13(storageDir, "context.db");
|
|
211279
214816
|
let db;
|
|
211280
214817
|
try {
|
|
211281
|
-
db =
|
|
214818
|
+
db = await openDatabaseAsync();
|
|
211282
214819
|
} catch (err5) {
|
|
211283
214820
|
const message = err5 instanceof Error ? err5.message : String(err5);
|
|
211284
214821
|
warn(`Magic Context (pi) failed to open SQLite store at ${dbPath}: ${message}. ` + "Plugin will not register hooks; storage path is unwritable or corrupt.");
|
|
@@ -211289,10 +214826,12 @@ async function src_default2(pi) {
|
|
|
211289
214826
|
return;
|
|
211290
214827
|
}
|
|
211291
214828
|
const database = db;
|
|
211292
|
-
|
|
211293
|
-
|
|
214829
|
+
scheduleAfterBootQuiet(() => {
|
|
214830
|
+
runDeferredV22Backfill(db).catch((err5) => {
|
|
214831
|
+
warn(`[v22-backfill] background runner failed: ${err5}`);
|
|
214832
|
+
});
|
|
211294
214833
|
});
|
|
211295
|
-
|
|
214834
|
+
scheduleAfterBootQuiet(() => {
|
|
211296
214835
|
(async () => {
|
|
211297
214836
|
try {
|
|
211298
214837
|
const api2 = await loadDefaultPiSessionApi();
|