@cortexkit/pi-magic-context 0.32.1 → 0.32.3
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 +3799 -172
- 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) {
|
|
@@ -159225,26 +159549,32 @@ init_logger();
|
|
|
159225
159549
|
// ../plugin/src/features/magic-context/compaction-marker.ts
|
|
159226
159550
|
init_data_path();
|
|
159227
159551
|
init_logger();
|
|
159552
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
159228
159553
|
import { existsSync as existsSync10 } from "node:fs";
|
|
159229
159554
|
import { join as join9 } from "node:path";
|
|
159230
159555
|
var BASE62_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
159231
|
-
|
|
159232
|
-
|
|
159233
|
-
|
|
159234
|
-
|
|
159556
|
+
var ID_PREFIX_HEX_LENGTH = 12;
|
|
159557
|
+
var ID_SUFFIX_LENGTH = 14;
|
|
159558
|
+
var ID_PREFIX_MASK = (1n << BigInt(ID_PREFIX_HEX_LENGTH * 4)) - 1n;
|
|
159559
|
+
function deterministicBase62(seed, length) {
|
|
159560
|
+
let value = BigInt(`0x${createHash7("sha256").update(seed).digest("hex")}`);
|
|
159561
|
+
const chars = Array(length);
|
|
159562
|
+
for (let index = length - 1;index >= 0; index -= 1) {
|
|
159563
|
+
chars[index] = BASE62_CHARS[Number(value % 62n)];
|
|
159564
|
+
value /= 62n;
|
|
159235
159565
|
}
|
|
159236
159566
|
return chars.join("");
|
|
159237
159567
|
}
|
|
159238
|
-
function generateId(prefix, timestampMs, counter
|
|
159239
|
-
const encoded = BigInt(timestampMs) * 0x1000n + counter;
|
|
159240
|
-
const hex = encoded.toString(16).padStart(
|
|
159241
|
-
return `${prefix}_${hex}${
|
|
159568
|
+
function generateId(prefix, timestampMs, counter, identity) {
|
|
159569
|
+
const encoded = BigInt(Math.max(0, Math.floor(timestampMs))) * 0x1000n + counter & ID_PREFIX_MASK;
|
|
159570
|
+
const hex = encoded.toString(16).padStart(ID_PREFIX_HEX_LENGTH, "0");
|
|
159571
|
+
return `${prefix}_${hex}${deterministicBase62(`${prefix}\x00${identity}`, ID_SUFFIX_LENGTH)}`;
|
|
159242
159572
|
}
|
|
159243
|
-
function generateMessageId(timestampMs, counter = 0n) {
|
|
159244
|
-
return generateId("msg", timestampMs, counter);
|
|
159573
|
+
function generateMessageId(timestampMs, counter = 0n, identity = "") {
|
|
159574
|
+
return generateId("msg", timestampMs, counter, identity);
|
|
159245
159575
|
}
|
|
159246
|
-
function generatePartId(timestampMs, counter = 0n) {
|
|
159247
|
-
return generateId("prt", timestampMs, counter);
|
|
159576
|
+
function generatePartId(timestampMs, counter = 0n, identity = "") {
|
|
159577
|
+
return generateId("prt", timestampMs, counter, identity);
|
|
159248
159578
|
}
|
|
159249
159579
|
function getOpenCodeDbPath2() {
|
|
159250
159580
|
return join9(getDataDir(), "opencode", "opencode.db");
|
|
@@ -159349,6 +159679,38 @@ function compareOpenCodeMessagesByCanonicalOrder(sessionId, leftMessageId, right
|
|
|
159349
159679
|
return 1;
|
|
159350
159680
|
return 0;
|
|
159351
159681
|
}
|
|
159682
|
+
function removeLegacyMarkerLineageRows(db, args) {
|
|
159683
|
+
const legacySummaries = db.prepare(`SELECT m.id
|
|
159684
|
+
FROM message m
|
|
159685
|
+
WHERE m.session_id = ?
|
|
159686
|
+
AND m.id <> ?
|
|
159687
|
+
AND COALESCE(json_extract(m.data, '$.summary'), 0) = 1
|
|
159688
|
+
AND COALESCE(json_extract(m.data, '$.finish'), '') = 'stop'
|
|
159689
|
+
AND COALESCE(json_extract(m.data, '$.parentID'), '') = ?
|
|
159690
|
+
AND EXISTS (
|
|
159691
|
+
SELECT 1
|
|
159692
|
+
FROM part p
|
|
159693
|
+
WHERE p.session_id = m.session_id
|
|
159694
|
+
AND p.message_id = m.id
|
|
159695
|
+
AND COALESCE(json_extract(p.data, '$.type'), '') = 'text'
|
|
159696
|
+
AND COALESCE(json_extract(p.data, '$.text'), '') = ?
|
|
159697
|
+
)`).all(args.sessionId, args.summaryMessageId, args.boundaryMessageId, args.summaryText);
|
|
159698
|
+
const legacySummaryIds = legacySummaries.flatMap((row) => typeof row.id === "string" ? [row.id] : []);
|
|
159699
|
+
if (legacySummaryIds.length === 0)
|
|
159700
|
+
return;
|
|
159701
|
+
const deleteSummaryParts = db.prepare("DELETE FROM part WHERE session_id = ? AND message_id = ?");
|
|
159702
|
+
const deleteSummary = db.prepare("DELETE FROM message WHERE session_id = ? AND id = ?");
|
|
159703
|
+
for (const summaryMessageId of legacySummaryIds) {
|
|
159704
|
+
deleteSummaryParts.run(args.sessionId, summaryMessageId);
|
|
159705
|
+
deleteSummary.run(args.sessionId, summaryMessageId);
|
|
159706
|
+
}
|
|
159707
|
+
db.prepare(`DELETE FROM part
|
|
159708
|
+
WHERE session_id = ?
|
|
159709
|
+
AND message_id = ?
|
|
159710
|
+
AND id <> ?
|
|
159711
|
+
AND COALESCE(json_extract(data, '$.type'), '') = 'compaction'
|
|
159712
|
+
AND COALESCE(json_extract(data, '$.auto'), 0) = 1`).run(args.sessionId, args.boundaryMessageId, args.compactionPartId);
|
|
159713
|
+
}
|
|
159352
159714
|
function injectCompactionMarker(args) {
|
|
159353
159715
|
const db = getWritableOpenCodeDb();
|
|
159354
159716
|
if (!isOpenCodeSchemaCompatible(db, getOpenCodeDbPath2())) {
|
|
@@ -159360,9 +159722,10 @@ function injectCompactionMarker(args) {
|
|
|
159360
159722
|
return null;
|
|
159361
159723
|
}
|
|
159362
159724
|
const boundaryTime = boundary.timeCreated;
|
|
159363
|
-
const
|
|
159364
|
-
const
|
|
159365
|
-
const
|
|
159725
|
+
const markerIdentity = `${args.sessionId}\x00${args.endMessageId}`;
|
|
159726
|
+
const summaryMsgId = generateMessageId(boundaryTime + 1, 1n, `${markerIdentity}\x00summary-message`);
|
|
159727
|
+
const compactionPartId = generatePartId(boundaryTime, 1n, `${markerIdentity}\x00compaction-part`);
|
|
159728
|
+
const summaryPartId = generatePartId(boundaryTime + 1, 2n, `${markerIdentity}\x00summary-part`);
|
|
159366
159729
|
const summaryMsgData = JSON.stringify({
|
|
159367
159730
|
role: "assistant",
|
|
159368
159731
|
parentID: boundary.id,
|
|
@@ -159379,9 +159742,36 @@ function injectCompactionMarker(args) {
|
|
|
159379
159742
|
});
|
|
159380
159743
|
try {
|
|
159381
159744
|
db.transaction(() => {
|
|
159382
|
-
db
|
|
159383
|
-
|
|
159384
|
-
|
|
159745
|
+
removeLegacyMarkerLineageRows(db, {
|
|
159746
|
+
sessionId: args.sessionId,
|
|
159747
|
+
boundaryMessageId: boundary.id,
|
|
159748
|
+
summaryText: args.summaryText,
|
|
159749
|
+
summaryMessageId: summaryMsgId,
|
|
159750
|
+
compactionPartId
|
|
159751
|
+
});
|
|
159752
|
+
db.prepare(`INSERT INTO part (id, message_id, session_id, time_created, time_updated, data)
|
|
159753
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
159754
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
159755
|
+
message_id = excluded.message_id,
|
|
159756
|
+
session_id = excluded.session_id,
|
|
159757
|
+
time_created = excluded.time_created,
|
|
159758
|
+
time_updated = excluded.time_updated,
|
|
159759
|
+
data = excluded.data`).run(compactionPartId, boundary.id, args.sessionId, boundaryTime, boundaryTime, '{"type":"compaction","auto":true}');
|
|
159760
|
+
db.prepare(`INSERT INTO message (id, session_id, time_created, time_updated, data)
|
|
159761
|
+
VALUES (?, ?, ?, ?, ?)
|
|
159762
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
159763
|
+
session_id = excluded.session_id,
|
|
159764
|
+
time_created = excluded.time_created,
|
|
159765
|
+
time_updated = excluded.time_updated,
|
|
159766
|
+
data = excluded.data`).run(summaryMsgId, args.sessionId, boundaryTime + 1, boundaryTime + 1, summaryMsgData);
|
|
159767
|
+
db.prepare(`INSERT INTO part (id, message_id, session_id, time_created, time_updated, data)
|
|
159768
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
159769
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
159770
|
+
message_id = excluded.message_id,
|
|
159771
|
+
session_id = excluded.session_id,
|
|
159772
|
+
time_created = excluded.time_created,
|
|
159773
|
+
time_updated = excluded.time_updated,
|
|
159774
|
+
data = excluded.data`).run(summaryPartId, summaryMsgId, args.sessionId, boundaryTime + 1, boundaryTime + 1, JSON.stringify({ type: "text", text: args.summaryText }));
|
|
159385
159775
|
})();
|
|
159386
159776
|
log(`[magic-context] compaction-marker: injected boundary at user msg ${boundary.id} (ordinal ~${args.endOrdinal}), summary msg ${summaryMsgId}`);
|
|
159387
159777
|
return {
|
|
@@ -159428,7 +159818,7 @@ function openOpenCodeDb() {
|
|
|
159428
159818
|
}
|
|
159429
159819
|
|
|
159430
159820
|
// ../plugin/src/features/magic-context/dreamer/task-executor.ts
|
|
159431
|
-
import { createHash as
|
|
159821
|
+
import { createHash as createHash13 } from "node:crypto";
|
|
159432
159822
|
import { existsSync as existsSync15 } from "node:fs";
|
|
159433
159823
|
|
|
159434
159824
|
// ../plugin/src/agents/dreamer.ts
|
|
@@ -159819,6 +160209,9 @@ function parseFields(json) {
|
|
|
159819
160209
|
} catch {}
|
|
159820
160210
|
return {};
|
|
159821
160211
|
}
|
|
160212
|
+
// ../plugin/src/config/schema/magic-context.ts
|
|
160213
|
+
import { homedir as homedir3 } from "node:os";
|
|
160214
|
+
|
|
159822
160215
|
// ../../node_modules/.bun/zod@4.4.3/node_modules/zod/v4/classic/external.js
|
|
159823
160216
|
var exports_external = {};
|
|
159824
160217
|
__export(exports_external, {
|
|
@@ -174342,8 +174735,18 @@ var HistorianConfigSchema = AgentOverrideConfigSchema.extend({
|
|
|
174342
174735
|
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
174736
|
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
174737
|
}).optional();
|
|
174738
|
+
var EmbeddingFallbackProviderSchema = exports_external.enum(["local", "openai-compatible", "off"]);
|
|
174739
|
+
function expandConfigPath(value) {
|
|
174740
|
+
const trimmed = value.trim();
|
|
174741
|
+
if (trimmed === "~")
|
|
174742
|
+
return homedir3();
|
|
174743
|
+
if (trimmed.startsWith("~/"))
|
|
174744
|
+
return `${homedir3()}/${trimmed.slice(2)}`;
|
|
174745
|
+
return trimmed;
|
|
174746
|
+
}
|
|
174345
174747
|
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."),
|
|
174748
|
+
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."),
|
|
174749
|
+
fallback_provider: EmbeddingFallbackProviderSchema.optional().describe("Fallback provider for the Synapse lane. Required when provider is 'synapse'; local, openai-compatible, and off are valid."),
|
|
174347
174750
|
model: exports_external.string().optional().describe("Embedding model name. Required for openai-compatible, ignored for local."),
|
|
174348
174751
|
endpoint: exports_external.string().optional().describe("API endpoint URL. Required when provider is openai-compatible."),
|
|
174349
174752
|
api_key: exports_external.string().optional().describe("API key for remote embedding provider (optional)"),
|
|
@@ -174352,14 +174755,15 @@ var BaseEmbeddingConfigSchema = exports_external.object({
|
|
|
174352
174755
|
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
174756
|
max_input_tokens: exports_external.number().int().positive().optional().describe("Optional maximum input tokens for chunk embeddings. Defaults conservatively to 512 when omitted.")
|
|
174354
174757
|
}).superRefine((data, ctx) => {
|
|
174355
|
-
|
|
174758
|
+
const validationProvider = data.provider === "synapse" ? data.fallback_provider : data.provider;
|
|
174759
|
+
if (validationProvider === "openai-compatible" && !data.endpoint?.trim()) {
|
|
174356
174760
|
ctx.addIssue({
|
|
174357
174761
|
code: "custom",
|
|
174358
174762
|
path: ["endpoint"],
|
|
174359
174763
|
message: "endpoint is required when embedding.provider is openai-compatible"
|
|
174360
174764
|
});
|
|
174361
174765
|
}
|
|
174362
|
-
if (
|
|
174766
|
+
if (validationProvider === "openai-compatible" && !data.model?.trim()) {
|
|
174363
174767
|
ctx.addIssue({
|
|
174364
174768
|
code: "custom",
|
|
174365
174769
|
path: ["model"],
|
|
@@ -174368,6 +174772,25 @@ var BaseEmbeddingConfigSchema = exports_external.object({
|
|
|
174368
174772
|
}
|
|
174369
174773
|
});
|
|
174370
174774
|
var EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data) => {
|
|
174775
|
+
if (data.provider === "synapse") {
|
|
174776
|
+
const model = data.model?.trim();
|
|
174777
|
+
const endpoint = data.endpoint?.trim();
|
|
174778
|
+
const apiKey = data.api_key?.trim();
|
|
174779
|
+
const inputType = data.input_type?.trim();
|
|
174780
|
+
const queryInputType = data.query_input_type?.trim();
|
|
174781
|
+
const truncate = data.truncate?.trim();
|
|
174782
|
+
return {
|
|
174783
|
+
provider: "synapse",
|
|
174784
|
+
...data.fallback_provider ? { fallback_provider: data.fallback_provider } : {},
|
|
174785
|
+
...model ? { model } : {},
|
|
174786
|
+
...endpoint ? { endpoint } : {},
|
|
174787
|
+
...apiKey ? { api_key: apiKey } : {},
|
|
174788
|
+
...inputType ? { input_type: inputType } : {},
|
|
174789
|
+
...queryInputType ? { query_input_type: queryInputType } : {},
|
|
174790
|
+
...truncate ? { truncate } : {},
|
|
174791
|
+
...data.max_input_tokens ? { max_input_tokens: data.max_input_tokens } : {}
|
|
174792
|
+
};
|
|
174793
|
+
}
|
|
174371
174794
|
if (data.provider === "local") {
|
|
174372
174795
|
return {
|
|
174373
174796
|
provider: "local",
|
|
@@ -174395,6 +174818,7 @@ var EmbeddingConfigSchema = BaseEmbeddingConfigSchema.transform((data) => {
|
|
|
174395
174818
|
});
|
|
174396
174819
|
var MagicContextConfigSchema = exports_external.object({
|
|
174397
174820
|
enabled: exports_external.boolean().default(true).describe("Enable magic context (default: true)"),
|
|
174821
|
+
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
174822
|
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
174823
|
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
174824
|
historian: HistorianConfigSchema.describe("Historian agent configuration (model, fallback_models, variant, temperature, maxTokens, permission, two_pass, etc.)"),
|
|
@@ -174431,6 +174855,12 @@ var MagicContextConfigSchema = exports_external.object({
|
|
|
174431
174855
|
provider: "local",
|
|
174432
174856
|
model: DEFAULT_LOCAL_EMBEDDING_MODEL
|
|
174433
174857
|
}).describe("Embedding provider configuration"),
|
|
174858
|
+
subc: exports_external.object({
|
|
174859
|
+
connection_file: exports_external.string().trim().min(1).transform(expandConfigPath).describe("Path to the owner-only subc connection file.")
|
|
174860
|
+
}).optional().describe("User-only Synapse daemon connection settings."),
|
|
174861
|
+
shadow_embedding: exports_external.object({
|
|
174862
|
+
enabled: exports_external.boolean().default(false).describe("Developer-only Synapse shadow embedding lane switch.")
|
|
174863
|
+
}).default({ enabled: false }).describe("Developer-only Synapse shadow embedding lane."),
|
|
174434
174864
|
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
174865
|
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
174866
|
shadow_transform: exports_external.object({
|
|
@@ -174480,7 +174910,7 @@ var MagicContextConfigSchema = exports_external.object({
|
|
|
174480
174910
|
init_logger();
|
|
174481
174911
|
|
|
174482
174912
|
// ../plugin/src/features/magic-context/compartment-chunk-embedding.ts
|
|
174483
|
-
import { createHash as
|
|
174913
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
174484
174914
|
init_logger();
|
|
174485
174915
|
|
|
174486
174916
|
// ../plugin/src/features/magic-context/recursive-text-splitter.ts
|
|
@@ -174692,7 +175122,7 @@ function parseCanonicalLineRange(line) {
|
|
|
174692
175122
|
return { start, end };
|
|
174693
175123
|
}
|
|
174694
175124
|
function hashChunkText(text) {
|
|
174695
|
-
return
|
|
175125
|
+
return createHash8("sha256").update(text).digest("hex");
|
|
174696
175126
|
}
|
|
174697
175127
|
function vectorBlob2(vector) {
|
|
174698
175128
|
return new Uint8Array(vector.buffer, vector.byteOffset, vector.byteLength);
|
|
@@ -175084,14 +175514,2634 @@ function cosineSimilarity(a, b) {
|
|
|
175084
175514
|
return denominator === 0 ? 0 : dotProduct / denominator;
|
|
175085
175515
|
}
|
|
175086
175516
|
|
|
175517
|
+
// ../plugin/src/features/magic-context/memory/embedding-synapse.ts
|
|
175518
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
175519
|
+
|
|
175520
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.4.1/node_modules/@cortexkit/subc-client/dist/client.js
|
|
175521
|
+
import { promises as fs4 } from "node:fs";
|
|
175522
|
+
import { debuglog } from "node:util";
|
|
175523
|
+
|
|
175524
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.4.1/node_modules/@cortexkit/subc-client/dist/auth.js
|
|
175525
|
+
import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
|
175526
|
+
var NONCE_LEN = 32;
|
|
175527
|
+
var MAX_AUTH_MESSAGE_LEN = 4096;
|
|
175528
|
+
var SERVER_PROOF_DOMAIN = "subc-server-v1";
|
|
175529
|
+
var CLIENT_AUTH_DOMAIN = "subc-client-v1";
|
|
175530
|
+
var DEFAULT_CLIENT_ROLE = "client";
|
|
175531
|
+
|
|
175532
|
+
class AuthError extends Error {
|
|
175533
|
+
}
|
|
175534
|
+
function computeProof(key, domain2, clientNonce, serverNonce, daemonId) {
|
|
175535
|
+
const mac3 = createHmac("sha256", Buffer.from(key));
|
|
175536
|
+
mac3.update(Buffer.from(domain2, "utf8"));
|
|
175537
|
+
mac3.update(Buffer.from(clientNonce));
|
|
175538
|
+
mac3.update(Buffer.from(serverNonce));
|
|
175539
|
+
mac3.update(Buffer.from(daemonId));
|
|
175540
|
+
return new Uint8Array(mac3.digest());
|
|
175541
|
+
}
|
|
175542
|
+
function constantTimeEq(a, b) {
|
|
175543
|
+
if (a.length !== b.length)
|
|
175544
|
+
return false;
|
|
175545
|
+
return timingSafeEqual(Buffer.from(a), Buffer.from(b));
|
|
175546
|
+
}
|
|
175547
|
+
async function writeMessage(sock, value, deadlineMs) {
|
|
175548
|
+
const json2 = Buffer.from(JSON.stringify(value), "utf8");
|
|
175549
|
+
if (json2.length > MAX_AUTH_MESSAGE_LEN) {
|
|
175550
|
+
throw new AuthError(`auth message too large: ${json2.length} > ${MAX_AUTH_MESSAGE_LEN}`);
|
|
175551
|
+
}
|
|
175552
|
+
const lenPrefix = new Uint8Array(4);
|
|
175553
|
+
new DataView(lenPrefix.buffer).setUint32(0, json2.length, true);
|
|
175554
|
+
await sock.write(lenPrefix, deadlineMs);
|
|
175555
|
+
await sock.write(json2, deadlineMs);
|
|
175556
|
+
}
|
|
175557
|
+
async function readMessage(sock, deadlineMs) {
|
|
175558
|
+
const lenBytes = await sock.readExact(4, deadlineMs);
|
|
175559
|
+
const len = new DataView(lenBytes.buffer, lenBytes.byteOffset, 4).getUint32(0, true);
|
|
175560
|
+
if (len > MAX_AUTH_MESSAGE_LEN) {
|
|
175561
|
+
throw new AuthError(`auth message too large: ${len} > ${MAX_AUTH_MESSAGE_LEN}`);
|
|
175562
|
+
}
|
|
175563
|
+
const body = len === 0 ? new Uint8Array(0) : await sock.readExact(len, deadlineMs);
|
|
175564
|
+
try {
|
|
175565
|
+
return JSON.parse(Buffer.from(body).toString("utf8"));
|
|
175566
|
+
} catch (err) {
|
|
175567
|
+
throw new AuthError(`auth message JSON decode failed: ${String(err)}`);
|
|
175568
|
+
}
|
|
175569
|
+
}
|
|
175570
|
+
async function authenticateClient(sock, conn, deadlineMs) {
|
|
175571
|
+
const clientNonce = new Uint8Array(randomBytes(NONCE_LEN));
|
|
175572
|
+
await writeMessage(sock, { client_nonce: Array.from(clientNonce), role: DEFAULT_CLIENT_ROLE }, deadlineMs);
|
|
175573
|
+
const proof = await readMessage(sock, deadlineMs);
|
|
175574
|
+
const serverNonce = Uint8Array.from(proof.server_nonce);
|
|
175575
|
+
const daemonId = Uint8Array.from(proof.daemon_id);
|
|
175576
|
+
const serverProof = Uint8Array.from(proof.server_proof);
|
|
175577
|
+
const expected = computeProof(conn.key, SERVER_PROOF_DOMAIN, clientNonce, serverNonce, daemonId);
|
|
175578
|
+
if (!constantTimeEq(expected, serverProof)) {
|
|
175579
|
+
throw new AuthError("server proof mismatch — wrong key or impostor daemon");
|
|
175580
|
+
}
|
|
175581
|
+
if (!constantTimeEq(daemonId, conn.daemonId)) {
|
|
175582
|
+
throw new AuthError("daemon id mismatch — connection file points at a different daemon");
|
|
175583
|
+
}
|
|
175584
|
+
const clientAuth = computeProof(conn.key, CLIENT_AUTH_DOMAIN, clientNonce, serverNonce, daemonId);
|
|
175585
|
+
await writeMessage(sock, { client_auth: Array.from(clientAuth) }, deadlineMs);
|
|
175586
|
+
}
|
|
175587
|
+
|
|
175588
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.4.1/node_modules/@cortexkit/subc-client/dist/connection-file.js
|
|
175589
|
+
import { promises as fs3 } from "node:fs";
|
|
175590
|
+
var SCHEMA_VERSION = 1;
|
|
175591
|
+
var MIN_KEY_LEN = 32;
|
|
175592
|
+
var DAEMON_ID_LEN = 16;
|
|
175593
|
+
|
|
175594
|
+
class ConnectionFileError extends Error {
|
|
175595
|
+
}
|
|
175596
|
+
function toBytes(value, field) {
|
|
175597
|
+
if (!Array.isArray(value) || value.some((n) => typeof n !== "number")) {
|
|
175598
|
+
throw new ConnectionFileError(`connection file field '${field}' must be a JSON array of bytes`);
|
|
175599
|
+
}
|
|
175600
|
+
return Uint8Array.from(value);
|
|
175601
|
+
}
|
|
175602
|
+
function validate(info) {
|
|
175603
|
+
if (info.schema !== SCHEMA_VERSION) {
|
|
175604
|
+
throw new ConnectionFileError(`unsupported connection file schema ${info.schema}; expected ${SCHEMA_VERSION}`);
|
|
175605
|
+
}
|
|
175606
|
+
if (info.endpoints.length === 0) {
|
|
175607
|
+
throw new ConnectionFileError("connection file must include at least one endpoint");
|
|
175608
|
+
}
|
|
175609
|
+
if (info.key.length < MIN_KEY_LEN) {
|
|
175610
|
+
throw new ConnectionFileError(`connection file key is too short: ${info.key.length} bytes, need at least ${MIN_KEY_LEN}`);
|
|
175611
|
+
}
|
|
175612
|
+
if (info.daemonId.length !== DAEMON_ID_LEN) {
|
|
175613
|
+
throw new ConnectionFileError(`connection file daemon_id must be ${DAEMON_ID_LEN} bytes, got ${info.daemonId.length}`);
|
|
175614
|
+
}
|
|
175615
|
+
}
|
|
175616
|
+
async function verifyOwnerOnly(path7) {
|
|
175617
|
+
if (process.platform === "win32")
|
|
175618
|
+
return;
|
|
175619
|
+
const stat = await fs3.stat(path7);
|
|
175620
|
+
const mode = stat.mode & 511;
|
|
175621
|
+
if ((mode & 63) !== 0) {
|
|
175622
|
+
throw new ConnectionFileError(`connection file ${path7} has insecure permissions 0o${mode.toString(8)}; expected owner-only 0600`);
|
|
175623
|
+
}
|
|
175624
|
+
}
|
|
175625
|
+
async function readConnectionFile(path7) {
|
|
175626
|
+
await verifyOwnerOnly(path7);
|
|
175627
|
+
const raw = await fs3.readFile(path7, "utf8");
|
|
175628
|
+
let parsed;
|
|
175629
|
+
try {
|
|
175630
|
+
parsed = JSON.parse(raw);
|
|
175631
|
+
} catch (err) {
|
|
175632
|
+
throw new ConnectionFileError(`connection file JSON read failed for ${path7}: ${String(err)}`);
|
|
175633
|
+
}
|
|
175634
|
+
const endpointsRaw = parsed.endpoints;
|
|
175635
|
+
if (!Array.isArray(endpointsRaw)) {
|
|
175636
|
+
throw new ConnectionFileError("connection file 'endpoints' must be an array");
|
|
175637
|
+
}
|
|
175638
|
+
const endpoints = endpointsRaw.map((e) => {
|
|
175639
|
+
const ep = e;
|
|
175640
|
+
if (typeof ep.host !== "string" || typeof ep.port !== "number") {
|
|
175641
|
+
throw new ConnectionFileError("connection file endpoint must be { host: string, port: number }");
|
|
175642
|
+
}
|
|
175643
|
+
return { host: ep.host, port: ep.port };
|
|
175644
|
+
});
|
|
175645
|
+
const info = {
|
|
175646
|
+
schema: parsed.schema,
|
|
175647
|
+
endpoints,
|
|
175648
|
+
key: toBytes(parsed.key, "key"),
|
|
175649
|
+
daemonId: toBytes(parsed.daemon_id, "daemon_id"),
|
|
175650
|
+
pid: parsed.pid,
|
|
175651
|
+
daemonVer: parsed.daemon_ver ?? ""
|
|
175652
|
+
};
|
|
175653
|
+
validate(info);
|
|
175654
|
+
return info;
|
|
175655
|
+
}
|
|
175656
|
+
|
|
175657
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.4.1/node_modules/@cortexkit/subc-client/dist/envelope.js
|
|
175658
|
+
var PROTOCOL_VERSION = 2;
|
|
175659
|
+
var HEADER_LEN = 21;
|
|
175660
|
+
var FROZEN_PREFIX_LEN = 5;
|
|
175661
|
+
var MAX_FRAME_BODY_LEN = 64 * 1024 * 1024;
|
|
175662
|
+
var FrameType;
|
|
175663
|
+
(function(FrameType2) {
|
|
175664
|
+
FrameType2[FrameType2["Request"] = 0] = "Request";
|
|
175665
|
+
FrameType2[FrameType2["Response"] = 1] = "Response";
|
|
175666
|
+
FrameType2[FrameType2["Push"] = 2] = "Push";
|
|
175667
|
+
FrameType2[FrameType2["StreamData"] = 3] = "StreamData";
|
|
175668
|
+
FrameType2[FrameType2["StreamEnd"] = 4] = "StreamEnd";
|
|
175669
|
+
FrameType2[FrameType2["Error"] = 5] = "Error";
|
|
175670
|
+
FrameType2[FrameType2["Cancel"] = 6] = "Cancel";
|
|
175671
|
+
FrameType2[FrameType2["Ping"] = 7] = "Ping";
|
|
175672
|
+
FrameType2[FrameType2["Pong"] = 8] = "Pong";
|
|
175673
|
+
FrameType2[FrameType2["Hello"] = 9] = "Hello";
|
|
175674
|
+
FrameType2[FrameType2["HelloAck"] = 10] = "HelloAck";
|
|
175675
|
+
FrameType2[FrameType2["Goodbye"] = 11] = "Goodbye";
|
|
175676
|
+
})(FrameType || (FrameType = {}));
|
|
175677
|
+
var FRAME_TYPE_MAX = FrameType.Goodbye;
|
|
175678
|
+
function isPureHeader(ty) {
|
|
175679
|
+
return ty === FrameType.Cancel || ty === FrameType.Ping || ty === FrameType.Pong || ty === FrameType.Goodbye;
|
|
175680
|
+
}
|
|
175681
|
+
var Priority;
|
|
175682
|
+
(function(Priority2) {
|
|
175683
|
+
Priority2[Priority2["Passive"] = 0] = "Passive";
|
|
175684
|
+
Priority2[Priority2["Interactive"] = 1] = "Interactive";
|
|
175685
|
+
Priority2[Priority2["Background"] = 2] = "Background";
|
|
175686
|
+
})(Priority || (Priority = {}));
|
|
175687
|
+
var AdmissionClass;
|
|
175688
|
+
(function(AdmissionClass2) {
|
|
175689
|
+
AdmissionClass2[AdmissionClass2["Normal"] = 0] = "Normal";
|
|
175690
|
+
AdmissionClass2[AdmissionClass2["Expedite"] = 1] = "Expedite";
|
|
175691
|
+
AdmissionClass2[AdmissionClass2["Sheddable"] = 2] = "Sheddable";
|
|
175692
|
+
})(AdmissionClass || (AdmissionClass = {}));
|
|
175693
|
+
var FLAG_BINARY = 1;
|
|
175694
|
+
var FLAG_PRIORITY_MASK = 6;
|
|
175695
|
+
var FLAG_PRIORITY_SHIFT = 1;
|
|
175696
|
+
var FLAG_LAST = 8;
|
|
175697
|
+
var FLAG_ADMISSION_MASK = 48;
|
|
175698
|
+
var FLAG_ADMISSION_SHIFT = 4;
|
|
175699
|
+
var FLAG_RESERVED_MASK = 192;
|
|
175700
|
+
function buildFlags(binary, priority, last, admissionClass = AdmissionClass.Normal) {
|
|
175701
|
+
let flags = 0;
|
|
175702
|
+
if (binary)
|
|
175703
|
+
flags |= FLAG_BINARY;
|
|
175704
|
+
flags |= priority << FLAG_PRIORITY_SHIFT;
|
|
175705
|
+
if (last)
|
|
175706
|
+
flags |= FLAG_LAST;
|
|
175707
|
+
flags |= admissionClass << FLAG_ADMISSION_SHIFT;
|
|
175708
|
+
return flags;
|
|
175709
|
+
}
|
|
175710
|
+
function encodeHeader(header) {
|
|
175711
|
+
const buffer2 = new Uint8Array(HEADER_LEN);
|
|
175712
|
+
const view = new DataView(buffer2.buffer);
|
|
175713
|
+
view.setUint32(0, header.len, true);
|
|
175714
|
+
buffer2[4] = header.ver;
|
|
175715
|
+
buffer2[5] = header.ty;
|
|
175716
|
+
buffer2[6] = header.flags;
|
|
175717
|
+
view.setUint16(7, header.channel, true);
|
|
175718
|
+
view.setUint32(9, header.epoch, true);
|
|
175719
|
+
view.setBigUint64(13, header.corr, true);
|
|
175720
|
+
return buffer2;
|
|
175721
|
+
}
|
|
175722
|
+
|
|
175723
|
+
class DecodeError extends Error {
|
|
175724
|
+
code;
|
|
175725
|
+
constructor(message, code) {
|
|
175726
|
+
super(message);
|
|
175727
|
+
this.code = code;
|
|
175728
|
+
this.name = "DecodeError";
|
|
175729
|
+
}
|
|
175730
|
+
}
|
|
175731
|
+
function decodeHeader(bytes) {
|
|
175732
|
+
if (bytes.length < FROZEN_PREFIX_LEN) {
|
|
175733
|
+
throw new DecodeError(`header shorter than frozen prefix: have ${bytes.length} bytes`, "too_short_for_prefix");
|
|
175734
|
+
}
|
|
175735
|
+
const ver = bytes[4];
|
|
175736
|
+
if (ver !== PROTOCOL_VERSION)
|
|
175737
|
+
throw new DecodeError(`unsupported envelope version ${ver}`, "unsupported_version");
|
|
175738
|
+
if (bytes.length < HEADER_LEN) {
|
|
175739
|
+
throw new DecodeError(`header too short for version: have ${bytes.length} bytes, need ${HEADER_LEN}`, "too_short_for_header");
|
|
175740
|
+
}
|
|
175741
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
175742
|
+
const len = view.getUint32(0, true);
|
|
175743
|
+
const typeByte = bytes[5];
|
|
175744
|
+
if (typeByte > FRAME_TYPE_MAX)
|
|
175745
|
+
throw new DecodeError(`unknown frame type byte ${typeByte}`, "unknown_frame_type");
|
|
175746
|
+
const ty = typeByte;
|
|
175747
|
+
const flags = bytes[6];
|
|
175748
|
+
if ((flags & FLAG_RESERVED_MASK) !== 0) {
|
|
175749
|
+
throw new DecodeError(`reserved flag bits set in flags 0b${flags.toString(2).padStart(8, "0")}`, "reserved_flag_bits");
|
|
175750
|
+
}
|
|
175751
|
+
if ((flags & FLAG_PRIORITY_MASK) >> FLAG_PRIORITY_SHIFT === 3) {
|
|
175752
|
+
throw new DecodeError(`reserved priority bits set in flags 0b${flags.toString(2).padStart(8, "0")}`, "reserved_priority_bits");
|
|
175753
|
+
}
|
|
175754
|
+
const admission = (flags & FLAG_ADMISSION_MASK) >> FLAG_ADMISSION_SHIFT;
|
|
175755
|
+
if (admission === 3) {
|
|
175756
|
+
throw new DecodeError(`reserved admission class set in flags 0b${flags.toString(2).padStart(8, "0")}`, "reserved_admission_class");
|
|
175757
|
+
}
|
|
175758
|
+
if (admission === AdmissionClass.Sheddable && ty !== FrameType.Push && ty !== FrameType.StreamData) {
|
|
175759
|
+
throw new DecodeError(`SHEDDABLE admission class is illegal on ${FrameType[ty]} in flags 0b${flags.toString(2).padStart(8, "0")}`, "sheddable_illegal_frame_type");
|
|
175760
|
+
}
|
|
175761
|
+
const channel = view.getUint16(7, true);
|
|
175762
|
+
const epoch = view.getUint32(9, true);
|
|
175763
|
+
if (channel === 0 && epoch !== 0) {
|
|
175764
|
+
throw new DecodeError(`control channel carried nonzero epoch ${epoch}`, "nonzero_epoch_on_control_channel");
|
|
175765
|
+
}
|
|
175766
|
+
if (isPureHeader(ty) && len !== 0) {
|
|
175767
|
+
throw new DecodeError(`pure-header frame ${FrameType[ty]} declared non-zero body length ${len}`, "pure_header_frame_with_body");
|
|
175768
|
+
}
|
|
175769
|
+
return { len, ver, ty, flags, channel, epoch, corr: view.getBigUint64(13, true) };
|
|
175770
|
+
}
|
|
175771
|
+
function buildFrame(ty, flags, channel, epoch, corr, body) {
|
|
175772
|
+
return buildFrameWithVersion(PROTOCOL_VERSION, ty, flags, channel, epoch, corr, body);
|
|
175773
|
+
}
|
|
175774
|
+
function buildFrameWithVersion(ver, ty, flags, channel, epoch, corr, body) {
|
|
175775
|
+
if (body.length > MAX_FRAME_BODY_LEN) {
|
|
175776
|
+
throw new DecodeError(`frame body ${body.length} exceeds max ${MAX_FRAME_BODY_LEN}`, "frame_body_too_large");
|
|
175777
|
+
}
|
|
175778
|
+
const header = { len: body.length, ver, ty, flags, channel, epoch, corr };
|
|
175779
|
+
decodeHeader(encodeHeader(header));
|
|
175780
|
+
return { header, body };
|
|
175781
|
+
}
|
|
175782
|
+
function encodeFrame(frame) {
|
|
175783
|
+
if (frame.header.len !== frame.body.length) {
|
|
175784
|
+
throw new DecodeError(`frame header length ${frame.header.len} does not match body length ${frame.body.length}`, "frame_length_mismatch");
|
|
175785
|
+
}
|
|
175786
|
+
const header = encodeHeader(frame.header);
|
|
175787
|
+
const output = new Uint8Array(header.length + frame.body.length);
|
|
175788
|
+
output.set(header, 0);
|
|
175789
|
+
output.set(frame.body, header.length);
|
|
175790
|
+
return output;
|
|
175791
|
+
}
|
|
175792
|
+
|
|
175793
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.4.1/node_modules/@cortexkit/subc-client/dist/route-handle.js
|
|
175794
|
+
var connectionToken = new WeakMap;
|
|
175795
|
+
|
|
175796
|
+
class RouteHandle {
|
|
175797
|
+
channel;
|
|
175798
|
+
epoch;
|
|
175799
|
+
constructor(channel, epoch, token) {
|
|
175800
|
+
if (!Number.isInteger(channel) || channel <= 0 || channel > 65535) {
|
|
175801
|
+
throw new RangeError(`route channel must be an integer in 1..65535, got ${channel}`);
|
|
175802
|
+
}
|
|
175803
|
+
if (!Number.isInteger(epoch) || epoch <= 0 || epoch > 4294967295) {
|
|
175804
|
+
throw new RangeError(`route epoch must be an integer in 1..4294967295, got ${epoch}`);
|
|
175805
|
+
}
|
|
175806
|
+
this.channel = channel;
|
|
175807
|
+
this.epoch = epoch;
|
|
175808
|
+
connectionToken.set(this, token);
|
|
175809
|
+
Object.freeze(this);
|
|
175810
|
+
}
|
|
175811
|
+
static create(channel, epoch, token) {
|
|
175812
|
+
return new RouteHandle(channel, epoch, token);
|
|
175813
|
+
}
|
|
175814
|
+
}
|
|
175815
|
+
function createRouteHandle(channel, epoch, token) {
|
|
175816
|
+
const factory = RouteHandle;
|
|
175817
|
+
return factory.create(channel, epoch, token);
|
|
175818
|
+
}
|
|
175819
|
+
|
|
175820
|
+
class StaleRouteHandleError extends Error {
|
|
175821
|
+
handle;
|
|
175822
|
+
code = "stale_route_handle";
|
|
175823
|
+
constructor(handle) {
|
|
175824
|
+
super(`route handle (${handle.channel}, ${handle.epoch}) is not live on the current connection`);
|
|
175825
|
+
this.handle = handle;
|
|
175826
|
+
this.name = "StaleRouteHandleError";
|
|
175827
|
+
}
|
|
175828
|
+
}
|
|
175829
|
+
function newConnectionToken() {
|
|
175830
|
+
return Object.freeze({});
|
|
175831
|
+
}
|
|
175832
|
+
function belongsToConnection(handle, token) {
|
|
175833
|
+
return connectionToken.get(handle) === token;
|
|
175834
|
+
}
|
|
175835
|
+
function sameRouteHandle(left, right) {
|
|
175836
|
+
return left === right;
|
|
175837
|
+
}
|
|
175838
|
+
|
|
175839
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.4.1/node_modules/@cortexkit/subc-client/dist/socket.js
|
|
175840
|
+
import net from "node:net";
|
|
175841
|
+
class SocketClosedError extends Error {
|
|
175842
|
+
}
|
|
175843
|
+
|
|
175844
|
+
class SocketTimeoutError extends Error {
|
|
175845
|
+
}
|
|
175846
|
+
|
|
175847
|
+
class SocketWriteNotQueuedError extends Error {
|
|
175848
|
+
cause;
|
|
175849
|
+
constructor(message, cause) {
|
|
175850
|
+
super(message);
|
|
175851
|
+
this.cause = cause;
|
|
175852
|
+
}
|
|
175853
|
+
}
|
|
175854
|
+
|
|
175855
|
+
class SocketWriteQueuedError extends Error {
|
|
175856
|
+
cause;
|
|
175857
|
+
constructor(message, cause) {
|
|
175858
|
+
super(message);
|
|
175859
|
+
this.cause = cause;
|
|
175860
|
+
}
|
|
175861
|
+
}
|
|
175862
|
+
|
|
175863
|
+
class SubcSocket {
|
|
175864
|
+
sock;
|
|
175865
|
+
chunks = [];
|
|
175866
|
+
buffered = 0;
|
|
175867
|
+
waiter = null;
|
|
175868
|
+
closedErr = null;
|
|
175869
|
+
bufferedBytes() {
|
|
175870
|
+
return this.buffered;
|
|
175871
|
+
}
|
|
175872
|
+
constructor(sock) {
|
|
175873
|
+
this.sock = sock;
|
|
175874
|
+
sock.on("data", (chunk) => {
|
|
175875
|
+
this.chunks.push(chunk);
|
|
175876
|
+
this.buffered += chunk.length;
|
|
175877
|
+
this.tryServe();
|
|
175878
|
+
});
|
|
175879
|
+
const fail = (err) => {
|
|
175880
|
+
if (!this.closedErr)
|
|
175881
|
+
this.closedErr = err;
|
|
175882
|
+
this.tryServe();
|
|
175883
|
+
};
|
|
175884
|
+
sock.on("error", (err) => fail(err instanceof Error ? err : new Error(String(err))));
|
|
175885
|
+
sock.on("end", () => fail(new SocketClosedError("subc closed the connection")));
|
|
175886
|
+
sock.on("close", () => fail(new SocketClosedError("subc connection closed")));
|
|
175887
|
+
}
|
|
175888
|
+
localPort() {
|
|
175889
|
+
return this.sock.localPort ?? null;
|
|
175890
|
+
}
|
|
175891
|
+
static connect(host, port, deadlineMs) {
|
|
175892
|
+
return new Promise((resolve, reject) => {
|
|
175893
|
+
const sock = net.connect({ host, port });
|
|
175894
|
+
sock.setNoDelay(true);
|
|
175895
|
+
const timer = setTimeout(() => {
|
|
175896
|
+
sock.destroy();
|
|
175897
|
+
reject(new SocketTimeoutError(`timed out connecting to ${host}:${port}`));
|
|
175898
|
+
}, Math.max(0, deadlineMs - Date.now()));
|
|
175899
|
+
sock.once("connect", () => {
|
|
175900
|
+
clearTimeout(timer);
|
|
175901
|
+
resolve(new SubcSocket(sock));
|
|
175902
|
+
});
|
|
175903
|
+
sock.once("error", (err) => {
|
|
175904
|
+
clearTimeout(timer);
|
|
175905
|
+
reject(err);
|
|
175906
|
+
});
|
|
175907
|
+
});
|
|
175908
|
+
}
|
|
175909
|
+
async readFrame(headerDeadlineMs, bodyDeadline, onHeader) {
|
|
175910
|
+
const prefix = await this.readExact(FROZEN_PREFIX_LEN, headerDeadlineMs);
|
|
175911
|
+
const version2 = prefix[4];
|
|
175912
|
+
if (version2 !== PROTOCOL_VERSION)
|
|
175913
|
+
throw new DecodeError(`unsupported envelope version ${version2}`, "unsupported_version");
|
|
175914
|
+
const remainder = await this.readExact(HEADER_LEN - FROZEN_PREFIX_LEN, headerDeadlineMs);
|
|
175915
|
+
const headerBytes = new Uint8Array(HEADER_LEN);
|
|
175916
|
+
headerBytes.set(prefix);
|
|
175917
|
+
headerBytes.set(remainder, FROZEN_PREFIX_LEN);
|
|
175918
|
+
const header = decodeHeader(headerBytes);
|
|
175919
|
+
if (header.len > MAX_FRAME_BODY_LEN) {
|
|
175920
|
+
throw new DecodeError(`frame body ${header.len} exceeds max ${MAX_FRAME_BODY_LEN}`, "frame_body_too_large");
|
|
175921
|
+
}
|
|
175922
|
+
onHeader?.();
|
|
175923
|
+
const bodyDeadlineMs = typeof bodyDeadline === "number" ? bodyDeadline : Date.now() + bodyDeadline.afterHeaderMs;
|
|
175924
|
+
const body = header.len === 0 ? new Uint8Array(0) : await this.readExact(header.len, bodyDeadlineMs);
|
|
175925
|
+
return { header, body };
|
|
175926
|
+
}
|
|
175927
|
+
readExact(n, deadlineMs) {
|
|
175928
|
+
if (this.waiter) {
|
|
175929
|
+
return Promise.reject(new Error("concurrent readExact is not supported"));
|
|
175930
|
+
}
|
|
175931
|
+
if (n === 0)
|
|
175932
|
+
return Promise.resolve(new Uint8Array(0));
|
|
175933
|
+
return new Promise((resolve, reject) => {
|
|
175934
|
+
let timer = null;
|
|
175935
|
+
if (Number.isFinite(deadlineMs)) {
|
|
175936
|
+
const remaining = deadlineMs - Date.now();
|
|
175937
|
+
if (remaining <= 0) {
|
|
175938
|
+
reject(new SocketTimeoutError(`timed out waiting for ${n} bytes`));
|
|
175939
|
+
return;
|
|
175940
|
+
}
|
|
175941
|
+
timer = setTimeout(() => {
|
|
175942
|
+
this.waiter = null;
|
|
175943
|
+
reject(new SocketTimeoutError(`timed out waiting for ${n} bytes`));
|
|
175944
|
+
}, remaining);
|
|
175945
|
+
}
|
|
175946
|
+
this.waiter = { need: n, resolve, reject, timer };
|
|
175947
|
+
this.tryServe();
|
|
175948
|
+
});
|
|
175949
|
+
}
|
|
175950
|
+
async write(bytes, deadlineMs) {
|
|
175951
|
+
try {
|
|
175952
|
+
await this.writeTracked(bytes, deadlineMs).completed;
|
|
175953
|
+
} catch (err) {
|
|
175954
|
+
if (err instanceof SocketWriteNotQueuedError || err instanceof SocketWriteQueuedError) {
|
|
175955
|
+
throw err.cause ?? err;
|
|
175956
|
+
}
|
|
175957
|
+
throw err;
|
|
175958
|
+
}
|
|
175959
|
+
}
|
|
175960
|
+
writeTracked(bytes, deadlineMs) {
|
|
175961
|
+
if (this.closedErr) {
|
|
175962
|
+
return {
|
|
175963
|
+
queued: false,
|
|
175964
|
+
completed: Promise.reject(new SocketWriteNotQueuedError("subc socket was closed before bytes could be queued", this.closedErr))
|
|
175965
|
+
};
|
|
175966
|
+
}
|
|
175967
|
+
let queued = false;
|
|
175968
|
+
let settled = false;
|
|
175969
|
+
let timer = null;
|
|
175970
|
+
const completed = new Promise((resolve, reject) => {
|
|
175971
|
+
const settle = (run) => {
|
|
175972
|
+
if (settled)
|
|
175973
|
+
return;
|
|
175974
|
+
settled = true;
|
|
175975
|
+
if (timer)
|
|
175976
|
+
clearTimeout(timer);
|
|
175977
|
+
run();
|
|
175978
|
+
};
|
|
175979
|
+
const remaining = deadlineMs - Date.now();
|
|
175980
|
+
if (remaining <= 0) {
|
|
175981
|
+
settle(() => reject(new SocketWriteNotQueuedError("timed out before bytes could be queued to subc", new SocketTimeoutError("timed out writing to subc"))));
|
|
175982
|
+
return;
|
|
175983
|
+
}
|
|
175984
|
+
timer = setTimeout(() => {
|
|
175985
|
+
const timeout = new SocketTimeoutError("timed out writing to subc");
|
|
175986
|
+
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)));
|
|
175987
|
+
}, remaining);
|
|
175988
|
+
try {
|
|
175989
|
+
this.sock.write(Buffer.from(bytes), (err) => {
|
|
175990
|
+
settle(() => {
|
|
175991
|
+
if (err) {
|
|
175992
|
+
reject(new SocketWriteQueuedError("subc socket reported a write error after bytes were handed to the socket", err instanceof Error ? err : new Error(String(err))));
|
|
175993
|
+
} else {
|
|
175994
|
+
resolve();
|
|
175995
|
+
}
|
|
175996
|
+
});
|
|
175997
|
+
});
|
|
175998
|
+
queued = true;
|
|
175999
|
+
} catch (err) {
|
|
176000
|
+
settle(() => reject(new SocketWriteNotQueuedError("subc socket write threw before bytes could be queued", err instanceof Error ? err : new Error(String(err)))));
|
|
176001
|
+
}
|
|
176002
|
+
});
|
|
176003
|
+
return { queued, completed };
|
|
176004
|
+
}
|
|
176005
|
+
close() {
|
|
176006
|
+
this.sock.destroy();
|
|
176007
|
+
}
|
|
176008
|
+
tryServe() {
|
|
176009
|
+
const w = this.waiter;
|
|
176010
|
+
if (!w)
|
|
176011
|
+
return;
|
|
176012
|
+
if (this.buffered >= w.need) {
|
|
176013
|
+
const out = this.take(w.need);
|
|
176014
|
+
this.waiter = null;
|
|
176015
|
+
if (w.timer)
|
|
176016
|
+
clearTimeout(w.timer);
|
|
176017
|
+
w.resolve(out);
|
|
176018
|
+
return;
|
|
176019
|
+
}
|
|
176020
|
+
if (this.closedErr) {
|
|
176021
|
+
this.waiter = null;
|
|
176022
|
+
if (w.timer)
|
|
176023
|
+
clearTimeout(w.timer);
|
|
176024
|
+
w.reject(this.closedErr);
|
|
176025
|
+
}
|
|
176026
|
+
}
|
|
176027
|
+
take(n) {
|
|
176028
|
+
const out = Buffer.allocUnsafe(n);
|
|
176029
|
+
let off = 0;
|
|
176030
|
+
while (off < n) {
|
|
176031
|
+
const head = this.chunks[0];
|
|
176032
|
+
const want = n - off;
|
|
176033
|
+
if (head.length <= want) {
|
|
176034
|
+
head.copy(out, off);
|
|
176035
|
+
off += head.length;
|
|
176036
|
+
this.chunks.shift();
|
|
176037
|
+
} else {
|
|
176038
|
+
head.copy(out, off, 0, want);
|
|
176039
|
+
this.chunks[0] = head.subarray(want);
|
|
176040
|
+
off += want;
|
|
176041
|
+
}
|
|
176042
|
+
}
|
|
176043
|
+
this.buffered -= n;
|
|
176044
|
+
return out;
|
|
176045
|
+
}
|
|
176046
|
+
}
|
|
176047
|
+
|
|
176048
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.4.1/node_modules/@cortexkit/subc-client/dist/client.js
|
|
176049
|
+
var debug = debuglog("subc-client");
|
|
176050
|
+
var DEFAULT_HANDSHAKE_TIMEOUT_MS = 1e4;
|
|
176051
|
+
var DEFAULT_REQUEST_TIMEOUT_MS = 30000;
|
|
176052
|
+
var TIMEOUT_ARBITRATION_GRACE_MS = 50;
|
|
176053
|
+
var REQUEST_DEADLINE_MARKER = "request_deadline";
|
|
176054
|
+
var DEADLINE_NO_DROP_CODE = "deadline_exceeded_no_drop_observed";
|
|
176055
|
+
var ROUTE_OPEN_RETRY_DEADLINE_MS = 30000;
|
|
176056
|
+
var BODY_READ_TIMEOUT_MS = 30000;
|
|
176057
|
+
var EMPTY_BODY = new Uint8Array(0);
|
|
176058
|
+
var DEFAULT_MANAGED_TARGET_KIND = "management_surface";
|
|
176059
|
+
var SUBC_MODULE_ID_ENV = "SUBC_MODULE_ID";
|
|
176060
|
+
var SUBC_LAUNCH_NONCE_ENV = "SUBC_LAUNCH_NONCE";
|
|
176061
|
+
var DEFAULT_RECONNECT_BACKOFF = {
|
|
176062
|
+
baseMs: 100,
|
|
176063
|
+
capMs: 2000,
|
|
176064
|
+
maxAttempts: 6
|
|
176065
|
+
};
|
|
176066
|
+
|
|
176067
|
+
class SubcCallError extends Error {
|
|
176068
|
+
kind;
|
|
176069
|
+
code;
|
|
176070
|
+
cause;
|
|
176071
|
+
constructor(kind, message, code, cause) {
|
|
176072
|
+
super(message);
|
|
176073
|
+
this.kind = kind;
|
|
176074
|
+
this.code = code;
|
|
176075
|
+
this.cause = cause;
|
|
176076
|
+
this.name = "SubcCallError";
|
|
176077
|
+
}
|
|
176078
|
+
}
|
|
176079
|
+
|
|
176080
|
+
class SubcError extends Error {
|
|
176081
|
+
code;
|
|
176082
|
+
constructor(message, code) {
|
|
176083
|
+
super(message);
|
|
176084
|
+
this.code = code;
|
|
176085
|
+
}
|
|
176086
|
+
}
|
|
176087
|
+
|
|
176088
|
+
class SubcClient {
|
|
176089
|
+
sock;
|
|
176090
|
+
currentConn;
|
|
176091
|
+
opts;
|
|
176092
|
+
nextCorr = 1n;
|
|
176093
|
+
pending = new Map;
|
|
176094
|
+
lateResponses = new Map;
|
|
176095
|
+
routes = new Map;
|
|
176096
|
+
liveRoutes = new Map;
|
|
176097
|
+
connectionToken = newConnectionToken();
|
|
176098
|
+
ingressEpochDropCount = 0;
|
|
176099
|
+
closedErr = null;
|
|
176100
|
+
closeStarted = false;
|
|
176101
|
+
reconnecting = null;
|
|
176102
|
+
generation = 1;
|
|
176103
|
+
readerActive = false;
|
|
176104
|
+
constructor(sock, currentConn, opts) {
|
|
176105
|
+
this.sock = sock;
|
|
176106
|
+
this.currentConn = currentConn;
|
|
176107
|
+
this.opts = opts;
|
|
176108
|
+
this.readLoop(sock, this.generation);
|
|
176109
|
+
}
|
|
176110
|
+
get conn() {
|
|
176111
|
+
return this.currentConn;
|
|
176112
|
+
}
|
|
176113
|
+
static async connect(opts) {
|
|
176114
|
+
const normalized = normalizeConnectOptions(opts);
|
|
176115
|
+
const opened = await SubcClient.openConnection(normalized);
|
|
176116
|
+
return new SubcClient(opened.sock, opened.conn, normalized);
|
|
176117
|
+
}
|
|
176118
|
+
async catalogList(moduleId) {
|
|
176119
|
+
const body = this.encode(moduleId === undefined ? { op: "catalog.list" } : { op: "catalog.list", module_id: moduleId });
|
|
176120
|
+
const reply = await this.controlRpc(body);
|
|
176121
|
+
const parsed = this.parseJson(reply);
|
|
176122
|
+
return parsed.modules ?? [];
|
|
176123
|
+
}
|
|
176124
|
+
async routeOpen(target, identity, opts = {}) {
|
|
176125
|
+
const consumerIdentity = routeOpenConsumerIdentity(opts);
|
|
176126
|
+
const consumerCapabilities = opts.consumerCapabilities;
|
|
176127
|
+
const body = this.encode({
|
|
176128
|
+
op: "route.open",
|
|
176129
|
+
target,
|
|
176130
|
+
identity,
|
|
176131
|
+
...consumerIdentity ? { consumer_identity: consumerIdentity } : {},
|
|
176132
|
+
...consumerCapabilities !== undefined ? { consumer_capabilities: consumerCapabilities } : {}
|
|
176133
|
+
});
|
|
176134
|
+
let installed = null;
|
|
176135
|
+
const install = (frame) => {
|
|
176136
|
+
if (frame.header.ty !== FrameType.Response)
|
|
176137
|
+
return true;
|
|
176138
|
+
const parsed = this.parseJson(frame);
|
|
176139
|
+
if (typeof parsed.route_channel !== "number" || typeof parsed.route_epoch !== "number") {
|
|
176140
|
+
throw new SubcError(`route.open returned no route handle: ${JSON.stringify(parsed)}`);
|
|
176141
|
+
}
|
|
176142
|
+
installed = this.installRoute(parsed.route_channel, parsed.route_epoch);
|
|
176143
|
+
return true;
|
|
176144
|
+
};
|
|
176145
|
+
const closeLateRoute = (frame) => {
|
|
176146
|
+
if (frame.header.ty !== FrameType.Response)
|
|
176147
|
+
return;
|
|
176148
|
+
try {
|
|
176149
|
+
const parsed = this.parseJson(frame);
|
|
176150
|
+
if (typeof parsed.route_channel !== "number" || typeof parsed.route_epoch !== "number")
|
|
176151
|
+
return;
|
|
176152
|
+
const lateHandle = this.installRoute(parsed.route_channel, parsed.route_epoch);
|
|
176153
|
+
this.failHandle(lateHandle, new SubcError("late route.open was closed", "route_closed"));
|
|
176154
|
+
this.liveRoutes.delete(lateHandle.channel);
|
|
176155
|
+
this.sendRouteGoodbye(lateHandle, true);
|
|
176156
|
+
} catch {
|
|
176157
|
+
this.closeConnectionAfterCleanupFailure();
|
|
176158
|
+
}
|
|
176159
|
+
};
|
|
176160
|
+
await this.controlRpc(body, install, closeLateRoute);
|
|
176161
|
+
if (!installed)
|
|
176162
|
+
throw new SubcError("route.open response was not installed");
|
|
176163
|
+
return installed;
|
|
176164
|
+
}
|
|
176165
|
+
async request(handle, body, opts = {}) {
|
|
176166
|
+
this.assertLiveHandle(handle);
|
|
176167
|
+
const bytes = body instanceof Uint8Array ? body : this.encode(body);
|
|
176168
|
+
const priority = opts.priority ?? Priority.Interactive;
|
|
176169
|
+
const admission = opts.admissionClass ?? AdmissionClass.Normal;
|
|
176170
|
+
const reply = await this.send(handle, bytes, priority, admission, opts.timeoutMs, opts.onProgress);
|
|
176171
|
+
return this.parseJson(reply);
|
|
176172
|
+
}
|
|
176173
|
+
async call(moduleId, method, params, opts = {}) {
|
|
176174
|
+
const body = params === undefined ? { method } : { method, params };
|
|
176175
|
+
let retriedUnknownChannel = false;
|
|
176176
|
+
for (;; ) {
|
|
176177
|
+
const routeHandle = await this.cachedRouteHandle(moduleId, opts);
|
|
176178
|
+
try {
|
|
176179
|
+
return await this.managedRequest(routeHandle, body, opts);
|
|
176180
|
+
} catch (err) {
|
|
176181
|
+
if (!(err instanceof SubcCallError))
|
|
176182
|
+
throw this.terminalCallError("managed call failed", err);
|
|
176183
|
+
if (err.code === "unknown_channel" && !retriedUnknownChannel && !this.closeStarted) {
|
|
176184
|
+
retriedUnknownChannel = true;
|
|
176185
|
+
this.evictRouteHandle(routeHandle);
|
|
176186
|
+
continue;
|
|
176187
|
+
}
|
|
176188
|
+
if (err.kind === "not_sent") {
|
|
176189
|
+
try {
|
|
176190
|
+
await this.reconnectAfterDrop(err);
|
|
176191
|
+
} catch (reconnectErr) {
|
|
176192
|
+
throw this.notSentRecoveryError("managed call was not sent", reconnectErr);
|
|
176193
|
+
}
|
|
176194
|
+
continue;
|
|
176195
|
+
}
|
|
176196
|
+
if (err.kind === "outcome_unknown" && err.code !== DEADLINE_NO_DROP_CODE) {
|
|
176197
|
+
this.scheduleReconnectAfterDrop(err);
|
|
176198
|
+
}
|
|
176199
|
+
throw err;
|
|
176200
|
+
}
|
|
176201
|
+
}
|
|
176202
|
+
}
|
|
176203
|
+
subscribe(handle, body, onEvent, opts = {}) {
|
|
176204
|
+
this.assertLiveHandle(handle);
|
|
176205
|
+
const bytes = body instanceof Uint8Array ? body : this.encode(body);
|
|
176206
|
+
const priority = opts.priority ?? Priority.Interactive;
|
|
176207
|
+
const admission = opts.admissionClass ?? AdmissionClass.Normal;
|
|
176208
|
+
const corr = this.allocateCorr();
|
|
176209
|
+
const key = pendingKey(handle, corr);
|
|
176210
|
+
const closed = new Promise((resolve, reject) => {
|
|
176211
|
+
if (this.closedErr) {
|
|
176212
|
+
reject(this.closedErr);
|
|
176213
|
+
return;
|
|
176214
|
+
}
|
|
176215
|
+
this.pending.set(key, {
|
|
176216
|
+
handle,
|
|
176217
|
+
resolve: () => resolve(),
|
|
176218
|
+
reject,
|
|
176219
|
+
onProgress: onEvent,
|
|
176220
|
+
timer: null,
|
|
176221
|
+
subscription: true
|
|
176222
|
+
});
|
|
176223
|
+
const frame = buildFrame(FrameType.Request, buildFlags(false, priority, false, admission), handle.channel, handle.epoch, corr, bytes);
|
|
176224
|
+
this.sock.write(encodeFrame(frame), Date.now() + DEFAULT_REQUEST_TIMEOUT_MS).catch((err) => {
|
|
176225
|
+
const pending = this.pending.get(key);
|
|
176226
|
+
if (pending)
|
|
176227
|
+
this.rejectPending(key, pending, err instanceof Error ? err : new SubcError(String(err)));
|
|
176228
|
+
});
|
|
176229
|
+
});
|
|
176230
|
+
let cancelled = false;
|
|
176231
|
+
const unsubscribe = () => {
|
|
176232
|
+
if (cancelled)
|
|
176233
|
+
return;
|
|
176234
|
+
cancelled = true;
|
|
176235
|
+
this.cancel(handle, corr, priority);
|
|
176236
|
+
};
|
|
176237
|
+
return { unsubscribe, closed };
|
|
176238
|
+
}
|
|
176239
|
+
cancel(handle, corr, priority = Priority.Interactive) {
|
|
176240
|
+
this.assertLiveHandle(handle);
|
|
176241
|
+
const cancel = buildFrame(FrameType.Cancel, buildFlags(false, priority, false), handle.channel, handle.epoch, corr, EMPTY_BODY);
|
|
176242
|
+
this.sock.write(encodeFrame(cancel), Date.now() + DEFAULT_REQUEST_TIMEOUT_MS).catch(() => {
|
|
176243
|
+
return;
|
|
176244
|
+
});
|
|
176245
|
+
}
|
|
176246
|
+
async routePoll(handle, kind) {
|
|
176247
|
+
this.assertLiveHandle(handle);
|
|
176248
|
+
const body = this.encode({
|
|
176249
|
+
op: "route.poll",
|
|
176250
|
+
route_channel: handle.channel,
|
|
176251
|
+
route_epoch: handle.epoch,
|
|
176252
|
+
kind
|
|
176253
|
+
});
|
|
176254
|
+
const reply = await this.controlRpc(body, (frame) => {
|
|
176255
|
+
if (frame.header.ty !== FrameType.Response)
|
|
176256
|
+
return true;
|
|
176257
|
+
const parsed = this.parseJson(frame);
|
|
176258
|
+
return parsed.route_channel === handle.channel && parsed.route_epoch === handle.epoch;
|
|
176259
|
+
});
|
|
176260
|
+
return this.parseJson(reply);
|
|
176261
|
+
}
|
|
176262
|
+
async closeRoute(handle, opts = {}) {
|
|
176263
|
+
this.assertLiveHandle(handle);
|
|
176264
|
+
for (const [key, cached2] of this.routes) {
|
|
176265
|
+
if (cached2.handle && sameRouteHandle(cached2.handle, handle)) {
|
|
176266
|
+
cached2.closed = true;
|
|
176267
|
+
cached2.handle = null;
|
|
176268
|
+
this.routes.delete(key);
|
|
176269
|
+
}
|
|
176270
|
+
}
|
|
176271
|
+
if (opts.drain)
|
|
176272
|
+
await this.drainUnaryOnHandle(handle);
|
|
176273
|
+
this.failHandle(handle, new SubcError("route closed by closeRoute", "route_closed"));
|
|
176274
|
+
if (this.liveRoutes.get(handle.channel) === handle)
|
|
176275
|
+
this.liveRoutes.delete(handle.channel);
|
|
176276
|
+
this.sendRouteGoodbye(handle);
|
|
176277
|
+
}
|
|
176278
|
+
async closeManagedRoute(target, identity, opts = {}) {
|
|
176279
|
+
const key = routeCacheKey(target, identity, routeOpenConsumerIdentity(opts));
|
|
176280
|
+
const cached2 = this.routes.get(key);
|
|
176281
|
+
if (!cached2)
|
|
176282
|
+
return;
|
|
176283
|
+
cached2.closed = true;
|
|
176284
|
+
this.routes.delete(key);
|
|
176285
|
+
const handle = cached2.handle;
|
|
176286
|
+
cached2.handle = null;
|
|
176287
|
+
if (handle)
|
|
176288
|
+
await this.closeRoute(handle, opts);
|
|
176289
|
+
}
|
|
176290
|
+
async closeRouteChannel(handle, opts = {}) {
|
|
176291
|
+
await this.closeRoute(handle, opts);
|
|
176292
|
+
}
|
|
176293
|
+
close() {
|
|
176294
|
+
this.closeStarted = true;
|
|
176295
|
+
this.fail(new SubcError("client closed"));
|
|
176296
|
+
this.sock.close();
|
|
176297
|
+
}
|
|
176298
|
+
drainUnaryOnHandle(handle) {
|
|
176299
|
+
const waiters = [];
|
|
176300
|
+
for (const pending of this.pending.values()) {
|
|
176301
|
+
if (pending.handle === handle && !pending.subscription) {
|
|
176302
|
+
waiters.push(new Promise((resolve) => {
|
|
176303
|
+
const previous = pending.onSettle;
|
|
176304
|
+
pending.onSettle = () => {
|
|
176305
|
+
previous?.();
|
|
176306
|
+
resolve();
|
|
176307
|
+
};
|
|
176308
|
+
}));
|
|
176309
|
+
}
|
|
176310
|
+
}
|
|
176311
|
+
return Promise.all(waiters).then(() => {
|
|
176312
|
+
return;
|
|
176313
|
+
});
|
|
176314
|
+
}
|
|
176315
|
+
sendRouteGoodbye(handle, closeOnQueueFailure = false) {
|
|
176316
|
+
this.assertLiveConnection(handle);
|
|
176317
|
+
if (this.closedErr) {
|
|
176318
|
+
if (closeOnQueueFailure)
|
|
176319
|
+
this.closeConnectionAfterCleanupFailure();
|
|
176320
|
+
return;
|
|
176321
|
+
}
|
|
176322
|
+
const goodbye = buildFrame(FrameType.Goodbye, buildFlags(false, Priority.Interactive, false), handle.channel, handle.epoch, 0n, EMPTY_BODY);
|
|
176323
|
+
const write = this.sock.writeTracked(encodeFrame(goodbye), Date.now() + DEFAULT_REQUEST_TIMEOUT_MS);
|
|
176324
|
+
if (!write.queued && closeOnQueueFailure)
|
|
176325
|
+
this.closeConnectionAfterCleanupFailure();
|
|
176326
|
+
write.completed.catch(() => {
|
|
176327
|
+
if (closeOnQueueFailure && !write.queued)
|
|
176328
|
+
this.closeConnectionAfterCleanupFailure();
|
|
176329
|
+
});
|
|
176330
|
+
}
|
|
176331
|
+
static async openConnection(opts) {
|
|
176332
|
+
const conn = await readConnectionFile(opts.connectionFile);
|
|
176333
|
+
const deadline = Date.now() + (opts.handshakeTimeoutMs ?? DEFAULT_HANDSHAKE_TIMEOUT_MS);
|
|
176334
|
+
const endpoint = conn.endpoints[0];
|
|
176335
|
+
const sock = await SubcSocket.connect(endpoint.host, endpoint.port, deadline);
|
|
176336
|
+
try {
|
|
176337
|
+
await authenticateClient(sock, conn, deadline);
|
|
176338
|
+
} catch (err) {
|
|
176339
|
+
sock.close();
|
|
176340
|
+
throw err;
|
|
176341
|
+
}
|
|
176342
|
+
return { sock, conn };
|
|
176343
|
+
}
|
|
176344
|
+
async controlRpc(body, acceptFrame, onLateResponse) {
|
|
176345
|
+
return this.send(null, body, Priority.Interactive, AdmissionClass.Normal, undefined, undefined, acceptFrame, onLateResponse);
|
|
176346
|
+
}
|
|
176347
|
+
send(handle, body, priority, admission, timeoutMs, onProgress, acceptFrame, onLateResponse) {
|
|
176348
|
+
if (handle)
|
|
176349
|
+
this.assertLiveHandle(handle);
|
|
176350
|
+
if (this.closedErr)
|
|
176351
|
+
return Promise.reject(this.closedErr);
|
|
176352
|
+
let corr;
|
|
176353
|
+
try {
|
|
176354
|
+
corr = this.allocateCorr();
|
|
176355
|
+
} catch (error51) {
|
|
176356
|
+
return Promise.reject(error51);
|
|
176357
|
+
}
|
|
176358
|
+
const key = pendingKey(handle, corr);
|
|
176359
|
+
const channel = handle?.channel ?? 0;
|
|
176360
|
+
const epoch = handle?.epoch ?? 0;
|
|
176361
|
+
const frame = buildFrame(FrameType.Request, buildFlags(false, priority, false, admission), channel, epoch, corr, body);
|
|
176362
|
+
return new Promise((resolve, reject) => {
|
|
176363
|
+
const ms = timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
176364
|
+
const pending = {
|
|
176365
|
+
handle,
|
|
176366
|
+
resolve,
|
|
176367
|
+
reject,
|
|
176368
|
+
onProgress,
|
|
176369
|
+
timer: null,
|
|
176370
|
+
acceptFrame,
|
|
176371
|
+
onLateResponse
|
|
176372
|
+
};
|
|
176373
|
+
pending.timer = setTimeout(() => this.arbitrateTimeout(key, pending, channel, corr, ms), ms);
|
|
176374
|
+
this.pending.set(key, pending);
|
|
176375
|
+
this.sock.write(encodeFrame(frame), Date.now() + ms).catch((error51) => {
|
|
176376
|
+
const current = this.pending.get(key);
|
|
176377
|
+
if (current)
|
|
176378
|
+
this.rejectPending(key, current, error51 instanceof Error ? error51 : new SubcError(String(error51)));
|
|
176379
|
+
});
|
|
176380
|
+
});
|
|
176381
|
+
}
|
|
176382
|
+
arbitrateTimeout(key, pending, channel, corr, ms) {
|
|
176383
|
+
const settleAsTimeout = () => {
|
|
176384
|
+
if (pending.onLateResponse)
|
|
176385
|
+
this.lateResponses.set(key, pending.onLateResponse);
|
|
176386
|
+
this.rejectPending(key, pending, new SubcError(this.timeoutMessage(channel, corr, ms), REQUEST_DEADLINE_MARKER));
|
|
176387
|
+
};
|
|
176388
|
+
const graceDeadline = Date.now() + this.opts.timeoutArbitrationGraceMs;
|
|
176389
|
+
const arbitrate = () => {
|
|
176390
|
+
if (this.pending.get(key) !== pending)
|
|
176391
|
+
return;
|
|
176392
|
+
const readerDraining = this.readerActive || this.sock.bufferedBytes() > 0;
|
|
176393
|
+
if (readerDraining && Date.now() < graceDeadline) {
|
|
176394
|
+
setImmediate(arbitrate);
|
|
176395
|
+
return;
|
|
176396
|
+
}
|
|
176397
|
+
settleAsTimeout();
|
|
176398
|
+
};
|
|
176399
|
+
setImmediate(arbitrate);
|
|
176400
|
+
}
|
|
176401
|
+
async managedRequest(handle, body, opts) {
|
|
176402
|
+
const bytes = body instanceof Uint8Array ? body : this.encode(body);
|
|
176403
|
+
const priority = opts.priority ?? Priority.Interactive;
|
|
176404
|
+
const admission = opts.admissionClass ?? AdmissionClass.Normal;
|
|
176405
|
+
try {
|
|
176406
|
+
const reply = await this.sendManaged(handle, bytes, priority, admission, opts.timeoutMs, opts.onProgress);
|
|
176407
|
+
return this.parseJson(reply);
|
|
176408
|
+
} catch (error51) {
|
|
176409
|
+
if (error51 instanceof SubcCallError)
|
|
176410
|
+
throw error51;
|
|
176411
|
+
throw this.terminalCallError("managed call failed", error51);
|
|
176412
|
+
}
|
|
176413
|
+
}
|
|
176414
|
+
sendManaged(handle, body, priority, admission, timeoutMs, onProgress) {
|
|
176415
|
+
try {
|
|
176416
|
+
this.assertLiveHandle(handle);
|
|
176417
|
+
} catch (error51) {
|
|
176418
|
+
return Promise.reject(this.notSentCallError("request used a stale route handle", error51));
|
|
176419
|
+
}
|
|
176420
|
+
if (this.closedErr) {
|
|
176421
|
+
return Promise.reject(this.notSentCallError("request was not sent because the subc connection was already closed", this.closedErr));
|
|
176422
|
+
}
|
|
176423
|
+
let corr;
|
|
176424
|
+
try {
|
|
176425
|
+
corr = this.allocateCorr();
|
|
176426
|
+
} catch (error51) {
|
|
176427
|
+
return Promise.reject(this.notSentCallError("request correlation allocator was exhausted", error51));
|
|
176428
|
+
}
|
|
176429
|
+
const key = pendingKey(handle, corr);
|
|
176430
|
+
const frame = buildFrame(FrameType.Request, buildFlags(false, priority, false, admission), handle.channel, handle.epoch, corr, body);
|
|
176431
|
+
let handedToSocket = false;
|
|
176432
|
+
const classifyFailure = (error51) => {
|
|
176433
|
+
if (!handedToSocket)
|
|
176434
|
+
return this.notSentCallError("request bytes were not queued to the subc socket", error51);
|
|
176435
|
+
if (error51 instanceof SubcError && error51.code === REQUEST_DEADLINE_MARKER) {
|
|
176436
|
+
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);
|
|
176437
|
+
}
|
|
176438
|
+
return this.outcomeUnknownCallError("connection dropped before the managed call returned a response", error51);
|
|
176439
|
+
};
|
|
176440
|
+
return new Promise((resolve, reject) => {
|
|
176441
|
+
const ms = timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
176442
|
+
const pending = {
|
|
176443
|
+
handle,
|
|
176444
|
+
resolve,
|
|
176445
|
+
reject,
|
|
176446
|
+
onProgress,
|
|
176447
|
+
timer: null,
|
|
176448
|
+
classifyFailure
|
|
176449
|
+
};
|
|
176450
|
+
pending.timer = setTimeout(() => this.arbitrateTimeout(key, pending, handle.channel, corr, ms), ms);
|
|
176451
|
+
this.pending.set(key, pending);
|
|
176452
|
+
const write = this.sock.writeTracked(encodeFrame(frame), Date.now() + ms);
|
|
176453
|
+
handedToSocket = write.queued;
|
|
176454
|
+
write.completed.catch((error51) => {
|
|
176455
|
+
const current = this.pending.get(key);
|
|
176456
|
+
if (current)
|
|
176457
|
+
this.rejectPending(key, current, error51 instanceof Error ? error51 : new SubcError(String(error51)));
|
|
176458
|
+
});
|
|
176459
|
+
});
|
|
176460
|
+
}
|
|
176461
|
+
async cachedRouteHandle(moduleId, opts) {
|
|
176462
|
+
const identity = opts.identity ?? this.opts.identity;
|
|
176463
|
+
if (!identity) {
|
|
176464
|
+
throw new SubcCallError("terminal", "managed call requires a BindIdentity in SubcClient.connect({ identity }) or call(..., { identity })", "missing_identity");
|
|
176465
|
+
}
|
|
176466
|
+
const target = { kind: opts.targetKind ?? this.opts.targetKind, module_id: moduleId };
|
|
176467
|
+
const consumerIdentity = routeOpenConsumerIdentity(opts);
|
|
176468
|
+
const key = routeCacheKey(target, identity, consumerIdentity);
|
|
176469
|
+
let cached2 = this.routes.get(key);
|
|
176470
|
+
if (!cached2) {
|
|
176471
|
+
cached2 = {
|
|
176472
|
+
key,
|
|
176473
|
+
moduleId,
|
|
176474
|
+
target,
|
|
176475
|
+
identity,
|
|
176476
|
+
consumerIdentity,
|
|
176477
|
+
handle: null,
|
|
176478
|
+
opening: null
|
|
176479
|
+
};
|
|
176480
|
+
this.routes.set(key, cached2);
|
|
176481
|
+
}
|
|
176482
|
+
if (cached2.handle && this.isLiveHandle(cached2.handle))
|
|
176483
|
+
return cached2.handle;
|
|
176484
|
+
if (!cached2.opening) {
|
|
176485
|
+
cached2.opening = this.openCachedRoute(cached2).finally(() => {
|
|
176486
|
+
cached2.opening = null;
|
|
176487
|
+
});
|
|
176488
|
+
}
|
|
176489
|
+
return cached2.opening;
|
|
176490
|
+
}
|
|
176491
|
+
async openCachedRoute(cached2) {
|
|
176492
|
+
const routeRetryDeadline = Date.now() + ROUTE_OPEN_RETRY_DEADLINE_MS;
|
|
176493
|
+
let routeRetryDelay = this.opts.reconnectBackoff.baseMs;
|
|
176494
|
+
let routeRetryAttempt = 0;
|
|
176495
|
+
for (;; ) {
|
|
176496
|
+
if (cached2.closed)
|
|
176497
|
+
throw this.routeClosedDuringOpen();
|
|
176498
|
+
try {
|
|
176499
|
+
await this.ensureConnectedForManaged();
|
|
176500
|
+
} catch (error51) {
|
|
176501
|
+
throw this.notSentRecoveryError("route.open could not run because reconnect failed", error51);
|
|
176502
|
+
}
|
|
176503
|
+
if (cached2.handle && this.isLiveHandle(cached2.handle))
|
|
176504
|
+
return cached2.handle;
|
|
176505
|
+
try {
|
|
176506
|
+
const handle = await this.routeOpen(cached2.target, cached2.identity, {
|
|
176507
|
+
consumerIdentity: cached2.consumerIdentity ?? null
|
|
176508
|
+
});
|
|
176509
|
+
if (cached2.closed) {
|
|
176510
|
+
this.liveRoutes.delete(handle.channel);
|
|
176511
|
+
this.sendRouteGoodbye(handle);
|
|
176512
|
+
throw this.routeClosedDuringOpen();
|
|
176513
|
+
}
|
|
176514
|
+
cached2.handle = handle;
|
|
176515
|
+
return handle;
|
|
176516
|
+
} catch (error51) {
|
|
176517
|
+
if (error51 instanceof SubcCallError && error51.code === "route_closed")
|
|
176518
|
+
throw error51;
|
|
176519
|
+
if (!this.closeStarted && isConsumerReconnectTransient(error51)) {
|
|
176520
|
+
try {
|
|
176521
|
+
await this.reconnectAfterDrop(error51);
|
|
176522
|
+
} catch (reconnectError) {
|
|
176523
|
+
throw this.notSentRecoveryError("route.open was not sent and reconnect failed", reconnectError);
|
|
176524
|
+
}
|
|
176525
|
+
continue;
|
|
176526
|
+
}
|
|
176527
|
+
if (!this.closeStarted && error51 instanceof SubcError && isRetryableRouteOpenCode(error51.code)) {
|
|
176528
|
+
routeRetryAttempt += 1;
|
|
176529
|
+
if (routeRetryAttempt < this.opts.reconnectBackoff.maxAttempts && Date.now() < routeRetryDeadline) {
|
|
176530
|
+
await this.opts.sleep(routeRetryDelay);
|
|
176531
|
+
routeRetryDelay = Math.min(routeRetryDelay * 2, this.opts.reconnectBackoff.capMs);
|
|
176532
|
+
continue;
|
|
176533
|
+
}
|
|
176534
|
+
throw this.notSentCallError(`route.open failed for module ${cached2.moduleId}: ${error51.code} (retry budget exhausted)`, error51);
|
|
176535
|
+
}
|
|
176536
|
+
throw this.terminalCallError(`route.open failed for module ${cached2.moduleId}`, error51);
|
|
176537
|
+
}
|
|
176538
|
+
}
|
|
176539
|
+
}
|
|
176540
|
+
async ensureConnectedForManaged() {
|
|
176541
|
+
if (this.closeStarted)
|
|
176542
|
+
throw new SubcError("client closed");
|
|
176543
|
+
if (this.reconnecting)
|
|
176544
|
+
await this.reconnecting;
|
|
176545
|
+
if (this.closedErr)
|
|
176546
|
+
await this.reconnectAfterDrop(this.closedErr);
|
|
176547
|
+
}
|
|
176548
|
+
scheduleReconnectAfterDrop(err) {
|
|
176549
|
+
if (this.closeStarted || this.reconnecting)
|
|
176550
|
+
return;
|
|
176551
|
+
this.reconnectAfterDrop(err).catch(() => {});
|
|
176552
|
+
}
|
|
176553
|
+
reconnectAfterDrop(trigger) {
|
|
176554
|
+
if (this.closeStarted)
|
|
176555
|
+
return Promise.reject(new SubcError("client closed"));
|
|
176556
|
+
if (this.reconnecting)
|
|
176557
|
+
return this.reconnecting;
|
|
176558
|
+
const promise2 = this.reconnectWithRetry(trigger).finally(() => {
|
|
176559
|
+
if (this.reconnecting === promise2)
|
|
176560
|
+
this.reconnecting = null;
|
|
176561
|
+
});
|
|
176562
|
+
this.reconnecting = promise2;
|
|
176563
|
+
return promise2;
|
|
176564
|
+
}
|
|
176565
|
+
async reconnectWithRetry(_trigger) {
|
|
176566
|
+
let attempt = 0;
|
|
176567
|
+
let delay = this.opts.reconnectBackoff.baseMs;
|
|
176568
|
+
for (;; ) {
|
|
176569
|
+
if (this.closeStarted)
|
|
176570
|
+
throw new SubcError("client closed");
|
|
176571
|
+
attempt += 1;
|
|
176572
|
+
try {
|
|
176573
|
+
const opened = await SubcClient.openConnection(this.opts);
|
|
176574
|
+
if (this.closeStarted) {
|
|
176575
|
+
opened.sock.close();
|
|
176576
|
+
throw new SubcError("client closed");
|
|
176577
|
+
}
|
|
176578
|
+
this.replaceConnection(opened);
|
|
176579
|
+
await this.reopenCachedRoutes();
|
|
176580
|
+
return;
|
|
176581
|
+
} catch (err) {
|
|
176582
|
+
if (!isConsumerReconnectTransient(err) || attempt >= this.opts.reconnectBackoff.maxAttempts) {
|
|
176583
|
+
if (err instanceof AuthError && attempt > 1) {
|
|
176584
|
+
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`);
|
|
176585
|
+
}
|
|
176586
|
+
throw err;
|
|
176587
|
+
}
|
|
176588
|
+
await this.opts.sleep(delay);
|
|
176589
|
+
delay = Math.min(delay * 2, this.opts.reconnectBackoff.capMs);
|
|
176590
|
+
}
|
|
176591
|
+
}
|
|
176592
|
+
}
|
|
176593
|
+
replaceConnection(opened) {
|
|
176594
|
+
this.sock.close();
|
|
176595
|
+
this.sock = opened.sock;
|
|
176596
|
+
this.currentConn = opened.conn;
|
|
176597
|
+
this.closedErr = null;
|
|
176598
|
+
this.generation += 1;
|
|
176599
|
+
this.connectionToken = newConnectionToken();
|
|
176600
|
+
this.liveRoutes.clear();
|
|
176601
|
+
this.lateResponses.clear();
|
|
176602
|
+
this.nextCorr = 1n;
|
|
176603
|
+
this.readLoop(opened.sock, this.generation);
|
|
176604
|
+
}
|
|
176605
|
+
async reopenCachedRoutes() {
|
|
176606
|
+
for (const cached2 of this.routes.values())
|
|
176607
|
+
cached2.handle = null;
|
|
176608
|
+
for (const cached2 of this.routes.values()) {
|
|
176609
|
+
if (cached2.closed)
|
|
176610
|
+
continue;
|
|
176611
|
+
const handle = await this.routeOpen(cached2.target, cached2.identity, {
|
|
176612
|
+
consumerIdentity: cached2.consumerIdentity ?? null
|
|
176613
|
+
});
|
|
176614
|
+
if (cached2.closed) {
|
|
176615
|
+
this.liveRoutes.delete(handle.channel);
|
|
176616
|
+
this.sendRouteGoodbye(handle);
|
|
176617
|
+
continue;
|
|
176618
|
+
}
|
|
176619
|
+
cached2.handle = handle;
|
|
176620
|
+
}
|
|
176621
|
+
}
|
|
176622
|
+
timeoutMessage(channel, corr, ms) {
|
|
176623
|
+
const port = this.sock.localPort();
|
|
176624
|
+
const where = port === null ? "channel" : `local_port=${port} channel`;
|
|
176625
|
+
return `request on ${where} ${channel} corr ${corr} timed out after ${ms}ms`;
|
|
176626
|
+
}
|
|
176627
|
+
routeClosedDuringOpen() {
|
|
176628
|
+
return new SubcCallError("not_sent", "route was closed before route.open completed", "route_closed");
|
|
176629
|
+
}
|
|
176630
|
+
async readLoop(sock, generation) {
|
|
176631
|
+
try {
|
|
176632
|
+
for (;; ) {
|
|
176633
|
+
this.readerActive = false;
|
|
176634
|
+
const frame = await sock.readFrame(Number.POSITIVE_INFINITY, { afterHeaderMs: BODY_READ_TIMEOUT_MS }, () => {
|
|
176635
|
+
this.readerActive = true;
|
|
176636
|
+
});
|
|
176637
|
+
try {
|
|
176638
|
+
if (this.sock === sock && this.generation === generation)
|
|
176639
|
+
this.dispatch(frame);
|
|
176640
|
+
} finally {
|
|
176641
|
+
this.readerActive = false;
|
|
176642
|
+
}
|
|
176643
|
+
}
|
|
176644
|
+
} catch (error51) {
|
|
176645
|
+
if (this.sock === sock && this.generation === generation) {
|
|
176646
|
+
this.fail(error51 instanceof Error ? error51 : new SubcError(String(error51)));
|
|
176647
|
+
}
|
|
176648
|
+
}
|
|
176649
|
+
}
|
|
176650
|
+
dispatch(frame) {
|
|
176651
|
+
let handle = null;
|
|
176652
|
+
if (frame.header.channel !== 0) {
|
|
176653
|
+
handle = this.liveRoutes.get(frame.header.channel) ?? null;
|
|
176654
|
+
if (!handle || handle.epoch !== frame.header.epoch) {
|
|
176655
|
+
this.ingressEpochDropCount += 1;
|
|
176656
|
+
return;
|
|
176657
|
+
}
|
|
176658
|
+
}
|
|
176659
|
+
const key = pendingKey(handle, frame.header.corr);
|
|
176660
|
+
const pending = this.pending.get(key);
|
|
176661
|
+
if (pending) {
|
|
176662
|
+
if (pending.acceptFrame && !pending.acceptFrame(frame))
|
|
176663
|
+
return;
|
|
176664
|
+
switch (frame.header.ty) {
|
|
176665
|
+
case FrameType.Push:
|
|
176666
|
+
case FrameType.StreamData:
|
|
176667
|
+
pending.onProgress?.(frame.body);
|
|
176668
|
+
return;
|
|
176669
|
+
case FrameType.Response:
|
|
176670
|
+
case FrameType.StreamEnd:
|
|
176671
|
+
this.settle(key, pending, () => pending.resolve(frame));
|
|
176672
|
+
return;
|
|
176673
|
+
case FrameType.Error:
|
|
176674
|
+
this.settle(key, pending, () => pending.reject(this.errorFromFrame(frame)));
|
|
176675
|
+
return;
|
|
176676
|
+
default:
|
|
176677
|
+
return;
|
|
176678
|
+
}
|
|
176679
|
+
}
|
|
176680
|
+
const late = this.lateResponses.get(key);
|
|
176681
|
+
if (late && (frame.header.ty === FrameType.Response || frame.header.ty === FrameType.Error)) {
|
|
176682
|
+
this.lateResponses.delete(key);
|
|
176683
|
+
late(frame);
|
|
176684
|
+
return;
|
|
176685
|
+
}
|
|
176686
|
+
if (frame.header.ty === FrameType.Goodbye && handle) {
|
|
176687
|
+
this.failHandle(handle, new SubcError("route closed by subc (GOODBYE)"));
|
|
176688
|
+
if (this.liveRoutes.get(handle.channel) === handle)
|
|
176689
|
+
this.liveRoutes.delete(handle.channel);
|
|
176690
|
+
this.evictRouteHandle(handle);
|
|
176691
|
+
return;
|
|
176692
|
+
}
|
|
176693
|
+
if (frame.header.ty === FrameType.Response || frame.header.ty === FrameType.Error || frame.header.ty === FrameType.StreamEnd) {
|
|
176694
|
+
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() ?? "?");
|
|
176695
|
+
}
|
|
176696
|
+
}
|
|
176697
|
+
settle(key, pending, run) {
|
|
176698
|
+
if (this.pending.get(key) !== pending)
|
|
176699
|
+
return false;
|
|
176700
|
+
this.pending.delete(key);
|
|
176701
|
+
if (pending.timer)
|
|
176702
|
+
clearTimeout(pending.timer);
|
|
176703
|
+
run();
|
|
176704
|
+
pending.onSettle?.();
|
|
176705
|
+
return true;
|
|
176706
|
+
}
|
|
176707
|
+
rejectPending(key, pending, err) {
|
|
176708
|
+
this.settle(key, pending, () => pending.reject(pending.classifyFailure?.(err) ?? err));
|
|
176709
|
+
}
|
|
176710
|
+
errorFromFrame(frame) {
|
|
176711
|
+
try {
|
|
176712
|
+
const parsed = JSON.parse(Buffer.from(frame.body).toString("utf8"));
|
|
176713
|
+
return new SubcError(parsed.message ?? "subc error", parsed.code);
|
|
176714
|
+
} catch {
|
|
176715
|
+
return new SubcError(Buffer.from(frame.body).toString("utf8") || "subc error");
|
|
176716
|
+
}
|
|
176717
|
+
}
|
|
176718
|
+
evictRouteHandle(handle) {
|
|
176719
|
+
for (const cached2 of this.routes.values()) {
|
|
176720
|
+
if (cached2.handle && sameRouteHandle(cached2.handle, handle))
|
|
176721
|
+
cached2.handle = null;
|
|
176722
|
+
}
|
|
176723
|
+
}
|
|
176724
|
+
failHandle(handle, error51) {
|
|
176725
|
+
for (const [key, pending] of this.pending) {
|
|
176726
|
+
if (pending.handle && sameRouteHandle(pending.handle, handle))
|
|
176727
|
+
this.rejectPending(key, pending, error51);
|
|
176728
|
+
}
|
|
176729
|
+
}
|
|
176730
|
+
fail(err) {
|
|
176731
|
+
if (!this.closedErr)
|
|
176732
|
+
this.closedErr = err;
|
|
176733
|
+
for (const [key, pending] of this.pending) {
|
|
176734
|
+
this.rejectPending(key, pending, err);
|
|
176735
|
+
}
|
|
176736
|
+
}
|
|
176737
|
+
notSentCallError(message, cause) {
|
|
176738
|
+
return new SubcCallError("not_sent", `${message}${causeMessage(cause)}`, errorCode(cause), cause);
|
|
176739
|
+
}
|
|
176740
|
+
outcomeUnknownCallError(message, cause) {
|
|
176741
|
+
return new SubcCallError("outcome_unknown", `${message}${causeMessage(cause)}`, errorCode(cause), cause);
|
|
176742
|
+
}
|
|
176743
|
+
terminalCallError(message, cause) {
|
|
176744
|
+
if (cause instanceof SubcCallError)
|
|
176745
|
+
return cause;
|
|
176746
|
+
return new SubcCallError("terminal", `${message}${causeMessage(cause)}`, errorCode(cause), cause);
|
|
176747
|
+
}
|
|
176748
|
+
notSentRecoveryError(message, cause) {
|
|
176749
|
+
if (cause instanceof SubcCallError)
|
|
176750
|
+
return cause;
|
|
176751
|
+
if (isConsumerReconnectTransient(cause))
|
|
176752
|
+
return this.notSentCallError(message, cause);
|
|
176753
|
+
return this.terminalCallError(message, cause);
|
|
176754
|
+
}
|
|
176755
|
+
get droppedIngressFrames() {
|
|
176756
|
+
return this.ingressEpochDropCount;
|
|
176757
|
+
}
|
|
176758
|
+
installRoute(channel, epoch) {
|
|
176759
|
+
const handle = createRouteHandle(channel, epoch, this.connectionToken);
|
|
176760
|
+
this.liveRoutes.set(channel, handle);
|
|
176761
|
+
return handle;
|
|
176762
|
+
}
|
|
176763
|
+
isLiveHandle(handle) {
|
|
176764
|
+
return belongsToConnection(handle, this.connectionToken) && this.liveRoutes.get(handle.channel) === handle;
|
|
176765
|
+
}
|
|
176766
|
+
assertLiveConnection(handle) {
|
|
176767
|
+
if (!belongsToConnection(handle, this.connectionToken))
|
|
176768
|
+
throw new StaleRouteHandleError(handle);
|
|
176769
|
+
}
|
|
176770
|
+
assertLiveHandle(handle) {
|
|
176771
|
+
if (!this.isLiveHandle(handle))
|
|
176772
|
+
throw new StaleRouteHandleError(handle);
|
|
176773
|
+
}
|
|
176774
|
+
allocateCorr() {
|
|
176775
|
+
const maximum = 0xffffffffffffffffn;
|
|
176776
|
+
if (this.nextCorr > maximum) {
|
|
176777
|
+
const error51 = new SubcError("channel-0 correlation id allocator exhausted", "corr_exhausted");
|
|
176778
|
+
this.fail(error51);
|
|
176779
|
+
this.sock.close();
|
|
176780
|
+
this.scheduleReconnectAfterDrop(error51);
|
|
176781
|
+
throw error51;
|
|
176782
|
+
}
|
|
176783
|
+
const corr = this.nextCorr;
|
|
176784
|
+
this.nextCorr += 1n;
|
|
176785
|
+
return corr;
|
|
176786
|
+
}
|
|
176787
|
+
closeConnectionAfterCleanupFailure() {
|
|
176788
|
+
const error51 = new SubcError("late route cleanup could not be queued", "late_route_cleanup_failed");
|
|
176789
|
+
this.fail(error51);
|
|
176790
|
+
this.sock.close();
|
|
176791
|
+
this.scheduleReconnectAfterDrop(error51);
|
|
176792
|
+
}
|
|
176793
|
+
encode(value) {
|
|
176794
|
+
return new Uint8Array(Buffer.from(JSON.stringify(value), "utf8"));
|
|
176795
|
+
}
|
|
176796
|
+
parseJson(frame) {
|
|
176797
|
+
return JSON.parse(Buffer.from(frame.body).toString("utf8"));
|
|
176798
|
+
}
|
|
176799
|
+
}
|
|
176800
|
+
function isConsumerReconnectTransient(err) {
|
|
176801
|
+
if (err instanceof SocketClosedError || err instanceof SocketTimeoutError)
|
|
176802
|
+
return true;
|
|
176803
|
+
if (err instanceof SocketWriteNotQueuedError || err instanceof SocketWriteQueuedError)
|
|
176804
|
+
return true;
|
|
176805
|
+
if (err instanceof SubcCallError)
|
|
176806
|
+
return err.kind === "not_sent" || err.kind === "outcome_unknown";
|
|
176807
|
+
if (err instanceof AuthError)
|
|
176808
|
+
return true;
|
|
176809
|
+
if (err instanceof SubcError || err instanceof ConnectionFileError)
|
|
176810
|
+
return false;
|
|
176811
|
+
const code = errorCode(err);
|
|
176812
|
+
return code === "ECONNREFUSED" || code === "ECONNRESET" || code === "EPIPE" || code === "ETIMEDOUT" || code === "ENOENT";
|
|
176813
|
+
}
|
|
176814
|
+
function isRetryableRouteOpenCode(code) {
|
|
176815
|
+
return code === "unknown_module" || code === "module_reloading" || code === "target_unavailable" || code === "module_timeout";
|
|
176816
|
+
}
|
|
176817
|
+
async function connectionFileExists(path7) {
|
|
176818
|
+
try {
|
|
176819
|
+
await fs4.access(path7);
|
|
176820
|
+
return true;
|
|
176821
|
+
} catch {
|
|
176822
|
+
return false;
|
|
176823
|
+
}
|
|
176824
|
+
}
|
|
176825
|
+
function normalizeConnectOptions(opts) {
|
|
176826
|
+
return {
|
|
176827
|
+
connectionFile: opts.connectionFile,
|
|
176828
|
+
handshakeTimeoutMs: opts.handshakeTimeoutMs,
|
|
176829
|
+
identity: opts.identity,
|
|
176830
|
+
targetKind: opts.targetKind ?? DEFAULT_MANAGED_TARGET_KIND,
|
|
176831
|
+
reconnectBackoff: opts.reconnectBackoff ?? DEFAULT_RECONNECT_BACKOFF,
|
|
176832
|
+
sleep: opts.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms))),
|
|
176833
|
+
timeoutArbitrationGraceMs: opts.timeoutArbitrationGraceMs ?? TIMEOUT_ARBITRATION_GRACE_MS
|
|
176834
|
+
};
|
|
176835
|
+
}
|
|
176836
|
+
function routeCacheKey(target, identity, consumerIdentity) {
|
|
176837
|
+
const consumerPart = consumerIdentity ? `${consumerIdentity.module_id}\x00${consumerIdentity.launch_nonce}` : "";
|
|
176838
|
+
return `${target.kind}\x00${target.module_id}\x00${identity.project_root}\x00${identity.harness}\x00${identity.session}\x00${consumerPart}`;
|
|
176839
|
+
}
|
|
176840
|
+
function routeOpenConsumerIdentity(opts = {}) {
|
|
176841
|
+
if (opts.consumerIdentity !== undefined)
|
|
176842
|
+
return opts.consumerIdentity ?? undefined;
|
|
176843
|
+
const moduleId = process.env[SUBC_MODULE_ID_ENV];
|
|
176844
|
+
const launchNonce = process.env[SUBC_LAUNCH_NONCE_ENV];
|
|
176845
|
+
if (!moduleId || !launchNonce)
|
|
176846
|
+
return;
|
|
176847
|
+
return { module_id: moduleId, launch_nonce: launchNonce };
|
|
176848
|
+
}
|
|
176849
|
+
function errorCode(err) {
|
|
176850
|
+
if (typeof err === "object" && err !== null && "code" in err) {
|
|
176851
|
+
const code = err.code;
|
|
176852
|
+
if (typeof code === "string")
|
|
176853
|
+
return code;
|
|
176854
|
+
}
|
|
176855
|
+
return;
|
|
176856
|
+
}
|
|
176857
|
+
function causeMessage(cause) {
|
|
176858
|
+
if (cause === undefined)
|
|
176859
|
+
return "";
|
|
176860
|
+
return `: ${cause instanceof Error ? cause.message : String(cause)}`;
|
|
176861
|
+
}
|
|
176862
|
+
function pendingKey(handle, corr) {
|
|
176863
|
+
return handle ? `${handle.channel}:${handle.epoch}:${corr}` : `0:0:${corr}`;
|
|
176864
|
+
}
|
|
176865
|
+
// ../../node_modules/.bun/@cortexkit+subc-client@0.4.1/node_modules/@cortexkit/subc-client/dist/provider.js
|
|
176866
|
+
import { Buffer as Buffer4 } from "node:buffer";
|
|
176867
|
+
var DEFAULT_HANDSHAKE_TIMEOUT_MS2 = 1e4;
|
|
176868
|
+
var BODY_READ_TIMEOUT_MS2 = 30000;
|
|
176869
|
+
var WRITE_TIMEOUT_MS = 30000;
|
|
176870
|
+
var DEFAULT_RESTORED_DEBOUNCE_MS = 250;
|
|
176871
|
+
var DEFAULT_PROVIDER_HANDLER_CAPACITY = 64;
|
|
176872
|
+
var HEALTH_CHECK_OP = "health.check";
|
|
176873
|
+
var HELLO_CORR = 1n;
|
|
176874
|
+
|
|
176875
|
+
class AsyncPermitPool {
|
|
176876
|
+
available;
|
|
176877
|
+
waiters = [];
|
|
176878
|
+
constructor(capacity) {
|
|
176879
|
+
if (!Number.isInteger(capacity) || capacity <= 0) {
|
|
176880
|
+
throw new SubcProviderError("provider handler capacity must be a positive integer", "invalid_handler_capacity");
|
|
176881
|
+
}
|
|
176882
|
+
this.available = capacity;
|
|
176883
|
+
}
|
|
176884
|
+
async acquire() {
|
|
176885
|
+
if (this.available > 0) {
|
|
176886
|
+
this.available -= 1;
|
|
176887
|
+
return this.releaseOnce();
|
|
176888
|
+
}
|
|
176889
|
+
await new Promise((resolve) => {
|
|
176890
|
+
this.waiters.push(resolve);
|
|
176891
|
+
});
|
|
176892
|
+
return this.releaseOnce();
|
|
176893
|
+
}
|
|
176894
|
+
releaseOnce() {
|
|
176895
|
+
let released = false;
|
|
176896
|
+
return () => {
|
|
176897
|
+
if (released)
|
|
176898
|
+
return;
|
|
176899
|
+
released = true;
|
|
176900
|
+
const next = this.waiters.shift();
|
|
176901
|
+
if (next) {
|
|
176902
|
+
next();
|
|
176903
|
+
} else {
|
|
176904
|
+
this.available += 1;
|
|
176905
|
+
}
|
|
176906
|
+
};
|
|
176907
|
+
}
|
|
176908
|
+
}
|
|
176909
|
+
|
|
176910
|
+
class SubcProviderError extends Error {
|
|
176911
|
+
code;
|
|
176912
|
+
constructor(message, code) {
|
|
176913
|
+
super(message);
|
|
176914
|
+
this.code = code;
|
|
176915
|
+
}
|
|
176916
|
+
}
|
|
176917
|
+
class SubcProvider {
|
|
176918
|
+
sock;
|
|
176919
|
+
currentConn;
|
|
176920
|
+
opts;
|
|
176921
|
+
closed;
|
|
176922
|
+
resolveClosed = () => {
|
|
176923
|
+
return;
|
|
176924
|
+
};
|
|
176925
|
+
closeStarted = false;
|
|
176926
|
+
closedErr = null;
|
|
176927
|
+
inflight = new Map;
|
|
176928
|
+
pending = new Map;
|
|
176929
|
+
liveRoutes = new Map;
|
|
176930
|
+
connectionToken = newConnectionToken();
|
|
176931
|
+
nextCorr = 1n;
|
|
176932
|
+
ingressEpochDropCount = 0;
|
|
176933
|
+
requestGate = new AsyncPermitPool(DEFAULT_PROVIDER_HANDLER_CAPACITY);
|
|
176934
|
+
reconnecting = null;
|
|
176935
|
+
generation = 1;
|
|
176936
|
+
connectionEpoch = 1;
|
|
176937
|
+
stateQueue = [];
|
|
176938
|
+
drainingStateQueue = false;
|
|
176939
|
+
restoredDebounceToken = 0;
|
|
176940
|
+
storage;
|
|
176941
|
+
constructor(sock, currentConn, opts, storage) {
|
|
176942
|
+
this.sock = sock;
|
|
176943
|
+
this.currentConn = currentConn;
|
|
176944
|
+
this.opts = opts;
|
|
176945
|
+
this.storage = storage;
|
|
176946
|
+
this.closed = new Promise((resolve) => {
|
|
176947
|
+
this.resolveClosed = resolve;
|
|
176948
|
+
});
|
|
176949
|
+
this.readLoop(sock, this.generation);
|
|
176950
|
+
this.enqueueConnectionState({ state: "connected", epoch: this.connectionEpoch });
|
|
176951
|
+
}
|
|
176952
|
+
get droppedIngressFrames() {
|
|
176953
|
+
return this.ingressEpochDropCount;
|
|
176954
|
+
}
|
|
176955
|
+
get conn() {
|
|
176956
|
+
return this.currentConn;
|
|
176957
|
+
}
|
|
176958
|
+
currentEpoch() {
|
|
176959
|
+
return this.connectionEpoch;
|
|
176960
|
+
}
|
|
176961
|
+
async request(handle, body, opts = {}) {
|
|
176962
|
+
this.assertLiveHandle(handle);
|
|
176963
|
+
const corr = this.allocateCorr();
|
|
176964
|
+
const key = routeKey(handle, corr);
|
|
176965
|
+
const timeoutMs = opts.timeoutMs ?? WRITE_TIMEOUT_MS;
|
|
176966
|
+
const frame = buildFrame(FrameType.Request, buildFlags(false, opts.priority ?? Priority.Interactive, false, opts.admissionClass ?? AdmissionClass.Normal), handle.channel, handle.epoch, corr, body);
|
|
176967
|
+
return await new Promise((resolve, reject) => {
|
|
176968
|
+
const timer = setTimeout(() => {
|
|
176969
|
+
if (this.pending.delete(key))
|
|
176970
|
+
reject(new SubcProviderError("reverse request timed out", "request_timeout"));
|
|
176971
|
+
}, timeoutMs);
|
|
176972
|
+
this.pending.set(key, {
|
|
176973
|
+
resolve: (response) => resolve(response.body),
|
|
176974
|
+
reject,
|
|
176975
|
+
timer
|
|
176976
|
+
});
|
|
176977
|
+
this.sendOn(this.sock, this.generation, frame).catch((error51) => {
|
|
176978
|
+
const pending = this.pending.get(key);
|
|
176979
|
+
if (!pending)
|
|
176980
|
+
return;
|
|
176981
|
+
this.pending.delete(key);
|
|
176982
|
+
clearTimeout(pending.timer);
|
|
176983
|
+
reject(error51 instanceof Error ? error51 : new SubcProviderError(String(error51)));
|
|
176984
|
+
});
|
|
176985
|
+
});
|
|
176986
|
+
}
|
|
176987
|
+
async push(handle, body, opts = {}) {
|
|
176988
|
+
this.assertLiveHandle(handle);
|
|
176989
|
+
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));
|
|
176990
|
+
}
|
|
176991
|
+
cancel(handle, corr) {
|
|
176992
|
+
this.assertLiveHandle(handle);
|
|
176993
|
+
this.sendOn(this.sock, this.generation, buildFrame(FrameType.Cancel, controlFlags(), handle.channel, handle.epoch, corr, new Uint8Array(0)));
|
|
176994
|
+
}
|
|
176995
|
+
closeRoute(handle) {
|
|
176996
|
+
this.assertLiveHandle(handle);
|
|
176997
|
+
this.liveRoutes.delete(handle.channel);
|
|
176998
|
+
this.abortHandle(handle);
|
|
176999
|
+
this.sendOn(this.sock, this.generation, buildFrame(FrameType.Goodbye, controlFlags(), handle.channel, handle.epoch, 0n, new Uint8Array(0)));
|
|
177000
|
+
}
|
|
177001
|
+
static async connect(opts) {
|
|
177002
|
+
if (opts.manifest.protocol_ver !== PROTOCOL_VERSION) {
|
|
177003
|
+
throw new SubcProviderError(`manifest protocol_ver ${opts.manifest.protocol_ver} does not match client protocol ${PROTOCOL_VERSION}`, "invalid_manifest");
|
|
177004
|
+
}
|
|
177005
|
+
const normalized = normalizeProviderConnectOptions(opts);
|
|
177006
|
+
const opened = await SubcProvider.openConnection(normalized);
|
|
177007
|
+
return new SubcProvider(opened.sock, opened.conn, normalized, opened.ack.storage);
|
|
177008
|
+
}
|
|
177009
|
+
async close() {
|
|
177010
|
+
if (!this.closeStarted) {
|
|
177011
|
+
this.closeStarted = true;
|
|
177012
|
+
this.cancelRestoredDebounce();
|
|
177013
|
+
const sock = this.sock;
|
|
177014
|
+
try {
|
|
177015
|
+
await sendFrame(sock, buildFrame(FrameType.Goodbye, controlFlags(), 0, 0, 0n, new Uint8Array(0)));
|
|
177016
|
+
} catch {} finally {
|
|
177017
|
+
sock.close();
|
|
177018
|
+
this.finishClosed();
|
|
177019
|
+
}
|
|
177020
|
+
}
|
|
177021
|
+
await this.closed;
|
|
177022
|
+
}
|
|
177023
|
+
static async openConnection(opts) {
|
|
177024
|
+
const conn = await readConnectionFile(opts.connectionFile);
|
|
177025
|
+
const deadline = Date.now() + (opts.handshakeTimeoutMs ?? DEFAULT_HANDSHAKE_TIMEOUT_MS2);
|
|
177026
|
+
const endpoint = conn.endpoints[0];
|
|
177027
|
+
const sock = await SubcSocket.connect(endpoint.host, endpoint.port, deadline);
|
|
177028
|
+
try {
|
|
177029
|
+
await authenticateClient(sock, conn, deadline);
|
|
177030
|
+
await sendFrame(sock, buildHelloFrame(opts));
|
|
177031
|
+
const ack = await expectHelloAck(sock, deadline);
|
|
177032
|
+
return { sock, conn, ack };
|
|
177033
|
+
} catch (err) {
|
|
177034
|
+
sock.close();
|
|
177035
|
+
throw err;
|
|
177036
|
+
}
|
|
177037
|
+
}
|
|
177038
|
+
async readLoop(sock, generation) {
|
|
177039
|
+
try {
|
|
177040
|
+
for (;; ) {
|
|
177041
|
+
const frame = await sock.readFrame(Number.POSITIVE_INFINITY, { afterHeaderMs: BODY_READ_TIMEOUT_MS2 });
|
|
177042
|
+
const keepGoing = await this.dispatch(frame, sock, generation);
|
|
177043
|
+
if (!keepGoing) {
|
|
177044
|
+
if (this.sock === sock && this.generation === generation)
|
|
177045
|
+
this.closeStarted = true;
|
|
177046
|
+
break;
|
|
177047
|
+
}
|
|
177048
|
+
}
|
|
177049
|
+
} catch (error51) {
|
|
177050
|
+
if (this.sock === sock && this.generation === generation && !this.closeStarted) {
|
|
177051
|
+
this.handleUnexpectedDrop(sock, generation, error51 instanceof Error ? error51 : new SubcProviderError(String(error51)));
|
|
177052
|
+
return;
|
|
177053
|
+
}
|
|
177054
|
+
} finally {
|
|
177055
|
+
if (this.sock === sock && this.generation === generation) {
|
|
177056
|
+
sock.close();
|
|
177057
|
+
if (this.closeStarted)
|
|
177058
|
+
this.finishClosed();
|
|
177059
|
+
}
|
|
177060
|
+
}
|
|
177061
|
+
}
|
|
177062
|
+
async dispatch(frame, sock, generation) {
|
|
177063
|
+
let handle = null;
|
|
177064
|
+
if (frame.header.channel !== 0) {
|
|
177065
|
+
handle = this.liveRoutes.get(frame.header.channel) ?? null;
|
|
177066
|
+
if (!handle || handle.epoch !== frame.header.epoch) {
|
|
177067
|
+
this.ingressEpochDropCount += 1;
|
|
177068
|
+
return true;
|
|
177069
|
+
}
|
|
177070
|
+
}
|
|
177071
|
+
if (handle) {
|
|
177072
|
+
const pendingKey2 = routeKey(handle, frame.header.corr);
|
|
177073
|
+
const pending = this.pending.get(pendingKey2);
|
|
177074
|
+
if (pending) {
|
|
177075
|
+
if (frame.header.ty === FrameType.Push || frame.header.ty === FrameType.StreamData)
|
|
177076
|
+
return true;
|
|
177077
|
+
if (frame.header.ty === FrameType.Response || frame.header.ty === FrameType.StreamEnd) {
|
|
177078
|
+
this.pending.delete(pendingKey2);
|
|
177079
|
+
clearTimeout(pending.timer);
|
|
177080
|
+
pending.resolve(frame);
|
|
177081
|
+
return true;
|
|
177082
|
+
}
|
|
177083
|
+
if (frame.header.ty === FrameType.Error) {
|
|
177084
|
+
this.pending.delete(pendingKey2);
|
|
177085
|
+
clearTimeout(pending.timer);
|
|
177086
|
+
pending.reject(providerErrorFromFrame(frame));
|
|
177087
|
+
return true;
|
|
177088
|
+
}
|
|
177089
|
+
}
|
|
177090
|
+
}
|
|
177091
|
+
switch (frame.header.ty) {
|
|
177092
|
+
case FrameType.Ping:
|
|
177093
|
+
if (frame.header.channel === 0) {
|
|
177094
|
+
await this.sendOn(sock, generation, buildFrameWithVersion(frame.header.ver, FrameType.Pong, frame.header.flags, 0, 0, frame.header.corr, new Uint8Array(0)));
|
|
177095
|
+
}
|
|
177096
|
+
return true;
|
|
177097
|
+
case FrameType.Goodbye:
|
|
177098
|
+
if (!handle)
|
|
177099
|
+
return false;
|
|
177100
|
+
this.liveRoutes.delete(handle.channel);
|
|
177101
|
+
this.abortHandle(handle);
|
|
177102
|
+
await this.opts.onRouteGone?.(handle);
|
|
177103
|
+
return true;
|
|
177104
|
+
case FrameType.Cancel:
|
|
177105
|
+
if (handle)
|
|
177106
|
+
this.inflight.get(routeKey(handle, frame.header.corr))?.abort();
|
|
177107
|
+
return true;
|
|
177108
|
+
case FrameType.Request:
|
|
177109
|
+
if (frame.header.channel === 0) {
|
|
177110
|
+
await this.handleControlRequest(frame, sock, generation);
|
|
177111
|
+
} else if (handle) {
|
|
177112
|
+
this.handleDataRequest(frame, handle, sock, generation).catch((error51) => {
|
|
177113
|
+
if (!this.closeStarted && this.sock === sock && this.generation === generation) {
|
|
177114
|
+
console.warn("SubcProvider handler failed after its request was dispatched", error51);
|
|
177115
|
+
}
|
|
177116
|
+
});
|
|
177117
|
+
}
|
|
177118
|
+
return true;
|
|
177119
|
+
default:
|
|
177120
|
+
return true;
|
|
177121
|
+
}
|
|
177122
|
+
}
|
|
177123
|
+
abortHandle(handle) {
|
|
177124
|
+
const prefix = `${handle.channel}:${handle.epoch}:`;
|
|
177125
|
+
for (const [key, controller] of this.inflight) {
|
|
177126
|
+
if (key.startsWith(prefix))
|
|
177127
|
+
controller.abort();
|
|
177128
|
+
}
|
|
177129
|
+
for (const [key, pending] of this.pending) {
|
|
177130
|
+
if (!key.startsWith(prefix))
|
|
177131
|
+
continue;
|
|
177132
|
+
this.pending.delete(key);
|
|
177133
|
+
clearTimeout(pending.timer);
|
|
177134
|
+
pending.reject(new StaleRouteHandleError(handle));
|
|
177135
|
+
}
|
|
177136
|
+
}
|
|
177137
|
+
abortGeneration(_generation) {
|
|
177138
|
+
this.abortAllInflight();
|
|
177139
|
+
for (const [key, pending] of this.pending) {
|
|
177140
|
+
this.pending.delete(key);
|
|
177141
|
+
clearTimeout(pending.timer);
|
|
177142
|
+
pending.reject(new SubcProviderError("provider connection dropped", "connection_dropped"));
|
|
177143
|
+
}
|
|
177144
|
+
this.liveRoutes.clear();
|
|
177145
|
+
}
|
|
177146
|
+
abortAllInflight() {
|
|
177147
|
+
for (const controller of this.inflight.values())
|
|
177148
|
+
controller.abort();
|
|
177149
|
+
}
|
|
177150
|
+
async handleControlRequest(frame, sock, generation) {
|
|
177151
|
+
const request = parseJson(frame.body);
|
|
177152
|
+
if (request.op === HEALTH_CHECK_OP) {
|
|
177153
|
+
this.handleHealthRequest(frame, sock, generation).catch((error51) => {
|
|
177154
|
+
if (!this.closeStarted && this.sock === sock && this.generation === generation) {
|
|
177155
|
+
console.warn("SubcProvider health handler failed after its request was dispatched", error51);
|
|
177156
|
+
}
|
|
177157
|
+
});
|
|
177158
|
+
return;
|
|
177159
|
+
}
|
|
177160
|
+
if (request.op !== "route.bind") {
|
|
177161
|
+
throw new SubcProviderError(`unsupported module control request ${request.op ?? "<missing op>"}`);
|
|
177162
|
+
}
|
|
177163
|
+
const tentative = createRouteHandle(numberField(request.route_channel, "route_channel"), numberField(request.epoch, "epoch"), this.connectionToken);
|
|
177164
|
+
const bindRequest = {
|
|
177165
|
+
handle: tentative,
|
|
177166
|
+
target: request.target,
|
|
177167
|
+
identity: request.identity,
|
|
177168
|
+
principal: request.principal,
|
|
177169
|
+
consumer_capabilities: request.consumer_capabilities
|
|
177170
|
+
};
|
|
177171
|
+
let decision;
|
|
177172
|
+
try {
|
|
177173
|
+
decision = await this.opts.onBind?.(bindRequest);
|
|
177174
|
+
} catch (error51) {
|
|
177175
|
+
try {
|
|
177176
|
+
await this.sendError(frame, "route_rejected", error51 instanceof Error ? error51.message : String(error51), controlFlags(), sock, generation);
|
|
177177
|
+
} finally {
|
|
177178
|
+
await this.opts.onRouteGone?.(tentative);
|
|
177179
|
+
}
|
|
177180
|
+
return;
|
|
177181
|
+
}
|
|
177182
|
+
const rejection = bindRejection(decision);
|
|
177183
|
+
if (rejection) {
|
|
177184
|
+
try {
|
|
177185
|
+
await this.sendError(frame, rejection.code, rejection.message, controlFlags(), sock, generation);
|
|
177186
|
+
} finally {
|
|
177187
|
+
await this.opts.onRouteGone?.(tentative);
|
|
177188
|
+
}
|
|
177189
|
+
return;
|
|
177190
|
+
}
|
|
177191
|
+
try {
|
|
177192
|
+
await this.sendOn(sock, generation, buildFrameWithVersion(frame.header.ver, FrameType.Response, controlFlags(), 0, 0, frame.header.corr, encodeJson({ op: "route.bind" })));
|
|
177193
|
+
} catch (error51) {
|
|
177194
|
+
await this.opts.onRouteGone?.(tentative);
|
|
177195
|
+
throw error51;
|
|
177196
|
+
}
|
|
177197
|
+
if (this.sock !== sock || this.generation !== generation || this.closeStarted || this.closedErr) {
|
|
177198
|
+
await this.opts.onRouteGone?.(tentative);
|
|
177199
|
+
return;
|
|
177200
|
+
}
|
|
177201
|
+
this.liveRoutes.set(tentative.channel, tentative);
|
|
177202
|
+
await this.opts.onBound?.(tentative);
|
|
177203
|
+
}
|
|
177204
|
+
async handleDataRequest(frame, handle, sock, generation) {
|
|
177205
|
+
const { corr, ver } = frame.header;
|
|
177206
|
+
const key = routeKey(handle, corr);
|
|
177207
|
+
const controller = new AbortController;
|
|
177208
|
+
this.inflight.set(key, controller);
|
|
177209
|
+
const context = {
|
|
177210
|
+
handle,
|
|
177211
|
+
signal: controller.signal,
|
|
177212
|
+
currentEpoch: () => this.connectionEpoch,
|
|
177213
|
+
emit: async (eventBody, options = {}) => {
|
|
177214
|
+
this.assertLiveHandle(handle);
|
|
177215
|
+
if (controller.signal.aborted)
|
|
177216
|
+
return;
|
|
177217
|
+
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));
|
|
177218
|
+
}
|
|
177219
|
+
};
|
|
177220
|
+
const releasePermit = await (this.requestGate ?? new AsyncPermitPool(DEFAULT_PROVIDER_HANDLER_CAPACITY)).acquire();
|
|
177221
|
+
const dataFlags = buildFlags(false, Priority.Interactive, false);
|
|
177222
|
+
try {
|
|
177223
|
+
const body = await this.opts.handler(handle, frame.body, context);
|
|
177224
|
+
if (controller.signal.aborted)
|
|
177225
|
+
return;
|
|
177226
|
+
this.assertLiveHandle(handle);
|
|
177227
|
+
if (body === undefined) {
|
|
177228
|
+
await this.sendOn(sock, generation, buildFrameWithVersion(ver, FrameType.StreamEnd, dataFlags, handle.channel, handle.epoch, corr, new Uint8Array(0)));
|
|
177229
|
+
} else if (body instanceof Uint8Array) {
|
|
177230
|
+
await this.sendOn(sock, generation, buildFrameWithVersion(ver, FrameType.Response, dataFlags, handle.channel, handle.epoch, corr, body));
|
|
177231
|
+
} else {
|
|
177232
|
+
throw new SubcProviderError("provider handler must return a Uint8Array or void", "invalid_handler_response");
|
|
177233
|
+
}
|
|
177234
|
+
} catch (error51) {
|
|
177235
|
+
if (error51 instanceof StaleRouteHandleError || controller.signal.aborted)
|
|
177236
|
+
return;
|
|
177237
|
+
await this.sendError(frame, error51 instanceof SubcProviderError && error51.code ? error51.code : "handler_error", error51 instanceof Error ? error51.message : String(error51), dataFlags, sock, generation);
|
|
177238
|
+
} finally {
|
|
177239
|
+
releasePermit();
|
|
177240
|
+
if (this.inflight.get(key) === controller)
|
|
177241
|
+
this.inflight.delete(key);
|
|
177242
|
+
}
|
|
177243
|
+
}
|
|
177244
|
+
async handleHealthRequest(frame, sock, generation) {
|
|
177245
|
+
const { corr, ver } = frame.header;
|
|
177246
|
+
const key = `control:${generation}:${corr}`;
|
|
177247
|
+
const controller = new AbortController;
|
|
177248
|
+
this.inflight.set(key, controller);
|
|
177249
|
+
const releasePermit = await (this.requestGate ?? new AsyncPermitPool(DEFAULT_PROVIDER_HANDLER_CAPACITY)).acquire();
|
|
177250
|
+
try {
|
|
177251
|
+
if (controller.signal.aborted)
|
|
177252
|
+
return;
|
|
177253
|
+
const report = await this.opts.health();
|
|
177254
|
+
await this.sendOn(sock, generation, buildFrameWithVersion(ver, FrameType.Response, controlFlags(), 0, 0, corr, encodeJson({
|
|
177255
|
+
op: HEALTH_CHECK_OP,
|
|
177256
|
+
status: report.status,
|
|
177257
|
+
...report.detail === undefined ? {} : { detail: report.detail },
|
|
177258
|
+
...report.metrics === undefined ? {} : { metrics: report.metrics }
|
|
177259
|
+
})));
|
|
177260
|
+
} catch (error51) {
|
|
177261
|
+
await this.sendError(frame, error51 instanceof SubcProviderError && error51.code ? error51.code : "health_error", error51 instanceof Error ? error51.message : String(error51), controlFlags(), sock, generation);
|
|
177262
|
+
} finally {
|
|
177263
|
+
releasePermit();
|
|
177264
|
+
if (this.inflight.get(key) === controller)
|
|
177265
|
+
this.inflight.delete(key);
|
|
177266
|
+
}
|
|
177267
|
+
}
|
|
177268
|
+
async sendError(frame, code, message, flags, sock, generation) {
|
|
177269
|
+
await this.sendOn(sock, generation, buildFrameWithVersion(frame.header.ver, FrameType.Error, flags, frame.header.channel, frame.header.epoch, frame.header.corr, encodeJson({ code, message })));
|
|
177270
|
+
}
|
|
177271
|
+
async sendOn(sock, generation, frame) {
|
|
177272
|
+
if (this.sock !== sock || this.generation !== generation || this.closeStarted || this.closedErr)
|
|
177273
|
+
return;
|
|
177274
|
+
await sendFrame(sock, frame);
|
|
177275
|
+
}
|
|
177276
|
+
handleUnexpectedDrop(sock, generation, cause) {
|
|
177277
|
+
this.cancelRestoredDebounce();
|
|
177278
|
+
this.abortGeneration(generation);
|
|
177279
|
+
this.generation += 1;
|
|
177280
|
+
sock.close();
|
|
177281
|
+
this.enqueueConnectionState({ state: "down", cause });
|
|
177282
|
+
this.scheduleReconnectAfterDrop(cause);
|
|
177283
|
+
}
|
|
177284
|
+
scheduleReconnectAfterDrop(trigger) {
|
|
177285
|
+
if (this.closeStarted || this.reconnecting)
|
|
177286
|
+
return;
|
|
177287
|
+
const promise2 = this.reconnectWithRetry(trigger).catch((err) => {
|
|
177288
|
+
if (!this.closeStarted)
|
|
177289
|
+
this.failFatal(err instanceof Error ? err : new SubcProviderError(String(err)));
|
|
177290
|
+
}).finally(() => {
|
|
177291
|
+
if (this.reconnecting === promise2)
|
|
177292
|
+
this.reconnecting = null;
|
|
177293
|
+
});
|
|
177294
|
+
this.reconnecting = promise2;
|
|
177295
|
+
}
|
|
177296
|
+
async reconnectWithRetry(_trigger) {
|
|
177297
|
+
let attempt = 0;
|
|
177298
|
+
let delay = this.opts.reconnectBackoff.baseMs;
|
|
177299
|
+
for (;; ) {
|
|
177300
|
+
if (this.closeStarted)
|
|
177301
|
+
throw new SubcProviderError("provider closed");
|
|
177302
|
+
attempt += 1;
|
|
177303
|
+
this.enqueueConnectionState({ state: "reconnecting", attempt });
|
|
177304
|
+
try {
|
|
177305
|
+
const opened = await SubcProvider.openConnection(this.opts);
|
|
177306
|
+
if (this.closeStarted) {
|
|
177307
|
+
opened.sock.close();
|
|
177308
|
+
throw new SubcProviderError("provider closed");
|
|
177309
|
+
}
|
|
177310
|
+
this.replaceConnection(opened);
|
|
177311
|
+
return;
|
|
177312
|
+
} catch (err) {
|
|
177313
|
+
if (this.closeStarted)
|
|
177314
|
+
throw err;
|
|
177315
|
+
if (!isProviderReconnectTransient(err))
|
|
177316
|
+
throw err;
|
|
177317
|
+
await this.opts.sleep(delay);
|
|
177318
|
+
delay = Math.min(delay * 2, this.opts.reconnectBackoff.capMs);
|
|
177319
|
+
}
|
|
177320
|
+
}
|
|
177321
|
+
}
|
|
177322
|
+
replaceConnection(opened) {
|
|
177323
|
+
this.sock.close();
|
|
177324
|
+
this.sock = opened.sock;
|
|
177325
|
+
this.currentConn = opened.conn;
|
|
177326
|
+
this.storage = opened.ack.storage;
|
|
177327
|
+
this.closedErr = null;
|
|
177328
|
+
this.connectionEpoch += 1;
|
|
177329
|
+
this.connectionToken = newConnectionToken();
|
|
177330
|
+
this.liveRoutes.clear();
|
|
177331
|
+
this.nextCorr = 1n;
|
|
177332
|
+
const generation = this.generation;
|
|
177333
|
+
this.readLoop(opened.sock, generation);
|
|
177334
|
+
this.scheduleRestored(generation, this.connectionEpoch);
|
|
177335
|
+
}
|
|
177336
|
+
scheduleRestored(generation, epoch) {
|
|
177337
|
+
if (!this.opts.onConnectionState)
|
|
177338
|
+
return;
|
|
177339
|
+
const token = ++this.restoredDebounceToken;
|
|
177340
|
+
this.opts.sleep(this.opts.restoredDebounceMs).then(() => {
|
|
177341
|
+
if (token === this.restoredDebounceToken && !this.closeStarted && this.sock && this.generation === generation && this.connectionEpoch === epoch) {
|
|
177342
|
+
this.enqueueConnectionState({ state: "restored", epoch });
|
|
177343
|
+
}
|
|
177344
|
+
}).catch((err) => {
|
|
177345
|
+
if (token === this.restoredDebounceToken && !this.closeStarted) {
|
|
177346
|
+
console.warn("SubcProvider restored debounce timer failed", err);
|
|
177347
|
+
}
|
|
177348
|
+
});
|
|
177349
|
+
}
|
|
177350
|
+
cancelRestoredDebounce() {
|
|
177351
|
+
this.restoredDebounceToken += 1;
|
|
177352
|
+
}
|
|
177353
|
+
enqueueConnectionState(event) {
|
|
177354
|
+
if (!this.opts.onConnectionState)
|
|
177355
|
+
return;
|
|
177356
|
+
this.stateQueue.push(event);
|
|
177357
|
+
if (!this.drainingStateQueue)
|
|
177358
|
+
this.drainConnectionStateQueue();
|
|
177359
|
+
}
|
|
177360
|
+
async drainConnectionStateQueue() {
|
|
177361
|
+
if (this.drainingStateQueue)
|
|
177362
|
+
return;
|
|
177363
|
+
this.drainingStateQueue = true;
|
|
177364
|
+
try {
|
|
177365
|
+
while (this.stateQueue.length > 0) {
|
|
177366
|
+
const event = this.stateQueue[0];
|
|
177367
|
+
try {
|
|
177368
|
+
await this.opts.onConnectionState?.(event);
|
|
177369
|
+
this.stateQueue.shift();
|
|
177370
|
+
} catch (err) {
|
|
177371
|
+
if (event.state === "restored") {
|
|
177372
|
+
console.warn("SubcProvider restored callback failed; retrying delivery", err);
|
|
177373
|
+
await pauseBeforeStateRetry();
|
|
177374
|
+
continue;
|
|
177375
|
+
}
|
|
177376
|
+
console.warn("SubcProvider connection-state callback failed", err);
|
|
177377
|
+
this.stateQueue.shift();
|
|
177378
|
+
}
|
|
177379
|
+
}
|
|
177380
|
+
} finally {
|
|
177381
|
+
this.drainingStateQueue = false;
|
|
177382
|
+
if (this.stateQueue.length > 0)
|
|
177383
|
+
this.drainConnectionStateQueue();
|
|
177384
|
+
}
|
|
177385
|
+
}
|
|
177386
|
+
isLiveHandle(handle) {
|
|
177387
|
+
return belongsToConnection(handle, this.connectionToken) && this.liveRoutes.get(handle.channel) === handle;
|
|
177388
|
+
}
|
|
177389
|
+
assertLiveHandle(handle) {
|
|
177390
|
+
if (!this.isLiveHandle(handle))
|
|
177391
|
+
throw new StaleRouteHandleError(handle);
|
|
177392
|
+
}
|
|
177393
|
+
allocateCorr() {
|
|
177394
|
+
const maximum = 0xffffffffffffffffn;
|
|
177395
|
+
if (this.nextCorr > maximum) {
|
|
177396
|
+
const error51 = new SubcProviderError("channel-0 correlation id allocator exhausted", "corr_exhausted");
|
|
177397
|
+
this.handleUnexpectedDrop(this.sock, this.generation, error51);
|
|
177398
|
+
throw error51;
|
|
177399
|
+
}
|
|
177400
|
+
const corr = this.nextCorr;
|
|
177401
|
+
this.nextCorr += 1n;
|
|
177402
|
+
return corr;
|
|
177403
|
+
}
|
|
177404
|
+
failFatal(err) {
|
|
177405
|
+
if (!this.closedErr)
|
|
177406
|
+
this.closedErr = err;
|
|
177407
|
+
this.closeStarted = true;
|
|
177408
|
+
this.cancelRestoredDebounce();
|
|
177409
|
+
this.abortAllInflight();
|
|
177410
|
+
this.sock.close();
|
|
177411
|
+
this.finishClosed();
|
|
177412
|
+
}
|
|
177413
|
+
finishClosed() {
|
|
177414
|
+
this.resolveClosed();
|
|
177415
|
+
}
|
|
177416
|
+
}
|
|
177417
|
+
function routeKey(handle, corr) {
|
|
177418
|
+
return `${handle.channel}:${handle.epoch}:${corr}`;
|
|
177419
|
+
}
|
|
177420
|
+
function providerErrorFromFrame(frame) {
|
|
177421
|
+
try {
|
|
177422
|
+
const body = parseJson(frame.body);
|
|
177423
|
+
return new SubcProviderError(body.message ?? "subc error", body.code);
|
|
177424
|
+
} catch {
|
|
177425
|
+
return new SubcProviderError(Buffer4.from(frame.body).toString("utf8") || "subc error");
|
|
177426
|
+
}
|
|
177427
|
+
}
|
|
177428
|
+
function launchNonce(opts) {
|
|
177429
|
+
const nonce = opts.launchNonce ?? process.env[SUBC_LAUNCH_NONCE_ENV2];
|
|
177430
|
+
return nonce && nonce.length > 0 ? nonce : undefined;
|
|
177431
|
+
}
|
|
177432
|
+
var SUBC_LAUNCH_NONCE_ENV2 = "SUBC_LAUNCH_NONCE";
|
|
177433
|
+
function normalizeProviderConnectOptions(opts) {
|
|
177434
|
+
return {
|
|
177435
|
+
connectionFile: opts.connectionFile,
|
|
177436
|
+
manifest: opts.manifest,
|
|
177437
|
+
handler: opts.handler,
|
|
177438
|
+
health: opts.health ?? (() => ({ status: "ok" })),
|
|
177439
|
+
handshakeTimeoutMs: opts.handshakeTimeoutMs,
|
|
177440
|
+
controlOps: opts.controlOps,
|
|
177441
|
+
onBind: opts.onBind,
|
|
177442
|
+
onBound: opts.onBound,
|
|
177443
|
+
onRouteGone: opts.onRouteGone,
|
|
177444
|
+
reconnectBackoff: opts.reconnectBackoff ?? DEFAULT_RECONNECT_BACKOFF,
|
|
177445
|
+
sleep: opts.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms))),
|
|
177446
|
+
restoredDebounceMs: opts.restoredDebounceMs ?? DEFAULT_RESTORED_DEBOUNCE_MS,
|
|
177447
|
+
onConnectionState: opts.onConnectionState,
|
|
177448
|
+
launchNonce: opts.launchNonce
|
|
177449
|
+
};
|
|
177450
|
+
}
|
|
177451
|
+
function normalizedControlOps(controlOps) {
|
|
177452
|
+
if (controlOps === null)
|
|
177453
|
+
return null;
|
|
177454
|
+
const merged = new Set(controlOps ?? []);
|
|
177455
|
+
merged.add(HEALTH_CHECK_OP);
|
|
177456
|
+
return [...merged];
|
|
177457
|
+
}
|
|
177458
|
+
function buildHelloFrame(opts) {
|
|
177459
|
+
const nonce = launchNonce(opts);
|
|
177460
|
+
return buildFrame(FrameType.Hello, controlFlags(), 0, 0, HELLO_CORR, encodeJson({
|
|
177461
|
+
manifest: normalizeManifest(opts.manifest),
|
|
177462
|
+
protocol_ver: PROTOCOL_VERSION,
|
|
177463
|
+
control_ops: normalizedControlOps(opts.controlOps),
|
|
177464
|
+
...nonce ? { launch_nonce: nonce } : {}
|
|
177465
|
+
}));
|
|
177466
|
+
}
|
|
177467
|
+
function isProviderReconnectTransient(err) {
|
|
177468
|
+
if (err instanceof SubcProviderError)
|
|
177469
|
+
return err.code === "duplicate_module_id";
|
|
177470
|
+
if (err instanceof SocketClosedError || err instanceof SocketTimeoutError)
|
|
177471
|
+
return true;
|
|
177472
|
+
if (err instanceof SocketWriteNotQueuedError || err instanceof SocketWriteQueuedError)
|
|
177473
|
+
return true;
|
|
177474
|
+
if (err instanceof AuthError)
|
|
177475
|
+
return true;
|
|
177476
|
+
if (err instanceof ConnectionFileError)
|
|
177477
|
+
return false;
|
|
177478
|
+
const code = errorCode2(err);
|
|
177479
|
+
return code === "ECONNREFUSED" || code === "ECONNRESET" || code === "EPIPE" || code === "ETIMEDOUT" || code === "ENOENT";
|
|
177480
|
+
}
|
|
177481
|
+
function errorCode2(err) {
|
|
177482
|
+
if (typeof err === "object" && err !== null && "code" in err) {
|
|
177483
|
+
const code = err.code;
|
|
177484
|
+
if (typeof code === "string")
|
|
177485
|
+
return code;
|
|
177486
|
+
}
|
|
177487
|
+
return;
|
|
177488
|
+
}
|
|
177489
|
+
async function pauseBeforeStateRetry() {
|
|
177490
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
177491
|
+
}
|
|
177492
|
+
function controlFlags() {
|
|
177493
|
+
return buildFlags(false, Priority.Passive, false);
|
|
177494
|
+
}
|
|
177495
|
+
async function sendFrame(sock, frame) {
|
|
177496
|
+
await sock.write(encodeFrame(frame), Date.now() + WRITE_TIMEOUT_MS);
|
|
177497
|
+
}
|
|
177498
|
+
async function expectHelloAck(sock, deadline) {
|
|
177499
|
+
const frame = await sock.readFrame(deadline, deadline);
|
|
177500
|
+
switch (frame.header.ty) {
|
|
177501
|
+
case FrameType.HelloAck: {
|
|
177502
|
+
const ack = parseJson(frame.body);
|
|
177503
|
+
if (ack.negotiated_ver !== PROTOCOL_VERSION) {
|
|
177504
|
+
throw new SubcProviderError(`subc negotiated protocol ${ack.negotiated_ver}; expected exactly ${PROTOCOL_VERSION}`, "unsupported_version");
|
|
177505
|
+
}
|
|
177506
|
+
return ack;
|
|
177507
|
+
}
|
|
177508
|
+
case FrameType.Error: {
|
|
177509
|
+
const error51 = parseJson(frame.body);
|
|
177510
|
+
throw new SubcProviderError(`subc rejected HELLO: ${error51.code ?? "unknown"} — ${error51.message ?? "subc error"}`, error51.code);
|
|
177511
|
+
}
|
|
177512
|
+
default:
|
|
177513
|
+
throw new SubcProviderError(`unexpected frame ${FrameType[frame.header.ty]} awaiting HELLO_ACK`);
|
|
177514
|
+
}
|
|
177515
|
+
}
|
|
177516
|
+
function encodeJson(value) {
|
|
177517
|
+
return new Uint8Array(Buffer4.from(JSON.stringify(value), "utf8"));
|
|
177518
|
+
}
|
|
177519
|
+
function parseJson(bytes) {
|
|
177520
|
+
return JSON.parse(Buffer4.from(bytes).toString("utf8"));
|
|
177521
|
+
}
|
|
177522
|
+
function numberField(value, field) {
|
|
177523
|
+
if (typeof value !== "number" || !Number.isInteger(value)) {
|
|
177524
|
+
throw new SubcProviderError(`route.bind ${field} must be an integer`);
|
|
177525
|
+
}
|
|
177526
|
+
return value;
|
|
177527
|
+
}
|
|
177528
|
+
function bindRejection(decision) {
|
|
177529
|
+
if (decision === undefined || decision === true)
|
|
177530
|
+
return null;
|
|
177531
|
+
if (decision === false) {
|
|
177532
|
+
return { code: "route_rejected", message: "route.bind rejected by provider" };
|
|
177533
|
+
}
|
|
177534
|
+
if (decision.accept)
|
|
177535
|
+
return null;
|
|
177536
|
+
return {
|
|
177537
|
+
code: decision.code ?? "route_rejected",
|
|
177538
|
+
message: decision.message ?? "route.bind rejected by provider"
|
|
177539
|
+
};
|
|
177540
|
+
}
|
|
177541
|
+
function normalizeManifest(manifest) {
|
|
177542
|
+
return {
|
|
177543
|
+
module_id: manifest.module_id,
|
|
177544
|
+
module_version: manifest.module_version,
|
|
177545
|
+
protocol_ver: manifest.protocol_ver,
|
|
177546
|
+
trust_tier: manifest.trust_tier,
|
|
177547
|
+
provides: manifest.provides.map(normalizeProviderRole),
|
|
177548
|
+
consumes: manifest.consumes.map(normalizeConsumerRole),
|
|
177549
|
+
scheduled_tasks: manifest.scheduled_tasks.map(normalizeScheduledTask),
|
|
177550
|
+
bindings: {
|
|
177551
|
+
storage: {
|
|
177552
|
+
kind: manifest.bindings.storage.kind,
|
|
177553
|
+
scope: manifest.bindings.storage.scope,
|
|
177554
|
+
owns_schema: manifest.bindings.storage.owns_schema
|
|
177555
|
+
},
|
|
177556
|
+
vault_grants: manifest.bindings.vault_grants.map((grant) => ({
|
|
177557
|
+
secret: grant.secret,
|
|
177558
|
+
reason: grant.reason
|
|
177559
|
+
})),
|
|
177560
|
+
identity: {
|
|
177561
|
+
requires: [...manifest.bindings.identity.requires],
|
|
177562
|
+
optional: [...manifest.bindings.identity.optional]
|
|
177563
|
+
}
|
|
177564
|
+
}
|
|
177565
|
+
};
|
|
177566
|
+
}
|
|
177567
|
+
function normalizeProviderRole(role) {
|
|
177568
|
+
switch (role.role) {
|
|
177569
|
+
case "tool_provider":
|
|
177570
|
+
return {
|
|
177571
|
+
role: "tool_provider",
|
|
177572
|
+
tools: role.tools.map((tool) => ({
|
|
177573
|
+
name: tool.name,
|
|
177574
|
+
...tool.description === undefined ? {} : { description: tool.description },
|
|
177575
|
+
execution_mode: tool.execution_mode,
|
|
177576
|
+
schema: tool.schema
|
|
177577
|
+
})),
|
|
177578
|
+
identity_scope: [...role.identity_scope],
|
|
177579
|
+
concurrency: role.concurrency,
|
|
177580
|
+
emits_push: role.emits_push,
|
|
177581
|
+
sub_supervises: role.sub_supervises
|
|
177582
|
+
};
|
|
177583
|
+
case "pipeline_stage":
|
|
177584
|
+
return {
|
|
177585
|
+
role: "pipeline_stage",
|
|
177586
|
+
stage: role.stage,
|
|
177587
|
+
applies_to: {
|
|
177588
|
+
provider: role.applies_to.provider,
|
|
177589
|
+
model: role.applies_to.model
|
|
177590
|
+
},
|
|
177591
|
+
interface: role.interface,
|
|
177592
|
+
declares_frozen_floor: role.declares_frozen_floor,
|
|
177593
|
+
needs_signals: [...role.needs_signals],
|
|
177594
|
+
conformance_class: role.conformance_class
|
|
177595
|
+
};
|
|
177596
|
+
case "management_surface":
|
|
177597
|
+
return {
|
|
177598
|
+
role: "management_surface",
|
|
177599
|
+
operations: role.operations.map((operation) => ({
|
|
177600
|
+
name: operation.name,
|
|
177601
|
+
kind: operation.kind
|
|
177602
|
+
})),
|
|
177603
|
+
config_schema: role.config_schema,
|
|
177604
|
+
observability: role.observability.map((surface) => ({
|
|
177605
|
+
name: surface.name,
|
|
177606
|
+
kind: surface.kind
|
|
177607
|
+
})),
|
|
177608
|
+
identity_scope: [...role.identity_scope]
|
|
177609
|
+
};
|
|
177610
|
+
case "internal_service":
|
|
177611
|
+
return {
|
|
177612
|
+
role: "internal_service",
|
|
177613
|
+
service_id: role.service_id,
|
|
177614
|
+
transport: role.transport,
|
|
177615
|
+
agent_facing: role.agent_facing,
|
|
177616
|
+
operations: [...role.operations]
|
|
177617
|
+
};
|
|
177618
|
+
}
|
|
177619
|
+
}
|
|
177620
|
+
function normalizeConsumerRole(role) {
|
|
177621
|
+
switch (role.role) {
|
|
177622
|
+
case "tool_client":
|
|
177623
|
+
return { role: "tool_client", of: [...role.of] };
|
|
177624
|
+
case "llm_client":
|
|
177625
|
+
return { role: "llm_client", via: role.via, auth: role.auth };
|
|
177626
|
+
case "service_client":
|
|
177627
|
+
return { role: "service_client", of: [...role.of] };
|
|
177628
|
+
}
|
|
177629
|
+
}
|
|
177630
|
+
function normalizeScheduledTask(task) {
|
|
177631
|
+
return {
|
|
177632
|
+
task_id: task.task_id,
|
|
177633
|
+
eligibility: {
|
|
177634
|
+
cooldown: task.eligibility.cooldown,
|
|
177635
|
+
window: task.eligibility.window
|
|
177636
|
+
},
|
|
177637
|
+
lease_scope: task.lease_scope,
|
|
177638
|
+
renews_during_calls: task.renews_during_calls,
|
|
177639
|
+
toolset: [...task.toolset],
|
|
177640
|
+
model_policy: {
|
|
177641
|
+
tier: task.model_policy.tier,
|
|
177642
|
+
fallback_chain: [...task.model_policy.fallback_chain]
|
|
177643
|
+
},
|
|
177644
|
+
step_cap: task.step_cap,
|
|
177645
|
+
circuit_breaker: {
|
|
177646
|
+
identical_failures: task.circuit_breaker.identical_failures
|
|
177647
|
+
}
|
|
177648
|
+
};
|
|
177649
|
+
}
|
|
177650
|
+
// ../plugin/src/features/magic-context/memory/embedding-synapse.ts
|
|
177651
|
+
init_logger();
|
|
177652
|
+
var SYNAPSE_DEFAULT_MODEL = "gte-modernbert-base-f16";
|
|
177653
|
+
var SYNAPSE_MAX_INPUT_TOKENS = 8192;
|
|
177654
|
+
var SYNAPSE_DEFAULT_QUERY_TIMEOUT_MS = 3000;
|
|
177655
|
+
var SYNAPSE_DEFAULT_BATCH_TIMEOUT_MS = 120000;
|
|
177656
|
+
|
|
177657
|
+
class SynapseEmbeddingError extends Error {
|
|
177658
|
+
code;
|
|
177659
|
+
retryAfterMs;
|
|
177660
|
+
permanent;
|
|
177661
|
+
constructor(code, message, options) {
|
|
177662
|
+
super(message, options?.cause === undefined ? undefined : { cause: options.cause });
|
|
177663
|
+
this.name = "SynapseEmbeddingError";
|
|
177664
|
+
this.code = code;
|
|
177665
|
+
this.permanent = options?.permanent ?? isPermanentSynapseCode(code);
|
|
177666
|
+
this.retryAfterMs = options?.retryAfterMs ?? (this.permanent ? undefined : 100);
|
|
177667
|
+
}
|
|
177668
|
+
}
|
|
177669
|
+
function isPermanentSynapseCode(code) {
|
|
177670
|
+
return code === "artifact_invalid" || code === "substitution_rejected" || code === "not_certified" || code === "probe_required" || code === "idempotency_conflict" || code === "schema_violation";
|
|
177671
|
+
}
|
|
177672
|
+
function asRecord(value) {
|
|
177673
|
+
return value !== null && typeof value === "object" ? value : null;
|
|
177674
|
+
}
|
|
177675
|
+
function responseBody(value) {
|
|
177676
|
+
const record2 = asRecord(value);
|
|
177677
|
+
const result = asRecord(record2?.result);
|
|
177678
|
+
return result ? { ...record2, ...result } : record2 ?? {};
|
|
177679
|
+
}
|
|
177680
|
+
function readRetryAfter(value) {
|
|
177681
|
+
const record2 = asRecord(value);
|
|
177682
|
+
const candidate = record2?.retry_after_ms ?? record2?.retryAfterMs;
|
|
177683
|
+
if (typeof candidate !== "number" || !Number.isFinite(candidate) || candidate < 0)
|
|
177684
|
+
return;
|
|
177685
|
+
return Math.ceil(candidate);
|
|
177686
|
+
}
|
|
177687
|
+
function readErrorCode(value) {
|
|
177688
|
+
const record2 = asRecord(value);
|
|
177689
|
+
if (typeof record2?.code === "string")
|
|
177690
|
+
return record2.code;
|
|
177691
|
+
if (value instanceof Error && "code" in value && typeof value.code === "string") {
|
|
177692
|
+
return value.code;
|
|
177693
|
+
}
|
|
177694
|
+
return;
|
|
177695
|
+
}
|
|
177696
|
+
function classifyError(value) {
|
|
177697
|
+
if (value instanceof SynapseEmbeddingError)
|
|
177698
|
+
return value;
|
|
177699
|
+
const code = readErrorCode(value) ?? (value instanceof Error ? value.name : "transport");
|
|
177700
|
+
const normalized = code.toLowerCase();
|
|
177701
|
+
let mapped = "transport";
|
|
177702
|
+
if (normalized.includes("queue_full"))
|
|
177703
|
+
mapped = "queue_full";
|
|
177704
|
+
else if (normalized.includes("model_loading"))
|
|
177705
|
+
mapped = "model_loading";
|
|
177706
|
+
else if (normalized.includes("timeout") || normalized.includes("deadline"))
|
|
177707
|
+
mapped = "timeout";
|
|
177708
|
+
else if (normalized.includes("artifact_invalid"))
|
|
177709
|
+
mapped = "artifact_invalid";
|
|
177710
|
+
else if (normalized.includes("substitution"))
|
|
177711
|
+
mapped = "substitution_rejected";
|
|
177712
|
+
else if (normalized.includes("not_certified"))
|
|
177713
|
+
mapped = "not_certified";
|
|
177714
|
+
else if (normalized.includes("probe_required"))
|
|
177715
|
+
mapped = "probe_required";
|
|
177716
|
+
else if (normalized.includes("idempotency_conflict"))
|
|
177717
|
+
mapped = "idempotency_conflict";
|
|
177718
|
+
else if (normalized.includes("schema"))
|
|
177719
|
+
mapped = "schema_violation";
|
|
177720
|
+
else if (normalized.includes("module_restarted") || normalized.includes("module restarted"))
|
|
177721
|
+
mapped = "module_restarted";
|
|
177722
|
+
const message = value instanceof Error ? value.message : String(value);
|
|
177723
|
+
return new SynapseEmbeddingError(mapped, message, {
|
|
177724
|
+
retryAfterMs: readRetryAfter(value) ?? (isPermanentSynapseCode(mapped) ? undefined : 100),
|
|
177725
|
+
cause: value
|
|
177726
|
+
});
|
|
177727
|
+
}
|
|
177728
|
+
function wait(ms) {
|
|
177729
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
177730
|
+
}
|
|
177731
|
+
function sha256(value) {
|
|
177732
|
+
return createHash9("sha256").update(value).digest("hex");
|
|
177733
|
+
}
|
|
177734
|
+
function stableJson(value) {
|
|
177735
|
+
if (Array.isArray(value))
|
|
177736
|
+
return `[${value.map(stableJson).join(",")}]`;
|
|
177737
|
+
if (value && typeof value === "object") {
|
|
177738
|
+
return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => `${JSON.stringify(key)}:${stableJson(entry)}`).join(",")}}`;
|
|
177739
|
+
}
|
|
177740
|
+
return JSON.stringify(value);
|
|
177741
|
+
}
|
|
177742
|
+
function getSynapseLaneIdentity(model, fingerprint) {
|
|
177743
|
+
return `synapse:v1:${sha256(stableJson({ model, fingerprint }))}`;
|
|
177744
|
+
}
|
|
177745
|
+
function getSynapseBatchRequestKey(args) {
|
|
177746
|
+
return sha256(stableJson({
|
|
177747
|
+
op: "embed.batch",
|
|
177748
|
+
model: args.model,
|
|
177749
|
+
required_fingerprint: args.fingerprint,
|
|
177750
|
+
required_epoch: args.tableEpoch,
|
|
177751
|
+
allow_equivalent: false,
|
|
177752
|
+
accept_declared: false,
|
|
177753
|
+
ids: args.items.map((item) => item.id),
|
|
177754
|
+
content_sha256: args.items.map((item) => item.contentSha256)
|
|
177755
|
+
}));
|
|
177756
|
+
}
|
|
177757
|
+
function hashContent(text) {
|
|
177758
|
+
return sha256(text);
|
|
177759
|
+
}
|
|
177760
|
+
function extractCatalogEntries(value) {
|
|
177761
|
+
const body = responseBody(value);
|
|
177762
|
+
const raw = Array.isArray(body.models) ? body.models : Array.isArray(body.entries) ? body.entries : Array.isArray(value) ? value : [];
|
|
177763
|
+
const envelopeEpoch = typeof body.table_epoch === "number" && Number.isInteger(body.table_epoch) ? body.table_epoch : undefined;
|
|
177764
|
+
return raw.flatMap((entry) => {
|
|
177765
|
+
const record2 = asRecord(entry);
|
|
177766
|
+
if (!record2)
|
|
177767
|
+
return [];
|
|
177768
|
+
const model = typeof record2.model === "string" && record2.model.length > 0 ? record2.model : typeof record2.model_id === "string" ? record2.model_id : "";
|
|
177769
|
+
const fingerprint = typeof record2.fingerprint === "string" && record2.fingerprint.length > 0 ? record2.fingerprint : Array.isArray(record2.fingerprints) && typeof record2.fingerprints[0] === "string" ? record2.fingerprints[0] : "";
|
|
177770
|
+
const entryEpoch = record2.table_epoch ?? record2.tableEpoch;
|
|
177771
|
+
const tableEpoch = typeof entryEpoch === "number" && Number.isInteger(entryEpoch) ? entryEpoch : envelopeEpoch;
|
|
177772
|
+
const dims = record2.dims ?? record2.dimensions;
|
|
177773
|
+
if (model.length === 0 || fingerprint.length === 0 || typeof tableEpoch !== "number" || !Number.isInteger(tableEpoch)) {
|
|
177774
|
+
return [];
|
|
177775
|
+
}
|
|
177776
|
+
const recommendedBatch = record2.recommended_batch ?? record2.recommendedBatch;
|
|
177777
|
+
const state = typeof record2.state === "string" ? record2.state : undefined;
|
|
177778
|
+
return [
|
|
177779
|
+
{
|
|
177780
|
+
model,
|
|
177781
|
+
fingerprint,
|
|
177782
|
+
table_epoch: tableEpoch,
|
|
177783
|
+
...typeof dims === "number" && Number.isInteger(dims) && dims > 0 ? { dims } : {},
|
|
177784
|
+
...typeof recommendedBatch === "number" && recommendedBatch > 0 ? { recommended_batch: Math.floor(recommendedBatch) } : {},
|
|
177785
|
+
...record2.provenance !== undefined ? { provenance: record2.provenance } : {},
|
|
177786
|
+
...typeof record2.certified === "boolean" ? { certified: record2.certified } : {},
|
|
177787
|
+
...typeof record2.status === "string" ? { status: record2.status } : state ? { status: state } : {}
|
|
177788
|
+
}
|
|
177789
|
+
];
|
|
177790
|
+
});
|
|
177791
|
+
}
|
|
177792
|
+
function extractVector(value) {
|
|
177793
|
+
const body = responseBody(value);
|
|
177794
|
+
const fromVectors = Array.isArray(body.vectors) ? asRecord(body.vectors[0])?.vector : undefined;
|
|
177795
|
+
const raw = fromVectors ?? body.vector ?? body.embedding;
|
|
177796
|
+
if (!Array.isArray(raw) || raw.some((item) => typeof item !== "number" || !Number.isFinite(item))) {
|
|
177797
|
+
return null;
|
|
177798
|
+
}
|
|
177799
|
+
return { vector: Float32Array.from(raw), metadata: body };
|
|
177800
|
+
}
|
|
177801
|
+
function extractBatchItems(value) {
|
|
177802
|
+
const body = responseBody(value);
|
|
177803
|
+
const raw = Array.isArray(body.vectors) ? body.vectors : Array.isArray(body.items) ? body.items : Array.isArray(body.results) ? body.results : [];
|
|
177804
|
+
return raw.flatMap((item) => {
|
|
177805
|
+
const record2 = asRecord(item);
|
|
177806
|
+
return record2 ? [record2] : [];
|
|
177807
|
+
});
|
|
177808
|
+
}
|
|
177809
|
+
var sharedClient = null;
|
|
177810
|
+
var sharedClientFile = null;
|
|
177811
|
+
var sharedClientPromise = null;
|
|
177812
|
+
var factoryClients = new WeakMap;
|
|
177813
|
+
async function getSharedClient(options) {
|
|
177814
|
+
if (options.clientFactory) {
|
|
177815
|
+
let promise2 = factoryClients.get(options.clientFactory);
|
|
177816
|
+
if (!promise2) {
|
|
177817
|
+
promise2 = options.clientFactory();
|
|
177818
|
+
factoryClients.set(options.clientFactory, promise2);
|
|
177819
|
+
}
|
|
177820
|
+
return promise2;
|
|
177821
|
+
}
|
|
177822
|
+
if (sharedClient && sharedClientFile === options.connectionFile)
|
|
177823
|
+
return sharedClient;
|
|
177824
|
+
if (sharedClientPromise && sharedClientFile === options.connectionFile)
|
|
177825
|
+
return sharedClientPromise;
|
|
177826
|
+
sharedClientFile = options.connectionFile;
|
|
177827
|
+
sharedClientPromise = SubcClient.connect({ connectionFile: options.connectionFile }).then((client) => {
|
|
177828
|
+
sharedClient = client;
|
|
177829
|
+
return client;
|
|
177830
|
+
});
|
|
177831
|
+
return sharedClientPromise;
|
|
177832
|
+
}
|
|
177833
|
+
|
|
177834
|
+
class SynapseEmbeddingProvider {
|
|
177835
|
+
modelId;
|
|
177836
|
+
maxInputTokens = SYNAPSE_MAX_INPUT_TOKENS;
|
|
177837
|
+
metadata;
|
|
177838
|
+
options;
|
|
177839
|
+
client = null;
|
|
177840
|
+
initialized = false;
|
|
177841
|
+
initializing = null;
|
|
177842
|
+
permanentFailure = false;
|
|
177843
|
+
batchLimit = 16;
|
|
177844
|
+
constructor(options) {
|
|
177845
|
+
this.options = options;
|
|
177846
|
+
const model = options.model || SYNAPSE_DEFAULT_MODEL;
|
|
177847
|
+
const fingerprint = options.fingerprint ?? "";
|
|
177848
|
+
this.metadata = fingerprint && Number.isInteger(options.tableEpoch) && Number.isInteger(options.dims) && (options.dims ?? 0) > 0 ? {
|
|
177849
|
+
model,
|
|
177850
|
+
fingerprint,
|
|
177851
|
+
table_epoch: options.tableEpoch,
|
|
177852
|
+
dims: options.dims,
|
|
177853
|
+
...options.recommendedBatch ? { recommended_batch: Math.max(1, Math.floor(options.recommendedBatch)) } : {},
|
|
177854
|
+
...options.provenance !== undefined ? { provenance: options.provenance } : {},
|
|
177855
|
+
laneIdentity: getSynapseLaneIdentity(model, fingerprint)
|
|
177856
|
+
} : null;
|
|
177857
|
+
this.modelId = this.metadata?.laneIdentity ?? "synapse:v1:pending";
|
|
177858
|
+
this.batchLimit = this.metadata?.recommended_batch ?? 16;
|
|
177859
|
+
}
|
|
177860
|
+
static async discover(options) {
|
|
177861
|
+
const provider = new SynapseEmbeddingProvider(options);
|
|
177862
|
+
if (!await provider.initialize() || !provider.metadata) {
|
|
177863
|
+
throw new SynapseEmbeddingError("not_certified", "Synapse lane is not ready");
|
|
177864
|
+
}
|
|
177865
|
+
return provider.metadata;
|
|
177866
|
+
}
|
|
177867
|
+
async initialize() {
|
|
177868
|
+
if (this.initialized)
|
|
177869
|
+
return true;
|
|
177870
|
+
if (this.permanentFailure)
|
|
177871
|
+
return false;
|
|
177872
|
+
if (this.initializing)
|
|
177873
|
+
return this.initializing;
|
|
177874
|
+
this.initializing = (async () => {
|
|
177875
|
+
try {
|
|
177876
|
+
if (!this.options.clientFactory && !await connectionFileExists(this.options.connectionFile)) {
|
|
177877
|
+
throw new SynapseEmbeddingError("transport", `Synapse connection file is unavailable: ${this.options.connectionFile}`);
|
|
177878
|
+
}
|
|
177879
|
+
this.client = await getSharedClient(this.options);
|
|
177880
|
+
if (!this.metadata) {
|
|
177881
|
+
const discovered = await this.callWithRetry("models.list", {}, this.options.queryTimeoutMs ?? SYNAPSE_DEFAULT_QUERY_TIMEOUT_MS, false);
|
|
177882
|
+
const entries = extractCatalogEntries(discovered);
|
|
177883
|
+
const requested = this.options.model?.trim() || SYNAPSE_DEFAULT_MODEL;
|
|
177884
|
+
const entry = entries.find((candidate) => candidate.model === requested);
|
|
177885
|
+
if (!entry) {
|
|
177886
|
+
throw new SynapseEmbeddingError("artifact_invalid", `Synapse models.list did not return requested model ${requested}`);
|
|
177887
|
+
}
|
|
177888
|
+
if (entry.certified === false || entry.status === "not_certified") {
|
|
177889
|
+
throw new SynapseEmbeddingError("not_certified", `Synapse model ${entry.model} is not certified`);
|
|
177890
|
+
}
|
|
177891
|
+
const metadata = {
|
|
177892
|
+
...entry,
|
|
177893
|
+
laneIdentity: getSynapseLaneIdentity(entry.model, entry.fingerprint)
|
|
177894
|
+
};
|
|
177895
|
+
this.metadata = metadata;
|
|
177896
|
+
this.modelId = metadata.laneIdentity;
|
|
177897
|
+
this.batchLimit = metadata.recommended_batch ?? this.batchLimit;
|
|
177898
|
+
}
|
|
177899
|
+
this.initialized = true;
|
|
177900
|
+
return true;
|
|
177901
|
+
} catch (error51) {
|
|
177902
|
+
const classified = classifyError(error51);
|
|
177903
|
+
if (classified.permanent) {
|
|
177904
|
+
this.permanentFailure = true;
|
|
177905
|
+
log(`[magic-context] Synapse lane disabled: ${classified.code}: ${classified.message}`);
|
|
177906
|
+
} else {
|
|
177907
|
+
log(`[magic-context] Synapse lane unavailable: ${classified.message}`);
|
|
177908
|
+
}
|
|
177909
|
+
this.initialized = false;
|
|
177910
|
+
return false;
|
|
177911
|
+
} finally {
|
|
177912
|
+
this.initializing = null;
|
|
177913
|
+
}
|
|
177914
|
+
})();
|
|
177915
|
+
return this.initializing;
|
|
177916
|
+
}
|
|
177917
|
+
async embed(text, signal) {
|
|
177918
|
+
if (!await this.initialize() || signal?.aborted || !this.metadata)
|
|
177919
|
+
return null;
|
|
177920
|
+
try {
|
|
177921
|
+
const value = await this.callWithRetry("embed.query", this.requestConstraints({
|
|
177922
|
+
text,
|
|
177923
|
+
deadline_ms: this.options.queryTimeoutMs ?? SYNAPSE_DEFAULT_QUERY_TIMEOUT_MS
|
|
177924
|
+
}), this.options.queryTimeoutMs ?? SYNAPSE_DEFAULT_QUERY_TIMEOUT_MS, true, signal);
|
|
177925
|
+
const extracted = extractVector(value);
|
|
177926
|
+
if (!extracted)
|
|
177927
|
+
throw new SynapseEmbeddingError("schema_violation", "Synapse query returned no vector");
|
|
177928
|
+
this.validateResponse(extracted.metadata, extracted.vector.length);
|
|
177929
|
+
return extracted.vector;
|
|
177930
|
+
} catch (error51) {
|
|
177931
|
+
this.logCallFailure(error51, "embed.query");
|
|
177932
|
+
return null;
|
|
177933
|
+
}
|
|
177934
|
+
}
|
|
177935
|
+
async embedBatch(texts, signal) {
|
|
177936
|
+
if (texts.length === 0)
|
|
177937
|
+
return [];
|
|
177938
|
+
const items = texts.map((text, index) => ({
|
|
177939
|
+
id: `item:${index}`,
|
|
177940
|
+
text,
|
|
177941
|
+
contentSha256: hashContent(text)
|
|
177942
|
+
}));
|
|
177943
|
+
const map2 = await this.embedItems(items, signal);
|
|
177944
|
+
return items.map((item) => map2.get(item.id) ?? null);
|
|
177945
|
+
}
|
|
177946
|
+
async embedItems(items, signal) {
|
|
177947
|
+
const output = new Map;
|
|
177948
|
+
if (items.length === 0 || !await this.initialize() || !this.metadata || signal?.aborted) {
|
|
177949
|
+
return output;
|
|
177950
|
+
}
|
|
177951
|
+
for (let start = 0;start < items.length; start += this.batchLimit) {
|
|
177952
|
+
if (signal?.aborted || this.permanentFailure)
|
|
177953
|
+
break;
|
|
177954
|
+
const page = items.slice(start, start + this.batchLimit);
|
|
177955
|
+
try {
|
|
177956
|
+
const requestKey = this.requestKey(page);
|
|
177957
|
+
let body = {};
|
|
177958
|
+
let restarted = false;
|
|
177959
|
+
for (;; ) {
|
|
177960
|
+
try {
|
|
177961
|
+
body = await this.callWithRetry("embed.batch", this.batchRequest(page, requestKey), this.options.batchTimeoutMs ?? SYNAPSE_DEFAULT_BATCH_TIMEOUT_MS, true, signal);
|
|
177962
|
+
const first = responseBody(body);
|
|
177963
|
+
const jobId = typeof first.job_id === "string" ? first.job_id : null;
|
|
177964
|
+
if (jobId)
|
|
177965
|
+
body = await this.pollBatch(jobId, requestKey, signal);
|
|
177966
|
+
break;
|
|
177967
|
+
} catch (error51) {
|
|
177968
|
+
const classified = classifyError(error51);
|
|
177969
|
+
if (classified.code !== "module_restarted" || restarted)
|
|
177970
|
+
throw classified;
|
|
177971
|
+
restarted = true;
|
|
177972
|
+
}
|
|
177973
|
+
}
|
|
177974
|
+
const batchEnvelope = responseBody(body);
|
|
177975
|
+
for (const item of extractBatchItems(body)) {
|
|
177976
|
+
const id = typeof item.id === "string" ? item.id : "";
|
|
177977
|
+
const vector = item.vector ?? item.embedding;
|
|
177978
|
+
if (!id || !Array.isArray(vector)) {
|
|
177979
|
+
throw new SynapseEmbeddingError("schema_violation", "Synapse batch item is malformed");
|
|
177980
|
+
}
|
|
177981
|
+
const expected = page.find((candidate) => candidate.id === id);
|
|
177982
|
+
if (!expected) {
|
|
177983
|
+
throw new SynapseEmbeddingError("schema_violation", `Synapse returned unknown item ${id}`);
|
|
177984
|
+
}
|
|
177985
|
+
if (typeof item.content_sha256 === "string" && item.content_sha256 !== expected.contentSha256) {
|
|
177986
|
+
throw new SynapseEmbeddingError("artifact_invalid", `Synapse content hash mismatch for item ${id}`);
|
|
177987
|
+
}
|
|
177988
|
+
const vectorArray = Float32Array.from(vector);
|
|
177989
|
+
this.validateResponse({ ...batchEnvelope, ...item }, vectorArray.length);
|
|
177990
|
+
output.set(id, vectorArray);
|
|
177991
|
+
}
|
|
177992
|
+
} catch (error51) {
|
|
177993
|
+
const classified = classifyError(error51);
|
|
177994
|
+
this.logCallFailure(classified, "embed.batch");
|
|
177995
|
+
if (classified.code === "idempotency_conflict")
|
|
177996
|
+
throw classified;
|
|
177997
|
+
if (classified.permanent) {
|
|
177998
|
+
this.permanentFailure = true;
|
|
177999
|
+
this.initialized = false;
|
|
178000
|
+
break;
|
|
178001
|
+
}
|
|
178002
|
+
}
|
|
178003
|
+
}
|
|
178004
|
+
return output;
|
|
178005
|
+
}
|
|
178006
|
+
async dispose() {
|
|
178007
|
+
this.initialized = false;
|
|
178008
|
+
this.client = null;
|
|
178009
|
+
}
|
|
178010
|
+
isLoaded() {
|
|
178011
|
+
return this.initialized;
|
|
178012
|
+
}
|
|
178013
|
+
requestConstraints(extra) {
|
|
178014
|
+
const metadata = this.metadata;
|
|
178015
|
+
if (!metadata)
|
|
178016
|
+
return extra;
|
|
178017
|
+
return {
|
|
178018
|
+
...extra,
|
|
178019
|
+
model: metadata.model,
|
|
178020
|
+
required_fingerprint: metadata.fingerprint,
|
|
178021
|
+
required_epoch: metadata.table_epoch,
|
|
178022
|
+
allow_equivalent: false,
|
|
178023
|
+
accept_declared: false
|
|
178024
|
+
};
|
|
178025
|
+
}
|
|
178026
|
+
batchRequest(items, requestKey) {
|
|
178027
|
+
return this.requestConstraints({
|
|
178028
|
+
items: items.map((item) => ({
|
|
178029
|
+
id: item.id,
|
|
178030
|
+
text: item.text,
|
|
178031
|
+
content_sha256: item.contentSha256
|
|
178032
|
+
})),
|
|
178033
|
+
request_key: requestKey
|
|
178034
|
+
});
|
|
178035
|
+
}
|
|
178036
|
+
requestKey(items) {
|
|
178037
|
+
if (!this.metadata)
|
|
178038
|
+
throw new SynapseEmbeddingError("transport", "Synapse metadata is unavailable");
|
|
178039
|
+
return getSynapseBatchRequestKey({
|
|
178040
|
+
model: this.metadata.model,
|
|
178041
|
+
fingerprint: this.metadata.fingerprint,
|
|
178042
|
+
tableEpoch: this.metadata.table_epoch,
|
|
178043
|
+
items
|
|
178044
|
+
});
|
|
178045
|
+
}
|
|
178046
|
+
async pollBatch(jobId, requestKey, signal) {
|
|
178047
|
+
let cursor = null;
|
|
178048
|
+
const allItems = [];
|
|
178049
|
+
for (;; ) {
|
|
178050
|
+
if (signal?.aborted)
|
|
178051
|
+
return {};
|
|
178052
|
+
const body = await this.callWithRetry("embed.result", this.requestConstraints({
|
|
178053
|
+
job_id: jobId,
|
|
178054
|
+
cursor,
|
|
178055
|
+
request_key: requestKey
|
|
178056
|
+
}), this.options.batchTimeoutMs ?? SYNAPSE_DEFAULT_BATCH_TIMEOUT_MS, true, signal);
|
|
178057
|
+
const parsed = responseBody(body);
|
|
178058
|
+
const items = extractBatchItems(body);
|
|
178059
|
+
allItems.push(...items);
|
|
178060
|
+
const nextCursor = parsed.next_cursor ?? parsed.cursor;
|
|
178061
|
+
const done = parsed.done === true || parsed.complete === true || nextCursor === undefined || nextCursor === null;
|
|
178062
|
+
if (done)
|
|
178063
|
+
return { ...parsed, items: allItems };
|
|
178064
|
+
cursor = nextCursor;
|
|
178065
|
+
}
|
|
178066
|
+
}
|
|
178067
|
+
async callWithRetry(method, params, timeoutMs, retryEmbeddings, signal) {
|
|
178068
|
+
let attempt = 0;
|
|
178069
|
+
for (;; ) {
|
|
178070
|
+
if (signal?.aborted)
|
|
178071
|
+
throw new SynapseEmbeddingError("transport", "Synapse request aborted");
|
|
178072
|
+
try {
|
|
178073
|
+
if (!this.client)
|
|
178074
|
+
throw new SynapseEmbeddingError("transport", "Synapse client is unavailable");
|
|
178075
|
+
return await this.client.call(this.options.moduleId ?? "synapse", method, params, {
|
|
178076
|
+
timeoutMs,
|
|
178077
|
+
targetKind: "management_surface",
|
|
178078
|
+
identity: {
|
|
178079
|
+
project_root: this.options.projectRoot,
|
|
178080
|
+
harness: getHarness(),
|
|
178081
|
+
session: this.options.session
|
|
178082
|
+
}
|
|
178083
|
+
});
|
|
178084
|
+
} catch (error51) {
|
|
178085
|
+
const classified = classifyError(error51);
|
|
178086
|
+
if (classified.code === "idempotency_conflict")
|
|
178087
|
+
throw classified;
|
|
178088
|
+
const outcomeUnknown = error51 instanceof SubcCallError && error51.kind === "outcome_unknown";
|
|
178089
|
+
const retryable = !classified.permanent && (retryEmbeddings || !outcomeUnknown);
|
|
178090
|
+
if (!retryable || attempt >= 3)
|
|
178091
|
+
throw classified;
|
|
178092
|
+
const delay = classified.retryAfterMs ?? Math.min(2000, 100 * 2 ** attempt);
|
|
178093
|
+
attempt += 1;
|
|
178094
|
+
await wait(delay);
|
|
178095
|
+
}
|
|
178096
|
+
}
|
|
178097
|
+
}
|
|
178098
|
+
validateResponse(body, dims) {
|
|
178099
|
+
const metadata = this.metadata;
|
|
178100
|
+
if (!metadata) {
|
|
178101
|
+
throw new SynapseEmbeddingError("artifact_invalid", "Synapse lane metadata missing");
|
|
178102
|
+
}
|
|
178103
|
+
if (metadata.dims === undefined) {
|
|
178104
|
+
const envelopeDims = body.dims;
|
|
178105
|
+
if (typeof envelopeDims === "number" && envelopeDims !== dims) {
|
|
178106
|
+
throw new SynapseEmbeddingError("artifact_invalid", `Synapse envelope declares ${envelopeDims} dimensions but the vector has ${dims}`);
|
|
178107
|
+
}
|
|
178108
|
+
metadata.dims = dims;
|
|
178109
|
+
}
|
|
178110
|
+
if (dims !== metadata.dims) {
|
|
178111
|
+
throw new SynapseEmbeddingError("artifact_invalid", `Synapse returned ${dims} dimensions, expected ${metadata.dims}`);
|
|
178112
|
+
}
|
|
178113
|
+
const fingerprint = body.fingerprint ?? body.served_fingerprint;
|
|
178114
|
+
if (typeof fingerprint !== "string") {
|
|
178115
|
+
throw new SynapseEmbeddingError("artifact_invalid", "Synapse response omitted the served fingerprint");
|
|
178116
|
+
}
|
|
178117
|
+
if (fingerprint !== metadata.fingerprint) {
|
|
178118
|
+
throw new SynapseEmbeddingError("substitution_rejected", `Synapse fingerprint changed from ${metadata.fingerprint} to ${fingerprint}`);
|
|
178119
|
+
}
|
|
178120
|
+
const epoch = body.table_epoch ?? body.tableEpoch;
|
|
178121
|
+
if (typeof epoch !== "number") {
|
|
178122
|
+
throw new SynapseEmbeddingError("artifact_invalid", "Synapse response omitted the served table epoch");
|
|
178123
|
+
}
|
|
178124
|
+
if (epoch !== metadata.table_epoch) {
|
|
178125
|
+
throw new SynapseEmbeddingError("substitution_rejected", `Synapse table epoch changed from ${metadata.table_epoch} to ${epoch}`);
|
|
178126
|
+
}
|
|
178127
|
+
}
|
|
178128
|
+
logCallFailure(error51, operation) {
|
|
178129
|
+
const classified = classifyError(error51);
|
|
178130
|
+
if (classified.permanent)
|
|
178131
|
+
this.permanentFailure = true;
|
|
178132
|
+
const suffix = classified.retryAfterMs === undefined ? "" : ` retry_after_ms=${classified.retryAfterMs}`;
|
|
178133
|
+
log(`[magic-context] Synapse ${operation} failed: ${classified.code}${suffix}: ${classified.message}`);
|
|
178134
|
+
}
|
|
178135
|
+
}
|
|
178136
|
+
|
|
175087
178137
|
// ../plugin/src/features/magic-context/memory/normalize-hash.ts
|
|
175088
|
-
import { createHash as
|
|
178138
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
175089
178139
|
function normalizeMemoryContent(content) {
|
|
175090
178140
|
return content.toLowerCase().replace(/\s+/g, " ").trim();
|
|
175091
178141
|
}
|
|
175092
178142
|
function computeNormalizedHash(content) {
|
|
175093
178143
|
const normalized = normalizeMemoryContent(content);
|
|
175094
|
-
return
|
|
178144
|
+
return createHash10("md5").update(normalized).digest("hex");
|
|
175095
178145
|
}
|
|
175096
178146
|
|
|
175097
178147
|
// ../plugin/src/features/magic-context/memory/embedding-identity.ts
|
|
@@ -175102,6 +178152,15 @@ function getEmbeddingProviderIdentity(config2) {
|
|
|
175102
178152
|
if (config2.provider === "off") {
|
|
175103
178153
|
return "embedding-provider:off";
|
|
175104
178154
|
}
|
|
178155
|
+
if (config2.provider === "synapse") {
|
|
178156
|
+
const resolved = config2;
|
|
178157
|
+
if (!resolved.model || !resolved.synapse_fingerprint)
|
|
178158
|
+
return "synapse:v1:pending";
|
|
178159
|
+
return getSynapseLaneIdentity(resolved.model, resolved.synapse_fingerprint);
|
|
178160
|
+
}
|
|
178161
|
+
if (config2.provider !== "local" && config2.provider !== "openai-compatible") {
|
|
178162
|
+
throw new Error("Unknown embedding provider");
|
|
178163
|
+
}
|
|
175105
178164
|
const truncate = config2.provider === "openai-compatible" ? config2.truncate?.trim() : undefined;
|
|
175106
178165
|
const identityInput = config2.provider === "openai-compatible" ? {
|
|
175107
178166
|
provider: "openai-compatible",
|
|
@@ -175891,7 +178950,7 @@ function getMemoryEmbedCoverage(db, projectPath, modelId) {
|
|
|
175891
178950
|
}
|
|
175892
178951
|
|
|
175893
178952
|
// ../plugin/src/features/magic-context/project-embedding-registry.ts
|
|
175894
|
-
import { createHash as
|
|
178953
|
+
import { createHash as createHash11, randomUUID as randomUUID2 } from "node:crypto";
|
|
175895
178954
|
init_logger();
|
|
175896
178955
|
|
|
175897
178956
|
// ../plugin/src/features/magic-context/git-commits/storage-git-commit-embeddings.ts
|
|
@@ -176282,6 +179341,12 @@ var EMBED_SLICE_RETRY_BASE_MS = 250;
|
|
|
176282
179341
|
var EMBED_SLOW_FAILURE_NO_RETRY_MS = 1e4;
|
|
176283
179342
|
var MAX_CONSECUTIVE_FAILED_BATCHES = 3;
|
|
176284
179343
|
var projectRegistrations = new Map;
|
|
179344
|
+
var shadowRegistrations = new Map;
|
|
179345
|
+
var shadowQueue = [];
|
|
179346
|
+
var shadowWorker = null;
|
|
179347
|
+
var SHADOW_MAX_ITEMS_PER_TICK = 64;
|
|
179348
|
+
var SHADOW_MAX_BYTES_PER_TICK = 512 * 1024;
|
|
179349
|
+
var SHADOW_MAX_WALL_CLOCK_MS = 2000;
|
|
176285
179350
|
var loadUnembeddedMemoriesStatements = new WeakMap;
|
|
176286
179351
|
var upsertActiveIdentityStatements = new WeakMap;
|
|
176287
179352
|
var backfillActiveIdentityStatements = new Map;
|
|
@@ -176293,6 +179358,35 @@ function markProjectLoadUntrusted(projectIdentity) {
|
|
|
176293
179358
|
untrustedLoadProjects.add(projectIdentity);
|
|
176294
179359
|
}
|
|
176295
179360
|
var testProviderFactory = null;
|
|
179361
|
+
function synapseConfigFields(config2) {
|
|
179362
|
+
const raw = config2;
|
|
179363
|
+
return {
|
|
179364
|
+
...typeof raw.model === "string" ? { model: raw.model } : {},
|
|
179365
|
+
...typeof raw.synapse_fingerprint === "string" ? { fingerprint: raw.synapse_fingerprint } : {},
|
|
179366
|
+
...typeof raw.synapse_table_epoch === "number" ? { tableEpoch: raw.synapse_table_epoch } : {},
|
|
179367
|
+
...typeof raw.synapse_dims === "number" ? { dims: raw.synapse_dims } : {},
|
|
179368
|
+
...raw.synapse_provenance !== undefined ? { provenance: raw.synapse_provenance } : {}
|
|
179369
|
+
};
|
|
179370
|
+
}
|
|
179371
|
+
function persistPrimaryDescriptor(db, registration) {
|
|
179372
|
+
const descriptorTable = db.prepare("SELECT 1 AS present FROM sqlite_master WHERE type = 'table' AND name = 'embedding_registrations'").get();
|
|
179373
|
+
if (!descriptorTable)
|
|
179374
|
+
return;
|
|
179375
|
+
const fields = synapseConfigFields(registration.config);
|
|
179376
|
+
db.prepare(`INSERT INTO embedding_registrations
|
|
179377
|
+
(project_path, provider_identity, model_id, chunk_model_id, fingerprint, table_epoch, dims, provenance_json, generation, updated_at)
|
|
179378
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
179379
|
+
ON CONFLICT(project_path) DO UPDATE SET
|
|
179380
|
+
provider_identity = excluded.provider_identity,
|
|
179381
|
+
model_id = excluded.model_id,
|
|
179382
|
+
chunk_model_id = excluded.chunk_model_id,
|
|
179383
|
+
fingerprint = excluded.fingerprint,
|
|
179384
|
+
table_epoch = excluded.table_epoch,
|
|
179385
|
+
dims = excluded.dims,
|
|
179386
|
+
provenance_json = excluded.provenance_json,
|
|
179387
|
+
generation = excluded.generation,
|
|
179388
|
+
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());
|
|
179389
|
+
}
|
|
176296
179390
|
function resolveEmbeddingConfig(config2) {
|
|
176297
179391
|
if (!config2 || config2.provider === "local") {
|
|
176298
179392
|
return {
|
|
@@ -176321,9 +179415,26 @@ function resolveEmbeddingConfig(config2) {
|
|
|
176321
179415
|
} : {}
|
|
176322
179416
|
};
|
|
176323
179417
|
}
|
|
176324
|
-
|
|
179418
|
+
if (config2.provider === "off") {
|
|
179419
|
+
return { provider: "off" };
|
|
179420
|
+
}
|
|
179421
|
+
if (config2.provider === "synapse") {
|
|
179422
|
+
const synapse = config2;
|
|
179423
|
+
return {
|
|
179424
|
+
provider: "synapse",
|
|
179425
|
+
model: synapse.model?.trim() || "gte-modernbert-base-f16",
|
|
179426
|
+
max_input_tokens: SYNAPSE_MAX_INPUT_TOKENS,
|
|
179427
|
+
...synapse.synapse_connection_file ? { synapse_connection_file: synapse.synapse_connection_file } : {},
|
|
179428
|
+
...synapse.synapse_fingerprint ? { synapse_fingerprint: synapse.synapse_fingerprint } : {},
|
|
179429
|
+
...typeof synapse.synapse_table_epoch === "number" ? { synapse_table_epoch: synapse.synapse_table_epoch } : {},
|
|
179430
|
+
...typeof synapse.synapse_dims === "number" ? { synapse_dims: synapse.synapse_dims } : {},
|
|
179431
|
+
...typeof synapse.synapse_recommended_batch === "number" ? { synapse_recommended_batch: synapse.synapse_recommended_batch } : {},
|
|
179432
|
+
...synapse.synapse_provenance !== undefined ? { synapse_provenance: synapse.synapse_provenance } : {}
|
|
179433
|
+
};
|
|
179434
|
+
}
|
|
179435
|
+
throw new Error("Unknown embedding provider");
|
|
176325
179436
|
}
|
|
176326
|
-
function createProvider(config2) {
|
|
179437
|
+
function createProvider(config2, context) {
|
|
176327
179438
|
if (testProviderFactory) {
|
|
176328
179439
|
return testProviderFactory(config2);
|
|
176329
179440
|
}
|
|
@@ -176341,7 +179452,24 @@ function createProvider(config2) {
|
|
|
176341
179452
|
maxInputTokens: config2.max_input_tokens
|
|
176342
179453
|
});
|
|
176343
179454
|
}
|
|
176344
|
-
|
|
179455
|
+
if (config2.provider === "local") {
|
|
179456
|
+
return new LocalEmbeddingProvider(config2.model, config2.max_input_tokens);
|
|
179457
|
+
}
|
|
179458
|
+
if (config2.provider === "synapse") {
|
|
179459
|
+
const synapse = config2;
|
|
179460
|
+
return new SynapseEmbeddingProvider({
|
|
179461
|
+
connectionFile: synapse.synapse_connection_file ?? "",
|
|
179462
|
+
projectRoot: context?.projectRoot ?? "",
|
|
179463
|
+
session: context?.session ?? "embedding",
|
|
179464
|
+
model: synapse.model,
|
|
179465
|
+
fingerprint: synapse.synapse_fingerprint,
|
|
179466
|
+
tableEpoch: synapse.synapse_table_epoch,
|
|
179467
|
+
dims: synapse.synapse_dims,
|
|
179468
|
+
recommendedBatch: synapse.synapse_recommended_batch,
|
|
179469
|
+
provenance: synapse.synapse_provenance
|
|
179470
|
+
});
|
|
179471
|
+
}
|
|
179472
|
+
throw new Error("Unknown embedding provider");
|
|
176345
179473
|
}
|
|
176346
179474
|
function stableStringify2(value) {
|
|
176347
179475
|
if (Array.isArray(value)) {
|
|
@@ -176354,7 +179482,10 @@ function stableStringify2(value) {
|
|
|
176354
179482
|
return JSON.stringify(value);
|
|
176355
179483
|
}
|
|
176356
179484
|
function sha256Prefix(value, length = 16) {
|
|
176357
|
-
return
|
|
179485
|
+
return createHash11("sha256").update(value).digest("hex").slice(0, length);
|
|
179486
|
+
}
|
|
179487
|
+
function contentSha256(value) {
|
|
179488
|
+
return createHash11("sha256").update(value).digest("hex");
|
|
176358
179489
|
}
|
|
176359
179490
|
function getRuntimeFingerprint(config2) {
|
|
176360
179491
|
if (config2.provider === "off") {
|
|
@@ -176381,6 +179512,7 @@ function snapshotFor(registration) {
|
|
|
176381
179512
|
const providerIsOn = registration.providerIdentity !== OFF_PROVIDER_IDENTITY;
|
|
176382
179513
|
const enabled = !registration.observationMode && providerIsOn && registration.features.memoryEnabled;
|
|
176383
179514
|
const gitCommitEnabled = !registration.observationMode && providerIsOn && registration.features.gitCommitEnabled;
|
|
179515
|
+
const configuredModel = "model" in registration.config && typeof registration.config.model === "string" ? registration.config.model.trim() : "";
|
|
176384
179516
|
return {
|
|
176385
179517
|
projectIdentity: registration.projectIdentity,
|
|
176386
179518
|
sourceDirectory: registration.sourceDirectory,
|
|
@@ -176392,7 +179524,7 @@ function snapshotFor(registration) {
|
|
|
176392
179524
|
gitCommitEnabled,
|
|
176393
179525
|
modelId: registration.observationMode || !providerIsOn ? "off" : registration.modelId,
|
|
176394
179526
|
chunkModelId: registration.observationMode || !providerIsOn ? "off" : registration.chunkModelId,
|
|
176395
|
-
model: registration.observationMode || !providerIsOn ? "off" :
|
|
179527
|
+
model: registration.observationMode || !providerIsOn ? "off" : configuredModel ? configuredModel : registration.modelId,
|
|
176396
179528
|
provider: registration.observationMode || !providerIsOn ? "off" : registration.config.provider ?? "local"
|
|
176397
179529
|
};
|
|
176398
179530
|
}
|
|
@@ -176499,9 +179631,9 @@ function getDeleteActiveIdentityStatement(db) {
|
|
|
176499
179631
|
}
|
|
176500
179632
|
return stmt;
|
|
176501
179633
|
}
|
|
176502
|
-
function staleModelsForScope(db, projectIdentity, scope, currentModelId, cutoff) {
|
|
179634
|
+
function staleModelsForScope(db, projectIdentity, scope, currentModelId, cutoff, protectedModelIds = new Set([currentModelId])) {
|
|
176503
179635
|
const rows = getStaleIdentityStatement(db, scope).all(projectIdentity, scope, currentModelId, cutoff);
|
|
176504
|
-
return rows.map((row) => row.modelId).filter((modelId) => typeof modelId === "string");
|
|
179636
|
+
return rows.map((row) => row.modelId).filter((modelId) => typeof modelId === "string" && !protectedModelIds.has(modelId));
|
|
176505
179637
|
}
|
|
176506
179638
|
function deleteStaleEmbeddingBatch(db, scope, projectIdentity, modelId, limit) {
|
|
176507
179639
|
if (scope === "memory") {
|
|
@@ -176566,6 +179698,12 @@ function sweepStaleEmbeddingIdentitiesForProject(db, projectIdentity, now = Date
|
|
|
176566
179698
|
return result;
|
|
176567
179699
|
const cutoff = now - EMBEDDING_IDENTITY_GC_GRACE_MS;
|
|
176568
179700
|
const deleteTracking = getDeleteActiveIdentityStatement(db);
|
|
179701
|
+
const shadow = shadowRegistrations.get(projectIdentity);
|
|
179702
|
+
const protectedModels = {
|
|
179703
|
+
memory: new Set([snapshot.modelId, ...shadow ? [shadow.modelId] : []]),
|
|
179704
|
+
commit: new Set([snapshot.modelId, ...shadow ? [shadow.modelId] : []]),
|
|
179705
|
+
chunk: new Set([snapshot.chunkModelId, ...shadow ? [shadow.chunkModelId] : []])
|
|
179706
|
+
};
|
|
176569
179707
|
const scopes = [
|
|
176570
179708
|
{
|
|
176571
179709
|
scope: "memory",
|
|
@@ -176589,7 +179727,7 @@ function sweepStaleEmbeddingIdentitiesForProject(db, projectIdentity, now = Date
|
|
|
176589
179727
|
for (const { scope, enabled, currentModelId } of scopes) {
|
|
176590
179728
|
if (!enabled || remainingBudget === 0)
|
|
176591
179729
|
continue;
|
|
176592
|
-
for (const modelId of staleModelsForScope(db, projectIdentity, scope, currentModelId, cutoff)) {
|
|
179730
|
+
for (const modelId of staleModelsForScope(db, projectIdentity, scope, currentModelId, cutoff, protectedModels[scope])) {
|
|
176593
179731
|
if (remainingBudget === 0)
|
|
176594
179732
|
break;
|
|
176595
179733
|
const deleted = deleteStaleEmbeddingBatch(db, scope, projectIdentity, modelId, remainingBudget);
|
|
@@ -176643,11 +179781,206 @@ function registerProjectEmbedding(db, projectIdentity, config2, features, source
|
|
|
176643
179781
|
observationMode: false
|
|
176644
179782
|
};
|
|
176645
179783
|
projectRegistrations.set(projectIdentity, registration);
|
|
179784
|
+
persistPrimaryDescriptor(db, registration);
|
|
176646
179785
|
if (!canReuseProvider) {
|
|
176647
179786
|
disposeProvider(prior?.provider ?? null);
|
|
176648
179787
|
}
|
|
176649
179788
|
return snapshotFor(registration);
|
|
176650
179789
|
}
|
|
179790
|
+
function startShadowWorker() {
|
|
179791
|
+
if (shadowWorker)
|
|
179792
|
+
return;
|
|
179793
|
+
shadowWorker = runShadowWorker().finally(() => {
|
|
179794
|
+
shadowWorker = null;
|
|
179795
|
+
if (shadowQueue.length > 0)
|
|
179796
|
+
startShadowWorker();
|
|
179797
|
+
});
|
|
179798
|
+
}
|
|
179799
|
+
function getShadowEmbeddingMeasurementCohort(projectIdentity) {
|
|
179800
|
+
const registration = shadowRegistrations.get(projectIdentity);
|
|
179801
|
+
if (!registration)
|
|
179802
|
+
return null;
|
|
179803
|
+
const fields = synapseConfigFields(registration.config);
|
|
179804
|
+
return {
|
|
179805
|
+
modelId: registration.modelId,
|
|
179806
|
+
chunkModelId: registration.chunkModelId,
|
|
179807
|
+
fingerprint: fields.fingerprint ?? "",
|
|
179808
|
+
epoch: fields.tableEpoch ?? 0,
|
|
179809
|
+
dims: fields.dims ?? 0
|
|
179810
|
+
};
|
|
179811
|
+
}
|
|
179812
|
+
function getPrimaryEmbeddingMeasurementCohort(projectIdentity) {
|
|
179813
|
+
const registration = projectRegistrations.get(projectIdentity);
|
|
179814
|
+
if (!registration)
|
|
179815
|
+
return null;
|
|
179816
|
+
const fields = synapseConfigFields(registration.config);
|
|
179817
|
+
return {
|
|
179818
|
+
modelId: registration.modelId,
|
|
179819
|
+
chunkModelId: registration.chunkModelId,
|
|
179820
|
+
fingerprint: fields.fingerprint ?? "",
|
|
179821
|
+
epoch: fields.tableEpoch ?? 0,
|
|
179822
|
+
dims: fields.dims ?? 0
|
|
179823
|
+
};
|
|
179824
|
+
}
|
|
179825
|
+
async function embedShadowTextForProject(projectIdentity, text, signal) {
|
|
179826
|
+
const registration = shadowRegistrations.get(projectIdentity);
|
|
179827
|
+
if (!registration)
|
|
179828
|
+
return null;
|
|
179829
|
+
try {
|
|
179830
|
+
return await registration.provider.embed(text, signal, "query");
|
|
179831
|
+
} catch (error51) {
|
|
179832
|
+
log("[magic-context] Synapse shadow query failed:", error51);
|
|
179833
|
+
return null;
|
|
179834
|
+
}
|
|
179835
|
+
}
|
|
179836
|
+
function enqueueShadowEmbeddingItems(projectIdentity, scope, ids) {
|
|
179837
|
+
if (ids.length === 0 || !shadowRegistrations.has(projectIdentity))
|
|
179838
|
+
return;
|
|
179839
|
+
shadowQueue.push({ projectIdentity, scope, ids: [...ids] });
|
|
179840
|
+
startShadowWorker();
|
|
179841
|
+
}
|
|
179842
|
+
async function embedShadowItems(registration, items, db, scope) {
|
|
179843
|
+
const raw = registration.config;
|
|
179844
|
+
const fingerprint = typeof raw.synapse_fingerprint === "string" ? raw.synapse_fingerprint : "";
|
|
179845
|
+
const tableEpoch = typeof raw.synapse_table_epoch === "number" ? raw.synapse_table_epoch : 0;
|
|
179846
|
+
const requestKey = getSynapseBatchRequestKey({
|
|
179847
|
+
model: typeof raw.model === "string" ? raw.model : SYNAPSE_DEFAULT_MODEL,
|
|
179848
|
+
fingerprint,
|
|
179849
|
+
tableEpoch,
|
|
179850
|
+
items
|
|
179851
|
+
});
|
|
179852
|
+
beginSynapseBatchLedger(db, {
|
|
179853
|
+
sessionId: `shadow:${registration.projectIdentity}`,
|
|
179854
|
+
projectPath: registration.projectIdentity,
|
|
179855
|
+
scope,
|
|
179856
|
+
manifest: items.map(({ id, contentSha256: contentSha2562 }) => ({ id, contentSha256: contentSha2562 })),
|
|
179857
|
+
requestKey
|
|
179858
|
+
});
|
|
179859
|
+
try {
|
|
179860
|
+
if (registration.provider.embedItems) {
|
|
179861
|
+
const vectors2 = await registration.provider.embedItems(items);
|
|
179862
|
+
finishSynapseBatchLedger(db, `shadow:${registration.projectIdentity}`, requestKey, vectors2.size === items.length ? "complete" : "partial");
|
|
179863
|
+
return vectors2;
|
|
179864
|
+
}
|
|
179865
|
+
const positional = await registration.provider.embedBatch(items.map((item) => item.text));
|
|
179866
|
+
const vectors = new Map(items.flatMap((item, index) => {
|
|
179867
|
+
const vector = positional[index];
|
|
179868
|
+
return vector ? [[item.id, vector]] : [];
|
|
179869
|
+
}));
|
|
179870
|
+
finishSynapseBatchLedger(db, `shadow:${registration.projectIdentity}`, requestKey, vectors.size === items.length ? "complete" : "partial");
|
|
179871
|
+
return vectors;
|
|
179872
|
+
} catch (error51) {
|
|
179873
|
+
finishSynapseBatchLedger(db, `shadow:${registration.projectIdentity}`, requestKey, "failed");
|
|
179874
|
+
throw error51;
|
|
179875
|
+
}
|
|
179876
|
+
}
|
|
179877
|
+
async function processShadowQueueItem(item) {
|
|
179878
|
+
const registration = shadowRegistrations.get(item.projectIdentity);
|
|
179879
|
+
if (!registration)
|
|
179880
|
+
return;
|
|
179881
|
+
const boundedIds = item.ids.slice(0, SHADOW_MAX_ITEMS_PER_TICK);
|
|
179882
|
+
if (item.scope === "memory") {
|
|
179883
|
+
const db2 = dbForShadowQueue.get(item.projectIdentity);
|
|
179884
|
+
if (!db2)
|
|
179885
|
+
return;
|
|
179886
|
+
const placeholders4 = boundedIds.map(() => "?").join(",");
|
|
179887
|
+
const rows = db2.prepare(`SELECT id, content, normalized_hash FROM memories
|
|
179888
|
+
WHERE project_path = ? AND id IN (${placeholders4}) AND status = 'active'`).all(item.projectIdentity, ...boundedIds.map((id) => Number(id)));
|
|
179889
|
+
const vectors2 = await embedShadowItems(registration, rows.map((row) => ({
|
|
179890
|
+
id: `memory:${row.id}`,
|
|
179891
|
+
text: row.content,
|
|
179892
|
+
contentSha256: contentSha256(row.content)
|
|
179893
|
+
})), db2, "memory");
|
|
179894
|
+
db2.transaction(() => {
|
|
179895
|
+
for (const row of rows) {
|
|
179896
|
+
const vector = vectors2.get(`memory:${row.id}`);
|
|
179897
|
+
if (vector)
|
|
179898
|
+
saveEmbeddingIfHashMatches(db2, row.id, vector, registration.modelId, row.normalized_hash);
|
|
179899
|
+
}
|
|
179900
|
+
})();
|
|
179901
|
+
return;
|
|
179902
|
+
}
|
|
179903
|
+
if (item.scope === "commit") {
|
|
179904
|
+
const db2 = dbForShadowQueue.get(item.projectIdentity);
|
|
179905
|
+
if (!db2)
|
|
179906
|
+
return;
|
|
179907
|
+
const placeholders4 = boundedIds.map(() => "?").join(",");
|
|
179908
|
+
const rows = db2.prepare(`SELECT sha, message FROM git_commits WHERE project_path = ? AND sha IN (${placeholders4})`).all(item.projectIdentity, ...boundedIds);
|
|
179909
|
+
const vectors2 = await embedShadowItems(registration, rows.map((row) => ({
|
|
179910
|
+
id: `commit:${row.sha}`,
|
|
179911
|
+
text: row.message,
|
|
179912
|
+
contentSha256: contentSha256(row.message)
|
|
179913
|
+
})), db2, "commit");
|
|
179914
|
+
db2.transaction(() => {
|
|
179915
|
+
for (const row of rows) {
|
|
179916
|
+
const vector = vectors2.get(`commit:${row.sha}`);
|
|
179917
|
+
if (vector)
|
|
179918
|
+
saveCommitEmbedding(db2, row.sha, vector, registration.modelId);
|
|
179919
|
+
}
|
|
179920
|
+
})();
|
|
179921
|
+
return;
|
|
179922
|
+
}
|
|
179923
|
+
const db = dbForShadowQueue.get(item.projectIdentity);
|
|
179924
|
+
if (!db)
|
|
179925
|
+
return;
|
|
179926
|
+
const placeholders3 = boundedIds.map(() => "?").join(",");
|
|
179927
|
+
const candidates = db.prepare(`SELECT id, session_id, start_message, end_message
|
|
179928
|
+
FROM compartments WHERE id IN (${placeholders3})`).all(...boundedIds.map((id) => Number(id)));
|
|
179929
|
+
const prepared = candidates.flatMap((candidate) => {
|
|
179930
|
+
const text = buildCanonicalChunkTextFromFts(db, candidate.session_id, candidate.start_message, candidate.end_message) || buildCompartmentSummaryFallbackText(db, candidate.id);
|
|
179931
|
+
if (!text)
|
|
179932
|
+
return [];
|
|
179933
|
+
const windows = chunkCanonicalText(text, candidate.start_message, candidate.end_message, SYNAPSE_MAX_INPUT_TOKENS);
|
|
179934
|
+
return windows.length > 0 ? [{ candidate, windows }] : [];
|
|
179935
|
+
});
|
|
179936
|
+
const items = prepared.flatMap((item2) => item2.windows.map((window) => ({
|
|
179937
|
+
id: `chunk:${item2.candidate.id}:${window.windowIndex}`,
|
|
179938
|
+
text: window.text,
|
|
179939
|
+
contentSha256: contentSha256(window.text)
|
|
179940
|
+
})));
|
|
179941
|
+
const vectors = await embedShadowItems(registration, items, db, "chunk");
|
|
179942
|
+
for (const item2 of prepared) {
|
|
179943
|
+
const rows = item2.windows.flatMap((window) => {
|
|
179944
|
+
const vector = vectors.get(`chunk:${item2.candidate.id}:${window.windowIndex}`);
|
|
179945
|
+
return vector ? [
|
|
179946
|
+
{
|
|
179947
|
+
compartmentId: item2.candidate.id,
|
|
179948
|
+
sessionId: item2.candidate.session_id,
|
|
179949
|
+
projectPath: registration.projectIdentity,
|
|
179950
|
+
window,
|
|
179951
|
+
modelId: registration.chunkModelId,
|
|
179952
|
+
vector
|
|
179953
|
+
}
|
|
179954
|
+
] : [];
|
|
179955
|
+
});
|
|
179956
|
+
if (rows.length === item2.windows.length)
|
|
179957
|
+
replaceCompartmentChunkEmbeddings(db, rows);
|
|
179958
|
+
}
|
|
179959
|
+
}
|
|
179960
|
+
var dbForShadowQueue = new Map;
|
|
179961
|
+
async function runShadowWorker() {
|
|
179962
|
+
const startedAt = Date.now();
|
|
179963
|
+
let processed = 0;
|
|
179964
|
+
let processedBytes = 0;
|
|
179965
|
+
while (shadowQueue.length > 0 && processed < SHADOW_MAX_ITEMS_PER_TICK && Date.now() - startedAt < SHADOW_MAX_WALL_CLOCK_MS) {
|
|
179966
|
+
const item = shadowQueue.shift();
|
|
179967
|
+
if (!item)
|
|
179968
|
+
break;
|
|
179969
|
+
const itemBytes = item.ids.reduce((total, id) => total + id.length, 0);
|
|
179970
|
+
if (processed > 0 && processedBytes + itemBytes > SHADOW_MAX_BYTES_PER_TICK) {
|
|
179971
|
+
shadowQueue.unshift(item);
|
|
179972
|
+
break;
|
|
179973
|
+
}
|
|
179974
|
+
try {
|
|
179975
|
+
await processShadowQueueItem(item);
|
|
179976
|
+
} catch (error51) {
|
|
179977
|
+
log("[magic-context] Synapse shadow write failed:", error51);
|
|
179978
|
+
}
|
|
179979
|
+
processed += item.ids.length;
|
|
179980
|
+
processedBytes += itemBytes;
|
|
179981
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
179982
|
+
}
|
|
179983
|
+
}
|
|
176651
179984
|
function registerProjectInObservationMode(db, projectIdentity, sourceDirectory, failedConfig, failureSummary) {
|
|
176652
179985
|
const prior = projectRegistrations.get(projectIdentity);
|
|
176653
179986
|
const runtimeFingerprint = `observation:${sha256Prefix(failureSummary)}`;
|
|
@@ -176689,7 +180022,10 @@ function getOrCreateProjectProvider(registration) {
|
|
|
176689
180022
|
if (registration.provider) {
|
|
176690
180023
|
return registration.provider;
|
|
176691
180024
|
}
|
|
176692
|
-
const provider = createProvider(registration.config
|
|
180025
|
+
const provider = createProvider(registration.config, {
|
|
180026
|
+
projectRoot: registration.sourceDirectory,
|
|
180027
|
+
session: `project:${registration.projectIdentity}`
|
|
180028
|
+
});
|
|
176693
180029
|
registration.provider = provider;
|
|
176694
180030
|
return provider;
|
|
176695
180031
|
}
|
|
@@ -176709,7 +180045,7 @@ async function embedTextForProject(projectIdentity, text, signal, purpose = "pas
|
|
|
176709
180045
|
if (!current || current.generation !== generation || current.runtimeFingerprint !== registration.runtimeFingerprint) {
|
|
176710
180046
|
return null;
|
|
176711
180047
|
}
|
|
176712
|
-
return { vector, modelId, generation };
|
|
180048
|
+
return { vector, modelId, chunkModelId: registration.chunkModelId, generation };
|
|
176713
180049
|
}
|
|
176714
180050
|
async function embedBatchForProject(projectIdentity, texts, signal, purpose = "passage") {
|
|
176715
180051
|
if (texts.length === 0) {
|
|
@@ -176734,31 +180070,81 @@ async function embedBatchForProject(projectIdentity, texts, signal, purpose = "p
|
|
|
176734
180070
|
}
|
|
176735
180071
|
return { vectors, modelId, generation };
|
|
176736
180072
|
}
|
|
176737
|
-
async function
|
|
176738
|
-
|
|
176739
|
-
|
|
180073
|
+
async function embedItemsForProject(projectIdentity, items, signal, db, sessionId = projectIdentity) {
|
|
180074
|
+
const registration = projectRegistrations.get(projectIdentity);
|
|
180075
|
+
if (!registration || registration.observationMode || items.length === 0)
|
|
180076
|
+
return null;
|
|
180077
|
+
const generation = registration.generation;
|
|
180078
|
+
const modelId = registration.modelId;
|
|
180079
|
+
const runtimeFingerprint = registration.runtimeFingerprint;
|
|
180080
|
+
const provider = getOrCreateProjectProvider(registration);
|
|
180081
|
+
if (!provider)
|
|
180082
|
+
return null;
|
|
180083
|
+
const rawConfig = registration.config;
|
|
180084
|
+
const fingerprint = typeof rawConfig.synapse_fingerprint === "string" ? rawConfig.synapse_fingerprint : "";
|
|
180085
|
+
const tableEpoch = typeof rawConfig.synapse_table_epoch === "number" ? rawConfig.synapse_table_epoch : undefined;
|
|
180086
|
+
const isSynapse = registration.providerIdentity.startsWith("synapse:v1:") && fingerprint && tableEpoch !== undefined;
|
|
180087
|
+
const ledgerKey = isSynapse ? getSynapseBatchRequestKey({
|
|
180088
|
+
model: typeof rawConfig.model === "string" ? rawConfig.model : registration.modelId,
|
|
180089
|
+
fingerprint,
|
|
180090
|
+
tableEpoch,
|
|
180091
|
+
items
|
|
180092
|
+
}) : null;
|
|
180093
|
+
if (db && ledgerKey) {
|
|
180094
|
+
beginSynapseBatchLedger(db, {
|
|
180095
|
+
sessionId,
|
|
180096
|
+
projectPath: projectIdentity,
|
|
180097
|
+
scope: items[0].id.split(":", 1)[0],
|
|
180098
|
+
manifest: items.map(({ id, contentSha256: contentSha2562 }) => ({ id, contentSha256: contentSha2562 })),
|
|
180099
|
+
requestKey: ledgerKey
|
|
180100
|
+
});
|
|
180101
|
+
}
|
|
180102
|
+
let vectors;
|
|
180103
|
+
try {
|
|
180104
|
+
if (provider.embedItems) {
|
|
180105
|
+
vectors = await provider.embedItems(items, signal);
|
|
180106
|
+
} else {
|
|
180107
|
+
const positional = await provider.embedBatch(items.map((item) => item.text), signal, "passage");
|
|
180108
|
+
vectors = new Map(items.flatMap((item, index) => {
|
|
180109
|
+
const vector = positional[index];
|
|
180110
|
+
return vector ? [[item.id, vector]] : [];
|
|
180111
|
+
}));
|
|
180112
|
+
}
|
|
180113
|
+
} catch (error51) {
|
|
180114
|
+
if (db && ledgerKey)
|
|
180115
|
+
finishSynapseBatchLedger(db, sessionId, ledgerKey, "failed");
|
|
180116
|
+
throw error51;
|
|
180117
|
+
}
|
|
180118
|
+
if (db && ledgerKey) {
|
|
180119
|
+
finishSynapseBatchLedger(db, sessionId, ledgerKey, vectors.size === items.length ? "complete" : "partial");
|
|
180120
|
+
}
|
|
180121
|
+
const current = projectRegistrations.get(projectIdentity);
|
|
180122
|
+
if (!current || current.generation !== generation || current.runtimeFingerprint !== runtimeFingerprint) {
|
|
180123
|
+
return null;
|
|
180124
|
+
}
|
|
180125
|
+
return { vectors, modelId, generation };
|
|
180126
|
+
}
|
|
180127
|
+
async function embedItemsWindowBounded(projectIdentity, items, signal, db) {
|
|
180128
|
+
if (items.length <= MAX_WINDOWS_PER_EMBED_CALL) {
|
|
180129
|
+
return embedItemsForProject(projectIdentity, items, signal, db, projectIdentity);
|
|
176740
180130
|
}
|
|
176741
|
-
const vectors =
|
|
180131
|
+
const vectors = new Map;
|
|
176742
180132
|
let modelId = null;
|
|
176743
180133
|
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);
|
|
180134
|
+
for (let start = 0;start < items.length; start += MAX_WINDOWS_PER_EMBED_CALL) {
|
|
180135
|
+
const result = await embedItemsForProject(projectIdentity, items.slice(start, start + MAX_WINDOWS_PER_EMBED_CALL), signal, db, projectIdentity);
|
|
176749
180136
|
if (!result)
|
|
176750
180137
|
return null;
|
|
176751
180138
|
if (modelId === null) {
|
|
176752
180139
|
modelId = result.modelId;
|
|
176753
180140
|
generation = result.generation;
|
|
176754
|
-
} else if (
|
|
180141
|
+
} else if (modelId !== result.modelId || generation !== result.generation) {
|
|
176755
180142
|
return null;
|
|
176756
180143
|
}
|
|
176757
|
-
|
|
180144
|
+
for (const [id, vector] of result.vectors)
|
|
180145
|
+
vectors.set(id, vector);
|
|
176758
180146
|
}
|
|
176759
|
-
|
|
176760
|
-
return null;
|
|
176761
|
-
return { vectors, modelId, generation };
|
|
180147
|
+
return modelId === null || generation === null ? null : { vectors, modelId, generation };
|
|
176762
180148
|
}
|
|
176763
180149
|
function isUnembeddedMemoryRow(row) {
|
|
176764
180150
|
if (row === null || typeof row !== "object")
|
|
@@ -176783,13 +180169,17 @@ async function embedUnembeddedMemoriesForProject(db, projectIdentity, batchSize
|
|
|
176783
180169
|
if (memories.length === 0)
|
|
176784
180170
|
return 0;
|
|
176785
180171
|
try {
|
|
176786
|
-
const result = await
|
|
180172
|
+
const result = await embedItemsForProject(projectIdentity, memories.map((memory) => ({
|
|
180173
|
+
id: `memory:${memory.id}`,
|
|
180174
|
+
text: memory.content,
|
|
180175
|
+
contentSha256: contentSha256(memory.content)
|
|
180176
|
+
})), undefined, db, projectIdentity);
|
|
176787
180177
|
if (!result)
|
|
176788
180178
|
return 0;
|
|
176789
180179
|
let embeddedCount = 0;
|
|
176790
180180
|
db.transaction(() => {
|
|
176791
|
-
for (const
|
|
176792
|
-
const embedding = result.vectors
|
|
180181
|
+
for (const memory of memories) {
|
|
180182
|
+
const embedding = result.vectors.get(`memory:${memory.id}`);
|
|
176793
180183
|
if (!embedding)
|
|
176794
180184
|
continue;
|
|
176795
180185
|
if (saveEmbeddingIfHashMatches(db, memory.id, embedding, result.modelId, memory.normalized_hash)) {
|
|
@@ -176797,6 +180187,7 @@ async function embedUnembeddedMemoriesForProject(db, projectIdentity, batchSize
|
|
|
176797
180187
|
}
|
|
176798
180188
|
}
|
|
176799
180189
|
})();
|
|
180190
|
+
enqueueShadowEmbeddingItems(projectIdentity, "memory", memories.filter((memory) => result.vectors.has(`memory:${memory.id}`)).map((memory) => String(memory.id)));
|
|
176800
180191
|
return embeddedCount;
|
|
176801
180192
|
} catch (error51) {
|
|
176802
180193
|
log("[magic-context] failed to proactively embed missing memories:", error51);
|
|
@@ -176810,19 +180201,24 @@ async function embedCommitBatch(db, projectIdentity, batchSize) {
|
|
|
176810
180201
|
const commits = loadUnembeddedCommits(db, projectIdentity, snapshot.modelId, Math.max(1, Math.floor(batchSize)));
|
|
176811
180202
|
if (commits.length === 0)
|
|
176812
180203
|
return 0;
|
|
176813
|
-
const result = await
|
|
180204
|
+
const result = await embedItemsForProject(projectIdentity, commits.map((commit) => ({
|
|
180205
|
+
id: `commit:${commit.sha}`,
|
|
180206
|
+
text: commit.message,
|
|
180207
|
+
contentSha256: contentSha256(commit.message)
|
|
180208
|
+
})), undefined, db, projectIdentity);
|
|
176814
180209
|
if (!result)
|
|
176815
180210
|
return 0;
|
|
176816
180211
|
let embeddedCount = 0;
|
|
176817
180212
|
db.transaction(() => {
|
|
176818
|
-
for (const
|
|
176819
|
-
const embedding = result.vectors
|
|
180213
|
+
for (const commit of commits) {
|
|
180214
|
+
const embedding = result.vectors.get(`commit:${commit.sha}`);
|
|
176820
180215
|
if (!embedding)
|
|
176821
180216
|
continue;
|
|
176822
180217
|
saveCommitEmbedding(db, commit.sha, embedding, result.modelId);
|
|
176823
180218
|
embeddedCount += 1;
|
|
176824
180219
|
}
|
|
176825
180220
|
})();
|
|
180221
|
+
enqueueShadowEmbeddingItems(projectIdentity, "commit", commits.filter((commit) => result.vectors.has(`commit:${commit.sha}`)).map((commit) => commit.sha));
|
|
176826
180222
|
return embeddedCount;
|
|
176827
180223
|
}
|
|
176828
180224
|
async function drainCommitBacklogForProject(db, projectIdentity, deadline) {
|
|
@@ -176895,9 +180291,11 @@ async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates
|
|
|
176895
180291
|
windowCount += item.windows.length;
|
|
176896
180292
|
i += 1;
|
|
176897
180293
|
} while (i < prepared.length && windowCount + prepared[i].windows.length <= MAX_WINDOWS_PER_EMBED_CALL);
|
|
176898
|
-
const
|
|
176899
|
-
|
|
176900
|
-
|
|
180294
|
+
const items = slice.flatMap((item) => item.windows.map((window) => ({
|
|
180295
|
+
id: `chunk:${item.candidate.id}:${window.windowIndex}`,
|
|
180296
|
+
text: window.text,
|
|
180297
|
+
contentSha256: contentSha256(window.text)
|
|
180298
|
+
})));
|
|
176901
180299
|
const persistedIds = new Set;
|
|
176902
180300
|
for (let attempt = 0;attempt < EMBED_SLICE_RETRY_ATTEMPTS; attempt++) {
|
|
176903
180301
|
if (signal?.aborted)
|
|
@@ -176905,19 +180303,17 @@ async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates
|
|
|
176905
180303
|
let result = null;
|
|
176906
180304
|
const attemptStart = Date.now();
|
|
176907
180305
|
try {
|
|
176908
|
-
result = await
|
|
180306
|
+
result = await embedItemsWindowBounded(projectIdentity, items, signal, db);
|
|
176909
180307
|
} catch (error51) {
|
|
176910
180308
|
log("[magic-context] failed to proactively embed compartment chunks:", error51);
|
|
176911
180309
|
}
|
|
176912
180310
|
if (signal?.aborted)
|
|
176913
180311
|
break;
|
|
176914
180312
|
if (result) {
|
|
176915
|
-
let offset = 0;
|
|
176916
180313
|
for (const item of slice) {
|
|
176917
|
-
const vectors = result.vectors.slice(offset, offset + item.windows.length);
|
|
176918
|
-
offset += item.windows.length;
|
|
176919
180314
|
if (persistedIds.has(item.candidate.id))
|
|
176920
180315
|
continue;
|
|
180316
|
+
const vectors = item.windows.map((window) => result.vectors.get(`chunk:${item.candidate.id}:${window.windowIndex}`));
|
|
176921
180317
|
if (vectors.length !== item.windows.length || vectors.some((v) => !v)) {
|
|
176922
180318
|
continue;
|
|
176923
180319
|
}
|
|
@@ -176931,6 +180327,9 @@ async function embedCandidateChunkBatch(db, projectIdentity, modelId, candidates
|
|
|
176931
180327
|
}));
|
|
176932
180328
|
replaceCompartmentChunkEmbeddings(db, rows);
|
|
176933
180329
|
persistedIds.add(item.candidate.id);
|
|
180330
|
+
enqueueShadowEmbeddingItems(projectIdentity, "chunk", [
|
|
180331
|
+
String(item.candidate.id)
|
|
180332
|
+
]);
|
|
176934
180333
|
}
|
|
176935
180334
|
}
|
|
176936
180335
|
if (persistedIds.size === slice.length)
|
|
@@ -177099,7 +180498,24 @@ function createProvider2(config2) {
|
|
|
177099
180498
|
maxInputTokens: config2.max_input_tokens
|
|
177100
180499
|
});
|
|
177101
180500
|
}
|
|
177102
|
-
|
|
180501
|
+
if (config2.provider === "local") {
|
|
180502
|
+
return new LocalEmbeddingProvider(config2.model, config2.max_input_tokens);
|
|
180503
|
+
}
|
|
180504
|
+
if (config2.provider === "synapse") {
|
|
180505
|
+
const synapse = config2;
|
|
180506
|
+
return new SynapseEmbeddingProvider({
|
|
180507
|
+
connectionFile: synapse.synapse_connection_file ?? "",
|
|
180508
|
+
projectRoot: "",
|
|
180509
|
+
session: "embedding",
|
|
180510
|
+
model: synapse.model,
|
|
180511
|
+
fingerprint: synapse.synapse_fingerprint,
|
|
180512
|
+
tableEpoch: synapse.synapse_table_epoch,
|
|
180513
|
+
dims: synapse.synapse_dims,
|
|
180514
|
+
recommendedBatch: synapse.synapse_recommended_batch,
|
|
180515
|
+
provenance: synapse.synapse_provenance
|
|
180516
|
+
});
|
|
180517
|
+
}
|
|
180518
|
+
throw new Error("Unknown embedding provider");
|
|
177103
180519
|
}
|
|
177104
180520
|
function getOrCreateProvider() {
|
|
177105
180521
|
if (provider) {
|
|
@@ -178991,7 +182407,7 @@ If no promotions are warranted, return empty arrays. Always consume reviewed can
|
|
|
178991
182407
|
init_logger();
|
|
178992
182408
|
|
|
178993
182409
|
// ../plugin/src/shared/redaction.ts
|
|
178994
|
-
import { homedir as
|
|
182410
|
+
import { homedir as homedir4, userInfo } from "node:os";
|
|
178995
182411
|
function escapeRegex3(value) {
|
|
178996
182412
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
178997
182413
|
}
|
|
@@ -179040,7 +182456,7 @@ var SECRET_QUALIFIERS = new Set([
|
|
|
179040
182456
|
"azure"
|
|
179041
182457
|
]);
|
|
179042
182458
|
function sanitizePathString(value) {
|
|
179043
|
-
const home =
|
|
182459
|
+
const home = homedir4();
|
|
179044
182460
|
const username = userInfo().username;
|
|
179045
182461
|
let sanitized = value;
|
|
179046
182462
|
if (home) {
|
|
@@ -180036,7 +183452,7 @@ function throwIfAborted2(signal) {
|
|
|
180036
183452
|
}
|
|
180037
183453
|
|
|
180038
183454
|
// ../plugin/src/features/magic-context/smart-notes/compiler.ts
|
|
180039
|
-
import { createHash as
|
|
183455
|
+
import { createHash as createHash12 } from "node:crypto";
|
|
180040
183456
|
init_logger();
|
|
180041
183457
|
|
|
180042
183458
|
// ../plugin/src/features/magic-context/smart-notes/compiler-prompt.ts
|
|
@@ -180347,7 +183763,7 @@ Remember: output only the JSON object described by the system prompt.`;
|
|
|
180347
183763
|
});
|
|
180348
183764
|
const response = run.validated;
|
|
180349
183765
|
const compiledCheck = normalizeCompiledCheck(response.compiled_check);
|
|
180350
|
-
const manifest =
|
|
183766
|
+
const manifest = normalizeManifest2(response.manifest);
|
|
180351
183767
|
const checkCron = normalizeCron(response.check_cron);
|
|
180352
183768
|
for (const warning of manifestAdvisoryWarnings(compiledCheck, manifest)) {
|
|
180353
183769
|
log(`[dreamer] smart note #${args.note.id}: manifest advisory — ${warning}`);
|
|
@@ -180426,7 +183842,7 @@ function normalizeCompiledCheck(source) {
|
|
|
180426
183842
|
}
|
|
180427
183843
|
return code;
|
|
180428
183844
|
}
|
|
180429
|
-
function
|
|
183845
|
+
function normalizeManifest2(manifest) {
|
|
180430
183846
|
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
183847
|
return {
|
|
180432
183848
|
capabilities,
|
|
@@ -180470,7 +183886,7 @@ function manifestAdvisoryWarnings(code, manifest) {
|
|
|
180470
183886
|
return warnings;
|
|
180471
183887
|
}
|
|
180472
183888
|
function hashCheck(surfaceCondition, compiledCheck, manifest, checkCron) {
|
|
180473
|
-
return
|
|
183889
|
+
return createHash12("sha256").update(surfaceCondition ?? "").update("\x00").update(compiledCheck).update("\x00").update(JSON.stringify(manifest)).update("\x00").update(checkCron).digest("hex");
|
|
180474
183890
|
}
|
|
180475
183891
|
function extractJsonObject(output) {
|
|
180476
183892
|
const fenced = output.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
@@ -182405,7 +185821,7 @@ function normalizeOpenCodeRows(db, sessionId, rows) {
|
|
|
182405
185821
|
const partsByMessageId = new Map;
|
|
182406
185822
|
for (const row of partRows) {
|
|
182407
185823
|
const parts = partsByMessageId.get(row.message_id) ?? [];
|
|
182408
|
-
const parsed =
|
|
185824
|
+
const parsed = parseJson2(row.data);
|
|
182409
185825
|
if (parsed !== null)
|
|
182410
185826
|
parts.push(parsed);
|
|
182411
185827
|
partsByMessageId.set(row.message_id, parts);
|
|
@@ -182516,7 +185932,7 @@ function stringifyToolOutput(value) {
|
|
|
182516
185932
|
return String(value);
|
|
182517
185933
|
}
|
|
182518
185934
|
}
|
|
182519
|
-
function
|
|
185935
|
+
function parseJson2(value) {
|
|
182520
185936
|
try {
|
|
182521
185937
|
return JSON.parse(value);
|
|
182522
185938
|
} catch {
|
|
@@ -182524,7 +185940,7 @@ function parseJson(value) {
|
|
|
182524
185940
|
}
|
|
182525
185941
|
}
|
|
182526
185942
|
function parseJsonRecord2(value) {
|
|
182527
|
-
const parsed =
|
|
185943
|
+
const parsed = parseJson2(value);
|
|
182528
185944
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
|
|
182529
185945
|
return null;
|
|
182530
185946
|
return parsed;
|
|
@@ -183282,7 +186698,7 @@ function parseFrictionGateVerdict(verdict) {
|
|
|
183282
186698
|
}
|
|
183283
186699
|
function computeRetrospectiveWindowKey(flagged) {
|
|
183284
186700
|
const anchors = flagged.map((message) => `${message.sessionId}:${message.ts}`).sort().join("|");
|
|
183285
|
-
return
|
|
186701
|
+
return createHash13("sha256").update(anchors).digest("hex").slice(0, 32);
|
|
183286
186702
|
}
|
|
183287
186703
|
function renderFrictionWindow(messages, flaggedOrdinals, radius = 2) {
|
|
183288
186704
|
const flagged = new Set(flaggedOrdinals);
|
|
@@ -184136,18 +187552,23 @@ async function embedUnembeddedCommits(db, projectPath) {
|
|
|
184136
187552
|
break;
|
|
184137
187553
|
let embeddedThisBatch = 0;
|
|
184138
187554
|
try {
|
|
184139
|
-
const result = await
|
|
187555
|
+
const result = await embedItemsForProject(projectPath, rows.map((row) => ({
|
|
187556
|
+
id: `commit:${row.sha}`,
|
|
187557
|
+
text: row.message,
|
|
187558
|
+
contentSha256: contentSha256(row.message)
|
|
187559
|
+
})), undefined, db, projectPath);
|
|
184140
187560
|
if (!result)
|
|
184141
187561
|
break;
|
|
184142
187562
|
db.transaction(() => {
|
|
184143
|
-
for (const
|
|
184144
|
-
const embedding2 = result.vectors
|
|
187563
|
+
for (const row of rows) {
|
|
187564
|
+
const embedding2 = result.vectors.get(`commit:${row.sha}`);
|
|
184145
187565
|
if (!embedding2)
|
|
184146
187566
|
continue;
|
|
184147
187567
|
saveCommitEmbedding(db, row.sha, embedding2, result.modelId);
|
|
184148
187568
|
embeddedThisBatch += 1;
|
|
184149
187569
|
}
|
|
184150
187570
|
})();
|
|
187571
|
+
enqueueShadowEmbeddingItems(projectPath, "commit", rows.filter((row) => result.vectors.has(`commit:${row.sha}`)).map((row) => row.sha));
|
|
184151
187572
|
} catch (error51) {
|
|
184152
187573
|
log(`[git-commits] embed batch failed for ${projectPath}: ${error51 instanceof Error ? error51.message : String(error51)}`);
|
|
184153
187574
|
break;
|
|
@@ -184303,7 +187724,11 @@ function searchGitCommitsSync(db, projectPath, query, options) {
|
|
|
184303
187724
|
init_logger();
|
|
184304
187725
|
var DREAM_TIMER_INTERVAL_MS = 15 * 60 * 1000;
|
|
184305
187726
|
var GIT_COMMIT_BACKLOG_DRAIN_MAX_MS = 5 * 60 * 1000;
|
|
187727
|
+
var BOOT_PROJECT_JITTER_SLOT_MS = 1000;
|
|
184306
187728
|
var activeTimer = null;
|
|
187729
|
+
var startupTimers = new Map;
|
|
187730
|
+
var startupJitters = new Map;
|
|
187731
|
+
var nextStartupJitterSlot = 0;
|
|
184307
187732
|
function directoryStillExists(directory) {
|
|
184308
187733
|
try {
|
|
184309
187734
|
return statSync6(directory).isDirectory();
|
|
@@ -184327,39 +187752,53 @@ function openTimerDatabaseOrNull(context) {
|
|
|
184327
187752
|
}
|
|
184328
187753
|
var registeredProjects = new Map;
|
|
184329
187754
|
async function startDreamScheduleTimer(args) {
|
|
187755
|
+
beginBootQuietPeriod();
|
|
184330
187756
|
const db = openTimerDatabaseOrNull("schedule timer registration");
|
|
184331
187757
|
if (!db)
|
|
184332
187758
|
return;
|
|
184333
|
-
await args.ensureRegistered(args.directory, db);
|
|
184334
|
-
const snapshot = getProjectEmbeddingSnapshot(args.projectIdentity);
|
|
184335
187759
|
const dreamingEnabled = Boolean(args.dreamerConfig && args.dreamerConfig.disable !== true);
|
|
184336
|
-
const embeddingSweepEnabled =
|
|
184337
|
-
const commitIndexingEnabled =
|
|
187760
|
+
const embeddingSweepEnabled = args.memoryEnabled === true;
|
|
187761
|
+
const commitIndexingEnabled = args.gitCommitIndexing?.enabled === true;
|
|
184338
187762
|
if (!dreamingEnabled && !embeddingSweepEnabled && !commitIndexingEnabled) {
|
|
184339
187763
|
return;
|
|
184340
187764
|
}
|
|
184341
|
-
const
|
|
187765
|
+
const previousRegistration = registeredProjects.get(args.directory);
|
|
187766
|
+
const isNewRegistration = previousRegistration === undefined;
|
|
187767
|
+
if (previousRegistration && previousRegistration !== args) {
|
|
187768
|
+
const pendingStartup = startupTimers.get(args.directory);
|
|
187769
|
+
if (pendingStartup) {
|
|
187770
|
+
clearTimeout(pendingStartup);
|
|
187771
|
+
startupTimers.delete(args.directory);
|
|
187772
|
+
}
|
|
187773
|
+
}
|
|
184342
187774
|
registeredProjects.set(args.directory, args);
|
|
184343
187775
|
if (isNewRegistration) {
|
|
184344
187776
|
log(`[dreamer] registered project ${args.projectIdentity} (dreaming=${dreamingEnabled} embeddings=${embeddingSweepEnabled} commits=${commitIndexingEnabled}; total=${registeredProjects.size})`);
|
|
184345
187777
|
}
|
|
184346
187778
|
if (!activeTimer) {
|
|
184347
187779
|
log(`[dreamer] started independent schedule timer (every ${DREAM_TIMER_INTERVAL_MS / 60000}m)`);
|
|
184348
|
-
runTick("startup");
|
|
187780
|
+
scheduleAfterBootQuiet(() => runTick("startup"));
|
|
184349
187781
|
const timer = setInterval(() => runTick("interval"), DREAM_TIMER_INTERVAL_MS);
|
|
184350
187782
|
if (typeof timer === "object" && "unref" in timer) {
|
|
184351
187783
|
timer.unref();
|
|
184352
187784
|
}
|
|
184353
187785
|
activeTimer = timer;
|
|
184354
|
-
} else if (isNewRegistration) {
|
|
184355
|
-
|
|
187786
|
+
} else if (isNewRegistration || previousRegistration !== args) {
|
|
187787
|
+
scheduleInitialProjectRun(args, db);
|
|
184356
187788
|
}
|
|
184357
187789
|
return () => {
|
|
184358
187790
|
registeredProjects.delete(args.directory);
|
|
187791
|
+
const startupTimer = startupTimers.get(args.directory);
|
|
187792
|
+
if (startupTimer) {
|
|
187793
|
+
clearTimeout(startupTimer);
|
|
187794
|
+
startupTimers.delete(args.directory);
|
|
187795
|
+
}
|
|
184359
187796
|
log(`[dreamer] unregistered project ${args.projectIdentity} (remaining=${registeredProjects.size})`);
|
|
184360
187797
|
if (registeredProjects.size === 0 && activeTimer) {
|
|
184361
187798
|
clearInterval(activeTimer);
|
|
184362
187799
|
activeTimer = null;
|
|
187800
|
+
startupJitters.clear();
|
|
187801
|
+
nextStartupJitterSlot = 0;
|
|
184363
187802
|
log("[dreamer] stopped dream schedule timer (no projects left)");
|
|
184364
187803
|
}
|
|
184365
187804
|
};
|
|
@@ -184372,22 +187811,53 @@ function runTick(origin) {
|
|
|
184372
187811
|
if (!db)
|
|
184373
187812
|
return;
|
|
184374
187813
|
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
|
-
}
|
|
187814
|
+
if (origin === "startup") {
|
|
187815
|
+
scheduleInitialProjectRun(reg, db);
|
|
187816
|
+
} else {
|
|
187817
|
+
await runProjectMaintenance(reg, origin, db);
|
|
184382
187818
|
}
|
|
184383
|
-
await sweepProject(reg, origin, db);
|
|
184384
187819
|
}
|
|
187820
|
+
if (origin === "startup")
|
|
187821
|
+
return;
|
|
184385
187822
|
runSqliteOptimize(db);
|
|
184386
187823
|
} catch (error51) {
|
|
184387
187824
|
log("[magic-context] timer-triggered maintenance check failed:", error51);
|
|
184388
187825
|
}
|
|
184389
187826
|
})();
|
|
184390
187827
|
}
|
|
187828
|
+
function startupJitterMs(directory) {
|
|
187829
|
+
const existing = startupJitters.get(directory);
|
|
187830
|
+
if (existing !== undefined)
|
|
187831
|
+
return existing;
|
|
187832
|
+
const slot = nextStartupJitterSlot;
|
|
187833
|
+
nextStartupJitterSlot += 1;
|
|
187834
|
+
const hash2 = [...directory].reduce((value, character) => value * 33 + character.charCodeAt(0) >>> 0, 5381);
|
|
187835
|
+
const jitter = slot * BOOT_PROJECT_JITTER_SLOT_MS + hash2 % BOOT_PROJECT_JITTER_SLOT_MS;
|
|
187836
|
+
startupJitters.set(directory, jitter);
|
|
187837
|
+
return jitter;
|
|
187838
|
+
}
|
|
187839
|
+
function scheduleInitialProjectRun(reg, db) {
|
|
187840
|
+
if (startupTimers.has(reg.directory))
|
|
187841
|
+
return;
|
|
187842
|
+
const timer = scheduleAfterBootQuiet(() => {
|
|
187843
|
+
startupTimers.delete(reg.directory);
|
|
187844
|
+
if (registeredProjects.get(reg.directory) !== reg)
|
|
187845
|
+
return;
|
|
187846
|
+
runProjectMaintenance(reg, "startup", db);
|
|
187847
|
+
}, startupJitterMs(reg.directory));
|
|
187848
|
+
startupTimers.set(reg.directory, timer);
|
|
187849
|
+
}
|
|
187850
|
+
async function runProjectMaintenance(reg, origin, db) {
|
|
187851
|
+
await reg.ensureRegistered(reg.directory, db);
|
|
187852
|
+
const memorySnapshot = getProjectEmbeddingSnapshot(reg.projectIdentity);
|
|
187853
|
+
if (memorySnapshot?.enabled) {
|
|
187854
|
+
const embeddedCount = await embedUnembeddedMemoriesForProject(db, reg.projectIdentity);
|
|
187855
|
+
if (embeddedCount > 0) {
|
|
187856
|
+
log(`[magic-context] proactively embedded ${embeddedCount} ${embeddedCount === 1 ? "memory" : "memories"} for project ${reg.projectIdentity}`);
|
|
187857
|
+
}
|
|
187858
|
+
}
|
|
187859
|
+
await sweepProject(reg, origin, db);
|
|
187860
|
+
}
|
|
184391
187861
|
async function sweepProject(reg, origin, db, gitCommitEnabled) {
|
|
184392
187862
|
if (!directoryStillExists(reg.directory)) {
|
|
184393
187863
|
log(`[dreamer] project directory no longer exists (${reg.projectIdentity}); skipping + unregistering`);
|
|
@@ -184555,7 +188025,7 @@ async function sweepGitCommits(args) {
|
|
|
184555
188025
|
import { statSync as statSync7 } from "node:fs";
|
|
184556
188026
|
|
|
184557
188027
|
// ../plugin/src/plugin/embedding-bootstrap-helpers.ts
|
|
184558
|
-
import { createHash as
|
|
188028
|
+
import { createHash as createHash14 } from "node:crypto";
|
|
184559
188029
|
init_logger();
|
|
184560
188030
|
var EMBEDDING_AFFECTING_KEYS = new Set([
|
|
184561
188031
|
"embedding.api_key",
|
|
@@ -184565,7 +188035,11 @@ var EMBEDDING_AFFECTING_KEYS = new Set([
|
|
|
184565
188035
|
"embedding.input_type",
|
|
184566
188036
|
"embedding.truncate",
|
|
184567
188037
|
"embedding.max_input_tokens",
|
|
184568
|
-
"embedding.query_input_type"
|
|
188038
|
+
"embedding.query_input_type",
|
|
188039
|
+
"embedding.fallback_provider",
|
|
188040
|
+
"subc",
|
|
188041
|
+
"subc.connection_file",
|
|
188042
|
+
"shadow_embedding"
|
|
184569
188043
|
]);
|
|
184570
188044
|
var LITERAL_CONFIG_TOKEN_RE = /\{(?:env|file):[^}]+\}/;
|
|
184571
188045
|
function embeddingConfigHasLiteralTokens(embedding2) {
|
|
@@ -184578,7 +188052,13 @@ function embeddingConfigHasLiteralTokens(embedding2) {
|
|
|
184578
188052
|
}
|
|
184579
188053
|
return false;
|
|
184580
188054
|
}
|
|
184581
|
-
var EMBEDDING_AFFECTING_TOP_LEVEL_KEYS = new Set([
|
|
188055
|
+
var EMBEDDING_AFFECTING_TOP_LEVEL_KEYS = new Set([
|
|
188056
|
+
"embedding",
|
|
188057
|
+
"memory",
|
|
188058
|
+
"experimental",
|
|
188059
|
+
"subc",
|
|
188060
|
+
"shadow_embedding"
|
|
188061
|
+
]);
|
|
184582
188062
|
var EMBEDDING_WARNING_TERMS = [
|
|
184583
188063
|
"api_key",
|
|
184584
188064
|
"endpoint",
|
|
@@ -184586,11 +188066,13 @@ var EMBEDDING_WARNING_TERMS = [
|
|
|
184586
188066
|
"provider",
|
|
184587
188067
|
"embedding",
|
|
184588
188068
|
"input_type",
|
|
184589
|
-
"truncate"
|
|
188069
|
+
"truncate",
|
|
188070
|
+
"subc",
|
|
188071
|
+
"shadow_embedding"
|
|
184590
188072
|
];
|
|
184591
188073
|
var loggedFailureSignatures = new Map;
|
|
184592
188074
|
function sha256Prefix2(value, length = 16) {
|
|
184593
|
-
return
|
|
188075
|
+
return createHash14("sha256").update(value).digest("hex").slice(0, length);
|
|
184594
188076
|
}
|
|
184595
188077
|
function warningLooksEmbeddingRelated(message) {
|
|
184596
188078
|
const lower = message.toLowerCase();
|
|
@@ -184989,7 +188471,7 @@ function migrateLegacyExperimental(rawConfig, warnings) {
|
|
|
184989
188471
|
var HIDDEN_AGENT_KEYS = ["historian", "dreamer", "sidekick"];
|
|
184990
188472
|
var HISTORIAN_USER_ONLY_FIELDS = ["model", "fallback_models"];
|
|
184991
188473
|
var AGENT_ESCALATION_FIELDS = ["prompt", "permission", "tools", "system_prompt"];
|
|
184992
|
-
var EMBEDDING_DESTINATION_FIELDS = ["endpoint", "provider"];
|
|
188474
|
+
var EMBEDDING_DESTINATION_FIELDS = ["endpoint", "provider", "fallback_provider"];
|
|
184993
188475
|
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
188476
|
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
188477
|
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 +188587,12 @@ function stripUnsafeProjectConfigFields(projectRaw) {
|
|
|
185105
188587
|
delete projectRaw.shadow_transform;
|
|
185106
188588
|
warnings.push("Ignoring shadow_transform from project config (security: this developer-only mirror lane is user-level only).");
|
|
185107
188589
|
}
|
|
188590
|
+
for (const field of ["subc", "shadow_embedding"]) {
|
|
188591
|
+
if (field in projectRaw) {
|
|
188592
|
+
delete projectRaw[field];
|
|
188593
|
+
warnings.push(`Ignoring ${field} from project config (security: daemon routing and developer-only embedding traffic are user-level settings).`);
|
|
188594
|
+
}
|
|
188595
|
+
}
|
|
185108
188596
|
const embedding2 = projectRaw.embedding;
|
|
185109
188597
|
if (isPlainObject3(embedding2)) {
|
|
185110
188598
|
const removed = [];
|
|
@@ -185280,7 +188768,7 @@ function dropInheritedEmbeddingKeyOnRedirect(projectRaw, mergedRaw, userRaw) {
|
|
|
185280
188768
|
|
|
185281
188769
|
// ../plugin/src/config/variable.ts
|
|
185282
188770
|
import { existsSync as existsSync16, readFileSync as readFileSync7 } from "node:fs";
|
|
185283
|
-
import { homedir as
|
|
188771
|
+
import { homedir as homedir5 } from "node:os";
|
|
185284
188772
|
import { dirname as dirname6, isAbsolute as isAbsolute2, resolve } from "node:path";
|
|
185285
188773
|
|
|
185286
188774
|
// ../plugin/src/shared/jsonc-parser.ts
|
|
@@ -185343,7 +188831,7 @@ function stripJsonComments(content) {
|
|
|
185343
188831
|
var ENV_PATTERN = /\{env:([^}]+)\}/g;
|
|
185344
188832
|
var FILE_PATTERN = /\{file:([^}]+)\}/g;
|
|
185345
188833
|
function sensitiveFilePathReason(resolvedPath) {
|
|
185346
|
-
const home =
|
|
188834
|
+
const home = homedir5();
|
|
185347
188835
|
const sensitiveDirs = [
|
|
185348
188836
|
{ dir: resolve(home, ".ssh"), label: "SSH keys" },
|
|
185349
188837
|
{ dir: resolve(home, ".aws"), label: "AWS credentials" },
|
|
@@ -185406,7 +188894,7 @@ function substituteConfigVariables(input) {
|
|
|
185406
188894
|
}
|
|
185407
188895
|
let filePath = rawPath.trim();
|
|
185408
188896
|
if (filePath.startsWith("~/")) {
|
|
185409
|
-
filePath = resolve(
|
|
188897
|
+
filePath = resolve(homedir5(), filePath.slice(2));
|
|
185410
188898
|
} else if (!isAbsolute2(filePath)) {
|
|
185411
188899
|
filePath = resolve(configDir, filePath);
|
|
185412
188900
|
}
|
|
@@ -190767,6 +194255,93 @@ function containsProbeVerbatim(text, probes) {
|
|
|
190767
194255
|
return probes.some((probe) => haystack.includes(probe.toLowerCase()));
|
|
190768
194256
|
}
|
|
190769
194257
|
|
|
194258
|
+
// ../plugin/src/features/magic-context/search-measurement.ts
|
|
194259
|
+
import { createHash as createHash15 } from "node:crypto";
|
|
194260
|
+
function resultId(result) {
|
|
194261
|
+
switch (result.source) {
|
|
194262
|
+
case "memory":
|
|
194263
|
+
return `memory:${result.memoryId}`;
|
|
194264
|
+
case "message":
|
|
194265
|
+
return `message:${result.messageId}`;
|
|
194266
|
+
case "compartment":
|
|
194267
|
+
return `chunk:${result.compartmentId}`;
|
|
194268
|
+
case "git_commit":
|
|
194269
|
+
return `commit:${result.sha}`;
|
|
194270
|
+
case "primer":
|
|
194271
|
+
return `primer:${result.primerId}`;
|
|
194272
|
+
case "note":
|
|
194273
|
+
return `note:${result.noteId}`;
|
|
194274
|
+
}
|
|
194275
|
+
}
|
|
194276
|
+
async function recordShadowMeasurement(args) {
|
|
194277
|
+
const shadowCohort = getShadowEmbeddingMeasurementCohort(args.projectPath);
|
|
194278
|
+
if (!shadowCohort)
|
|
194279
|
+
return;
|
|
194280
|
+
const primaryCohort = getPrimaryEmbeddingMeasurementCohort(args.projectPath);
|
|
194281
|
+
const shadowStartedAt = Date.now();
|
|
194282
|
+
let shadowFailed = false;
|
|
194283
|
+
let shadowResults = [];
|
|
194284
|
+
try {
|
|
194285
|
+
const vector = await embedShadowTextForProject(args.projectPath, args.query);
|
|
194286
|
+
if (!vector) {
|
|
194287
|
+
shadowFailed = true;
|
|
194288
|
+
} else {
|
|
194289
|
+
shadowResults = await args.search(args.db, args.sessionId, args.projectPath, args.query, {
|
|
194290
|
+
...args.options,
|
|
194291
|
+
embedQuery: async () => vector,
|
|
194292
|
+
isEmbeddingRuntimeEnabled: () => true,
|
|
194293
|
+
embeddingEnabled: true,
|
|
194294
|
+
embeddingModelIdOverride: shadowCohort.modelId,
|
|
194295
|
+
chunkModelIdOverride: shadowCohort.chunkModelId,
|
|
194296
|
+
measurementDisabled: true,
|
|
194297
|
+
countRetrievals: false
|
|
194298
|
+
});
|
|
194299
|
+
}
|
|
194300
|
+
} catch {
|
|
194301
|
+
shadowFailed = true;
|
|
194302
|
+
}
|
|
194303
|
+
const primaryModelId = args.primaryQuery?.modelId ?? primaryCohort?.modelId ?? "";
|
|
194304
|
+
const primaryFingerprint = primaryCohort?.fingerprint ?? "";
|
|
194305
|
+
const primaryEpoch = primaryCohort?.epoch ?? 0;
|
|
194306
|
+
const cohortKey = JSON.stringify({
|
|
194307
|
+
primaryFingerprint,
|
|
194308
|
+
primaryEpoch,
|
|
194309
|
+
shadowFingerprint: shadowCohort.fingerprint,
|
|
194310
|
+
shadowEpoch: shadowCohort.epoch
|
|
194311
|
+
});
|
|
194312
|
+
const primaryIds = args.primaryResults.map(resultId);
|
|
194313
|
+
const shadowIds = shadowResults.map(resultId);
|
|
194314
|
+
const corpusHash = sha2562(JSON.stringify({ query: args.query, primaryIds, shadowIds }));
|
|
194315
|
+
recordEmbeddingMeasurement(args.db, {
|
|
194316
|
+
sessionId: args.sessionId,
|
|
194317
|
+
projectPath: args.projectPath,
|
|
194318
|
+
queryText: args.query,
|
|
194319
|
+
cohortKey,
|
|
194320
|
+
primaryResultIds: primaryIds,
|
|
194321
|
+
shadowResultIds: shadowIds,
|
|
194322
|
+
primaryLatencyMs: args.primaryLatencyMs,
|
|
194323
|
+
shadowLatencyMs: Date.now() - shadowStartedAt,
|
|
194324
|
+
primaryFailed: args.primaryQuery === null,
|
|
194325
|
+
shadowFailed,
|
|
194326
|
+
primaryModelId,
|
|
194327
|
+
shadowModelId: shadowCohort.modelId,
|
|
194328
|
+
primaryFingerprint,
|
|
194329
|
+
shadowFingerprint: shadowCohort.fingerprint,
|
|
194330
|
+
primaryEpoch,
|
|
194331
|
+
shadowEpoch: shadowCohort.epoch,
|
|
194332
|
+
corpusHash,
|
|
194333
|
+
coverage: {
|
|
194334
|
+
primaryResultCount: primaryIds.length,
|
|
194335
|
+
shadowResultCount: shadowIds.length,
|
|
194336
|
+
primaryAnswered: args.primaryQuery !== null,
|
|
194337
|
+
shadowAnswered: !shadowFailed
|
|
194338
|
+
}
|
|
194339
|
+
});
|
|
194340
|
+
}
|
|
194341
|
+
function sha2562(value) {
|
|
194342
|
+
return createHash15("sha256").update(value).digest("hex");
|
|
194343
|
+
}
|
|
194344
|
+
|
|
190770
194345
|
// ../plugin/src/features/magic-context/search.ts
|
|
190771
194346
|
var DEFAULT_UNIFIED_SEARCH_LIMIT = 10;
|
|
190772
194347
|
var FTS_SEMANTIC_CANDIDATE_LIMIT = 50;
|
|
@@ -191524,6 +195099,7 @@ function resolveSources(sources) {
|
|
|
191524
195099
|
}
|
|
191525
195100
|
async function unifiedSearch(db, sessionId, projectPath, query, options = {}) {
|
|
191526
195101
|
const trimmedQuery = query.trim();
|
|
195102
|
+
const measurementStartedAt = Date.now();
|
|
191527
195103
|
if (trimmedQuery.length === 0) {
|
|
191528
195104
|
return [];
|
|
191529
195105
|
}
|
|
@@ -191556,11 +195132,14 @@ async function unifiedSearch(db, sessionId, projectPath, query, options = {}) {
|
|
|
191556
195132
|
maxOrdinal: options.maxMessageOrdinal,
|
|
191557
195133
|
probes: messageProbes
|
|
191558
195134
|
}) : [];
|
|
191559
|
-
const
|
|
191560
|
-
const workspace = resolveSearchWorkspaceContext(db, projectPath);
|
|
195135
|
+
const capturedQuery = await queryEmbeddingPromise;
|
|
191561
195136
|
const embeddingSnapshot = getProjectEmbeddingSnapshot(projectPath);
|
|
191562
|
-
const
|
|
191563
|
-
const
|
|
195137
|
+
const queryContract = capturedQuery instanceof Float32Array || capturedQuery === null ? null : capturedQuery;
|
|
195138
|
+
const generationIsCurrent = queryContract === null || embeddingSnapshot !== null && embeddingSnapshot.generation === queryContract.generation;
|
|
195139
|
+
const queryEmbedding = generationIsCurrent ? queryContract?.vector ?? (capturedQuery instanceof Float32Array ? capturedQuery : null) : null;
|
|
195140
|
+
const workspace = resolveSearchWorkspaceContext(db, projectPath);
|
|
195141
|
+
const embeddingModelId = queryContract?.modelId ?? options.embeddingModelIdOverride ?? embeddingSnapshot?.modelId;
|
|
195142
|
+
const chunkModelId = queryContract?.chunkModelId ?? options.chunkModelIdOverride ?? embeddingSnapshot?.chunkModelId;
|
|
191564
195143
|
const compartmentResults = runCompartmentChunks ? searchCompartmentChunks({
|
|
191565
195144
|
db,
|
|
191566
195145
|
sessionId,
|
|
@@ -191619,6 +195198,19 @@ async function unifiedSearch(db, sessionId, projectPath, query, options = {}) {
|
|
|
191619
195198
|
...gitCommitResults,
|
|
191620
195199
|
...noteResults
|
|
191621
195200
|
].sort(compareUnifiedResults).slice(0, limit);
|
|
195201
|
+
if (!options.measurementDisabled) {
|
|
195202
|
+
recordShadowMeasurement({
|
|
195203
|
+
db,
|
|
195204
|
+
sessionId,
|
|
195205
|
+
projectPath,
|
|
195206
|
+
query: trimmedQuery,
|
|
195207
|
+
options,
|
|
195208
|
+
primaryResults: results,
|
|
195209
|
+
primaryQuery: queryContract,
|
|
195210
|
+
primaryLatencyMs: Date.now() - measurementStartedAt,
|
|
195211
|
+
search: unifiedSearch
|
|
195212
|
+
});
|
|
195213
|
+
}
|
|
191622
195214
|
const countRetrievals = options.countRetrievals ?? true;
|
|
191623
195215
|
if (countRetrievals) {
|
|
191624
195216
|
const memoryIds = results.filter((result) => result.source === "memory").map((result) => result.memoryId);
|
|
@@ -193774,15 +197366,19 @@ async function embedAndStoreCompartmentChunks(db, sessionId, projectPath, compar
|
|
|
193774
197366
|
if (currentModelId !== "off" && chunkEmbeddingWindowsAreCurrent(db, compartment.id, currentModelId, windows, projectPath)) {
|
|
193775
197367
|
continue;
|
|
193776
197368
|
}
|
|
193777
|
-
const result = await
|
|
197369
|
+
const result = await embedItemsForProject(projectPath, windows.map((window) => ({
|
|
197370
|
+
id: `chunk:${compartment.id}:${window.windowIndex}`,
|
|
197371
|
+
text: window.text,
|
|
197372
|
+
contentSha256: contentSha256(window.text)
|
|
197373
|
+
})), undefined, db, sessionId);
|
|
193778
197374
|
if (!result)
|
|
193779
197375
|
continue;
|
|
193780
197376
|
if (chunkEmbeddingWindowsAreCurrent(db, compartment.id, currentModelId, windows, projectPath)) {
|
|
193781
197377
|
continue;
|
|
193782
197378
|
}
|
|
193783
197379
|
const rows = [];
|
|
193784
|
-
for (const
|
|
193785
|
-
const vector = result.vectors
|
|
197380
|
+
for (const window of windows) {
|
|
197381
|
+
const vector = result.vectors.get(`chunk:${compartment.id}:${window.windowIndex}`);
|
|
193786
197382
|
if (!vector)
|
|
193787
197383
|
continue;
|
|
193788
197384
|
rows.push({
|
|
@@ -193796,6 +197392,7 @@ async function embedAndStoreCompartmentChunks(db, sessionId, projectPath, compar
|
|
|
193796
197392
|
}
|
|
193797
197393
|
if (rows.length === windows.length) {
|
|
193798
197394
|
replaceCompartmentChunkEmbeddings(db, rows);
|
|
197395
|
+
enqueueShadowEmbeddingItems(projectPath, "chunk", [String(compartment.id)]);
|
|
193799
197396
|
}
|
|
193800
197397
|
} catch (error51) {
|
|
193801
197398
|
sessionLog(sessionId, `compartment chunk embedding failed for compartment ${compartment.id}:`, error51);
|
|
@@ -198230,7 +201827,7 @@ function stripPiProcessedImages(args) {
|
|
|
198230
201827
|
}
|
|
198231
201828
|
|
|
198232
201829
|
// src/system-prompt.ts
|
|
198233
|
-
import { createHash as
|
|
201830
|
+
import { createHash as createHash16 } from "node:crypto";
|
|
198234
201831
|
|
|
198235
201832
|
// ../plugin/src/agents/magic-context-prompt.ts
|
|
198236
201833
|
var LONG_TERM_PARTNER_FRAME = `### You are the user's long-term partner on this project — not a one-off hire
|
|
@@ -198383,7 +201980,7 @@ function processSystemPromptForCache(args) {
|
|
|
198383
201980
|
sessionLog(sessionId, `system prompt date frozen: real=${liveDate}, using=${stickyDate} (cache-stable pass)`);
|
|
198384
201981
|
}
|
|
198385
201982
|
}
|
|
198386
|
-
const currentHash =
|
|
201983
|
+
const currentHash = createHash16("md5").update(frozenPrompt).digest("hex");
|
|
198387
201984
|
const hashChanged = !isFirstHash && currentHash !== previousHash;
|
|
198388
201985
|
if (hashChanged) {
|
|
198389
201986
|
sessionLog(sessionId, `system prompt hash changed: ${previousHash} → ${currentHash} (len=${frozenPrompt.length})`);
|
|
@@ -201531,6 +205128,19 @@ init_logger();
|
|
|
201531
205128
|
init_data_path();
|
|
201532
205129
|
init_logger();
|
|
201533
205130
|
var MARKER_SUMMARY_TEXT = "[Compacted by magic-context — session history is managed by the plugin]";
|
|
205131
|
+
function dropMarkerSummaryTag(db, sessionId, summaryMessageId) {
|
|
205132
|
+
const tagNumber = getTagNumberByMessageId(db, sessionId, `${summaryMessageId}:p0`);
|
|
205133
|
+
if (tagNumber !== null)
|
|
205134
|
+
updateTagStatus(db, sessionId, tagNumber, "dropped");
|
|
205135
|
+
}
|
|
205136
|
+
function persistMarkerStateAndDropReplacedTag(db, sessionId, state, replacedSummaryMessageId) {
|
|
205137
|
+
db.transaction(() => {
|
|
205138
|
+
setPersistedCompactionMarkerState(db, sessionId, state);
|
|
205139
|
+
if (replacedSummaryMessageId !== null) {
|
|
205140
|
+
dropMarkerSummaryTag(db, sessionId, replacedSummaryMessageId);
|
|
205141
|
+
}
|
|
205142
|
+
})();
|
|
205143
|
+
}
|
|
201534
205144
|
function getCompartmentEndMessageIdForOrdinal(db, sessionId, endOrdinal) {
|
|
201535
205145
|
const row = db.prepare(`SELECT end_message_id
|
|
201536
205146
|
FROM compartments
|
|
@@ -201571,6 +205181,7 @@ function updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEn
|
|
|
201571
205181
|
return false;
|
|
201572
205182
|
}
|
|
201573
205183
|
const existing = getPersistedCompactionMarkerState(db, sessionId);
|
|
205184
|
+
const removedSummaryMessageId = existing?.summaryMessageId ?? null;
|
|
201574
205185
|
if (existing) {
|
|
201575
205186
|
if (existingMarkerAlreadyCoversTarget(sessionId, existing, lastCompartmentEnd, targetEndMessageId)) {
|
|
201576
205187
|
return true;
|
|
@@ -201587,7 +205198,7 @@ function updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEn
|
|
|
201587
205198
|
sessionLog(sessionId, `compaction-marker: failed to remove old boundary at ordinal ${existing.boundaryOrdinal}; preserving persisted state for retry (not injecting new marker this pass)`);
|
|
201588
205199
|
return false;
|
|
201589
205200
|
}
|
|
201590
|
-
|
|
205201
|
+
persistMarkerStateAndDropReplacedTag(db, sessionId, null, removedSummaryMessageId);
|
|
201591
205202
|
sessionLog(sessionId, `compaction-marker: removed old boundary at ordinal ${existing.boundaryOrdinal}, moving to ${lastCompartmentEnd}`);
|
|
201592
205203
|
}
|
|
201593
205204
|
const result = injectCompactionMarker({
|
|
@@ -201599,11 +205210,11 @@ function updateCompactionMarkerAfterPublication(db, sessionId, lastCompartmentEn
|
|
|
201599
205210
|
resolvedBoundary: boundary
|
|
201600
205211
|
});
|
|
201601
205212
|
if (result) {
|
|
201602
|
-
|
|
205213
|
+
persistMarkerStateAndDropReplacedTag(db, sessionId, {
|
|
201603
205214
|
...result,
|
|
201604
205215
|
boundaryOrdinal: lastCompartmentEnd,
|
|
201605
205216
|
targetEndMessageId
|
|
201606
|
-
});
|
|
205217
|
+
}, removedSummaryMessageId);
|
|
201607
205218
|
sessionLog(sessionId, `compaction-marker: injected at ordinal ${lastCompartmentEnd}, boundary user msg ${result.boundaryMessageId}`);
|
|
201608
205219
|
return true;
|
|
201609
205220
|
}
|
|
@@ -203487,6 +207098,11 @@ function applyMemoryMigration(db, projectPath, result) {
|
|
|
203487
207098
|
}
|
|
203488
207099
|
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
207100
|
|
|
207101
|
+
// ../plugin/src/hooks/magic-context/lkg-slot.ts
|
|
207102
|
+
var LKG_TOTAL_BYTES = 64 * 1024 * 1024;
|
|
207103
|
+
var LKG_SINGLE_SLOT_BYTES = 24 * 1024 * 1024;
|
|
207104
|
+
var slots = new Map;
|
|
207105
|
+
|
|
203490
207106
|
// ../plugin/src/hooks/magic-context/recomp-orchestrator.ts
|
|
203491
207107
|
function isRecompFailure(message) {
|
|
203492
207108
|
return /—\s*(Failed|Skipped)/.test(message);
|
|
@@ -203925,7 +207541,13 @@ import {
|
|
|
203925
207541
|
|
|
203926
207542
|
// ../plugin/src/hooks/magic-context/m0-token-breakdown.ts
|
|
203927
207543
|
function computeM0BlockTokens(db, sessionId, args) {
|
|
203928
|
-
const {
|
|
207544
|
+
const {
|
|
207545
|
+
m0Text,
|
|
207546
|
+
projectIdentity,
|
|
207547
|
+
injectionBudgetTokens,
|
|
207548
|
+
memoryBlockCount,
|
|
207549
|
+
compartmentTokensOverride
|
|
207550
|
+
} = args;
|
|
203929
207551
|
const docsBlock = extractM0Block(m0Text, "project-docs");
|
|
203930
207552
|
const docsTokens = docsBlock ? estimateTokens(docsBlock) : 0;
|
|
203931
207553
|
const profileBlock = extractM0Block(m0Text, "user-profile");
|
|
@@ -203939,7 +207561,9 @@ function computeM0BlockTokens(db, sessionId, args) {
|
|
|
203939
207561
|
}
|
|
203940
207562
|
let compartmentTokens = 0;
|
|
203941
207563
|
const historyBlock = extractM0Block(m0Text, "session-history");
|
|
203942
|
-
if (
|
|
207564
|
+
if (typeof compartmentTokensOverride === "number" && Number.isFinite(compartmentTokensOverride) && compartmentTokensOverride >= 0) {
|
|
207565
|
+
compartmentTokens = compartmentTokensOverride;
|
|
207566
|
+
} else if (historyBlock) {
|
|
203943
207567
|
compartmentTokens = estimateTokens(historyBlock);
|
|
203944
207568
|
} else {
|
|
203945
207569
|
try {
|
|
@@ -203976,7 +207600,7 @@ function formatThresholdPercent(value) {
|
|
|
203976
207600
|
// package.json
|
|
203977
207601
|
var package_default = {
|
|
203978
207602
|
name: "@cortexkit/pi-magic-context",
|
|
203979
|
-
version: "0.32.
|
|
207603
|
+
version: "0.32.3",
|
|
203980
207604
|
type: "module",
|
|
203981
207605
|
description: "Pi coding agent extension for Magic Context — cross-session memory and context management",
|
|
203982
207606
|
main: "dist/index.js",
|
|
@@ -211274,11 +214898,12 @@ async function src_default2(pi) {
|
|
|
211274
214898
|
log(`${PREFIX} subagent child detected (${MAGIC_CONTEXT_PI_SUBAGENT_ENV}=1); skipping full extension registration`);
|
|
211275
214899
|
return;
|
|
211276
214900
|
}
|
|
214901
|
+
beginBootQuietPeriod();
|
|
211277
214902
|
const storageDir = getMagicContextStorageDir();
|
|
211278
214903
|
const dbPath = join13(storageDir, "context.db");
|
|
211279
214904
|
let db;
|
|
211280
214905
|
try {
|
|
211281
|
-
db =
|
|
214906
|
+
db = await openDatabaseAsync();
|
|
211282
214907
|
} catch (err5) {
|
|
211283
214908
|
const message = err5 instanceof Error ? err5.message : String(err5);
|
|
211284
214909
|
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 +214914,12 @@ async function src_default2(pi) {
|
|
|
211289
214914
|
return;
|
|
211290
214915
|
}
|
|
211291
214916
|
const database = db;
|
|
211292
|
-
|
|
211293
|
-
|
|
214917
|
+
scheduleAfterBootQuiet(() => {
|
|
214918
|
+
runDeferredV22Backfill(db).catch((err5) => {
|
|
214919
|
+
warn(`[v22-backfill] background runner failed: ${err5}`);
|
|
214920
|
+
});
|
|
211294
214921
|
});
|
|
211295
|
-
|
|
214922
|
+
scheduleAfterBootQuiet(() => {
|
|
211296
214923
|
(async () => {
|
|
211297
214924
|
try {
|
|
211298
214925
|
const api2 = await loadDefaultPiSessionApi();
|