agent-recall-core 3.4.18 → 3.4.20
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 +404 -981
- package/dist/index.d.ts +35 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +28 -1
- package/dist/index.js.map +1 -1
- package/dist/naming.d.ts +109 -0
- package/dist/naming.d.ts.map +1 -0
- package/dist/naming.js +198 -0
- package/dist/naming.js.map +1 -0
- package/dist/palace/awareness.d.ts +6 -0
- package/dist/palace/awareness.d.ts.map +1 -1
- package/dist/palace/awareness.js +19 -0
- package/dist/palace/awareness.js.map +1 -1
- package/dist/palace/fsrs.d.ts +64 -0
- package/dist/palace/fsrs.d.ts.map +1 -0
- package/dist/palace/fsrs.js +86 -0
- package/dist/palace/fsrs.js.map +1 -0
- package/dist/palace/hopfield.d.ts +108 -0
- package/dist/palace/hopfield.d.ts.map +1 -0
- package/dist/palace/hopfield.js +237 -0
- package/dist/palace/hopfield.js.map +1 -0
- package/dist/palace/obsidian.d.ts.map +1 -1
- package/dist/palace/obsidian.js +25 -1
- package/dist/palace/obsidian.js.map +1 -1
- package/dist/palace/pipeline.d.ts +61 -0
- package/dist/palace/pipeline.d.ts.map +1 -0
- package/dist/palace/pipeline.js +289 -0
- package/dist/palace/pipeline.js.map +1 -0
- package/dist/palace/skills.d.ts +71 -0
- package/dist/palace/skills.d.ts.map +1 -0
- package/dist/palace/skills.js +225 -0
- package/dist/palace/skills.js.map +1 -0
- package/dist/storage/corrections.d.ts +54 -0
- package/dist/storage/corrections.d.ts.map +1 -1
- package/dist/storage/corrections.js +93 -4
- package/dist/storage/corrections.js.map +1 -1
- package/dist/storage/paths.d.ts +12 -0
- package/dist/storage/paths.d.ts.map +1 -1
- package/dist/storage/paths.js +37 -15
- package/dist/storage/paths.js.map +1 -1
- package/dist/storage/session.d.ts.map +1 -1
- package/dist/storage/session.js +16 -1
- package/dist/storage/session.js.map +1 -1
- package/dist/tools-logic/bootstrap.d.ts.map +1 -1
- package/dist/tools-logic/bootstrap.js +69 -6
- package/dist/tools-logic/bootstrap.js.map +1 -1
- package/dist/tools-logic/dashboard-export.d.ts +75 -0
- package/dist/tools-logic/dashboard-export.d.ts.map +1 -0
- package/dist/tools-logic/dashboard-export.js +271 -0
- package/dist/tools-logic/dashboard-export.js.map +1 -0
- package/dist/tools-logic/pipeline-close.d.ts +22 -0
- package/dist/tools-logic/pipeline-close.d.ts.map +1 -0
- package/dist/tools-logic/pipeline-close.js +66 -0
- package/dist/tools-logic/pipeline-close.js.map +1 -0
- package/dist/tools-logic/pipeline-current.d.ts +11 -0
- package/dist/tools-logic/pipeline-current.d.ts.map +1 -0
- package/dist/tools-logic/pipeline-current.js +8 -0
- package/dist/tools-logic/pipeline-current.js.map +1 -0
- package/dist/tools-logic/pipeline-list.d.ts +11 -0
- package/dist/tools-logic/pipeline-list.d.ts.map +1 -0
- package/dist/tools-logic/pipeline-list.js +8 -0
- package/dist/tools-logic/pipeline-list.js.map +1 -0
- package/dist/tools-logic/pipeline-open.d.ts +23 -0
- package/dist/tools-logic/pipeline-open.d.ts.map +1 -0
- package/dist/tools-logic/pipeline-open.js +109 -0
- package/dist/tools-logic/pipeline-open.js.map +1 -0
- package/dist/tools-logic/pipeline-show.d.ts +34 -0
- package/dist/tools-logic/pipeline-show.d.ts.map +1 -0
- package/dist/tools-logic/pipeline-show.js +134 -0
- package/dist/tools-logic/pipeline-show.js.map +1 -0
- package/dist/tools-logic/session-end-reflect.d.ts +57 -0
- package/dist/tools-logic/session-end-reflect.d.ts.map +1 -0
- package/dist/tools-logic/session-end-reflect.js +117 -0
- package/dist/tools-logic/session-end-reflect.js.map +1 -0
- package/dist/tools-logic/session-end.d.ts +29 -0
- package/dist/tools-logic/session-end.d.ts.map +1 -1
- package/dist/tools-logic/session-end.js +34 -0
- package/dist/tools-logic/session-end.js.map +1 -1
- package/dist/tools-logic/session-start-lite.d.ts +26 -0
- package/dist/tools-logic/session-start-lite.d.ts.map +1 -0
- package/dist/tools-logic/session-start-lite.js +46 -0
- package/dist/tools-logic/session-start-lite.js.map +1 -0
- package/dist/tools-logic/session-start.d.ts +12 -0
- package/dist/tools-logic/session-start.d.ts.map +1 -1
- package/dist/tools-logic/session-start.js +30 -2
- package/dist/tools-logic/session-start.js.map +1 -1
- package/dist/tools-logic/skill-list.d.ts +19 -0
- package/dist/tools-logic/skill-list.d.ts.map +1 -0
- package/dist/tools-logic/skill-list.js +20 -0
- package/dist/tools-logic/skill-list.js.map +1 -0
- package/dist/tools-logic/skill-recall.d.ts +26 -0
- package/dist/tools-logic/skill-recall.d.ts.map +1 -0
- package/dist/tools-logic/skill-recall.js +32 -0
- package/dist/tools-logic/skill-recall.js.map +1 -0
- package/dist/tools-logic/skill-write.d.ts +23 -0
- package/dist/tools-logic/skill-write.d.ts.map +1 -0
- package/dist/tools-logic/skill-write.js +46 -0
- package/dist/tools-logic/skill-write.js.map +1 -0
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -1
- package/package.json +1 -1
|
@@ -16,6 +16,50 @@ export interface CorrectionRecord {
|
|
|
16
16
|
kind?: "correction" | "insight" | "hunch" | "fact";
|
|
17
17
|
weight?: number;
|
|
18
18
|
active?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Outcome KPIs — closes the learning loop.
|
|
21
|
+
* V9 (research vantage 9, 2026-05-30): the only KPI that matters is
|
|
22
|
+
* "does the same bug recur after this correction was retrieved?"
|
|
23
|
+
*/
|
|
24
|
+
retrieved_count?: number;
|
|
25
|
+
heeded_count?: number;
|
|
26
|
+
recurrence_count?: number;
|
|
27
|
+
precision?: number;
|
|
28
|
+
last_retrieved?: string;
|
|
29
|
+
last_outcome?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface CorrectionOutcome {
|
|
32
|
+
correction_id: string;
|
|
33
|
+
project: string;
|
|
34
|
+
/** "heeded" = agent's action honored the warning. "recurred" = same bug happened again. */
|
|
35
|
+
kind: "retrieved" | "heeded" | "recurred";
|
|
36
|
+
/** ISO timestamp */
|
|
37
|
+
at: string;
|
|
38
|
+
/** Free-text evidence — what made you decide. */
|
|
39
|
+
evidence?: string;
|
|
40
|
+
}
|
|
41
|
+
export interface CorrectionKPI {
|
|
42
|
+
project: string;
|
|
43
|
+
total: number;
|
|
44
|
+
active: number;
|
|
45
|
+
retrieved: number;
|
|
46
|
+
heeded: number;
|
|
47
|
+
recurred: number;
|
|
48
|
+
/** Aggregate precision = sum(heeded) / sum(retrieved). NaN if retrieved=0. */
|
|
49
|
+
precision: number;
|
|
50
|
+
/** Insights below 0.3 precision — archive candidates. */
|
|
51
|
+
noise_candidates: Array<{
|
|
52
|
+
id: string;
|
|
53
|
+
rule: string;
|
|
54
|
+
precision: number;
|
|
55
|
+
}>;
|
|
56
|
+
/** Insights above 0.8 precision with ≥3 retrievals — promote candidates. */
|
|
57
|
+
high_signal: Array<{
|
|
58
|
+
id: string;
|
|
59
|
+
rule: string;
|
|
60
|
+
precision: number;
|
|
61
|
+
retrieved: number;
|
|
62
|
+
}>;
|
|
19
63
|
}
|
|
20
64
|
/**
|
|
21
65
|
* Write a correction to persistent storage.
|
|
@@ -35,4 +79,14 @@ export declare function readActiveCorrections(project: string): CorrectionRecord
|
|
|
35
79
|
* Respects active field — archived corrections (active:false) are excluded.
|
|
36
80
|
*/
|
|
37
81
|
export declare function readP0Corrections(project: string): CorrectionRecord[];
|
|
82
|
+
/**
|
|
83
|
+
* Record an outcome event for a correction (retrieved / heeded / recurred).
|
|
84
|
+
* Appends to _outcomes.jsonl and also updates the correction JSON's counters
|
|
85
|
+
* + precision cache. Atomic per-write.
|
|
86
|
+
*/
|
|
87
|
+
export declare function recordOutcome(outcome: CorrectionOutcome): void;
|
|
88
|
+
/**
|
|
89
|
+
* Aggregate KPIs over all corrections for a project — the "is this learning loop working?" view.
|
|
90
|
+
*/
|
|
91
|
+
export declare function getCorrectionKPIs(project: string): CorrectionKPI;
|
|
38
92
|
//# sourceMappingURL=corrections.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"corrections.d.ts","sourceRoot":"","sources":["../../src/storage/corrections.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,IAAI,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,YAAY,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;IACnD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"corrections.d.ts","sourceRoot":"","sources":["../../src/storage/corrections.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,IAAI,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,YAAY,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;IACnD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,2FAA2F;IAC3F,IAAI,EAAE,WAAW,GAAG,QAAQ,GAAG,UAAU,CAAC;IAC1C,oBAAoB;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,iDAAiD;IACjD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,8EAA8E;IAC9E,SAAS,EAAE,MAAM,CAAC;IAClB,yDAAyD;IACzD,gBAAgB,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACzE,4EAA4E;IAC5E,WAAW,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACxF;AA8DD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,gBAAgB,GAAG,IAAI,CAenF;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAqBnE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAEzE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAErE;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI,CAqC9D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,CAkChE"}
|
|
@@ -12,14 +12,22 @@ import { ensureDir } from "./fs-utils.js";
|
|
|
12
12
|
// Internal helpers
|
|
13
13
|
// ---------------------------------------------------------------------------
|
|
14
14
|
function correctionsDir(project) {
|
|
15
|
-
|
|
15
|
+
// Hardened sanitizer — same rule as storage/paths.ts. No dots (prevents ".." escape).
|
|
16
|
+
const safe = (project || "unnamed")
|
|
17
|
+
.replace(/[^a-zA-Z0-9_\-]/g, "-")
|
|
18
|
+
.replace(/^-+|-+$/g, "")
|
|
19
|
+
.slice(0, 100) || "unnamed";
|
|
16
20
|
const root = getRoot();
|
|
17
21
|
const resolved = path.join(root, "projects", safe, "corrections");
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
const rootSep = root.endsWith(path.sep) ? root : root + path.sep;
|
|
23
|
+
if (!resolved.startsWith(rootSep)) {
|
|
24
|
+
throw new Error(`Invalid project (path escape): ${project}`);
|
|
20
25
|
}
|
|
21
26
|
return resolved;
|
|
22
27
|
}
|
|
28
|
+
function outcomesPath(project) {
|
|
29
|
+
return path.join(correctionsDir(project), "_outcomes.jsonl");
|
|
30
|
+
}
|
|
23
31
|
/** Auto-detect severity: p0 if uses strong negation/mandate language, else p1. */
|
|
24
32
|
function detectSeverity(text) {
|
|
25
33
|
const p0Patterns = /\bnever\b|\balways\b|\bdon'?t\b|\bdo not\b|\bmust not\b|\bforbid\b|\bprohibit\b/i;
|
|
@@ -63,7 +71,10 @@ export function writeCorrection(project, correction) {
|
|
|
63
71
|
const record = applyCorrectionDefaults({ ...correction, severity }, todayDate());
|
|
64
72
|
const filename = `${record.date}-${slugify(record.rule || record.id)}.json`;
|
|
65
73
|
const filepath = path.join(dir, filename);
|
|
66
|
-
|
|
74
|
+
// Atomic write — tmp + rename, mode 0600
|
|
75
|
+
const tmp = `${filepath}.tmp.${process.pid}.${Date.now()}`;
|
|
76
|
+
fs.writeFileSync(tmp, JSON.stringify(record, null, 2), { encoding: "utf-8", mode: 0o600 });
|
|
77
|
+
fs.renameSync(tmp, filepath);
|
|
67
78
|
}
|
|
68
79
|
/**
|
|
69
80
|
* Read all corrections for a project, sorted newest first.
|
|
@@ -102,4 +113,82 @@ export function readActiveCorrections(project) {
|
|
|
102
113
|
export function readP0Corrections(project) {
|
|
103
114
|
return readCorrections(project).filter((r) => r.severity === "p0" && r.active !== false);
|
|
104
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* Record an outcome event for a correction (retrieved / heeded / recurred).
|
|
118
|
+
* Appends to _outcomes.jsonl and also updates the correction JSON's counters
|
|
119
|
+
* + precision cache. Atomic per-write.
|
|
120
|
+
*/
|
|
121
|
+
export function recordOutcome(outcome) {
|
|
122
|
+
const dir = correctionsDir(outcome.project);
|
|
123
|
+
ensureDir(dir);
|
|
124
|
+
// Append jsonl event (audit trail).
|
|
125
|
+
const line = JSON.stringify(outcome) + "\n";
|
|
126
|
+
fs.appendFileSync(outcomesPath(outcome.project), line, "utf-8");
|
|
127
|
+
// Update the per-correction file's counters.
|
|
128
|
+
const target = readCorrections(outcome.project).find((r) => r.id === outcome.correction_id);
|
|
129
|
+
if (!target)
|
|
130
|
+
return; // outcome can still be replayed later if record is restored
|
|
131
|
+
const updated = {
|
|
132
|
+
...target,
|
|
133
|
+
retrieved_count: target.retrieved_count ?? 0,
|
|
134
|
+
heeded_count: target.heeded_count ?? 0,
|
|
135
|
+
recurrence_count: target.recurrence_count ?? 0,
|
|
136
|
+
};
|
|
137
|
+
if (outcome.kind === "retrieved") {
|
|
138
|
+
updated.retrieved_count = (updated.retrieved_count ?? 0) + 1;
|
|
139
|
+
updated.last_retrieved = outcome.at;
|
|
140
|
+
}
|
|
141
|
+
else if (outcome.kind === "heeded") {
|
|
142
|
+
updated.heeded_count = (updated.heeded_count ?? 0) + 1;
|
|
143
|
+
updated.last_outcome = outcome.at;
|
|
144
|
+
}
|
|
145
|
+
else if (outcome.kind === "recurred") {
|
|
146
|
+
updated.recurrence_count = (updated.recurrence_count ?? 0) + 1;
|
|
147
|
+
updated.last_outcome = outcome.at;
|
|
148
|
+
}
|
|
149
|
+
const r = updated.retrieved_count ?? 0;
|
|
150
|
+
updated.precision = r > 0 ? Number(((updated.heeded_count ?? 0) / r).toFixed(3)) : undefined;
|
|
151
|
+
// Re-write the JSON file atomically (tmp + rename — prevents truncation on SIGTERM).
|
|
152
|
+
const filename = `${updated.date}-${slugify(updated.rule || updated.id)}.json`;
|
|
153
|
+
const filepath = path.join(dir, filename);
|
|
154
|
+
const tmp = `${filepath}.tmp.${process.pid}.${Date.now()}`;
|
|
155
|
+
fs.writeFileSync(tmp, JSON.stringify(updated, null, 2), { encoding: "utf-8", mode: 0o600 });
|
|
156
|
+
fs.renameSync(tmp, filepath);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Aggregate KPIs over all corrections for a project — the "is this learning loop working?" view.
|
|
160
|
+
*/
|
|
161
|
+
export function getCorrectionKPIs(project) {
|
|
162
|
+
const all = readCorrections(project);
|
|
163
|
+
const active = all.filter((r) => r.active !== false);
|
|
164
|
+
let retrieved = 0;
|
|
165
|
+
let heeded = 0;
|
|
166
|
+
let recurred = 0;
|
|
167
|
+
const noise = [];
|
|
168
|
+
const hot = [];
|
|
169
|
+
for (const r of all) {
|
|
170
|
+
retrieved += r.retrieved_count ?? 0;
|
|
171
|
+
heeded += r.heeded_count ?? 0;
|
|
172
|
+
recurred += r.recurrence_count ?? 0;
|
|
173
|
+
const p = r.precision ?? null;
|
|
174
|
+
const ret = r.retrieved_count ?? 0;
|
|
175
|
+
if (p !== null && ret >= 3 && p < 0.3) {
|
|
176
|
+
noise.push({ id: r.id, rule: r.rule, precision: p });
|
|
177
|
+
}
|
|
178
|
+
if (p !== null && ret >= 3 && p >= 0.8) {
|
|
179
|
+
hot.push({ id: r.id, rule: r.rule, precision: p, retrieved: ret });
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
project,
|
|
184
|
+
total: all.length,
|
|
185
|
+
active: active.length,
|
|
186
|
+
retrieved,
|
|
187
|
+
heeded,
|
|
188
|
+
recurred,
|
|
189
|
+
precision: retrieved > 0 ? Number((heeded / retrieved).toFixed(3)) : NaN,
|
|
190
|
+
noise_candidates: noise,
|
|
191
|
+
high_signal: hot,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
105
194
|
//# sourceMappingURL=corrections.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"corrections.js","sourceRoot":"","sources":["../../src/storage/corrections.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"corrections.js","sourceRoot":"","sources":["../../src/storage/corrections.ts"],"names":[],"mappings":"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"}
|
package/dist/storage/paths.d.ts
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Journal and palace directory path resolution.
|
|
3
|
+
*
|
|
4
|
+
* Security: every project-name sanitizer strips dots (preventing ".." traversal)
|
|
5
|
+
* AND verifies the resolved path stays under root with a trailing-separator check
|
|
6
|
+
* (preventing `~/.agent-recallEVIL` prefix bypass).
|
|
3
7
|
*/
|
|
8
|
+
/**
|
|
9
|
+
* Sanitize a project name for safe use in path.join().
|
|
10
|
+
* Strips ALL non-alphanumeric chars (including dots) to prevent ".." traversal.
|
|
11
|
+
*
|
|
12
|
+
* Exported so other modules (bootstrap, etc.) share the same hardened slug
|
|
13
|
+
* grammar instead of rolling their own. Future-proofing against drift.
|
|
14
|
+
*/
|
|
15
|
+
export declare function sanitizeProject(project: string): string;
|
|
4
16
|
/**
|
|
5
17
|
* Resolve the journal directory for a project.
|
|
6
18
|
* For writes, always use the new location.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/storage/paths.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/storage/paths.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAOvD;AAaD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAMlD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CA6BrD;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAMjD;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAKjE;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOlD;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAMjD;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAExC"}
|
package/dist/storage/paths.js
CHANGED
|
@@ -1,20 +1,48 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Journal and palace directory path resolution.
|
|
3
|
+
*
|
|
4
|
+
* Security: every project-name sanitizer strips dots (preventing ".." traversal)
|
|
5
|
+
* AND verifies the resolved path stays under root with a trailing-separator check
|
|
6
|
+
* (preventing `~/.agent-recallEVIL` prefix bypass).
|
|
3
7
|
*/
|
|
4
8
|
import * as fs from "node:fs";
|
|
5
9
|
import * as path from "node:path";
|
|
6
10
|
import { getRoot, getLegacyRoot } from "../types.js";
|
|
11
|
+
/**
|
|
12
|
+
* Sanitize a project name for safe use in path.join().
|
|
13
|
+
* Strips ALL non-alphanumeric chars (including dots) to prevent ".." traversal.
|
|
14
|
+
*
|
|
15
|
+
* Exported so other modules (bootstrap, etc.) share the same hardened slug
|
|
16
|
+
* grammar instead of rolling their own. Future-proofing against drift.
|
|
17
|
+
*/
|
|
18
|
+
export function sanitizeProject(project) {
|
|
19
|
+
if (!project)
|
|
20
|
+
return "unnamed";
|
|
21
|
+
const safe = project
|
|
22
|
+
.replace(/[^a-zA-Z0-9_\-]/g, "-")
|
|
23
|
+
.replace(/^-+|-+$/g, "")
|
|
24
|
+
.slice(0, 100);
|
|
25
|
+
return safe || "unnamed";
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Check that `resolved` is strictly inside `root` (rejects prefix matches like
|
|
29
|
+
* "/foo/bar" being inside "/foo/ba"). Throws if not.
|
|
30
|
+
*/
|
|
31
|
+
function assertInsideRoot(resolved, root, project) {
|
|
32
|
+
const rootWithSep = root.endsWith(path.sep) ? root : root + path.sep;
|
|
33
|
+
if (!resolved.startsWith(rootWithSep) && resolved !== root) {
|
|
34
|
+
throw new Error(`Invalid project name (path escape): ${project}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
7
37
|
/**
|
|
8
38
|
* Resolve the journal directory for a project.
|
|
9
39
|
* For writes, always use the new location.
|
|
10
40
|
*/
|
|
11
41
|
export function journalDir(project) {
|
|
12
|
-
const safe = project
|
|
42
|
+
const safe = sanitizeProject(project);
|
|
13
43
|
const root = getRoot();
|
|
14
44
|
const resolved = path.join(root, "projects", safe, "journal");
|
|
15
|
-
|
|
16
|
-
throw new Error(`Invalid project name: ${project}`);
|
|
17
|
-
}
|
|
45
|
+
assertInsideRoot(resolved, root, project);
|
|
18
46
|
return resolved;
|
|
19
47
|
}
|
|
20
48
|
/**
|
|
@@ -49,12 +77,10 @@ export function journalDirs(project) {
|
|
|
49
77
|
* Resolve the palace directory for a project.
|
|
50
78
|
*/
|
|
51
79
|
export function palaceDir(project) {
|
|
52
|
-
const safe = project
|
|
80
|
+
const safe = sanitizeProject(project);
|
|
53
81
|
const root = getRoot();
|
|
54
82
|
const resolved = path.join(root, "projects", safe, "palace");
|
|
55
|
-
|
|
56
|
-
throw new Error(`Invalid project name: ${project}`);
|
|
57
|
-
}
|
|
83
|
+
assertInsideRoot(resolved, root, project);
|
|
58
84
|
return resolved;
|
|
59
85
|
}
|
|
60
86
|
/**
|
|
@@ -63,9 +89,7 @@ export function palaceDir(project) {
|
|
|
63
89
|
export function roomDir(project, roomSlug) {
|
|
64
90
|
const safeSlug = roomSlug.replace(/[^a-zA-Z0-9_\-]/g, "-");
|
|
65
91
|
const resolved = path.join(palaceDir(project), "rooms", safeSlug);
|
|
66
|
-
|
|
67
|
-
throw new Error(`Invalid room slug: ${roomSlug}`);
|
|
68
|
-
}
|
|
92
|
+
assertInsideRoot(resolved, getRoot(), `${project}/${roomSlug}`);
|
|
69
93
|
return resolved;
|
|
70
94
|
}
|
|
71
95
|
/**
|
|
@@ -86,12 +110,10 @@ export function sanitizeSlug(input) {
|
|
|
86
110
|
* Resolve the digest directory for a project.
|
|
87
111
|
*/
|
|
88
112
|
export function digestDir(project) {
|
|
89
|
-
const safe = project
|
|
113
|
+
const safe = sanitizeProject(project);
|
|
90
114
|
const root = getRoot();
|
|
91
115
|
const resolved = path.join(root, "projects", safe, "digest");
|
|
92
|
-
|
|
93
|
-
throw new Error(`Invalid project name: ${project}`);
|
|
94
|
-
}
|
|
116
|
+
assertInsideRoot(resolved, root, project);
|
|
95
117
|
return resolved;
|
|
96
118
|
}
|
|
97
119
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/storage/paths.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/storage/paths.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/storage/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAMH,OAAO,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAQjF,0CAA0C;AAC1C,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED,wCAAwC;AACxC,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,WAAW,GAAG,UAAU,GAAG,iBAAiB,GAAG,SAAS,CAAC;AAE3F,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,eAAe,CAAC;IACtB,KAAK,CAAC,EAAE,QAAQ,CAAC;CAClB;AAED,YAAY,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAUjF;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,aAAa,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/storage/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAMH,OAAO,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAQjF,0CAA0C;AAC1C,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED,wCAAwC;AACxC,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,WAAW,GAAG,UAAU,GAAG,iBAAiB,GAAG,SAAS,CAAC;AAE3F,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,eAAe,CAAC;IACtB,KAAK,CAAC,EAAE,QAAQ,CAAC;CAClB;AAED,YAAY,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAUjF;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,aAAa,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CA+D7G;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,aAAa,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAoBhH;AAED,+DAA+D;AAC/D,wBAAgB,eAAe,IAAI,IAAI,CAEtC;AAED,gFAAgF;AAChF,wBAAgB,iBAAiB,IAAI,IAAI,CAExC"}
|
package/dist/storage/session.js
CHANGED
|
@@ -43,7 +43,22 @@ function topicSlug(content) {
|
|
|
43
43
|
export function journalFileName(date, baseExists, opts, dir) {
|
|
44
44
|
// New intelligent naming
|
|
45
45
|
if (opts?.saveType && opts?.content) {
|
|
46
|
-
//
|
|
46
|
+
// SESSION-SCOPED EXCEPTION (arsaveall):
|
|
47
|
+
// When multiple parallel sessions all touch the same project today,
|
|
48
|
+
// the SAME-DAY rule would collapse them into one file and silently
|
|
49
|
+
// drop content. For arsaveall specifically we bypass it and generate
|
|
50
|
+
// a unique per-call session-scoped filename so every session survives.
|
|
51
|
+
if (opts.saveType === "arsaveall") {
|
|
52
|
+
const slug = topicSlug(opts.content);
|
|
53
|
+
const sigTag = opts.sig ?? "none";
|
|
54
|
+
const themeTag = opts.theme ?? "none";
|
|
55
|
+
const uniq = crypto.randomBytes(3).toString("hex"); // per-call random — unique across iterations
|
|
56
|
+
const name = `${date}--arsaveall--${sigTag}--${themeTag}--${slug}--${uniq}.md`;
|
|
57
|
+
if (dir)
|
|
58
|
+
ownedFiles.add(`smart:${name}`);
|
|
59
|
+
return name;
|
|
60
|
+
}
|
|
61
|
+
// SAME-DAY RULE: one file per day per project (other saveTypes only).
|
|
47
62
|
// If ANY file for today already exists (smart or legacy), append to it.
|
|
48
63
|
if (dir) {
|
|
49
64
|
const existingToday = fs.readdirSync(dir)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/storage/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAE9B,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAGvD,mEAAmE;AACnE,MAAM,UAAU,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAEzD,yDAAyD;AACzD,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;AAErC,0CAA0C;AAC1C,MAAM,UAAU,YAAY;IAC1B,OAAO,UAAU,CAAC;AACpB,CAAC;AAcD;;GAEG;AACH,SAAS,SAAS,CAAC,OAAe;IAChC,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;IACrC,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAClC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,UAAmB,EAAE,IAAoB,EAAE,GAAY;IACnG,yBAAyB;IACzB,IAAI,IAAI,EAAE,QAAQ,IAAI,IAAI,EAAE,OAAO,EAAE,CAAC;QACpC
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/storage/session.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../../src/tools-logic/bootstrap.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../../src/tools-logic/bootstrap.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAiEH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,KAAK,CAAC;QACb,IAAI,EAAE,KAAK,GAAG,eAAe,GAAG,UAAU,GAAG,cAAc,CAAC;QAC5D,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC,CAAC;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,OAAO,CAAC;IACvB,gBAAgB,EAAE,cAAc,EAAE,CAAC;CACpC;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,UAAU,GAAG,QAAQ,GAAG,cAAc,GAAG,YAAY,CAAC;IAC5D,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,YAAY,EAAE,cAAc,EAAE,CAAC;IAC/B,KAAK,EAAE;QACL,cAAc,EAAE,MAAM,CAAC;QACvB,sBAAsB,EAAE,MAAM,CAAC;QAC/B,mBAAmB,EAAE,MAAM,CAAC;QAC5B,gBAAgB,EAAE,MAAM,CAAC;KAC1B,CAAC;CACH;AAED,MAAM,WAAW,eAAe;IAC9B,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC3B,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChE,WAAW,EAAE,MAAM,CAAC;CACrB;AAqPD,wBAAsB,aAAa,CAAC,OAAO,CAAC,EAAE;IAC5C,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA2Q/B;AAMD,wBAAsB,eAAe,CACnC,IAAI,EAAE,mBAAmB,EACzB,SAAS,CAAC,EAAE,eAAe,GAC1B,OAAO,CAAC,YAAY,CAAC,CAmRvB"}
|
|
@@ -18,9 +18,54 @@ import { todayISO } from "../storage/fs-utils.js";
|
|
|
18
18
|
import { palaceWrite } from "./palace-write.js";
|
|
19
19
|
import { journalWrite } from "./journal-write.js";
|
|
20
20
|
import { awarenessUpdate } from "./awareness-update.js";
|
|
21
|
-
import { palaceDir } from "../storage/paths.js";
|
|
21
|
+
import { palaceDir, sanitizeProject } from "../storage/paths.js";
|
|
22
22
|
const execFileAsync = promisify(execFile);
|
|
23
23
|
// ---------------------------------------------------------------------------
|
|
24
|
+
// Denylist — parent / system / cache directories that should NEVER be imported
|
|
25
|
+
// as projects. AutoMemory encoded paths like "-Users-tongwu-Downloads" decode
|
|
26
|
+
// to basename "Downloads", which without filtering becomes a phantom project.
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
const SYSTEM_DIR_DENYLIST = new Set([
|
|
29
|
+
// Standard macOS home directories
|
|
30
|
+
"Downloads", "Documents", "Desktop", "Library", "Applications",
|
|
31
|
+
"Movies", "Music", "Pictures", "Public", "Sites",
|
|
32
|
+
// Common code-root containers (containers, not actual projects)
|
|
33
|
+
"Projects", "Code", "Codebases", "Repos", "GitHub", "Repositories",
|
|
34
|
+
"Source", "Sources", "src", "work", "Work", "dev", "Dev",
|
|
35
|
+
// Build / vendor / cache directories
|
|
36
|
+
"node_modules", "dist", "build", ".next", ".turbo", ".cache",
|
|
37
|
+
"tmp", "temp", ".tmp",
|
|
38
|
+
// Tool / IDE / agent directories
|
|
39
|
+
"claude", "Claude", ".cursor", ".vscode", ".idea", ".git",
|
|
40
|
+
]);
|
|
41
|
+
function isSystemDir(name) {
|
|
42
|
+
if (!name)
|
|
43
|
+
return true;
|
|
44
|
+
if (name.length < 2)
|
|
45
|
+
return true;
|
|
46
|
+
if (name.startsWith("."))
|
|
47
|
+
return true; // dotfiles / hidden
|
|
48
|
+
if (name.includes("paperclip-instances"))
|
|
49
|
+
return true; // UUID-suffixed pattern
|
|
50
|
+
if (/^[0-9a-f-]{20,}$/i.test(name))
|
|
51
|
+
return true; // bare UUIDs
|
|
52
|
+
return SYSTEM_DIR_DENYLIST.has(name);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Strip prompt-injection patterns from imported user content before writing it
|
|
56
|
+
* to palace. CLAUDE.md and AutoMemory files frequently contain
|
|
57
|
+
* `<system-reminder>` markers etc. that, once in palace, would surface to future
|
|
58
|
+
* agents as if they were system instructions.
|
|
59
|
+
*/
|
|
60
|
+
function scrubPromptInjection(s) {
|
|
61
|
+
return s
|
|
62
|
+
.replace(/<\/?\s*(system[-_]?(reminder|prompt|message|instruction)|important|critical)\b[^>]*>/gi, "[stripped tag]")
|
|
63
|
+
.replace(/<\|im_(start|end)\|>/gi, "[stripped]")
|
|
64
|
+
.replace(/\b(ignore|disregard|forget)\s+(all\s+)?(previous|prior|above)\s+(instructions?|prompts?|messages?)/gi, "[stripped injection attempt]")
|
|
65
|
+
.replace(/[--]/g, "") // bidi override
|
|
66
|
+
.replace(/\0/g, "");
|
|
67
|
+
}
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
24
69
|
// Constants
|
|
25
70
|
// ---------------------------------------------------------------------------
|
|
26
71
|
const DEFAULT_SCAN_DIRS = [
|
|
@@ -289,7 +334,14 @@ export async function bootstrapScan(options) {
|
|
|
289
334
|
const pkgInfo = readPackageInfo(repoDir);
|
|
290
335
|
const remoteName = remoteUrl ? path.basename(remoteUrl, ".git") : "";
|
|
291
336
|
const name = pkgInfo.name ?? (remoteName || path.basename(repoDir));
|
|
292
|
-
|
|
337
|
+
// Skip if this resolved to a system / container directory.
|
|
338
|
+
// Throw a sentinel — Promise.allSettled will reject this one;
|
|
339
|
+
// downstream `.filter(r => r.status === "fulfilled")` drops it.
|
|
340
|
+
if (isSystemDir(name)) {
|
|
341
|
+
throw new Error(`SKIP_SYSTEM_DIR:${name}`);
|
|
342
|
+
}
|
|
343
|
+
// Hardened slug — same grammar as paths.ts (no dots, no traversal).
|
|
344
|
+
const slug = sanitizeProject(toSlug(name));
|
|
293
345
|
const language = detectLanguage(repoDir);
|
|
294
346
|
const lastActivity = lastCommitIso || undefined;
|
|
295
347
|
const description = pkgInfo.description ?? readReadmeDescription(repoDir);
|
|
@@ -395,7 +447,12 @@ export async function bootstrapScan(options) {
|
|
|
395
447
|
// Decode encoded path: "-Users-tongwu-Projects-myapp" → last segment
|
|
396
448
|
const decoded = encodedName.replace(/^-/, "").replace(/-/g, "/");
|
|
397
449
|
const projectName = path.basename(decoded) || encodedName;
|
|
398
|
-
|
|
450
|
+
// SKIP system/parent/cache dirs — AutoMemory may have indexed
|
|
451
|
+
// things like ~/Downloads or ~/Projects; those aren't real projects.
|
|
452
|
+
if (isSystemDir(projectName))
|
|
453
|
+
continue;
|
|
454
|
+
// Hardened slug — matches paths.ts sanitizer (no dots, no path chars).
|
|
455
|
+
const slug = sanitizeProject(toSlug(projectName));
|
|
399
456
|
// List .md files in memory/ (skip > 5KB, skip secrets)
|
|
400
457
|
let mdFiles = [];
|
|
401
458
|
try {
|
|
@@ -624,7 +681,10 @@ export async function bootstrapImport(scan, selection) {
|
|
|
624
681
|
continue;
|
|
625
682
|
}
|
|
626
683
|
const raw = fs.readFileSync(item.source_path, "utf-8");
|
|
627
|
-
|
|
684
|
+
// Scrub prompt-injection patterns before importing user-controlled
|
|
685
|
+
// content into palace. CLAUDE.md often contains <system-reminder>
|
|
686
|
+
// tags etc. that would surface to future agents as if real.
|
|
687
|
+
const claudemdContent = scrubPromptInjection(raw.slice(0, 3000));
|
|
628
688
|
await palaceWrite({
|
|
629
689
|
room: "architecture",
|
|
630
690
|
topic: "project-conventions",
|
|
@@ -649,7 +709,9 @@ export async function bootstrapImport(scan, selection) {
|
|
|
649
709
|
continue;
|
|
650
710
|
}
|
|
651
711
|
const rawContent = fs.readFileSync(item.source_path, "utf-8");
|
|
652
|
-
const { body, meta } = stripFrontmatter(rawContent);
|
|
712
|
+
const { body: rawBody, meta } = stripFrontmatter(rawContent);
|
|
713
|
+
// Scrub prompt-injection patterns before storing imported content.
|
|
714
|
+
const body = scrubPromptInjection(rawBody);
|
|
653
715
|
const rawTopic = (meta["name"] ?? item.id.replace("claude-memory:", "")).replace(/\.md$/, "");
|
|
654
716
|
const topic = rawTopic.replace(/[^a-zA-Z0-9_\-]/g, "-");
|
|
655
717
|
if (meta["type"] === "user") {
|
|
@@ -727,7 +789,8 @@ export async function bootstrapImport(scan, selection) {
|
|
|
727
789
|
continue;
|
|
728
790
|
}
|
|
729
791
|
const rawContent = fs.readFileSync(item.source_path, "utf-8");
|
|
730
|
-
const { body, meta } = stripFrontmatter(rawContent);
|
|
792
|
+
const { body: rawBody, meta } = stripFrontmatter(rawContent);
|
|
793
|
+
const body = scrubPromptInjection(rawBody);
|
|
731
794
|
const rawTopic2 = (meta["name"] ?? item.id.replace("global:", "")).replace(/\.md$/, "");
|
|
732
795
|
const topic = rawTopic2.replace(/[^a-zA-Z0-9_\-]/g, "-");
|
|
733
796
|
if (meta["type"] === "user") {
|