@memnexus-ai/cli 1.7.218 → 1.7.219
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 -14
- package/dist/commands/memories.d.ts.map +1 -1
- package/dist/commands/memories.js +119 -101
- package/dist/commands/memories.js.map +1 -1
- package/dist/lib/retry.d.ts +98 -0
- package/dist/lib/retry.d.ts.map +1 -0
- package/dist/lib/retry.js +183 -0
- package/dist/lib/retry.js.map +1 -0
- package/package.json +2 -2
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
+
import { isRateLimitError, isServiceUnavailableError, isQuotaError, getRetryAfterMs, sleep } from '../lib/retry';
|
|
2
3
|
/** Number of deletions within the window that triggers the guard. */
|
|
3
4
|
export declare const BULK_DELETE_GUARD_THRESHOLD = 5;
|
|
4
5
|
/** Rolling window in milliseconds (60 seconds). */
|
|
@@ -66,16 +67,7 @@ export declare function parseBatchIds(values: string[] | undefined): string[];
|
|
|
66
67
|
export declare function isValidIsoDate(value: string): boolean;
|
|
67
68
|
/** Split ids into pages of at most `size` (default 100). */
|
|
68
69
|
export declare function chunkIds(ids: string[], size?: number): string[][];
|
|
69
|
-
|
|
70
|
-
export declare function isRateLimitError(error: any): boolean;
|
|
71
|
-
/**
|
|
72
|
-
* Extract the server's Retry-After hint in milliseconds, or null if absent.
|
|
73
|
-
* The API surfaces it as a body field (`retryAfter`, seconds) on 429s
|
|
74
|
-
* (see lib/errors.ts) and may also send a Retry-After header; we honor both.
|
|
75
|
-
*/
|
|
76
|
-
export declare function getRetryAfterMs(error: any): number | null;
|
|
77
|
-
/** Promise-based sleep. Extracted so tests can inject a no-op/spy. */
|
|
78
|
-
export declare function sleep(ms: number): Promise<void>;
|
|
70
|
+
export { isRateLimitError, isServiceUnavailableError, isQuotaError, getRetryAfterMs, sleep };
|
|
79
71
|
export interface BulkDeleteDeps {
|
|
80
72
|
/** Deletes one page (≤100 ids) and returns the server's counts. */
|
|
81
73
|
deleteFn: (ids: string[]) => Promise<BulkDeleteResult>;
|
|
@@ -152,12 +144,8 @@ export declare const BATCH_CREATE_MAX_LINE_BYTES: number;
|
|
|
152
144
|
* single-create, out of scope for the streaming path (noted in the PR).
|
|
153
145
|
*/
|
|
154
146
|
export declare const BATCH_CREATE_ALLOWED_KEYS: Set<string>;
|
|
155
|
-
/** True when an error is an HTTP 503 (service unavailable — transient). */
|
|
156
|
-
export declare function isServiceUnavailableError(error: any): boolean;
|
|
157
147
|
/** True when a create should be retried: 429 (rate limited) or 503 (transient). */
|
|
158
148
|
export declare function isRetryableCreateError(error: any): boolean;
|
|
159
|
-
/** True when an error is an HTTP 402 (quota / payment required). */
|
|
160
|
-
export declare function isQuotaError(error: any): boolean;
|
|
161
149
|
export interface BatchCreateItem {
|
|
162
150
|
/** 1-based input line number (blank lines are skipped, so this is not dense). */
|
|
163
151
|
line: number;
|
|
@@ -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.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.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;
|
|
@@ -45,15 +45,10 @@ exports.parseCodeContextFlag = parseCodeContextFlag;
|
|
|
45
45
|
exports.parseBatchIds = parseBatchIds;
|
|
46
46
|
exports.isValidIsoDate = isValidIsoDate;
|
|
47
47
|
exports.chunkIds = chunkIds;
|
|
48
|
-
exports.isRateLimitError = isRateLimitError;
|
|
49
|
-
exports.getRetryAfterMs = getRetryAfterMs;
|
|
50
|
-
exports.sleep = sleep;
|
|
51
48
|
exports.bulkDeleteWithRetry = bulkDeleteWithRetry;
|
|
52
49
|
exports.runBulkDeletePaged = runBulkDeletePaged;
|
|
53
50
|
exports.collectAllMemoryIds = collectAllMemoryIds;
|
|
54
|
-
exports.isServiceUnavailableError = isServiceUnavailableError;
|
|
55
51
|
exports.isRetryableCreateError = isRetryableCreateError;
|
|
56
|
-
exports.isQuotaError = isQuotaError;
|
|
57
52
|
exports.parseJsonlCreateLine = parseJsonlCreateLine;
|
|
58
53
|
exports.resolveCreateConcurrency = resolveCreateConcurrency;
|
|
59
54
|
exports.batchCreateWithRetry = batchCreateWithRetry;
|
|
@@ -74,6 +69,12 @@ const chalk_1 = __importDefault(require("chalk"));
|
|
|
74
69
|
const api_client_1 = require("../lib/api-client");
|
|
75
70
|
const config_1 = require("../lib/config");
|
|
76
71
|
const errors_1 = require("../lib/errors");
|
|
72
|
+
const retry_1 = require("../lib/retry");
|
|
73
|
+
Object.defineProperty(exports, "isRateLimitError", { enumerable: true, get: function () { return retry_1.isRateLimitError; } });
|
|
74
|
+
Object.defineProperty(exports, "isServiceUnavailableError", { enumerable: true, get: function () { return retry_1.isServiceUnavailableError; } });
|
|
75
|
+
Object.defineProperty(exports, "isQuotaError", { enumerable: true, get: function () { return retry_1.isQuotaError; } });
|
|
76
|
+
Object.defineProperty(exports, "getRetryAfterMs", { enumerable: true, get: function () { return retry_1.getRetryAfterMs; } });
|
|
77
|
+
Object.defineProperty(exports, "sleep", { enumerable: true, get: function () { return retry_1.sleep; } });
|
|
77
78
|
const formatters_1 = require("../lib/formatters");
|
|
78
79
|
const js_yaml_1 = __importDefault(require("js-yaml"));
|
|
79
80
|
const spinner_1 = require("../lib/spinner");
|
|
@@ -270,11 +271,11 @@ async function resolveShortIds(ids, opts = {}) {
|
|
|
270
271
|
}
|
|
271
272
|
else {
|
|
272
273
|
// Resolve short ID
|
|
273
|
-
const listResponse = await (0, api_client_1.getSdk)().memories.listMemories({
|
|
274
|
+
const listResponse = await retryRead(() => (0, api_client_1.getSdk)().memories.listMemories({
|
|
274
275
|
limit: 10,
|
|
275
276
|
offset: 0,
|
|
276
277
|
idPrefix: id,
|
|
277
|
-
});
|
|
278
|
+
}));
|
|
278
279
|
const matches = listResponse.data?.data || [];
|
|
279
280
|
if (matches.length === 0) {
|
|
280
281
|
warn(`Warning: No memory found with prefix "${id}"`);
|
|
@@ -392,10 +393,10 @@ async function handleBatchMemoryGet(ids, options) {
|
|
|
392
393
|
}
|
|
393
394
|
spinner.text = `Fetching ${resolvedIds.length} memories...`;
|
|
394
395
|
const batchDetail = options.detail || 'minimal';
|
|
395
|
-
const response = await (0, api_client_1.getSdk)().memories.getMemoriesBatch({
|
|
396
|
+
const response = await retryRead(() => (0, api_client_1.getSdk)().memories.getMemoriesBatch({
|
|
396
397
|
ids: resolvedIds,
|
|
397
398
|
detail: batchDetail,
|
|
398
|
-
});
|
|
399
|
+
}));
|
|
399
400
|
const result = response.data;
|
|
400
401
|
if (result && result.meta && result.meta.missing && result.meta.missing.length > 0) {
|
|
401
402
|
spinner.warn(`Found ${result.meta.found} of ${result.meta.requested} memories`);
|
|
@@ -506,42 +507,46 @@ function chunkIds(ids, size = exports.BULK_DELETE_MAX_IDS) {
|
|
|
506
507
|
}
|
|
507
508
|
return chunks;
|
|
508
509
|
}
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
510
|
+
// ── Single-shot retry wiring (PO CLI-latency directive, item 3) ──────────────
|
|
511
|
+
// These thin wrappers apply the shared withSingleShotRetry helper at the plain
|
|
512
|
+
// single-request SDK call sites. The idempotency flag is passed PER CALL SITE —
|
|
513
|
+
// that is the crux of the retry matrix, and it cannot be inferred centrally
|
|
514
|
+
// because the same SDK method (createMemory) serves BOTH single create (retry)
|
|
515
|
+
// and the batch --stdin-jsonl path (already retries via batchCreateWithRetry).
|
|
516
|
+
// Wrapping per single-shot site is therefore the correct, non-double-wrapping
|
|
517
|
+
// chokepoint; the batch helpers are left untouched.
|
|
518
|
+
/** STDERR-only retry notice. Never touches stdout, so `--format json` stays pure. */
|
|
519
|
+
function emitRetryNotice(info) {
|
|
520
|
+
const reason = info.status === 429 ? 'rate limited' : `transient error ${info.status ?? ''}`.trim();
|
|
521
|
+
console.error(chalk_1.default.dim(`⏳ ${reason}; retry ${info.attempt}/${info.maxRetries} in ${(info.waitMs / 1000).toFixed(1)}s`));
|
|
513
522
|
}
|
|
514
523
|
/**
|
|
515
|
-
*
|
|
516
|
-
*
|
|
517
|
-
*
|
|
524
|
+
* Wrap an idempotent single-shot read (GET search/get/list/recall) or a
|
|
525
|
+
* naturally idempotent write (DELETE — deleting an already-deleted id is a
|
|
526
|
+
* no-op). Retries 429 and transient 5xx (502/503/504/408). Do NOT use this for
|
|
527
|
+
* named-memory PUT: it is a version bump that is only idempotent when
|
|
528
|
+
* expectedHeadId is supplied, and the CLI/SDK never supply it — use retryWrite.
|
|
518
529
|
*/
|
|
519
|
-
function
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
let headerVal;
|
|
527
|
-
if (headers) {
|
|
528
|
-
if (typeof headers.get === 'function') {
|
|
529
|
-
headerVal = headers.get('retry-after') ?? headers.get('Retry-After');
|
|
530
|
-
}
|
|
531
|
-
else {
|
|
532
|
-
headerVal = headers['retry-after'] ?? headers['Retry-After'];
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
if (headerVal) {
|
|
536
|
-
const n = parseInt(String(headerVal), 10);
|
|
537
|
-
if (Number.isFinite(n) && n >= 0)
|
|
538
|
-
return n * 1000;
|
|
539
|
-
}
|
|
540
|
-
return null;
|
|
530
|
+
function retryRead(fn) {
|
|
531
|
+
return (0, retry_1.withSingleShotRetry)(fn, {
|
|
532
|
+
idempotent: true,
|
|
533
|
+
sleep: retry_1.sleep,
|
|
534
|
+
maxRetries: retry_1.SINGLE_SHOT_MAX_RETRIES,
|
|
535
|
+
onRetry: emitRetryNotice,
|
|
536
|
+
});
|
|
541
537
|
}
|
|
542
|
-
/**
|
|
543
|
-
|
|
544
|
-
|
|
538
|
+
/**
|
|
539
|
+
* Wrap a non-idempotent single-shot write (plain POST create, non-CAS update).
|
|
540
|
+
* Retries ONLY 429 (server rejected before processing — no duplicate risk);
|
|
541
|
+
* a transient 5xx is NOT auto-retried because the write may already have landed.
|
|
542
|
+
*/
|
|
543
|
+
function retryWrite(fn) {
|
|
544
|
+
return (0, retry_1.withSingleShotRetry)(fn, {
|
|
545
|
+
idempotent: false,
|
|
546
|
+
sleep: retry_1.sleep,
|
|
547
|
+
maxRetries: retry_1.SINGLE_SHOT_MAX_RETRIES,
|
|
548
|
+
onRetry: emitRetryNotice,
|
|
549
|
+
});
|
|
545
550
|
}
|
|
546
551
|
/**
|
|
547
552
|
* Delete a single page, retrying on 429 up to `maxRetries` times. Honors the
|
|
@@ -556,8 +561,8 @@ async function bulkDeleteWithRetry(ids, deps) {
|
|
|
556
561
|
return await deps.deleteFn(ids);
|
|
557
562
|
}
|
|
558
563
|
catch (error) {
|
|
559
|
-
if (isRateLimitError(error) && attempt < maxRetries) {
|
|
560
|
-
const waitMs = getRetryAfterMs(error) ?? defaultRetryMs * (attempt + 1);
|
|
564
|
+
if ((0, retry_1.isRateLimitError)(error) && attempt < maxRetries) {
|
|
565
|
+
const waitMs = (0, retry_1.getRetryAfterMs)(error) ?? defaultRetryMs * (attempt + 1);
|
|
561
566
|
deps.onRateLimited?.(waitMs);
|
|
562
567
|
await deps.sleep(waitMs);
|
|
563
568
|
attempt++;
|
|
@@ -737,7 +742,7 @@ async function handleBatchDelete(rawIds, options, mode) {
|
|
|
737
742
|
try {
|
|
738
743
|
const result = await runBulkDeletePaged(resolvedIds, {
|
|
739
744
|
deleteFn: bulkDeleteViaSdk,
|
|
740
|
-
sleep,
|
|
745
|
+
sleep: retry_1.sleep,
|
|
741
746
|
onPage: (i, total) => {
|
|
742
747
|
if (total > 1 && !jsonMode) {
|
|
743
748
|
spinner.text = `Deleting page ${i + 1} of ${total}...`;
|
|
@@ -841,19 +846,9 @@ exports.BATCH_CREATE_ALLOWED_KEYS = new Set([
|
|
|
841
846
|
'outcome',
|
|
842
847
|
'distill',
|
|
843
848
|
]);
|
|
844
|
-
/** True when an error is an HTTP 503 (service unavailable — transient). */
|
|
845
|
-
function isServiceUnavailableError(error) {
|
|
846
|
-
const status = error?.status ?? error?.statusCode ?? error?.response?.status;
|
|
847
|
-
return status === 503;
|
|
848
|
-
}
|
|
849
849
|
/** True when a create should be retried: 429 (rate limited) or 503 (transient). */
|
|
850
850
|
function isRetryableCreateError(error) {
|
|
851
|
-
return isRateLimitError(error) || isServiceUnavailableError(error);
|
|
852
|
-
}
|
|
853
|
-
/** True when an error is an HTTP 402 (quota / payment required). */
|
|
854
|
-
function isQuotaError(error) {
|
|
855
|
-
const status = error?.status ?? error?.statusCode ?? error?.response?.status;
|
|
856
|
-
return status === 402;
|
|
851
|
+
return (0, retry_1.isRateLimitError)(error) || (0, retry_1.isServiceUnavailableError)(error);
|
|
857
852
|
}
|
|
858
853
|
/**
|
|
859
854
|
* Parse and validate one JSONL line into a createMemory body.
|
|
@@ -990,10 +985,10 @@ async function batchCreateWithRetry(data, deps) {
|
|
|
990
985
|
return await deps.createFn(data);
|
|
991
986
|
}
|
|
992
987
|
catch (error) {
|
|
993
|
-
if (isQuotaError(error))
|
|
988
|
+
if ((0, retry_1.isQuotaError)(error))
|
|
994
989
|
throw error;
|
|
995
990
|
if (isRetryableCreateError(error) && attempt < maxRetries) {
|
|
996
|
-
const waitMs = getRetryAfterMs(error) ?? defaultRetryMs * (attempt + 1);
|
|
991
|
+
const waitMs = (0, retry_1.getRetryAfterMs)(error) ?? defaultRetryMs * (attempt + 1);
|
|
997
992
|
deps.onRateLimited?.(waitMs);
|
|
998
993
|
await deps.sleep(waitMs);
|
|
999
994
|
attempt++;
|
|
@@ -1087,7 +1082,7 @@ async function runBatchCreate(items, deps) {
|
|
|
1087
1082
|
}
|
|
1088
1083
|
catch (error) {
|
|
1089
1084
|
summary.failed++;
|
|
1090
|
-
if (isQuotaError(error)) {
|
|
1085
|
+
if ((0, retry_1.isQuotaError)(error)) {
|
|
1091
1086
|
// Quota exhausted — pointless to keep hammering. Stop pulling new
|
|
1092
1087
|
// lines; in-flight workers finish, remaining lines are not-attempted.
|
|
1093
1088
|
stopped = true;
|
|
@@ -1253,7 +1248,7 @@ async function handleStreamingCreate(maxStdinBytes = exports.BATCH_CREATE_MAX_ST
|
|
|
1253
1248
|
const claudeSessionId = claudeSession?.sessionId || undefined;
|
|
1254
1249
|
const summary = await runBatchCreate(items, {
|
|
1255
1250
|
createFn: (data) => createOneViaSdk(claudeSessionId ? { ...data, claudeSessionId } : data),
|
|
1256
|
-
sleep,
|
|
1251
|
+
sleep: retry_1.sleep,
|
|
1257
1252
|
concurrency,
|
|
1258
1253
|
onResult: (r) => {
|
|
1259
1254
|
// Pure JSONL on stdout — the ONLY stdout write on this path. A broken pipe
|
|
@@ -1319,7 +1314,7 @@ function registerMemoriesCommands(program) {
|
|
|
1319
1314
|
// Named memories filter — fetch and filter client-side
|
|
1320
1315
|
if (options.named) {
|
|
1321
1316
|
const spinner = (0, spinner_1.createSpinner)('Fetching named memories...', options.format).start();
|
|
1322
|
-
const namedResponse = await (0, api_client_1.getSdk)().memories.listMemories({ limit: 100, offset: 0 });
|
|
1317
|
+
const namedResponse = await retryRead(() => (0, api_client_1.getSdk)().memories.listMemories({ limit: 100, offset: 0 }));
|
|
1323
1318
|
const namedMemories = (namedResponse.data?.data || []).filter((m) => m.name);
|
|
1324
1319
|
spinner.succeed(`Found ${namedMemories.length} named memories`);
|
|
1325
1320
|
if (options.format === 'table' || !options.format) {
|
|
@@ -1420,7 +1415,7 @@ function registerMemoriesCommands(program) {
|
|
|
1420
1415
|
.filter((t) => t.length > 0)
|
|
1421
1416
|
.join(',');
|
|
1422
1417
|
}
|
|
1423
|
-
const response = await (0, api_client_1.getSdk)().memories.listMemories(listParams);
|
|
1418
|
+
const response = await retryRead(() => (0, api_client_1.getSdk)().memories.listMemories(listParams));
|
|
1424
1419
|
const result = response.data;
|
|
1425
1420
|
spinner.succeed(`Found ${result?.data?.length || 0} memories`);
|
|
1426
1421
|
// Format for table display
|
|
@@ -1479,9 +1474,9 @@ function registerMemoriesCommands(program) {
|
|
|
1479
1474
|
// Named memory lookup — short-circuits the entire ID-based flow
|
|
1480
1475
|
if (options.name) {
|
|
1481
1476
|
const spinner = (0, spinner_1.createSpinner)('Fetching named memory...', options.format).start();
|
|
1482
|
-
const response = await (0, api_client_1.getSdk)().memories.getMemoryByName(options.name, {
|
|
1477
|
+
const response = await retryRead(() => (0, api_client_1.getSdk)().memories.getMemoryByName(options.name, {
|
|
1483
1478
|
detail: options.detail || 'standard',
|
|
1484
|
-
});
|
|
1479
|
+
}));
|
|
1485
1480
|
const memory = response.data?.data;
|
|
1486
1481
|
if (!memory) {
|
|
1487
1482
|
spinner.fail(`No memory found with name '${options.name}'`);
|
|
@@ -1521,9 +1516,9 @@ function registerMemoriesCommands(program) {
|
|
|
1521
1516
|
if (isNamedKey) {
|
|
1522
1517
|
const spinner = (0, spinner_1.createSpinner)('Fetching named memory...', options.format).start();
|
|
1523
1518
|
try {
|
|
1524
|
-
const response = await (0, api_client_1.getSdk)().memories.getMemoryByName(singleId, {
|
|
1519
|
+
const response = await retryRead(() => (0, api_client_1.getSdk)().memories.getMemoryByName(singleId, {
|
|
1525
1520
|
detail: options.detail || 'standard',
|
|
1526
|
-
});
|
|
1521
|
+
}));
|
|
1527
1522
|
const memory = response.data?.data;
|
|
1528
1523
|
if (!memory) {
|
|
1529
1524
|
spinner.fail(`No memory found with name '${singleId}'`);
|
|
@@ -1552,11 +1547,11 @@ function registerMemoriesCommands(program) {
|
|
|
1552
1547
|
let resolvedMemory = null;
|
|
1553
1548
|
if (isShortId) {
|
|
1554
1549
|
const spinner = (0, spinner_1.createSpinner)('Searching for matching memory...', options.format).start();
|
|
1555
|
-
const listResponse = await (0, api_client_1.getSdk)().memories.listMemories({
|
|
1550
|
+
const listResponse = await retryRead(() => (0, api_client_1.getSdk)().memories.listMemories({
|
|
1556
1551
|
limit: 10,
|
|
1557
1552
|
offset: 0,
|
|
1558
1553
|
idPrefix: singleId,
|
|
1559
|
-
});
|
|
1554
|
+
}));
|
|
1560
1555
|
const matches = listResponse.data?.data || [];
|
|
1561
1556
|
if (matches.length === 0) {
|
|
1562
1557
|
spinner.fail(`No memory found with ID prefix: ${singleId}`);
|
|
@@ -1597,9 +1592,9 @@ function registerMemoriesCommands(program) {
|
|
|
1597
1592
|
});
|
|
1598
1593
|
}
|
|
1599
1594
|
else {
|
|
1600
|
-
const response = await (0, api_client_1.getSdk)().memories.getMemoryById(memoryId, {
|
|
1595
|
+
const response = await retryRead(() => (0, api_client_1.getSdk)().memories.getMemoryById(memoryId, {
|
|
1601
1596
|
detail,
|
|
1602
|
-
});
|
|
1597
|
+
}));
|
|
1603
1598
|
const memory = response.data?.data;
|
|
1604
1599
|
printMemoryWithDetail(memory, options.format, {
|
|
1605
1600
|
includeVectors: !!options.includeVectors,
|
|
@@ -1789,7 +1784,12 @@ function registerMemoriesCommands(program) {
|
|
|
1789
1784
|
let response;
|
|
1790
1785
|
let upserted = false;
|
|
1791
1786
|
try {
|
|
1792
|
-
|
|
1787
|
+
// Single create is non-idempotent → retryWrite (429-only: a rejected
|
|
1788
|
+
// 429 never reached processing, so a retry cannot duplicate the create;
|
|
1789
|
+
// a transient 5xx is NOT retried here to avoid a duplicate). A 409
|
|
1790
|
+
// (named already exists) is not retryable and falls through to the
|
|
1791
|
+
// auto-upsert branch below unchanged.
|
|
1792
|
+
response = await retryWrite(() => (0, api_client_1.getSdk)().memories.createMemory(memoryData));
|
|
1793
1793
|
}
|
|
1794
1794
|
catch (createError) {
|
|
1795
1795
|
// 409 = named memory already exists → auto-upsert via updateNamedMemory
|
|
@@ -1811,7 +1811,15 @@ function registerMemoriesCommands(program) {
|
|
|
1811
1811
|
// intentionally NOT forwarded here: it is applied uniformly for both the
|
|
1812
1812
|
// create and upsert paths by the createMemoryRelationship call below, so
|
|
1813
1813
|
// forwarding it in the body too would create a duplicate SUPERSEDES edge.
|
|
1814
|
-
|
|
1814
|
+
// Named update is NOT idempotent on this path: the CLI/SDK never
|
|
1815
|
+
// supply expectedHeadId, so PUT /named/:name does an UNCONDITIONAL
|
|
1816
|
+
// version bump (buildNamedVersionBumpCypher). retryWrite → 429-only:
|
|
1817
|
+
// the named-write gate acquires the permit BEFORE this.write, so a
|
|
1818
|
+
// 429 is pre-write and safe to retry; a 5xx is ambiguous (the bump
|
|
1819
|
+
// may already have committed — e.g. Kong 504 after Neo4j commits)
|
|
1820
|
+
// and MUST NOT be retried, or it silently creates a second identical
|
|
1821
|
+
// version (memory-history corruption).
|
|
1822
|
+
response = await retryWrite(() => (0, api_client_1.getSdk)().memories.updateNamedMemory(memoryData.name, {
|
|
1815
1823
|
content: memoryData.content,
|
|
1816
1824
|
...(options.memoryType && { memoryType: options.memoryType }),
|
|
1817
1825
|
...(memoryData.topics && { topics: memoryData.topics }),
|
|
@@ -1825,7 +1833,7 @@ function registerMemoriesCommands(program) {
|
|
|
1825
1833
|
options.conversationId.toUpperCase() !== 'NEW' && {
|
|
1826
1834
|
conversationId: options.conversationId,
|
|
1827
1835
|
}),
|
|
1828
|
-
});
|
|
1836
|
+
}));
|
|
1829
1837
|
upserted = true;
|
|
1830
1838
|
}
|
|
1831
1839
|
else {
|
|
@@ -1858,12 +1866,12 @@ function registerMemoriesCommands(program) {
|
|
|
1858
1866
|
if (options.supersedes && memory?.id) {
|
|
1859
1867
|
const relSpinner = (0, spinner_1.createSpinner)('Creating SUPERSEDES relationship...', options.format).start();
|
|
1860
1868
|
try {
|
|
1861
|
-
await (0, api_client_1.getSdk)().memories.createMemoryRelationship(memory.id, {
|
|
1869
|
+
await retryWrite(() => (0, api_client_1.getSdk)().memories.createMemoryRelationship(memory.id, {
|
|
1862
1870
|
targetMemoryId: options.supersedes,
|
|
1863
1871
|
type: 'SUPERSEDES',
|
|
1864
1872
|
confidence: 1.0,
|
|
1865
1873
|
reason: 'Created via --supersedes flag',
|
|
1866
|
-
});
|
|
1874
|
+
}));
|
|
1867
1875
|
relSpinner.succeed(`Relationship created: ${memory.id.substring(0, 8)}... SUPERSEDES ${options.supersedes.substring(0, 8)}...`);
|
|
1868
1876
|
}
|
|
1869
1877
|
catch (relError) {
|
|
@@ -1928,7 +1936,7 @@ function registerMemoriesCommands(program) {
|
|
|
1928
1936
|
}
|
|
1929
1937
|
updates.name = options.assignName;
|
|
1930
1938
|
const spinner = (0, spinner_1.createSpinner)('Assigning name...', options.format).start();
|
|
1931
|
-
const response = await (0, api_client_1.getSdk)().memories.updateMemory(id, updates);
|
|
1939
|
+
const response = await retryWrite(() => (0, api_client_1.getSdk)().memories.updateMemory(id, updates));
|
|
1932
1940
|
const memory = response.data?.data;
|
|
1933
1941
|
spinner.succeed(`Memory ${id} named '${options.assignName}'`);
|
|
1934
1942
|
(0, formatters_1.printOutput)(memory, options.format);
|
|
@@ -1939,15 +1947,15 @@ function registerMemoriesCommands(program) {
|
|
|
1939
1947
|
if (options.name) {
|
|
1940
1948
|
// Look up current memory by name, then update its name
|
|
1941
1949
|
const spinner = (0, spinner_1.createSpinner)('Renaming...', options.format).start();
|
|
1942
|
-
const getResponse = await (0, api_client_1.getSdk)().memories.getMemoryByName(options.name);
|
|
1950
|
+
const getResponse = await retryRead(() => (0, api_client_1.getSdk)().memories.getMemoryByName(options.name));
|
|
1943
1951
|
const currentMemory = getResponse.data?.data;
|
|
1944
1952
|
if (!currentMemory?.id) {
|
|
1945
1953
|
spinner.fail(`Named memory '${options.name}' not found`);
|
|
1946
1954
|
return;
|
|
1947
1955
|
}
|
|
1948
|
-
const response = await (0, api_client_1.getSdk)().memories.updateMemory(currentMemory.id, {
|
|
1956
|
+
const response = await retryWrite(() => (0, api_client_1.getSdk)().memories.updateMemory(currentMemory.id, {
|
|
1949
1957
|
name: options.rename,
|
|
1950
|
-
});
|
|
1958
|
+
}));
|
|
1951
1959
|
const memory = response.data?.data;
|
|
1952
1960
|
spinner.succeed(`Named memory '${options.name}' renamed to '${options.rename}'`);
|
|
1953
1961
|
(0, formatters_1.printOutput)(memory, options.format);
|
|
@@ -1956,7 +1964,7 @@ function registerMemoriesCommands(program) {
|
|
|
1956
1964
|
if (id) {
|
|
1957
1965
|
updates.name = options.rename;
|
|
1958
1966
|
const spinner = (0, spinner_1.createSpinner)('Renaming...', options.format).start();
|
|
1959
|
-
const response = await (0, api_client_1.getSdk)().memories.updateMemory(id, updates);
|
|
1967
|
+
const response = await retryWrite(() => (0, api_client_1.getSdk)().memories.updateMemory(id, updates));
|
|
1960
1968
|
const memory = response.data?.data;
|
|
1961
1969
|
spinner.succeed(`Memory ${id} renamed to '${options.rename}'`);
|
|
1962
1970
|
(0, formatters_1.printOutput)(memory, options.format);
|
|
@@ -1972,7 +1980,13 @@ function registerMemoriesCommands(program) {
|
|
|
1972
1980
|
// Named memory update — creates new version
|
|
1973
1981
|
if (options.name) {
|
|
1974
1982
|
const spinner = (0, spinner_1.createSpinner)('Creating new version...', options.format).start();
|
|
1975
|
-
|
|
1983
|
+
// Named update is NOT idempotent without expectedHeadId (which the
|
|
1984
|
+
// CLI/SDK never supply): PUT /named/:name unconditionally bumps the
|
|
1985
|
+
// version. retryWrite → 429-only: the gate takes the permit before the
|
|
1986
|
+
// write, so a 429 is pre-write and safe to retry; a 5xx is ambiguous
|
|
1987
|
+
// (the bump may already have committed) and MUST NOT be retried, or it
|
|
1988
|
+
// creates a second identical version (silent history corruption).
|
|
1989
|
+
const response = await retryWrite(() => (0, api_client_1.getSdk)().memories.updateNamedMemory(options.name, updates));
|
|
1976
1990
|
const memory = response.data?.data;
|
|
1977
1991
|
const meta = response.data?.meta;
|
|
1978
1992
|
spinner.succeed(`Named memory '${options.name}' updated to v${meta?.version}`);
|
|
@@ -1983,7 +1997,7 @@ function registerMemoriesCommands(program) {
|
|
|
1983
1997
|
return;
|
|
1984
1998
|
}
|
|
1985
1999
|
const spinner = (0, spinner_1.createSpinner)('Updating memory...', options.format).start();
|
|
1986
|
-
const response = await (0, api_client_1.getSdk)().memories.updateMemory(id, updates);
|
|
2000
|
+
const response = await retryWrite(() => (0, api_client_1.getSdk)().memories.updateMemory(id, updates));
|
|
1987
2001
|
const memory = response.data?.data;
|
|
1988
2002
|
spinner.succeed(`Memory ${id} updated successfully`);
|
|
1989
2003
|
(0, formatters_1.printOutput)(memory, options.format);
|
|
@@ -2004,7 +2018,7 @@ function registerMemoriesCommands(program) {
|
|
|
2004
2018
|
.action(async (name, options) => {
|
|
2005
2019
|
try {
|
|
2006
2020
|
const spinner = (0, spinner_1.createSpinner)('Fetching version history...', options.format).start();
|
|
2007
|
-
const response = await (0, api_client_1.getSdk)().memories.getNamedMemoryHistory(name);
|
|
2021
|
+
const response = await retryRead(() => (0, api_client_1.getSdk)().memories.getNamedMemoryHistory(name));
|
|
2008
2022
|
const data = response.data;
|
|
2009
2023
|
if (!data?.versions?.length) {
|
|
2010
2024
|
spinner.fail(`No memory found with name '${name}'`);
|
|
@@ -2205,7 +2219,9 @@ function registerMemoriesCommands(program) {
|
|
|
2205
2219
|
}
|
|
2206
2220
|
}
|
|
2207
2221
|
const spinner = (0, spinner_1.createSpinner)('Deleting memory...', 'table').start();
|
|
2208
|
-
|
|
2222
|
+
// DELETE is idempotent (deleting an already-deleted id is a no-op), so a
|
|
2223
|
+
// transient 5xx is retry-safe as well as a 429.
|
|
2224
|
+
await retryRead(() => (0, api_client_1.getSdk)().memories.deleteMemory(id));
|
|
2209
2225
|
// Record this deletion in the cross-process guard file so the next
|
|
2210
2226
|
// invocation's threshold check reflects it.
|
|
2211
2227
|
if (isNonInteractive) {
|
|
@@ -2426,7 +2442,7 @@ function registerMemoriesCommands(program) {
|
|
|
2426
2442
|
searchRequest.order = options.order;
|
|
2427
2443
|
}
|
|
2428
2444
|
// Use GET endpoint for better performance (Issue #79)
|
|
2429
|
-
const response = await (0, api_client_1.getSdk)().memories.searchMemoriesGet(searchRequest);
|
|
2445
|
+
const response = await retryRead(() => (0, api_client_1.getSdk)().memories.searchMemoriesGet(searchRequest));
|
|
2430
2446
|
const searchData = response.data;
|
|
2431
2447
|
// Normalize: API may return flat objects or {memory, score} wrappers.
|
|
2432
2448
|
// Ensure consistent {memory, score, matchedVia} shape for display code.
|
|
@@ -2844,19 +2860,19 @@ async function handleMemoryDiscovery(memoryId, options) {
|
|
|
2844
2860
|
const sdk = (0, api_client_1.getSdk)();
|
|
2845
2861
|
if (options.similar) {
|
|
2846
2862
|
discoveryType = 'similar';
|
|
2847
|
-
const response = await sdk.memories.getSimilarMemories(memoryId, { limit: options.limit });
|
|
2863
|
+
const response = await retryRead(() => sdk.memories.getSimilarMemories(memoryId, { limit: options.limit }));
|
|
2848
2864
|
result = response.data;
|
|
2849
2865
|
}
|
|
2850
2866
|
else if (options.conversation) {
|
|
2851
2867
|
discoveryType = 'conversation';
|
|
2852
|
-
const response = await sdk.memories.getConversationMemories(memoryId, {
|
|
2868
|
+
const response = await retryRead(() => sdk.memories.getConversationMemories(memoryId, {
|
|
2853
2869
|
limit: options.limit,
|
|
2854
|
-
});
|
|
2870
|
+
}));
|
|
2855
2871
|
result = response.data;
|
|
2856
2872
|
}
|
|
2857
2873
|
else if (options.related) {
|
|
2858
2874
|
discoveryType = 'related';
|
|
2859
|
-
const response = await sdk.memories.getRelatedMemories(memoryId, { limit: options.limit });
|
|
2875
|
+
const response = await retryRead(() => sdk.memories.getRelatedMemories(memoryId, { limit: options.limit }));
|
|
2860
2876
|
result = response.data;
|
|
2861
2877
|
}
|
|
2862
2878
|
else {
|
|
@@ -2957,9 +2973,9 @@ function registerMemoryRelationshipCommands(memories) {
|
|
|
2957
2973
|
return;
|
|
2958
2974
|
}
|
|
2959
2975
|
const spinner = (0, spinner_1.createSpinner)('Fetching relationships...', options.format).start();
|
|
2960
|
-
const response = await (0, api_client_1.getSdk)().memories.getMemoryRelationships(id, {
|
|
2976
|
+
const response = await retryRead(() => (0, api_client_1.getSdk)().memories.getMemoryRelationships(id, {
|
|
2961
2977
|
direction: options.direction,
|
|
2962
|
-
});
|
|
2978
|
+
}));
|
|
2963
2979
|
const result = response.data;
|
|
2964
2980
|
spinner.succeed(`Found ${result?.data?.length || 0} relationships`);
|
|
2965
2981
|
if (options.format === 'table' || !options.format) {
|
|
@@ -3065,7 +3081,7 @@ function registerMemoryRelationshipCommands(memories) {
|
|
|
3065
3081
|
};
|
|
3066
3082
|
}
|
|
3067
3083
|
const spinner = (0, spinner_1.createSpinner)('Creating relationship...', options.format).start();
|
|
3068
|
-
const response = await (0, api_client_1.getSdk)().memories.createMemoryRelationship(id, relationshipData);
|
|
3084
|
+
const response = await retryWrite(() => (0, api_client_1.getSdk)().memories.createMemoryRelationship(id, relationshipData));
|
|
3069
3085
|
const relationship = response.data?.data;
|
|
3070
3086
|
spinner.succeed(`Relationship created: ${id} ${relationship?.type} ${relationshipData.targetMemoryId}`);
|
|
3071
3087
|
(0, formatters_1.printOutput)(relationship, options.format);
|
|
@@ -3108,7 +3124,7 @@ function registerMemoryRelationshipCommands(memories) {
|
|
|
3108
3124
|
}
|
|
3109
3125
|
// Non-interactive without --force: auto-confirm single delete
|
|
3110
3126
|
const spinner = (0, spinner_1.createSpinner)('Deleting relationship...', 'table').start();
|
|
3111
|
-
await (0, api_client_1.getSdk)().memories.deleteMemoryRelationship(id, relationshipId);
|
|
3127
|
+
await retryRead(() => (0, api_client_1.getSdk)().memories.deleteMemoryRelationship(id, relationshipId));
|
|
3112
3128
|
spinner.succeed(`Relationship ${relationshipId} deleted successfully`);
|
|
3113
3129
|
}
|
|
3114
3130
|
catch (error) {
|
|
@@ -3136,7 +3152,7 @@ function registerMemoryTimelineCommand(memories) {
|
|
|
3136
3152
|
const before = parseInt(options.before, 10);
|
|
3137
3153
|
const after = parseInt(options.after, 10);
|
|
3138
3154
|
const spinner = (0, spinner_1.createSpinner)('Fetching timeline context...', options.format).start();
|
|
3139
|
-
const response = await (0, api_client_1.getSdk)().memories.getMemoryTimeline(id, { before, after });
|
|
3155
|
+
const response = await retryRead(() => (0, api_client_1.getSdk)().memories.getMemoryTimeline(id, { before, after }));
|
|
3140
3156
|
const result = response.data;
|
|
3141
3157
|
spinner.succeed('Timeline context retrieved');
|
|
3142
3158
|
if (options.format === 'table' || !options.format) {
|
|
@@ -3199,12 +3215,14 @@ function registerDetectRelationshipsCommand(memories) {
|
|
|
3199
3215
|
const spinner = (0, spinner_1.createSpinner)(options.autoCreate
|
|
3200
3216
|
? 'Detecting and creating relationships...'
|
|
3201
3217
|
: 'Detecting relationships...', options.format).start();
|
|
3202
|
-
|
|
3218
|
+
// detect with --auto-create writes relationship edges, so it is
|
|
3219
|
+
// non-idempotent → retryWrite (429-only) to avoid duplicate edges on 5xx.
|
|
3220
|
+
const response = await retryWrite(() => (0, api_client_1.getSdk)().memories.detectMemoryRelationships(id, {
|
|
3203
3221
|
body: {
|
|
3204
3222
|
autoCreate: options.autoCreate || false,
|
|
3205
3223
|
minConfidence,
|
|
3206
3224
|
},
|
|
3207
|
-
});
|
|
3225
|
+
}));
|
|
3208
3226
|
const result = response.data;
|
|
3209
3227
|
const suggestions = result?.data || [];
|
|
3210
3228
|
spinner.succeed(`Found ${suggestions.length} potential relationships`);
|
|
@@ -3344,7 +3362,7 @@ function registerDigestCommand(memories) {
|
|
|
3344
3362
|
sdk.config.timeoutMs = RECALL_MAX_MS;
|
|
3345
3363
|
let response;
|
|
3346
3364
|
try {
|
|
3347
|
-
response = await sdk.memories.generateMemoryDigest(body);
|
|
3365
|
+
response = await retryRead(() => sdk.memories.generateMemoryDigest(body));
|
|
3348
3366
|
}
|
|
3349
3367
|
finally {
|
|
3350
3368
|
if (sdk.config)
|
|
@@ -3433,7 +3451,7 @@ function registerStatusCommand(memories) {
|
|
|
3433
3451
|
sdk.config.timeoutMs = 180000;
|
|
3434
3452
|
let response;
|
|
3435
3453
|
try {
|
|
3436
|
-
response = await sdk.memories.generateMemoryDigest(body);
|
|
3454
|
+
response = await retryRead(() => sdk.memories.generateMemoryDigest(body));
|
|
3437
3455
|
}
|
|
3438
3456
|
finally {
|
|
3439
3457
|
if (sdk.config)
|
|
@@ -3579,7 +3597,7 @@ function registerRecapCommand(memories) {
|
|
|
3579
3597
|
};
|
|
3580
3598
|
if (excludeTopicsParam)
|
|
3581
3599
|
searchRequest.excludeTopics = excludeTopicsParam;
|
|
3582
|
-
const response = await (0, api_client_1.getSdk)().memories.searchMemoriesGet(searchRequest);
|
|
3600
|
+
const response = await retryRead(() => (0, api_client_1.getSdk)().memories.searchMemoriesGet(searchRequest));
|
|
3583
3601
|
// Normalize: API may return flat objects or {memory, score} wrappers.
|
|
3584
3602
|
results = (response.data?.data || []).map((r) => r.memory ? r : { memory: r, score: r.score, matchedVia: r.matchedVia });
|
|
3585
3603
|
}
|
|
@@ -3595,7 +3613,7 @@ function registerRecapCommand(memories) {
|
|
|
3595
3613
|
};
|
|
3596
3614
|
if (excludeTopicsParam)
|
|
3597
3615
|
listRequest.excludeTopics = excludeTopicsParam;
|
|
3598
|
-
const response = await (0, api_client_1.getSdk)().memories.listMemories(listRequest);
|
|
3616
|
+
const response = await retryRead(() => (0, api_client_1.getSdk)().memories.listMemories(listRequest));
|
|
3599
3617
|
// Wrap in search-result shape expected by groupByConversation / displayRecap
|
|
3600
3618
|
results = (response.data?.data || []).map((m) => ({ memory: m }));
|
|
3601
3619
|
}
|
|
@@ -3833,7 +3851,7 @@ function registerBuildContextCommand(memories) {
|
|
|
3833
3851
|
body.files = options.files;
|
|
3834
3852
|
}
|
|
3835
3853
|
const sdk = (0, api_client_1.getSdk)();
|
|
3836
|
-
const response = await sdk.memories.buildContext(body);
|
|
3854
|
+
const response = await retryRead(() => sdk.memories.buildContext(body));
|
|
3837
3855
|
const data = response?.data || response;
|
|
3838
3856
|
if (!data) {
|
|
3839
3857
|
spinner.fail('No response data received');
|