@memnexus-ai/cli 1.7.220 → 1.7.221
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.map +1 -1
- package/dist/commands/memories.js +174 -87
- 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 +137 -48
- package/dist/lib/errors.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 +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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"}
|
|
@@ -1342,8 +1342,7 @@ function registerMemoriesCommands(program) {
|
|
|
1342
1342
|
if (options.last) {
|
|
1343
1343
|
limit = parseInt(options.last, 10);
|
|
1344
1344
|
if (isNaN(limit) || limit < 1) {
|
|
1345
|
-
|
|
1346
|
-
return;
|
|
1345
|
+
throw (0, errors_1.validationError)('--last must be a positive number');
|
|
1347
1346
|
}
|
|
1348
1347
|
sortField = 'createdAt';
|
|
1349
1348
|
sortOrder = 'desc';
|
|
@@ -1354,17 +1353,14 @@ function registerMemoriesCommands(program) {
|
|
|
1354
1353
|
const validFields = ['createdAt', 'eventTime'];
|
|
1355
1354
|
const validOrders = ['asc', 'desc'];
|
|
1356
1355
|
if (sortParts.length !== 2) {
|
|
1357
|
-
|
|
1358
|
-
return;
|
|
1356
|
+
throw (0, errors_1.validationError)('--sort must be in format field:order (e.g., createdAt:desc)');
|
|
1359
1357
|
}
|
|
1360
1358
|
const [field, order] = sortParts;
|
|
1361
1359
|
if (!validFields.includes(field)) {
|
|
1362
|
-
|
|
1363
|
-
return;
|
|
1360
|
+
throw (0, errors_1.validationError)(`Sort field must be one of: ${validFields.join(', ')}`);
|
|
1364
1361
|
}
|
|
1365
1362
|
if (!validOrders.includes(order)) {
|
|
1366
|
-
|
|
1367
|
-
return;
|
|
1363
|
+
throw (0, errors_1.validationError)(`Sort order must be one of: ${validOrders.join(', ')}`);
|
|
1368
1364
|
}
|
|
1369
1365
|
sortField = field;
|
|
1370
1366
|
sortOrder = order;
|
|
@@ -1387,9 +1383,7 @@ function registerMemoriesCommands(program) {
|
|
|
1387
1383
|
if (recentDuration) {
|
|
1388
1384
|
const recentTs = (0, date_utils_1.getRecentTimestamp)(recentDuration);
|
|
1389
1385
|
if (!recentTs) {
|
|
1390
|
-
|
|
1391
|
-
console.log(chalk_1.default.yellow('Supported formats: 30m, 1h, 7d, 2w'));
|
|
1392
|
-
return;
|
|
1386
|
+
throw (0, errors_1.validationError)(`Invalid duration format: ${recentDuration}. Supported formats: 30m, 1h, 7d, 2w`);
|
|
1393
1387
|
}
|
|
1394
1388
|
listParams.from = recentTs;
|
|
1395
1389
|
}
|
|
@@ -1479,7 +1473,7 @@ function registerMemoriesCommands(program) {
|
|
|
1479
1473
|
}));
|
|
1480
1474
|
const memory = response.data?.data;
|
|
1481
1475
|
if (!memory) {
|
|
1482
|
-
spinner.
|
|
1476
|
+
spinner.softFail(`No memory found with name '${options.name}'`);
|
|
1483
1477
|
return;
|
|
1484
1478
|
}
|
|
1485
1479
|
spinner.succeed(`Found named memory '${options.name}' (v${memory.version || 1})`);
|
|
@@ -1498,14 +1492,12 @@ function registerMemoriesCommands(program) {
|
|
|
1498
1492
|
// Remove duplicates while preserving order
|
|
1499
1493
|
allIds = [...new Set(allIds)].filter((id) => id.trim().length > 0);
|
|
1500
1494
|
if (allIds.length === 0) {
|
|
1501
|
-
|
|
1502
|
-
return;
|
|
1495
|
+
throw (0, errors_1.validationError)('No memory IDs provided');
|
|
1503
1496
|
}
|
|
1504
1497
|
// Discovery options only work with single ID
|
|
1505
1498
|
const hasDiscoveryOptions = options.similar || options.conversation || options.related;
|
|
1506
1499
|
if (hasDiscoveryOptions && allIds.length > 1) {
|
|
1507
|
-
|
|
1508
|
-
return;
|
|
1500
|
+
throw (0, errors_1.validationError)('--similar, --conversation, and --related options only work with a single memory ID');
|
|
1509
1501
|
}
|
|
1510
1502
|
// Single ID mode - use existing logic with short ID support
|
|
1511
1503
|
if (allIds.length === 1) {
|
|
@@ -1521,7 +1513,7 @@ function registerMemoriesCommands(program) {
|
|
|
1521
1513
|
}));
|
|
1522
1514
|
const memory = response.data?.data;
|
|
1523
1515
|
if (!memory) {
|
|
1524
|
-
spinner.
|
|
1516
|
+
spinner.softFail(`No memory found with name '${singleId}'`);
|
|
1525
1517
|
return;
|
|
1526
1518
|
}
|
|
1527
1519
|
spinner.succeed(`Found named memory '${singleId}' (v${memory.version || 1})`);
|
|
@@ -1531,7 +1523,16 @@ function registerMemoriesCommands(program) {
|
|
|
1531
1523
|
return;
|
|
1532
1524
|
}
|
|
1533
1525
|
catch (e) {
|
|
1534
|
-
|
|
1526
|
+
// Only a genuine 404 is "no such name" (exit 0 + banner — a fleet
|
|
1527
|
+
// hook substring-matches this line). Every other failure (5xx,
|
|
1528
|
+
// network, auth) previously rendered as the same banner and exited
|
|
1529
|
+
// 0, which reported an outage as an empty store. Rethrow those to
|
|
1530
|
+
// handleError. Mirrors the explicit --name path above, which has
|
|
1531
|
+
// no catch and always propagates.
|
|
1532
|
+
if (!(0, errors_1.isNotFoundError)(e)) {
|
|
1533
|
+
throw e;
|
|
1534
|
+
}
|
|
1535
|
+
spinner.softFail(`No memory found with name '${singleId}'`);
|
|
1535
1536
|
console.log(chalk_1.default.gray(` Tip: use --name explicitly or pass a UUID/prefix`));
|
|
1536
1537
|
return;
|
|
1537
1538
|
}
|
|
@@ -1539,9 +1540,8 @@ function registerMemoriesCommands(program) {
|
|
|
1539
1540
|
const isShortId = singleId.length < 36;
|
|
1540
1541
|
const MIN_PREFIX_LENGTH = 6;
|
|
1541
1542
|
if (isShortId && singleId.length < MIN_PREFIX_LENGTH) {
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
return;
|
|
1543
|
+
throw (0, errors_1.validationError)(`ID prefix must be at least ${MIN_PREFIX_LENGTH} characters. ` +
|
|
1544
|
+
'Example: mx memories get 2d09116a');
|
|
1545
1545
|
}
|
|
1546
1546
|
let memoryId = singleId;
|
|
1547
1547
|
let resolvedMemory = null;
|
|
@@ -1554,7 +1554,7 @@ function registerMemoriesCommands(program) {
|
|
|
1554
1554
|
}));
|
|
1555
1555
|
const matches = listResponse.data?.data || [];
|
|
1556
1556
|
if (matches.length === 0) {
|
|
1557
|
-
spinner.
|
|
1557
|
+
spinner.softFail(`No memory found with ID prefix: ${singleId}`);
|
|
1558
1558
|
return;
|
|
1559
1559
|
}
|
|
1560
1560
|
if (matches.length > 1) {
|
|
@@ -1750,14 +1750,13 @@ function registerMemoriesCommands(program) {
|
|
|
1750
1750
|
else {
|
|
1751
1751
|
// Direct mode - validate conversationId is provided
|
|
1752
1752
|
if (!options.conversationId) {
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
return;
|
|
1753
|
+
throw (0, errors_1.validationError)('--conversation-id is required.\n' +
|
|
1754
|
+
'\nTo create a new conversation, use:\n' +
|
|
1755
|
+
' mx memories create --conversation-id NEW --content "Your message"\n' +
|
|
1756
|
+
'\nTo add to an existing conversation, use:\n' +
|
|
1757
|
+
' mx memories create --conversation-id conv_abc123 --content "Your message"\n' +
|
|
1758
|
+
'\nOr use interactive mode:\n' +
|
|
1759
|
+
' mx memories create --interactive');
|
|
1761
1760
|
}
|
|
1762
1761
|
// Auto-detect Claude Code session ID for conversation grouping
|
|
1763
1762
|
const claudeSession = config_1.config.isAIAgentContext() ? (0, session_reader_1.readSessionContext)() : undefined;
|
|
@@ -1843,6 +1842,15 @@ function registerMemoriesCommands(program) {
|
|
|
1843
1842
|
// Response is properly typed as CreateMemoryResponse with data and meta fields
|
|
1844
1843
|
const memory = response.data?.data;
|
|
1845
1844
|
const meta = response.data?.meta;
|
|
1845
|
+
// A 2xx without an id means nothing was persisted that we can address.
|
|
1846
|
+
// The success lines below used to render "ID: undefined", and the
|
|
1847
|
+
// --supersedes step below is gated on `memory?.id` so it silently did
|
|
1848
|
+
// nothing — the caller saw a success glyph for a write that produced no
|
|
1849
|
+
// usable memory (v4.04).
|
|
1850
|
+
if (!memory?.id) {
|
|
1851
|
+
spinner.stop();
|
|
1852
|
+
throw (0, errors_1.unusableResponseError)(upserted ? 'Named memory update' : 'Memory creation', responseShape(response.data));
|
|
1853
|
+
}
|
|
1846
1854
|
if (upserted) {
|
|
1847
1855
|
const version = meta?.version || 'new';
|
|
1848
1856
|
spinner.succeed(`Named memory '${memoryData.name}' updated (v${version}) via auto-upsert. ID: ${memory?.id}`);
|
|
@@ -1880,9 +1888,13 @@ function registerMemoriesCommands(program) {
|
|
|
1880
1888
|
}
|
|
1881
1889
|
catch (relError) {
|
|
1882
1890
|
relSpinner.fail(`Failed to create relationship: ${relError.message || 'Unknown error'}`);
|
|
1891
|
+
// Partial failure: the memory exists but the SUPERSEDES edge does
|
|
1892
|
+
// not. relSpinner.fail() has already set a non-zero exit code so the
|
|
1893
|
+
// caller cannot mistake this for a clean create. Recovery guidance
|
|
1894
|
+
// goes to stderr — stdout still carries the created-memory body.
|
|
1883
1895
|
if (options.format !== 'json' && options.format !== 'yaml') {
|
|
1884
|
-
console.
|
|
1885
|
-
console.
|
|
1896
|
+
console.error(chalk_1.default.yellow('Memory was created successfully. You can manually create the relationship with:'));
|
|
1897
|
+
console.error(chalk_1.default.cyan(` mx memories relationships create ${memory.id} --target ${options.supersedes} --type SUPERSEDES`));
|
|
1886
1898
|
}
|
|
1887
1899
|
}
|
|
1888
1900
|
}
|
|
@@ -1912,13 +1924,11 @@ function registerMemoriesCommands(program) {
|
|
|
1912
1924
|
.action(async (id, options) => {
|
|
1913
1925
|
try {
|
|
1914
1926
|
if (!id && !options.name) {
|
|
1915
|
-
|
|
1916
|
-
return;
|
|
1927
|
+
throw (0, errors_1.validationError)('Provide a memory ID or use --name <name>');
|
|
1917
1928
|
}
|
|
1918
1929
|
// --assign-name and --rename are mutually exclusive
|
|
1919
1930
|
if (options.assignName && options.rename) {
|
|
1920
|
-
|
|
1921
|
-
return;
|
|
1931
|
+
throw (0, errors_1.validationError)('Cannot use --assign-name and --rename together');
|
|
1922
1932
|
}
|
|
1923
1933
|
const updates = {};
|
|
1924
1934
|
if (options.content)
|
|
@@ -1937,8 +1947,7 @@ function registerMemoriesCommands(program) {
|
|
|
1937
1947
|
// --assign-name: assign a name to an existing memory by ID
|
|
1938
1948
|
if (options.assignName) {
|
|
1939
1949
|
if (!id) {
|
|
1940
|
-
|
|
1941
|
-
return;
|
|
1950
|
+
throw (0, errors_1.validationError)('--assign-name requires a memory ID');
|
|
1942
1951
|
}
|
|
1943
1952
|
updates.name = options.assignName;
|
|
1944
1953
|
const spinner = (0, spinner_1.createSpinner)('Assigning name...', options.format).start();
|
|
@@ -1976,8 +1985,7 @@ function registerMemoriesCommands(program) {
|
|
|
1976
1985
|
(0, formatters_1.printOutput)(memory, options.format);
|
|
1977
1986
|
return;
|
|
1978
1987
|
}
|
|
1979
|
-
|
|
1980
|
-
return;
|
|
1988
|
+
throw (0, errors_1.validationError)('--rename requires a memory ID or --name <current-name>');
|
|
1981
1989
|
}
|
|
1982
1990
|
if (Object.keys(updates).length === 0) {
|
|
1983
1991
|
console.log(chalk_1.default.yellow('No updates provided. Use --help to see available options.'));
|
|
@@ -2029,7 +2037,7 @@ function registerMemoriesCommands(program) {
|
|
|
2029
2037
|
const response = await retryRead(() => (0, api_client_1.getSdk)().memories.getNamedMemoryHistory(name));
|
|
2030
2038
|
const data = response.data;
|
|
2031
2039
|
if (!data?.versions?.length) {
|
|
2032
|
-
spinner.
|
|
2040
|
+
spinner.softFail(`No memory found with name '${name}'`);
|
|
2033
2041
|
return;
|
|
2034
2042
|
}
|
|
2035
2043
|
const limit = parseInt(options.limit, 10) || 20;
|
|
@@ -2288,41 +2296,34 @@ function registerMemoriesCommands(program) {
|
|
|
2288
2296
|
.action(async (options, command) => {
|
|
2289
2297
|
try {
|
|
2290
2298
|
if (!options.query) {
|
|
2291
|
-
|
|
2292
|
-
return;
|
|
2299
|
+
throw (0, errors_1.validationError)('--query is required');
|
|
2293
2300
|
}
|
|
2294
2301
|
// Validate search method
|
|
2295
2302
|
const validMethods = ['keyword', 'semantic', 'hybrid'];
|
|
2296
2303
|
if (options.method && !validMethods.includes(options.method)) {
|
|
2297
|
-
|
|
2298
|
-
return;
|
|
2304
|
+
throw (0, errors_1.validationError)(`--method must be one of: ${validMethods.join(', ')}`);
|
|
2299
2305
|
}
|
|
2300
2306
|
// Validate search mode
|
|
2301
2307
|
const validModes = ['unified', 'content', 'facts'];
|
|
2302
2308
|
if (options.mode && !validModes.includes(options.mode)) {
|
|
2303
|
-
|
|
2304
|
-
return;
|
|
2309
|
+
throw (0, errors_1.validationError)(`--mode must be one of: ${validModes.join(', ')}`);
|
|
2305
2310
|
}
|
|
2306
2311
|
// Validate memory type (before spinner starts)
|
|
2307
2312
|
const validTypes = ['episodic', 'semantic', 'procedural'];
|
|
2308
2313
|
if (options.type && !validTypes.includes(options.type)) {
|
|
2309
|
-
|
|
2310
|
-
return;
|
|
2314
|
+
throw (0, errors_1.validationError)(`--type must be one of: ${validTypes.join(', ')}`);
|
|
2311
2315
|
}
|
|
2312
2316
|
// Validate --group-by field
|
|
2313
2317
|
const validGroupByFields = ['conversation'];
|
|
2314
2318
|
if (options.groupBy && !validGroupByFields.includes(options.groupBy)) {
|
|
2315
|
-
|
|
2316
|
-
return;
|
|
2319
|
+
throw (0, errors_1.validationError)(`--group-by must be one of: ${validGroupByFields.join(', ')}`);
|
|
2317
2320
|
}
|
|
2318
2321
|
// Handle --recent duration shortcut
|
|
2319
2322
|
let recentTimestamp = null;
|
|
2320
2323
|
if (options.recent) {
|
|
2321
2324
|
recentTimestamp = (0, date_utils_1.getRecentTimestamp)(options.recent);
|
|
2322
2325
|
if (!recentTimestamp) {
|
|
2323
|
-
|
|
2324
|
-
console.log(chalk_1.default.yellow('Supported formats: 30m, 1h, 7d, 2w'));
|
|
2325
|
-
return;
|
|
2326
|
+
throw (0, errors_1.validationError)(`Invalid duration format: ${options.recent}. Supported formats: 30m, 1h, 7d, 2w`);
|
|
2326
2327
|
}
|
|
2327
2328
|
}
|
|
2328
2329
|
// Handle --brief preset: apply quick lookup defaults
|
|
@@ -2434,8 +2435,7 @@ function registerMemoriesCommands(program) {
|
|
|
2434
2435
|
if (options.sort) {
|
|
2435
2436
|
const validSortFields = ['score', 'createdAt', 'updatedAt', 'eventTime'];
|
|
2436
2437
|
if (!validSortFields.includes(options.sort)) {
|
|
2437
|
-
|
|
2438
|
-
return;
|
|
2438
|
+
throw (0, errors_1.validationError)(`--sort must be one of: ${validSortFields.join(', ')}`);
|
|
2439
2439
|
}
|
|
2440
2440
|
if (options.sort !== 'score') {
|
|
2441
2441
|
searchRequest.sortBy = options.sort;
|
|
@@ -2444,14 +2444,21 @@ function registerMemoriesCommands(program) {
|
|
|
2444
2444
|
if (options.order) {
|
|
2445
2445
|
const validOrders = ['asc', 'desc'];
|
|
2446
2446
|
if (!validOrders.includes(options.order)) {
|
|
2447
|
-
|
|
2448
|
-
return;
|
|
2447
|
+
throw (0, errors_1.validationError)(`--order must be one of: ${validOrders.join(', ')}`);
|
|
2449
2448
|
}
|
|
2450
2449
|
searchRequest.order = options.order;
|
|
2451
2450
|
}
|
|
2452
2451
|
// Use GET endpoint for better performance (Issue #79)
|
|
2453
2452
|
const response = await retryRead(() => (0, api_client_1.getSdk)().memories.searchMemoriesGet(searchRequest));
|
|
2454
2453
|
const searchData = response.data;
|
|
2454
|
+
// A 2xx with no results array is NOT an empty result set: the search did
|
|
2455
|
+
// not return data. Rendering it as "Found 0 results" (exit 0) made a
|
|
2456
|
+
// backend outage look like an empty store (v4.04). A genuine zero-hit
|
|
2457
|
+
// search returns `data: []` and still reports "Found 0 results".
|
|
2458
|
+
if (!searchData || !Array.isArray(searchData.data)) {
|
|
2459
|
+
spinner?.stop();
|
|
2460
|
+
throw (0, errors_1.unusableResponseError)('Memory search', responseShape(searchData));
|
|
2461
|
+
}
|
|
2455
2462
|
// Normalize: API may return flat objects or {memory, score} wrappers.
|
|
2456
2463
|
// Ensure consistent {memory, score, matchedVia} shape for display code.
|
|
2457
2464
|
if (searchData?.data) {
|
|
@@ -2977,8 +2984,7 @@ function registerMemoryRelationshipCommands(memories) {
|
|
|
2977
2984
|
try {
|
|
2978
2985
|
const validDirections = ['outgoing', 'incoming', 'both'];
|
|
2979
2986
|
if (!validDirections.includes(options.direction)) {
|
|
2980
|
-
|
|
2981
|
-
return;
|
|
2987
|
+
throw (0, errors_1.validationError)(`--direction must be one of: ${validDirections.join(', ')}`);
|
|
2982
2988
|
}
|
|
2983
2989
|
const spinner = (0, spinner_1.createSpinner)('Fetching relationships...', options.format).start();
|
|
2984
2990
|
const response = await retryRead(() => (0, api_client_1.getSdk)().memories.getMemoryRelationships(id, {
|
|
@@ -3078,8 +3084,7 @@ function registerMemoryRelationshipCommands(memories) {
|
|
|
3078
3084
|
}
|
|
3079
3085
|
else {
|
|
3080
3086
|
if (!validTypes.includes(options.type.toUpperCase())) {
|
|
3081
|
-
|
|
3082
|
-
return;
|
|
3087
|
+
throw (0, errors_1.validationError)(`--type must be one of: ${validTypes.join(', ')}`);
|
|
3083
3088
|
}
|
|
3084
3089
|
relationshipData = {
|
|
3085
3090
|
targetMemoryId: options.target,
|
|
@@ -3337,12 +3342,10 @@ function registerDigestCommand(memories) {
|
|
|
3337
3342
|
.action(async (options) => {
|
|
3338
3343
|
try {
|
|
3339
3344
|
if (!options.query) {
|
|
3340
|
-
|
|
3341
|
-
return;
|
|
3345
|
+
throw (0, errors_1.validationError)('--query is required');
|
|
3342
3346
|
}
|
|
3343
3347
|
if (!validDigestFormats.includes(options.digestFormat)) {
|
|
3344
|
-
|
|
3345
|
-
return;
|
|
3348
|
+
throw (0, errors_1.validationError)(`--digest-format must be one of: ${validDigestFormats.join(', ')}`);
|
|
3346
3349
|
}
|
|
3347
3350
|
const spinner = (0, spinner_1.createSpinner)('Generating digest...', options.format).start();
|
|
3348
3351
|
const body = {
|
|
@@ -3377,12 +3380,24 @@ function registerDigestCommand(memories) {
|
|
|
3377
3380
|
sdk.config.timeoutMs = prevTimeout;
|
|
3378
3381
|
}
|
|
3379
3382
|
const data = response.data;
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
+
// A 2xx with no body means the digest was never produced. Reporting it
|
|
3384
|
+
// as a soft failure and exiting 0 made an outage look like "nothing to
|
|
3385
|
+
// say" (v4.04).
|
|
3386
|
+
if (!data || !data.metadata) {
|
|
3387
|
+
spinner.stop();
|
|
3388
|
+
throw (0, errors_1.unusableResponseError)('Digest generation', responseShape(data));
|
|
3383
3389
|
}
|
|
3384
3390
|
const { digest, metadata, sources, keyFacts, entities, effectiveStateBreakdown } = data;
|
|
3385
|
-
|
|
3391
|
+
if (metadata.sourcesUsed === 0) {
|
|
3392
|
+
// Zero sources is a genuine empty result (exit 0), but it is not a
|
|
3393
|
+
// success: the old "Digest generated from 0 memories" success glyph
|
|
3394
|
+
// read as "here is your answer" when nothing was retrieved.
|
|
3395
|
+
spinner.info('No sources met the relevance threshold for this query');
|
|
3396
|
+
}
|
|
3397
|
+
else {
|
|
3398
|
+
spinner.succeed(`Digest generated from ${metadata.sourcesUsed} memories across ${metadata.conversationsSpanned} conversations`);
|
|
3399
|
+
}
|
|
3400
|
+
printRetrievalCaveats(metadata, data.meta);
|
|
3386
3401
|
// JSON/YAML output
|
|
3387
3402
|
if (options.format === 'json' || options.format === 'yaml') {
|
|
3388
3403
|
(0, formatters_1.printOutput)(data, options.format);
|
|
@@ -3429,13 +3444,11 @@ function registerStatusCommand(memories) {
|
|
|
3429
3444
|
.action(async (options) => {
|
|
3430
3445
|
try {
|
|
3431
3446
|
if (!options.query) {
|
|
3432
|
-
|
|
3433
|
-
return;
|
|
3447
|
+
throw (0, errors_1.validationError)('--query is required');
|
|
3434
3448
|
}
|
|
3435
3449
|
const maxSources = parseInt(options.maxSources, 10);
|
|
3436
3450
|
if (isNaN(maxSources) || maxSources < 1) {
|
|
3437
|
-
|
|
3438
|
-
return;
|
|
3451
|
+
throw (0, errors_1.validationError)('--max-sources must be a positive integer');
|
|
3439
3452
|
}
|
|
3440
3453
|
const spinner = (0, spinner_1.createSpinner)('Generating status report...', options.format).start();
|
|
3441
3454
|
const body = {
|
|
@@ -3466,12 +3479,21 @@ function registerStatusCommand(memories) {
|
|
|
3466
3479
|
sdk.config.timeoutMs = prevTimeout;
|
|
3467
3480
|
}
|
|
3468
3481
|
const data = response.data;
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3482
|
+
// A 2xx with no body means the digest was never produced. Reporting it
|
|
3483
|
+
// as a soft failure and exiting 0 made an outage look like "nothing to
|
|
3484
|
+
// say" (v4.04).
|
|
3485
|
+
if (!data || !data.metadata) {
|
|
3486
|
+
spinner.stop();
|
|
3487
|
+
throw (0, errors_1.unusableResponseError)('Status report generation', responseShape(data));
|
|
3472
3488
|
}
|
|
3473
3489
|
const { digest, metadata, sources, keyFacts, entities, effectiveStateBreakdown } = data;
|
|
3474
|
-
|
|
3490
|
+
if (metadata.sourcesUsed === 0) {
|
|
3491
|
+
spinner.info('No sources met the relevance threshold for this query');
|
|
3492
|
+
}
|
|
3493
|
+
else {
|
|
3494
|
+
spinner.succeed(`Status report from ${metadata.sourcesUsed} memories across ${metadata.conversationsSpanned} conversations`);
|
|
3495
|
+
}
|
|
3496
|
+
printRetrievalCaveats(metadata, data.meta);
|
|
3475
3497
|
if (options.format === 'json' || options.format === 'yaml') {
|
|
3476
3498
|
(0, formatters_1.printOutput)(data, options.format);
|
|
3477
3499
|
return;
|
|
@@ -3487,6 +3509,68 @@ function registerStatusCommand(memories) {
|
|
|
3487
3509
|
}
|
|
3488
3510
|
});
|
|
3489
3511
|
}
|
|
3512
|
+
/**
|
|
3513
|
+
* Describe an unusable response body for the DEBUG-only details payload.
|
|
3514
|
+
*
|
|
3515
|
+
* Never pass the body itself: it can hold memory content and transcript text,
|
|
3516
|
+
* and DEBUG=1 stderr is collected by CI and agent harnesses. A shape summary
|
|
3517
|
+
* is what actually helps diagnosis — which keys came back, and what the data
|
|
3518
|
+
* field was.
|
|
3519
|
+
*/
|
|
3520
|
+
function responseShape(body) {
|
|
3521
|
+
const keys = body && typeof body === 'object' && !Array.isArray(body) ? Object.keys(body) : [];
|
|
3522
|
+
const data = body?.data;
|
|
3523
|
+
const dataType = Array.isArray(data) ? 'array' : data === null ? 'null' : typeof data;
|
|
3524
|
+
return { responseKeys: keys, dataType };
|
|
3525
|
+
}
|
|
3526
|
+
/**
|
|
3527
|
+
* Surface retrieval-quality caveats that travel with a digest/status response.
|
|
3528
|
+
*
|
|
3529
|
+
* The server sets these when it fell back to weak or heterogeneous matches
|
|
3530
|
+
* (core-api/src/schemas/digest.schema.ts — "Consumers should surface a warning
|
|
3531
|
+
* to the calling agent"). Without them a loosely-related digest reads exactly
|
|
3532
|
+
* like a well-matched one.
|
|
3533
|
+
*
|
|
3534
|
+
* Caveats go to STDERR so they reach the caller in every output format,
|
|
3535
|
+
* including `--format json`, where the digest body must remain the only
|
|
3536
|
+
* top-level JSON on stdout.
|
|
3537
|
+
*
|
|
3538
|
+
* @param metadata - DigestMetadata from the response
|
|
3539
|
+
* @param meta - Optional response-level meta (forward-compat, see below)
|
|
3540
|
+
*/
|
|
3541
|
+
function printRetrievalCaveats(metadata, meta) {
|
|
3542
|
+
const avg = typeof metadata?.averageRelevance === 'number' ? metadata.averageRelevance : null;
|
|
3543
|
+
const avgLabel = avg === null ? 'unknown' : avg.toFixed(3);
|
|
3544
|
+
if (metadata?.narrowWindowFallback) {
|
|
3545
|
+
console.error(chalk_1.default.yellow(`⚠ Low-relevance results: average relevance ${avgLabel} is below the confidence ` +
|
|
3546
|
+
'threshold — this digest was synthesised from weak matches and may be only ' +
|
|
3547
|
+
'loosely related to your query.'));
|
|
3548
|
+
}
|
|
3549
|
+
if (metadata?.crossProjectFallback) {
|
|
3550
|
+
console.error(chalk_1.default.yellow(`⚠ Cross-project results: average relevance ${avgLabel} is below the confidence ` +
|
|
3551
|
+
'threshold and the sources span more than one project — this content may come ' +
|
|
3552
|
+
'from a different project than the one you are working in.'));
|
|
3553
|
+
}
|
|
3554
|
+
// Forward-compat with the retrieval team's degraded-retrieval contract
|
|
3555
|
+
// (meta.degraded / meta.branchFailures). Both fields are absent on today's
|
|
3556
|
+
// responses, so every access is guarded.
|
|
3557
|
+
if (meta?.degraded) {
|
|
3558
|
+
const failures = Array.isArray(meta.branchFailures) ? meta.branchFailures : [];
|
|
3559
|
+
// Branch names are server-supplied and land on stderr, which agents read.
|
|
3560
|
+
// Constrain them to an identifier shape and cap the count rather than
|
|
3561
|
+
// echoing arbitrary text: an unbounded string here is an injection channel
|
|
3562
|
+
// into agent context (and can carry ANSI escapes from a hostile apiUrl).
|
|
3563
|
+
const names = failures
|
|
3564
|
+
.map((b) => (typeof b === 'string' ? b : b?.branch || b?.name))
|
|
3565
|
+
.filter((n) => typeof n === 'string' && /^[A-Za-z0-9_-]{1,32}$/.test(n))
|
|
3566
|
+
.slice(0, 8);
|
|
3567
|
+
const detail = names.length
|
|
3568
|
+
? `failed branches: ${names.join(', ')}`
|
|
3569
|
+
: 'one or more retrieval branches failed';
|
|
3570
|
+
console.error(chalk_1.default.yellow(`⚠ Degraded retrieval — ${detail}. These results are incomplete; re-run before ` +
|
|
3571
|
+
'treating them as a full answer.'));
|
|
3572
|
+
}
|
|
3573
|
+
}
|
|
3490
3574
|
/**
|
|
3491
3575
|
* Display a formatted digest with header box and metadata footer
|
|
3492
3576
|
*/
|
|
@@ -3578,9 +3662,8 @@ function registerRecapCommand(memories) {
|
|
|
3578
3662
|
// Calculate time filter
|
|
3579
3663
|
const recentTimestamp = (0, date_utils_1.getRecentTimestamp)(options.recent);
|
|
3580
3664
|
if (!recentTimestamp) {
|
|
3581
|
-
spinner.
|
|
3582
|
-
|
|
3583
|
-
return;
|
|
3665
|
+
spinner.stop();
|
|
3666
|
+
throw (0, errors_1.validationError)(`Invalid duration format: ${options.recent}. Supported formats: 30m, 1h, 7d, 2w`);
|
|
3584
3667
|
}
|
|
3585
3668
|
const fetchLimit = parseInt(options.limit, 10);
|
|
3586
3669
|
// Build exclude-topics value (used by both paths)
|
|
@@ -3860,10 +3943,15 @@ function registerBuildContextCommand(memories) {
|
|
|
3860
3943
|
}
|
|
3861
3944
|
const sdk = (0, api_client_1.getSdk)();
|
|
3862
3945
|
const response = await retryRead(() => sdk.memories.buildContext(body));
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3946
|
+
// `|| response` used to make the guard below unreachable: on a 2xx with
|
|
3947
|
+
// an empty body it fell back to the truthy response envelope, so the
|
|
3948
|
+
// command printed "Context briefing ready" and an empty briefing (exit
|
|
3949
|
+
// 0). Unwrap first, then test what we actually got (v4.04).
|
|
3950
|
+
const envelope = response;
|
|
3951
|
+
const data = envelope && typeof envelope === 'object' && 'data' in envelope ? envelope.data : envelope;
|
|
3952
|
+
if (!data || typeof data !== 'object') {
|
|
3953
|
+
spinner.stop();
|
|
3954
|
+
throw (0, errors_1.unusableResponseError)('Context briefing', responseShape(data));
|
|
3867
3955
|
}
|
|
3868
3956
|
spinner.succeed('Context briefing ready');
|
|
3869
3957
|
// JSON output
|
|
@@ -3945,8 +4033,7 @@ function registerBuildContextCommand(memories) {
|
|
|
3945
4033
|
try {
|
|
3946
4034
|
const validDimensions = ['day', 'week', 'month', 'topic'];
|
|
3947
4035
|
if (!options.groupBy || !validDimensions.includes(options.groupBy)) {
|
|
3948
|
-
|
|
3949
|
-
return;
|
|
4036
|
+
throw (0, errors_1.validationError)('--group-by is required and must be one of: day, week, month, topic');
|
|
3950
4037
|
}
|
|
3951
4038
|
const spinner = (0, spinner_1.createSpinner)('Fetching memory stats...', options.format).start();
|
|
3952
4039
|
const apiUrl = config_1.config.getApiUrl();
|