quilltap 4.11.0-dev.53 → 4.11.0-dev.58
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/README.md +3 -2
- package/lib/completion/bash.template +2 -2
- package/lib/completion/fish.template +4 -1
- package/lib/completion/zsh.template +4 -1
- package/lib/memories-commands.js +94 -15
- package/lib/memories-server.js +39 -20
- package/lib/recall-replay-command.js +3 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -346,13 +346,14 @@ quilltap memories tree <id|prefix> [--depth N] [--max-nodes N] # ASCII w
|
|
|
346
346
|
quilltap memories status [--character <name|id>] [--port <n>] # Per-holder rollup, maintenance ledger + dangling-edge check
|
|
347
347
|
quilltap memories validate [--character <name|id>] [--list] # Graph health check; exit 1 if any dangling link remains
|
|
348
348
|
quilltap memories validate --repair [--write] # Rehearse (or, with --write, apply) the server's graph repair
|
|
349
|
+
quilltap memories keystone <id|prefix> [--off] [--write] # Rehearse (or, with --write, apply) marking a keystone — a standing fact
|
|
349
350
|
quilltap memories grep --semantic --character Ariadne "the argument" # Embedding search (server required, one holder)
|
|
350
351
|
quilltap memories maintain --character Ariadne --dry-run # Rehearse every maintenance phase (server required)
|
|
351
352
|
quilltap memories maintain --character Ariadne --phases consolidate # Queue a consolidation-only maintenance job
|
|
352
353
|
quilltap memories maintain --character all [--phases demote,retire] # Queue maintenance for every character
|
|
353
354
|
```
|
|
354
355
|
|
|
355
|
-
Shared filter flags apply to `ls`, `find`, `grep`, and `status` where they make sense: `--character`, `--about` (with `self` / `none` shortcuts), `--source` (AUTO / MANUAL / CONSOLIDATED), `--tier` (hot / cold; both by default, cold rows are starred in the `src` column), `--chat` (with `none` for manual entries), `--project`, `--since`, `--until`, `--min-importance`, `--min-reinforced`, `--has-embedding` / `--no-embedding`. Sort flags (`--sort reinforced|importance|created|accessed|reinforcement-count|links`, plus `-r` / `--reverse` to flip it, `--limit N` (default 50) and `--full-titles` to stop chat titles being truncated to 32 characters) apply to `ls`, `find`, and `grep`. `grep -l` / `--paths-only` prints only the IDs of matching memories, one per line. Names accept fuzzy substrings; ambiguous names print candidates and exit 2. `--json` is supported by every verb. `validate` is `status`'s terse twin — read-only; it counts dangling `relatedMemoryIds` edges, dangling `consolidatedFrom` members (digests naming notes that are gone), dangling `supersededById` pointers, and rows with no embedding. It exits 1 if any dangling link remains (a missing embedding only warns), and `--list` prints the offending IDs. `grep --semantic` defaults to `--top 20`, `--threshold 0.5`, and scopes to **one holder at a time**: `--character all` is rejected. The legacy `quilltap db memories --character <name>` verb remains, now with `--tier hot|cold` (rows with no tier count as hot).
|
|
356
|
+
Shared filter flags apply to `ls`, `find`, `grep`, and `status` where they make sense: `--character`, `--about` (with `self` / `none` shortcuts), `--source` (AUTO / MANUAL / CONSOLIDATED), `--tier` (hot / cold; both by default, cold rows are starred in the `src` column), `--keystone` (keystones only; a keystone's summary is prefixed `◆` in tables, and `show` prints a `Keystone:` line), `--chat` (with `none` for manual entries), `--project`, `--since`, `--until`, `--min-importance`, `--min-reinforced`, `--has-embedding` / `--no-embedding`. Sort flags (`--sort reinforced|importance|created|accessed|reinforcement-count|links`, plus `-r` / `--reverse` to flip it, `--limit N` (default 50) and `--full-titles` to stop chat titles being truncated to 32 characters) apply to `ls`, `find`, and `grep`. `grep -l` / `--paths-only` prints only the IDs of matching memories, one per line. Names accept fuzzy substrings; ambiguous names print candidates and exit 2. `--json` is supported by every verb. `validate` is `status`'s terse twin — read-only; it counts dangling `relatedMemoryIds` edges, dangling `consolidatedFrom` members (digests naming notes that are gone), dangling `supersededById` pointers, and rows with no embedding. It exits 1 if any dangling link remains (a missing embedding only warns), and `--list` prints the offending IDs. `grep --semantic` defaults to `--top 20`, `--threshold 0.5`, and scopes to **one holder at a time**: `--character all` is rejected. The legacy `quilltap db memories --character <name>` verb remains, now with `--tier hot|cold` (rows with no tier count as hot).
|
|
356
357
|
|
|
357
358
|
Verbs that call the server (`grep --semantic`, `status`'s ledger, `maintain`, `validate --repair`) take `--port`; without it they use the port recorded in the `--instance` / `--data-dir` instance's lock, else 3000.
|
|
358
359
|
|
|
@@ -391,7 +392,7 @@ Flags: `--turn <n>` (default: last), `--char <characterId>` (default: first LLM-
|
|
|
391
392
|
|
|
392
393
|
Tuning and comparable runs:
|
|
393
394
|
|
|
394
|
-
- `--tuning '<json>'` / `--tuning-file <path>` — retuning constants for the new path only (the old path stays the reference): `boostGateAbs`, `boostGateMargin`, `boostGateRamp` (relevance gate on boosts), `boostCap`, `freshBypassesGate`, `windowBypassesGate`, `multipliers` (`{ "freshEvent24h": 1.3, … }`), `specificAnchors`, `anchorMinHits`, `anchorOrder`, `backgroundReserve
|
|
395
|
+
- `--tuning '<json>'` / `--tuning-file <path>` — retuning constants for the new path only (the old path stays the reference): `boostGateAbs`, `boostGateMargin`, `boostGateRamp` (relevance gate on boosts), `boostCap`, `freshBypassesGate`, `windowBypassesGate`, `multipliers` (`{ "freshEvent24h": 1.3, … }`), `specificAnchors`, `anchorMinHits`, `anchorOrder`, `backgroundReserve`, `innerCircleShare` (share of the frozen archive the group inner circle may take, default `0.333…`), `innerCircleAllowance` (digests per circle member by tier, tightest group first, default `[3, 2, 1]`). Unknown keys are rejected. The header's `Tuning` line shows what is active.
|
|
395
396
|
- `--signals-from <replay.json>` — reuse the signals saved in an earlier `--json` replay of the same chat and turn. The distillation is a cheap-LLM call and words the same turn differently each time, so runs you mean to compare should share it.
|
|
396
397
|
- `--as-of` — search only memories created before the turn's opening message, with weights decayed to the turn's own clock: the corpus as it stood then. The header's `Harness` line shows the cutoff and how many later memories were left out.
|
|
397
398
|
|
|
@@ -321,12 +321,12 @@ _quilltap_complete() {
|
|
|
321
321
|
fi
|
|
322
322
|
;;
|
|
323
323
|
memories)
|
|
324
|
-
local mem_verbs="ls find grep show tree status validate maintain"
|
|
324
|
+
local mem_verbs="ls find grep show tree status validate keystone maintain"
|
|
325
325
|
local mem_flags="--character --about --source --tier --chat --project --since --until \
|
|
326
326
|
--min-importance --min-reinforced --has-embedding --no-embedding \
|
|
327
327
|
--sort -r --reverse --limit --full-titles --in --no-related --list \
|
|
328
328
|
--ignore-case -i --paths-only -l --max --context --depth --max-nodes \
|
|
329
|
-
--semantic --top --threshold --dry-run --phases --cap --repair --write \
|
|
329
|
+
--semantic --top --threshold --dry-run --phases --cap --repair --write --keystone --off \
|
|
330
330
|
--instance --data-dir --passphrase -p --port --json --help"
|
|
331
331
|
if [[ -z "$subverb" ]]; then
|
|
332
332
|
if [[ "$cur" == -* ]]; then
|
|
@@ -273,6 +273,7 @@ complete -c quilltap -n '__quilltap_using_subcommand memories' -f -a 'show' -d '
|
|
|
273
273
|
complete -c quilltap -n '__quilltap_using_subcommand memories' -f -a 'tree' -d 'Graph walk'
|
|
274
274
|
complete -c quilltap -n '__quilltap_using_subcommand memories' -f -a 'status' -d 'Memory status'
|
|
275
275
|
complete -c quilltap -n '__quilltap_using_subcommand memories' -f -a 'validate' -d 'Health check'
|
|
276
|
+
complete -c quilltap -n '__quilltap_using_subcommand memories' -f -a 'keystone' -d 'Mark or unmark a keystone'
|
|
276
277
|
complete -c quilltap -n '__quilltap_using_subcommand memories' -f -a 'maintain' -d 'Run memory maintenance'
|
|
277
278
|
|
|
278
279
|
# memories flags
|
|
@@ -284,7 +285,9 @@ complete -c quilltap -n '__quilltap_using_subcommand memories' -l 'dry-run' -d '
|
|
|
284
285
|
complete -c quilltap -n '__quilltap_using_subcommand memories' -l 'phases' -d 'Maintain: comma-separated phases' -x -a 'fold-near-duplicates consolidate demote retire mirror'
|
|
285
286
|
complete -c quilltap -n '__quilltap_using_subcommand memories' -l 'cap' -d 'Maintain: hot-row cap for demotion' -x
|
|
286
287
|
complete -c quilltap -n '__quilltap_using_subcommand memories' -l 'repair' -d 'Validate: repair the graph on the running server'
|
|
287
|
-
complete -c quilltap -n '__quilltap_using_subcommand memories' -l 'write' -d 'Validate --repair: apply the
|
|
288
|
+
complete -c quilltap -n '__quilltap_using_subcommand memories' -l 'write' -d 'Validate --repair / keystone: apply the change'
|
|
289
|
+
complete -c quilltap -n '__quilltap_using_subcommand memories' -l 'keystone' -d 'Only keystones'
|
|
290
|
+
complete -c quilltap -n '__quilltap_using_subcommand memories' -l 'off' -d 'Keystone: unmark instead of mark'
|
|
288
291
|
complete -c quilltap -n '__quilltap_using_subcommand memories' -l 'chat' -d 'Chat id or title' -x
|
|
289
292
|
complete -c quilltap -n '__quilltap_using_subcommand memories' -l 'project' -d 'Project name or id' -x
|
|
290
293
|
complete -c quilltap -n '__quilltap_using_subcommand memories' -l 'since' -d 'Since timestamp' -x
|
|
@@ -539,6 +539,7 @@ _quilltap_memories() {
|
|
|
539
539
|
'tree:ASCII walk of graph'
|
|
540
540
|
'status:Per-character rollup'
|
|
541
541
|
'validate:Health check'
|
|
542
|
+
'keystone:Mark or unmark a keystone (standing fact) via the running server'
|
|
542
543
|
'maintain:Run memory maintenance (fold, consolidate, demote, retire, mirror)'
|
|
543
544
|
)
|
|
544
545
|
|
|
@@ -556,7 +557,9 @@ _quilltap_memories() {
|
|
|
556
557
|
'--phases[Maintain: comma-separated phases]:phases:_sequence compadd - fold-near-duplicates consolidate demote retire mirror'
|
|
557
558
|
'--cap[Maintain: hot-row cap for demotion]:n:'
|
|
558
559
|
'--repair[Validate: repair the graph on the running server (dry run without --write)]'
|
|
559
|
-
'--write[Validate --repair: apply the
|
|
560
|
+
'--write[Validate --repair / keystone: apply the change]'
|
|
561
|
+
'--keystone[Only keystones]'
|
|
562
|
+
'--off[Keystone: unmark instead of mark]'
|
|
560
563
|
'--chat[Chat id or title]:chat:'
|
|
561
564
|
'--project[Project name or id]:project:'
|
|
562
565
|
'--since[Since timestamp]:date:'
|
package/lib/memories-commands.js
CHANGED
|
@@ -18,7 +18,7 @@ const {
|
|
|
18
18
|
memoryColumns,
|
|
19
19
|
} = require('./graph-integrity');
|
|
20
20
|
const { cmdMaintain } = require('./memories-maintain-command');
|
|
21
|
-
const { postMemoriesAction, resolveMemoriesServerPort } = require('./memories-server');
|
|
21
|
+
const { postMemoriesAction, putMemory, resolveMemoriesServerPort } = require('./memories-server');
|
|
22
22
|
|
|
23
23
|
// ---------- colour & marker helpers ----------
|
|
24
24
|
|
|
@@ -152,6 +152,8 @@ function parseFlags(args) {
|
|
|
152
152
|
case '--list': flags.list = true; break;
|
|
153
153
|
case '--repair': flags.repair = true; break;
|
|
154
154
|
case '--write': flags.write = true; break;
|
|
155
|
+
case '--keystone': flags.keystone = true; break;
|
|
156
|
+
case '--off': flags.off = true; break;
|
|
155
157
|
|
|
156
158
|
case '--phases': flags.phases = args[++i] || ''; break;
|
|
157
159
|
case '--cap': {
|
|
@@ -257,6 +259,11 @@ function buildWhereClause(db, flags, openMounts = null) {
|
|
|
257
259
|
params.push(s);
|
|
258
260
|
}
|
|
259
261
|
|
|
262
|
+
if (flags.keystone) {
|
|
263
|
+
// Instances that haven't run add-memory-keystone-v1 have no keystones.
|
|
264
|
+
clauses.push(hasMemoryColumn(db, 'keystone') ? 'm.keystone = 1' : '0');
|
|
265
|
+
}
|
|
266
|
+
|
|
260
267
|
if (flags.tier) {
|
|
261
268
|
const t = String(flags.tier).toLowerCase();
|
|
262
269
|
if (t !== 'hot' && t !== 'cold') {
|
|
@@ -386,17 +393,23 @@ const SELECT_BASE_TEMPLATE = `
|
|
|
386
393
|
LEFT JOIN projects ON projects.id = m.projectId
|
|
387
394
|
`;
|
|
388
395
|
|
|
389
|
-
|
|
390
|
-
// substitute constants so the CLI still reads them (rows read as hot).
|
|
391
|
-
function selectBase(db) {
|
|
392
|
-
let hasTier = false;
|
|
396
|
+
function hasMemoryColumn(db, name) {
|
|
393
397
|
try {
|
|
394
|
-
|
|
395
|
-
} catch {
|
|
396
|
-
|
|
398
|
+
return db.prepare("PRAGMA table_info('memories')").all().some((c) => c.name === name);
|
|
399
|
+
} catch {
|
|
400
|
+
return false; // treat as legacy
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Instances that haven't yet run add-memory-tiers-v1 lack the tier columns (and
|
|
405
|
+
// those before add-memory-keystone-v1 the keystone column); substitute
|
|
406
|
+
// constants so the CLI still reads them (rows read as hot, not keystones).
|
|
407
|
+
function selectBase(db) {
|
|
408
|
+
const tierCols = hasMemoryColumn(db, 'tier')
|
|
397
409
|
? 'm.tier, m.supersededById, m.consolidatedFrom, m.consolidatedAt,'
|
|
398
410
|
: "'hot' AS tier, NULL AS supersededById, '[]' AS consolidatedFrom, NULL AS consolidatedAt,";
|
|
399
|
-
|
|
411
|
+
const keystoneCol = hasMemoryColumn(db, 'keystone') ? 'm.keystone,' : '0 AS keystone,';
|
|
412
|
+
return SELECT_BASE_TEMPLATE.replace('__TIER_COLUMNS__', `${tierCols} ${keystoneCol}`);
|
|
400
413
|
}
|
|
401
414
|
|
|
402
415
|
|
|
@@ -499,6 +512,7 @@ function rowToJson(row) {
|
|
|
499
512
|
supersededById: row.supersededById || null,
|
|
500
513
|
consolidatedFrom: (() => { try { return JSON.parse(row.consolidatedFrom || '[]'); } catch { return []; } })(),
|
|
501
514
|
consolidatedAt: row.consolidatedAt || null,
|
|
515
|
+
keystone: Number(row.keystone) === 1,
|
|
502
516
|
importance: row.importance,
|
|
503
517
|
reinforcedImportance: row.reinforcedImportance,
|
|
504
518
|
reinforcementCount: row.reinforcementCount,
|
|
@@ -546,7 +560,8 @@ function renderTable(rows, { showHolder, impField, fullTitles }) {
|
|
|
546
560
|
const chat = truncate(chatLabel(row, fullTitles), fullTitles ? 24 : 32).padEnd(fullTitles ? 24 : 32);
|
|
547
561
|
const links = String(linkCount(row)).padStart(5);
|
|
548
562
|
const emb = row.hasEmbedding ? 'Y' : '-';
|
|
549
|
-
|
|
563
|
+
// A keystone's line is marked ◆ (a standing fact; never folded or demoted).
|
|
564
|
+
const summary = (Number(row.keystone) === 1 ? '◆ ' : '') + truncate(row.summary || '', sumW);
|
|
550
565
|
|
|
551
566
|
const cells = [
|
|
552
567
|
showHolder ? truncate(row.holderName || '(?)', 14).padEnd(14) : null,
|
|
@@ -937,6 +952,7 @@ function renderShowText(row, graph, depth) {
|
|
|
937
952
|
console.log(` About: ${aboutName}${row.aboutCharacterId && row.aboutCharacterId !== row.characterId ? ` (${shortId(row.aboutCharacterId)})` : ''}`);
|
|
938
953
|
console.log(` Source: ${row.source || '(?)'}`);
|
|
939
954
|
console.log(` Tier: ${row.tier || 'hot'}${row.supersededById ? ` (superseded by ${shortId(row.supersededById)})` : ''}`);
|
|
955
|
+
console.log(` Keystone: ${Number(row.keystone) === 1 ? 'yes (standing fact)' : 'no'}`);
|
|
940
956
|
const reinf = row.reinforcedImportance != null ? Number(row.reinforcedImportance).toFixed(2) : '?';
|
|
941
957
|
const baseImp = row.importance != null ? Number(row.importance).toFixed(2) : '?';
|
|
942
958
|
console.log(` Importance: ${reinf} (reinforced from ${baseImp}, count: ${row.reinforcementCount ?? 0})`);
|
|
@@ -1507,6 +1523,51 @@ function renderRepairText(report) {
|
|
|
1507
1523
|
}
|
|
1508
1524
|
}
|
|
1509
1525
|
|
|
1526
|
+
// ---------- keystone ----------
|
|
1527
|
+
|
|
1528
|
+
/**
|
|
1529
|
+
* Mark or unmark a keystone. The id resolves locally (read-only); the change
|
|
1530
|
+
* itself goes to the running server's memory PUT, through the edit chokepoint
|
|
1531
|
+
* that enforces the per-subject limit and invalidates the frozen archive.
|
|
1532
|
+
*/
|
|
1533
|
+
async function cmdKeystone(flags, positional) {
|
|
1534
|
+
const idArg = positional[0];
|
|
1535
|
+
if (!idArg) {
|
|
1536
|
+
throw new Error('memories keystone: missing <id>. Usage: quilltap memories keystone <id|prefix> [--off] [--write]');
|
|
1537
|
+
}
|
|
1538
|
+
const want = !flags.off;
|
|
1539
|
+
const { db } = await openDb(flags);
|
|
1540
|
+
let row;
|
|
1541
|
+
try {
|
|
1542
|
+
const id = resolveMemoryId(db, idArg);
|
|
1543
|
+
row = db.prepare(`${selectBase(db)} WHERE m.id = ?`).get(id);
|
|
1544
|
+
if (!row) throw new Error(`Memory ${id} not found`);
|
|
1545
|
+
} finally {
|
|
1546
|
+
db.close();
|
|
1547
|
+
}
|
|
1548
|
+
const is = Number(row.keystone) === 1;
|
|
1549
|
+
const label = `${shortId(row.id)} (${truncate(row.summary || '', 60)})`;
|
|
1550
|
+
if (is === want) {
|
|
1551
|
+
const msg = `${label} is ${want ? 'already' : 'not'} a keystone; nothing to do.`;
|
|
1552
|
+
if (flags.json) renderJson({ id: row.id, keystone: is, changed: false }); else console.log(msg);
|
|
1553
|
+
return;
|
|
1554
|
+
}
|
|
1555
|
+
if (!flags.write) {
|
|
1556
|
+
const msg = `Would ${want ? 'mark' : 'unmark'} ${label} as a keystone. Re-run with --write to apply.`;
|
|
1557
|
+
if (flags.json) renderJson({ id: row.id, keystone: is, wouldBe: want, changed: false, dryRun: true }); else console.log(msg);
|
|
1558
|
+
return;
|
|
1559
|
+
}
|
|
1560
|
+
const payload = await putMemory(flags, row.id, { keystone: want }, {
|
|
1561
|
+
why: 'Changing a keystone needs the running server (the only database writer).',
|
|
1562
|
+
});
|
|
1563
|
+
const now = Boolean(payload && payload.memory && payload.memory.keystone);
|
|
1564
|
+
if (flags.json) {
|
|
1565
|
+
renderJson({ id: row.id, keystone: now, changed: now === want });
|
|
1566
|
+
} else {
|
|
1567
|
+
console.log(`${want ? 'Marked' : 'Unmarked'} ${label} as a keystone.`);
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1510
1571
|
// ---------- help ----------
|
|
1511
1572
|
|
|
1512
1573
|
function printMemoriesHelp() {
|
|
@@ -1559,6 +1620,16 @@ Subcommands:
|
|
|
1559
1620
|
server (the only database
|
|
1560
1621
|
writer, holder of the instance
|
|
1561
1622
|
lock), so the server must be up.
|
|
1623
|
+
keystone <id|prefix> [--off] [--write] [--port N]
|
|
1624
|
+
Mark a memory a keystone (a
|
|
1625
|
+
standing fact, always recalled in
|
|
1626
|
+
full, never folded, consolidated
|
|
1627
|
+
or demoted), or unmark it with
|
|
1628
|
+
--off. Without --write it only
|
|
1629
|
+
says what would change; with
|
|
1630
|
+
--write the running server makes
|
|
1631
|
+
the change (at most 5 keystones
|
|
1632
|
+
per holder and subject).
|
|
1562
1633
|
maintain --character <name|id|all> [--phases a,b,…] [--dry-run]
|
|
1563
1634
|
[--max N] [--threshold 0..1] [--cap N] [--port N] [--json]
|
|
1564
1635
|
Run memory maintenance by hand on
|
|
@@ -1586,6 +1657,7 @@ Shared filter flags:
|
|
|
1586
1657
|
--about <name|id|self|none> Subject (aboutCharacterId).
|
|
1587
1658
|
--source AUTO|MANUAL|CONSOLIDATED Restrict by source.
|
|
1588
1659
|
--tier hot|cold Restrict by recall tier (default: both).
|
|
1660
|
+
--keystone Only keystones (marked ◆ in tables).
|
|
1589
1661
|
--chat <id|title|none> Source chat ('none' for manual memories).
|
|
1590
1662
|
--project <id|name> Project context.
|
|
1591
1663
|
--since <date> ISO date floor on createdAt.
|
|
@@ -1617,9 +1689,9 @@ Note: -i is reserved here for 'grep --ignore-case'. Use the long --instance
|
|
|
1617
1689
|
form to target a registered instance.
|
|
1618
1690
|
|
|
1619
1691
|
All memories verbs are read-only. They open the main encrypted database
|
|
1620
|
-
(quilltap.db) and never write to it. 'maintain' and 'validate
|
|
1621
|
-
the writing to the running server; '
|
|
1622
|
-
--write.
|
|
1692
|
+
(quilltap.db) and never write to it. 'maintain', 'keystone' and 'validate
|
|
1693
|
+
--repair' leave the writing to the running server; 'keystone' and 'validate
|
|
1694
|
+
--repair' change data only with --write.
|
|
1623
1695
|
|
|
1624
1696
|
Examples:
|
|
1625
1697
|
quilltap memories ls
|
|
@@ -1634,6 +1706,9 @@ Examples:
|
|
|
1634
1706
|
quilltap memories validate --list
|
|
1635
1707
|
quilltap memories validate --repair # rehearse the repair
|
|
1636
1708
|
quilltap memories validate --repair --write # apply it
|
|
1709
|
+
quilltap memories ls --character Friday --keystone
|
|
1710
|
+
quilltap memories keystone abc12345 --write # mark a standing fact
|
|
1711
|
+
quilltap memories keystone abc12345 --off --write # unmark it
|
|
1637
1712
|
quilltap memories maintain --instance Friday --character Friday --dry-run
|
|
1638
1713
|
quilltap memories maintain --instance Friday --character Friday --phases consolidate --max 100
|
|
1639
1714
|
quilltap memories maintain --character all --phases demote,retire --dry-run
|
|
@@ -1671,8 +1746,11 @@ async function memoriesCommand(args) {
|
|
|
1671
1746
|
if (flags.repair && verb !== 'validate') {
|
|
1672
1747
|
throw new Error('--repair applies only to memories validate.');
|
|
1673
1748
|
}
|
|
1674
|
-
if (flags.write && !(verb === 'validate' && flags.repair)) {
|
|
1675
|
-
throw new Error('--write applies only to memories validate --repair.');
|
|
1749
|
+
if (flags.write && !(verb === 'validate' && flags.repair) && verb !== 'keystone') {
|
|
1750
|
+
throw new Error('--write applies only to memories validate --repair and memories keystone.');
|
|
1751
|
+
}
|
|
1752
|
+
if (flags.off && verb !== 'keystone') {
|
|
1753
|
+
throw new Error('--off applies only to memories keystone.');
|
|
1676
1754
|
}
|
|
1677
1755
|
switch (verb) {
|
|
1678
1756
|
case 'ls': await cmdLs(flags); break;
|
|
@@ -1682,6 +1760,7 @@ async function memoriesCommand(args) {
|
|
|
1682
1760
|
case 'tree': await cmdTree(flags, positional); break;
|
|
1683
1761
|
case 'status': await cmdStatus(flags); break;
|
|
1684
1762
|
case 'validate': await cmdValidate(flags); break;
|
|
1763
|
+
case 'keystone': await cmdKeystone(flags, positional); break;
|
|
1685
1764
|
case 'maintain': await cmdMaintain(flags, resolveCharacterIdForServer); break;
|
|
1686
1765
|
default:
|
|
1687
1766
|
console.error(`Unknown memories subcommand: ${verb}`);
|
package/lib/memories-server.js
CHANGED
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* The one way a `quilltap memories` verb asks the running server to do what
|
|
5
5
|
* the CLI cannot: anything that calls a model, computes an embedding or
|
|
6
|
-
* writes. The CLI's own database access is read-only; `maintain
|
|
7
|
-
* `validate --repair` resolve what they can locally and
|
|
8
|
-
* `/api/v1/memories?action=<action
|
|
6
|
+
* writes. The CLI's own database access is read-only; `maintain`,
|
|
7
|
+
* `validate --repair` and `keystone` resolve what they can locally and send
|
|
8
|
+
* the rest to `/api/v1/memories?action=<action>` (or, for a hand edit, the
|
|
9
|
+
* memory's own PUT).
|
|
9
10
|
*
|
|
10
11
|
* The port comes from `resolveServerPort` (lock-helpers): an explicit
|
|
11
12
|
* `--port`, else the port recorded in the `--instance` / `--data-dir`
|
|
@@ -29,28 +30,20 @@ function resolveMemoriesServerPort(flags, { quiet = false } = {}) {
|
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
/**
|
|
32
|
-
*
|
|
33
|
-
* payload. Throws an Error with a readable message on a connection
|
|
34
|
-
* or a non-2xx response.
|
|
35
|
-
*
|
|
36
|
-
* @param {object} flags parsed memories flags (port / instance / dataDir)
|
|
37
|
-
* @param {string} action the `?action=` name
|
|
38
|
-
* @param {object} body JSON request body
|
|
39
|
-
* @param {object} [opts]
|
|
40
|
-
* @param {string} [opts.why] one line on why the server is needed
|
|
41
|
-
* @param {(msg: string) => void} [opts.announce] called with the URL before the request
|
|
42
|
-
* @param {typeof fetch} [opts.fetchImpl] for tests
|
|
33
|
+
* Send `body` to a memories API path on the running server and return the
|
|
34
|
+
* parsed payload. Throws an Error with a readable message on a connection
|
|
35
|
+
* failure or a non-2xx response.
|
|
43
36
|
*/
|
|
44
|
-
async function
|
|
45
|
-
const { why, announce, fetchImpl = fetch } = opts;
|
|
37
|
+
async function requestMemoriesApi(flags, method, path, body, opts = {}) {
|
|
38
|
+
const { why, announce, fetchImpl = fetch, action } = opts;
|
|
46
39
|
const target = resolveMemoriesServerPort(flags);
|
|
47
|
-
const url = `http://localhost:${target.port}
|
|
40
|
+
const url = `http://localhost:${target.port}${path}`;
|
|
48
41
|
if (announce) announce(url);
|
|
49
42
|
|
|
50
43
|
let res;
|
|
51
44
|
try {
|
|
52
45
|
res = await fetchImpl(url, {
|
|
53
|
-
method
|
|
46
|
+
method,
|
|
54
47
|
headers: { 'Content-Type': 'application/json' },
|
|
55
48
|
body: JSON.stringify(body),
|
|
56
49
|
});
|
|
@@ -75,7 +68,7 @@ async function postMemoriesAction(flags, action, body, opts = {}) {
|
|
|
75
68
|
.map((d) => `${Array.isArray(d.path) && d.path.length ? d.path.join('.') + ' ' : ''}${d.message || ''}`.trim())
|
|
76
69
|
.join('; ');
|
|
77
70
|
}
|
|
78
|
-
if (res.status === 400 && /unknown action|invalid action/i.test(msg)) {
|
|
71
|
+
if (action && res.status === 400 && /unknown action|invalid action/i.test(msg)) {
|
|
79
72
|
msg += ` (is the server older than this CLI? '${action}' needs a matching Quilltap server)`;
|
|
80
73
|
}
|
|
81
74
|
throw new Error(msg);
|
|
@@ -83,4 +76,30 @@ async function postMemoriesAction(flags, action, body, opts = {}) {
|
|
|
83
76
|
return payload;
|
|
84
77
|
}
|
|
85
78
|
|
|
86
|
-
|
|
79
|
+
/**
|
|
80
|
+
* POST `body` to `/api/v1/memories?action=<action>` and return the parsed
|
|
81
|
+
* payload. Throws an Error with a readable message on a connection failure
|
|
82
|
+
* or a non-2xx response.
|
|
83
|
+
*
|
|
84
|
+
* @param {object} flags parsed memories flags (port / instance / dataDir)
|
|
85
|
+
* @param {string} action the `?action=` name
|
|
86
|
+
* @param {object} body JSON request body
|
|
87
|
+
* @param {object} [opts]
|
|
88
|
+
* @param {string} [opts.why] one line on why the server is needed
|
|
89
|
+
* @param {(msg: string) => void} [opts.announce] called with the URL before the request
|
|
90
|
+
* @param {typeof fetch} [opts.fetchImpl] for tests
|
|
91
|
+
*/
|
|
92
|
+
async function postMemoriesAction(flags, action, body, opts = {}) {
|
|
93
|
+
return requestMemoriesApi(flags, 'POST', `/api/v1/memories?action=${encodeURIComponent(action)}`, body, { ...opts, action });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* PUT a hand edit to `/api/v1/memories/<id>` (the edit chokepoint) and return
|
|
98
|
+
* the parsed payload (`{ memory }`). Same options and errors as
|
|
99
|
+
* {@link postMemoriesAction}.
|
|
100
|
+
*/
|
|
101
|
+
async function putMemory(flags, id, body, opts = {}) {
|
|
102
|
+
return requestMemoriesApi(flags, 'PUT', `/api/v1/memories/${encodeURIComponent(id)}`, body, opts);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
module.exports = { postMemoriesAction, putMemory, resolveMemoriesServerPort };
|
|
@@ -61,7 +61,9 @@ Options:
|
|
|
61
61
|
multipliers
|
|
62
62
|
{RECALL_MULTIPLIERS key:
|
|
63
63
|
value}, specificAnchors, anchorMinHits,
|
|
64
|
-
anchorOrder, backgroundReserve
|
|
64
|
+
anchorOrder, backgroundReserve,
|
|
65
|
+
innerCircleShare, innerCircleAllowance
|
|
66
|
+
([3,2,1] = digests per member by tier).
|
|
65
67
|
Unknown keys are rejected.
|
|
66
68
|
--tuning-file <path> Read the --tuning JSON from a file
|
|
67
69
|
--signals-from <path> Reuse the signals saved in a previous --json
|