wendkeep 0.58.3 → 0.60.0
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 +93 -0
- package/README.en.md +45 -3
- package/README.md +45 -3
- package/bin/wendkeep.mjs +54 -6
- package/docs/en/commands/changes-and-verification.md +9 -3
- package/docs/en/commands/getting-started.md +7 -3
- package/docs/en/commands/memory.md +20 -2
- package/docs/en/commands/operating-profiles.md +173 -0
- package/docs/en/commands/sessions-and-import.md +8 -4
- package/docs/en/commands/verify.md +12 -6
- package/docs/pt-BR/commands/changes-and-verification.md +9 -4
- package/docs/pt-BR/commands/getting-started.md +7 -3
- package/docs/pt-BR/commands/memory.md +18 -2
- package/docs/pt-BR/commands/operating-profiles.md +171 -0
- package/docs/pt-BR/commands/sessions-and-import.md +7 -3
- package/docs/pt-BR/commands/verify.md +11 -5
- package/hooks/brain-core.mjs +159 -159
- package/hooks/brain-inject.mjs +83 -26
- package/hooks/brain-recall.mjs +32 -32
- package/hooks/brain-reindex.mjs +13 -13
- package/hooks/change-context.mjs +24 -10
- package/hooks/change-core.mjs +174 -37
- package/hooks/change-guard.mjs +115 -16
- package/hooks/change-nag.mjs +20 -5
- package/hooks/change-warn.mjs +27 -9
- package/hooks/decision-capture.mjs +1 -1
- package/hooks/derived-sections.mjs +1 -1
- package/hooks/flow-core.mjs +891 -0
- package/hooks/flow-protected-policy.mjs +218 -0
- package/hooks/frontmatter-repair.mjs +3 -1
- package/hooks/git-snapshot.mjs +722 -0
- package/hooks/import-sessions.mjs +10 -5
- package/hooks/memory-mode.mjs +63 -13
- package/hooks/memory-store.mjs +309 -69
- package/hooks/obsidian-common.mjs +39 -55
- package/hooks/operating-profile-runtime.mjs +157 -0
- package/hooks/plan-capture.mjs +14 -3
- package/hooks/sensors-core.mjs +15 -3
- package/hooks/session-backfill.mjs +7 -2
- package/hooks/session-ensure.mjs +6 -4
- package/hooks/session-iteration.mjs +65 -0
- package/hooks/session-memory-lifecycle.mjs +10 -5
- package/hooks/session-note-io.mjs +130 -15
- package/hooks/session-observability.mjs +4 -2
- package/hooks/session-stop.mjs +65 -19
- package/hooks/spec-core.mjs +91 -12
- package/hooks/subagent-stop.mjs +4 -1
- package/hooks/subagent-usage.mjs +2 -2
- package/hooks/task-log.mjs +3 -1
- package/hooks/token-usage.mjs +1 -1
- package/hooks/vault-health.mjs +183 -37
- package/hooks/vault-path-safety.mjs +2 -0
- package/hooks/vault-runtime-store.mjs +558 -0
- package/package.json +10 -3
- package/packages/cli/package.json +5 -0
- package/packages/harness/package.json +5 -0
- package/packages/integrations/package.json +5 -0
- package/packages/mcp/package.json +5 -0
- package/packages/pi/package.json +5 -0
- package/packages/vault/package.json +6 -0
- package/packages/vault/src/index.mjs +2 -0
- package/packages/vault/src/project-vault.mjs +327 -0
- package/packages/vault/src/vault-path-safety.mjs +558 -0
- package/src/change.mjs +2 -1
- package/src/flow.mjs +232 -0
- package/src/init.mjs +26 -3
- package/src/memory.mjs +785 -35
- package/src/operating-profile.mjs +133 -0
- package/src/profile.mjs +224 -0
- package/src/project-vault.mjs +2 -221
- package/src/rebuild-costs.mjs +11 -4
- package/src/skills-seed.mjs +38 -16
- package/src/sync-defs.mjs +16 -7
- package/src/sync.mjs +9 -1
- package/src/taxonomy.mjs +8 -0
- package/src/validate-memory.mjs +21 -8
- package/src/verify.mjs +12 -2
|
@@ -245,12 +245,12 @@ export function importSession(vaultBase, txPath, opts = {}) {
|
|
|
245
245
|
// One iteration block per turn (insertIteration dedups by turn marker -> re-import safe).
|
|
246
246
|
for (const turn of turns) {
|
|
247
247
|
const block = buildIterationBlock(tx, { turn_id: turn.turnId, now: turn.timestamp });
|
|
248
|
-
insertIteration(absPath, block, turn.turnId, tx);
|
|
248
|
+
insertIteration(absPath, block, turn.turnId, tx, vaultBase);
|
|
249
249
|
}
|
|
250
250
|
|
|
251
251
|
// Cost + subagent telemetry, exactly like the live Stop hook. Fail-open.
|
|
252
252
|
try {
|
|
253
|
-
updateSessionObservability({ sessionPath: absPath, transcriptPath: txPath });
|
|
253
|
+
updateSessionObservability({ vaultBase, sessionPath: absPath, transcriptPath: txPath });
|
|
254
254
|
} catch { /* observability is best-effort */ }
|
|
255
255
|
|
|
256
256
|
// Finalize: derived notes + closing section + ended_at from the last turn.
|
|
@@ -259,7 +259,7 @@ export function importSession(vaultBase, txPath, opts = {}) {
|
|
|
259
259
|
createLinkedNotes(vaultBase, formatDate(endDate), relPath, tx),
|
|
260
260
|
findLinkedDerivedNotes(vaultBase, relPath),
|
|
261
261
|
);
|
|
262
|
-
finalizeSessionFile(absPath, tx, created, endedAt);
|
|
262
|
+
finalizeSessionFile(absPath, tx, created, endedAt, vaultBase);
|
|
263
263
|
|
|
264
264
|
upsertSessionRegistry(vaultBase, sessionId, {
|
|
265
265
|
session_file: relPath,
|
|
@@ -399,9 +399,14 @@ export function runImport(vaultBase, opts = {}) {
|
|
|
399
399
|
}
|
|
400
400
|
try {
|
|
401
401
|
for (const turn of missing) {
|
|
402
|
-
insertIteration(
|
|
402
|
+
insertIteration(
|
|
403
|
+
existingNote, buildIterationBlock(tx, { turn_id: turn.turnId, now: turn.timestamp }),
|
|
404
|
+
turn.turnId, tx, vaultBase,
|
|
405
|
+
);
|
|
403
406
|
}
|
|
404
|
-
try {
|
|
407
|
+
try {
|
|
408
|
+
updateSessionObservability({ vaultBase, sessionPath: existingNote, transcriptPath: t.path });
|
|
409
|
+
} catch { /* best-effort */ }
|
|
405
410
|
report.sessions.push({ sessionId: t.sessionId, turns: missing.length, repaired: true });
|
|
406
411
|
report.repaired++;
|
|
407
412
|
done++;
|
package/hooks/memory-mode.mjs
CHANGED
|
@@ -1,37 +1,87 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFileSync, readdirSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
|
|
4
4
|
import { classifySharedMemory } from './memory-schema.mjs';
|
|
5
|
+
import { assertVaultPathSafe } from './vault-path-safety.mjs';
|
|
5
6
|
|
|
6
7
|
export const LEGACY_MEMORY_WARNING = 'Vault legado: CORE+DIGEST permanece ativo; execute `wendkeep memory migrate --apply` quando a curadoria estiver pronta.';
|
|
7
8
|
|
|
8
|
-
function
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
function aliasBoundaryError(error) {
|
|
10
|
+
return error?.code === 'VAULT_PATH_UNSAFE'
|
|
11
|
+
&& /link simbólico|junction|reparse|hardlink|nlink|redirecion|escapa logicamente/i
|
|
12
|
+
.test(String(error?.message || error));
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
function
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
function readText(vaultBase, path, label) {
|
|
16
|
+
try {
|
|
17
|
+
let checked = assertVaultPathSafe(vaultBase, path, { expectedType: 'file', label });
|
|
18
|
+
if (!checked.exists) return { exists: false, content: '', error: null };
|
|
19
|
+
checked = assertVaultPathSafe(vaultBase, checked.target, {
|
|
20
|
+
allowMissing: false, expectedType: 'file', label,
|
|
21
|
+
});
|
|
22
|
+
return { exists: true, content: readFileSync(checked.target, 'utf8'), error: null };
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
if (aliasBoundaryError(error)) throw error;
|
|
26
|
+
return { exists: true, content: '', error };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function hasOutboxEvidence(vaultBase, path) {
|
|
31
|
+
try {
|
|
32
|
+
let checked = assertVaultPathSafe(vaultBase, path, {
|
|
33
|
+
expectedType: 'directory', label: 'outbox de memória',
|
|
34
|
+
});
|
|
35
|
+
if (!checked.exists) return false;
|
|
36
|
+
checked = assertVaultPathSafe(vaultBase, checked.target, {
|
|
37
|
+
allowMissing: false, expectedType: 'directory', label: 'outbox de memória',
|
|
38
|
+
});
|
|
39
|
+
const entries = readdirSync(checked.target, { withFileTypes: true });
|
|
40
|
+
for (const entry of entries) {
|
|
41
|
+
assertVaultPathSafe(vaultBase, join(checked.target, entry.name), {
|
|
42
|
+
allowMissing: false, label: `entrada ${entry.name} da outbox de memória`,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
return entries.some((entry) => entry.isFile());
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
if (aliasBoundaryError(error)) throw error;
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function preflightOptionalMemoryFile(vaultBase, path, label) {
|
|
54
|
+
try { assertVaultPathSafe(vaultBase, path, { expectedType: 'file', label }); }
|
|
55
|
+
catch (error) {
|
|
56
|
+
if (aliasBoundaryError(error)) throw error;
|
|
57
|
+
// Ordinary unreadable/wrong-type layers preserve the existing degraded-mode contract.
|
|
58
|
+
}
|
|
18
59
|
}
|
|
19
60
|
|
|
20
61
|
/** A single, read-only mode decision shared by injection, health and SessionStop. */
|
|
21
62
|
export function detectMemoryMode(vaultBase) {
|
|
22
63
|
const brain = join(vaultBase, '.brain');
|
|
23
|
-
|
|
64
|
+
assertVaultPathSafe(vaultBase, brain, {
|
|
65
|
+
expectedType: 'directory', label: 'raiz .brain da memória',
|
|
66
|
+
});
|
|
67
|
+
// Preflight the layers consumed immediately after mode detection by injection and Stop.
|
|
68
|
+
for (const name of ['CORE.md', 'PROJECT.json', 'DIGEST.md']) {
|
|
69
|
+
preflightOptionalMemoryFile(vaultBase, join(brain, name), `camada de memória ${name}`);
|
|
70
|
+
}
|
|
71
|
+
const shared = readText(vaultBase, join(brain, 'SHARED_MEMORY.md'), 'camada SHARED_MEMORY.md');
|
|
24
72
|
if (shared.error) return { mode: 'v2', reason: 'shared-unreadable' };
|
|
25
73
|
const classified = classifySharedMemory(shared.content);
|
|
26
74
|
if (classified.mode === 'v2') return classified;
|
|
27
75
|
|
|
28
|
-
const ledger = readText(join(brain, 'MEMORY_EVENTS.jsonl'));
|
|
76
|
+
const ledger = readText(vaultBase, join(brain, 'MEMORY_EVENTS.jsonl'), 'ledger MEMORY_EVENTS.jsonl');
|
|
29
77
|
if (ledger.error) return { mode: 'v2', reason: 'ledger-unreadable' };
|
|
30
|
-
const candidates = readText(
|
|
78
|
+
const candidates = readText(
|
|
79
|
+
vaultBase, join(brain, 'MEMORY_CANDIDATES.jsonl'), 'sidecar MEMORY_CANDIDATES.jsonl',
|
|
80
|
+
);
|
|
31
81
|
if (candidates.error) return { mode: 'v2', reason: 'candidates-unreadable' };
|
|
32
82
|
const ledgerHasEvents = ledger.content.trim().length > 0;
|
|
33
83
|
const candidatesHaveEntries = candidates.content.trim().length > 0;
|
|
34
|
-
const outboxHasEvents = hasOutboxEvidence(join(brain, 'memory-outbox'));
|
|
84
|
+
const outboxHasEvents = hasOutboxEvidence(vaultBase, join(brain, 'memory-outbox'));
|
|
35
85
|
if (ledgerHasEvents || candidatesHaveEntries || outboxHasEvents) {
|
|
36
86
|
return { mode: 'v2', reason: 'operational-evidence' };
|
|
37
87
|
}
|