@memnexus-ai/cli 1.7.221 → 1.7.223
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 +187 -81
- package/dist/commands/memories.js.map +1 -1
- package/dist/lib/errors.js +35 -0
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/renderable-retrieval.d.ts +164 -0
- package/dist/lib/renderable-retrieval.d.ts.map +1 -0
- package/dist/lib/renderable-retrieval.js +364 -0
- package/dist/lib/renderable-retrieval.js.map +1 -0
- 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/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;AAWpC,OAAO,EACL,gBAAgB,EAChB,yBAAyB,EACzB,YAAY,EACZ,eAAe,EACf,KAAK,EAGN,MAAM,cAAc,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,9 +73,11 @@ 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");
|
|
80
|
+
const renderable_retrieval_1 = require("../lib/renderable-retrieval");
|
|
79
81
|
const js_yaml_1 = __importDefault(require("js-yaml"));
|
|
80
82
|
const spinner_1 = require("../lib/spinner");
|
|
81
83
|
const date_utils_1 = require("../lib/date-utils");
|
|
@@ -1791,9 +1793,20 @@ function registerMemoriesCommands(program) {
|
|
|
1791
1793
|
response = await retryWrite(() => (0, api_client_1.getSdk)().memories.createMemory(memoryData));
|
|
1792
1794
|
}
|
|
1793
1795
|
catch (createError) {
|
|
1794
|
-
// 409
|
|
1795
|
-
|
|
1796
|
-
|
|
1796
|
+
// 409 is OVERLOADED: it is returned both for "named memory already
|
|
1797
|
+
// exists" (retry as an upsert) and for QUOTA_EXCEEDED (do not retry).
|
|
1798
|
+
// This branch used to test the bare status, so a free-tier user at
|
|
1799
|
+
// their cap creating a NAMED memory had the quota rejection retried
|
|
1800
|
+
// against PUT /named/:name — which applies the same quota gate and
|
|
1801
|
+
// rejects again. Two rejections burned, and the failure was reported as
|
|
1802
|
+
// a name collision when the actual cause was capacity. Branch on the
|
|
1803
|
+
// code (see isQuotaError / isNameConflictError in lib/retry.ts), the
|
|
1804
|
+
// same way mcp-server's create_memory does (#2925).
|
|
1805
|
+
if ((0, retry_1.isQuotaError)(createError)) {
|
|
1806
|
+
spinner.fail('Memory limit reached');
|
|
1807
|
+
throw createError;
|
|
1808
|
+
}
|
|
1809
|
+
if ((0, retry_1.isNameConflictError)(createError) && memoryData.name) {
|
|
1797
1810
|
// v3.85 (named-memory operation integrity): the auto-upsert retry must
|
|
1798
1811
|
// PRESERVE every field the user did not explicitly provide and FORWARD
|
|
1799
1812
|
// every field they did. The previous body (a) re-sent the truthy-
|
|
@@ -2459,26 +2472,40 @@ function registerMemoriesCommands(program) {
|
|
|
2459
2472
|
spinner?.stop();
|
|
2460
2473
|
throw (0, errors_1.unusableResponseError)('Memory search', responseShape(searchData));
|
|
2461
2474
|
}
|
|
2462
|
-
//
|
|
2463
|
-
//
|
|
2464
|
-
|
|
2465
|
-
|
|
2475
|
+
// Every render path below takes the renderable, never the raw body: the
|
|
2476
|
+
// factory normalizes {memory, score, matchedVia} AND maps meta.degraded
|
|
2477
|
+
// once, so no format can render a degraded result as healthy (#5124).
|
|
2478
|
+
const search = renderable_retrieval_1.RenderableSearchResult.fromApiResponse(searchData);
|
|
2479
|
+
// The headline is the first thing a caller reads and was unconditional:
|
|
2480
|
+
// "Found N results" over an incomplete gather reads as a full answer.
|
|
2481
|
+
if (search.health.degraded) {
|
|
2482
|
+
spinner?.warn(`Found ${search.results.length} results${(0, renderable_retrieval_1.degradationHeadlineSuffix)(search.health)}`);
|
|
2483
|
+
}
|
|
2484
|
+
else {
|
|
2485
|
+
spinner?.succeed(`Found ${search.results.length} results`);
|
|
2486
|
+
}
|
|
2487
|
+
// Degradation marker — stderr, before any format branch, so it reaches
|
|
2488
|
+
// the caller in every output mode including --id-only and --format json
|
|
2489
|
+
// where stdout must stay machine-parseable.
|
|
2490
|
+
const degradedLine = (0, renderable_retrieval_1.degradationWarningLine)(search.health);
|
|
2491
|
+
if (degradedLine) {
|
|
2492
|
+
console.error(chalk_1.default.yellow(degradedLine));
|
|
2466
2493
|
}
|
|
2467
|
-
spinner?.succeed(`Found ${searchData?.data?.length || 0} results`);
|
|
2468
2494
|
// Surface extraction failures (#2862). Failed memories are invisible to
|
|
2469
2495
|
// fact/entity search, so flag them on stderr where they won't pollute
|
|
2470
2496
|
// piped JSON. Callers in table mode also see an inline indicator below.
|
|
2471
|
-
const failedResultCount =
|
|
2497
|
+
const failedResultCount = search.results.filter((r) => {
|
|
2472
2498
|
const m = r?.memory ?? r;
|
|
2473
2499
|
return m && m.extractionStatus === 'failed';
|
|
2474
2500
|
}).length;
|
|
2475
2501
|
if (failedResultCount > 0 && !options.idOnly) {
|
|
2476
|
-
const total =
|
|
2502
|
+
const total = search.results.length;
|
|
2477
2503
|
console.error(chalk_1.default.yellow(`⚠ ${failedResultCount} of ${total} results have failed extraction (won\u2019t appear in fact/entity searches).`));
|
|
2478
2504
|
}
|
|
2479
|
-
// --id-only mode: output only IDs for piping
|
|
2505
|
+
// --id-only mode: output only IDs for piping. The degradation marker
|
|
2506
|
+
// already went to stderr above, so it survives the pipe.
|
|
2480
2507
|
if (options.idOnly) {
|
|
2481
|
-
const ids =
|
|
2508
|
+
const ids = search.results
|
|
2482
2509
|
.map((result) => result.memory?.id)
|
|
2483
2510
|
.filter((id) => id);
|
|
2484
2511
|
console.log(ids.join('\n'));
|
|
@@ -2486,12 +2513,12 @@ function registerMemoriesCommands(program) {
|
|
|
2486
2513
|
}
|
|
2487
2514
|
// --timeline mode: chronological display with timestamps and effective state
|
|
2488
2515
|
if (options.timeline) {
|
|
2489
|
-
displayTimelineResults(
|
|
2516
|
+
displayTimelineResults(search.results, options.query, previewLength, search.health);
|
|
2490
2517
|
return;
|
|
2491
2518
|
}
|
|
2492
2519
|
// --group-by conversation: group results by conversation ID
|
|
2493
2520
|
if (options.groupBy === 'conversation') {
|
|
2494
|
-
const results =
|
|
2521
|
+
const results = search.results;
|
|
2495
2522
|
const grouped = groupByConversation(results);
|
|
2496
2523
|
if (effectiveFormat === 'json' || effectiveFormat === 'yaml') {
|
|
2497
2524
|
// Convert Map to array for JSON/YAML serialization
|
|
@@ -2500,10 +2527,13 @@ function registerMemoriesCommands(program) {
|
|
|
2500
2527
|
memoryCount: memories.length,
|
|
2501
2528
|
memories: memories,
|
|
2502
2529
|
}));
|
|
2530
|
+
// Array-shaped payload: adding a health object would change the
|
|
2531
|
+
// top-level shape, so the marker for this path is the stderr line
|
|
2532
|
+
// emitted above.
|
|
2503
2533
|
(0, formatters_1.printOutput)(groupedArray, effectiveFormat);
|
|
2504
2534
|
}
|
|
2505
2535
|
else {
|
|
2506
|
-
displayGroupedSearch(grouped, options.query);
|
|
2536
|
+
displayGroupedSearch(grouped, options.query, search.health);
|
|
2507
2537
|
}
|
|
2508
2538
|
return;
|
|
2509
2539
|
}
|
|
@@ -2511,7 +2541,7 @@ function registerMemoriesCommands(program) {
|
|
|
2511
2541
|
if (effectiveFormat === 'llm') {
|
|
2512
2542
|
// LLM format: clean markdown without ANSI codes
|
|
2513
2543
|
// Extract nested memory objects for formatting
|
|
2514
|
-
const llmData =
|
|
2544
|
+
const llmData = search.results.map((result) => {
|
|
2515
2545
|
const m = result.memory;
|
|
2516
2546
|
return {
|
|
2517
2547
|
id: m.id,
|
|
@@ -2520,15 +2550,19 @@ function registerMemoriesCommands(program) {
|
|
|
2520
2550
|
createdAt: m.createdAt,
|
|
2521
2551
|
};
|
|
2522
2552
|
});
|
|
2553
|
+
const llmMarker = (0, renderable_retrieval_1.degradationSummaryLine)(search.health);
|
|
2554
|
+
if (llmMarker) {
|
|
2555
|
+
console.log(llmMarker);
|
|
2556
|
+
}
|
|
2523
2557
|
(0, formatters_1.printOutput)(llmData, 'llm', undefined, {
|
|
2524
|
-
totalCount:
|
|
2558
|
+
totalCount: search.total,
|
|
2525
2559
|
previewLength: Math.min(previewLength, 100), // Shorter preview for LLM
|
|
2526
2560
|
title: 'Search Results',
|
|
2527
2561
|
});
|
|
2528
2562
|
}
|
|
2529
2563
|
else if (effectiveFormat === 'compact') {
|
|
2530
2564
|
// Compact format: ID + date + first line
|
|
2531
|
-
const compactData =
|
|
2565
|
+
const compactData = search.results.map((result) => {
|
|
2532
2566
|
const m = result.memory;
|
|
2533
2567
|
return {
|
|
2534
2568
|
id: m.id,
|
|
@@ -2536,13 +2570,17 @@ function registerMemoriesCommands(program) {
|
|
|
2536
2570
|
createdAt: m.createdAt,
|
|
2537
2571
|
};
|
|
2538
2572
|
});
|
|
2573
|
+
const compactMarker = (0, renderable_retrieval_1.degradationSummaryLine)(search.health);
|
|
2574
|
+
if (compactMarker) {
|
|
2575
|
+
console.log(chalk_1.default.yellow(compactMarker));
|
|
2576
|
+
}
|
|
2539
2577
|
(0, formatters_1.printOutput)(compactData, 'compact');
|
|
2540
2578
|
}
|
|
2541
2579
|
else if (effectiveFormat === 'table' || !effectiveFormat) {
|
|
2542
2580
|
// Table format: default display with colors and borders
|
|
2543
2581
|
// Extract nested memory object from search results
|
|
2544
2582
|
// API returns: {data: [{memory: {...}, score: 0.66}]}
|
|
2545
|
-
const tableData =
|
|
2583
|
+
const tableData = search.results.map((result) => {
|
|
2546
2584
|
const m = result.memory; // Extract the nested memory object
|
|
2547
2585
|
const score = result.score;
|
|
2548
2586
|
// Smart truncation with term highlighting
|
|
@@ -2578,19 +2616,23 @@ function registerMemoriesCommands(program) {
|
|
|
2578
2616
|
columnWidths: tableWidths,
|
|
2579
2617
|
});
|
|
2580
2618
|
// Show search metadata
|
|
2581
|
-
const resultCount =
|
|
2582
|
-
const searchMethodUsed =
|
|
2619
|
+
const resultCount = search.total;
|
|
2620
|
+
const searchMethodUsed = search.searchMethod || effectiveMethod;
|
|
2583
2621
|
// Detect graph branches used
|
|
2584
2622
|
const allBranches = new Set();
|
|
2585
|
-
for (const r of
|
|
2623
|
+
for (const r of search.results) {
|
|
2586
2624
|
if (r.matchedVia) {
|
|
2587
2625
|
r.matchedVia.forEach((b) => allBranches.add(b));
|
|
2588
2626
|
}
|
|
2589
2627
|
}
|
|
2590
2628
|
const branchInfo = allBranches.size > 2 ? ` (branches: ${[...allBranches].join(', ')})` : '';
|
|
2591
2629
|
console.log(chalk_1.default.gray(`\nFound ${resultCount} results using ${searchMethodUsed} search${branchInfo}`));
|
|
2630
|
+
const tableMarker = (0, renderable_retrieval_1.degradationSummaryLine)(search.health);
|
|
2631
|
+
if (tableMarker) {
|
|
2632
|
+
console.log(chalk_1.default.yellow(tableMarker));
|
|
2633
|
+
}
|
|
2592
2634
|
// Show temporal filters if applied
|
|
2593
|
-
const temporal =
|
|
2635
|
+
const temporal = search.temporalMetadata;
|
|
2594
2636
|
if (temporal?.temporalFieldsIncluded) {
|
|
2595
2637
|
const filters = [];
|
|
2596
2638
|
if (temporal.eventTimeRange?.from)
|
|
@@ -2605,7 +2647,9 @@ function registerMemoriesCommands(program) {
|
|
|
2605
2647
|
else {
|
|
2606
2648
|
// JSON/YAML formats: pass raw or content-only data
|
|
2607
2649
|
if (options.contentOnly) {
|
|
2608
|
-
|
|
2650
|
+
// Array-shaped payload by contract ({id, createdAt, topics,
|
|
2651
|
+
// content}); the marker for this path is the stderr line above.
|
|
2652
|
+
const contentOnlyData = search.results.map((result) => {
|
|
2609
2653
|
const m = result.memory;
|
|
2610
2654
|
const obj = {
|
|
2611
2655
|
id: m.id,
|
|
@@ -2621,7 +2665,8 @@ function registerMemoriesCommands(program) {
|
|
|
2621
2665
|
(0, formatters_1.printOutput)(contentOnlyData, effectiveFormat);
|
|
2622
2666
|
}
|
|
2623
2667
|
else {
|
|
2624
|
-
|
|
2668
|
+
// Object-shaped payload: carries the health object when degraded.
|
|
2669
|
+
(0, formatters_1.printOutput)(search.toSerializable(), effectiveFormat);
|
|
2625
2670
|
}
|
|
2626
2671
|
}
|
|
2627
2672
|
}
|
|
@@ -2725,7 +2770,13 @@ function escapeRegex(str) {
|
|
|
2725
2770
|
/**
|
|
2726
2771
|
* Display search results in timeline format (chronological with timestamps and state)
|
|
2727
2772
|
*/
|
|
2728
|
-
function displayTimelineResults(results, query, previewLength) {
|
|
2773
|
+
function displayTimelineResults(results, query, previewLength, health) {
|
|
2774
|
+
// The marker prints before the "no results" return: an empty timeline over a
|
|
2775
|
+
// degraded gather is exactly the case that must not read as "nothing exists".
|
|
2776
|
+
const marker = (0, renderable_retrieval_1.degradationSummaryLine)(health);
|
|
2777
|
+
if (marker) {
|
|
2778
|
+
console.log(chalk_1.default.yellow(`\n${marker}`));
|
|
2779
|
+
}
|
|
2729
2780
|
if (results.length === 0) {
|
|
2730
2781
|
console.log(chalk_1.default.yellow('\nNo results found.'));
|
|
2731
2782
|
return;
|
|
@@ -3387,20 +3438,28 @@ function registerDigestCommand(memories) {
|
|
|
3387
3438
|
spinner.stop();
|
|
3388
3439
|
throw (0, errors_1.unusableResponseError)('Digest generation', responseShape(data));
|
|
3389
3440
|
}
|
|
3390
|
-
|
|
3441
|
+
// One construction path; the meta-vs-metadata mapping lives in the
|
|
3442
|
+
// factory (#5124).
|
|
3443
|
+
const renderable = renderable_retrieval_1.RenderableDigestResult.fromApiResponse(data);
|
|
3444
|
+
const { digest, metadata, sources, keyFacts, entities, effectiveStateBreakdown } = renderable;
|
|
3391
3445
|
if (metadata.sourcesUsed === 0) {
|
|
3392
3446
|
// Zero sources is a genuine empty result (exit 0), but it is not a
|
|
3393
3447
|
// success: the old "Digest generated from 0 memories" success glyph
|
|
3394
3448
|
// read as "here is your answer" when nothing was retrieved.
|
|
3395
3449
|
spinner.info('No sources met the relevance threshold for this query');
|
|
3396
3450
|
}
|
|
3451
|
+
else if (renderable.health.degraded) {
|
|
3452
|
+
spinner.warn(`Digest generated from ${metadata.sourcesUsed} memories across ` +
|
|
3453
|
+
`${metadata.conversationsSpanned} conversations` +
|
|
3454
|
+
(0, renderable_retrieval_1.degradationHeadlineSuffix)(renderable.health));
|
|
3455
|
+
}
|
|
3397
3456
|
else {
|
|
3398
3457
|
spinner.succeed(`Digest generated from ${metadata.sourcesUsed} memories across ${metadata.conversationsSpanned} conversations`);
|
|
3399
3458
|
}
|
|
3400
|
-
printRetrievalCaveats(
|
|
3459
|
+
printRetrievalCaveats(renderable);
|
|
3401
3460
|
// JSON/YAML output
|
|
3402
3461
|
if (options.format === 'json' || options.format === 'yaml') {
|
|
3403
|
-
(0, formatters_1.printOutput)(
|
|
3462
|
+
(0, formatters_1.printOutput)(renderable.toSerializable(), options.format);
|
|
3404
3463
|
return;
|
|
3405
3464
|
}
|
|
3406
3465
|
// LLM output — raw digest text only
|
|
@@ -3409,7 +3468,7 @@ function registerDigestCommand(memories) {
|
|
|
3409
3468
|
return;
|
|
3410
3469
|
}
|
|
3411
3470
|
// Default display with box formatting
|
|
3412
|
-
displayDigest(digest, metadata, sources, options, keyFacts, entities, effectiveStateBreakdown);
|
|
3471
|
+
displayDigest(digest, metadata, sources, options, keyFacts, entities, effectiveStateBreakdown, renderable.health);
|
|
3413
3472
|
}
|
|
3414
3473
|
catch (error) {
|
|
3415
3474
|
// KI-77: name the ceiling and give a narrower-query fix instead of the SDK's
|
|
@@ -3486,23 +3545,29 @@ function registerStatusCommand(memories) {
|
|
|
3486
3545
|
spinner.stop();
|
|
3487
3546
|
throw (0, errors_1.unusableResponseError)('Status report generation', responseShape(data));
|
|
3488
3547
|
}
|
|
3489
|
-
const
|
|
3548
|
+
const renderable = renderable_retrieval_1.RenderableDigestResult.fromApiResponse(data);
|
|
3549
|
+
const { digest, metadata, sources, keyFacts, entities, effectiveStateBreakdown } = renderable;
|
|
3490
3550
|
if (metadata.sourcesUsed === 0) {
|
|
3491
3551
|
spinner.info('No sources met the relevance threshold for this query');
|
|
3492
3552
|
}
|
|
3553
|
+
else if (renderable.health.degraded) {
|
|
3554
|
+
spinner.warn(`Status report from ${metadata.sourcesUsed} memories across ` +
|
|
3555
|
+
`${metadata.conversationsSpanned} conversations` +
|
|
3556
|
+
(0, renderable_retrieval_1.degradationHeadlineSuffix)(renderable.health));
|
|
3557
|
+
}
|
|
3493
3558
|
else {
|
|
3494
3559
|
spinner.succeed(`Status report from ${metadata.sourcesUsed} memories across ${metadata.conversationsSpanned} conversations`);
|
|
3495
3560
|
}
|
|
3496
|
-
printRetrievalCaveats(
|
|
3561
|
+
printRetrievalCaveats(renderable);
|
|
3497
3562
|
if (options.format === 'json' || options.format === 'yaml') {
|
|
3498
|
-
(0, formatters_1.printOutput)(
|
|
3563
|
+
(0, formatters_1.printOutput)(renderable.toSerializable(), options.format);
|
|
3499
3564
|
return;
|
|
3500
3565
|
}
|
|
3501
3566
|
if (options.format === 'llm') {
|
|
3502
3567
|
console.log(digest);
|
|
3503
3568
|
return;
|
|
3504
3569
|
}
|
|
3505
|
-
displayDigest(digest, metadata, sources, options, keyFacts, entities, effectiveStateBreakdown);
|
|
3570
|
+
displayDigest(digest, metadata, sources, options, keyFacts, entities, effectiveStateBreakdown, renderable.health);
|
|
3506
3571
|
}
|
|
3507
3572
|
catch (error) {
|
|
3508
3573
|
(0, errors_1.handleError)(error);
|
|
@@ -3535,10 +3600,16 @@ function responseShape(body) {
|
|
|
3535
3600
|
* including `--format json`, where the digest body must remain the only
|
|
3536
3601
|
* top-level JSON on stdout.
|
|
3537
3602
|
*
|
|
3538
|
-
*
|
|
3539
|
-
*
|
|
3603
|
+
* #5124: the degraded branch below read `meta.degraded` on a response that
|
|
3604
|
+
* carries the flag at `metadata.degraded`, so it had never fired on any real
|
|
3605
|
+
* response. It now reads `renderable.health`, which the factory populates from
|
|
3606
|
+
* the field the wire actually uses. Taking the renderable rather than two loose
|
|
3607
|
+
* `any` bags is what stops the same mistake being made again.
|
|
3608
|
+
*
|
|
3609
|
+
* @param renderable - The digest response, health already mapped
|
|
3540
3610
|
*/
|
|
3541
|
-
function printRetrievalCaveats(
|
|
3611
|
+
function printRetrievalCaveats(renderable) {
|
|
3612
|
+
const metadata = renderable.metadata;
|
|
3542
3613
|
const avg = typeof metadata?.averageRelevance === 'number' ? metadata.averageRelevance : null;
|
|
3543
3614
|
const avgLabel = avg === null ? 'unknown' : avg.toFixed(3);
|
|
3544
3615
|
if (metadata?.narrowWindowFallback) {
|
|
@@ -3551,30 +3622,18 @@ function printRetrievalCaveats(metadata, meta) {
|
|
|
3551
3622
|
'threshold and the sources span more than one project — this content may come ' +
|
|
3552
3623
|
'from a different project than the one you are working in.'));
|
|
3553
3624
|
}
|
|
3554
|
-
//
|
|
3555
|
-
//
|
|
3556
|
-
//
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
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.'));
|
|
3625
|
+
// Degraded retrieval. Branch-name sanitising (identifier shape, capped count)
|
|
3626
|
+
// lives in the renderable factory now — branch names are server-supplied and
|
|
3627
|
+
// land on stderr, which agents read.
|
|
3628
|
+
const degradedLine = (0, renderable_retrieval_1.degradationWarningLine)(renderable.health);
|
|
3629
|
+
if (degradedLine) {
|
|
3630
|
+
console.error(chalk_1.default.yellow(degradedLine));
|
|
3572
3631
|
}
|
|
3573
3632
|
}
|
|
3574
3633
|
/**
|
|
3575
3634
|
* Display a formatted digest with header box and metadata footer
|
|
3576
3635
|
*/
|
|
3577
|
-
function displayDigest(digest, metadata, sources, options, keyFacts, entities, effectiveStateBreakdown) {
|
|
3636
|
+
function displayDigest(digest, metadata, sources, options, keyFacts, entities, effectiveStateBreakdown, health) {
|
|
3578
3637
|
const width = 66;
|
|
3579
3638
|
const border = '═'.repeat(width);
|
|
3580
3639
|
// Format header info
|
|
@@ -3588,6 +3647,12 @@ function displayDigest(digest, metadata, sources, options, keyFacts, entities, e
|
|
|
3588
3647
|
console.log(chalk_1.default.cyan(`║ ${headerLine1.padEnd(width - 2)}║`));
|
|
3589
3648
|
console.log(chalk_1.default.cyan(`║ ${headerLine2.padEnd(width - 2)}║`));
|
|
3590
3649
|
console.log(chalk_1.default.cyan(`╚${border}╝`));
|
|
3650
|
+
// Degradation marker inside the box output, so a reader of the digest text
|
|
3651
|
+
// itself sees it — not only the caller reading stderr.
|
|
3652
|
+
const digestMarker = (0, renderable_retrieval_1.degradationSummaryLine)(health);
|
|
3653
|
+
if (digestMarker) {
|
|
3654
|
+
console.log(chalk_1.default.yellow(digestMarker));
|
|
3655
|
+
}
|
|
3591
3656
|
// Digest content
|
|
3592
3657
|
console.log('');
|
|
3593
3658
|
console.log(digest);
|
|
@@ -3676,7 +3741,7 @@ function registerRecapCommand(memories) {
|
|
|
3676
3741
|
: undefined;
|
|
3677
3742
|
// Fetch memories: list endpoint (time-range browse) when no query is given;
|
|
3678
3743
|
// search endpoint (semantic ranking) when --query is provided.
|
|
3679
|
-
let
|
|
3744
|
+
let renderable;
|
|
3680
3745
|
if (options.query) {
|
|
3681
3746
|
const searchRequest = {
|
|
3682
3747
|
q: options.query,
|
|
@@ -3689,8 +3754,8 @@ function registerRecapCommand(memories) {
|
|
|
3689
3754
|
if (excludeTopicsParam)
|
|
3690
3755
|
searchRequest.excludeTopics = excludeTopicsParam;
|
|
3691
3756
|
const response = await retryRead(() => (0, api_client_1.getSdk)().memories.searchMemoriesGet(searchRequest));
|
|
3692
|
-
//
|
|
3693
|
-
|
|
3757
|
+
// Normalisation and the meta.degraded mapping both live in the factory.
|
|
3758
|
+
renderable = renderable_retrieval_1.RenderableSearchResult.fromApiResponse(response.data);
|
|
3694
3759
|
}
|
|
3695
3760
|
else {
|
|
3696
3761
|
// Issue #1628: search endpoint requires a query string; use list endpoint for
|
|
@@ -3705,9 +3770,12 @@ function registerRecapCommand(memories) {
|
|
|
3705
3770
|
if (excludeTopicsParam)
|
|
3706
3771
|
listRequest.excludeTopics = excludeTopicsParam;
|
|
3707
3772
|
const response = await retryRead(() => (0, api_client_1.getSdk)().memories.listMemories(listRequest));
|
|
3708
|
-
// Wrap in search-result shape expected by groupByConversation / displayRecap
|
|
3709
|
-
|
|
3773
|
+
// Wrap in search-result shape expected by groupByConversation / displayRecap.
|
|
3774
|
+
// The list endpoint runs no search branches, so it is healthy by construction.
|
|
3775
|
+
renderable = renderable_retrieval_1.RenderableSearchResult.fromListResponse(response.data);
|
|
3710
3776
|
}
|
|
3777
|
+
const health = renderable.health;
|
|
3778
|
+
let results = [...renderable.results];
|
|
3711
3779
|
// Client-side filtering as fallback (API exclude may not work with wildcard query)
|
|
3712
3780
|
if (options.excludeTopics) {
|
|
3713
3781
|
const excludeSet = new Set(options.excludeTopics.split(',').map((t) => t.trim().toLowerCase()));
|
|
@@ -3717,7 +3785,18 @@ function registerRecapCommand(memories) {
|
|
|
3717
3785
|
});
|
|
3718
3786
|
}
|
|
3719
3787
|
const truncated = results.length >= fetchLimit;
|
|
3720
|
-
|
|
3788
|
+
if (health.degraded) {
|
|
3789
|
+
spinner.warn(`Found ${results.length} memories${(0, renderable_retrieval_1.degradationHeadlineSuffix)(health)}`);
|
|
3790
|
+
}
|
|
3791
|
+
else {
|
|
3792
|
+
spinner.succeed(`Found ${results.length} memories`);
|
|
3793
|
+
}
|
|
3794
|
+
// stderr, before the zero-result return: "no memories in the last 24h"
|
|
3795
|
+
// over an incomplete gather is the exact wrong conclusion to record.
|
|
3796
|
+
const recapDegraded = (0, renderable_retrieval_1.degradationWarningLine)(health);
|
|
3797
|
+
if (recapDegraded) {
|
|
3798
|
+
console.error(chalk_1.default.yellow(recapDegraded));
|
|
3799
|
+
}
|
|
3721
3800
|
if (results.length === 0) {
|
|
3722
3801
|
console.log(chalk_1.default.yellow(`\nNo memories found in the last ${options.recent}.`));
|
|
3723
3802
|
return;
|
|
@@ -3734,7 +3813,7 @@ function registerRecapCommand(memories) {
|
|
|
3734
3813
|
// Group memories by conversation
|
|
3735
3814
|
const conversations = groupByConversation(results);
|
|
3736
3815
|
// Display recap
|
|
3737
|
-
displayRecap(conversations, options.recent);
|
|
3816
|
+
displayRecap(conversations, options.recent, health);
|
|
3738
3817
|
}
|
|
3739
3818
|
catch (error) {
|
|
3740
3819
|
(0, errors_1.handleError)(error);
|
|
@@ -3767,8 +3846,12 @@ function groupByConversation(results) {
|
|
|
3767
3846
|
/**
|
|
3768
3847
|
* Display recap output grouped by conversation
|
|
3769
3848
|
*/
|
|
3770
|
-
function displayRecap(conversations, duration) {
|
|
3849
|
+
function displayRecap(conversations, duration, health) {
|
|
3771
3850
|
console.log(chalk_1.default.cyan(`\n📅 Work Recap (last ${duration})`));
|
|
3851
|
+
const recapMarker = (0, renderable_retrieval_1.degradationSummaryLine)(health);
|
|
3852
|
+
if (recapMarker) {
|
|
3853
|
+
console.log(chalk_1.default.yellow(recapMarker));
|
|
3854
|
+
}
|
|
3772
3855
|
console.log(chalk_1.default.gray('═'.repeat(60)));
|
|
3773
3856
|
// Sort conversations by most recent activity
|
|
3774
3857
|
const sortedConversations = Array.from(conversations.entries()).sort((a, b) => {
|
|
@@ -3864,8 +3947,12 @@ function extractReferences(memories) {
|
|
|
3864
3947
|
/**
|
|
3865
3948
|
* Display search results grouped by conversation
|
|
3866
3949
|
*/
|
|
3867
|
-
function displayGroupedSearch(conversations, query) {
|
|
3950
|
+
function displayGroupedSearch(conversations, query, health) {
|
|
3868
3951
|
console.log(chalk_1.default.cyan(`\n🔍 Search Results for "${query}" (grouped by conversation)`));
|
|
3952
|
+
const groupedMarker = (0, renderable_retrieval_1.degradationSummaryLine)(health);
|
|
3953
|
+
if (groupedMarker) {
|
|
3954
|
+
console.log(chalk_1.default.yellow(groupedMarker));
|
|
3955
|
+
}
|
|
3869
3956
|
console.log(chalk_1.default.gray('═'.repeat(60)));
|
|
3870
3957
|
// Sort conversations by most recent activity
|
|
3871
3958
|
const sortedConversations = Array.from(conversations.entries()).sort((a, b) => {
|
|
@@ -3953,17 +4040,36 @@ function registerBuildContextCommand(memories) {
|
|
|
3953
4040
|
spinner.stop();
|
|
3954
4041
|
throw (0, errors_1.unusableResponseError)('Context briefing', responseShape(data));
|
|
3955
4042
|
}
|
|
3956
|
-
|
|
4043
|
+
// The API has been sending meta.degraded (with timedOutSections /
|
|
4044
|
+
// failedSections) here all along; the CLI dropped it. A briefing missing
|
|
4045
|
+
// its gotchas section reads exactly like a project with no gotchas.
|
|
4046
|
+
const briefing = renderable_retrieval_1.RenderableContextBriefing.fromApiResponse(data);
|
|
4047
|
+
const briefingHealth = briefing.health;
|
|
4048
|
+
if (briefingHealth.degraded) {
|
|
4049
|
+
spinner.warn(`Context briefing ready${(0, renderable_retrieval_1.degradationHeadlineSuffix)(briefingHealth)}`);
|
|
4050
|
+
}
|
|
4051
|
+
else {
|
|
4052
|
+
spinner.succeed('Context briefing ready');
|
|
4053
|
+
}
|
|
4054
|
+
const briefingDegraded = (0, renderable_retrieval_1.degradationWarningLine)(briefingHealth);
|
|
4055
|
+
if (briefingDegraded) {
|
|
4056
|
+
console.error(chalk_1.default.yellow(briefingDegraded));
|
|
4057
|
+
}
|
|
3957
4058
|
// JSON output
|
|
3958
4059
|
if (options.format === 'json' || options.format === 'yaml') {
|
|
3959
|
-
(0, formatters_1.printOutput)(
|
|
4060
|
+
(0, formatters_1.printOutput)(briefing.toSerializable(), options.format);
|
|
3960
4061
|
return;
|
|
3961
4062
|
}
|
|
3962
4063
|
// Human-readable output
|
|
3963
4064
|
console.log('');
|
|
4065
|
+
const briefingMarker = (0, renderable_retrieval_1.degradationSummaryLine)(briefingHealth);
|
|
4066
|
+
if (briefingMarker) {
|
|
4067
|
+
console.log(chalk_1.default.yellow(briefingMarker));
|
|
4068
|
+
console.log('');
|
|
4069
|
+
}
|
|
3964
4070
|
// Active Work
|
|
3965
|
-
if (data.activeWork) {
|
|
3966
|
-
const aw = data.activeWork;
|
|
4071
|
+
if (briefing.data.activeWork) {
|
|
4072
|
+
const aw = briefing.data.activeWork;
|
|
3967
4073
|
console.log(chalk_1.default.yellow.bold('Active Work'));
|
|
3968
4074
|
console.log(` You were working on: ${chalk_1.default.white(aw.title || '(untitled)')} (${chalk_1.default.gray(aw.conversationId)})`);
|
|
3969
4075
|
if (aw.lastMemory?.summary) {
|
|
@@ -3972,43 +4078,43 @@ function registerBuildContextCommand(memories) {
|
|
|
3972
4078
|
console.log('');
|
|
3973
4079
|
}
|
|
3974
4080
|
// Key Facts
|
|
3975
|
-
if (data.relevantFacts?.length) {
|
|
4081
|
+
if (briefing.data.relevantFacts?.length) {
|
|
3976
4082
|
console.log(chalk_1.default.cyan.bold('Key Facts'));
|
|
3977
|
-
for (const f of data.relevantFacts) {
|
|
4083
|
+
for (const f of briefing.data.relevantFacts) {
|
|
3978
4084
|
console.log(` ${chalk_1.default.gray('•')} ${f.subject} ${f.predicate} ${f.object}`);
|
|
3979
4085
|
}
|
|
3980
4086
|
console.log('');
|
|
3981
4087
|
}
|
|
3982
4088
|
// Gotchas
|
|
3983
|
-
if (data.gotchas?.length) {
|
|
4089
|
+
if (briefing.data.gotchas?.length) {
|
|
3984
4090
|
console.log(chalk_1.default.red.bold('Gotchas'));
|
|
3985
|
-
for (const g of data.gotchas) {
|
|
4091
|
+
for (const g of briefing.data.gotchas) {
|
|
3986
4092
|
console.log(` ${chalk_1.default.red('⚠')} ${chalk_1.default.bold(g.fact)} ${chalk_1.default.gray(`(${g.sourceCount} sources)`)}`);
|
|
3987
4093
|
}
|
|
3988
4094
|
console.log('');
|
|
3989
4095
|
}
|
|
3990
4096
|
// Recent Activity
|
|
3991
|
-
if (data.recentActivity?.length) {
|
|
3992
|
-
const hours = data.meta?.recentHours || 24;
|
|
4097
|
+
if (briefing.data.recentActivity?.length) {
|
|
4098
|
+
const hours = briefing.data.meta?.recentHours || 24;
|
|
3993
4099
|
console.log(chalk_1.default.green.bold(`Recent Activity (last ${hours}h)`));
|
|
3994
|
-
for (const a of data.recentActivity) {
|
|
4100
|
+
for (const a of briefing.data.recentActivity) {
|
|
3995
4101
|
const date = a.date ? (0, date_utils_1.formatApiDate)(a.date) : '';
|
|
3996
4102
|
console.log(` ${chalk_1.default.gray('•')} ${a.summary} ${chalk_1.default.gray(`(${date})`)}`);
|
|
3997
4103
|
}
|
|
3998
4104
|
console.log('');
|
|
3999
4105
|
}
|
|
4000
4106
|
// Patterns
|
|
4001
|
-
if (data.relatedPatterns?.length) {
|
|
4107
|
+
if (briefing.data.relatedPatterns?.length) {
|
|
4002
4108
|
console.log(chalk_1.default.magenta.bold('Relevant Patterns'));
|
|
4003
|
-
for (const p of data.relatedPatterns) {
|
|
4109
|
+
for (const p of briefing.data.relatedPatterns) {
|
|
4004
4110
|
const conf = p.confidence != null ? chalk_1.default.gray(` (${(p.confidence * 100).toFixed(0)}%)`) : '';
|
|
4005
4111
|
console.log(` ${chalk_1.default.gray('•')} ${p.pattern}${conf}`);
|
|
4006
4112
|
}
|
|
4007
4113
|
console.log('');
|
|
4008
4114
|
}
|
|
4009
4115
|
// Meta
|
|
4010
|
-
if (data.meta?.contextTerms?.length) {
|
|
4011
|
-
console.log(chalk_1.default.gray(`Search terms: ${data.meta.contextTerms.join(', ')}`));
|
|
4116
|
+
if (briefing.data.meta?.contextTerms?.length) {
|
|
4117
|
+
console.log(chalk_1.default.gray(`Search terms: ${briefing.data.meta.contextTerms.join(', ')}`));
|
|
4012
4118
|
}
|
|
4013
4119
|
}
|
|
4014
4120
|
catch (error) {
|