@usewhisper/mcp-server 2.14.0 → 2.15.0
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/server.js +363 -74
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -7,7 +7,7 @@ import { z } from "zod";
|
|
|
7
7
|
import { execSync, spawnSync } from "child_process";
|
|
8
8
|
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync, appendFileSync } from "fs";
|
|
9
9
|
import { join, relative, extname, normalize as normalizePath, resolve as resolvePath } from "path";
|
|
10
|
-
import { homedir } from "os";
|
|
10
|
+
import { homedir, userInfo } from "os";
|
|
11
11
|
import { createHash, randomUUID } from "crypto";
|
|
12
12
|
|
|
13
13
|
// ../src/sdk/core/telemetry.ts
|
|
@@ -4711,12 +4711,28 @@ async function ingestSessionWithSyncFallback(params) {
|
|
|
4711
4711
|
return whisper.ingestSession(params);
|
|
4712
4712
|
}
|
|
4713
4713
|
}
|
|
4714
|
+
function apiKeyFingerprint(apiKey) {
|
|
4715
|
+
const value = String(apiKey || "").trim();
|
|
4716
|
+
if (!value) return "anon";
|
|
4717
|
+
return createHash("sha256").update(value).digest("hex").slice(0, 12);
|
|
4718
|
+
}
|
|
4714
4719
|
function defaultMcpUserId(params) {
|
|
4715
4720
|
const explicit = process.env.WHISPER_USER_ID?.trim();
|
|
4716
4721
|
if (explicit) return explicit;
|
|
4722
|
+
let osUsername = process.env.USER?.trim() || process.env.USERNAME?.trim();
|
|
4723
|
+
if (!osUsername) {
|
|
4724
|
+
try {
|
|
4725
|
+
osUsername = userInfo().username?.trim();
|
|
4726
|
+
} catch {
|
|
4727
|
+
osUsername = "";
|
|
4728
|
+
}
|
|
4729
|
+
}
|
|
4730
|
+
if (!osUsername) {
|
|
4731
|
+
throw new Error("Unable to derive user identity. Set WHISPER_USER_ID or pass user_id.");
|
|
4732
|
+
}
|
|
4717
4733
|
const workspacePath = params?.workspacePath || canonicalizeWorkspacePath(process.cwd());
|
|
4718
4734
|
const projectRef = params?.project || DEFAULT_PROJECT || "default";
|
|
4719
|
-
const seed = `${workspacePath}|${projectRef}|${API_KEY.
|
|
4735
|
+
const seed = `${workspacePath}|${projectRef}|${apiKeyFingerprint(API_KEY)}|${osUsername.toLowerCase()}`;
|
|
4720
4736
|
return `mcp-user-${createHash("sha256").update(seed).digest("hex").slice(0, 12)}`;
|
|
4721
4737
|
}
|
|
4722
4738
|
function resolveMcpScope(params, options) {
|
|
@@ -4732,6 +4748,68 @@ function resolveMcpScope(params, options) {
|
|
|
4732
4748
|
workspacePath
|
|
4733
4749
|
};
|
|
4734
4750
|
}
|
|
4751
|
+
function resolveMemoryScope(params) {
|
|
4752
|
+
const base = resolveMcpScope({
|
|
4753
|
+
project: params?.project,
|
|
4754
|
+
user_id: params?.user_id,
|
|
4755
|
+
session_id: params?.session_id,
|
|
4756
|
+
path: params?.path
|
|
4757
|
+
});
|
|
4758
|
+
const scopeMode = params?.scope === "shared_project" ? "shared_project" : "personal";
|
|
4759
|
+
if (scopeMode === "shared_project") {
|
|
4760
|
+
if (!base.project && !DEFAULT_PROJECT) {
|
|
4761
|
+
throw new Error("shared_project scope requires a project. Set WHISPER_PROJECT or pass project.");
|
|
4762
|
+
}
|
|
4763
|
+
const projectRef = base.project || DEFAULT_PROJECT || "default";
|
|
4764
|
+
return {
|
|
4765
|
+
...base,
|
|
4766
|
+
userId: `shared-project:${projectRef}`,
|
|
4767
|
+
sessionId: void 0,
|
|
4768
|
+
actorUserId: base.userId,
|
|
4769
|
+
actorSessionId: base.sessionId,
|
|
4770
|
+
scopeMode,
|
|
4771
|
+
sharedProjectRef: projectRef
|
|
4772
|
+
};
|
|
4773
|
+
}
|
|
4774
|
+
return {
|
|
4775
|
+
...base,
|
|
4776
|
+
actorUserId: base.userId,
|
|
4777
|
+
actorSessionId: base.sessionId,
|
|
4778
|
+
scopeMode,
|
|
4779
|
+
sharedProjectRef: null
|
|
4780
|
+
};
|
|
4781
|
+
}
|
|
4782
|
+
function sharedProjectMemoryUserId(projectRef) {
|
|
4783
|
+
return `shared-project:${projectRef}`;
|
|
4784
|
+
}
|
|
4785
|
+
function buildMemoryScopeMetadata(args) {
|
|
4786
|
+
const { memoryScope } = args;
|
|
4787
|
+
const metadata = {
|
|
4788
|
+
scope: memoryScope.scopeMode,
|
|
4789
|
+
...memoryScope.sharedProjectRef ? { shared_project_ref: memoryScope.sharedProjectRef } : {}
|
|
4790
|
+
};
|
|
4791
|
+
if (memoryScope.scopeMode === "shared_project") {
|
|
4792
|
+
metadata.provenance = {
|
|
4793
|
+
writer_user_id: memoryScope.actorUserId,
|
|
4794
|
+
writer_session_id: memoryScope.actorSessionId || null,
|
|
4795
|
+
writer_agent_id: args.agent_id || process.env.WHISPER_AGENT_ID || null,
|
|
4796
|
+
written_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
4797
|
+
};
|
|
4798
|
+
}
|
|
4799
|
+
return metadata;
|
|
4800
|
+
}
|
|
4801
|
+
function isMemoryInScope(args) {
|
|
4802
|
+
const memory = args.memory;
|
|
4803
|
+
if (!memory || typeof memory !== "object") return false;
|
|
4804
|
+
const memoryUserId = String(memory.userId || memory.user_id || "").trim();
|
|
4805
|
+
if (args.memoryScope.scopeMode === "shared_project") {
|
|
4806
|
+
const metadata = memory.metadata && typeof memory.metadata === "object" ? memory.metadata : {};
|
|
4807
|
+
const metaScope = String(metadata.scope || "").trim();
|
|
4808
|
+
const metaProjectRef = String(metadata.shared_project_ref || "").trim();
|
|
4809
|
+
return memoryUserId === args.memoryScope.userId || metaScope === "shared_project" && metaProjectRef === (args.memoryScope.sharedProjectRef || "");
|
|
4810
|
+
}
|
|
4811
|
+
return memoryUserId === args.memoryScope.userId;
|
|
4812
|
+
}
|
|
4735
4813
|
function noteAutomaticSourceActivity(params) {
|
|
4736
4814
|
if (!runtimeClient) return;
|
|
4737
4815
|
const sourceIds = [...new Set((params.sourceIds || []).map((value) => String(value || "").trim()).filter(Boolean))];
|
|
@@ -5298,6 +5376,82 @@ function resolveForgetQueryCandidates(rawResults, query) {
|
|
|
5298
5376
|
}
|
|
5299
5377
|
return { memory_ids: [], resolved_by: "none", warning: "Query did not resolve to a reliable memory match. No memories were changed." };
|
|
5300
5378
|
}
|
|
5379
|
+
async function searchMemoriesForContextQuery(args) {
|
|
5380
|
+
return whisper.searchMemoriesSOTA({
|
|
5381
|
+
project: args.project,
|
|
5382
|
+
query: args.query,
|
|
5383
|
+
user_id: args.user_id,
|
|
5384
|
+
session_id: args.session_id,
|
|
5385
|
+
top_k: args.top_k ?? 5,
|
|
5386
|
+
include_relations: false,
|
|
5387
|
+
include_pending: true
|
|
5388
|
+
});
|
|
5389
|
+
}
|
|
5390
|
+
async function runContextQueryMemoryRescue(args) {
|
|
5391
|
+
const scoped = await searchMemoriesForContextQuery(args);
|
|
5392
|
+
const personalResults = formatCanonicalMemoryResults(scoped);
|
|
5393
|
+
const sharedUserId = sharedProjectMemoryUserId(args.project);
|
|
5394
|
+
const shared = args.user_id === sharedUserId ? { results: [] } : await searchMemoriesForContextQuery({
|
|
5395
|
+
project: args.project,
|
|
5396
|
+
query: args.query,
|
|
5397
|
+
user_id: sharedUserId,
|
|
5398
|
+
top_k: args.top_k
|
|
5399
|
+
});
|
|
5400
|
+
const sharedResults = formatCanonicalMemoryResults(shared);
|
|
5401
|
+
const deduped = /* @__PURE__ */ new Set();
|
|
5402
|
+
const combined = [
|
|
5403
|
+
...personalResults.map((result) => ({
|
|
5404
|
+
...result,
|
|
5405
|
+
memory_scope: "personal",
|
|
5406
|
+
ranking_score: clamp012(Number(result.similarity ?? 0.5) + 0.08)
|
|
5407
|
+
})),
|
|
5408
|
+
...sharedResults.map((result) => ({
|
|
5409
|
+
...result,
|
|
5410
|
+
memory_scope: "shared_project",
|
|
5411
|
+
ranking_score: clamp012(Number(result.similarity ?? 0.5))
|
|
5412
|
+
}))
|
|
5413
|
+
].filter((result) => {
|
|
5414
|
+
if (!result.id) return false;
|
|
5415
|
+
if (deduped.has(result.id)) return false;
|
|
5416
|
+
deduped.add(result.id);
|
|
5417
|
+
return true;
|
|
5418
|
+
}).sort((a, b) => b.ranking_score - a.ranking_score);
|
|
5419
|
+
const rescueMode = combined.length === 0 ? null : sharedResults.length > 0 ? "scoped_plus_shared" : "scoped_only";
|
|
5420
|
+
return {
|
|
5421
|
+
personal_results: personalResults,
|
|
5422
|
+
shared_results: sharedResults,
|
|
5423
|
+
combined_results: combined,
|
|
5424
|
+
rescue_mode: rescueMode
|
|
5425
|
+
};
|
|
5426
|
+
}
|
|
5427
|
+
function renderContextQueryMemoryRescue(args) {
|
|
5428
|
+
const lines = args.combined_results.map(
|
|
5429
|
+
(result, index) => `${index + 1}. [${result.memory_scope}, ${result.memory_type || "memory"}, score: ${result.ranking_score.toFixed(2)}] ${result.content}`
|
|
5430
|
+
);
|
|
5431
|
+
return `Found ${args.combined_results.length} memory result(s) (project=${args.project}, user=${args.scope.userId}, session=${args.scope.sessionId}, personal=${args.personal_results.length}, shared=${args.shared_results.length}, ordering=personal_then_shared_bias):
|
|
5432
|
+
|
|
5433
|
+
${lines.join("\n\n")}`;
|
|
5434
|
+
}
|
|
5435
|
+
function memoryRescueResultsToEvidence(args) {
|
|
5436
|
+
return args.hits.map(
|
|
5437
|
+
(hit) => toEvidenceRef({
|
|
5438
|
+
id: `memory:${hit.id || randomUUID()}`,
|
|
5439
|
+
source: "memory",
|
|
5440
|
+
document: "memory",
|
|
5441
|
+
content: hit.content,
|
|
5442
|
+
score: hit.ranking_score,
|
|
5443
|
+
retrieval_source: "memory",
|
|
5444
|
+
metadata: {
|
|
5445
|
+
path: `memory://${hit.memory_scope}/${hit.id || "unknown"}`,
|
|
5446
|
+
line_start: 1,
|
|
5447
|
+
line_end: 1,
|
|
5448
|
+
score: hit.ranking_score,
|
|
5449
|
+
memory_scope: hit.memory_scope,
|
|
5450
|
+
memory_type: hit.memory_type || "factual"
|
|
5451
|
+
}
|
|
5452
|
+
}, args.workspaceId, "memory")
|
|
5453
|
+
);
|
|
5454
|
+
}
|
|
5301
5455
|
function likelyEmbeddingFailure(error) {
|
|
5302
5456
|
const message = String(error?.message || error || "").toLowerCase();
|
|
5303
5457
|
return message.includes("embedding") || message.includes("vector") || message.includes("timeout") || message.includes("timed out") || message.includes("temporarily unavailable");
|
|
@@ -6244,6 +6398,7 @@ async function runUnifiedContextRetrieval(params) {
|
|
|
6244
6398
|
let retrievalReadiness = preflight.retrieval_readiness;
|
|
6245
6399
|
let retrievalRoute = preflight.retrieval_route;
|
|
6246
6400
|
let latencyMs = 0;
|
|
6401
|
+
let memoryContribution = null;
|
|
6247
6402
|
if (preflight.retrieval_route === "local_workspace_fallback") {
|
|
6248
6403
|
const localRetrieval = await runLocalWorkspaceRetrieval({
|
|
6249
6404
|
query: params.question,
|
|
@@ -6302,6 +6457,39 @@ async function runUnifiedContextRetrieval(params) {
|
|
|
6302
6457
|
retrievalRoute = preflight.repo_grounded ? "project_repo" : "none";
|
|
6303
6458
|
}
|
|
6304
6459
|
}
|
|
6460
|
+
if (resolvedProject && params.include_memories === true && !preflight.repo_grounded) {
|
|
6461
|
+
try {
|
|
6462
|
+
memoryContribution = await runContextQueryMemoryRescue({
|
|
6463
|
+
project: resolvedProject,
|
|
6464
|
+
query: params.question,
|
|
6465
|
+
user_id: scope.userId,
|
|
6466
|
+
session_id: params.session_id,
|
|
6467
|
+
top_k: Math.max(4, Math.min(topK, 8))
|
|
6468
|
+
});
|
|
6469
|
+
if (memoryContribution.combined_results.length > 0) {
|
|
6470
|
+
const memoryEvidence = memoryRescueResultsToEvidence({
|
|
6471
|
+
workspaceId: preflight.trust_state.workspace_id,
|
|
6472
|
+
hits: memoryContribution.combined_results
|
|
6473
|
+
});
|
|
6474
|
+
const existingSourceIds = new Set(evidence.map((item) => item.source_id));
|
|
6475
|
+
const dedupedMemoryEvidence = memoryEvidence.filter((item) => !existingSourceIds.has(item.source_id));
|
|
6476
|
+
evidence = [...evidence, ...dedupedMemoryEvidence];
|
|
6477
|
+
const memoryContext = renderContextQueryMemoryRescue({
|
|
6478
|
+
project: resolvedProject,
|
|
6479
|
+
scope,
|
|
6480
|
+
personal_results: memoryContribution.personal_results,
|
|
6481
|
+
shared_results: memoryContribution.shared_results,
|
|
6482
|
+
combined_results: memoryContribution.combined_results
|
|
6483
|
+
});
|
|
6484
|
+
contextText = contextText.trim().length > 0 ? `${contextText}
|
|
6485
|
+
|
|
6486
|
+
${memoryContext}` : memoryContext;
|
|
6487
|
+
if (retrievalRoute === "none") retrievalRoute = "memory_only";
|
|
6488
|
+
}
|
|
6489
|
+
} catch (error) {
|
|
6490
|
+
warnings.push(`Scoped memory contribution failed: ${String(error?.message || error)}`);
|
|
6491
|
+
}
|
|
6492
|
+
}
|
|
6305
6493
|
recordSemanticAttempt(preflight.trust_state.workspace_id, semanticStatus === "failed");
|
|
6306
6494
|
const semanticStats = getWorkspaceSemanticFailureStats(preflight.trust_state.workspace_id);
|
|
6307
6495
|
const trustScore = computeTrustScore({
|
|
@@ -6374,6 +6562,14 @@ async function runUnifiedContextRetrieval(params) {
|
|
|
6374
6562
|
user_id: scope.userId,
|
|
6375
6563
|
session_id: params.session_id || null
|
|
6376
6564
|
},
|
|
6565
|
+
memory: {
|
|
6566
|
+
included: resolvedProject ? params.include_memories === true && !preflight.repo_grounded : false,
|
|
6567
|
+
personal_count: memoryContribution?.personal_results.length || 0,
|
|
6568
|
+
shared_count: memoryContribution?.shared_results.length || 0,
|
|
6569
|
+
combined_count: memoryContribution?.combined_results.length || 0,
|
|
6570
|
+
shared_user_id: resolvedProject ? sharedProjectMemoryUserId(resolvedProject) : null,
|
|
6571
|
+
ordering: "personal_then_shared_bias"
|
|
6572
|
+
},
|
|
6377
6573
|
retrieval_profile: retrievalProfile,
|
|
6378
6574
|
trust_score: trustScore,
|
|
6379
6575
|
semantic_failure_rate: semanticStats.rate,
|
|
@@ -6383,7 +6579,11 @@ async function runUnifiedContextRetrieval(params) {
|
|
|
6383
6579
|
}
|
|
6384
6580
|
};
|
|
6385
6581
|
if (params.include_alias_warning) {
|
|
6386
|
-
payload.warnings = Array.from(/* @__PURE__ */ new Set([
|
|
6582
|
+
payload.warnings = Array.from(/* @__PURE__ */ new Set([
|
|
6583
|
+
...payload.warnings,
|
|
6584
|
+
"deprecated_alias_use_context_query",
|
|
6585
|
+
"deprecated_alias_use_context.query"
|
|
6586
|
+
]));
|
|
6387
6587
|
}
|
|
6388
6588
|
return payload;
|
|
6389
6589
|
}
|
|
@@ -6671,22 +6871,27 @@ server.tool(
|
|
|
6671
6871
|
project: z.string().optional().describe("Project name or slug"),
|
|
6672
6872
|
content: z.string().describe("The memory content to store"),
|
|
6673
6873
|
memory_type: z.enum(["factual", "preference", "event", "relationship", "opinion", "goal", "instruction"]).optional().default("factual"),
|
|
6874
|
+
scope: z.enum(["personal", "shared_project"]).optional().default("personal"),
|
|
6674
6875
|
user_id: z.string().optional().describe("User this memory belongs to"),
|
|
6675
6876
|
session_id: z.string().optional().describe("Session scope"),
|
|
6676
6877
|
agent_id: z.string().optional().describe("Agent scope"),
|
|
6677
6878
|
importance: z.number().optional().default(0.5).describe("Importance 0-1")
|
|
6678
6879
|
},
|
|
6679
|
-
async ({ project, content, memory_type, user_id, session_id, agent_id, importance }) => {
|
|
6880
|
+
async ({ project, content, memory_type, scope, user_id, session_id, agent_id, importance }) => {
|
|
6680
6881
|
try {
|
|
6681
|
-
const
|
|
6882
|
+
const memoryScope = resolveMemoryScope({ project, user_id, session_id, scope });
|
|
6682
6883
|
const result = await whisper.addMemory({
|
|
6683
|
-
project:
|
|
6884
|
+
project: memoryScope.project,
|
|
6684
6885
|
content,
|
|
6685
6886
|
memory_type,
|
|
6686
|
-
user_id:
|
|
6687
|
-
session_id:
|
|
6887
|
+
user_id: memoryScope.userId,
|
|
6888
|
+
session_id: memoryScope.sessionId,
|
|
6688
6889
|
agent_id,
|
|
6689
|
-
importance
|
|
6890
|
+
importance,
|
|
6891
|
+
metadata: buildMemoryScopeMetadata({
|
|
6892
|
+
memoryScope,
|
|
6893
|
+
agent_id
|
|
6894
|
+
})
|
|
6690
6895
|
});
|
|
6691
6896
|
const memoryId = result?.memory_id || result.id;
|
|
6692
6897
|
const jobId = result?.job_id;
|
|
@@ -6699,12 +6904,16 @@ server.tool(
|
|
|
6699
6904
|
mode: mode || null,
|
|
6700
6905
|
semantic_status: semanticStatus || null,
|
|
6701
6906
|
memory_type: memory_type || "factual",
|
|
6907
|
+
scope: memoryScope.scopeMode,
|
|
6702
6908
|
queued: mode === "async" || Boolean(jobId),
|
|
6703
6909
|
diagnostics: {
|
|
6704
6910
|
scope: {
|
|
6705
|
-
project:
|
|
6706
|
-
user_id:
|
|
6707
|
-
session_id:
|
|
6911
|
+
project: memoryScope.project || null,
|
|
6912
|
+
user_id: memoryScope.userId,
|
|
6913
|
+
session_id: memoryScope.sessionId || null,
|
|
6914
|
+
scope: memoryScope.scopeMode,
|
|
6915
|
+
actor_user_id: memoryScope.actorUserId,
|
|
6916
|
+
actor_session_id: memoryScope.actorSessionId || null
|
|
6708
6917
|
}
|
|
6709
6918
|
}
|
|
6710
6919
|
});
|
|
@@ -6719,28 +6928,29 @@ server.tool(
|
|
|
6719
6928
|
{
|
|
6720
6929
|
project: z.string().optional().describe("Project name or slug"),
|
|
6721
6930
|
query: z.string().describe("What to search for"),
|
|
6931
|
+
scope: z.enum(["personal", "shared_project"]).optional().default("personal"),
|
|
6722
6932
|
user_id: z.string().optional().describe("Filter by user"),
|
|
6723
6933
|
session_id: z.string().optional().describe("Filter by session"),
|
|
6724
6934
|
top_k: z.number().optional().default(10).describe("Number of results"),
|
|
6725
6935
|
memory_types: z.array(z.enum(["factual", "preference", "event", "relationship", "opinion", "goal", "instruction"])).optional()
|
|
6726
6936
|
},
|
|
6727
|
-
async ({ project, query, user_id, session_id, top_k, memory_types }) => {
|
|
6937
|
+
async ({ project, query, scope, user_id, session_id, top_k, memory_types }) => {
|
|
6728
6938
|
try {
|
|
6729
|
-
const
|
|
6939
|
+
const memoryScope = resolveMemoryScope({ project, user_id, session_id, scope });
|
|
6730
6940
|
const results = runtimeClient && (!memory_types || memory_types.length <= 1) ? await runtimeClient.memory.search({
|
|
6731
|
-
project:
|
|
6941
|
+
project: memoryScope.project,
|
|
6732
6942
|
query,
|
|
6733
|
-
user_id:
|
|
6734
|
-
session_id:
|
|
6943
|
+
user_id: memoryScope.userId,
|
|
6944
|
+
session_id: memoryScope.sessionId,
|
|
6735
6945
|
top_k,
|
|
6736
6946
|
include_pending: true,
|
|
6737
6947
|
profile: "balanced",
|
|
6738
6948
|
...memory_types?.length === 1 ? { memory_type: memory_types[0] } : {}
|
|
6739
6949
|
}) : await whisper.searchMemoriesSOTA({
|
|
6740
|
-
project:
|
|
6950
|
+
project: memoryScope.project,
|
|
6741
6951
|
query,
|
|
6742
|
-
user_id:
|
|
6743
|
-
session_id:
|
|
6952
|
+
user_id: memoryScope.userId,
|
|
6953
|
+
session_id: memoryScope.sessionId,
|
|
6744
6954
|
top_k,
|
|
6745
6955
|
memory_types
|
|
6746
6956
|
});
|
|
@@ -6748,15 +6958,17 @@ server.tool(
|
|
|
6748
6958
|
return primaryToolSuccess({
|
|
6749
6959
|
tool: "memory.search",
|
|
6750
6960
|
query,
|
|
6751
|
-
|
|
6752
|
-
|
|
6961
|
+
scope: memoryScope.scopeMode,
|
|
6962
|
+
user_id: memoryScope.userId,
|
|
6963
|
+
session_id: memoryScope.sessionId,
|
|
6753
6964
|
results: normalizedResults,
|
|
6754
6965
|
count: normalizedResults.length,
|
|
6755
6966
|
diagnostics: {
|
|
6756
6967
|
scope: {
|
|
6757
|
-
project:
|
|
6758
|
-
user_id:
|
|
6759
|
-
session_id:
|
|
6968
|
+
project: memoryScope.project || null,
|
|
6969
|
+
user_id: memoryScope.userId,
|
|
6970
|
+
session_id: memoryScope.sessionId || null,
|
|
6971
|
+
scope: memoryScope.scopeMode
|
|
6760
6972
|
}
|
|
6761
6973
|
}
|
|
6762
6974
|
});
|
|
@@ -7013,6 +7225,7 @@ server.tool(
|
|
|
7013
7225
|
{
|
|
7014
7226
|
project: z.string().optional().describe("Project name or slug"),
|
|
7015
7227
|
query: z.string().describe("Search query (supports temporal: 'yesterday', 'last week')"),
|
|
7228
|
+
scope: z.enum(["personal", "shared_project"]).optional().default("personal"),
|
|
7016
7229
|
user_id: z.string().optional().describe("Filter by user"),
|
|
7017
7230
|
session_id: z.string().optional().describe("Filter by session"),
|
|
7018
7231
|
question_date: z.string().optional().describe("ISO datetime for temporal grounding"),
|
|
@@ -7020,14 +7233,14 @@ server.tool(
|
|
|
7020
7233
|
top_k: z.number().optional().default(10),
|
|
7021
7234
|
include_relations: z.boolean().optional().default(true).describe("Include related memories via knowledge graph")
|
|
7022
7235
|
},
|
|
7023
|
-
async ({ project, query, user_id, session_id, question_date, memory_types, top_k, include_relations }) => {
|
|
7236
|
+
async ({ project, query, scope, user_id, session_id, question_date, memory_types, top_k, include_relations }) => {
|
|
7024
7237
|
try {
|
|
7025
|
-
const
|
|
7238
|
+
const memoryScope = resolveMemoryScope({ project, user_id, session_id, scope });
|
|
7026
7239
|
const results = await whisper.searchMemoriesSOTA({
|
|
7027
|
-
project:
|
|
7240
|
+
project: memoryScope.project,
|
|
7028
7241
|
query,
|
|
7029
|
-
user_id:
|
|
7030
|
-
session_id:
|
|
7242
|
+
user_id: memoryScope.userId,
|
|
7243
|
+
session_id: memoryScope.sessionId,
|
|
7031
7244
|
question_date,
|
|
7032
7245
|
memory_types,
|
|
7033
7246
|
top_k,
|
|
@@ -7037,17 +7250,19 @@ server.tool(
|
|
|
7037
7250
|
return primaryToolSuccess({
|
|
7038
7251
|
tool: "memory.search_sota",
|
|
7039
7252
|
query,
|
|
7040
|
-
|
|
7041
|
-
|
|
7253
|
+
scope: memoryScope.scopeMode,
|
|
7254
|
+
user_id: memoryScope.userId,
|
|
7255
|
+
session_id: memoryScope.sessionId || null,
|
|
7042
7256
|
question_date: question_date || null,
|
|
7043
7257
|
include_relations,
|
|
7044
7258
|
results: normalizedResults,
|
|
7045
7259
|
count: normalizedResults.length,
|
|
7046
7260
|
diagnostics: {
|
|
7047
7261
|
scope: {
|
|
7048
|
-
project:
|
|
7049
|
-
user_id:
|
|
7050
|
-
session_id:
|
|
7262
|
+
project: memoryScope.project || null,
|
|
7263
|
+
user_id: memoryScope.userId,
|
|
7264
|
+
session_id: memoryScope.sessionId || null,
|
|
7265
|
+
scope: memoryScope.scopeMode
|
|
7051
7266
|
}
|
|
7052
7267
|
}
|
|
7053
7268
|
});
|
|
@@ -7071,29 +7286,40 @@ server.tool(
|
|
|
7071
7286
|
},
|
|
7072
7287
|
async ({ project, session_id, user_id, messages }) => {
|
|
7073
7288
|
try {
|
|
7074
|
-
const
|
|
7289
|
+
const memoryScope = resolveMemoryScope({
|
|
7290
|
+
project,
|
|
7291
|
+
user_id,
|
|
7292
|
+
session_id,
|
|
7293
|
+
scope: "personal"
|
|
7294
|
+
});
|
|
7075
7295
|
const normalizedMessages = messages.map((message) => ({
|
|
7076
7296
|
role: message.role,
|
|
7077
7297
|
content: message.content,
|
|
7078
7298
|
timestamp: message.timestamp
|
|
7079
7299
|
}));
|
|
7080
7300
|
const result = await ingestSessionWithSyncFallback({
|
|
7081
|
-
project:
|
|
7082
|
-
session_id:
|
|
7083
|
-
user_id:
|
|
7301
|
+
project: memoryScope.project,
|
|
7302
|
+
session_id: memoryScope.sessionId,
|
|
7303
|
+
user_id: memoryScope.userId,
|
|
7084
7304
|
messages: normalizedMessages
|
|
7085
7305
|
});
|
|
7086
|
-
return {
|
|
7087
|
-
|
|
7088
|
-
|
|
7089
|
-
|
|
7090
|
-
|
|
7091
|
-
|
|
7092
|
-
|
|
7093
|
-
|
|
7094
|
-
|
|
7095
|
-
|
|
7096
|
-
|
|
7306
|
+
return primaryToolSuccess({
|
|
7307
|
+
tool: "memory.ingest_conversation",
|
|
7308
|
+
messages_processed: normalizedMessages.length,
|
|
7309
|
+
memories_created: result.memories_created,
|
|
7310
|
+
relations_created: result.relations_created,
|
|
7311
|
+
memories_invalidated: result.memories_invalidated,
|
|
7312
|
+
errors: result.errors || [],
|
|
7313
|
+
scope: memoryScope.scopeMode,
|
|
7314
|
+
diagnostics: {
|
|
7315
|
+
scope: {
|
|
7316
|
+
project: memoryScope.project || null,
|
|
7317
|
+
user_id: memoryScope.userId,
|
|
7318
|
+
session_id: memoryScope.sessionId || null,
|
|
7319
|
+
scope: memoryScope.scopeMode
|
|
7320
|
+
}
|
|
7321
|
+
}
|
|
7322
|
+
});
|
|
7097
7323
|
} catch (error) {
|
|
7098
7324
|
return { content: [{ type: "text", text: `Error: ${error.message}` }] };
|
|
7099
7325
|
}
|
|
@@ -7310,6 +7536,9 @@ server.tool(
|
|
|
7310
7536
|
{
|
|
7311
7537
|
workspace_id: z.string().optional(),
|
|
7312
7538
|
project: z.string().optional(),
|
|
7539
|
+
scope: z.enum(["personal", "shared_project"]).optional().default("personal"),
|
|
7540
|
+
user_id: z.string().optional(),
|
|
7541
|
+
session_id: z.string().optional(),
|
|
7313
7542
|
target: z.object({
|
|
7314
7543
|
memory_id: z.string().optional(),
|
|
7315
7544
|
query: z.string().optional()
|
|
@@ -7317,7 +7546,7 @@ server.tool(
|
|
|
7317
7546
|
mode: z.enum(["delete", "invalidate"]).optional().default("invalidate"),
|
|
7318
7547
|
reason: z.string().optional()
|
|
7319
7548
|
},
|
|
7320
|
-
async ({ workspace_id, project, target, mode, reason }) => {
|
|
7549
|
+
async ({ workspace_id, project, scope, user_id, session_id, target, mode, reason }) => {
|
|
7321
7550
|
try {
|
|
7322
7551
|
if (!target.memory_id && !target.query) {
|
|
7323
7552
|
return { content: [{ type: "text", text: "Error: target.memory_id or target.query is required." }] };
|
|
@@ -7326,8 +7555,22 @@ server.tool(
|
|
|
7326
7555
|
let queryResolution = null;
|
|
7327
7556
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7328
7557
|
const actor = process.env.WHISPER_AGENT_ID || process.env.USERNAME || "api_key_principal";
|
|
7329
|
-
const
|
|
7558
|
+
const memoryScope = resolveMemoryScope({
|
|
7559
|
+
project,
|
|
7560
|
+
user_id,
|
|
7561
|
+
session_id,
|
|
7562
|
+
scope
|
|
7563
|
+
});
|
|
7564
|
+
const resolvedProject = await resolveProjectRef(memoryScope.project);
|
|
7565
|
+
async function assertScopedMemoryId(memoryId) {
|
|
7566
|
+
const memoryResult = await whisper.getMemory(memoryId);
|
|
7567
|
+
const memory = memoryResult?.memory || memoryResult;
|
|
7568
|
+
if (!isMemoryInScope({ memory, memoryScope })) {
|
|
7569
|
+
throw new Error(`memory_id ${memoryId} is outside the effective ${memoryScope.scopeMode} scope.`);
|
|
7570
|
+
}
|
|
7571
|
+
}
|
|
7330
7572
|
async function applyToMemory(memoryId) {
|
|
7573
|
+
await assertScopedMemoryId(memoryId);
|
|
7331
7574
|
if (mode === "delete") {
|
|
7332
7575
|
await whisper.deleteMemory(memoryId);
|
|
7333
7576
|
} else {
|
|
@@ -7342,7 +7585,10 @@ server.tool(
|
|
|
7342
7585
|
project: resolvedProject,
|
|
7343
7586
|
content: `Invalidated memory ${memoryId} at ${now}. Reason: ${reason || "No reason provided"}`,
|
|
7344
7587
|
memory_type: "instruction",
|
|
7345
|
-
|
|
7588
|
+
user_id: memoryScope.userId,
|
|
7589
|
+
session_id: memoryScope.sessionId,
|
|
7590
|
+
importance: 1,
|
|
7591
|
+
metadata: buildMemoryScopeMetadata({ memoryScope })
|
|
7346
7592
|
});
|
|
7347
7593
|
}
|
|
7348
7594
|
}
|
|
@@ -7374,13 +7620,25 @@ server.tool(
|
|
|
7374
7620
|
mode: audit2.mode,
|
|
7375
7621
|
...audit2.reason ? { reason: audit2.reason } : {}
|
|
7376
7622
|
},
|
|
7377
|
-
warning: "No project resolved for query-based forget. Nothing was changed."
|
|
7623
|
+
warning: "No project resolved for query-based forget. Nothing was changed.",
|
|
7624
|
+
diagnostics: {
|
|
7625
|
+
scope: {
|
|
7626
|
+
project: memoryScope.project || null,
|
|
7627
|
+
user_id: memoryScope.userId,
|
|
7628
|
+
session_id: memoryScope.sessionId || null,
|
|
7629
|
+
scope: memoryScope.scopeMode,
|
|
7630
|
+
actor_user_id: memoryScope.actorUserId,
|
|
7631
|
+
actor_session_id: memoryScope.actorSessionId || null
|
|
7632
|
+
}
|
|
7633
|
+
}
|
|
7378
7634
|
};
|
|
7379
7635
|
return { content: [{ type: "text", text: JSON.stringify(payload2, null, 2) }] };
|
|
7380
7636
|
}
|
|
7381
7637
|
const search = await whisper.searchMemoriesSOTA({
|
|
7382
7638
|
project: resolvedProject,
|
|
7383
7639
|
query: target.query || "",
|
|
7640
|
+
user_id: memoryScope.userId,
|
|
7641
|
+
session_id: memoryScope.sessionId,
|
|
7384
7642
|
top_k: 25,
|
|
7385
7643
|
include_relations: false,
|
|
7386
7644
|
include_pending: true
|
|
@@ -7393,7 +7651,17 @@ server.tool(
|
|
|
7393
7651
|
status: "completed",
|
|
7394
7652
|
affected_ids: affectedIds,
|
|
7395
7653
|
warning: resolved.warning || "Query did not resolve to a reliable memory match. No memories were changed.",
|
|
7396
|
-
resolved_by: resolved.resolved_by
|
|
7654
|
+
resolved_by: resolved.resolved_by,
|
|
7655
|
+
diagnostics: {
|
|
7656
|
+
scope: {
|
|
7657
|
+
project: resolvedProject,
|
|
7658
|
+
user_id: memoryScope.userId,
|
|
7659
|
+
session_id: memoryScope.sessionId || null,
|
|
7660
|
+
scope: memoryScope.scopeMode,
|
|
7661
|
+
actor_user_id: memoryScope.actorUserId,
|
|
7662
|
+
actor_session_id: memoryScope.actorSessionId || null
|
|
7663
|
+
}
|
|
7664
|
+
}
|
|
7397
7665
|
};
|
|
7398
7666
|
return { content: [{ type: "text", text: JSON.stringify(payload2, null, 2) }] };
|
|
7399
7667
|
}
|
|
@@ -7422,6 +7690,16 @@ server.tool(
|
|
|
7422
7690
|
called_at: audit.called_at,
|
|
7423
7691
|
mode: audit.mode,
|
|
7424
7692
|
...audit.reason ? { reason: audit.reason } : {}
|
|
7693
|
+
},
|
|
7694
|
+
diagnostics: {
|
|
7695
|
+
scope: {
|
|
7696
|
+
project: resolvedProject || null,
|
|
7697
|
+
user_id: memoryScope.userId,
|
|
7698
|
+
session_id: memoryScope.sessionId || null,
|
|
7699
|
+
scope: memoryScope.scopeMode,
|
|
7700
|
+
actor_user_id: memoryScope.actorUserId,
|
|
7701
|
+
actor_session_id: memoryScope.actorSessionId || null
|
|
7702
|
+
}
|
|
7425
7703
|
}
|
|
7426
7704
|
};
|
|
7427
7705
|
return { content: [{ type: "text", text: JSON.stringify(payload, null, 2) }] };
|
|
@@ -8345,22 +8623,27 @@ server.tool(
|
|
|
8345
8623
|
project: z.string().optional(),
|
|
8346
8624
|
content: z.string().describe("Memory content"),
|
|
8347
8625
|
memory_type: z.enum(["factual", "preference", "event", "relationship", "opinion", "goal", "instruction"]).optional().default("factual"),
|
|
8626
|
+
scope: z.enum(["personal", "shared_project"]).optional().default("personal"),
|
|
8348
8627
|
user_id: z.string().optional(),
|
|
8349
8628
|
session_id: z.string().optional(),
|
|
8350
8629
|
agent_id: z.string().optional(),
|
|
8351
8630
|
importance: z.number().optional().default(0.5)
|
|
8352
8631
|
},
|
|
8353
|
-
async ({ project, content, memory_type, user_id, session_id, agent_id, importance }) => {
|
|
8632
|
+
async ({ project, content, memory_type, scope, user_id, session_id, agent_id, importance }) => {
|
|
8354
8633
|
try {
|
|
8355
|
-
const
|
|
8634
|
+
const memoryScope = resolveMemoryScope({ project, user_id, session_id, scope });
|
|
8356
8635
|
const result = await whisper.addMemory({
|
|
8357
|
-
project:
|
|
8636
|
+
project: memoryScope.project,
|
|
8358
8637
|
content,
|
|
8359
8638
|
memory_type,
|
|
8360
|
-
user_id:
|
|
8361
|
-
session_id:
|
|
8639
|
+
user_id: memoryScope.userId,
|
|
8640
|
+
session_id: memoryScope.sessionId,
|
|
8362
8641
|
agent_id,
|
|
8363
|
-
importance
|
|
8642
|
+
importance,
|
|
8643
|
+
metadata: buildMemoryScopeMetadata({
|
|
8644
|
+
memoryScope,
|
|
8645
|
+
agent_id
|
|
8646
|
+
})
|
|
8364
8647
|
});
|
|
8365
8648
|
const memoryId = result?.memory_id || (result.mode === "sync" ? result.id : null);
|
|
8366
8649
|
const jobId = result?.job_id || (result.mode === "async" ? result.id : null);
|
|
@@ -8371,13 +8654,17 @@ server.tool(
|
|
|
8371
8654
|
job_id: jobId,
|
|
8372
8655
|
mode: result?.mode || null,
|
|
8373
8656
|
memory_type,
|
|
8657
|
+
scope: memoryScope.scopeMode,
|
|
8374
8658
|
stored: result.success === true,
|
|
8375
8659
|
queued: result?.mode === "async" || Boolean(jobId),
|
|
8376
8660
|
diagnostics: {
|
|
8377
8661
|
scope: {
|
|
8378
|
-
project:
|
|
8379
|
-
user_id:
|
|
8380
|
-
session_id:
|
|
8662
|
+
project: memoryScope.project || null,
|
|
8663
|
+
user_id: memoryScope.userId,
|
|
8664
|
+
session_id: memoryScope.sessionId || null,
|
|
8665
|
+
scope: memoryScope.scopeMode,
|
|
8666
|
+
actor_user_id: memoryScope.actorUserId,
|
|
8667
|
+
actor_session_id: memoryScope.actorSessionId || null
|
|
8381
8668
|
}
|
|
8382
8669
|
}
|
|
8383
8670
|
});
|
|
@@ -8415,29 +8702,31 @@ server.tool(
|
|
|
8415
8702
|
timestamp
|
|
8416
8703
|
});
|
|
8417
8704
|
const resolvedProject = await resolveProjectRef(project);
|
|
8418
|
-
const
|
|
8705
|
+
const memoryScope = resolveMemoryScope({
|
|
8419
8706
|
project: resolvedProject || project,
|
|
8420
8707
|
user_id,
|
|
8421
|
-
session_id
|
|
8708
|
+
session_id,
|
|
8709
|
+
scope: "personal"
|
|
8422
8710
|
});
|
|
8423
8711
|
const result = await ingestSessionWithSyncFallback({
|
|
8424
|
-
project:
|
|
8425
|
-
session_id:
|
|
8426
|
-
user_id:
|
|
8712
|
+
project: memoryScope.project,
|
|
8713
|
+
session_id: memoryScope.sessionId || session_id,
|
|
8714
|
+
user_id: memoryScope.userId,
|
|
8427
8715
|
messages: normalizedMessages
|
|
8428
8716
|
});
|
|
8429
8717
|
return primaryToolSuccess({
|
|
8430
8718
|
tool: "record",
|
|
8431
|
-
session_id:
|
|
8432
|
-
user_id:
|
|
8719
|
+
session_id: memoryScope.sessionId || session_id,
|
|
8720
|
+
user_id: memoryScope.userId,
|
|
8433
8721
|
messages_recorded: normalizedMessages.length,
|
|
8434
8722
|
memories_created: result.memories_created,
|
|
8435
8723
|
relations_created: result.relations_created,
|
|
8436
8724
|
diagnostics: {
|
|
8437
8725
|
scope: {
|
|
8438
|
-
project:
|
|
8439
|
-
user_id:
|
|
8440
|
-
session_id:
|
|
8726
|
+
project: memoryScope.project || null,
|
|
8727
|
+
user_id: memoryScope.userId,
|
|
8728
|
+
session_id: memoryScope.sessionId || session_id,
|
|
8729
|
+
scope: memoryScope.scopeMode
|
|
8441
8730
|
}
|
|
8442
8731
|
}
|
|
8443
8732
|
});
|