import type { ExtractedProjectMemory } from '../types.js'; /** * The names of the three checks `assertSafeMemory` can fail on, written once. * * The name of the check that fired rides the refusal message, so the reader is * told WHICH rule stopped the write instead of only that something was * refused. Callers route their remedy on these same constants (see * `memoryExtractNextActions` in `src/cli/commands/core/memory-command.ts`) — * two strings kept equal by hand is how a message and its advice drift apart. */ export declare const SENSITIVE_MEMORY_CHECKS: { readonly metadataKey: "metadata key scan"; readonly content: "content scan"; readonly title: "title scan"; }; /** * The credential term this title contains (as a run of words), or `null`. * * Returns the MATCHED TERM rather than a boolean because the refusal message * names it: `title scan matched the credential term "apikey"` is actionable, * while "Refusing to store sensitive memory content" is what sent slice C0's * user looking for a secret that was not there. The returned string is a * dictionary word from the list above — never the title's own text, and never * a value — so echoing it into the error envelope cannot leak anything. */ export declare function findSensitiveMemoryTitleTerm(title: string): string | null; /** * Which content rule refused this text — the id, never the match. A rule whose * candidate fails `shaped` does not stop the scan: a later rule may still fire, * exactly as a later alternative in the old `||` chain would have. */ export declare function findSensitiveMemoryContentRule(content: string): string | null; export declare function hasSensitiveMemoryContent(content: string): boolean; /** * The refusal `assertSafeMemory` raises: the check that fired and the term it * matched travel as DATA, not only as prose. * * WHY A TYPE RATHER THAN A MESSAGE. The envelope redactor behind every * `fail()` (`packages/peaks-loop-shared/src/result.ts`) strips the words * secret / token / password / api-key out of every failure MESSAGE — a blanket * rule, applied to messages this module does not own. So a message that names * the matched term reaches the CLI reading `… the credential term * "[redacted]" …`: the term is named, and then removed, on the way out. * * The term is a word from `SENSITIVE_PROSE_TERMS` — a fixed vocabulary, never a * value — so there is nothing about it to redact, and it rides a field * instead: `fail()` redacts `message` only. The CLI routes its remedy on * `check` for the same reason — a substring test against a message whose words * can be rewritten is not a routing rule, it is a guess. * * NOTHING HERE WEAKENS THE REDACTOR. Both scans put a NAME in this field, never * a match: the title term comes from `SENSITIVE_PROSE_TERMS` and the content * value from the closed id list in `SENSITIVE_CONTENT_RULES`, so no credential * can reach an envelope through it. The redactor's guarantee over `message` is * intact and is pinned by the cases in * `tests/unit/services/memory/memory-title-sensitive-scan.test.ts`. */ export declare class UnsafeMemoryError extends Error { /** One of `SENSITIVE_MEMORY_CHECKS` — which rule refused the write. */ readonly check: string; /** * WHAT matched, as a name rather than a value: the credential term for the * title scan, the rule id for the content scan, `null` only for the metadata * key scan (whose predicate answers by a config-key vocabulary the memory * metadata never contains — see the note on `assertSafeMemory`). * * It is never the match itself: for most content patterns the match IS the * credential (`ghp_…`, the PEM header, the JWT), which is why their messages * describe the pattern family and never echo the text. */ readonly matchedTerm: string | null; constructor(check: string, detail: string, matchedTerm?: string | null); } export declare function assertSafeMemory(memory: ExtractedProjectMemory): void; export declare function assertSafeMemoryFileContent(content: string): void; export declare function writeNewFile(path: string, content: string): void;