@memnexus-ai/cli 1.7.220 → 1.7.222
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/commands/memories.d.ts +2 -2
- package/dist/commands/memories.d.ts.map +1 -1
- package/dist/commands/memories.js +190 -91
- package/dist/commands/memories.js.map +1 -1
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/errors.d.ts +47 -0
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +172 -48
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/retry.d.ts +83 -1
- package/dist/lib/retry.d.ts.map +1 -1
- package/dist/lib/retry.js +143 -3
- package/dist/lib/retry.js.map +1 -1
- package/dist/lib/spinner.d.ts +32 -2
- package/dist/lib/spinner.d.ts.map +1 -1
- package/dist/lib/spinner.js +40 -2
- package/dist/lib/spinner.js.map +1 -1
- package/hooks/capture-hooks.test.sh +465 -0
- package/hooks/capture-precompact.sh +11 -1
- package/hooks/capture-session-end.sh +101 -7
- package/package.json +2 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import { isRateLimitError, isServiceUnavailableError, isQuotaError, getRetryAfterMs, sleep } from '../lib/retry';
|
|
2
|
+
import { isRateLimitError, isServiceUnavailableError, isQuotaError, isNameConflictError, getRetryAfterMs, sleep } from '../lib/retry';
|
|
3
3
|
/** Number of deletions within the window that triggers the guard. */
|
|
4
4
|
export declare const BULK_DELETE_GUARD_THRESHOLD = 5;
|
|
5
5
|
/** Rolling window in milliseconds (60 seconds). */
|
|
@@ -67,7 +67,7 @@ export declare function parseBatchIds(values: string[] | undefined): string[];
|
|
|
67
67
|
export declare function isValidIsoDate(value: string): boolean;
|
|
68
68
|
/** Split ids into pages of at most `size` (default 100). */
|
|
69
69
|
export declare function chunkIds(ids: string[], size?: number): string[][];
|
|
70
|
-
export { isRateLimitError, isServiceUnavailableError, isQuotaError, getRetryAfterMs, sleep };
|
|
70
|
+
export { isRateLimitError, isServiceUnavailableError, isQuotaError, isNameConflictError, getRetryAfterMs, sleep, };
|
|
71
71
|
export interface BulkDeleteDeps {
|
|
72
72
|
/** Deletes one page (≤100 ids) and returns the server's counts. */
|
|
73
73
|
deleteFn: (ids: string[]) => Promise<BulkDeleteResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memories.d.ts","sourceRoot":"","sources":["../../src/commands/memories.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"memories.d.ts","sourceRoot":"","sources":["../../src/commands/memories.ts"],"names":[],"mappings":"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"}
|
|
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.StdinTooLargeError = exports.BATCH_CREATE_ALLOWED_KEYS = exports.BATCH_CREATE_MAX_LINE_BYTES = exports.BATCH_CREATE_MAX_STDIN_BYTES = exports.BATCH_CREATE_MAX_RETRIES = exports.BATCH_CREATE_CONCURRENCY_MAX = exports.BATCH_CREATE_CONCURRENCY_MIN = exports.BATCH_CREATE_DEFAULT_CONCURRENCY = exports.sleep = exports.getRetryAfterMs = exports.isQuotaError = exports.isServiceUnavailableError = exports.isRateLimitError = exports.BULK_DELETE_LIST_PAGE_SIZE = exports.BULK_DELETE_MAX_RETRIES = exports.BULK_DELETE_PAGE_PAUSE_MS = exports.BULK_DELETE_MAX_IDS = exports.BULK_DELETE_GUARD_WINDOW_MS = exports.BULK_DELETE_GUARD_THRESHOLD = void 0;
|
|
39
|
+
exports.StdinTooLargeError = exports.BATCH_CREATE_ALLOWED_KEYS = exports.BATCH_CREATE_MAX_LINE_BYTES = exports.BATCH_CREATE_MAX_STDIN_BYTES = exports.BATCH_CREATE_MAX_RETRIES = exports.BATCH_CREATE_CONCURRENCY_MAX = exports.BATCH_CREATE_CONCURRENCY_MIN = exports.BATCH_CREATE_DEFAULT_CONCURRENCY = exports.sleep = exports.getRetryAfterMs = exports.isNameConflictError = exports.isQuotaError = exports.isServiceUnavailableError = exports.isRateLimitError = exports.BULK_DELETE_LIST_PAGE_SIZE = exports.BULK_DELETE_MAX_RETRIES = exports.BULK_DELETE_PAGE_PAUSE_MS = exports.BULK_DELETE_MAX_IDS = exports.BULK_DELETE_GUARD_WINDOW_MS = exports.BULK_DELETE_GUARD_THRESHOLD = void 0;
|
|
40
40
|
exports.getBulkDeleteGuardPath = getBulkDeleteGuardPath;
|
|
41
41
|
exports.readDeleteGuardTimestamps = readDeleteGuardTimestamps;
|
|
42
42
|
exports.recordDeletion = recordDeletion;
|
|
@@ -73,6 +73,7 @@ const retry_1 = require("../lib/retry");
|
|
|
73
73
|
Object.defineProperty(exports, "isRateLimitError", { enumerable: true, get: function () { return retry_1.isRateLimitError; } });
|
|
74
74
|
Object.defineProperty(exports, "isServiceUnavailableError", { enumerable: true, get: function () { return retry_1.isServiceUnavailableError; } });
|
|
75
75
|
Object.defineProperty(exports, "isQuotaError", { enumerable: true, get: function () { return retry_1.isQuotaError; } });
|
|
76
|
+
Object.defineProperty(exports, "isNameConflictError", { enumerable: true, get: function () { return retry_1.isNameConflictError; } });
|
|
76
77
|
Object.defineProperty(exports, "getRetryAfterMs", { enumerable: true, get: function () { return retry_1.getRetryAfterMs; } });
|
|
77
78
|
Object.defineProperty(exports, "sleep", { enumerable: true, get: function () { return retry_1.sleep; } });
|
|
78
79
|
const formatters_1 = require("../lib/formatters");
|
|
@@ -1342,8 +1343,7 @@ function registerMemoriesCommands(program) {
|
|
|
1342
1343
|
if (options.last) {
|
|
1343
1344
|
limit = parseInt(options.last, 10);
|
|
1344
1345
|
if (isNaN(limit) || limit < 1) {
|
|
1345
|
-
|
|
1346
|
-
return;
|
|
1346
|
+
throw (0, errors_1.validationError)('--last must be a positive number');
|
|
1347
1347
|
}
|
|
1348
1348
|
sortField = 'createdAt';
|
|
1349
1349
|
sortOrder = 'desc';
|
|
@@ -1354,17 +1354,14 @@ function registerMemoriesCommands(program) {
|
|
|
1354
1354
|
const validFields = ['createdAt', 'eventTime'];
|
|
1355
1355
|
const validOrders = ['asc', 'desc'];
|
|
1356
1356
|
if (sortParts.length !== 2) {
|
|
1357
|
-
|
|
1358
|
-
return;
|
|
1357
|
+
throw (0, errors_1.validationError)('--sort must be in format field:order (e.g., createdAt:desc)');
|
|
1359
1358
|
}
|
|
1360
1359
|
const [field, order] = sortParts;
|
|
1361
1360
|
if (!validFields.includes(field)) {
|
|
1362
|
-
|
|
1363
|
-
return;
|
|
1361
|
+
throw (0, errors_1.validationError)(`Sort field must be one of: ${validFields.join(', ')}`);
|
|
1364
1362
|
}
|
|
1365
1363
|
if (!validOrders.includes(order)) {
|
|
1366
|
-
|
|
1367
|
-
return;
|
|
1364
|
+
throw (0, errors_1.validationError)(`Sort order must be one of: ${validOrders.join(', ')}`);
|
|
1368
1365
|
}
|
|
1369
1366
|
sortField = field;
|
|
1370
1367
|
sortOrder = order;
|
|
@@ -1387,9 +1384,7 @@ function registerMemoriesCommands(program) {
|
|
|
1387
1384
|
if (recentDuration) {
|
|
1388
1385
|
const recentTs = (0, date_utils_1.getRecentTimestamp)(recentDuration);
|
|
1389
1386
|
if (!recentTs) {
|
|
1390
|
-
|
|
1391
|
-
console.log(chalk_1.default.yellow('Supported formats: 30m, 1h, 7d, 2w'));
|
|
1392
|
-
return;
|
|
1387
|
+
throw (0, errors_1.validationError)(`Invalid duration format: ${recentDuration}. Supported formats: 30m, 1h, 7d, 2w`);
|
|
1393
1388
|
}
|
|
1394
1389
|
listParams.from = recentTs;
|
|
1395
1390
|
}
|
|
@@ -1479,7 +1474,7 @@ function registerMemoriesCommands(program) {
|
|
|
1479
1474
|
}));
|
|
1480
1475
|
const memory = response.data?.data;
|
|
1481
1476
|
if (!memory) {
|
|
1482
|
-
spinner.
|
|
1477
|
+
spinner.softFail(`No memory found with name '${options.name}'`);
|
|
1483
1478
|
return;
|
|
1484
1479
|
}
|
|
1485
1480
|
spinner.succeed(`Found named memory '${options.name}' (v${memory.version || 1})`);
|
|
@@ -1498,14 +1493,12 @@ function registerMemoriesCommands(program) {
|
|
|
1498
1493
|
// Remove duplicates while preserving order
|
|
1499
1494
|
allIds = [...new Set(allIds)].filter((id) => id.trim().length > 0);
|
|
1500
1495
|
if (allIds.length === 0) {
|
|
1501
|
-
|
|
1502
|
-
return;
|
|
1496
|
+
throw (0, errors_1.validationError)('No memory IDs provided');
|
|
1503
1497
|
}
|
|
1504
1498
|
// Discovery options only work with single ID
|
|
1505
1499
|
const hasDiscoveryOptions = options.similar || options.conversation || options.related;
|
|
1506
1500
|
if (hasDiscoveryOptions && allIds.length > 1) {
|
|
1507
|
-
|
|
1508
|
-
return;
|
|
1501
|
+
throw (0, errors_1.validationError)('--similar, --conversation, and --related options only work with a single memory ID');
|
|
1509
1502
|
}
|
|
1510
1503
|
// Single ID mode - use existing logic with short ID support
|
|
1511
1504
|
if (allIds.length === 1) {
|
|
@@ -1521,7 +1514,7 @@ function registerMemoriesCommands(program) {
|
|
|
1521
1514
|
}));
|
|
1522
1515
|
const memory = response.data?.data;
|
|
1523
1516
|
if (!memory) {
|
|
1524
|
-
spinner.
|
|
1517
|
+
spinner.softFail(`No memory found with name '${singleId}'`);
|
|
1525
1518
|
return;
|
|
1526
1519
|
}
|
|
1527
1520
|
spinner.succeed(`Found named memory '${singleId}' (v${memory.version || 1})`);
|
|
@@ -1531,7 +1524,16 @@ function registerMemoriesCommands(program) {
|
|
|
1531
1524
|
return;
|
|
1532
1525
|
}
|
|
1533
1526
|
catch (e) {
|
|
1534
|
-
|
|
1527
|
+
// Only a genuine 404 is "no such name" (exit 0 + banner — a fleet
|
|
1528
|
+
// hook substring-matches this line). Every other failure (5xx,
|
|
1529
|
+
// network, auth) previously rendered as the same banner and exited
|
|
1530
|
+
// 0, which reported an outage as an empty store. Rethrow those to
|
|
1531
|
+
// handleError. Mirrors the explicit --name path above, which has
|
|
1532
|
+
// no catch and always propagates.
|
|
1533
|
+
if (!(0, errors_1.isNotFoundError)(e)) {
|
|
1534
|
+
throw e;
|
|
1535
|
+
}
|
|
1536
|
+
spinner.softFail(`No memory found with name '${singleId}'`);
|
|
1535
1537
|
console.log(chalk_1.default.gray(` Tip: use --name explicitly or pass a UUID/prefix`));
|
|
1536
1538
|
return;
|
|
1537
1539
|
}
|
|
@@ -1539,9 +1541,8 @@ function registerMemoriesCommands(program) {
|
|
|
1539
1541
|
const isShortId = singleId.length < 36;
|
|
1540
1542
|
const MIN_PREFIX_LENGTH = 6;
|
|
1541
1543
|
if (isShortId && singleId.length < MIN_PREFIX_LENGTH) {
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
return;
|
|
1544
|
+
throw (0, errors_1.validationError)(`ID prefix must be at least ${MIN_PREFIX_LENGTH} characters. ` +
|
|
1545
|
+
'Example: mx memories get 2d09116a');
|
|
1545
1546
|
}
|
|
1546
1547
|
let memoryId = singleId;
|
|
1547
1548
|
let resolvedMemory = null;
|
|
@@ -1554,7 +1555,7 @@ function registerMemoriesCommands(program) {
|
|
|
1554
1555
|
}));
|
|
1555
1556
|
const matches = listResponse.data?.data || [];
|
|
1556
1557
|
if (matches.length === 0) {
|
|
1557
|
-
spinner.
|
|
1558
|
+
spinner.softFail(`No memory found with ID prefix: ${singleId}`);
|
|
1558
1559
|
return;
|
|
1559
1560
|
}
|
|
1560
1561
|
if (matches.length > 1) {
|
|
@@ -1750,14 +1751,13 @@ function registerMemoriesCommands(program) {
|
|
|
1750
1751
|
else {
|
|
1751
1752
|
// Direct mode - validate conversationId is provided
|
|
1752
1753
|
if (!options.conversationId) {
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
return;
|
|
1754
|
+
throw (0, errors_1.validationError)('--conversation-id is required.\n' +
|
|
1755
|
+
'\nTo create a new conversation, use:\n' +
|
|
1756
|
+
' mx memories create --conversation-id NEW --content "Your message"\n' +
|
|
1757
|
+
'\nTo add to an existing conversation, use:\n' +
|
|
1758
|
+
' mx memories create --conversation-id conv_abc123 --content "Your message"\n' +
|
|
1759
|
+
'\nOr use interactive mode:\n' +
|
|
1760
|
+
' mx memories create --interactive');
|
|
1761
1761
|
}
|
|
1762
1762
|
// Auto-detect Claude Code session ID for conversation grouping
|
|
1763
1763
|
const claudeSession = config_1.config.isAIAgentContext() ? (0, session_reader_1.readSessionContext)() : undefined;
|
|
@@ -1792,9 +1792,20 @@ function registerMemoriesCommands(program) {
|
|
|
1792
1792
|
response = await retryWrite(() => (0, api_client_1.getSdk)().memories.createMemory(memoryData));
|
|
1793
1793
|
}
|
|
1794
1794
|
catch (createError) {
|
|
1795
|
-
// 409
|
|
1796
|
-
|
|
1797
|
-
|
|
1795
|
+
// 409 is OVERLOADED: it is returned both for "named memory already
|
|
1796
|
+
// exists" (retry as an upsert) and for QUOTA_EXCEEDED (do not retry).
|
|
1797
|
+
// This branch used to test the bare status, so a free-tier user at
|
|
1798
|
+
// their cap creating a NAMED memory had the quota rejection retried
|
|
1799
|
+
// against PUT /named/:name — which applies the same quota gate and
|
|
1800
|
+
// rejects again. Two rejections burned, and the failure was reported as
|
|
1801
|
+
// a name collision when the actual cause was capacity. Branch on the
|
|
1802
|
+
// code (see isQuotaError / isNameConflictError in lib/retry.ts), the
|
|
1803
|
+
// same way mcp-server's create_memory does (#2925).
|
|
1804
|
+
if ((0, retry_1.isQuotaError)(createError)) {
|
|
1805
|
+
spinner.fail('Memory limit reached');
|
|
1806
|
+
throw createError;
|
|
1807
|
+
}
|
|
1808
|
+
if ((0, retry_1.isNameConflictError)(createError) && memoryData.name) {
|
|
1798
1809
|
// v3.85 (named-memory operation integrity): the auto-upsert retry must
|
|
1799
1810
|
// PRESERVE every field the user did not explicitly provide and FORWARD
|
|
1800
1811
|
// every field they did. The previous body (a) re-sent the truthy-
|
|
@@ -1843,6 +1854,15 @@ function registerMemoriesCommands(program) {
|
|
|
1843
1854
|
// Response is properly typed as CreateMemoryResponse with data and meta fields
|
|
1844
1855
|
const memory = response.data?.data;
|
|
1845
1856
|
const meta = response.data?.meta;
|
|
1857
|
+
// A 2xx without an id means nothing was persisted that we can address.
|
|
1858
|
+
// The success lines below used to render "ID: undefined", and the
|
|
1859
|
+
// --supersedes step below is gated on `memory?.id` so it silently did
|
|
1860
|
+
// nothing — the caller saw a success glyph for a write that produced no
|
|
1861
|
+
// usable memory (v4.04).
|
|
1862
|
+
if (!memory?.id) {
|
|
1863
|
+
spinner.stop();
|
|
1864
|
+
throw (0, errors_1.unusableResponseError)(upserted ? 'Named memory update' : 'Memory creation', responseShape(response.data));
|
|
1865
|
+
}
|
|
1846
1866
|
if (upserted) {
|
|
1847
1867
|
const version = meta?.version || 'new';
|
|
1848
1868
|
spinner.succeed(`Named memory '${memoryData.name}' updated (v${version}) via auto-upsert. ID: ${memory?.id}`);
|
|
@@ -1880,9 +1900,13 @@ function registerMemoriesCommands(program) {
|
|
|
1880
1900
|
}
|
|
1881
1901
|
catch (relError) {
|
|
1882
1902
|
relSpinner.fail(`Failed to create relationship: ${relError.message || 'Unknown error'}`);
|
|
1903
|
+
// Partial failure: the memory exists but the SUPERSEDES edge does
|
|
1904
|
+
// not. relSpinner.fail() has already set a non-zero exit code so the
|
|
1905
|
+
// caller cannot mistake this for a clean create. Recovery guidance
|
|
1906
|
+
// goes to stderr — stdout still carries the created-memory body.
|
|
1883
1907
|
if (options.format !== 'json' && options.format !== 'yaml') {
|
|
1884
|
-
console.
|
|
1885
|
-
console.
|
|
1908
|
+
console.error(chalk_1.default.yellow('Memory was created successfully. You can manually create the relationship with:'));
|
|
1909
|
+
console.error(chalk_1.default.cyan(` mx memories relationships create ${memory.id} --target ${options.supersedes} --type SUPERSEDES`));
|
|
1886
1910
|
}
|
|
1887
1911
|
}
|
|
1888
1912
|
}
|
|
@@ -1912,13 +1936,11 @@ function registerMemoriesCommands(program) {
|
|
|
1912
1936
|
.action(async (id, options) => {
|
|
1913
1937
|
try {
|
|
1914
1938
|
if (!id && !options.name) {
|
|
1915
|
-
|
|
1916
|
-
return;
|
|
1939
|
+
throw (0, errors_1.validationError)('Provide a memory ID or use --name <name>');
|
|
1917
1940
|
}
|
|
1918
1941
|
// --assign-name and --rename are mutually exclusive
|
|
1919
1942
|
if (options.assignName && options.rename) {
|
|
1920
|
-
|
|
1921
|
-
return;
|
|
1943
|
+
throw (0, errors_1.validationError)('Cannot use --assign-name and --rename together');
|
|
1922
1944
|
}
|
|
1923
1945
|
const updates = {};
|
|
1924
1946
|
if (options.content)
|
|
@@ -1937,8 +1959,7 @@ function registerMemoriesCommands(program) {
|
|
|
1937
1959
|
// --assign-name: assign a name to an existing memory by ID
|
|
1938
1960
|
if (options.assignName) {
|
|
1939
1961
|
if (!id) {
|
|
1940
|
-
|
|
1941
|
-
return;
|
|
1962
|
+
throw (0, errors_1.validationError)('--assign-name requires a memory ID');
|
|
1942
1963
|
}
|
|
1943
1964
|
updates.name = options.assignName;
|
|
1944
1965
|
const spinner = (0, spinner_1.createSpinner)('Assigning name...', options.format).start();
|
|
@@ -1976,8 +1997,7 @@ function registerMemoriesCommands(program) {
|
|
|
1976
1997
|
(0, formatters_1.printOutput)(memory, options.format);
|
|
1977
1998
|
return;
|
|
1978
1999
|
}
|
|
1979
|
-
|
|
1980
|
-
return;
|
|
2000
|
+
throw (0, errors_1.validationError)('--rename requires a memory ID or --name <current-name>');
|
|
1981
2001
|
}
|
|
1982
2002
|
if (Object.keys(updates).length === 0) {
|
|
1983
2003
|
console.log(chalk_1.default.yellow('No updates provided. Use --help to see available options.'));
|
|
@@ -2029,7 +2049,7 @@ function registerMemoriesCommands(program) {
|
|
|
2029
2049
|
const response = await retryRead(() => (0, api_client_1.getSdk)().memories.getNamedMemoryHistory(name));
|
|
2030
2050
|
const data = response.data;
|
|
2031
2051
|
if (!data?.versions?.length) {
|
|
2032
|
-
spinner.
|
|
2052
|
+
spinner.softFail(`No memory found with name '${name}'`);
|
|
2033
2053
|
return;
|
|
2034
2054
|
}
|
|
2035
2055
|
const limit = parseInt(options.limit, 10) || 20;
|
|
@@ -2288,41 +2308,34 @@ function registerMemoriesCommands(program) {
|
|
|
2288
2308
|
.action(async (options, command) => {
|
|
2289
2309
|
try {
|
|
2290
2310
|
if (!options.query) {
|
|
2291
|
-
|
|
2292
|
-
return;
|
|
2311
|
+
throw (0, errors_1.validationError)('--query is required');
|
|
2293
2312
|
}
|
|
2294
2313
|
// Validate search method
|
|
2295
2314
|
const validMethods = ['keyword', 'semantic', 'hybrid'];
|
|
2296
2315
|
if (options.method && !validMethods.includes(options.method)) {
|
|
2297
|
-
|
|
2298
|
-
return;
|
|
2316
|
+
throw (0, errors_1.validationError)(`--method must be one of: ${validMethods.join(', ')}`);
|
|
2299
2317
|
}
|
|
2300
2318
|
// Validate search mode
|
|
2301
2319
|
const validModes = ['unified', 'content', 'facts'];
|
|
2302
2320
|
if (options.mode && !validModes.includes(options.mode)) {
|
|
2303
|
-
|
|
2304
|
-
return;
|
|
2321
|
+
throw (0, errors_1.validationError)(`--mode must be one of: ${validModes.join(', ')}`);
|
|
2305
2322
|
}
|
|
2306
2323
|
// Validate memory type (before spinner starts)
|
|
2307
2324
|
const validTypes = ['episodic', 'semantic', 'procedural'];
|
|
2308
2325
|
if (options.type && !validTypes.includes(options.type)) {
|
|
2309
|
-
|
|
2310
|
-
return;
|
|
2326
|
+
throw (0, errors_1.validationError)(`--type must be one of: ${validTypes.join(', ')}`);
|
|
2311
2327
|
}
|
|
2312
2328
|
// Validate --group-by field
|
|
2313
2329
|
const validGroupByFields = ['conversation'];
|
|
2314
2330
|
if (options.groupBy && !validGroupByFields.includes(options.groupBy)) {
|
|
2315
|
-
|
|
2316
|
-
return;
|
|
2331
|
+
throw (0, errors_1.validationError)(`--group-by must be one of: ${validGroupByFields.join(', ')}`);
|
|
2317
2332
|
}
|
|
2318
2333
|
// Handle --recent duration shortcut
|
|
2319
2334
|
let recentTimestamp = null;
|
|
2320
2335
|
if (options.recent) {
|
|
2321
2336
|
recentTimestamp = (0, date_utils_1.getRecentTimestamp)(options.recent);
|
|
2322
2337
|
if (!recentTimestamp) {
|
|
2323
|
-
|
|
2324
|
-
console.log(chalk_1.default.yellow('Supported formats: 30m, 1h, 7d, 2w'));
|
|
2325
|
-
return;
|
|
2338
|
+
throw (0, errors_1.validationError)(`Invalid duration format: ${options.recent}. Supported formats: 30m, 1h, 7d, 2w`);
|
|
2326
2339
|
}
|
|
2327
2340
|
}
|
|
2328
2341
|
// Handle --brief preset: apply quick lookup defaults
|
|
@@ -2434,8 +2447,7 @@ function registerMemoriesCommands(program) {
|
|
|
2434
2447
|
if (options.sort) {
|
|
2435
2448
|
const validSortFields = ['score', 'createdAt', 'updatedAt', 'eventTime'];
|
|
2436
2449
|
if (!validSortFields.includes(options.sort)) {
|
|
2437
|
-
|
|
2438
|
-
return;
|
|
2450
|
+
throw (0, errors_1.validationError)(`--sort must be one of: ${validSortFields.join(', ')}`);
|
|
2439
2451
|
}
|
|
2440
2452
|
if (options.sort !== 'score') {
|
|
2441
2453
|
searchRequest.sortBy = options.sort;
|
|
@@ -2444,14 +2456,21 @@ function registerMemoriesCommands(program) {
|
|
|
2444
2456
|
if (options.order) {
|
|
2445
2457
|
const validOrders = ['asc', 'desc'];
|
|
2446
2458
|
if (!validOrders.includes(options.order)) {
|
|
2447
|
-
|
|
2448
|
-
return;
|
|
2459
|
+
throw (0, errors_1.validationError)(`--order must be one of: ${validOrders.join(', ')}`);
|
|
2449
2460
|
}
|
|
2450
2461
|
searchRequest.order = options.order;
|
|
2451
2462
|
}
|
|
2452
2463
|
// Use GET endpoint for better performance (Issue #79)
|
|
2453
2464
|
const response = await retryRead(() => (0, api_client_1.getSdk)().memories.searchMemoriesGet(searchRequest));
|
|
2454
2465
|
const searchData = response.data;
|
|
2466
|
+
// A 2xx with no results array is NOT an empty result set: the search did
|
|
2467
|
+
// not return data. Rendering it as "Found 0 results" (exit 0) made a
|
|
2468
|
+
// backend outage look like an empty store (v4.04). A genuine zero-hit
|
|
2469
|
+
// search returns `data: []` and still reports "Found 0 results".
|
|
2470
|
+
if (!searchData || !Array.isArray(searchData.data)) {
|
|
2471
|
+
spinner?.stop();
|
|
2472
|
+
throw (0, errors_1.unusableResponseError)('Memory search', responseShape(searchData));
|
|
2473
|
+
}
|
|
2455
2474
|
// Normalize: API may return flat objects or {memory, score} wrappers.
|
|
2456
2475
|
// Ensure consistent {memory, score, matchedVia} shape for display code.
|
|
2457
2476
|
if (searchData?.data) {
|
|
@@ -2977,8 +2996,7 @@ function registerMemoryRelationshipCommands(memories) {
|
|
|
2977
2996
|
try {
|
|
2978
2997
|
const validDirections = ['outgoing', 'incoming', 'both'];
|
|
2979
2998
|
if (!validDirections.includes(options.direction)) {
|
|
2980
|
-
|
|
2981
|
-
return;
|
|
2999
|
+
throw (0, errors_1.validationError)(`--direction must be one of: ${validDirections.join(', ')}`);
|
|
2982
3000
|
}
|
|
2983
3001
|
const spinner = (0, spinner_1.createSpinner)('Fetching relationships...', options.format).start();
|
|
2984
3002
|
const response = await retryRead(() => (0, api_client_1.getSdk)().memories.getMemoryRelationships(id, {
|
|
@@ -3078,8 +3096,7 @@ function registerMemoryRelationshipCommands(memories) {
|
|
|
3078
3096
|
}
|
|
3079
3097
|
else {
|
|
3080
3098
|
if (!validTypes.includes(options.type.toUpperCase())) {
|
|
3081
|
-
|
|
3082
|
-
return;
|
|
3099
|
+
throw (0, errors_1.validationError)(`--type must be one of: ${validTypes.join(', ')}`);
|
|
3083
3100
|
}
|
|
3084
3101
|
relationshipData = {
|
|
3085
3102
|
targetMemoryId: options.target,
|
|
@@ -3337,12 +3354,10 @@ function registerDigestCommand(memories) {
|
|
|
3337
3354
|
.action(async (options) => {
|
|
3338
3355
|
try {
|
|
3339
3356
|
if (!options.query) {
|
|
3340
|
-
|
|
3341
|
-
return;
|
|
3357
|
+
throw (0, errors_1.validationError)('--query is required');
|
|
3342
3358
|
}
|
|
3343
3359
|
if (!validDigestFormats.includes(options.digestFormat)) {
|
|
3344
|
-
|
|
3345
|
-
return;
|
|
3360
|
+
throw (0, errors_1.validationError)(`--digest-format must be one of: ${validDigestFormats.join(', ')}`);
|
|
3346
3361
|
}
|
|
3347
3362
|
const spinner = (0, spinner_1.createSpinner)('Generating digest...', options.format).start();
|
|
3348
3363
|
const body = {
|
|
@@ -3377,12 +3392,24 @@ function registerDigestCommand(memories) {
|
|
|
3377
3392
|
sdk.config.timeoutMs = prevTimeout;
|
|
3378
3393
|
}
|
|
3379
3394
|
const data = response.data;
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3395
|
+
// A 2xx with no body means the digest was never produced. Reporting it
|
|
3396
|
+
// as a soft failure and exiting 0 made an outage look like "nothing to
|
|
3397
|
+
// say" (v4.04).
|
|
3398
|
+
if (!data || !data.metadata) {
|
|
3399
|
+
spinner.stop();
|
|
3400
|
+
throw (0, errors_1.unusableResponseError)('Digest generation', responseShape(data));
|
|
3383
3401
|
}
|
|
3384
3402
|
const { digest, metadata, sources, keyFacts, entities, effectiveStateBreakdown } = data;
|
|
3385
|
-
|
|
3403
|
+
if (metadata.sourcesUsed === 0) {
|
|
3404
|
+
// Zero sources is a genuine empty result (exit 0), but it is not a
|
|
3405
|
+
// success: the old "Digest generated from 0 memories" success glyph
|
|
3406
|
+
// read as "here is your answer" when nothing was retrieved.
|
|
3407
|
+
spinner.info('No sources met the relevance threshold for this query');
|
|
3408
|
+
}
|
|
3409
|
+
else {
|
|
3410
|
+
spinner.succeed(`Digest generated from ${metadata.sourcesUsed} memories across ${metadata.conversationsSpanned} conversations`);
|
|
3411
|
+
}
|
|
3412
|
+
printRetrievalCaveats(metadata, data.meta);
|
|
3386
3413
|
// JSON/YAML output
|
|
3387
3414
|
if (options.format === 'json' || options.format === 'yaml') {
|
|
3388
3415
|
(0, formatters_1.printOutput)(data, options.format);
|
|
@@ -3429,13 +3456,11 @@ function registerStatusCommand(memories) {
|
|
|
3429
3456
|
.action(async (options) => {
|
|
3430
3457
|
try {
|
|
3431
3458
|
if (!options.query) {
|
|
3432
|
-
|
|
3433
|
-
return;
|
|
3459
|
+
throw (0, errors_1.validationError)('--query is required');
|
|
3434
3460
|
}
|
|
3435
3461
|
const maxSources = parseInt(options.maxSources, 10);
|
|
3436
3462
|
if (isNaN(maxSources) || maxSources < 1) {
|
|
3437
|
-
|
|
3438
|
-
return;
|
|
3463
|
+
throw (0, errors_1.validationError)('--max-sources must be a positive integer');
|
|
3439
3464
|
}
|
|
3440
3465
|
const spinner = (0, spinner_1.createSpinner)('Generating status report...', options.format).start();
|
|
3441
3466
|
const body = {
|
|
@@ -3466,12 +3491,21 @@ function registerStatusCommand(memories) {
|
|
|
3466
3491
|
sdk.config.timeoutMs = prevTimeout;
|
|
3467
3492
|
}
|
|
3468
3493
|
const data = response.data;
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3494
|
+
// A 2xx with no body means the digest was never produced. Reporting it
|
|
3495
|
+
// as a soft failure and exiting 0 made an outage look like "nothing to
|
|
3496
|
+
// say" (v4.04).
|
|
3497
|
+
if (!data || !data.metadata) {
|
|
3498
|
+
spinner.stop();
|
|
3499
|
+
throw (0, errors_1.unusableResponseError)('Status report generation', responseShape(data));
|
|
3472
3500
|
}
|
|
3473
3501
|
const { digest, metadata, sources, keyFacts, entities, effectiveStateBreakdown } = data;
|
|
3474
|
-
|
|
3502
|
+
if (metadata.sourcesUsed === 0) {
|
|
3503
|
+
spinner.info('No sources met the relevance threshold for this query');
|
|
3504
|
+
}
|
|
3505
|
+
else {
|
|
3506
|
+
spinner.succeed(`Status report from ${metadata.sourcesUsed} memories across ${metadata.conversationsSpanned} conversations`);
|
|
3507
|
+
}
|
|
3508
|
+
printRetrievalCaveats(metadata, data.meta);
|
|
3475
3509
|
if (options.format === 'json' || options.format === 'yaml') {
|
|
3476
3510
|
(0, formatters_1.printOutput)(data, options.format);
|
|
3477
3511
|
return;
|
|
@@ -3487,6 +3521,68 @@ function registerStatusCommand(memories) {
|
|
|
3487
3521
|
}
|
|
3488
3522
|
});
|
|
3489
3523
|
}
|
|
3524
|
+
/**
|
|
3525
|
+
* Describe an unusable response body for the DEBUG-only details payload.
|
|
3526
|
+
*
|
|
3527
|
+
* Never pass the body itself: it can hold memory content and transcript text,
|
|
3528
|
+
* and DEBUG=1 stderr is collected by CI and agent harnesses. A shape summary
|
|
3529
|
+
* is what actually helps diagnosis — which keys came back, and what the data
|
|
3530
|
+
* field was.
|
|
3531
|
+
*/
|
|
3532
|
+
function responseShape(body) {
|
|
3533
|
+
const keys = body && typeof body === 'object' && !Array.isArray(body) ? Object.keys(body) : [];
|
|
3534
|
+
const data = body?.data;
|
|
3535
|
+
const dataType = Array.isArray(data) ? 'array' : data === null ? 'null' : typeof data;
|
|
3536
|
+
return { responseKeys: keys, dataType };
|
|
3537
|
+
}
|
|
3538
|
+
/**
|
|
3539
|
+
* Surface retrieval-quality caveats that travel with a digest/status response.
|
|
3540
|
+
*
|
|
3541
|
+
* The server sets these when it fell back to weak or heterogeneous matches
|
|
3542
|
+
* (core-api/src/schemas/digest.schema.ts — "Consumers should surface a warning
|
|
3543
|
+
* to the calling agent"). Without them a loosely-related digest reads exactly
|
|
3544
|
+
* like a well-matched one.
|
|
3545
|
+
*
|
|
3546
|
+
* Caveats go to STDERR so they reach the caller in every output format,
|
|
3547
|
+
* including `--format json`, where the digest body must remain the only
|
|
3548
|
+
* top-level JSON on stdout.
|
|
3549
|
+
*
|
|
3550
|
+
* @param metadata - DigestMetadata from the response
|
|
3551
|
+
* @param meta - Optional response-level meta (forward-compat, see below)
|
|
3552
|
+
*/
|
|
3553
|
+
function printRetrievalCaveats(metadata, meta) {
|
|
3554
|
+
const avg = typeof metadata?.averageRelevance === 'number' ? metadata.averageRelevance : null;
|
|
3555
|
+
const avgLabel = avg === null ? 'unknown' : avg.toFixed(3);
|
|
3556
|
+
if (metadata?.narrowWindowFallback) {
|
|
3557
|
+
console.error(chalk_1.default.yellow(`⚠ Low-relevance results: average relevance ${avgLabel} is below the confidence ` +
|
|
3558
|
+
'threshold — this digest was synthesised from weak matches and may be only ' +
|
|
3559
|
+
'loosely related to your query.'));
|
|
3560
|
+
}
|
|
3561
|
+
if (metadata?.crossProjectFallback) {
|
|
3562
|
+
console.error(chalk_1.default.yellow(`⚠ Cross-project results: average relevance ${avgLabel} is below the confidence ` +
|
|
3563
|
+
'threshold and the sources span more than one project — this content may come ' +
|
|
3564
|
+
'from a different project than the one you are working in.'));
|
|
3565
|
+
}
|
|
3566
|
+
// Forward-compat with the retrieval team's degraded-retrieval contract
|
|
3567
|
+
// (meta.degraded / meta.branchFailures). Both fields are absent on today's
|
|
3568
|
+
// responses, so every access is guarded.
|
|
3569
|
+
if (meta?.degraded) {
|
|
3570
|
+
const failures = Array.isArray(meta.branchFailures) ? meta.branchFailures : [];
|
|
3571
|
+
// Branch names are server-supplied and land on stderr, which agents read.
|
|
3572
|
+
// Constrain them to an identifier shape and cap the count rather than
|
|
3573
|
+
// echoing arbitrary text: an unbounded string here is an injection channel
|
|
3574
|
+
// into agent context (and can carry ANSI escapes from a hostile apiUrl).
|
|
3575
|
+
const names = failures
|
|
3576
|
+
.map((b) => (typeof b === 'string' ? b : b?.branch || b?.name))
|
|
3577
|
+
.filter((n) => typeof n === 'string' && /^[A-Za-z0-9_-]{1,32}$/.test(n))
|
|
3578
|
+
.slice(0, 8);
|
|
3579
|
+
const detail = names.length
|
|
3580
|
+
? `failed branches: ${names.join(', ')}`
|
|
3581
|
+
: 'one or more retrieval branches failed';
|
|
3582
|
+
console.error(chalk_1.default.yellow(`⚠ Degraded retrieval — ${detail}. These results are incomplete; re-run before ` +
|
|
3583
|
+
'treating them as a full answer.'));
|
|
3584
|
+
}
|
|
3585
|
+
}
|
|
3490
3586
|
/**
|
|
3491
3587
|
* Display a formatted digest with header box and metadata footer
|
|
3492
3588
|
*/
|
|
@@ -3578,9 +3674,8 @@ function registerRecapCommand(memories) {
|
|
|
3578
3674
|
// Calculate time filter
|
|
3579
3675
|
const recentTimestamp = (0, date_utils_1.getRecentTimestamp)(options.recent);
|
|
3580
3676
|
if (!recentTimestamp) {
|
|
3581
|
-
spinner.
|
|
3582
|
-
|
|
3583
|
-
return;
|
|
3677
|
+
spinner.stop();
|
|
3678
|
+
throw (0, errors_1.validationError)(`Invalid duration format: ${options.recent}. Supported formats: 30m, 1h, 7d, 2w`);
|
|
3584
3679
|
}
|
|
3585
3680
|
const fetchLimit = parseInt(options.limit, 10);
|
|
3586
3681
|
// Build exclude-topics value (used by both paths)
|
|
@@ -3860,10 +3955,15 @@ function registerBuildContextCommand(memories) {
|
|
|
3860
3955
|
}
|
|
3861
3956
|
const sdk = (0, api_client_1.getSdk)();
|
|
3862
3957
|
const response = await retryRead(() => sdk.memories.buildContext(body));
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3958
|
+
// `|| response` used to make the guard below unreachable: on a 2xx with
|
|
3959
|
+
// an empty body it fell back to the truthy response envelope, so the
|
|
3960
|
+
// command printed "Context briefing ready" and an empty briefing (exit
|
|
3961
|
+
// 0). Unwrap first, then test what we actually got (v4.04).
|
|
3962
|
+
const envelope = response;
|
|
3963
|
+
const data = envelope && typeof envelope === 'object' && 'data' in envelope ? envelope.data : envelope;
|
|
3964
|
+
if (!data || typeof data !== 'object') {
|
|
3965
|
+
spinner.stop();
|
|
3966
|
+
throw (0, errors_1.unusableResponseError)('Context briefing', responseShape(data));
|
|
3867
3967
|
}
|
|
3868
3968
|
spinner.succeed('Context briefing ready');
|
|
3869
3969
|
// JSON output
|
|
@@ -3945,8 +4045,7 @@ function registerBuildContextCommand(memories) {
|
|
|
3945
4045
|
try {
|
|
3946
4046
|
const validDimensions = ['day', 'week', 'month', 'topic'];
|
|
3947
4047
|
if (!options.groupBy || !validDimensions.includes(options.groupBy)) {
|
|
3948
|
-
|
|
3949
|
-
return;
|
|
4048
|
+
throw (0, errors_1.validationError)('--group-by is required and must be one of: day, week, month, topic');
|
|
3950
4049
|
}
|
|
3951
4050
|
const spinner = (0, spinner_1.createSpinner)('Fetching memory stats...', options.format).start();
|
|
3952
4051
|
const apiUrl = config_1.config.getApiUrl();
|