@context-engine-bridge/context-engine-mcp-bridge 0.0.82 → 0.0.84
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/package.json +1 -1
- package/src/authConfig.js +23 -0
- package/src/mcpServer.js +333 -40
- package/audit-log-audit.png +0 -0
- package/billing-page-audit.png +0 -0
- package/billing-page-desktop.png +0 -0
- package/feedback-page-empty.png +0 -0
- package/feedback-preview-verification.png +0 -0
- package/feedback-preview-visible.png +0 -0
- package/feedback-reply-modal-verification.png +0 -0
- package/feedback-reply-modal.png +0 -0
- package/local-platform-desktop-final.png +0 -0
- package/local-platform-desktop.png +0 -0
- package/local-platform-mobile-final-2.png +0 -0
- package/local-platform-mobile-final.png +0 -0
- package/local-platform-mobile.png +0 -0
- package/organizations-table-audit.png +0 -0
- package/orgs-page-before-sort.png +0 -0
- package/orgs-table-desktop.png +0 -0
- package/platform-dashboard-audit.png +0 -0
- package/platform-dashboard-desktop.png +0 -0
- package/platform-dashboard-mobile.png +0 -0
- package/users-page-desktop.png +0 -0
- package/users-table-audit.png +0 -0
package/package.json
CHANGED
package/src/authConfig.js
CHANGED
|
@@ -152,3 +152,26 @@ export function saveResolvedCollection(backendUrl, scope, collection) {
|
|
|
152
152
|
};
|
|
153
153
|
writeConfig(all);
|
|
154
154
|
}
|
|
155
|
+
|
|
156
|
+
export function clearResolvedCollection(backendUrl, scope) {
|
|
157
|
+
if (!backendUrl) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
const scopeKey = buildResolvedCollectionScopeKey(scope);
|
|
161
|
+
if (!scopeKey) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const all = readConfig();
|
|
165
|
+
const key = String(backendUrl);
|
|
166
|
+
const existingEntry = all[key] && typeof all[key] === "object" ? all[key] : {};
|
|
167
|
+
const resolvedCollections = { ...getResolvedCollections(existingEntry) };
|
|
168
|
+
if (!Object.prototype.hasOwnProperty.call(resolvedCollections, scopeKey)) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
delete resolvedCollections[scopeKey];
|
|
172
|
+
all[key] = {
|
|
173
|
+
...existingEntry,
|
|
174
|
+
[RESOLVED_COLLECTIONS_KEY]: resolvedCollections,
|
|
175
|
+
};
|
|
176
|
+
writeConfig(all);
|
|
177
|
+
}
|
package/src/mcpServer.js
CHANGED
|
@@ -12,6 +12,7 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
|
12
12
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
13
13
|
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
14
14
|
import {
|
|
15
|
+
clearResolvedCollection,
|
|
15
16
|
loadAnyAuthEntry,
|
|
16
17
|
loadAuthEntry,
|
|
17
18
|
loadResolvedCollection,
|
|
@@ -522,6 +523,70 @@ function isConnectionDeadError(error) {
|
|
|
522
523
|
}
|
|
523
524
|
}
|
|
524
525
|
|
|
526
|
+
/**
|
|
527
|
+
* Detect a strong signal that the active collection has been deleted on the server.
|
|
528
|
+
* Only triggers on unambiguous signals to avoid false positives on transient errors.
|
|
529
|
+
*
|
|
530
|
+
* Signals recognized:
|
|
531
|
+
* - HTTP 404 error whose message contains a collection-related keyword
|
|
532
|
+
* - Error code "COLLECTION_DELETED"
|
|
533
|
+
* - Error message containing "collection_deleted", "collection not found",
|
|
534
|
+
* "collection doesn't exist", or "collection does not exist"
|
|
535
|
+
*/
|
|
536
|
+
function isCollectionDeletedSignal(error) {
|
|
537
|
+
try {
|
|
538
|
+
if (!error) return false;
|
|
539
|
+
// Explicit error code
|
|
540
|
+
if (
|
|
541
|
+
typeof error.code === "string" &&
|
|
542
|
+
error.code.toUpperCase() === "COLLECTION_DELETED"
|
|
543
|
+
) {
|
|
544
|
+
return true;
|
|
545
|
+
}
|
|
546
|
+
const msg = _errorMessage(error);
|
|
547
|
+
if (!msg) return false;
|
|
548
|
+
const lower = msg.toLowerCase();
|
|
549
|
+
// 404 with a collection-related keyword is a strong signal
|
|
550
|
+
if (
|
|
551
|
+
(lower.includes("404") || lower.includes("not found")) &&
|
|
552
|
+
lower.includes("collection")
|
|
553
|
+
) {
|
|
554
|
+
return true;
|
|
555
|
+
}
|
|
556
|
+
// Explicit deletion phrases
|
|
557
|
+
if (
|
|
558
|
+
lower.includes("collection_deleted") ||
|
|
559
|
+
lower.includes("collection not found") ||
|
|
560
|
+
lower.includes("collection doesn't exist") ||
|
|
561
|
+
lower.includes("collection does not exist")
|
|
562
|
+
) {
|
|
563
|
+
return true;
|
|
564
|
+
}
|
|
565
|
+
return false;
|
|
566
|
+
} catch {
|
|
567
|
+
return false;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* Inspect a successful tool call result for an explicit collection_deleted flag.
|
|
573
|
+
* Returns true only when the response payload contains `collection_deleted: true`.
|
|
574
|
+
*/
|
|
575
|
+
function resultIndicatesCollectionDeleted(result) {
|
|
576
|
+
try {
|
|
577
|
+
if (!result || !Array.isArray(result.content)) return false;
|
|
578
|
+
const textBlock = result.content.find((c) => c && c.type === "text");
|
|
579
|
+
if (!textBlock || typeof textBlock.text !== "string") return false;
|
|
580
|
+
const parsed = JSON.parse(textBlock.text);
|
|
581
|
+
if (parsed && parsed.collection_deleted === true) return true;
|
|
582
|
+
// Also check nested result envelope
|
|
583
|
+
if (parsed && parsed.result && parsed.result.collection_deleted === true) return true;
|
|
584
|
+
return false;
|
|
585
|
+
} catch {
|
|
586
|
+
return false;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
525
590
|
// MCP stdio server implemented using the official MCP TypeScript SDK.
|
|
526
591
|
// Acts as a low-level proxy for tools, forwarding tools/list and tools/call
|
|
527
592
|
// to the remote qdrant-indexer MCP server while adding a local `ping` tool.
|
|
@@ -647,6 +712,12 @@ async function fetchBridgeCollectionState({
|
|
|
647
712
|
);
|
|
648
713
|
return null;
|
|
649
714
|
}
|
|
715
|
+
if (resp.status === 404) {
|
|
716
|
+
// Server is reachable but has no workspace state — do NOT fall back to local caches.
|
|
717
|
+
// This can happen when workspace state was cleaned up during collection deletion.
|
|
718
|
+
debugLog("[ctxce] /bridge/state responded 404; server has no state for this workspace");
|
|
719
|
+
return { _server_reachable: true };
|
|
720
|
+
}
|
|
650
721
|
throw new Error(`bridge/state responded ${resp.status}`);
|
|
651
722
|
}
|
|
652
723
|
debugLog(`[ctxce] bridge/state responded ${resp.status}`);
|
|
@@ -711,26 +782,11 @@ export async function buildDefaultsPayload({
|
|
|
711
782
|
logicalRepoId = "";
|
|
712
783
|
}
|
|
713
784
|
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
defaultsPayload.collection = exactWorkspaceCollection;
|
|
720
|
-
log(`[ctxce] Using exact workspace cached collection: ${exactWorkspaceCollection}`);
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
if (!defaultsPayload.collection && backendHint && logicalRepoId) {
|
|
724
|
-
const cachedCollection = loadResolvedCollectionHint(backendHint, {
|
|
725
|
-
orgId: resolvedAuthEntry?.org_id,
|
|
726
|
-
orgSlug: resolvedAuthEntry?.org_slug,
|
|
727
|
-
logicalRepoId,
|
|
728
|
-
});
|
|
729
|
-
if (cachedCollection) {
|
|
730
|
-
defaultsPayload.collection = cachedCollection;
|
|
731
|
-
log(`[ctxce] Using cached resolved collection from auth store: ${cachedCollection}`);
|
|
732
|
-
}
|
|
733
|
-
}
|
|
785
|
+
// --- Server-first collection resolution ---
|
|
786
|
+
// Priority: pinned > server /bridge/state > local caches (fallback only).
|
|
787
|
+
// This prevents stale local caches from winning over server truth
|
|
788
|
+
// after a collection is deleted.
|
|
789
|
+
let serverResolved = false;
|
|
734
790
|
|
|
735
791
|
if (!pinnedCollection) {
|
|
736
792
|
try {
|
|
@@ -745,31 +801,87 @@ export async function buildDefaultsPayload({
|
|
|
745
801
|
uploadServiceUrl,
|
|
746
802
|
});
|
|
747
803
|
if (state) {
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
804
|
+
// Server responded 404 — reachable but no workspace state.
|
|
805
|
+
// Mark serverResolved to prevent falling back to stale local caches.
|
|
806
|
+
if (state._server_reachable && !state.collection_deleted && !state.serving_collection && !state.active_collection) {
|
|
807
|
+
serverResolved = true;
|
|
808
|
+
log("[ctxce] Server reachable but no workspace state found, skipping local cache fallback");
|
|
809
|
+
}
|
|
810
|
+
// Handle explicit collection_deleted signal from the server
|
|
811
|
+
if (state.collection_deleted === true) {
|
|
812
|
+
log("[ctxce] Server reports collection deleted, clearing all local caches");
|
|
813
|
+
defaultsPayload.collection = "";
|
|
814
|
+
serverResolved = true;
|
|
815
|
+
try {
|
|
816
|
+
_clearExactWorkspaceCachedCollection(workspace);
|
|
817
|
+
} catch (wsErr) {
|
|
818
|
+
log("[ctxce] Failed to clear workspace ctx_config.json default_collection: " + String(wsErr));
|
|
819
|
+
}
|
|
820
|
+
// Clear the auth.json resolved collection cache
|
|
821
|
+
try {
|
|
822
|
+
if (backendHint && logicalRepoId) {
|
|
823
|
+
clearResolvedCollection(backendHint, {
|
|
824
|
+
orgId: resolvedAuthEntry?.org_id,
|
|
825
|
+
orgSlug: resolvedAuthEntry?.org_slug,
|
|
826
|
+
logicalRepoId,
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
} catch (authErr) {
|
|
830
|
+
log("[ctxce] Failed to clear auth.json resolved collection: " + String(authErr));
|
|
758
831
|
}
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
832
|
+
} else {
|
|
833
|
+
const servingCollection = typeof state.serving_collection === "string"
|
|
834
|
+
? state.serving_collection.trim()
|
|
835
|
+
: "";
|
|
836
|
+
const activeCollection = typeof state.active_collection === "string"
|
|
837
|
+
? state.active_collection.trim()
|
|
838
|
+
: "";
|
|
839
|
+
if (servingCollection) {
|
|
840
|
+
defaultsPayload.collection = servingCollection;
|
|
841
|
+
serverResolved = true;
|
|
842
|
+
if (!configuredCollection || configuredCollection !== servingCollection) {
|
|
843
|
+
log(`[ctxce] Using serving collection from /bridge/state: ${servingCollection}`);
|
|
844
|
+
}
|
|
845
|
+
// Update local caches to match server truth
|
|
846
|
+
if (backendHint && logicalRepoId) {
|
|
847
|
+
saveResolvedCollectionHint(backendHint, {
|
|
848
|
+
orgId: resolvedAuthEntry?.org_id,
|
|
849
|
+
orgSlug: resolvedAuthEntry?.org_slug,
|
|
850
|
+
logicalRepoId,
|
|
851
|
+
}, servingCollection);
|
|
852
|
+
}
|
|
853
|
+
_writeExactWorkspaceCachedCollection(workspace, servingCollection);
|
|
854
|
+
} else if (activeCollection) {
|
|
855
|
+
defaultsPayload.collection = activeCollection;
|
|
856
|
+
serverResolved = true;
|
|
857
|
+
log(`[ctxce] Using active collection from /bridge/state: ${activeCollection}`);
|
|
765
858
|
}
|
|
766
|
-
} else if (!defaultsPayload.collection && activeCollection) {
|
|
767
|
-
defaultsPayload.collection = activeCollection;
|
|
768
|
-
log(`[ctxce] Using active collection from /bridge/state fallback: ${activeCollection}`);
|
|
769
859
|
}
|
|
770
860
|
}
|
|
771
861
|
} catch (err) {
|
|
772
|
-
log("[ctxce] bridge/state lookup failed: " + String(err));
|
|
862
|
+
log("[ctxce] bridge/state lookup failed, falling back to local caches: " + String(err));
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
// Fallback to local caches ONLY when server is unreachable
|
|
867
|
+
if (!serverResolved && !pinnedCollection) {
|
|
868
|
+
const exactWorkspaceCollection = _readExactWorkspaceCachedCollection(workspace);
|
|
869
|
+
|
|
870
|
+
if (!defaultsPayload.collection && exactWorkspaceCollection) {
|
|
871
|
+
defaultsPayload.collection = exactWorkspaceCollection;
|
|
872
|
+
log(`[ctxce] WARNING: Using cached collection ${exactWorkspaceCollection} (server unavailable — may be stale)`);
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
if (!defaultsPayload.collection && backendHint && logicalRepoId) {
|
|
876
|
+
const cachedCollection = loadResolvedCollectionHint(backendHint, {
|
|
877
|
+
orgId: resolvedAuthEntry?.org_id,
|
|
878
|
+
orgSlug: resolvedAuthEntry?.org_slug,
|
|
879
|
+
logicalRepoId,
|
|
880
|
+
});
|
|
881
|
+
if (cachedCollection) {
|
|
882
|
+
defaultsPayload.collection = cachedCollection;
|
|
883
|
+
log(`[ctxce] WARNING: Using cached collection ${cachedCollection} from auth store (server unavailable — may be stale)`);
|
|
884
|
+
}
|
|
773
885
|
}
|
|
774
886
|
}
|
|
775
887
|
|
|
@@ -832,6 +944,74 @@ function _readExactWorkspaceCachedCollection(workspacePath) {
|
|
|
832
944
|
}
|
|
833
945
|
}
|
|
834
946
|
|
|
947
|
+
function _writeExactWorkspaceCachedCollection(workspacePath, collection) {
|
|
948
|
+
try {
|
|
949
|
+
const resolvedWorkspace = _validateWorkspacePath(workspacePath);
|
|
950
|
+
if (!resolvedWorkspace) return;
|
|
951
|
+
const wsDir = _computeWorkspaceDir(resolvedWorkspace);
|
|
952
|
+
const configPath = path.join(wsDir, "ctx_config.json");
|
|
953
|
+
if (!fs.existsSync(configPath)) return;
|
|
954
|
+
const existing = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
955
|
+
if (existing && typeof existing === "object") {
|
|
956
|
+
existing.default_collection = collection;
|
|
957
|
+
fs.writeFileSync(configPath, JSON.stringify(existing, null, 2), "utf8");
|
|
958
|
+
}
|
|
959
|
+
} catch (_) {}
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
function _clearExactWorkspaceCachedCollection(workspacePath) {
|
|
963
|
+
try {
|
|
964
|
+
const resolvedWorkspace = _validateWorkspacePath(workspacePath);
|
|
965
|
+
if (!resolvedWorkspace) return;
|
|
966
|
+
const wsDir = _computeWorkspaceDir(resolvedWorkspace);
|
|
967
|
+
// Clear default_collection from ctx_config.json
|
|
968
|
+
const configPath = path.join(wsDir, "ctx_config.json");
|
|
969
|
+
if (fs.existsSync(configPath)) {
|
|
970
|
+
try {
|
|
971
|
+
const existing = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
972
|
+
if (existing && existing.default_collection) {
|
|
973
|
+
existing.default_collection = "";
|
|
974
|
+
fs.writeFileSync(configPath, JSON.stringify(existing, null, 2), "utf8");
|
|
975
|
+
}
|
|
976
|
+
} catch (_) {}
|
|
977
|
+
}
|
|
978
|
+
// Clear session_collections.json (may reference the deleted collection)
|
|
979
|
+
const sessionCollPath = path.join(wsDir, "session_collections.json");
|
|
980
|
+
try {
|
|
981
|
+
if (fs.existsSync(sessionCollPath)) {
|
|
982
|
+
fs.unlinkSync(sessionCollPath);
|
|
983
|
+
debugLog("[ctxce] Cleared session_collections.json after collection deletion");
|
|
984
|
+
}
|
|
985
|
+
} catch (_) {}
|
|
986
|
+
// Clear workspace hints (PID-keyed hint files in ~/.ctxce/workspace-hints/)
|
|
987
|
+
try {
|
|
988
|
+
const hintsDir = path.join(os.homedir(), ".ctxce", "workspace-hints");
|
|
989
|
+
if (fs.existsSync(hintsDir)) {
|
|
990
|
+
// Clear hint for current process tree (ppid is the VS Code process)
|
|
991
|
+
const currentPpid = process.ppid;
|
|
992
|
+
if (currentPpid && currentPpid > 1) {
|
|
993
|
+
const currentHint = path.join(hintsDir, `${currentPpid}.json`);
|
|
994
|
+
try {
|
|
995
|
+
if (fs.existsSync(currentHint)) {
|
|
996
|
+
fs.unlinkSync(currentHint);
|
|
997
|
+
debugLog(`[ctxce] Cleared workspace hint for current VS Code process (PID ${currentPpid})`);
|
|
998
|
+
}
|
|
999
|
+
} catch (_) {}
|
|
1000
|
+
}
|
|
1001
|
+
// Also clean up stale hints for dead PIDs
|
|
1002
|
+
for (const name of fs.readdirSync(hintsDir)) {
|
|
1003
|
+
if (!name.endsWith(".json")) continue;
|
|
1004
|
+
const pid = parseInt(name.replace(".json", ""), 10);
|
|
1005
|
+
if (!pid || pid <= 1) continue;
|
|
1006
|
+
try { process.kill(pid, 0); } catch (_) {
|
|
1007
|
+
try { fs.unlinkSync(path.join(hintsDir, name)); } catch (_) {}
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
} catch (_) {}
|
|
1012
|
+
} catch (_) {}
|
|
1013
|
+
}
|
|
1014
|
+
|
|
835
1015
|
const MAX_WS_SCAN = 50;
|
|
836
1016
|
|
|
837
1017
|
function _resolveWorkspace(providedWorkspace) {
|
|
@@ -1523,10 +1703,123 @@ async function createBridgeServer(options) {
|
|
|
1523
1703
|
}
|
|
1524
1704
|
} catch (e) { debugLog("[ctxce] LSP not_available inject failed: " + String(e)); }
|
|
1525
1705
|
}
|
|
1706
|
+
// Detect explicit collection_deleted flag in a successful response
|
|
1707
|
+
if (resultIndicatesCollectionDeleted(result)) {
|
|
1708
|
+
debugLog("[ctxce] Collection appears deleted, clearing cache and re-resolving");
|
|
1709
|
+
defaultsPayload.collection = "";
|
|
1710
|
+
try {
|
|
1711
|
+
_clearExactWorkspaceCachedCollection(workspace);
|
|
1712
|
+
} catch (wsErr) {
|
|
1713
|
+
debugLog("[ctxce] Failed to clear workspace collection cache: " + String(wsErr));
|
|
1714
|
+
}
|
|
1715
|
+
try {
|
|
1716
|
+
if (backendHint) {
|
|
1717
|
+
const authEntry = loadAuthEntry(backendHint);
|
|
1718
|
+
let repoId = "";
|
|
1719
|
+
try { repoId = computeLogicalRepoIdentity(workspace)?.id || ""; } catch { repoId = ""; }
|
|
1720
|
+
clearResolvedCollection(backendHint, {
|
|
1721
|
+
orgId: authEntry?.org_id,
|
|
1722
|
+
orgSlug: authEntry?.org_slug,
|
|
1723
|
+
logicalRepoId: repoId,
|
|
1724
|
+
});
|
|
1725
|
+
}
|
|
1726
|
+
} catch (clearErr) {
|
|
1727
|
+
debugLog("[ctxce] Failed to clear resolved collection cache: " + String(clearErr));
|
|
1728
|
+
}
|
|
1729
|
+
// Best-effort re-resolve via /bridge/state
|
|
1730
|
+
try {
|
|
1731
|
+
const freshPayload = await buildDefaultsPayload({
|
|
1732
|
+
workspace,
|
|
1733
|
+
sessionId,
|
|
1734
|
+
explicitCollection: "",
|
|
1735
|
+
defaultCollection: "",
|
|
1736
|
+
defaultMode,
|
|
1737
|
+
defaultUnder,
|
|
1738
|
+
config,
|
|
1739
|
+
backendHint,
|
|
1740
|
+
uploadServiceUrl,
|
|
1741
|
+
});
|
|
1742
|
+
if (freshPayload.collection) {
|
|
1743
|
+
defaultsPayload.collection = freshPayload.collection;
|
|
1744
|
+
debugLog("[ctxce] Re-resolved collection after deletion: " + freshPayload.collection);
|
|
1745
|
+
}
|
|
1746
|
+
} catch (reResolveErr) {
|
|
1747
|
+
debugLog("[ctxce] Failed to re-resolve collection after deletion: " + String(reResolveErr));
|
|
1748
|
+
}
|
|
1749
|
+
// Re-send session defaults so remote MCP servers stop using the stale collection
|
|
1750
|
+
try {
|
|
1751
|
+
if (indexerClient) {
|
|
1752
|
+
await sendSessionDefaults(indexerClient, defaultsPayload, "indexer");
|
|
1753
|
+
}
|
|
1754
|
+
if (memoryClient) {
|
|
1755
|
+
await sendSessionDefaults(memoryClient, defaultsPayload, "memory");
|
|
1756
|
+
}
|
|
1757
|
+
debugLog("[ctxce] Re-sent session defaults after collection deletion detection");
|
|
1758
|
+
} catch (sdErr) {
|
|
1759
|
+
debugLog("[ctxce] Failed to re-send session defaults after deletion: " + String(sdErr));
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1526
1763
|
return maybeRemapToolResult(name, result, workspace);
|
|
1527
1764
|
} catch (err) {
|
|
1528
1765
|
lastError = err;
|
|
1529
1766
|
|
|
1767
|
+
// Detect collection deletion from error signals
|
|
1768
|
+
if (isCollectionDeletedSignal(err)) {
|
|
1769
|
+
debugLog("[ctxce] Collection appears deleted, clearing cache and re-resolving");
|
|
1770
|
+
defaultsPayload.collection = "";
|
|
1771
|
+
try {
|
|
1772
|
+
_clearExactWorkspaceCachedCollection(workspace);
|
|
1773
|
+
} catch (wsErr) {
|
|
1774
|
+
debugLog("[ctxce] Failed to clear workspace collection cache: " + String(wsErr));
|
|
1775
|
+
}
|
|
1776
|
+
try {
|
|
1777
|
+
if (backendHint) {
|
|
1778
|
+
const authEntry = loadAuthEntry(backendHint);
|
|
1779
|
+
let repoId = "";
|
|
1780
|
+
try { repoId = computeLogicalRepoIdentity(workspace)?.id || ""; } catch { repoId = ""; }
|
|
1781
|
+
clearResolvedCollection(backendHint, {
|
|
1782
|
+
orgId: authEntry?.org_id,
|
|
1783
|
+
orgSlug: authEntry?.org_slug,
|
|
1784
|
+
logicalRepoId: repoId,
|
|
1785
|
+
});
|
|
1786
|
+
}
|
|
1787
|
+
} catch (clearErr) {
|
|
1788
|
+
debugLog("[ctxce] Failed to clear resolved collection cache: " + String(clearErr));
|
|
1789
|
+
}
|
|
1790
|
+
// Best-effort re-resolve via /bridge/state (fire-and-forget)
|
|
1791
|
+
buildDefaultsPayload({
|
|
1792
|
+
workspace,
|
|
1793
|
+
sessionId,
|
|
1794
|
+
explicitCollection: "",
|
|
1795
|
+
defaultCollection: "",
|
|
1796
|
+
defaultMode,
|
|
1797
|
+
defaultUnder,
|
|
1798
|
+
config,
|
|
1799
|
+
backendHint,
|
|
1800
|
+
uploadServiceUrl,
|
|
1801
|
+
}).then(async (freshPayload) => {
|
|
1802
|
+
if (freshPayload.collection) {
|
|
1803
|
+
defaultsPayload.collection = freshPayload.collection;
|
|
1804
|
+
debugLog("[ctxce] Re-resolved collection after deletion: " + freshPayload.collection);
|
|
1805
|
+
}
|
|
1806
|
+
// Re-send session defaults so remote MCP servers stop using the stale collection
|
|
1807
|
+
try {
|
|
1808
|
+
if (indexerClient) {
|
|
1809
|
+
await sendSessionDefaults(indexerClient, defaultsPayload, "indexer");
|
|
1810
|
+
}
|
|
1811
|
+
if (memoryClient) {
|
|
1812
|
+
await sendSessionDefaults(memoryClient, defaultsPayload, "memory");
|
|
1813
|
+
}
|
|
1814
|
+
debugLog("[ctxce] Re-sent session defaults after collection deletion detection (error path)");
|
|
1815
|
+
} catch (sdErr) {
|
|
1816
|
+
debugLog("[ctxce] Failed to re-send session defaults after deletion: " + String(sdErr));
|
|
1817
|
+
}
|
|
1818
|
+
}).catch((reResolveErr) => {
|
|
1819
|
+
debugLog("[ctxce] Failed to re-resolve collection after deletion: " + String(reResolveErr));
|
|
1820
|
+
});
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1530
1823
|
if (isConnectionDeadError(err) && !connectionRetried) {
|
|
1531
1824
|
debugLog(
|
|
1532
1825
|
"[ctxce] tools/call: connection dead (server may have restarted); recreating clients and retrying: " +
|
package/audit-log-audit.png
DELETED
|
Binary file
|
package/billing-page-audit.png
DELETED
|
Binary file
|
package/billing-page-desktop.png
DELETED
|
Binary file
|
package/feedback-page-empty.png
DELETED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/feedback-reply-modal.png
DELETED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/orgs-table-desktop.png
DELETED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/users-page-desktop.png
DELETED
|
Binary file
|
package/users-table-audit.png
DELETED
|
Binary file
|