@usewhisper/mcp-server 2.14.0 → 2.16.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 +1153 -515
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
// ../src/mcp/server.ts
|
|
4
4
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
5
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
-
import { z } from "zod";
|
|
6
|
+
import { z as z2 } 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
|
|
@@ -127,6 +127,8 @@ var RuntimeClientError = class extends Error {
|
|
|
127
127
|
code;
|
|
128
128
|
details;
|
|
129
129
|
traceId;
|
|
130
|
+
hint;
|
|
131
|
+
requestId;
|
|
130
132
|
constructor(args) {
|
|
131
133
|
super(args.message);
|
|
132
134
|
this.name = "RuntimeClientError";
|
|
@@ -135,6 +137,8 @@ var RuntimeClientError = class extends Error {
|
|
|
135
137
|
this.code = args.code;
|
|
136
138
|
this.details = args.details;
|
|
137
139
|
this.traceId = args.traceId;
|
|
140
|
+
this.hint = args.hint;
|
|
141
|
+
this.requestId = args.requestId;
|
|
138
142
|
}
|
|
139
143
|
};
|
|
140
144
|
var RuntimeClient = class {
|
|
@@ -339,14 +343,22 @@ var RuntimeClient = class {
|
|
|
339
343
|
};
|
|
340
344
|
}
|
|
341
345
|
const message = toMessage(payload, response.status, response.statusText);
|
|
342
|
-
const
|
|
346
|
+
const payloadObject = isObject(payload) ? payload : {};
|
|
347
|
+
const payloadCode = typeof payloadObject.code === "string" ? payloadObject.code : void 0;
|
|
348
|
+
const payloadHint = typeof payloadObject.hint === "string" ? payloadObject.hint : void 0;
|
|
349
|
+
const payloadRequestId = typeof payloadObject.requestId === "string" ? payloadObject.requestId : typeof payloadObject.request_id === "string" ? payloadObject.request_id : void 0;
|
|
350
|
+
const payloadRetryable = typeof payloadObject.retryable === "boolean" ? payloadObject.retryable : void 0;
|
|
351
|
+
const statusRetryable = this.shouldRetryStatus(response.status);
|
|
352
|
+
const retryable = payloadRetryable ?? statusRetryable;
|
|
343
353
|
const error = new RuntimeClientError({
|
|
344
354
|
message,
|
|
345
355
|
status: response.status,
|
|
346
356
|
retryable,
|
|
347
|
-
code: response.status === 404 ? "NOT_FOUND" : "REQUEST_FAILED",
|
|
357
|
+
code: payloadCode || (response.status === 404 ? "NOT_FOUND" : "REQUEST_FAILED"),
|
|
348
358
|
details: payload,
|
|
349
|
-
traceId
|
|
359
|
+
traceId: payloadRequestId || traceId,
|
|
360
|
+
requestId: payloadRequestId || traceId,
|
|
361
|
+
hint: payloadHint
|
|
350
362
|
});
|
|
351
363
|
lastError = error;
|
|
352
364
|
if (!retryable || attempt === maxAttempts - 1) {
|
|
@@ -360,7 +372,8 @@ var RuntimeClient = class {
|
|
|
360
372
|
message: isAbort ? "Request timed out" : error instanceof Error ? error.message : "Network error",
|
|
361
373
|
retryable: this.retryPolicy.retryOnNetworkError ?? true,
|
|
362
374
|
code: isAbort ? "TIMEOUT" : "NETWORK_ERROR",
|
|
363
|
-
traceId
|
|
375
|
+
traceId,
|
|
376
|
+
requestId: traceId
|
|
364
377
|
});
|
|
365
378
|
lastError = mapped;
|
|
366
379
|
this.diagnostics.add({
|
|
@@ -391,6 +404,26 @@ var RuntimeClient = class {
|
|
|
391
404
|
}
|
|
392
405
|
};
|
|
393
406
|
|
|
407
|
+
// ../src/sdk/errors.ts
|
|
408
|
+
var WhisperError = class extends Error {
|
|
409
|
+
code;
|
|
410
|
+
status;
|
|
411
|
+
retryable;
|
|
412
|
+
hint;
|
|
413
|
+
requestId;
|
|
414
|
+
details;
|
|
415
|
+
constructor(args) {
|
|
416
|
+
super(args.message, args.cause ? { cause: args.cause } : void 0);
|
|
417
|
+
this.name = "WhisperError";
|
|
418
|
+
this.code = args.code;
|
|
419
|
+
this.status = args.status;
|
|
420
|
+
this.retryable = args.retryable ?? false;
|
|
421
|
+
this.hint = args.hint;
|
|
422
|
+
this.requestId = args.requestId;
|
|
423
|
+
this.details = args.details;
|
|
424
|
+
}
|
|
425
|
+
};
|
|
426
|
+
|
|
394
427
|
// ../src/sdk/core/cache.ts
|
|
395
428
|
var SearchResponseCache = class {
|
|
396
429
|
ttlMs;
|
|
@@ -2186,12 +2219,44 @@ ${lines.join("\n")}`;
|
|
|
2186
2219
|
|
|
2187
2220
|
// ../src/sdk/whisper.ts
|
|
2188
2221
|
var PROJECT_CACHE_TTL_MS = 3e4;
|
|
2222
|
+
var IDENTITY_WARNINGS = /* @__PURE__ */ new Set();
|
|
2189
2223
|
function isLikelyProjectId(projectRef) {
|
|
2190
2224
|
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(projectRef);
|
|
2191
2225
|
}
|
|
2226
|
+
function randomRequestId() {
|
|
2227
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
2228
|
+
return crypto.randomUUID();
|
|
2229
|
+
}
|
|
2230
|
+
return `req_${Math.random().toString(36).slice(2, 11)}`;
|
|
2231
|
+
}
|
|
2232
|
+
function parseIdentityMode(value) {
|
|
2233
|
+
return value === "app-identity" ? "app-identity" : "demo-local";
|
|
2234
|
+
}
|
|
2235
|
+
function parseEnvironment(value) {
|
|
2236
|
+
if (value === "staging" || value === "production") return value;
|
|
2237
|
+
return "local";
|
|
2238
|
+
}
|
|
2239
|
+
function classifyRuntimeErrorCode(error) {
|
|
2240
|
+
if (error.code === "MISSING_PROJECT") return "MISSING_PROJECT";
|
|
2241
|
+
if (error.code === "TIMEOUT") return "TIMEOUT";
|
|
2242
|
+
if (error.code === "NETWORK_ERROR") return "NETWORK_ERROR";
|
|
2243
|
+
if (error.code === "VALIDATION_ERROR") return "VALIDATION_ERROR";
|
|
2244
|
+
if (error.status === 401 || error.status === 403) return "INVALID_API_KEY";
|
|
2245
|
+
if (error.status === 408) return "TIMEOUT";
|
|
2246
|
+
if (error.status === 429) return "RATE_LIMITED";
|
|
2247
|
+
if (error.status && error.status >= 500) return "TEMPORARY_UNAVAILABLE";
|
|
2248
|
+
if (error.code === "PROJECT_NOT_FOUND" || error.code === "NOT_FOUND") return "PROJECT_NOT_FOUND";
|
|
2249
|
+
return "REQUEST_FAILED";
|
|
2250
|
+
}
|
|
2192
2251
|
var WhisperClient = class _WhisperClient {
|
|
2193
2252
|
constructor(config) {
|
|
2194
2253
|
this.config = config;
|
|
2254
|
+
const env = typeof process !== "undefined" ? process.env : {};
|
|
2255
|
+
this.identityMode = parseIdentityMode(config.identityMode || env.WHISPER_IDENTITY_MODE);
|
|
2256
|
+
this.environment = parseEnvironment(config.environment || env.WHISPER_ENV || (env.NODE_ENV === "production" ? "production" : "local"));
|
|
2257
|
+
this.strictIdentityMode = config.strictIdentityMode ?? env.WHISPER_DEMO_LOCAL_STRICT === "true";
|
|
2258
|
+
this.getIdentity = config.getIdentity;
|
|
2259
|
+
this.enforceIdentityModeGuardrail();
|
|
2195
2260
|
this.diagnosticsStore = new DiagnosticsStore(config.telemetry?.maxEntries || 1e3);
|
|
2196
2261
|
this.runtimeClient = new RuntimeClient(
|
|
2197
2262
|
{
|
|
@@ -2301,26 +2366,29 @@ var WhisperClient = class _WhisperClient {
|
|
|
2301
2366
|
status: () => this.writeQueue.status()
|
|
2302
2367
|
};
|
|
2303
2368
|
this.memory = {
|
|
2304
|
-
add: (params) => this.memoryModule.add(params),
|
|
2305
|
-
addBulk: (params) => this.memoryModule.addBulk(
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2369
|
+
add: (params) => this.runOrThrow(async () => this.memoryModule.add(await this.withIdentity(params))),
|
|
2370
|
+
addBulk: (params) => this.runOrThrow(async () => this.memoryModule.addBulk({
|
|
2371
|
+
...params,
|
|
2372
|
+
memories: await Promise.all(params.memories.map((memory) => this.withIdentity(memory)))
|
|
2373
|
+
})),
|
|
2374
|
+
search: (params) => this.runOrThrow(async () => this.memoryModule.search(await this.withIdentity(params))),
|
|
2375
|
+
get: (memoryId) => this.runOrThrow(async () => this.memoryModule.get(memoryId)),
|
|
2376
|
+
getUserProfile: (params) => this.runOrThrow(async () => this.profileModule.getUserProfile(await this.withIdentity(params, true))),
|
|
2377
|
+
getSessionMemories: (params) => this.runOrThrow(async () => this.profileModule.getSessionMemories(await this.withIdentity(params))),
|
|
2378
|
+
update: (memoryId, params) => this.runOrThrow(async () => this.memoryModule.update(memoryId, params)),
|
|
2379
|
+
delete: (memoryId) => this.runOrThrow(async () => this.memoryModule.delete(memoryId)),
|
|
2380
|
+
flag: (params) => this.runOrThrow(async () => this.memoryModule.flag(params))
|
|
2313
2381
|
};
|
|
2314
2382
|
this.session = {
|
|
2315
|
-
start: (params) => this.sessionModule.start(params),
|
|
2316
|
-
event: (params) => this.sessionModule.event(params),
|
|
2317
|
-
suspend: (params) => this.sessionModule.suspend(params),
|
|
2318
|
-
resume: (params) => this.sessionModule.resume(params),
|
|
2319
|
-
end: (params) => this.sessionModule.end(params)
|
|
2383
|
+
start: (params) => this.runOrThrow(async () => this.sessionModule.start(await this.withSessionIdentity(params))),
|
|
2384
|
+
event: (params) => this.runOrThrow(async () => this.sessionModule.event(params)),
|
|
2385
|
+
suspend: (params) => this.runOrThrow(async () => this.sessionModule.suspend(params)),
|
|
2386
|
+
resume: (params) => this.runOrThrow(async () => this.sessionModule.resume(params)),
|
|
2387
|
+
end: (params) => this.runOrThrow(async () => this.sessionModule.end(params))
|
|
2320
2388
|
};
|
|
2321
2389
|
this.profile = {
|
|
2322
|
-
getUserProfile: (params) => this.profileModule.getUserProfile(params),
|
|
2323
|
-
getSessionMemories: (params) => this.profileModule.getSessionMemories(params)
|
|
2390
|
+
getUserProfile: (params) => this.runOrThrow(async () => this.profileModule.getUserProfile(await this.withIdentity(params, true))),
|
|
2391
|
+
getSessionMemories: (params) => this.runOrThrow(async () => this.profileModule.getSessionMemories(await this.withIdentity(params)))
|
|
2324
2392
|
};
|
|
2325
2393
|
this.analytics = {
|
|
2326
2394
|
diagnosticsSnapshot: () => this.analyticsModule.diagnosticsSnapshot(),
|
|
@@ -2342,18 +2410,158 @@ var WhisperClient = class _WhisperClient {
|
|
|
2342
2410
|
profileModule;
|
|
2343
2411
|
analyticsModule;
|
|
2344
2412
|
projectRefToId = /* @__PURE__ */ new Map();
|
|
2413
|
+
identityMode;
|
|
2414
|
+
environment;
|
|
2415
|
+
strictIdentityMode;
|
|
2416
|
+
getIdentity;
|
|
2345
2417
|
projectCache = [];
|
|
2346
2418
|
projectCacheExpiresAt = 0;
|
|
2419
|
+
enforceIdentityModeGuardrail(requestId = randomRequestId()) {
|
|
2420
|
+
if (this.identityMode !== "demo-local") return;
|
|
2421
|
+
if (this.environment === "local") return;
|
|
2422
|
+
const message = "[Whisper SDK] WHISPER_IDENTITY_MODE=demo-local is intended only for local development. Switch to app-identity and provide getIdentity() or per-call user_id/session_id.";
|
|
2423
|
+
if (this.strictIdentityMode || this.environment === "production") {
|
|
2424
|
+
throw new WhisperError({
|
|
2425
|
+
code: "MISCONFIGURED_IDENTITY_MODE",
|
|
2426
|
+
message,
|
|
2427
|
+
retryable: false,
|
|
2428
|
+
hint: "Set identityMode: 'app-identity' and provide a getIdentity() function that returns { userId, sessionId? }. To override for testing, set environment: 'local' explicitly."
|
|
2429
|
+
});
|
|
2430
|
+
}
|
|
2431
|
+
const warningKey = `${this.environment}:${this.identityMode}`;
|
|
2432
|
+
if (!IDENTITY_WARNINGS.has(warningKey)) {
|
|
2433
|
+
IDENTITY_WARNINGS.add(warningKey);
|
|
2434
|
+
if (typeof console !== "undefined" && typeof console.warn === "function") {
|
|
2435
|
+
console.warn(`${message} requestId=${requestId}`);
|
|
2436
|
+
}
|
|
2437
|
+
}
|
|
2438
|
+
}
|
|
2439
|
+
toWhisperError(error, hint) {
|
|
2440
|
+
if (error instanceof WhisperError) return error;
|
|
2441
|
+
if (error instanceof RuntimeClientError) {
|
|
2442
|
+
return new WhisperError({
|
|
2443
|
+
code: classifyRuntimeErrorCode(error),
|
|
2444
|
+
message: error.message,
|
|
2445
|
+
status: error.status,
|
|
2446
|
+
retryable: error.retryable,
|
|
2447
|
+
hint: error.hint || hint,
|
|
2448
|
+
requestId: error.requestId || error.traceId,
|
|
2449
|
+
details: error.details,
|
|
2450
|
+
cause: error
|
|
2451
|
+
});
|
|
2452
|
+
}
|
|
2453
|
+
if (error instanceof Error) {
|
|
2454
|
+
return new WhisperError({
|
|
2455
|
+
code: "REQUEST_FAILED",
|
|
2456
|
+
message: error.message,
|
|
2457
|
+
retryable: false,
|
|
2458
|
+
hint,
|
|
2459
|
+
cause: error
|
|
2460
|
+
});
|
|
2461
|
+
}
|
|
2462
|
+
return new WhisperError({
|
|
2463
|
+
code: "REQUEST_FAILED",
|
|
2464
|
+
message: "Unknown SDK error",
|
|
2465
|
+
retryable: false,
|
|
2466
|
+
hint,
|
|
2467
|
+
details: error
|
|
2468
|
+
});
|
|
2469
|
+
}
|
|
2470
|
+
async runOrThrow(work, hint) {
|
|
2471
|
+
try {
|
|
2472
|
+
return await work();
|
|
2473
|
+
} catch (error) {
|
|
2474
|
+
throw this.toWhisperError(error, hint);
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2477
|
+
async resolveIdentityOverride() {
|
|
2478
|
+
if (!this.getIdentity) return null;
|
|
2479
|
+
const resolved = await this.getIdentity();
|
|
2480
|
+
const userId = String(resolved?.userId || "").trim();
|
|
2481
|
+
const sessionId = resolved?.sessionId ? String(resolved.sessionId).trim() : void 0;
|
|
2482
|
+
if (!userId) {
|
|
2483
|
+
throw new WhisperError({
|
|
2484
|
+
code: "AUTH_IDENTITY_INVALID",
|
|
2485
|
+
message: "getIdentity() returned an invalid identity payload.",
|
|
2486
|
+
retryable: false,
|
|
2487
|
+
hint: "Return { userId, sessionId? } from getIdentity()."
|
|
2488
|
+
});
|
|
2489
|
+
}
|
|
2490
|
+
return {
|
|
2491
|
+
userId,
|
|
2492
|
+
sessionId: sessionId || void 0
|
|
2493
|
+
};
|
|
2494
|
+
}
|
|
2495
|
+
async withIdentity(params, requireUser = false) {
|
|
2496
|
+
const currentUser = params.user_id ? String(params.user_id).trim() : "";
|
|
2497
|
+
const currentSession = params.session_id ? String(params.session_id).trim() : "";
|
|
2498
|
+
if (currentUser) {
|
|
2499
|
+
return {
|
|
2500
|
+
...params,
|
|
2501
|
+
user_id: currentUser,
|
|
2502
|
+
session_id: currentSession || params.session_id
|
|
2503
|
+
};
|
|
2504
|
+
}
|
|
2505
|
+
const resolved = await this.resolveIdentityOverride();
|
|
2506
|
+
if (resolved) {
|
|
2507
|
+
return {
|
|
2508
|
+
...params,
|
|
2509
|
+
user_id: resolved.userId,
|
|
2510
|
+
session_id: currentSession || resolved.sessionId
|
|
2511
|
+
};
|
|
2512
|
+
}
|
|
2513
|
+
if (requireUser || this.identityMode === "app-identity") {
|
|
2514
|
+
throw new WhisperError({
|
|
2515
|
+
code: "AUTH_IDENTITY_REQUIRED",
|
|
2516
|
+
message: "A user identity is required in app-identity mode.",
|
|
2517
|
+
retryable: false,
|
|
2518
|
+
hint: "Provide user_id/session_id per call or configure getIdentity() in WhisperClient."
|
|
2519
|
+
});
|
|
2520
|
+
}
|
|
2521
|
+
return params;
|
|
2522
|
+
}
|
|
2523
|
+
async withSessionIdentity(params) {
|
|
2524
|
+
const userId = params.userId ? String(params.userId).trim() : "";
|
|
2525
|
+
const sessionId = params.sessionId ? String(params.sessionId).trim() : "";
|
|
2526
|
+
if (userId) {
|
|
2527
|
+
return {
|
|
2528
|
+
...params,
|
|
2529
|
+
userId,
|
|
2530
|
+
sessionId: sessionId || params.sessionId
|
|
2531
|
+
};
|
|
2532
|
+
}
|
|
2533
|
+
const resolved = await this.resolveIdentityOverride();
|
|
2534
|
+
if (resolved?.userId) {
|
|
2535
|
+
return {
|
|
2536
|
+
...params,
|
|
2537
|
+
userId: resolved.userId,
|
|
2538
|
+
sessionId: sessionId || resolved.sessionId
|
|
2539
|
+
};
|
|
2540
|
+
}
|
|
2541
|
+
throw new WhisperError({
|
|
2542
|
+
code: "AUTH_IDENTITY_REQUIRED",
|
|
2543
|
+
message: "Session operations require a user identity.",
|
|
2544
|
+
retryable: false,
|
|
2545
|
+
hint: "Pass userId explicitly or configure getIdentity() in WhisperClient."
|
|
2546
|
+
});
|
|
2547
|
+
}
|
|
2347
2548
|
static fromEnv(overrides = {}) {
|
|
2348
2549
|
const env = typeof process !== "undefined" ? process.env : {};
|
|
2349
2550
|
const apiKey = overrides.apiKey || env.WHISPER_API_KEY || env.USEWHISPER_API_KEY || env.API_KEY;
|
|
2350
2551
|
if (!apiKey) {
|
|
2351
|
-
throw new
|
|
2552
|
+
throw new WhisperError({
|
|
2553
|
+
code: "INVALID_API_KEY",
|
|
2554
|
+
message: "Missing API key. Set WHISPER_API_KEY / USEWHISPER_API_KEY / API_KEY.",
|
|
2555
|
+
retryable: false
|
|
2556
|
+
});
|
|
2352
2557
|
}
|
|
2353
2558
|
return new _WhisperClient({
|
|
2354
2559
|
apiKey,
|
|
2355
2560
|
baseUrl: overrides.baseUrl || env.WHISPER_BASE_URL || env.API_BASE_URL || "https://context.usewhisper.dev",
|
|
2356
2561
|
project: overrides.project || env.WHISPER_PROJECT || env.PROJECT,
|
|
2562
|
+
identityMode: overrides.identityMode || parseIdentityMode(env.WHISPER_IDENTITY_MODE),
|
|
2563
|
+
environment: overrides.environment || parseEnvironment(env.WHISPER_ENV || (env.NODE_ENV === "production" ? "production" : "local")),
|
|
2564
|
+
strictIdentityMode: overrides.strictIdentityMode ?? env.WHISPER_DEMO_LOCAL_STRICT === "true",
|
|
2357
2565
|
...overrides
|
|
2358
2566
|
});
|
|
2359
2567
|
}
|
|
@@ -2390,10 +2598,11 @@ var WhisperClient = class _WhisperClient {
|
|
|
2390
2598
|
getRequiredProject(project) {
|
|
2391
2599
|
const resolved = project || this.config.project;
|
|
2392
2600
|
if (!resolved) {
|
|
2393
|
-
throw new
|
|
2601
|
+
throw new WhisperError({
|
|
2394
2602
|
code: "MISSING_PROJECT",
|
|
2395
2603
|
message: "Project is required",
|
|
2396
|
-
retryable: false
|
|
2604
|
+
retryable: false,
|
|
2605
|
+
hint: "Pass project in the call or configure a default project in WhisperClient."
|
|
2397
2606
|
});
|
|
2398
2607
|
}
|
|
2399
2608
|
return resolved;
|
|
@@ -2435,81 +2644,182 @@ var WhisperClient = class _WhisperClient {
|
|
|
2435
2644
|
}
|
|
2436
2645
|
}
|
|
2437
2646
|
async resolveProject(projectRef) {
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2647
|
+
return this.runOrThrow(async () => {
|
|
2648
|
+
const resolvedRef = this.getRequiredProject(projectRef);
|
|
2649
|
+
const cachedProjects = await this.refreshProjectCache(false);
|
|
2650
|
+
const cachedProject = cachedProjects.find(
|
|
2651
|
+
(project) => project.id === resolvedRef || project.slug === resolvedRef || project.name === resolvedRef
|
|
2652
|
+
);
|
|
2653
|
+
if (cachedProject) {
|
|
2654
|
+
return cachedProject;
|
|
2655
|
+
}
|
|
2656
|
+
const resolvedProject = await this.fetchResolvedProject(resolvedRef);
|
|
2657
|
+
if (resolvedProject) {
|
|
2658
|
+
this.projectRefToId.set(resolvedProject.id, resolvedProject.id);
|
|
2659
|
+
this.projectRefToId.set(resolvedProject.slug, resolvedProject.id);
|
|
2660
|
+
this.projectRefToId.set(resolvedProject.name, resolvedProject.id);
|
|
2661
|
+
this.projectCache = [
|
|
2662
|
+
...this.projectCache.filter((project) => project.id !== resolvedProject.id),
|
|
2663
|
+
resolvedProject
|
|
2664
|
+
];
|
|
2665
|
+
this.projectCacheExpiresAt = Date.now() + PROJECT_CACHE_TTL_MS;
|
|
2666
|
+
return resolvedProject;
|
|
2667
|
+
}
|
|
2668
|
+
if (isLikelyProjectId(resolvedRef)) {
|
|
2669
|
+
return {
|
|
2670
|
+
id: resolvedRef,
|
|
2671
|
+
orgId: "",
|
|
2672
|
+
name: resolvedRef,
|
|
2673
|
+
slug: resolvedRef,
|
|
2674
|
+
createdAt: (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
2675
|
+
updatedAt: (/* @__PURE__ */ new Date(0)).toISOString()
|
|
2676
|
+
};
|
|
2677
|
+
}
|
|
2678
|
+
throw new WhisperError({
|
|
2679
|
+
code: "PROJECT_NOT_FOUND",
|
|
2680
|
+
message: `Project '${resolvedRef}' not found`,
|
|
2681
|
+
retryable: false
|
|
2682
|
+
});
|
|
2683
|
+
});
|
|
2684
|
+
}
|
|
2685
|
+
async preflight(options) {
|
|
2686
|
+
const requestId = randomRequestId();
|
|
2687
|
+
const checks = [];
|
|
2688
|
+
try {
|
|
2689
|
+
this.enforceIdentityModeGuardrail(requestId);
|
|
2690
|
+
} catch (error) {
|
|
2691
|
+
throw this.toWhisperError(error, "Update identity mode before running preflight.");
|
|
2692
|
+
}
|
|
2693
|
+
const apiKeyOk = typeof this.config.apiKey === "string" && this.config.apiKey.trim().length > 0;
|
|
2694
|
+
checks.push({
|
|
2695
|
+
check: "api_key",
|
|
2696
|
+
ok: apiKeyOk,
|
|
2697
|
+
message: apiKeyOk ? "API key is configured." : "Missing API key.",
|
|
2698
|
+
hint: apiKeyOk ? void 0 : "Set WHISPER_API_KEY or pass apiKey to WhisperClient."
|
|
2699
|
+
});
|
|
2700
|
+
try {
|
|
2701
|
+
await this.runtimeClient.request({
|
|
2702
|
+
endpoint: "/v1/projects",
|
|
2703
|
+
method: "GET",
|
|
2704
|
+
operation: "get",
|
|
2705
|
+
idempotent: true,
|
|
2706
|
+
traceId: requestId
|
|
2707
|
+
});
|
|
2708
|
+
checks.push({
|
|
2709
|
+
check: "api_connectivity",
|
|
2710
|
+
ok: true,
|
|
2711
|
+
message: "Connected to Whisper API."
|
|
2712
|
+
});
|
|
2713
|
+
} catch (error) {
|
|
2714
|
+
const mapped = this.toWhisperError(error, "Confirm WHISPER_BASE_URL and API key permissions.");
|
|
2715
|
+
checks.push({
|
|
2716
|
+
check: "api_connectivity",
|
|
2717
|
+
ok: false,
|
|
2718
|
+
message: mapped.message,
|
|
2719
|
+
hint: mapped.hint
|
|
2720
|
+
});
|
|
2445
2721
|
}
|
|
2446
|
-
const
|
|
2447
|
-
if (
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2722
|
+
const projectRef = options?.project || this.config.project;
|
|
2723
|
+
if (projectRef) {
|
|
2724
|
+
try {
|
|
2725
|
+
await this.resolveProject(projectRef);
|
|
2726
|
+
checks.push({
|
|
2727
|
+
check: "project_access",
|
|
2728
|
+
ok: true,
|
|
2729
|
+
message: `Project '${projectRef}' is reachable.`
|
|
2730
|
+
});
|
|
2731
|
+
} catch (error) {
|
|
2732
|
+
const mapped = this.toWhisperError(error, "Create or grant access to the configured project.");
|
|
2733
|
+
checks.push({
|
|
2734
|
+
check: "project_access",
|
|
2735
|
+
ok: false,
|
|
2736
|
+
message: mapped.message,
|
|
2737
|
+
hint: mapped.hint
|
|
2738
|
+
});
|
|
2739
|
+
}
|
|
2740
|
+
} else {
|
|
2741
|
+
checks.push({
|
|
2742
|
+
check: "project_access",
|
|
2743
|
+
ok: true,
|
|
2744
|
+
message: "No default project configured (project will be required per call)."
|
|
2745
|
+
});
|
|
2457
2746
|
}
|
|
2458
|
-
if (
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2747
|
+
if (options?.requireIdentity || this.identityMode === "app-identity") {
|
|
2748
|
+
try {
|
|
2749
|
+
const identity = await this.resolveIdentityOverride();
|
|
2750
|
+
const ok = Boolean(identity?.userId);
|
|
2751
|
+
checks.push({
|
|
2752
|
+
check: "identity_resolution",
|
|
2753
|
+
ok,
|
|
2754
|
+
message: ok ? "Identity resolver is configured." : "Identity resolver is missing.",
|
|
2755
|
+
hint: ok ? void 0 : "Provide getIdentity() or pass user_id/session_id per call."
|
|
2756
|
+
});
|
|
2757
|
+
} catch (error) {
|
|
2758
|
+
const mapped = this.toWhisperError(error, "Fix identity resolver output before production usage.");
|
|
2759
|
+
checks.push({
|
|
2760
|
+
check: "identity_resolution",
|
|
2761
|
+
ok: false,
|
|
2762
|
+
message: mapped.message,
|
|
2763
|
+
hint: mapped.hint
|
|
2764
|
+
});
|
|
2765
|
+
}
|
|
2467
2766
|
}
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2767
|
+
return {
|
|
2768
|
+
ok: checks.every((check) => check.ok),
|
|
2769
|
+
checks,
|
|
2770
|
+
requestId,
|
|
2771
|
+
identityMode: this.identityMode,
|
|
2772
|
+
environment: this.environment
|
|
2773
|
+
};
|
|
2473
2774
|
}
|
|
2474
2775
|
async query(params) {
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2776
|
+
return this.runOrThrow(async () => {
|
|
2777
|
+
const identityParams = await this.withIdentity(params);
|
|
2778
|
+
const project = (await this.resolveProject(identityParams.project)).id;
|
|
2779
|
+
const response = await this.runtimeClient.request({
|
|
2780
|
+
endpoint: "/v1/context/query",
|
|
2781
|
+
method: "POST",
|
|
2782
|
+
operation: "search",
|
|
2783
|
+
body: {
|
|
2784
|
+
...identityParams,
|
|
2785
|
+
project
|
|
2786
|
+
},
|
|
2787
|
+
idempotent: true
|
|
2788
|
+
});
|
|
2789
|
+
return response.data;
|
|
2485
2790
|
});
|
|
2486
|
-
return response.data;
|
|
2487
2791
|
}
|
|
2488
2792
|
async ingestSession(params) {
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2793
|
+
return this.runOrThrow(async () => {
|
|
2794
|
+
const identityParams = await this.withIdentity(params);
|
|
2795
|
+
const project = (await this.resolveProject(identityParams.project)).id;
|
|
2796
|
+
const response = await this.runtimeClient.request({
|
|
2797
|
+
endpoint: "/v1/memory/ingest/session",
|
|
2798
|
+
method: "POST",
|
|
2799
|
+
operation: "session",
|
|
2800
|
+
body: {
|
|
2801
|
+
...identityParams,
|
|
2802
|
+
project
|
|
2803
|
+
}
|
|
2804
|
+
});
|
|
2805
|
+
return response.data;
|
|
2498
2806
|
});
|
|
2499
|
-
return response.data;
|
|
2500
2807
|
}
|
|
2501
2808
|
async learn(params) {
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2809
|
+
return this.runOrThrow(async () => {
|
|
2810
|
+
const identityParams = params.mode === "conversation" ? await this.withIdentity(params) : params;
|
|
2811
|
+
const project = (await this.resolveProject(identityParams.project)).id;
|
|
2812
|
+
const response = await this.runtimeClient.request({
|
|
2813
|
+
endpoint: "/v1/learn",
|
|
2814
|
+
method: "POST",
|
|
2815
|
+
operation: params.mode === "conversation" ? "session" : "bulk",
|
|
2816
|
+
body: {
|
|
2817
|
+
...identityParams,
|
|
2818
|
+
project
|
|
2819
|
+
}
|
|
2820
|
+
});
|
|
2821
|
+
return response.data;
|
|
2511
2822
|
});
|
|
2512
|
-
return response.data;
|
|
2513
2823
|
}
|
|
2514
2824
|
createAgentRuntime(options = {}) {
|
|
2515
2825
|
const baseContext = {
|
|
@@ -2571,26 +2881,60 @@ var WhisperClient = class _WhisperClient {
|
|
|
2571
2881
|
diagnostics: base.diagnostics
|
|
2572
2882
|
};
|
|
2573
2883
|
}
|
|
2884
|
+
async deleteSource(sourceId) {
|
|
2885
|
+
return this.runOrThrow(async () => {
|
|
2886
|
+
const response = await this.runtimeClient.request({
|
|
2887
|
+
endpoint: `/v1/sources/${sourceId}`,
|
|
2888
|
+
method: "DELETE",
|
|
2889
|
+
operation: "writeAck"
|
|
2890
|
+
});
|
|
2891
|
+
return response.data;
|
|
2892
|
+
});
|
|
2893
|
+
}
|
|
2894
|
+
async extractMemories(params) {
|
|
2895
|
+
return this.runOrThrow(async () => {
|
|
2896
|
+
const project = (await this.resolveProject(params.project)).id;
|
|
2897
|
+
const response = await this.runtimeClient.request({
|
|
2898
|
+
endpoint: "/v1/memory/extract",
|
|
2899
|
+
method: "POST",
|
|
2900
|
+
operation: "writeAck",
|
|
2901
|
+
body: { project, message: params.message }
|
|
2902
|
+
});
|
|
2903
|
+
return response.data;
|
|
2904
|
+
});
|
|
2905
|
+
}
|
|
2574
2906
|
async shutdown() {
|
|
2575
2907
|
await this.writeQueue.stop();
|
|
2576
2908
|
}
|
|
2577
2909
|
};
|
|
2578
2910
|
|
|
2911
|
+
// ../src/sdk/adapters/tools.ts
|
|
2912
|
+
import { z } from "zod";
|
|
2913
|
+
var searchInputSchema = z.object({
|
|
2914
|
+
q: z.string().min(1),
|
|
2915
|
+
project: z.string().optional(),
|
|
2916
|
+
topK: z.number().int().positive().max(50).optional(),
|
|
2917
|
+
userId: z.string().optional(),
|
|
2918
|
+
sessionId: z.string().optional()
|
|
2919
|
+
});
|
|
2920
|
+
var rememberInputSchema = z.object({
|
|
2921
|
+
content: z.string().min(1),
|
|
2922
|
+
project: z.string().optional(),
|
|
2923
|
+
memoryType: z.enum([
|
|
2924
|
+
"factual",
|
|
2925
|
+
"preference",
|
|
2926
|
+
"event",
|
|
2927
|
+
"relationship",
|
|
2928
|
+
"opinion",
|
|
2929
|
+
"goal",
|
|
2930
|
+
"instruction"
|
|
2931
|
+
]).optional(),
|
|
2932
|
+
userId: z.string().optional(),
|
|
2933
|
+
sessionId: z.string().optional(),
|
|
2934
|
+
metadata: z.record(z.unknown()).optional()
|
|
2935
|
+
});
|
|
2936
|
+
|
|
2579
2937
|
// ../src/sdk/index.ts
|
|
2580
|
-
var WhisperError = class extends Error {
|
|
2581
|
-
code;
|
|
2582
|
-
status;
|
|
2583
|
-
retryable;
|
|
2584
|
-
details;
|
|
2585
|
-
constructor(args) {
|
|
2586
|
-
super(args.message);
|
|
2587
|
-
this.name = "WhisperError";
|
|
2588
|
-
this.code = args.code;
|
|
2589
|
-
this.status = args.status;
|
|
2590
|
-
this.retryable = args.retryable ?? false;
|
|
2591
|
-
this.details = args.details;
|
|
2592
|
-
}
|
|
2593
|
-
};
|
|
2594
2938
|
var DEFAULT_MAX_ATTEMPTS = 3;
|
|
2595
2939
|
var DEFAULT_BASE_DELAY_MS = 250;
|
|
2596
2940
|
var DEFAULT_MAX_DELAY_MS = 2e3;
|
|
@@ -2961,7 +3305,10 @@ var WhisperContext = class _WhisperContext {
|
|
|
2961
3305
|
message,
|
|
2962
3306
|
status: error.status,
|
|
2963
3307
|
retryable,
|
|
2964
|
-
|
|
3308
|
+
hint: error.hint,
|
|
3309
|
+
requestId: error.requestId || error.traceId,
|
|
3310
|
+
details: error.details,
|
|
3311
|
+
cause: error
|
|
2965
3312
|
});
|
|
2966
3313
|
}
|
|
2967
3314
|
}
|
|
@@ -4711,12 +5058,28 @@ async function ingestSessionWithSyncFallback(params) {
|
|
|
4711
5058
|
return whisper.ingestSession(params);
|
|
4712
5059
|
}
|
|
4713
5060
|
}
|
|
5061
|
+
function apiKeyFingerprint(apiKey) {
|
|
5062
|
+
const value = String(apiKey || "").trim();
|
|
5063
|
+
if (!value) return "anon";
|
|
5064
|
+
return createHash("sha256").update(value).digest("hex").slice(0, 12);
|
|
5065
|
+
}
|
|
4714
5066
|
function defaultMcpUserId(params) {
|
|
4715
5067
|
const explicit = process.env.WHISPER_USER_ID?.trim();
|
|
4716
5068
|
if (explicit) return explicit;
|
|
5069
|
+
let osUsername = process.env.USER?.trim() || process.env.USERNAME?.trim();
|
|
5070
|
+
if (!osUsername) {
|
|
5071
|
+
try {
|
|
5072
|
+
osUsername = userInfo().username?.trim();
|
|
5073
|
+
} catch {
|
|
5074
|
+
osUsername = "";
|
|
5075
|
+
}
|
|
5076
|
+
}
|
|
5077
|
+
if (!osUsername) {
|
|
5078
|
+
throw new Error("Unable to derive user identity. Set WHISPER_USER_ID or pass user_id.");
|
|
5079
|
+
}
|
|
4717
5080
|
const workspacePath = params?.workspacePath || canonicalizeWorkspacePath(process.cwd());
|
|
4718
5081
|
const projectRef = params?.project || DEFAULT_PROJECT || "default";
|
|
4719
|
-
const seed = `${workspacePath}|${projectRef}|${API_KEY.
|
|
5082
|
+
const seed = `${workspacePath}|${projectRef}|${apiKeyFingerprint(API_KEY)}|${osUsername.toLowerCase()}`;
|
|
4720
5083
|
return `mcp-user-${createHash("sha256").update(seed).digest("hex").slice(0, 12)}`;
|
|
4721
5084
|
}
|
|
4722
5085
|
function resolveMcpScope(params, options) {
|
|
@@ -4732,6 +5095,68 @@ function resolveMcpScope(params, options) {
|
|
|
4732
5095
|
workspacePath
|
|
4733
5096
|
};
|
|
4734
5097
|
}
|
|
5098
|
+
function resolveMemoryScope(params) {
|
|
5099
|
+
const base = resolveMcpScope({
|
|
5100
|
+
project: params?.project,
|
|
5101
|
+
user_id: params?.user_id,
|
|
5102
|
+
session_id: params?.session_id,
|
|
5103
|
+
path: params?.path
|
|
5104
|
+
});
|
|
5105
|
+
const scopeMode = params?.scope === "shared_project" ? "shared_project" : "personal";
|
|
5106
|
+
if (scopeMode === "shared_project") {
|
|
5107
|
+
if (!base.project && !DEFAULT_PROJECT) {
|
|
5108
|
+
throw new Error("shared_project scope requires a project. Set WHISPER_PROJECT or pass project.");
|
|
5109
|
+
}
|
|
5110
|
+
const projectRef = base.project || DEFAULT_PROJECT || "default";
|
|
5111
|
+
return {
|
|
5112
|
+
...base,
|
|
5113
|
+
userId: `shared-project:${projectRef}`,
|
|
5114
|
+
sessionId: void 0,
|
|
5115
|
+
actorUserId: base.userId,
|
|
5116
|
+
actorSessionId: base.sessionId,
|
|
5117
|
+
scopeMode,
|
|
5118
|
+
sharedProjectRef: projectRef
|
|
5119
|
+
};
|
|
5120
|
+
}
|
|
5121
|
+
return {
|
|
5122
|
+
...base,
|
|
5123
|
+
actorUserId: base.userId,
|
|
5124
|
+
actorSessionId: base.sessionId,
|
|
5125
|
+
scopeMode,
|
|
5126
|
+
sharedProjectRef: null
|
|
5127
|
+
};
|
|
5128
|
+
}
|
|
5129
|
+
function sharedProjectMemoryUserId(projectRef) {
|
|
5130
|
+
return `shared-project:${projectRef}`;
|
|
5131
|
+
}
|
|
5132
|
+
function buildMemoryScopeMetadata(args) {
|
|
5133
|
+
const { memoryScope } = args;
|
|
5134
|
+
const metadata = {
|
|
5135
|
+
scope: memoryScope.scopeMode,
|
|
5136
|
+
...memoryScope.sharedProjectRef ? { shared_project_ref: memoryScope.sharedProjectRef } : {}
|
|
5137
|
+
};
|
|
5138
|
+
if (memoryScope.scopeMode === "shared_project") {
|
|
5139
|
+
metadata.provenance = {
|
|
5140
|
+
writer_user_id: memoryScope.actorUserId,
|
|
5141
|
+
writer_session_id: memoryScope.actorSessionId || null,
|
|
5142
|
+
writer_agent_id: args.agent_id || process.env.WHISPER_AGENT_ID || null,
|
|
5143
|
+
written_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
5144
|
+
};
|
|
5145
|
+
}
|
|
5146
|
+
return metadata;
|
|
5147
|
+
}
|
|
5148
|
+
function isMemoryInScope(args) {
|
|
5149
|
+
const memory = args.memory;
|
|
5150
|
+
if (!memory || typeof memory !== "object") return false;
|
|
5151
|
+
const memoryUserId = String(memory.userId || memory.user_id || "").trim();
|
|
5152
|
+
if (args.memoryScope.scopeMode === "shared_project") {
|
|
5153
|
+
const metadata = memory.metadata && typeof memory.metadata === "object" ? memory.metadata : {};
|
|
5154
|
+
const metaScope = String(metadata.scope || "").trim();
|
|
5155
|
+
const metaProjectRef = String(metadata.shared_project_ref || "").trim();
|
|
5156
|
+
return memoryUserId === args.memoryScope.userId || metaScope === "shared_project" && metaProjectRef === (args.memoryScope.sharedProjectRef || "");
|
|
5157
|
+
}
|
|
5158
|
+
return memoryUserId === args.memoryScope.userId;
|
|
5159
|
+
}
|
|
4735
5160
|
function noteAutomaticSourceActivity(params) {
|
|
4736
5161
|
if (!runtimeClient) return;
|
|
4737
5162
|
const sourceIds = [...new Set((params.sourceIds || []).map((value) => String(value || "").trim()).filter(Boolean))];
|
|
@@ -5298,6 +5723,82 @@ function resolveForgetQueryCandidates(rawResults, query) {
|
|
|
5298
5723
|
}
|
|
5299
5724
|
return { memory_ids: [], resolved_by: "none", warning: "Query did not resolve to a reliable memory match. No memories were changed." };
|
|
5300
5725
|
}
|
|
5726
|
+
async function searchMemoriesForContextQuery(args) {
|
|
5727
|
+
return whisper.searchMemoriesSOTA({
|
|
5728
|
+
project: args.project,
|
|
5729
|
+
query: args.query,
|
|
5730
|
+
user_id: args.user_id,
|
|
5731
|
+
session_id: args.session_id,
|
|
5732
|
+
top_k: args.top_k ?? 5,
|
|
5733
|
+
include_relations: false,
|
|
5734
|
+
include_pending: true
|
|
5735
|
+
});
|
|
5736
|
+
}
|
|
5737
|
+
async function runContextQueryMemoryRescue(args) {
|
|
5738
|
+
const scoped = await searchMemoriesForContextQuery(args);
|
|
5739
|
+
const personalResults = formatCanonicalMemoryResults(scoped);
|
|
5740
|
+
const sharedUserId = sharedProjectMemoryUserId(args.project);
|
|
5741
|
+
const shared = args.user_id === sharedUserId ? { results: [] } : await searchMemoriesForContextQuery({
|
|
5742
|
+
project: args.project,
|
|
5743
|
+
query: args.query,
|
|
5744
|
+
user_id: sharedUserId,
|
|
5745
|
+
top_k: args.top_k
|
|
5746
|
+
});
|
|
5747
|
+
const sharedResults = formatCanonicalMemoryResults(shared);
|
|
5748
|
+
const deduped = /* @__PURE__ */ new Set();
|
|
5749
|
+
const combined = [
|
|
5750
|
+
...personalResults.map((result) => ({
|
|
5751
|
+
...result,
|
|
5752
|
+
memory_scope: "personal",
|
|
5753
|
+
ranking_score: clamp012(Number(result.similarity ?? 0.5) + 0.08)
|
|
5754
|
+
})),
|
|
5755
|
+
...sharedResults.map((result) => ({
|
|
5756
|
+
...result,
|
|
5757
|
+
memory_scope: "shared_project",
|
|
5758
|
+
ranking_score: clamp012(Number(result.similarity ?? 0.5))
|
|
5759
|
+
}))
|
|
5760
|
+
].filter((result) => {
|
|
5761
|
+
if (!result.id) return false;
|
|
5762
|
+
if (deduped.has(result.id)) return false;
|
|
5763
|
+
deduped.add(result.id);
|
|
5764
|
+
return true;
|
|
5765
|
+
}).sort((a, b) => b.ranking_score - a.ranking_score);
|
|
5766
|
+
const rescueMode = combined.length === 0 ? null : sharedResults.length > 0 ? "scoped_plus_shared" : "scoped_only";
|
|
5767
|
+
return {
|
|
5768
|
+
personal_results: personalResults,
|
|
5769
|
+
shared_results: sharedResults,
|
|
5770
|
+
combined_results: combined,
|
|
5771
|
+
rescue_mode: rescueMode
|
|
5772
|
+
};
|
|
5773
|
+
}
|
|
5774
|
+
function renderContextQueryMemoryRescue(args) {
|
|
5775
|
+
const lines = args.combined_results.map(
|
|
5776
|
+
(result, index) => `${index + 1}. [${result.memory_scope}, ${result.memory_type || "memory"}, score: ${result.ranking_score.toFixed(2)}] ${result.content}`
|
|
5777
|
+
);
|
|
5778
|
+
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):
|
|
5779
|
+
|
|
5780
|
+
${lines.join("\n\n")}`;
|
|
5781
|
+
}
|
|
5782
|
+
function memoryRescueResultsToEvidence(args) {
|
|
5783
|
+
return args.hits.map(
|
|
5784
|
+
(hit) => toEvidenceRef({
|
|
5785
|
+
id: `memory:${hit.id || randomUUID()}`,
|
|
5786
|
+
source: "memory",
|
|
5787
|
+
document: "memory",
|
|
5788
|
+
content: hit.content,
|
|
5789
|
+
score: hit.ranking_score,
|
|
5790
|
+
retrieval_source: "memory",
|
|
5791
|
+
metadata: {
|
|
5792
|
+
path: `memory://${hit.memory_scope}/${hit.id || "unknown"}`,
|
|
5793
|
+
line_start: 1,
|
|
5794
|
+
line_end: 1,
|
|
5795
|
+
score: hit.ranking_score,
|
|
5796
|
+
memory_scope: hit.memory_scope,
|
|
5797
|
+
memory_type: hit.memory_type || "factual"
|
|
5798
|
+
}
|
|
5799
|
+
}, args.workspaceId, "memory")
|
|
5800
|
+
);
|
|
5801
|
+
}
|
|
5301
5802
|
function likelyEmbeddingFailure(error) {
|
|
5302
5803
|
const message = String(error?.message || error || "").toLowerCase();
|
|
5303
5804
|
return message.includes("embedding") || message.includes("vector") || message.includes("timeout") || message.includes("timed out") || message.includes("temporarily unavailable");
|
|
@@ -5974,9 +6475,9 @@ server.tool(
|
|
|
5974
6475
|
"index.workspace_resolve",
|
|
5975
6476
|
"Resolve workspace identity from path + API key and map to a project without mandatory dashboard setup.",
|
|
5976
6477
|
{
|
|
5977
|
-
path:
|
|
5978
|
-
workspace_id:
|
|
5979
|
-
project:
|
|
6478
|
+
path: z2.string().optional().describe("Workspace path. Defaults to current working directory."),
|
|
6479
|
+
workspace_id: z2.string().optional(),
|
|
6480
|
+
project: z2.string().optional()
|
|
5980
6481
|
},
|
|
5981
6482
|
async ({ path, workspace_id, project }) => {
|
|
5982
6483
|
try {
|
|
@@ -6013,8 +6514,8 @@ server.tool(
|
|
|
6013
6514
|
"index.workspace_status",
|
|
6014
6515
|
"Check index freshness, coverage, commit, and pending changes before retrieval/edits.",
|
|
6015
6516
|
{
|
|
6016
|
-
workspace_id:
|
|
6017
|
-
path:
|
|
6517
|
+
workspace_id: z2.string().optional(),
|
|
6518
|
+
path: z2.string().optional()
|
|
6018
6519
|
},
|
|
6019
6520
|
async ({ workspace_id, path }) => {
|
|
6020
6521
|
try {
|
|
@@ -6051,10 +6552,10 @@ server.tool(
|
|
|
6051
6552
|
"index.workspace_run",
|
|
6052
6553
|
"Refresh local workspace index metadata (coverage, commit, freshness) for trust checks. This does not upload files or create backend embeddings.",
|
|
6053
6554
|
{
|
|
6054
|
-
workspace_id:
|
|
6055
|
-
path:
|
|
6056
|
-
mode:
|
|
6057
|
-
max_files:
|
|
6555
|
+
workspace_id: z2.string().optional(),
|
|
6556
|
+
path: z2.string().optional(),
|
|
6557
|
+
mode: z2.enum(["full", "incremental"]).optional().default("incremental"),
|
|
6558
|
+
max_files: z2.number().optional().default(1500)
|
|
6058
6559
|
},
|
|
6059
6560
|
async ({ workspace_id, path, mode, max_files }) => {
|
|
6060
6561
|
try {
|
|
@@ -6100,11 +6601,11 @@ server.tool(
|
|
|
6100
6601
|
"index.local_scan_ingest",
|
|
6101
6602
|
"Ingest local files into Whisper backend (chunk/embed/index) and persist incremental manifest. Requires WHISPER_MCP_MODE=auto or local.",
|
|
6102
6603
|
{
|
|
6103
|
-
project:
|
|
6104
|
-
path:
|
|
6105
|
-
glob:
|
|
6106
|
-
max_files:
|
|
6107
|
-
chunk_chars:
|
|
6604
|
+
project: z2.string().optional().describe("Project name or slug"),
|
|
6605
|
+
path: z2.string().optional().describe("Local path to ingest. Defaults to current working directory."),
|
|
6606
|
+
glob: z2.string().optional().describe("Optional include glob"),
|
|
6607
|
+
max_files: z2.number().optional().default(200),
|
|
6608
|
+
chunk_chars: z2.number().optional().default(2e4)
|
|
6108
6609
|
},
|
|
6109
6610
|
async ({ project, path, glob, max_files, chunk_chars }) => {
|
|
6110
6611
|
try {
|
|
@@ -6136,12 +6637,12 @@ server.tool(
|
|
|
6136
6637
|
"index.auto_repair",
|
|
6137
6638
|
"Diagnose and apply deterministic retrieval/setup repairs. This is the canonical zero-stress repair path.",
|
|
6138
6639
|
{
|
|
6139
|
-
path:
|
|
6140
|
-
workspace_id:
|
|
6141
|
-
project:
|
|
6142
|
-
apply:
|
|
6143
|
-
repair_scope:
|
|
6144
|
-
allow_restart_hint:
|
|
6640
|
+
path: z2.string().optional(),
|
|
6641
|
+
workspace_id: z2.string().optional(),
|
|
6642
|
+
project: z2.string().optional(),
|
|
6643
|
+
apply: z2.boolean().optional().default(true),
|
|
6644
|
+
repair_scope: z2.enum(["safe", "full"]).optional().default("safe"),
|
|
6645
|
+
allow_restart_hint: z2.boolean().optional().default(true)
|
|
6145
6646
|
},
|
|
6146
6647
|
async ({ path, workspace_id, project, apply, repair_scope, allow_restart_hint }) => {
|
|
6147
6648
|
try {
|
|
@@ -6163,12 +6664,12 @@ server.tool(
|
|
|
6163
6664
|
"fix",
|
|
6164
6665
|
"Primary alias for zero-stress setup/retrieval repair. Delegates to index.auto_repair.",
|
|
6165
6666
|
{
|
|
6166
|
-
path:
|
|
6167
|
-
workspace_id:
|
|
6168
|
-
project:
|
|
6169
|
-
apply:
|
|
6170
|
-
repair_scope:
|
|
6171
|
-
allow_restart_hint:
|
|
6667
|
+
path: z2.string().optional(),
|
|
6668
|
+
workspace_id: z2.string().optional(),
|
|
6669
|
+
project: z2.string().optional(),
|
|
6670
|
+
apply: z2.boolean().optional().default(true),
|
|
6671
|
+
repair_scope: z2.enum(["safe", "full"]).optional().default("safe"),
|
|
6672
|
+
allow_restart_hint: z2.boolean().optional().default(true)
|
|
6172
6673
|
},
|
|
6173
6674
|
async ({ path, workspace_id, project, apply, repair_scope, allow_restart_hint }) => {
|
|
6174
6675
|
try {
|
|
@@ -6244,6 +6745,7 @@ async function runUnifiedContextRetrieval(params) {
|
|
|
6244
6745
|
let retrievalReadiness = preflight.retrieval_readiness;
|
|
6245
6746
|
let retrievalRoute = preflight.retrieval_route;
|
|
6246
6747
|
let latencyMs = 0;
|
|
6748
|
+
let memoryContribution = null;
|
|
6247
6749
|
if (preflight.retrieval_route === "local_workspace_fallback") {
|
|
6248
6750
|
const localRetrieval = await runLocalWorkspaceRetrieval({
|
|
6249
6751
|
query: params.question,
|
|
@@ -6302,6 +6804,39 @@ async function runUnifiedContextRetrieval(params) {
|
|
|
6302
6804
|
retrievalRoute = preflight.repo_grounded ? "project_repo" : "none";
|
|
6303
6805
|
}
|
|
6304
6806
|
}
|
|
6807
|
+
if (resolvedProject && params.include_memories === true && !preflight.repo_grounded) {
|
|
6808
|
+
try {
|
|
6809
|
+
memoryContribution = await runContextQueryMemoryRescue({
|
|
6810
|
+
project: resolvedProject,
|
|
6811
|
+
query: params.question,
|
|
6812
|
+
user_id: scope.userId,
|
|
6813
|
+
session_id: params.session_id,
|
|
6814
|
+
top_k: Math.max(4, Math.min(topK, 8))
|
|
6815
|
+
});
|
|
6816
|
+
if (memoryContribution.combined_results.length > 0) {
|
|
6817
|
+
const memoryEvidence = memoryRescueResultsToEvidence({
|
|
6818
|
+
workspaceId: preflight.trust_state.workspace_id,
|
|
6819
|
+
hits: memoryContribution.combined_results
|
|
6820
|
+
});
|
|
6821
|
+
const existingSourceIds = new Set(evidence.map((item) => item.source_id));
|
|
6822
|
+
const dedupedMemoryEvidence = memoryEvidence.filter((item) => !existingSourceIds.has(item.source_id));
|
|
6823
|
+
evidence = [...evidence, ...dedupedMemoryEvidence];
|
|
6824
|
+
const memoryContext = renderContextQueryMemoryRescue({
|
|
6825
|
+
project: resolvedProject,
|
|
6826
|
+
scope,
|
|
6827
|
+
personal_results: memoryContribution.personal_results,
|
|
6828
|
+
shared_results: memoryContribution.shared_results,
|
|
6829
|
+
combined_results: memoryContribution.combined_results
|
|
6830
|
+
});
|
|
6831
|
+
contextText = contextText.trim().length > 0 ? `${contextText}
|
|
6832
|
+
|
|
6833
|
+
${memoryContext}` : memoryContext;
|
|
6834
|
+
if (retrievalRoute === "none") retrievalRoute = "memory_only";
|
|
6835
|
+
}
|
|
6836
|
+
} catch (error) {
|
|
6837
|
+
warnings.push(`Scoped memory contribution failed: ${String(error?.message || error)}`);
|
|
6838
|
+
}
|
|
6839
|
+
}
|
|
6305
6840
|
recordSemanticAttempt(preflight.trust_state.workspace_id, semanticStatus === "failed");
|
|
6306
6841
|
const semanticStats = getWorkspaceSemanticFailureStats(preflight.trust_state.workspace_id);
|
|
6307
6842
|
const trustScore = computeTrustScore({
|
|
@@ -6374,6 +6909,14 @@ async function runUnifiedContextRetrieval(params) {
|
|
|
6374
6909
|
user_id: scope.userId,
|
|
6375
6910
|
session_id: params.session_id || null
|
|
6376
6911
|
},
|
|
6912
|
+
memory: {
|
|
6913
|
+
included: resolvedProject ? params.include_memories === true && !preflight.repo_grounded : false,
|
|
6914
|
+
personal_count: memoryContribution?.personal_results.length || 0,
|
|
6915
|
+
shared_count: memoryContribution?.shared_results.length || 0,
|
|
6916
|
+
combined_count: memoryContribution?.combined_results.length || 0,
|
|
6917
|
+
shared_user_id: resolvedProject ? sharedProjectMemoryUserId(resolvedProject) : null,
|
|
6918
|
+
ordering: "personal_then_shared_bias"
|
|
6919
|
+
},
|
|
6377
6920
|
retrieval_profile: retrievalProfile,
|
|
6378
6921
|
trust_score: trustScore,
|
|
6379
6922
|
semantic_failure_rate: semanticStats.rate,
|
|
@@ -6383,7 +6926,11 @@ async function runUnifiedContextRetrieval(params) {
|
|
|
6383
6926
|
}
|
|
6384
6927
|
};
|
|
6385
6928
|
if (params.include_alias_warning) {
|
|
6386
|
-
payload.warnings = Array.from(/* @__PURE__ */ new Set([
|
|
6929
|
+
payload.warnings = Array.from(/* @__PURE__ */ new Set([
|
|
6930
|
+
...payload.warnings,
|
|
6931
|
+
"deprecated_alias_use_context_query",
|
|
6932
|
+
"deprecated_alias_use_context.query"
|
|
6933
|
+
]));
|
|
6387
6934
|
}
|
|
6388
6935
|
return payload;
|
|
6389
6936
|
}
|
|
@@ -6391,17 +6938,17 @@ server.tool(
|
|
|
6391
6938
|
"context.get_relevant",
|
|
6392
6939
|
"Default grounded retrieval step for workspace/project questions. Returns ranked evidence with file:line citations and may abstain when workspace/repo trust is not ready.",
|
|
6393
6940
|
{
|
|
6394
|
-
question:
|
|
6395
|
-
path:
|
|
6396
|
-
workspace_id:
|
|
6397
|
-
project:
|
|
6398
|
-
top_k:
|
|
6399
|
-
include_memories:
|
|
6400
|
-
include_graph:
|
|
6401
|
-
session_id:
|
|
6402
|
-
user_id:
|
|
6403
|
-
include_parent_content:
|
|
6404
|
-
retrieval_profile:
|
|
6941
|
+
question: z2.string().describe("Task/question to retrieve context for"),
|
|
6942
|
+
path: z2.string().optional().describe("Workspace path. Defaults to current working directory."),
|
|
6943
|
+
workspace_id: z2.string().optional(),
|
|
6944
|
+
project: z2.string().optional(),
|
|
6945
|
+
top_k: z2.number().optional().default(12),
|
|
6946
|
+
include_memories: z2.boolean().optional().default(true),
|
|
6947
|
+
include_graph: z2.boolean().optional().default(true),
|
|
6948
|
+
session_id: z2.string().optional(),
|
|
6949
|
+
user_id: z2.string().optional(),
|
|
6950
|
+
include_parent_content: z2.boolean().optional().default(false),
|
|
6951
|
+
retrieval_profile: z2.enum(MCP_RETRIEVAL_PROFILE_VALUES).optional()
|
|
6405
6952
|
},
|
|
6406
6953
|
async ({ question, path, workspace_id, project, top_k, include_memories, include_graph, session_id, user_id, include_parent_content, retrieval_profile }) => {
|
|
6407
6954
|
try {
|
|
@@ -6429,12 +6976,12 @@ server.tool(
|
|
|
6429
6976
|
"context.claim_verify",
|
|
6430
6977
|
"Verify whether a claim is supported by retrieved context. Returns supported/partial/unsupported with evidence.",
|
|
6431
6978
|
{
|
|
6432
|
-
claim:
|
|
6433
|
-
path:
|
|
6434
|
-
workspace_id:
|
|
6435
|
-
project:
|
|
6436
|
-
context_ids:
|
|
6437
|
-
strict:
|
|
6979
|
+
claim: z2.string().describe("Claim to verify"),
|
|
6980
|
+
path: z2.string().optional().describe("Workspace path. Defaults to current working directory."),
|
|
6981
|
+
workspace_id: z2.string().optional(),
|
|
6982
|
+
project: z2.string().optional(),
|
|
6983
|
+
context_ids: z2.array(z2.string()).optional(),
|
|
6984
|
+
strict: z2.boolean().optional().default(true)
|
|
6438
6985
|
},
|
|
6439
6986
|
async ({ claim, path, workspace_id, project, context_ids, strict }) => {
|
|
6440
6987
|
try {
|
|
@@ -6504,20 +7051,20 @@ server.tool(
|
|
|
6504
7051
|
"context.evidence_answer",
|
|
6505
7052
|
"Answer a question only when evidence requirements are met. Fails closed with an abstain payload when not verifiable.",
|
|
6506
7053
|
{
|
|
6507
|
-
question:
|
|
6508
|
-
path:
|
|
6509
|
-
workspace_id:
|
|
6510
|
-
project:
|
|
6511
|
-
constraints:
|
|
6512
|
-
require_citations:
|
|
6513
|
-
min_evidence_items:
|
|
6514
|
-
min_confidence:
|
|
6515
|
-
max_staleness_hours:
|
|
7054
|
+
question: z2.string(),
|
|
7055
|
+
path: z2.string().optional().describe("Workspace path. Defaults to current working directory."),
|
|
7056
|
+
workspace_id: z2.string().optional(),
|
|
7057
|
+
project: z2.string().optional(),
|
|
7058
|
+
constraints: z2.object({
|
|
7059
|
+
require_citations: z2.boolean().optional().default(true),
|
|
7060
|
+
min_evidence_items: z2.number().optional().default(2),
|
|
7061
|
+
min_confidence: z2.number().optional().default(0.65),
|
|
7062
|
+
max_staleness_hours: z2.number().optional().default(168)
|
|
6516
7063
|
}).optional(),
|
|
6517
|
-
retrieval:
|
|
6518
|
-
top_k:
|
|
6519
|
-
include_symbols:
|
|
6520
|
-
include_recent_decisions:
|
|
7064
|
+
retrieval: z2.object({
|
|
7065
|
+
top_k: z2.number().optional().default(12),
|
|
7066
|
+
include_symbols: z2.boolean().optional().default(true),
|
|
7067
|
+
include_recent_decisions: z2.boolean().optional().default(true)
|
|
6521
7068
|
}).optional()
|
|
6522
7069
|
},
|
|
6523
7070
|
async ({ question, path, workspace_id, project, constraints, retrieval }) => {
|
|
@@ -6630,18 +7177,18 @@ server.tool(
|
|
|
6630
7177
|
"context.query",
|
|
6631
7178
|
"Use this when answering from project knowledge rather than general model memory. Retrieves packed context and auto-falls back between repo, local, and memory routes based on trust/readiness.",
|
|
6632
7179
|
{
|
|
6633
|
-
project:
|
|
6634
|
-
query:
|
|
6635
|
-
path:
|
|
6636
|
-
top_k:
|
|
6637
|
-
chunk_types:
|
|
6638
|
-
include_memories:
|
|
6639
|
-
include_graph:
|
|
6640
|
-
user_id:
|
|
6641
|
-
session_id:
|
|
6642
|
-
max_tokens:
|
|
6643
|
-
include_parent_content:
|
|
6644
|
-
retrieval_profile:
|
|
7180
|
+
project: z2.string().optional().describe("Project name or slug (optional if WHISPER_PROJECT is set)"),
|
|
7181
|
+
query: z2.string().describe("What are you looking for?"),
|
|
7182
|
+
path: z2.string().optional().describe("Workspace path. Defaults to current working directory."),
|
|
7183
|
+
top_k: z2.number().optional().default(10).describe("Number of results"),
|
|
7184
|
+
chunk_types: z2.array(z2.string()).optional().describe("Filter: code, function, class, documentation, api_spec, schema, config, text"),
|
|
7185
|
+
include_memories: z2.boolean().optional().describe("Include relevant memories. Omit to use automatic runtime defaults."),
|
|
7186
|
+
include_graph: z2.boolean().optional().default(false).describe("Include knowledge graph traversal"),
|
|
7187
|
+
user_id: z2.string().optional().describe("User ID for memory scoping"),
|
|
7188
|
+
session_id: z2.string().optional().describe("Session ID for memory scoping"),
|
|
7189
|
+
max_tokens: z2.number().optional().describe("Max tokens for packed context"),
|
|
7190
|
+
include_parent_content: z2.boolean().optional().default(false),
|
|
7191
|
+
retrieval_profile: z2.enum(MCP_RETRIEVAL_PROFILE_VALUES).optional()
|
|
6645
7192
|
},
|
|
6646
7193
|
async ({ project, query, path, top_k, chunk_types, include_memories, include_graph, user_id, session_id, max_tokens, include_parent_content, retrieval_profile }) => {
|
|
6647
7194
|
try {
|
|
@@ -6668,25 +7215,30 @@ server.tool(
|
|
|
6668
7215
|
"memory.add",
|
|
6669
7216
|
"Store a memory (fact, preference, decision) that persists across conversations. Memories can be scoped to a user, session, or agent.",
|
|
6670
7217
|
{
|
|
6671
|
-
project:
|
|
6672
|
-
content:
|
|
6673
|
-
memory_type:
|
|
6674
|
-
|
|
6675
|
-
|
|
6676
|
-
|
|
6677
|
-
|
|
7218
|
+
project: z2.string().optional().describe("Project name or slug"),
|
|
7219
|
+
content: z2.string().describe("The memory content to store"),
|
|
7220
|
+
memory_type: z2.enum(["factual", "preference", "event", "relationship", "opinion", "goal", "instruction"]).optional().default("factual"),
|
|
7221
|
+
scope: z2.enum(["personal", "shared_project"]).optional().default("personal"),
|
|
7222
|
+
user_id: z2.string().optional().describe("User this memory belongs to"),
|
|
7223
|
+
session_id: z2.string().optional().describe("Session scope"),
|
|
7224
|
+
agent_id: z2.string().optional().describe("Agent scope"),
|
|
7225
|
+
importance: z2.number().optional().default(0.5).describe("Importance 0-1")
|
|
6678
7226
|
},
|
|
6679
|
-
async ({ project, content, memory_type, user_id, session_id, agent_id, importance }) => {
|
|
7227
|
+
async ({ project, content, memory_type, scope, user_id, session_id, agent_id, importance }) => {
|
|
6680
7228
|
try {
|
|
6681
|
-
const
|
|
7229
|
+
const memoryScope = resolveMemoryScope({ project, user_id, session_id, scope });
|
|
6682
7230
|
const result = await whisper.addMemory({
|
|
6683
|
-
project:
|
|
7231
|
+
project: memoryScope.project,
|
|
6684
7232
|
content,
|
|
6685
7233
|
memory_type,
|
|
6686
|
-
user_id:
|
|
6687
|
-
session_id:
|
|
7234
|
+
user_id: memoryScope.userId,
|
|
7235
|
+
session_id: memoryScope.sessionId,
|
|
6688
7236
|
agent_id,
|
|
6689
|
-
importance
|
|
7237
|
+
importance,
|
|
7238
|
+
metadata: buildMemoryScopeMetadata({
|
|
7239
|
+
memoryScope,
|
|
7240
|
+
agent_id
|
|
7241
|
+
})
|
|
6690
7242
|
});
|
|
6691
7243
|
const memoryId = result?.memory_id || result.id;
|
|
6692
7244
|
const jobId = result?.job_id;
|
|
@@ -6699,12 +7251,16 @@ server.tool(
|
|
|
6699
7251
|
mode: mode || null,
|
|
6700
7252
|
semantic_status: semanticStatus || null,
|
|
6701
7253
|
memory_type: memory_type || "factual",
|
|
7254
|
+
scope: memoryScope.scopeMode,
|
|
6702
7255
|
queued: mode === "async" || Boolean(jobId),
|
|
6703
7256
|
diagnostics: {
|
|
6704
7257
|
scope: {
|
|
6705
|
-
project:
|
|
6706
|
-
user_id:
|
|
6707
|
-
session_id:
|
|
7258
|
+
project: memoryScope.project || null,
|
|
7259
|
+
user_id: memoryScope.userId,
|
|
7260
|
+
session_id: memoryScope.sessionId || null,
|
|
7261
|
+
scope: memoryScope.scopeMode,
|
|
7262
|
+
actor_user_id: memoryScope.actorUserId,
|
|
7263
|
+
actor_session_id: memoryScope.actorSessionId || null
|
|
6708
7264
|
}
|
|
6709
7265
|
}
|
|
6710
7266
|
});
|
|
@@ -6717,30 +7273,31 @@ server.tool(
|
|
|
6717
7273
|
"memory.search",
|
|
6718
7274
|
"Call this before answering questions about user history (preferences, prior decisions, past tasks, or 'what did we discuss/search'). Returns memory context you would not otherwise know.",
|
|
6719
7275
|
{
|
|
6720
|
-
project:
|
|
6721
|
-
query:
|
|
6722
|
-
|
|
6723
|
-
|
|
6724
|
-
|
|
6725
|
-
|
|
7276
|
+
project: z2.string().optional().describe("Project name or slug"),
|
|
7277
|
+
query: z2.string().describe("What to search for"),
|
|
7278
|
+
scope: z2.enum(["personal", "shared_project"]).optional().default("personal"),
|
|
7279
|
+
user_id: z2.string().optional().describe("Filter by user"),
|
|
7280
|
+
session_id: z2.string().optional().describe("Filter by session"),
|
|
7281
|
+
top_k: z2.number().optional().default(10).describe("Number of results"),
|
|
7282
|
+
memory_types: z2.array(z2.enum(["factual", "preference", "event", "relationship", "opinion", "goal", "instruction"])).optional()
|
|
6726
7283
|
},
|
|
6727
|
-
async ({ project, query, user_id, session_id, top_k, memory_types }) => {
|
|
7284
|
+
async ({ project, query, scope, user_id, session_id, top_k, memory_types }) => {
|
|
6728
7285
|
try {
|
|
6729
|
-
const
|
|
7286
|
+
const memoryScope = resolveMemoryScope({ project, user_id, session_id, scope });
|
|
6730
7287
|
const results = runtimeClient && (!memory_types || memory_types.length <= 1) ? await runtimeClient.memory.search({
|
|
6731
|
-
project:
|
|
7288
|
+
project: memoryScope.project,
|
|
6732
7289
|
query,
|
|
6733
|
-
user_id:
|
|
6734
|
-
session_id:
|
|
7290
|
+
user_id: memoryScope.userId,
|
|
7291
|
+
session_id: memoryScope.sessionId,
|
|
6735
7292
|
top_k,
|
|
6736
7293
|
include_pending: true,
|
|
6737
7294
|
profile: "balanced",
|
|
6738
7295
|
...memory_types?.length === 1 ? { memory_type: memory_types[0] } : {}
|
|
6739
7296
|
}) : await whisper.searchMemoriesSOTA({
|
|
6740
|
-
project:
|
|
7297
|
+
project: memoryScope.project,
|
|
6741
7298
|
query,
|
|
6742
|
-
user_id:
|
|
6743
|
-
session_id:
|
|
7299
|
+
user_id: memoryScope.userId,
|
|
7300
|
+
session_id: memoryScope.sessionId,
|
|
6744
7301
|
top_k,
|
|
6745
7302
|
memory_types
|
|
6746
7303
|
});
|
|
@@ -6748,15 +7305,17 @@ server.tool(
|
|
|
6748
7305
|
return primaryToolSuccess({
|
|
6749
7306
|
tool: "memory.search",
|
|
6750
7307
|
query,
|
|
6751
|
-
|
|
6752
|
-
|
|
7308
|
+
scope: memoryScope.scopeMode,
|
|
7309
|
+
user_id: memoryScope.userId,
|
|
7310
|
+
session_id: memoryScope.sessionId,
|
|
6753
7311
|
results: normalizedResults,
|
|
6754
7312
|
count: normalizedResults.length,
|
|
6755
7313
|
diagnostics: {
|
|
6756
7314
|
scope: {
|
|
6757
|
-
project:
|
|
6758
|
-
user_id:
|
|
6759
|
-
session_id:
|
|
7315
|
+
project: memoryScope.project || null,
|
|
7316
|
+
user_id: memoryScope.userId,
|
|
7317
|
+
session_id: memoryScope.sessionId || null,
|
|
7318
|
+
scope: memoryScope.scopeMode
|
|
6760
7319
|
}
|
|
6761
7320
|
}
|
|
6762
7321
|
});
|
|
@@ -6794,7 +7353,7 @@ server.tool(
|
|
|
6794
7353
|
server.tool(
|
|
6795
7354
|
"context.list_sources",
|
|
6796
7355
|
"List all data sources connected to a project.",
|
|
6797
|
-
{ project:
|
|
7356
|
+
{ project: z2.string().optional().describe("Project name or slug") },
|
|
6798
7357
|
async ({ project }) => {
|
|
6799
7358
|
try {
|
|
6800
7359
|
const sourceData = await whisper.listSources(project || DEFAULT_PROJECT);
|
|
@@ -6810,38 +7369,38 @@ server.tool(
|
|
|
6810
7369
|
"context.add_source",
|
|
6811
7370
|
"Compatibility learning tool. Add a source to a project with normalized source contract and auto-index by default. Prefer `learn` for new integrations.",
|
|
6812
7371
|
{
|
|
6813
|
-
project:
|
|
6814
|
-
type:
|
|
6815
|
-
name:
|
|
6816
|
-
auto_index:
|
|
6817
|
-
metadata:
|
|
6818
|
-
ingestion_profile:
|
|
6819
|
-
strategy_override:
|
|
6820
|
-
profile_config:
|
|
6821
|
-
owner:
|
|
6822
|
-
repo:
|
|
6823
|
-
branch:
|
|
6824
|
-
paths:
|
|
6825
|
-
url:
|
|
6826
|
-
crawl_depth:
|
|
6827
|
-
include_paths:
|
|
6828
|
-
exclude_paths:
|
|
6829
|
-
file_path:
|
|
6830
|
-
path:
|
|
6831
|
-
glob:
|
|
6832
|
-
max_files:
|
|
6833
|
-
max_pages:
|
|
6834
|
-
extract_mode:
|
|
6835
|
-
workspace_id:
|
|
6836
|
-
channel_ids:
|
|
6837
|
-
since:
|
|
6838
|
-
token:
|
|
6839
|
-
auth_ref:
|
|
6840
|
-
platform:
|
|
6841
|
-
language:
|
|
6842
|
-
allow_stt_fallback:
|
|
6843
|
-
max_duration_minutes:
|
|
6844
|
-
max_chunks:
|
|
7372
|
+
project: z2.string().optional().describe("Project name or slug"),
|
|
7373
|
+
type: z2.enum(["github", "web", "playwright", "pdf", "local", "slack", "video"]).default("github"),
|
|
7374
|
+
name: z2.string().optional(),
|
|
7375
|
+
auto_index: z2.boolean().optional().default(true),
|
|
7376
|
+
metadata: z2.record(z2.string()).optional(),
|
|
7377
|
+
ingestion_profile: z2.enum(["auto", "repo", "web_docs", "pdf_layout", "video_transcript", "plain_text"]).optional(),
|
|
7378
|
+
strategy_override: z2.enum(["fixed", "recursive", "semantic", "hierarchical", "adaptive"]).optional(),
|
|
7379
|
+
profile_config: z2.record(z2.any()).optional(),
|
|
7380
|
+
owner: z2.string().optional(),
|
|
7381
|
+
repo: z2.string().optional(),
|
|
7382
|
+
branch: z2.string().optional(),
|
|
7383
|
+
paths: z2.array(z2.string()).optional(),
|
|
7384
|
+
url: z2.string().url().optional(),
|
|
7385
|
+
crawl_depth: z2.number().optional(),
|
|
7386
|
+
include_paths: z2.array(z2.string()).optional(),
|
|
7387
|
+
exclude_paths: z2.array(z2.string()).optional(),
|
|
7388
|
+
file_path: z2.string().optional(),
|
|
7389
|
+
path: z2.string().optional(),
|
|
7390
|
+
glob: z2.string().optional(),
|
|
7391
|
+
max_files: z2.number().optional(),
|
|
7392
|
+
max_pages: z2.number().optional(),
|
|
7393
|
+
extract_mode: z2.enum(["text", "structured", "markdown"]).optional(),
|
|
7394
|
+
workspace_id: z2.string().optional(),
|
|
7395
|
+
channel_ids: z2.array(z2.string()).optional(),
|
|
7396
|
+
since: z2.string().optional(),
|
|
7397
|
+
token: z2.string().optional(),
|
|
7398
|
+
auth_ref: z2.string().optional(),
|
|
7399
|
+
platform: z2.enum(["youtube", "loom", "generic"]).optional(),
|
|
7400
|
+
language: z2.string().optional(),
|
|
7401
|
+
allow_stt_fallback: z2.boolean().optional(),
|
|
7402
|
+
max_duration_minutes: z2.number().optional(),
|
|
7403
|
+
max_chunks: z2.number().optional()
|
|
6845
7404
|
},
|
|
6846
7405
|
async (input) => {
|
|
6847
7406
|
try {
|
|
@@ -6897,7 +7456,7 @@ server.tool(
|
|
|
6897
7456
|
"context.source_status",
|
|
6898
7457
|
"Get status and stage/progress details for a source sync job.",
|
|
6899
7458
|
{
|
|
6900
|
-
source_id:
|
|
7459
|
+
source_id: z2.string().describe("Source id")
|
|
6901
7460
|
},
|
|
6902
7461
|
async ({ source_id }) => {
|
|
6903
7462
|
try {
|
|
@@ -6912,12 +7471,12 @@ server.tool(
|
|
|
6912
7471
|
"context.add_text",
|
|
6913
7472
|
"Compatibility learning tool. Add text content to a project's knowledge base. Prefer `learn` for new integrations.",
|
|
6914
7473
|
{
|
|
6915
|
-
project:
|
|
6916
|
-
title:
|
|
6917
|
-
content:
|
|
6918
|
-
ingestion_profile:
|
|
6919
|
-
strategy_override:
|
|
6920
|
-
profile_config:
|
|
7474
|
+
project: z2.string().optional().describe("Project name or slug"),
|
|
7475
|
+
title: z2.string().describe("Title for this content"),
|
|
7476
|
+
content: z2.string().describe("The text content to index"),
|
|
7477
|
+
ingestion_profile: z2.enum(["auto", "repo", "web_docs", "pdf_layout", "video_transcript", "plain_text"]).optional(),
|
|
7478
|
+
strategy_override: z2.enum(["fixed", "recursive", "semantic", "hierarchical", "adaptive"]).optional(),
|
|
7479
|
+
profile_config: z2.record(z2.any()).optional()
|
|
6921
7480
|
},
|
|
6922
7481
|
async ({ project, title, content, ingestion_profile, strategy_override, profile_config }) => {
|
|
6923
7482
|
try {
|
|
@@ -6945,18 +7504,18 @@ server.tool(
|
|
|
6945
7504
|
"context.add_document",
|
|
6946
7505
|
"Compatibility learning tool. Ingest a document into project knowledge. Supports plain text and video URLs. Prefer `learn` for new integrations.",
|
|
6947
7506
|
{
|
|
6948
|
-
project:
|
|
6949
|
-
source_type:
|
|
6950
|
-
title:
|
|
6951
|
-
content:
|
|
6952
|
-
url:
|
|
6953
|
-
auto_sync:
|
|
6954
|
-
tags:
|
|
6955
|
-
platform:
|
|
6956
|
-
language:
|
|
6957
|
-
ingestion_profile:
|
|
6958
|
-
strategy_override:
|
|
6959
|
-
profile_config:
|
|
7507
|
+
project: z2.string().optional().describe("Project name or slug"),
|
|
7508
|
+
source_type: z2.enum(["text", "video"]).default("text"),
|
|
7509
|
+
title: z2.string().optional().describe("Title for text documents"),
|
|
7510
|
+
content: z2.string().optional().describe("Text document content"),
|
|
7511
|
+
url: z2.string().url().optional().describe("Video URL when source_type=video"),
|
|
7512
|
+
auto_sync: z2.boolean().optional().default(true),
|
|
7513
|
+
tags: z2.array(z2.string()).optional(),
|
|
7514
|
+
platform: z2.enum(["youtube", "loom", "generic"]).optional(),
|
|
7515
|
+
language: z2.string().optional(),
|
|
7516
|
+
ingestion_profile: z2.enum(["auto", "repo", "web_docs", "pdf_layout", "video_transcript", "plain_text"]).optional(),
|
|
7517
|
+
strategy_override: z2.enum(["fixed", "recursive", "semantic", "hierarchical", "adaptive"]).optional(),
|
|
7518
|
+
profile_config: z2.record(z2.any()).optional()
|
|
6960
7519
|
},
|
|
6961
7520
|
async ({ project, source_type, title, content, url, auto_sync, tags, platform, language, ingestion_profile, strategy_override, profile_config }) => {
|
|
6962
7521
|
try {
|
|
@@ -7011,23 +7570,24 @@ server.tool(
|
|
|
7011
7570
|
"memory.search_sota",
|
|
7012
7571
|
"SOTA memory search with temporal reasoning and relation graphs. Searches memories with support for temporal queries ('what did I say yesterday?'), type filtering, and knowledge graph traversal.",
|
|
7013
7572
|
{
|
|
7014
|
-
project:
|
|
7015
|
-
query:
|
|
7016
|
-
|
|
7017
|
-
|
|
7018
|
-
|
|
7019
|
-
|
|
7020
|
-
|
|
7021
|
-
|
|
7573
|
+
project: z2.string().optional().describe("Project name or slug"),
|
|
7574
|
+
query: z2.string().describe("Search query (supports temporal: 'yesterday', 'last week')"),
|
|
7575
|
+
scope: z2.enum(["personal", "shared_project"]).optional().default("personal"),
|
|
7576
|
+
user_id: z2.string().optional().describe("Filter by user"),
|
|
7577
|
+
session_id: z2.string().optional().describe("Filter by session"),
|
|
7578
|
+
question_date: z2.string().optional().describe("ISO datetime for temporal grounding"),
|
|
7579
|
+
memory_types: z2.array(z2.enum(["factual", "preference", "event", "relationship", "opinion", "goal", "instruction"])).optional(),
|
|
7580
|
+
top_k: z2.number().optional().default(10),
|
|
7581
|
+
include_relations: z2.boolean().optional().default(true).describe("Include related memories via knowledge graph")
|
|
7022
7582
|
},
|
|
7023
|
-
async ({ project, query, user_id, session_id, question_date, memory_types, top_k, include_relations }) => {
|
|
7583
|
+
async ({ project, query, scope, user_id, session_id, question_date, memory_types, top_k, include_relations }) => {
|
|
7024
7584
|
try {
|
|
7025
|
-
const
|
|
7585
|
+
const memoryScope = resolveMemoryScope({ project, user_id, session_id, scope });
|
|
7026
7586
|
const results = await whisper.searchMemoriesSOTA({
|
|
7027
|
-
project:
|
|
7587
|
+
project: memoryScope.project,
|
|
7028
7588
|
query,
|
|
7029
|
-
user_id:
|
|
7030
|
-
session_id:
|
|
7589
|
+
user_id: memoryScope.userId,
|
|
7590
|
+
session_id: memoryScope.sessionId,
|
|
7031
7591
|
question_date,
|
|
7032
7592
|
memory_types,
|
|
7033
7593
|
top_k,
|
|
@@ -7037,17 +7597,19 @@ server.tool(
|
|
|
7037
7597
|
return primaryToolSuccess({
|
|
7038
7598
|
tool: "memory.search_sota",
|
|
7039
7599
|
query,
|
|
7040
|
-
|
|
7041
|
-
|
|
7600
|
+
scope: memoryScope.scopeMode,
|
|
7601
|
+
user_id: memoryScope.userId,
|
|
7602
|
+
session_id: memoryScope.sessionId || null,
|
|
7042
7603
|
question_date: question_date || null,
|
|
7043
7604
|
include_relations,
|
|
7044
7605
|
results: normalizedResults,
|
|
7045
7606
|
count: normalizedResults.length,
|
|
7046
7607
|
diagnostics: {
|
|
7047
7608
|
scope: {
|
|
7048
|
-
project:
|
|
7049
|
-
user_id:
|
|
7050
|
-
session_id:
|
|
7609
|
+
project: memoryScope.project || null,
|
|
7610
|
+
user_id: memoryScope.userId,
|
|
7611
|
+
session_id: memoryScope.sessionId || null,
|
|
7612
|
+
scope: memoryScope.scopeMode
|
|
7051
7613
|
}
|
|
7052
7614
|
}
|
|
7053
7615
|
});
|
|
@@ -7060,40 +7622,51 @@ server.tool(
|
|
|
7060
7622
|
"memory.ingest_conversation",
|
|
7061
7623
|
"Compatibility learning tool. Extract memories from a conversation session. Prefer `learn` for new integrations.",
|
|
7062
7624
|
{
|
|
7063
|
-
project:
|
|
7064
|
-
session_id:
|
|
7065
|
-
user_id:
|
|
7066
|
-
messages:
|
|
7067
|
-
role:
|
|
7068
|
-
content:
|
|
7069
|
-
timestamp:
|
|
7625
|
+
project: z2.string().optional().describe("Project name or slug"),
|
|
7626
|
+
session_id: z2.string().describe("Session identifier"),
|
|
7627
|
+
user_id: z2.string().optional().describe("User identifier"),
|
|
7628
|
+
messages: z2.array(z2.object({
|
|
7629
|
+
role: z2.string(),
|
|
7630
|
+
content: z2.string(),
|
|
7631
|
+
timestamp: z2.string()
|
|
7070
7632
|
})).describe("Array of conversation messages with timestamps")
|
|
7071
7633
|
},
|
|
7072
7634
|
async ({ project, session_id, user_id, messages }) => {
|
|
7073
7635
|
try {
|
|
7074
|
-
const
|
|
7636
|
+
const memoryScope = resolveMemoryScope({
|
|
7637
|
+
project,
|
|
7638
|
+
user_id,
|
|
7639
|
+
session_id,
|
|
7640
|
+
scope: "personal"
|
|
7641
|
+
});
|
|
7075
7642
|
const normalizedMessages = messages.map((message) => ({
|
|
7076
7643
|
role: message.role,
|
|
7077
7644
|
content: message.content,
|
|
7078
7645
|
timestamp: message.timestamp
|
|
7079
7646
|
}));
|
|
7080
7647
|
const result = await ingestSessionWithSyncFallback({
|
|
7081
|
-
project:
|
|
7082
|
-
session_id:
|
|
7083
|
-
user_id:
|
|
7648
|
+
project: memoryScope.project,
|
|
7649
|
+
session_id: memoryScope.sessionId,
|
|
7650
|
+
user_id: memoryScope.userId,
|
|
7084
7651
|
messages: normalizedMessages
|
|
7085
7652
|
});
|
|
7086
|
-
return {
|
|
7087
|
-
|
|
7088
|
-
|
|
7089
|
-
|
|
7090
|
-
|
|
7091
|
-
|
|
7092
|
-
|
|
7093
|
-
|
|
7094
|
-
|
|
7095
|
-
|
|
7096
|
-
|
|
7653
|
+
return primaryToolSuccess({
|
|
7654
|
+
tool: "memory.ingest_conversation",
|
|
7655
|
+
messages_processed: normalizedMessages.length,
|
|
7656
|
+
memories_created: result.memories_created,
|
|
7657
|
+
relations_created: result.relations_created,
|
|
7658
|
+
memories_invalidated: result.memories_invalidated,
|
|
7659
|
+
errors: result.errors || [],
|
|
7660
|
+
scope: memoryScope.scopeMode,
|
|
7661
|
+
diagnostics: {
|
|
7662
|
+
scope: {
|
|
7663
|
+
project: memoryScope.project || null,
|
|
7664
|
+
user_id: memoryScope.userId,
|
|
7665
|
+
session_id: memoryScope.sessionId || null,
|
|
7666
|
+
scope: memoryScope.scopeMode
|
|
7667
|
+
}
|
|
7668
|
+
}
|
|
7669
|
+
});
|
|
7097
7670
|
} catch (error) {
|
|
7098
7671
|
return { content: [{ type: "text", text: `Error: ${error.message}` }] };
|
|
7099
7672
|
}
|
|
@@ -7103,11 +7676,11 @@ server.tool(
|
|
|
7103
7676
|
"research.oracle",
|
|
7104
7677
|
"Oracle Research Mode - Tree-guided document navigation with multi-step reasoning. More precise than standard search, especially for bleeding-edge features.",
|
|
7105
7678
|
{
|
|
7106
|
-
project:
|
|
7107
|
-
query:
|
|
7108
|
-
mode:
|
|
7109
|
-
max_results:
|
|
7110
|
-
max_steps:
|
|
7679
|
+
project: z2.string().optional().describe("Project name or slug"),
|
|
7680
|
+
query: z2.string().describe("Research question"),
|
|
7681
|
+
mode: z2.enum(["search", "research"]).optional().default("search").describe("'search' for tree-guided, 'research' for multi-step reasoning"),
|
|
7682
|
+
max_results: z2.number().optional().default(5),
|
|
7683
|
+
max_steps: z2.number().optional().default(5).describe("For research mode: max reasoning steps")
|
|
7111
7684
|
},
|
|
7112
7685
|
async ({ project, query, mode, max_results, max_steps }) => {
|
|
7113
7686
|
try {
|
|
@@ -7151,13 +7724,13 @@ server.tool(
|
|
|
7151
7724
|
"index.autosubscribe_deps",
|
|
7152
7725
|
"Automatically index a project's dependencies (package.json, requirements.txt, etc.). Resolves docs URLs and indexes documentation.",
|
|
7153
7726
|
{
|
|
7154
|
-
project:
|
|
7155
|
-
source_type:
|
|
7156
|
-
github_owner:
|
|
7157
|
-
github_repo:
|
|
7158
|
-
local_path:
|
|
7159
|
-
dependency_file:
|
|
7160
|
-
index_limit:
|
|
7727
|
+
project: z2.string().optional().describe("Project name or slug"),
|
|
7728
|
+
source_type: z2.enum(["github", "local"]).describe("Source location"),
|
|
7729
|
+
github_owner: z2.string().optional().describe("For GitHub: owner/org name"),
|
|
7730
|
+
github_repo: z2.string().optional().describe("For GitHub: repository name"),
|
|
7731
|
+
local_path: z2.string().optional().describe("For local: path to dependency file"),
|
|
7732
|
+
dependency_file: z2.enum(["package.json", "requirements.txt", "Cargo.toml", "go.mod", "Gemfile"]).optional(),
|
|
7733
|
+
index_limit: z2.number().optional().default(20).describe("Max dependencies to index")
|
|
7161
7734
|
},
|
|
7162
7735
|
async ({ project, source_type, github_owner, github_repo, local_path, dependency_file, index_limit }) => {
|
|
7163
7736
|
try {
|
|
@@ -7184,10 +7757,10 @@ server.tool(
|
|
|
7184
7757
|
"context.share",
|
|
7185
7758
|
"Create a shareable snapshot of a conversation with memories. Returns a URL that can be shared or resumed later.",
|
|
7186
7759
|
{
|
|
7187
|
-
project:
|
|
7188
|
-
session_id:
|
|
7189
|
-
title:
|
|
7190
|
-
expiry_days:
|
|
7760
|
+
project: z2.string().optional().describe("Project name or slug"),
|
|
7761
|
+
session_id: z2.string().describe("Session to share"),
|
|
7762
|
+
title: z2.string().optional().describe("Title for the shared context"),
|
|
7763
|
+
expiry_days: z2.number().optional().default(30).describe("Days until expiry")
|
|
7191
7764
|
},
|
|
7192
7765
|
async ({ project, session_id, title, expiry_days }) => {
|
|
7193
7766
|
try {
|
|
@@ -7222,9 +7795,9 @@ server.tool(
|
|
|
7222
7795
|
"memory.consolidate",
|
|
7223
7796
|
"Find and merge duplicate memories to reduce bloat. Uses vector similarity + LLM merging.",
|
|
7224
7797
|
{
|
|
7225
|
-
project:
|
|
7226
|
-
similarity_threshold:
|
|
7227
|
-
dry_run:
|
|
7798
|
+
project: z2.string().optional().describe("Project name or slug"),
|
|
7799
|
+
similarity_threshold: z2.number().optional().default(0.95).describe("Similarity threshold (0-1)"),
|
|
7800
|
+
dry_run: z2.boolean().optional().default(false).describe("Preview without merging")
|
|
7228
7801
|
},
|
|
7229
7802
|
async ({ project, similarity_threshold, dry_run }) => {
|
|
7230
7803
|
try {
|
|
@@ -7265,8 +7838,8 @@ server.tool(
|
|
|
7265
7838
|
"context.cost_summary",
|
|
7266
7839
|
"Get cost tracking summary showing spending by model and task. Includes savings vs always-Opus.",
|
|
7267
7840
|
{
|
|
7268
|
-
project:
|
|
7269
|
-
days:
|
|
7841
|
+
project: z2.string().optional().describe("Project name or slug (optional for org-wide)"),
|
|
7842
|
+
days: z2.number().optional().default(30).describe("Time period in days")
|
|
7270
7843
|
},
|
|
7271
7844
|
async ({ project, days }) => {
|
|
7272
7845
|
try {
|
|
@@ -7308,16 +7881,19 @@ server.tool(
|
|
|
7308
7881
|
"memory.forget",
|
|
7309
7882
|
"Delete or invalidate memories with immutable audit logging.",
|
|
7310
7883
|
{
|
|
7311
|
-
workspace_id:
|
|
7312
|
-
project:
|
|
7313
|
-
|
|
7314
|
-
|
|
7315
|
-
|
|
7884
|
+
workspace_id: z2.string().optional(),
|
|
7885
|
+
project: z2.string().optional(),
|
|
7886
|
+
scope: z2.enum(["personal", "shared_project"]).optional().default("personal"),
|
|
7887
|
+
user_id: z2.string().optional(),
|
|
7888
|
+
session_id: z2.string().optional(),
|
|
7889
|
+
target: z2.object({
|
|
7890
|
+
memory_id: z2.string().optional(),
|
|
7891
|
+
query: z2.string().optional()
|
|
7316
7892
|
}),
|
|
7317
|
-
mode:
|
|
7318
|
-
reason:
|
|
7893
|
+
mode: z2.enum(["delete", "invalidate"]).optional().default("invalidate"),
|
|
7894
|
+
reason: z2.string().optional()
|
|
7319
7895
|
},
|
|
7320
|
-
async ({ workspace_id, project, target, mode, reason }) => {
|
|
7896
|
+
async ({ workspace_id, project, scope, user_id, session_id, target, mode, reason }) => {
|
|
7321
7897
|
try {
|
|
7322
7898
|
if (!target.memory_id && !target.query) {
|
|
7323
7899
|
return { content: [{ type: "text", text: "Error: target.memory_id or target.query is required." }] };
|
|
@@ -7326,8 +7902,22 @@ server.tool(
|
|
|
7326
7902
|
let queryResolution = null;
|
|
7327
7903
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7328
7904
|
const actor = process.env.WHISPER_AGENT_ID || process.env.USERNAME || "api_key_principal";
|
|
7329
|
-
const
|
|
7905
|
+
const memoryScope = resolveMemoryScope({
|
|
7906
|
+
project,
|
|
7907
|
+
user_id,
|
|
7908
|
+
session_id,
|
|
7909
|
+
scope
|
|
7910
|
+
});
|
|
7911
|
+
const resolvedProject = await resolveProjectRef(memoryScope.project);
|
|
7912
|
+
async function assertScopedMemoryId(memoryId) {
|
|
7913
|
+
const memoryResult = await whisper.getMemory(memoryId);
|
|
7914
|
+
const memory = memoryResult?.memory || memoryResult;
|
|
7915
|
+
if (!isMemoryInScope({ memory, memoryScope })) {
|
|
7916
|
+
throw new Error(`memory_id ${memoryId} is outside the effective ${memoryScope.scopeMode} scope.`);
|
|
7917
|
+
}
|
|
7918
|
+
}
|
|
7330
7919
|
async function applyToMemory(memoryId) {
|
|
7920
|
+
await assertScopedMemoryId(memoryId);
|
|
7331
7921
|
if (mode === "delete") {
|
|
7332
7922
|
await whisper.deleteMemory(memoryId);
|
|
7333
7923
|
} else {
|
|
@@ -7342,7 +7932,10 @@ server.tool(
|
|
|
7342
7932
|
project: resolvedProject,
|
|
7343
7933
|
content: `Invalidated memory ${memoryId} at ${now}. Reason: ${reason || "No reason provided"}`,
|
|
7344
7934
|
memory_type: "instruction",
|
|
7345
|
-
|
|
7935
|
+
user_id: memoryScope.userId,
|
|
7936
|
+
session_id: memoryScope.sessionId,
|
|
7937
|
+
importance: 1,
|
|
7938
|
+
metadata: buildMemoryScopeMetadata({ memoryScope })
|
|
7346
7939
|
});
|
|
7347
7940
|
}
|
|
7348
7941
|
}
|
|
@@ -7374,13 +7967,25 @@ server.tool(
|
|
|
7374
7967
|
mode: audit2.mode,
|
|
7375
7968
|
...audit2.reason ? { reason: audit2.reason } : {}
|
|
7376
7969
|
},
|
|
7377
|
-
warning: "No project resolved for query-based forget. Nothing was changed."
|
|
7970
|
+
warning: "No project resolved for query-based forget. Nothing was changed.",
|
|
7971
|
+
diagnostics: {
|
|
7972
|
+
scope: {
|
|
7973
|
+
project: memoryScope.project || null,
|
|
7974
|
+
user_id: memoryScope.userId,
|
|
7975
|
+
session_id: memoryScope.sessionId || null,
|
|
7976
|
+
scope: memoryScope.scopeMode,
|
|
7977
|
+
actor_user_id: memoryScope.actorUserId,
|
|
7978
|
+
actor_session_id: memoryScope.actorSessionId || null
|
|
7979
|
+
}
|
|
7980
|
+
}
|
|
7378
7981
|
};
|
|
7379
7982
|
return { content: [{ type: "text", text: JSON.stringify(payload2, null, 2) }] };
|
|
7380
7983
|
}
|
|
7381
7984
|
const search = await whisper.searchMemoriesSOTA({
|
|
7382
7985
|
project: resolvedProject,
|
|
7383
7986
|
query: target.query || "",
|
|
7987
|
+
user_id: memoryScope.userId,
|
|
7988
|
+
session_id: memoryScope.sessionId,
|
|
7384
7989
|
top_k: 25,
|
|
7385
7990
|
include_relations: false,
|
|
7386
7991
|
include_pending: true
|
|
@@ -7393,7 +7998,17 @@ server.tool(
|
|
|
7393
7998
|
status: "completed",
|
|
7394
7999
|
affected_ids: affectedIds,
|
|
7395
8000
|
warning: resolved.warning || "Query did not resolve to a reliable memory match. No memories were changed.",
|
|
7396
|
-
resolved_by: resolved.resolved_by
|
|
8001
|
+
resolved_by: resolved.resolved_by,
|
|
8002
|
+
diagnostics: {
|
|
8003
|
+
scope: {
|
|
8004
|
+
project: resolvedProject,
|
|
8005
|
+
user_id: memoryScope.userId,
|
|
8006
|
+
session_id: memoryScope.sessionId || null,
|
|
8007
|
+
scope: memoryScope.scopeMode,
|
|
8008
|
+
actor_user_id: memoryScope.actorUserId,
|
|
8009
|
+
actor_session_id: memoryScope.actorSessionId || null
|
|
8010
|
+
}
|
|
8011
|
+
}
|
|
7397
8012
|
};
|
|
7398
8013
|
return { content: [{ type: "text", text: JSON.stringify(payload2, null, 2) }] };
|
|
7399
8014
|
}
|
|
@@ -7422,6 +8037,16 @@ server.tool(
|
|
|
7422
8037
|
called_at: audit.called_at,
|
|
7423
8038
|
mode: audit.mode,
|
|
7424
8039
|
...audit.reason ? { reason: audit.reason } : {}
|
|
8040
|
+
},
|
|
8041
|
+
diagnostics: {
|
|
8042
|
+
scope: {
|
|
8043
|
+
project: resolvedProject || null,
|
|
8044
|
+
user_id: memoryScope.userId,
|
|
8045
|
+
session_id: memoryScope.sessionId || null,
|
|
8046
|
+
scope: memoryScope.scopeMode,
|
|
8047
|
+
actor_user_id: memoryScope.actorUserId,
|
|
8048
|
+
actor_session_id: memoryScope.actorSessionId || null
|
|
8049
|
+
}
|
|
7425
8050
|
}
|
|
7426
8051
|
};
|
|
7427
8052
|
return { content: [{ type: "text", text: JSON.stringify(payload, null, 2) }] };
|
|
@@ -7434,8 +8059,8 @@ server.tool(
|
|
|
7434
8059
|
"context.export_bundle",
|
|
7435
8060
|
"Export project/workspace memory and context to a portable bundle with checksum.",
|
|
7436
8061
|
{
|
|
7437
|
-
workspace_id:
|
|
7438
|
-
project:
|
|
8062
|
+
workspace_id: z2.string().optional(),
|
|
8063
|
+
project: z2.string().optional()
|
|
7439
8064
|
},
|
|
7440
8065
|
async ({ workspace_id, project }) => {
|
|
7441
8066
|
try {
|
|
@@ -7485,26 +8110,26 @@ server.tool(
|
|
|
7485
8110
|
"context.import_bundle",
|
|
7486
8111
|
"Import a portable context bundle with merge/replace modes and checksum verification.",
|
|
7487
8112
|
{
|
|
7488
|
-
workspace_id:
|
|
7489
|
-
bundle:
|
|
7490
|
-
bundle_version:
|
|
7491
|
-
workspace_id:
|
|
7492
|
-
project:
|
|
7493
|
-
exported_at:
|
|
7494
|
-
contents:
|
|
7495
|
-
memories:
|
|
7496
|
-
entities:
|
|
7497
|
-
decisions:
|
|
7498
|
-
failures:
|
|
7499
|
-
annotations:
|
|
7500
|
-
session_summaries:
|
|
7501
|
-
documents:
|
|
7502
|
-
index_metadata:
|
|
8113
|
+
workspace_id: z2.string().optional(),
|
|
8114
|
+
bundle: z2.object({
|
|
8115
|
+
bundle_version: z2.string(),
|
|
8116
|
+
workspace_id: z2.string(),
|
|
8117
|
+
project: z2.string().optional(),
|
|
8118
|
+
exported_at: z2.string(),
|
|
8119
|
+
contents: z2.object({
|
|
8120
|
+
memories: z2.array(z2.any()).default([]),
|
|
8121
|
+
entities: z2.array(z2.any()).default([]),
|
|
8122
|
+
decisions: z2.array(z2.any()).default([]),
|
|
8123
|
+
failures: z2.array(z2.any()).default([]),
|
|
8124
|
+
annotations: z2.array(z2.any()).default([]),
|
|
8125
|
+
session_summaries: z2.array(z2.any()).default([]),
|
|
8126
|
+
documents: z2.array(z2.any()).default([]),
|
|
8127
|
+
index_metadata: z2.record(z2.any()).default({})
|
|
7503
8128
|
}),
|
|
7504
|
-
checksum:
|
|
8129
|
+
checksum: z2.string()
|
|
7505
8130
|
}),
|
|
7506
|
-
mode:
|
|
7507
|
-
dedupe_strategy:
|
|
8131
|
+
mode: z2.enum(["merge", "replace"]).optional().default("merge"),
|
|
8132
|
+
dedupe_strategy: z2.enum(["semantic", "id", "none"]).optional().default("semantic")
|
|
7508
8133
|
},
|
|
7509
8134
|
async ({ workspace_id, bundle, mode, dedupe_strategy }) => {
|
|
7510
8135
|
try {
|
|
@@ -7648,13 +8273,13 @@ server.tool(
|
|
|
7648
8273
|
"context.diff",
|
|
7649
8274
|
"Return deterministic context changes from an explicit anchor (session_id, timestamp, or commit).",
|
|
7650
8275
|
{
|
|
7651
|
-
workspace_id:
|
|
7652
|
-
anchor:
|
|
7653
|
-
type:
|
|
7654
|
-
value:
|
|
8276
|
+
workspace_id: z2.string().optional(),
|
|
8277
|
+
anchor: z2.object({
|
|
8278
|
+
type: z2.enum(["session_id", "timestamp", "commit"]),
|
|
8279
|
+
value: z2.string()
|
|
7655
8280
|
}).optional(),
|
|
7656
|
-
scope:
|
|
7657
|
-
include:
|
|
8281
|
+
scope: z2.object({
|
|
8282
|
+
include: z2.array(z2.enum(["decisions", "failures", "entities", "documents", "summaries"])).optional().default(["decisions", "failures", "entities", "documents", "summaries"])
|
|
7658
8283
|
}).optional()
|
|
7659
8284
|
},
|
|
7660
8285
|
async ({ workspace_id, anchor, scope }) => {
|
|
@@ -7765,12 +8390,12 @@ server.tool(
|
|
|
7765
8390
|
"code.search_semantic",
|
|
7766
8391
|
"Semantically search a local codebase without pre-indexing. Unlike grep/ripgrep, this understands meaning \u2014 so 'find authentication logic' finds auth code even if it doesn't literally say 'auth'. Uses vector embeddings via the Whisper API. Perfect for exploring unfamiliar codebases.",
|
|
7767
8392
|
{
|
|
7768
|
-
query:
|
|
7769
|
-
path:
|
|
7770
|
-
file_types:
|
|
7771
|
-
top_k:
|
|
7772
|
-
threshold:
|
|
7773
|
-
max_files:
|
|
8393
|
+
query: z2.string().describe("Natural language description of what you're looking for. E.g. 'authentication and session management', 'database connection pooling', 'error handling middleware'"),
|
|
8394
|
+
path: z2.string().optional().describe("Absolute path to the codebase root. Defaults to current working directory."),
|
|
8395
|
+
file_types: z2.array(z2.string()).optional().describe("Limit to specific extensions e.g. ['ts', 'py']. Defaults to all common code files."),
|
|
8396
|
+
top_k: z2.number().optional().default(10).describe("Number of most relevant files to return"),
|
|
8397
|
+
threshold: z2.number().optional().default(0.2).describe("Minimum similarity score 0-1. Lower = more results but less precise."),
|
|
8398
|
+
max_files: z2.number().optional().default(150).describe("Max files to scan. For large codebases, narrow with file_types instead of raising this.")
|
|
7774
8399
|
},
|
|
7775
8400
|
async ({ query, path: searchPath, file_types, top_k, threshold, max_files }) => {
|
|
7776
8401
|
try {
|
|
@@ -7845,13 +8470,13 @@ server.tool(
|
|
|
7845
8470
|
"code.search_text",
|
|
7846
8471
|
"Search files and content in a local directory without requiring pre-indexing. Uses ripgrep when available, falls back to Node.js. Great for finding files, functions, patterns, or any text across a codebase instantly.",
|
|
7847
8472
|
{
|
|
7848
|
-
query:
|
|
7849
|
-
path:
|
|
7850
|
-
mode:
|
|
7851
|
-
file_types:
|
|
7852
|
-
max_results:
|
|
7853
|
-
context_lines:
|
|
7854
|
-
case_sensitive:
|
|
8473
|
+
query: z2.string().describe("What to search for \u2014 natural language keyword, function name, pattern, etc."),
|
|
8474
|
+
path: z2.string().optional().describe("Absolute path to search in. Defaults to current working directory."),
|
|
8475
|
+
mode: z2.enum(["content", "filename", "both"]).optional().default("both").describe("Search file contents, filenames, or both"),
|
|
8476
|
+
file_types: z2.array(z2.string()).optional().describe("Limit to these file extensions e.g. ['ts', 'js', 'py', 'go']"),
|
|
8477
|
+
max_results: z2.number().optional().default(20).describe("Max number of matching files to return"),
|
|
8478
|
+
context_lines: z2.number().optional().default(2).describe("Lines of context around each match"),
|
|
8479
|
+
case_sensitive: z2.boolean().optional().default(false)
|
|
7855
8480
|
},
|
|
7856
8481
|
async ({ query, path: searchPath, mode, file_types, max_results, context_lines, case_sensitive }) => {
|
|
7857
8482
|
const rootPath = searchPath || process.cwd();
|
|
@@ -7984,13 +8609,13 @@ server.tool(
|
|
|
7984
8609
|
"code.semantic_documents",
|
|
7985
8610
|
"Semantic vector search over provided documents. Uses embeddings to find semantically similar content. Perfect for AI code search, finding similar functions, or searching by meaning rather than keywords.",
|
|
7986
8611
|
{
|
|
7987
|
-
query:
|
|
7988
|
-
documents:
|
|
7989
|
-
id:
|
|
7990
|
-
content:
|
|
8612
|
+
query: z2.string().describe("What to search for semantically (e.g. 'authentication logic', 'database connection')"),
|
|
8613
|
+
documents: z2.array(z2.object({
|
|
8614
|
+
id: z2.string().describe("Unique identifier (file path, URL, or any ID)"),
|
|
8615
|
+
content: z2.string().describe("The text content to search in")
|
|
7991
8616
|
})).describe("Documents to search over"),
|
|
7992
|
-
top_k:
|
|
7993
|
-
threshold:
|
|
8617
|
+
top_k: z2.number().optional().default(5).describe("Number of results to return"),
|
|
8618
|
+
threshold: z2.number().optional().default(0.3).describe("Minimum similarity score 0-1")
|
|
7994
8619
|
},
|
|
7995
8620
|
async ({ query, documents, top_k, threshold }) => {
|
|
7996
8621
|
try {
|
|
@@ -8028,14 +8653,14 @@ server.tool(
|
|
|
8028
8653
|
"search",
|
|
8029
8654
|
"Primary retrieval alias. Call this whenever the user asks to find or recall context: use `query` for semantic retrieval, `id` for exact memory fetch, or both for hybrid recall.",
|
|
8030
8655
|
{
|
|
8031
|
-
project:
|
|
8032
|
-
query:
|
|
8033
|
-
id:
|
|
8034
|
-
top_k:
|
|
8035
|
-
include_memories:
|
|
8036
|
-
include_graph:
|
|
8037
|
-
user_id:
|
|
8038
|
-
session_id:
|
|
8656
|
+
project: z2.string().optional().describe("Project name or slug"),
|
|
8657
|
+
query: z2.string().optional().describe("Semantic retrieval query"),
|
|
8658
|
+
id: z2.string().optional().describe("Exact memory id to fetch"),
|
|
8659
|
+
top_k: z2.number().optional().default(10),
|
|
8660
|
+
include_memories: z2.boolean().optional().default(false),
|
|
8661
|
+
include_graph: z2.boolean().optional().default(false),
|
|
8662
|
+
user_id: z2.string().optional(),
|
|
8663
|
+
session_id: z2.string().optional()
|
|
8039
8664
|
},
|
|
8040
8665
|
async ({ project, query, id, top_k, include_memories, include_graph, user_id, session_id }) => {
|
|
8041
8666
|
try {
|
|
@@ -8099,12 +8724,12 @@ server.tool(
|
|
|
8099
8724
|
"search_code",
|
|
8100
8725
|
"Semantically search a local codebase by meaning. Use this for questions like 'where is auth handled?' or 'find retry logic'.",
|
|
8101
8726
|
{
|
|
8102
|
-
query:
|
|
8103
|
-
path:
|
|
8104
|
-
file_types:
|
|
8105
|
-
top_k:
|
|
8106
|
-
threshold:
|
|
8107
|
-
max_files:
|
|
8727
|
+
query: z2.string().describe("Natural-language code search query"),
|
|
8728
|
+
path: z2.string().optional().describe("Codebase root. Defaults to current working directory."),
|
|
8729
|
+
file_types: z2.array(z2.string()).optional(),
|
|
8730
|
+
top_k: z2.number().optional().default(10),
|
|
8731
|
+
threshold: z2.number().optional().default(0.2),
|
|
8732
|
+
max_files: z2.number().optional().default(150)
|
|
8108
8733
|
},
|
|
8109
8734
|
async ({ query, path, file_types, top_k, threshold, max_files }) => {
|
|
8110
8735
|
try {
|
|
@@ -8125,11 +8750,11 @@ server.tool(
|
|
|
8125
8750
|
"grep",
|
|
8126
8751
|
"Regex or text search across a local codebase. Use this when you know the symbol, string, or pattern you want.",
|
|
8127
8752
|
{
|
|
8128
|
-
query:
|
|
8129
|
-
path:
|
|
8130
|
-
file_types:
|
|
8131
|
-
max_results:
|
|
8132
|
-
case_sensitive:
|
|
8753
|
+
query: z2.string().describe("Text or regex-like pattern to search for"),
|
|
8754
|
+
path: z2.string().optional().describe("Search root. Defaults to current working directory."),
|
|
8755
|
+
file_types: z2.array(z2.string()).optional(),
|
|
8756
|
+
max_results: z2.number().optional().default(20),
|
|
8757
|
+
case_sensitive: z2.boolean().optional().default(false)
|
|
8133
8758
|
},
|
|
8134
8759
|
async ({ query, path, file_types, max_results, case_sensitive }) => {
|
|
8135
8760
|
const rootPath = path || process.cwd();
|
|
@@ -8177,9 +8802,9 @@ server.tool(
|
|
|
8177
8802
|
"read",
|
|
8178
8803
|
"Read a local file with optional line ranges. Use this after search or grep when you want the actual source.",
|
|
8179
8804
|
{
|
|
8180
|
-
path:
|
|
8181
|
-
start_line:
|
|
8182
|
-
end_line:
|
|
8805
|
+
path: z2.string().describe("Absolute or relative path to the file to read."),
|
|
8806
|
+
start_line: z2.number().optional().default(1),
|
|
8807
|
+
end_line: z2.number().optional().default(200)
|
|
8183
8808
|
},
|
|
8184
8809
|
async ({ path, start_line, end_line }) => {
|
|
8185
8810
|
try {
|
|
@@ -8204,9 +8829,9 @@ server.tool(
|
|
|
8204
8829
|
"explore",
|
|
8205
8830
|
"Browse a repository tree or directory structure. Use this to orient yourself before reading files.",
|
|
8206
8831
|
{
|
|
8207
|
-
path:
|
|
8208
|
-
max_depth:
|
|
8209
|
-
max_entries:
|
|
8832
|
+
path: z2.string().optional().describe("Root directory to inspect. Defaults to current working directory."),
|
|
8833
|
+
max_depth: z2.number().optional().default(3),
|
|
8834
|
+
max_entries: z2.number().optional().default(200)
|
|
8210
8835
|
},
|
|
8211
8836
|
async ({ path, max_depth, max_entries }) => {
|
|
8212
8837
|
try {
|
|
@@ -8235,11 +8860,11 @@ server.tool(
|
|
|
8235
8860
|
"research",
|
|
8236
8861
|
"Run deeper research over indexed sources. Use this when search is not enough and you want synthesis or multi-step investigation.",
|
|
8237
8862
|
{
|
|
8238
|
-
project:
|
|
8239
|
-
query:
|
|
8240
|
-
mode:
|
|
8241
|
-
max_results:
|
|
8242
|
-
max_steps:
|
|
8863
|
+
project: z2.string().optional(),
|
|
8864
|
+
query: z2.string().describe("Research question"),
|
|
8865
|
+
mode: z2.enum(["search", "research"]).optional().default("research"),
|
|
8866
|
+
max_results: z2.number().optional().default(5),
|
|
8867
|
+
max_steps: z2.number().optional().default(5)
|
|
8243
8868
|
},
|
|
8244
8869
|
async ({ project, query, mode, max_results, max_steps }) => {
|
|
8245
8870
|
try {
|
|
@@ -8261,27 +8886,27 @@ server.tool(
|
|
|
8261
8886
|
"index",
|
|
8262
8887
|
"Administrative indexing tool. Call this when retrieval is stale/missing or the user asks to connect new data. Use action='source' to add GitHub/web/pdf/local/slack/video, action='workspace' to refresh local workspace metadata.",
|
|
8263
8888
|
{
|
|
8264
|
-
action:
|
|
8265
|
-
project:
|
|
8266
|
-
type:
|
|
8267
|
-
name:
|
|
8268
|
-
owner:
|
|
8269
|
-
repo:
|
|
8270
|
-
branch:
|
|
8271
|
-
url:
|
|
8272
|
-
file_path:
|
|
8273
|
-
path:
|
|
8274
|
-
glob:
|
|
8275
|
-
max_files:
|
|
8276
|
-
channel_ids:
|
|
8277
|
-
token:
|
|
8278
|
-
workspace_id:
|
|
8279
|
-
mode:
|
|
8280
|
-
platform:
|
|
8281
|
-
language:
|
|
8282
|
-
allow_stt_fallback:
|
|
8283
|
-
max_duration_minutes:
|
|
8284
|
-
max_chunks:
|
|
8889
|
+
action: z2.enum(["source", "workspace"]).default("source"),
|
|
8890
|
+
project: z2.string().optional(),
|
|
8891
|
+
type: z2.enum(["github", "web", "playwright", "pdf", "local", "slack", "video"]).optional(),
|
|
8892
|
+
name: z2.string().optional(),
|
|
8893
|
+
owner: z2.string().optional(),
|
|
8894
|
+
repo: z2.string().optional(),
|
|
8895
|
+
branch: z2.string().optional(),
|
|
8896
|
+
url: z2.string().optional(),
|
|
8897
|
+
file_path: z2.string().optional(),
|
|
8898
|
+
path: z2.string().optional(),
|
|
8899
|
+
glob: z2.string().optional(),
|
|
8900
|
+
max_files: z2.number().optional(),
|
|
8901
|
+
channel_ids: z2.array(z2.string()).optional(),
|
|
8902
|
+
token: z2.string().optional(),
|
|
8903
|
+
workspace_id: z2.string().optional(),
|
|
8904
|
+
mode: z2.enum(["full", "incremental"]).optional().default("incremental"),
|
|
8905
|
+
platform: z2.enum(["youtube", "loom", "generic"]).optional(),
|
|
8906
|
+
language: z2.string().optional(),
|
|
8907
|
+
allow_stt_fallback: z2.boolean().optional(),
|
|
8908
|
+
max_duration_minutes: z2.number().optional(),
|
|
8909
|
+
max_chunks: z2.number().optional()
|
|
8285
8910
|
},
|
|
8286
8911
|
async (input) => {
|
|
8287
8912
|
try {
|
|
@@ -8342,25 +8967,30 @@ server.tool(
|
|
|
8342
8967
|
"remember",
|
|
8343
8968
|
"Call this whenever the user states a durable preference, decision, instruction, or personal/project fact that should persist across sessions. Save proactively without waiting for an explicit 'remember this'.",
|
|
8344
8969
|
{
|
|
8345
|
-
project:
|
|
8346
|
-
content:
|
|
8347
|
-
memory_type:
|
|
8348
|
-
|
|
8349
|
-
|
|
8350
|
-
|
|
8351
|
-
|
|
8970
|
+
project: z2.string().optional(),
|
|
8971
|
+
content: z2.string().describe("Memory content"),
|
|
8972
|
+
memory_type: z2.enum(["factual", "preference", "event", "relationship", "opinion", "goal", "instruction"]).optional().default("factual"),
|
|
8973
|
+
scope: z2.enum(["personal", "shared_project"]).optional().default("personal"),
|
|
8974
|
+
user_id: z2.string().optional(),
|
|
8975
|
+
session_id: z2.string().optional(),
|
|
8976
|
+
agent_id: z2.string().optional(),
|
|
8977
|
+
importance: z2.number().optional().default(0.5)
|
|
8352
8978
|
},
|
|
8353
|
-
async ({ project, content, memory_type, user_id, session_id, agent_id, importance }) => {
|
|
8979
|
+
async ({ project, content, memory_type, scope, user_id, session_id, agent_id, importance }) => {
|
|
8354
8980
|
try {
|
|
8355
|
-
const
|
|
8981
|
+
const memoryScope = resolveMemoryScope({ project, user_id, session_id, scope });
|
|
8356
8982
|
const result = await whisper.addMemory({
|
|
8357
|
-
project:
|
|
8983
|
+
project: memoryScope.project,
|
|
8358
8984
|
content,
|
|
8359
8985
|
memory_type,
|
|
8360
|
-
user_id:
|
|
8361
|
-
session_id:
|
|
8986
|
+
user_id: memoryScope.userId,
|
|
8987
|
+
session_id: memoryScope.sessionId,
|
|
8362
8988
|
agent_id,
|
|
8363
|
-
importance
|
|
8989
|
+
importance,
|
|
8990
|
+
metadata: buildMemoryScopeMetadata({
|
|
8991
|
+
memoryScope,
|
|
8992
|
+
agent_id
|
|
8993
|
+
})
|
|
8364
8994
|
});
|
|
8365
8995
|
const memoryId = result?.memory_id || (result.mode === "sync" ? result.id : null);
|
|
8366
8996
|
const jobId = result?.job_id || (result.mode === "async" ? result.id : null);
|
|
@@ -8371,13 +9001,17 @@ server.tool(
|
|
|
8371
9001
|
job_id: jobId,
|
|
8372
9002
|
mode: result?.mode || null,
|
|
8373
9003
|
memory_type,
|
|
9004
|
+
scope: memoryScope.scopeMode,
|
|
8374
9005
|
stored: result.success === true,
|
|
8375
9006
|
queued: result?.mode === "async" || Boolean(jobId),
|
|
8376
9007
|
diagnostics: {
|
|
8377
9008
|
scope: {
|
|
8378
|
-
project:
|
|
8379
|
-
user_id:
|
|
8380
|
-
session_id:
|
|
9009
|
+
project: memoryScope.project || null,
|
|
9010
|
+
user_id: memoryScope.userId,
|
|
9011
|
+
session_id: memoryScope.sessionId || null,
|
|
9012
|
+
scope: memoryScope.scopeMode,
|
|
9013
|
+
actor_user_id: memoryScope.actorUserId,
|
|
9014
|
+
actor_session_id: memoryScope.actorSessionId || null
|
|
8381
9015
|
}
|
|
8382
9016
|
}
|
|
8383
9017
|
});
|
|
@@ -8390,17 +9024,17 @@ server.tool(
|
|
|
8390
9024
|
"record",
|
|
8391
9025
|
"Record what just happened in a session or conversation. Use this for temporal capture, not durable preference storage.",
|
|
8392
9026
|
{
|
|
8393
|
-
project:
|
|
8394
|
-
session_id:
|
|
8395
|
-
user_id:
|
|
8396
|
-
messages:
|
|
8397
|
-
role:
|
|
8398
|
-
content:
|
|
8399
|
-
timestamp:
|
|
9027
|
+
project: z2.string().optional(),
|
|
9028
|
+
session_id: z2.string().describe("Session to record into"),
|
|
9029
|
+
user_id: z2.string().optional(),
|
|
9030
|
+
messages: z2.array(z2.object({
|
|
9031
|
+
role: z2.string().optional(),
|
|
9032
|
+
content: z2.string(),
|
|
9033
|
+
timestamp: z2.string().optional()
|
|
8400
9034
|
})).optional(),
|
|
8401
|
-
role:
|
|
8402
|
-
content:
|
|
8403
|
-
timestamp:
|
|
9035
|
+
role: z2.string().optional(),
|
|
9036
|
+
content: z2.string().optional(),
|
|
9037
|
+
timestamp: z2.string().optional()
|
|
8404
9038
|
},
|
|
8405
9039
|
async ({ project, session_id, user_id, messages, role, content, timestamp }) => {
|
|
8406
9040
|
try {
|
|
@@ -8415,29 +9049,31 @@ server.tool(
|
|
|
8415
9049
|
timestamp
|
|
8416
9050
|
});
|
|
8417
9051
|
const resolvedProject = await resolveProjectRef(project);
|
|
8418
|
-
const
|
|
9052
|
+
const memoryScope = resolveMemoryScope({
|
|
8419
9053
|
project: resolvedProject || project,
|
|
8420
9054
|
user_id,
|
|
8421
|
-
session_id
|
|
9055
|
+
session_id,
|
|
9056
|
+
scope: "personal"
|
|
8422
9057
|
});
|
|
8423
9058
|
const result = await ingestSessionWithSyncFallback({
|
|
8424
|
-
project:
|
|
8425
|
-
session_id:
|
|
8426
|
-
user_id:
|
|
9059
|
+
project: memoryScope.project,
|
|
9060
|
+
session_id: memoryScope.sessionId || session_id,
|
|
9061
|
+
user_id: memoryScope.userId,
|
|
8427
9062
|
messages: normalizedMessages
|
|
8428
9063
|
});
|
|
8429
9064
|
return primaryToolSuccess({
|
|
8430
9065
|
tool: "record",
|
|
8431
|
-
session_id:
|
|
8432
|
-
user_id:
|
|
9066
|
+
session_id: memoryScope.sessionId || session_id,
|
|
9067
|
+
user_id: memoryScope.userId,
|
|
8433
9068
|
messages_recorded: normalizedMessages.length,
|
|
8434
9069
|
memories_created: result.memories_created,
|
|
8435
9070
|
relations_created: result.relations_created,
|
|
8436
9071
|
diagnostics: {
|
|
8437
9072
|
scope: {
|
|
8438
|
-
project:
|
|
8439
|
-
user_id:
|
|
8440
|
-
session_id:
|
|
9073
|
+
project: memoryScope.project || null,
|
|
9074
|
+
user_id: memoryScope.userId,
|
|
9075
|
+
session_id: memoryScope.sessionId || session_id,
|
|
9076
|
+
scope: memoryScope.scopeMode
|
|
8441
9077
|
}
|
|
8442
9078
|
}
|
|
8443
9079
|
});
|
|
@@ -8450,60 +9086,62 @@ server.tool(
|
|
|
8450
9086
|
"learn",
|
|
8451
9087
|
"Unified ingestion entrypoint. Call this when the user asks to import knowledge: mode='conversation' for chat logs, mode='text' for raw text, mode='source' for external sources to index. Prefer this over legacy compatibility tools.",
|
|
8452
9088
|
{
|
|
8453
|
-
mode:
|
|
8454
|
-
project:
|
|
8455
|
-
user_id:
|
|
8456
|
-
session_id:
|
|
8457
|
-
messages:
|
|
8458
|
-
role:
|
|
8459
|
-
content:
|
|
8460
|
-
timestamp:
|
|
9089
|
+
mode: z2.enum(["conversation", "text", "source"]).describe("What kind of learning to perform"),
|
|
9090
|
+
project: z2.string().optional(),
|
|
9091
|
+
user_id: z2.string().optional().describe("Optional end-user identity for conversation learning"),
|
|
9092
|
+
session_id: z2.string().optional().describe("Session identifier for conversation learning"),
|
|
9093
|
+
messages: z2.array(z2.object({
|
|
9094
|
+
role: z2.string(),
|
|
9095
|
+
content: z2.string(),
|
|
9096
|
+
timestamp: z2.string().optional()
|
|
8461
9097
|
})).optional().describe("Conversation messages to learn from"),
|
|
8462
|
-
title:
|
|
8463
|
-
content:
|
|
8464
|
-
metadata:
|
|
8465
|
-
tags:
|
|
8466
|
-
namespace:
|
|
8467
|
-
type:
|
|
8468
|
-
url:
|
|
8469
|
-
owner:
|
|
8470
|
-
repo:
|
|
8471
|
-
branch:
|
|
8472
|
-
paths:
|
|
8473
|
-
path:
|
|
8474
|
-
file_path:
|
|
8475
|
-
name:
|
|
8476
|
-
channel_ids:
|
|
8477
|
-
since:
|
|
8478
|
-
token:
|
|
8479
|
-
auth_ref:
|
|
8480
|
-
platform:
|
|
8481
|
-
language:
|
|
8482
|
-
options:
|
|
8483
|
-
async:
|
|
8484
|
-
auto_index:
|
|
8485
|
-
ingestion_profile:
|
|
8486
|
-
strategy_override:
|
|
8487
|
-
profile_config:
|
|
8488
|
-
crawl_depth:
|
|
8489
|
-
include_paths:
|
|
8490
|
-
exclude_paths:
|
|
8491
|
-
glob:
|
|
8492
|
-
max_files:
|
|
8493
|
-
max_pages:
|
|
8494
|
-
extract_mode:
|
|
8495
|
-
workspace_id:
|
|
8496
|
-
allow_stt_fallback:
|
|
8497
|
-
max_duration_minutes:
|
|
8498
|
-
max_chunks:
|
|
9098
|
+
title: z2.string().optional().describe("Title for text learning"),
|
|
9099
|
+
content: z2.string().optional().describe("Inline text content to ingest"),
|
|
9100
|
+
metadata: z2.record(z2.any()).optional(),
|
|
9101
|
+
tags: z2.array(z2.string()).optional(),
|
|
9102
|
+
namespace: z2.string().optional(),
|
|
9103
|
+
type: z2.enum(["github", "web", "playwright", "pdf", "local", "slack", "video"]).optional().describe("Source type when mode=source"),
|
|
9104
|
+
url: z2.string().optional().describe("URL to learn from"),
|
|
9105
|
+
owner: z2.string().optional().describe("GitHub owner"),
|
|
9106
|
+
repo: z2.string().optional().describe("GitHub repository"),
|
|
9107
|
+
branch: z2.string().optional(),
|
|
9108
|
+
paths: z2.array(z2.string()).optional(),
|
|
9109
|
+
path: z2.string().optional().describe("Local path to learn from"),
|
|
9110
|
+
file_path: z2.string().optional().describe("Single file path to learn from"),
|
|
9111
|
+
name: z2.string().optional().describe("Optional source name"),
|
|
9112
|
+
channel_ids: z2.array(z2.string()).optional(),
|
|
9113
|
+
since: z2.string().optional(),
|
|
9114
|
+
token: z2.string().optional(),
|
|
9115
|
+
auth_ref: z2.string().optional(),
|
|
9116
|
+
platform: z2.enum(["youtube", "loom", "generic"]).optional(),
|
|
9117
|
+
language: z2.string().optional(),
|
|
9118
|
+
options: z2.object({
|
|
9119
|
+
async: z2.boolean().optional(),
|
|
9120
|
+
auto_index: z2.boolean().optional(),
|
|
9121
|
+
ingestion_profile: z2.enum(["auto", "repo", "web_docs", "pdf_layout", "video_transcript", "plain_text"]).optional(),
|
|
9122
|
+
strategy_override: z2.enum(["fixed", "recursive", "semantic", "hierarchical", "adaptive"]).optional(),
|
|
9123
|
+
profile_config: z2.record(z2.any()).optional(),
|
|
9124
|
+
crawl_depth: z2.number().optional(),
|
|
9125
|
+
include_paths: z2.array(z2.string()).optional(),
|
|
9126
|
+
exclude_paths: z2.array(z2.string()).optional(),
|
|
9127
|
+
glob: z2.string().optional(),
|
|
9128
|
+
max_files: z2.number().optional(),
|
|
9129
|
+
max_pages: z2.number().optional(),
|
|
9130
|
+
extract_mode: z2.enum(["text", "structured", "markdown"]).optional(),
|
|
9131
|
+
workspace_id: z2.string().optional(),
|
|
9132
|
+
allow_stt_fallback: z2.boolean().optional(),
|
|
9133
|
+
max_duration_minutes: z2.number().optional(),
|
|
9134
|
+
max_chunks: z2.number().optional()
|
|
8499
9135
|
}).optional()
|
|
8500
9136
|
},
|
|
8501
9137
|
async (input) => {
|
|
8502
9138
|
try {
|
|
8503
9139
|
const result = await learnFromInput(input);
|
|
9140
|
+
const indexingNote = result.mode === "source" && result.index_started ? "Indexing has started in the background and usually completes in 1\u20132 minutes. Queries may return limited results until then. Run `index.workspace_status` or `context.source_status` to check progress." : void 0;
|
|
8504
9141
|
return primaryToolSuccess({
|
|
8505
9142
|
tool: "learn",
|
|
8506
|
-
...result
|
|
9143
|
+
...result,
|
|
9144
|
+
...indexingNote ? { _note: indexingNote } : {}
|
|
8507
9145
|
});
|
|
8508
9146
|
} catch (error) {
|
|
8509
9147
|
return primaryToolError(error.message);
|
|
@@ -8514,10 +9152,10 @@ server.tool(
|
|
|
8514
9152
|
"share_context",
|
|
8515
9153
|
"Create a shareable snapshot of a session so another agent or teammate can continue with the same context.",
|
|
8516
9154
|
{
|
|
8517
|
-
project:
|
|
8518
|
-
session_id:
|
|
8519
|
-
title:
|
|
8520
|
-
expiry_days:
|
|
9155
|
+
project: z2.string().optional(),
|
|
9156
|
+
session_id: z2.string().describe("Session to share"),
|
|
9157
|
+
title: z2.string().optional(),
|
|
9158
|
+
expiry_days: z2.number().optional().default(30)
|
|
8521
9159
|
},
|
|
8522
9160
|
async ({ project, session_id, title, expiry_days }) => {
|
|
8523
9161
|
try {
|