wendkeep 0.67.2 → 0.67.3
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/CHANGELOG.md +12 -0
- package/README.en.md +4 -2
- package/README.md +4 -2
- package/docs/en/commands/maintenance-and-diagnostics.md +8 -1
- package/docs/en/commands/memory.md +12 -0
- package/docs/pt-BR/commands/maintenance-and-diagnostics.md +8 -1
- package/docs/pt-BR/commands/memory.md +12 -0
- package/hooks/vault-health.mjs +19 -2
- package/package.json +2 -2
- package/packages/cli/src/index.mjs +1 -1
- package/src/memory.mjs +95 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,18 @@ All notable changes to **wendkeep** are documented here. Format based on
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this project follows
|
|
5
5
|
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.67.3] — 2026-08-02
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **Conflitos semânticos agora levam a uma curadoria humana executável.** O doctor diferencia
|
|
12
|
+
candidates em conflito de corrupção estrutural, explica que `memory repair` não escolhe um
|
|
13
|
+
vencedor e mostra `memory candidates --active --vault <vault>` com o Vault resolvido, seguido
|
|
14
|
+
pelos modelos explícitos para promover ou rejeitar a decisão.
|
|
15
|
+
- **A inspeção de candidates deixa de exigir leitura direta do sidecar.** O novo comando read-only
|
|
16
|
+
`memory candidates [--active]` devolve somente `candidate_id`, `reason`, `status`, `memory_key` e
|
|
17
|
+
`event_ids`, sem valores ou conteúdo, em ordem determinística e sem alterar o bundle de memória.
|
|
18
|
+
|
|
7
19
|
## [0.67.2] — 2026-08-02
|
|
8
20
|
|
|
9
21
|
### Fixed
|
package/README.en.md
CHANGED
|
@@ -369,8 +369,10 @@ backup/audit; divergent mirrors fail closed. A demonstrably superseded
|
|
|
369
369
|
ambiguity uses `memory reconcile <session> --by-session <successor>
|
|
370
370
|
--reason <reason>` as a dry run and requires `--apply`; the decision is backed up and audited
|
|
371
371
|
without rewriting ledger, CORE, or notes. Run `status --gate` again afterwards. Conflicts require
|
|
372
|
-
explicit, durable curation: `memory
|
|
373
|
-
|
|
372
|
+
explicit, durable curation: `memory candidates --active --vault <vault>` lists only safe IDs and
|
|
373
|
+
metadata in read-only mode — it does not expose memory values or content. After human review,
|
|
374
|
+
`memory promote <id> --event <event-id>` selects one event from the candidate, while
|
|
375
|
+
`memory reject <id>` keeps the current value. `memory repair` does not choose a winner. The decision is idempotent, and a
|
|
374
376
|
new promotion accepts a later Stop from the same session/activation without recreating a conflict.
|
|
375
377
|
A promotion written by 0.66.1 remains historical: if the next Stop forms a new candidate, update
|
|
376
378
|
to 0.66.3 and run `memory repair`. During replay, a transient candidate is re-evaluated against the
|
package/README.md
CHANGED
|
@@ -369,8 +369,10 @@ backup/audit; divergent mirrors fail closed. A demonstrably superseded
|
|
|
369
369
|
ambiguity uses `memory reconcile <session> --by-session <successor>
|
|
370
370
|
--reason <reason>` as a dry run and requires `--apply`; the decision is backed up and audited
|
|
371
371
|
without rewriting ledger, CORE, or notes. Run `status --gate` again afterwards. Conflicts require
|
|
372
|
-
explicit, durable curation: `memory
|
|
373
|
-
|
|
372
|
+
explicit, durable curation: `memory candidates --active --vault <vault>` lists only safe IDs and
|
|
373
|
+
metadata in read-only mode — it does not expose memory values or content. After human review,
|
|
374
|
+
`memory promote <id> --event <event-id>` selects one event from the candidate, while
|
|
375
|
+
`memory reject <id>` keeps the current value. `memory repair` does not choose a winner. The decision is idempotent, and a
|
|
374
376
|
new promotion accepts a later Stop from the same session/activation without recreating a conflict.
|
|
375
377
|
A promotion written by 0.66.1 remains historical: if the next Stop forms a new candidate, update
|
|
376
378
|
to 0.66.3 and run `memory repair`. During replay, a transient candidate is re-evaluated against the
|
|
@@ -57,6 +57,7 @@ npx --no-install wendkeep --version
|
|
|
57
57
|
npx --no-install wendkeep sync-defs --check --vault .MyApp-vault --project .
|
|
58
58
|
npx --no-install wendkeep doctor --vault .MyApp-vault
|
|
59
59
|
npx --no-install wendkeep memory status --gate --vault .MyApp-vault
|
|
60
|
+
npx --no-install wendkeep memory candidates --active --vault .MyApp-vault
|
|
60
61
|
npx --no-install wendkeep cost rebuild --session <id> --json --vault .MyApp-vault
|
|
61
62
|
npx --no-install wendkeep cost rebuild --session <id> --json --vault .MyApp-vault --apply
|
|
62
63
|
```
|
|
@@ -74,11 +75,17 @@ dry-run path before any write.
|
|
|
74
75
|
|
|
75
76
|
- `no vault`: run from the bound root or pass `--vault`.
|
|
76
77
|
- `defs stale`: confirm the version and run `sync-defs --reseed`.
|
|
77
|
-
- Legacy vault: this is a non-blocking warning;
|
|
78
|
+
- Legacy vault: this is a non-blocking warning; doctor shows
|
|
79
|
+
`npx --no-install wendkeep memory migrate --apply --vault <vault>` with the resolved Vault, but
|
|
80
|
+
migration remains opt-in and must be planned separately.
|
|
78
81
|
- `degraded` plus an intact outbox: warning; preserve the outbox and allow idempotent replay.
|
|
79
82
|
- `ambiguous`, lost publication, or a mismatched checkpoint: blocking; preserve registry, ledger,
|
|
80
83
|
outbox, and SHARED so `last_memory_attempt` can be correlated before repair.
|
|
81
84
|
- Corrupt bundle: preserve evidence and run `memory status --gate` before `memory repair`.
|
|
85
|
+
- An active semantic conflict requires a human decision: `memory repair` does not choose a winner.
|
|
86
|
+
List safe IDs with `memory candidates --active --vault <vault>`, review the evidence, and then use
|
|
87
|
+
`memory promote <candidate-id> --event <event-id> --vault <vault>` to select an event or
|
|
88
|
+
`memory reject <candidate-id> --vault <vault>` to keep the current operational value.
|
|
82
89
|
- `legacy`/`degraded`/`stale`/`manifest-unproven` observability: run
|
|
83
90
|
`npx --no-install wendkeep cost rebuild --session <id> --json --vault <vault>`, review diagnostics,
|
|
84
91
|
and only then authorize the second variant with `--apply`.
|
|
@@ -24,6 +24,7 @@ Pass the vault explicitly in automation. Preserve backups and evidence before re
|
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
26
|
npx wendkeep memory status [--gate] --vault <vault>
|
|
27
|
+
npx wendkeep memory candidates [--active] --vault <vault>
|
|
27
28
|
npx wendkeep memory repair --vault <vault>
|
|
28
29
|
npx wendkeep memory recover-attempt <session> [--apply] --vault <vault>
|
|
29
30
|
npx wendkeep memory reconcile <ambiguous-session> --by-session <successor-session> --reason <reason> [--apply] --vault <vault>
|
|
@@ -36,6 +37,13 @@ npx wendkeep validate-memory --vault <v2-vault>
|
|
|
36
37
|
## Options and exit codes
|
|
37
38
|
|
|
38
39
|
- `memory status` is read-only; `--gate` exits `1` only for blocking state.
|
|
40
|
+
- `memory candidates` is read-only and prints deterministic JSON containing only `candidate_id`,
|
|
41
|
+
`reason`, `status`, `memory_key`, and `event_ids`; it does not expose memory values or content and
|
|
42
|
+
does not create a lock or mutate the bundle. `--active` omits terminal candidates (`resolved`,
|
|
43
|
+
`rejected`, and `superseded`). A missing status is normalized to `active`.
|
|
44
|
+
- For `memory candidates`, exit `0` means a valid inventory (including empty or conflicted), exit
|
|
45
|
+
`1` means an invalid sidecar/unsafe topology, and exit `2` means a missing `--vault`, unknown or
|
|
46
|
+
duplicate option, extra argument, or an invalid value passed to `--active`.
|
|
39
47
|
- `Stop` writes events to the outbox before acknowledging `last_memory_attempt: enqueued`, then the
|
|
40
48
|
projector runs outside the registry lock. Retrying the same attempt reuses its frozen event IDs
|
|
41
49
|
and can project them at most once.
|
|
@@ -105,6 +113,7 @@ npx wendkeep validate-memory --vault <v2-vault>
|
|
|
105
113
|
|
|
106
114
|
```bash
|
|
107
115
|
npx wendkeep memory status --gate --vault .MyApp-vault
|
|
116
|
+
npx wendkeep memory candidates --active --vault .MyApp-vault
|
|
108
117
|
npx wendkeep memory recover-attempt session-123 --vault .MyApp-vault
|
|
109
118
|
npx wendkeep memory recover-attempt session-123 --apply --vault .MyApp-vault
|
|
110
119
|
npx wendkeep memory reconcile old --by-session current --reason "delivery continued" --vault .MyApp-vault
|
|
@@ -121,6 +130,9 @@ state of the last attempt. CORE stays hand-curated and canonical; SHARED stays a
|
|
|
121
130
|
operational projection. After successful projection, an attempt checkpoint may be a valid prefix
|
|
122
131
|
of a global projection that has already advanced with concurrent events.
|
|
123
132
|
|
|
133
|
+
`memory candidates` returns `status: "ok"` and the sanitized candidate list in stable order; with
|
|
134
|
+
`--active`, the list contains only decisions still open for human curation.
|
|
135
|
+
|
|
124
136
|
## Common errors and diagnosis
|
|
125
137
|
|
|
126
138
|
- `legacy`: follow the migration guide; this is not corruption.
|
|
@@ -57,6 +57,7 @@ npx --no-install wendkeep --version
|
|
|
57
57
|
npx --no-install wendkeep sync-defs --check --vault .MeuApp-vault --project .
|
|
58
58
|
npx --no-install wendkeep doctor --vault .MeuApp-vault
|
|
59
59
|
npx --no-install wendkeep memory status --gate --vault .MeuApp-vault
|
|
60
|
+
npx --no-install wendkeep memory candidates --active --vault .MeuApp-vault
|
|
60
61
|
npx --no-install wendkeep cost rebuild --session <id> --json --vault .MeuApp-vault
|
|
61
62
|
npx --no-install wendkeep cost rebuild --session <id> --json --vault .MeuApp-vault --apply
|
|
62
63
|
```
|
|
@@ -74,11 +75,17 @@ ou sem manifest comprovado e oferece um caminho dry-run antes de qualquer escrit
|
|
|
74
75
|
|
|
75
76
|
- `no vault`: execute da raiz vinculada ou passe `--vault`.
|
|
76
77
|
- `defs stale`: confirme a versão e rode `sync-defs --reseed`.
|
|
77
|
-
- Vault legado: é warning não bloqueante;
|
|
78
|
+
- Vault legado: é warning não bloqueante; o doctor mostra
|
|
79
|
+
`npx --no-install wendkeep memory migrate --apply --vault <cofre>` com o Vault resolvido, mas a
|
|
80
|
+
migração continua sendo opt-in e deve ser planejada separadamente.
|
|
78
81
|
- `degraded` + outbox íntegra: warning; preserve a outbox e permita replay idempotente.
|
|
79
82
|
- `ambiguous`, publicação perdida ou checkpoint divergente: bloqueante; preserve registry, ledger,
|
|
80
83
|
outbox e SHARED para correlacionar `last_memory_attempt` antes de reparar.
|
|
81
84
|
- Bundle corrompido: preserve a evidência e use `memory status --gate` antes de `memory repair`.
|
|
85
|
+
- Conflito semântico ativo exige decisão humana: `memory repair` não escolhe vencedor. Liste os IDs
|
|
86
|
+
seguros com `memory candidates --active --vault <cofre>`, revise a evidência e então use
|
|
87
|
+
`memory promote <candidate-id> --event <event-id> --vault <cofre>` para selecionar um evento ou
|
|
88
|
+
`memory reject <candidate-id> --vault <cofre>` para manter o valor operacional atual.
|
|
82
89
|
- Observabilidade `legacy`/`degraded`/`stale`/`manifest-unproven`: rode
|
|
83
90
|
`npx --no-install wendkeep cost rebuild --session <id> --json --vault <cofre>`, revise diagnostics
|
|
84
91
|
e só então autorize a segunda variante com `--apply`.
|
|
@@ -24,6 +24,7 @@ Informe o vault explicitamente em automações. Preserve backups e evidências a
|
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
26
|
npx wendkeep memory status [--gate] --vault <cofre>
|
|
27
|
+
npx wendkeep memory candidates [--active] --vault <cofre>
|
|
27
28
|
npx wendkeep memory repair --vault <cofre>
|
|
28
29
|
npx wendkeep memory recover-attempt <sessão> [--apply] --vault <cofre>
|
|
29
30
|
npx wendkeep memory reconcile <sessão-ambígua> --by-session <sessão-sucessora> --reason <motivo> [--apply] --vault <cofre>
|
|
@@ -36,6 +37,13 @@ npx wendkeep validate-memory --vault <cofre-v2>
|
|
|
36
37
|
## Opções e códigos de saída
|
|
37
38
|
|
|
38
39
|
- `memory status` é read-only; `--gate` retorna exit `1` apenas para estado bloqueante.
|
|
40
|
+
- `memory candidates` é read-only e imprime JSON determinístico com somente `candidate_id`,
|
|
41
|
+
`reason`, `status`, `memory_key` e `event_ids`; não expõe valores nem conteúdo da memória e não
|
|
42
|
+
cria lock nem altera o bundle. `--active` omite candidates terminais (`resolved`, `rejected` e
|
|
43
|
+
`superseded`). Status ausente é normalizado para `active`.
|
|
44
|
+
- Em `memory candidates`, exit `0` indica inventário válido (inclusive vazio ou com conflitos),
|
|
45
|
+
exit `1` indica sidecar inválido/topologia insegura e exit `2` indica `--vault` ausente, opção
|
|
46
|
+
desconhecida/duplicada, argumento extra ou valor indevido em `--active`.
|
|
39
47
|
- O `Stop` grava os eventos na outbox antes de reconhecer `last_memory_attempt: enqueued`; depois o
|
|
40
48
|
projector roda fora do lock do registry. Retry do mesmo attempt reutiliza os event IDs congelados
|
|
41
49
|
e pode projetá-los no máximo uma vez.
|
|
@@ -103,6 +111,7 @@ npx wendkeep validate-memory --vault <cofre-v2>
|
|
|
103
111
|
|
|
104
112
|
```bash
|
|
105
113
|
npx wendkeep memory status --gate --vault .MeuApp-vault
|
|
114
|
+
npx wendkeep memory candidates --active --vault .MeuApp-vault
|
|
106
115
|
npx wendkeep memory recover-attempt sessao-123 --vault .MeuApp-vault
|
|
107
116
|
npx wendkeep memory recover-attempt sessao-123 --apply --vault .MeuApp-vault
|
|
108
117
|
npx wendkeep memory reconcile antiga --by-session atual --reason "entrega continuada" --vault .MeuApp-vault
|
|
@@ -119,6 +128,9 @@ causal do último attempt. CORE permanece canônico e curado à mão; SHARED per
|
|
|
119
128
|
operacional verificável. Depois de uma projeção bem-sucedida, o checkpoint do attempt pode ser um
|
|
120
129
|
prefixo válido de uma projeção global que já avançou com eventos concorrentes.
|
|
121
130
|
|
|
131
|
+
`memory candidates` retorna `status: "ok"` e a lista sanitizada de candidates em ordem estável;
|
|
132
|
+
com `--active`, a lista contém somente decisões ainda abertas para curadoria humana.
|
|
133
|
+
|
|
122
134
|
## Erros comuns e diagnóstico
|
|
123
135
|
|
|
124
136
|
- `legacy`: siga o guia de migração; não é corrupção.
|
package/hooks/vault-health.mjs
CHANGED
|
@@ -106,6 +106,18 @@ const memoryStatusCommand = (vaultBase) => (
|
|
|
106
106
|
const memoryRepairCommand = (vaultBase) => (
|
|
107
107
|
`${WENDKEEP_COMMAND} memory repair --vault ${quoteCommandArgument(vaultBase)}`
|
|
108
108
|
);
|
|
109
|
+
const memoryMigrateCommand = (vaultBase) => (
|
|
110
|
+
`${WENDKEEP_COMMAND} memory migrate --apply --vault ${quoteCommandArgument(vaultBase)}`
|
|
111
|
+
);
|
|
112
|
+
const memoryCandidatesCommand = (vaultBase) => (
|
|
113
|
+
`${WENDKEEP_COMMAND} memory candidates --active --vault ${quoteCommandArgument(vaultBase)}`
|
|
114
|
+
);
|
|
115
|
+
const memoryPromoteCommand = (vaultBase) => (
|
|
116
|
+
`${WENDKEEP_COMMAND} memory promote <candidate-id> --event <event-id> --vault ${quoteCommandArgument(vaultBase)}`
|
|
117
|
+
);
|
|
118
|
+
const memoryRejectCommand = (vaultBase) => (
|
|
119
|
+
`${WENDKEEP_COMMAND} memory reject <candidate-id> --vault ${quoteCommandArgument(vaultBase)}`
|
|
120
|
+
);
|
|
109
121
|
|
|
110
122
|
function readJsonLines(vaultBase, path, label) {
|
|
111
123
|
let checked;
|
|
@@ -397,7 +409,7 @@ export function checkMemoryBundle(vaultBase, { registry } = {}) {
|
|
|
397
409
|
ok: true,
|
|
398
410
|
status: 'legacy',
|
|
399
411
|
failures: [],
|
|
400
|
-
warnings: [LEGACY_MEMORY_WARNING],
|
|
412
|
+
warnings: [`${LEGACY_MEMORY_WARNING} Comando com Vault resolvido: ${memoryMigrateCommand(vaultBase)}.`],
|
|
401
413
|
metrics: memoryMetrics(),
|
|
402
414
|
};
|
|
403
415
|
}
|
|
@@ -466,7 +478,12 @@ export function checkMemoryBundle(vaultBase, { registry } = {}) {
|
|
|
466
478
|
const activeConflicts = unresolved.filter((item) => item?.reason === 'conflict');
|
|
467
479
|
const ordinaryCandidates = unresolved.filter((item) => item?.reason !== 'conflict');
|
|
468
480
|
if (activeConflicts.length) {
|
|
469
|
-
|
|
481
|
+
const keys = [...new Set(activeConflicts.map((item) => item.memory_key || item.candidate_id))]
|
|
482
|
+
.sort((left, right) => String(left).localeCompare(String(right)));
|
|
483
|
+
const label = activeConflicts.length === 1
|
|
484
|
+
? '1 conflito ativo'
|
|
485
|
+
: `${activeConflicts.length} conflitos ativos`;
|
|
486
|
+
failures.push(`${label} em chave operacional (${keys.join(', ')}). Conflito semântico exige curadoria humana; memory repair não escolhe vencedor. Liste IDs seguros com: ${memoryCandidatesCommand(vaultBase)}. Depois da revisão, use ${memoryPromoteCommand(vaultBase)} ou ${memoryRejectCommand(vaultBase)}.`);
|
|
470
487
|
}
|
|
471
488
|
if (outbox.count) warnings.push(`${outbox.count} evento(s) pendente(s) na outbox; execute o projector quando seguro.`);
|
|
472
489
|
if (ordinaryCandidates.length) warnings.push(`${ordinaryCandidates.length} candidate(s) aguardando curadoria humana.`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wendkeep",
|
|
3
|
-
"version": "0.67.
|
|
3
|
+
"version": "0.67.3",
|
|
4
4
|
"description": "Vault-first persistent memory for AI coding agents, with an optional profile-aware governance runtime: OFF, FLOW, GUIDE, GOVERN, or ASSURE. Local-first and agent-agnostic (Claude Code, Codex, Cursor…).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"workspaces": [
|
|
@@ -70,6 +70,6 @@
|
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"acorn": "^8.18.0",
|
|
73
|
-
"wendkeep": "^0.67.
|
|
73
|
+
"wendkeep": "^0.67.2"
|
|
74
74
|
}
|
|
75
75
|
}
|
|
@@ -99,7 +99,7 @@ Usage:
|
|
|
99
99
|
in session notes from the linked derived notes — the body used to lag
|
|
100
100
|
behind the closing block. Dry-run by default · --apply · --json.
|
|
101
101
|
wendkeep lesson add "t" "l" Record a project-local lesson (injected at SessionStart).
|
|
102
|
-
wendkeep memory <sub> Shared memory v2: status | migrate [--apply] | repair |
|
|
102
|
+
wendkeep memory <sub> Shared memory v2: status | candidates [--active] | migrate [--apply] | repair |
|
|
103
103
|
recover-attempt <session> [--apply] |
|
|
104
104
|
reconcile <session> --by-session <session> --reason <text> [--apply] |
|
|
105
105
|
promote <candidate> [--event <event-id>] | reject <candidate>. --vault P.
|
package/src/memory.mjs
CHANGED
|
@@ -237,6 +237,48 @@ function readCandidates(vault) {
|
|
|
237
237
|
.split('\n').filter(Boolean).map((line) => JSON.parse(line));
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
+
const TERMINAL_CANDIDATE_STATUSES = new Set(['resolved', 'rejected', 'superseded']);
|
|
241
|
+
|
|
242
|
+
function lexicalCompare(left, right) {
|
|
243
|
+
if (left < right) return -1;
|
|
244
|
+
if (left > right) return 1;
|
|
245
|
+
return 0;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function sanitizedCandidate(candidate, index) {
|
|
249
|
+
if (!candidate || typeof candidate !== 'object' || Array.isArray(candidate)) {
|
|
250
|
+
throw new Error(`MEMORY_CANDIDATES.jsonl: candidate ${index + 1} inválido.`);
|
|
251
|
+
}
|
|
252
|
+
const required = ['candidate_id', 'reason', 'memory_key'];
|
|
253
|
+
const missing = required.filter((field) => typeof candidate[field] !== 'string' || !candidate[field]);
|
|
254
|
+
if (missing.length) {
|
|
255
|
+
throw new Error(`MEMORY_CANDIDATES.jsonl: candidate ${index + 1} sem ${missing.join(', ')}.`);
|
|
256
|
+
}
|
|
257
|
+
if (candidate.status !== undefined && (typeof candidate.status !== 'string' || !candidate.status)) {
|
|
258
|
+
throw new Error(`MEMORY_CANDIDATES.jsonl: candidate ${index + 1} possui status inválido.`);
|
|
259
|
+
}
|
|
260
|
+
const eventIds = candidate.event_ids ?? [];
|
|
261
|
+
if (!Array.isArray(eventIds) || eventIds.some((eventId) => typeof eventId !== 'string' || !eventId)) {
|
|
262
|
+
throw new Error(`MEMORY_CANDIDATES.jsonl: candidate ${index + 1} possui event_ids inválidos.`);
|
|
263
|
+
}
|
|
264
|
+
return {
|
|
265
|
+
candidate_id: candidate.candidate_id,
|
|
266
|
+
reason: candidate.reason,
|
|
267
|
+
status: candidate.status || 'active',
|
|
268
|
+
memory_key: candidate.memory_key,
|
|
269
|
+
event_ids: [...eventIds].sort(lexicalCompare),
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
export function listMemoryCandidates(vault, { activeOnly = false } = {}) {
|
|
274
|
+
const candidates = readCandidates(vault)
|
|
275
|
+
.map(sanitizedCandidate)
|
|
276
|
+
.filter((candidate) => !activeOnly || !TERMINAL_CANDIDATE_STATUSES.has(candidate.status))
|
|
277
|
+
.sort((left, right) => lexicalCompare(left.memory_key, right.memory_key)
|
|
278
|
+
|| lexicalCompare(left.candidate_id, right.candidate_id));
|
|
279
|
+
return { status: 'ok', candidates };
|
|
280
|
+
}
|
|
281
|
+
|
|
240
282
|
function priorCandidateDecision(vault, candidateId) {
|
|
241
283
|
return readMemoryLedger(vault).events.find(
|
|
242
284
|
(event) => event.candidate_decision?.candidate_id === candidateId,
|
|
@@ -1897,10 +1939,49 @@ function parseRecoverAttemptArgs(argv) {
|
|
|
1897
1939
|
};
|
|
1898
1940
|
}
|
|
1899
1941
|
|
|
1942
|
+
function parseCandidatesArgs(argv) {
|
|
1943
|
+
const seen = new Set();
|
|
1944
|
+
let activeOnly = false;
|
|
1945
|
+
let vault = '';
|
|
1946
|
+
|
|
1947
|
+
for (let index = 1; index < argv.length; index += 1) {
|
|
1948
|
+
const token = argv[index];
|
|
1949
|
+
if (!token.startsWith('--')) {
|
|
1950
|
+
throw memoryUsageError(`memory candidates recebeu argumento posicional extra: ${token}.`);
|
|
1951
|
+
}
|
|
1952
|
+
|
|
1953
|
+
const equalAt = token.indexOf('=');
|
|
1954
|
+
const name = equalAt >= 0 ? token.slice(0, equalAt) : token;
|
|
1955
|
+
if (name !== '--active' && name !== '--vault') {
|
|
1956
|
+
throw memoryUsageError(`memory candidates recebeu opção desconhecida: ${name}.`);
|
|
1957
|
+
}
|
|
1958
|
+
if (seen.has(name)) {
|
|
1959
|
+
throw memoryUsageError(`memory candidates recebeu opção duplicada: ${name}.`);
|
|
1960
|
+
}
|
|
1961
|
+
seen.add(name);
|
|
1962
|
+
|
|
1963
|
+
if (name === '--active') {
|
|
1964
|
+
if (equalAt >= 0) throw memoryUsageError('--active não aceita valor.');
|
|
1965
|
+
activeOnly = true;
|
|
1966
|
+
continue;
|
|
1967
|
+
}
|
|
1968
|
+
|
|
1969
|
+
const value = equalAt >= 0 ? token.slice(equalAt + 1) : argv[index + 1];
|
|
1970
|
+
if (!value || !value.trim() || value.startsWith('--')) {
|
|
1971
|
+
throw memoryUsageError('--vault requer valor não vazio que não comece com --.');
|
|
1972
|
+
}
|
|
1973
|
+
vault = value;
|
|
1974
|
+
if (equalAt < 0) index += 1;
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1977
|
+
return { activeOnly, vault };
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1900
1980
|
export function runMemory(argv) {
|
|
1901
1981
|
const [sub, positional] = argv;
|
|
1902
1982
|
let reconcileArgs = null;
|
|
1903
1983
|
let recoverAttemptArgs = null;
|
|
1984
|
+
let candidatesArgs = null;
|
|
1904
1985
|
if (sub === 'reconcile') {
|
|
1905
1986
|
try {
|
|
1906
1987
|
reconcileArgs = parseReconcileArgs(argv);
|
|
@@ -1919,14 +2000,26 @@ export function runMemory(argv) {
|
|
|
1919
2000
|
return;
|
|
1920
2001
|
}
|
|
1921
2002
|
}
|
|
2003
|
+
if (sub === 'candidates') {
|
|
2004
|
+
try {
|
|
2005
|
+
candidatesArgs = parseCandidatesArgs(argv);
|
|
2006
|
+
} catch (error) {
|
|
2007
|
+
process.stderr.write(`wendkeep memory: ${error.message}\n`);
|
|
2008
|
+
process.exitCode = error.code === 'WENDKEEP_MEMORY_USAGE' ? 2 : 1;
|
|
2009
|
+
return;
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
1922
2012
|
const vault = (
|
|
1923
|
-
recoverAttemptArgs?.vault || reconcileArgs?.vault || option(argv, '--vault')
|
|
2013
|
+
recoverAttemptArgs?.vault || reconcileArgs?.vault || candidatesArgs?.vault || option(argv, '--vault')
|
|
1924
2014
|
) || process.env.OBSIDIAN_VAULT_PATH;
|
|
1925
2015
|
if (!vault) { process.stderr.write('wendkeep memory: passe --vault <path>.\n'); process.exitCode = 2; return; }
|
|
1926
2016
|
if (!existsSync(vault)) { process.stderr.write(`wendkeep memory: not found: ${vault}\n`); process.exitCode = 2; return; }
|
|
1927
2017
|
try {
|
|
1928
2018
|
let result;
|
|
1929
2019
|
if (sub === 'status') result = memoryStatus(vault);
|
|
2020
|
+
else if (sub === 'candidates') {
|
|
2021
|
+
result = listMemoryCandidates(vault, { activeOnly: candidatesArgs.activeOnly });
|
|
2022
|
+
}
|
|
1930
2023
|
else if (sub === 'migrate') result = migrateMemory(vault, { apply: argv.includes('--apply') });
|
|
1931
2024
|
else if (sub === 'repair') result = repairMemory(vault);
|
|
1932
2025
|
else if (sub === 'reconcile') {
|
|
@@ -1950,7 +2043,7 @@ export function runMemory(argv) {
|
|
|
1950
2043
|
action: sub, candidateId: positional, ...(eventId ? { eventId } : {}),
|
|
1951
2044
|
});
|
|
1952
2045
|
}
|
|
1953
|
-
else { process.stderr.write('wendkeep memory: use status | migrate [--apply] | repair | recover-attempt <session> [--apply] | reconcile <session> --by-session <session> --reason <text> [--apply] | promote <candidate> [--event <event-id>] | reject <candidate>.\n'); process.exitCode = 2; return; }
|
|
2046
|
+
else { process.stderr.write('wendkeep memory: use status | candidates [--active] | migrate [--apply] | repair | recover-attempt <session> [--apply] | reconcile <session> --by-session <session> --reason <text> [--apply] | promote <candidate> [--event <event-id>] | reject <candidate>.\n'); process.exitCode = 2; return; }
|
|
1954
2047
|
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
1955
2048
|
if (sub === 'status' && argv.includes('--gate')) process.exitCode = result.status === 'blocked' ? 1 : 0;
|
|
1956
2049
|
else if (sub === 'reconcile' && reconcileArgs.apply) process.exitCode = result.health?.status === 'blocked' ? 1 : 0;
|