@vaur94/agz-memory 0.4.1 → 0.5.1

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.
Files changed (45) hide show
  1. package/ARCHITECTURE.md +41 -25
  2. package/CHANGELOG.md +76 -0
  3. package/README.md +42 -16
  4. package/README.tr.md +43 -17
  5. package/dist/admin.js +4226 -758
  6. package/dist/core.js +5208 -1023
  7. package/dist/server.js +4888 -905
  8. package/dist/types/admin/quarantine.d.ts +15 -0
  9. package/dist/types/admin/reindex.d.ts +41 -0
  10. package/dist/types/capture/contract.d.ts +7 -5
  11. package/dist/types/capture/redact.d.ts +1 -0
  12. package/dist/types/config.d.ts +1 -0
  13. package/dist/types/context.d.ts +1 -1
  14. package/dist/types/contracts/error.d.ts +16 -0
  15. package/dist/types/contracts/limits.d.ts +18 -0
  16. package/dist/types/contracts/mutation.d.ts +31 -0
  17. package/dist/types/contracts/pagination.d.ts +27 -0
  18. package/dist/types/core.d.ts +6 -2
  19. package/dist/types/db/backup.d.ts +2 -1
  20. package/dist/types/db/health.d.ts +5 -1
  21. package/dist/types/db/legacy-health.d.ts +4 -0
  22. package/dist/types/db/maintenance.d.ts +21 -0
  23. package/dist/types/db/migrations/v011.d.ts +3 -0
  24. package/dist/types/db/schema.d.ts +10 -1
  25. package/dist/types/db.d.ts +12 -1
  26. package/dist/types/hash.d.ts +4 -0
  27. package/dist/types/retrieval/contract.d.ts +15 -3
  28. package/dist/types/security/quarantine-key.d.ts +47 -0
  29. package/dist/types/server.d.ts +1 -1
  30. package/dist/types/store/capture.d.ts +17 -5
  31. package/dist/types/store/outbox.d.ts +15 -3
  32. package/dist/types/store/retrieval.d.ts +2 -2
  33. package/dist/types/store.d.ts +39 -12
  34. package/dist/types/types.d.ts +1 -1
  35. package/dist/types/version.d.ts +1 -1
  36. package/docs/adr/hash-identity-v2.md +57 -0
  37. package/docs/adr/maintenance-lock.md +93 -0
  38. package/docs/backup-restore-runbook.md +36 -16
  39. package/docs/backup-restore-runbook.tr.md +36 -16
  40. package/docs/repository-hardening.md +125 -0
  41. package/docs/review-resolution.md +78 -0
  42. package/docs/schema-v11.md +75 -0
  43. package/package.json +21 -6
  44. package/skills/agz-memory/agz-memory.md +39 -0
  45. package/skills/index.json +9 -0
@@ -0,0 +1,15 @@
1
+ import type { Database } from "bun:sqlite";
2
+ /** Content-free input for a future V12 quarantine migration decision. */
3
+ export declare function quarantinePrivacyReport(db: Database): {
4
+ quarantinedEvents: number;
5
+ keyedEvents: number;
6
+ unavailableKeyEvents: number;
7
+ legacyOrUnknownEvents: number;
8
+ keyIDs: string[];
9
+ digest: {
10
+ algorithm: "HMAC-SHA256";
11
+ input: "quarantine-source-identity-and-redacted-payload/2";
12
+ storage: "capture_events.payload_hash";
13
+ keyID: "capture_events.redaction_version quarantine-key suffix";
14
+ };
15
+ };
@@ -0,0 +1,41 @@
1
+ export interface ProcessStartMarkerReaders {
2
+ readLinuxStat?: (pid: number) => string | null;
3
+ readMacOSPs?: (pid: number) => string | null;
4
+ }
5
+ export interface ReindexTestOptions {
6
+ /** Test-only fault point after a durable batch commit but before its cursor is saved. */
7
+ afterCommitBeforeStateWrite?: () => void;
8
+ /** Test-only fault point immediately before the batch's database identity verification. */
9
+ beforeBatchDatabaseIdentity?: () => void;
10
+ /** Test-only synchronization point after a stale lock is classified but before takeover. */
11
+ beforeStaleLockTakeover?: () => void;
12
+ /** Test-only synchronization point while the owner lock is held. */
13
+ afterOwnerLockAcquired?: () => void;
14
+ }
15
+ export interface ReindexOwnerMetadata {
16
+ ownerID: string;
17
+ pid: number;
18
+ processStart: string;
19
+ hostname: string;
20
+ createdAt: number;
21
+ }
22
+ export declare function classifyReindexOwner(owner: unknown, localHostname: string, processAlive: boolean, currentProcessStart: string | null): "live" | "stale" | "unverifiable";
23
+ export declare function runResumableReindex(path: string, databaseID: string, backend: string, batchSize: number, maxBatches?: number, testOptions?: ReindexTestOptions): {
24
+ backend: string;
25
+ generation: number;
26
+ purges: number;
27
+ queued: number;
28
+ quarantined: number;
29
+ resumed: boolean;
30
+ incomplete?: undefined;
31
+ } | {
32
+ backend: string;
33
+ generation: number;
34
+ purges: number;
35
+ queued: number;
36
+ quarantined: number;
37
+ incomplete: boolean;
38
+ resumed: boolean;
39
+ };
40
+ export declare function readReindexProcessStartMarker(pid: number, platform?: NodeJS.Platform, readers?: ProcessStartMarkerReaders): string | null;
41
+ export declare function assertReindexOwnerLivenessSupported(platform?: NodeJS.Platform): void;
@@ -1,5 +1,5 @@
1
1
  import * as z from "zod/v4";
2
- export declare const CAPTURE_SCHEMA: "agz-memory.capture/1";
2
+ export declare const CAPTURE_SCHEMA: "agz-memory.capture/2";
3
3
  export declare const SUPPORTED_OPENCODE_VERSION: "0.0.0-beta-18743";
4
4
  export declare const CAPTURE_EVENT_MAX_BYTES: number;
5
5
  export declare const CAPTURE_CONTENT_MAX_CHARACTERS = 4800;
@@ -34,7 +34,7 @@ declare const candidateSchema: z.ZodObject<{
34
34
  }>;
35
35
  }, z.core.$strict>;
36
36
  export declare const captureEventSchema: z.ZodObject<{
37
- schema: z.ZodLiteral<"agz-memory.capture/1">;
37
+ schema: z.ZodLiteral<"agz-memory.capture/2">;
38
38
  idempotencyKey: z.ZodString;
39
39
  projectID: z.ZodUUID;
40
40
  bindingKey: z.ZodString;
@@ -96,7 +96,9 @@ export declare const captureEventSchema: z.ZodObject<{
96
96
  truncated: z.ZodBoolean;
97
97
  }, z.core.$strict>;
98
98
  }, z.core.$strict>;
99
- export type MemoryCandidateV1 = z.infer<typeof candidateSchema>;
100
- export type CaptureEventV1 = z.infer<typeof captureEventSchema>;
101
- export declare function parseCaptureEvent(value: unknown): CaptureEventV1;
99
+ export type MemoryCandidateV2 = z.infer<typeof candidateSchema>;
100
+ export type CaptureEventV2 = z.infer<typeof captureEventSchema>;
101
+ export type MemoryCandidateV1 = MemoryCandidateV2;
102
+ export type CaptureEventV1 = CaptureEventV2;
103
+ export declare function parseCaptureEvent(value: unknown): CaptureEventV2;
102
104
  export {};
@@ -9,4 +9,5 @@ export interface RedactionResult {
9
9
  export declare function redactText(value: string, options?: {
10
10
  maxCharacters?: number;
11
11
  denylist?: readonly string[];
12
+ sourceTruncated?: boolean;
12
13
  }): RedactionResult;
@@ -1,4 +1,5 @@
1
1
  export interface MemoryConfig {
2
2
  databasePath: string;
3
+ quarantineKeyringPath: string;
3
4
  }
4
5
  export declare function resolveConfig(environment?: NodeJS.ProcessEnv): MemoryConfig;
@@ -1 +1 @@
1
- export declare const MEMORY_GUIDANCE = "Use project-scoped memory for durable facts across sessions.\n- Start with project_list. Use the immutable projectID for stable references; projectName is a convenient unique lookup.\n- Create a project with project_create before storing its first note. Renaming a project never changes its ID.\n- Every memory_recall, memory_read, memory_update, memory_link, and memory_pin call must select exactly one project by projectID or projectName.\n- No notes are injected automatically; use memory_recall for relevant project history.\n- Read indexed note bodies and graph neighbors with memory_read.\n- Store only durable verified facts, decisions, procedures, research, preferences, or substantial completed work.\n- Use memory_pin to prioritize important matching notes inside their project. Pinning never moves notes between projects.\n- Never save transcripts, guesses, secrets, or routine progress.\n- project_delete permanently destroys the project and all of its memory. Call project_list first and provide the immutable ID, exact current name, and required confirmation phrase only when deletion is explicitly intended.";
1
+ export declare const MEMORY_GUIDANCE = "Use project-scoped memory for durable facts across sessions.\n- Start with project_list. Reuse a project only when it intentionally represents the same durable workspace or product; create one only when no matching project exists. If the listed projects are ambiguous, ask rather than guessing from a directory or session name.\n- Prefer the immutable projectID for stable references. projectName is a convenient unique lookup, but names can change.\n- Every memory_recall, memory_read, memory_update, memory_link, and memory_pin call must select exactly one project by projectID or projectName.\n- The MCP server does not inject notes automatically. Recall relevant history before relying on prior decisions, and use memory_read for full indexed content and graph neighbors.\n- After substantial completed work, update an existing note or create a new one only for durable verified facts, decisions, procedures, research, preferences, tasks, or context.\n- Never save transcripts, guesses, secrets, credentials, hidden reasoning, or routine progress.\n- Use memory_pin only to prioritize important matching notes. Read directed links as sourceID PREDICATE targetID; links never cross projects.\n- Inspect every result from a batch because mutations are ordered and non-atomic: earlier items remain applied when a later item fails.\n- memory_update with delete:true permanently deletes one note. project_delete permanently deletes a project and all owned memory. Verify current IDs first and use destructive operations only when explicitly intended.";
@@ -0,0 +1,16 @@
1
+ export type BusinessErrorCode = "invalid_request" | "not_found" | "conflict" | "limit_exceeded" | "invalid_cursor" | "cursor_scope_mismatch" | "stale_cursor" | "internal_error";
2
+ export declare class MemoryBusinessError extends Error {
3
+ readonly code: BusinessErrorCode;
4
+ readonly correlationID: string;
5
+ readonly cause?: unknown;
6
+ constructor(code: BusinessErrorCode, message: string, correlationID?: string, cause?: unknown);
7
+ }
8
+ export declare function correlationID(): string;
9
+ export declare function businessError(code: BusinessErrorCode, message: string, id?: string, cause?: unknown): MemoryBusinessError;
10
+ export declare function toPublicError(error: MemoryBusinessError): {
11
+ code: BusinessErrorCode;
12
+ correlationID: string;
13
+ retryable: boolean;
14
+ message: string;
15
+ };
16
+ export declare function asBusinessError(error: unknown, id?: string): MemoryBusinessError;
@@ -0,0 +1,18 @@
1
+ import * as z from "zod/v4";
2
+ /** Public payload limits, measured in UTF-8 bytes unless otherwise noted. */
3
+ export declare const LIMITS: {
4
+ readonly title: 240;
5
+ readonly summary: 4096;
6
+ readonly content: 65536;
7
+ readonly query: 4096;
8
+ readonly noteID: 256;
9
+ readonly batch: 10;
10
+ readonly requestBytes: 1048576;
11
+ readonly responseBytes: 1048576;
12
+ readonly pageSize: 100;
13
+ };
14
+ export type TextLimit = "title" | "summary" | "content" | "query" | "noteID";
15
+ export declare function utf8Bytes(value: string): number;
16
+ export declare function assertTextLimit(field: TextLimit, value: string): void;
17
+ export declare function boundedText(field: TextLimit, description: string): z.ZodString;
18
+ export declare function assertRequestLimit(value: unknown): void;
@@ -0,0 +1,31 @@
1
+ import { type Kind } from "../types";
2
+ export interface CreateMutation {
3
+ operation: "create";
4
+ kind: Kind;
5
+ title: string;
6
+ summary: string;
7
+ content?: string;
8
+ }
9
+ export interface PatchMutation {
10
+ operation: "patch";
11
+ id: string;
12
+ changes: Partial<Pick<CreateMutation, "kind" | "title" | "summary" | "content">>;
13
+ }
14
+ export interface DeleteMutation {
15
+ operation: "delete";
16
+ id: string;
17
+ confirmation?: string;
18
+ }
19
+ export type MutationOperation = CreateMutation | PatchMutation | DeleteMutation;
20
+ export interface LegacyMutation {
21
+ id?: string;
22
+ kind?: string;
23
+ title?: string;
24
+ summary?: string;
25
+ content?: string;
26
+ delete?: boolean;
27
+ confirmation?: string;
28
+ }
29
+ export declare function normalizeLegacyMutation(input: LegacyMutation): MutationOperation;
30
+ export declare function isMutationOperation(value: unknown): value is MutationOperation;
31
+ export declare function assertStrictMutationOperation(value: unknown): asserts value is MutationOperation;
@@ -0,0 +1,27 @@
1
+ export interface CursorScope {
2
+ projectID: string;
3
+ query: string;
4
+ snapshot: string;
5
+ }
6
+ interface CursorPayload extends CursorScope {
7
+ v: 1;
8
+ offset: number;
9
+ }
10
+ export interface PageOptions extends CursorScope {
11
+ limit: number;
12
+ cursor?: string;
13
+ /** Snapshot returned by the client on a follow-up request. */
14
+ requestedSnapshot?: string;
15
+ }
16
+ export interface Page<T> {
17
+ items: T[];
18
+ snapshot: string;
19
+ etag: string;
20
+ nextCursor?: string;
21
+ }
22
+ export declare function encodeCursor(payload: Omit<CursorPayload, "v">): string;
23
+ export declare function decodeCursor(cursor: string, scope: CursorScope): {
24
+ offset: number;
25
+ };
26
+ export declare function paginate<T>(items: readonly T[], options: PageOptions): Page<T>;
27
+ export {};
@@ -1,4 +1,4 @@
1
- import type { RetrievalBackend } from "./retrieval/contract";
1
+ import type { OutboxBackend, RetrievalBackend } from "./retrieval/contract";
2
2
  import { type OpenedDB } from "./db";
3
3
  import { CaptureStore } from "./store/capture";
4
4
  import { MemoryStore } from "./store";
@@ -7,7 +7,7 @@ import { RetrievalStore } from "./store/retrieval";
7
7
  export interface MemoryCoreOptions {
8
8
  indexBackends?: readonly string[];
9
9
  retrievalBackend?: RetrievalBackend;
10
- outboxBackends?: ReadonlyMap<string, RetrievalBackend>;
10
+ outboxBackends?: ReadonlyMap<string, OutboxBackend>;
11
11
  }
12
12
  export declare class MemoryCore {
13
13
  private opened;
@@ -27,5 +27,9 @@ export * from "./capture/redact";
27
27
  export * from "./retrieval/contract";
28
28
  export * from "./retrieval/formatter";
29
29
  export * from "./store/capture";
30
+ export * from "./contracts/limits";
31
+ export * from "./contracts/mutation";
32
+ export * from "./contracts/error";
33
+ export * from "./contracts/pagination";
30
34
  export * from "./types";
31
35
  export * from "./version";
@@ -1,4 +1,5 @@
1
1
  import { Database } from "bun:sqlite";
2
+ import { type MaintenanceGate } from "./maintenance";
2
3
  export declare const BACKUP_FORMAT: "agz-memory-backup/1";
3
4
  export interface BackupManifest {
4
5
  format: typeof BACKUP_FORMAT;
@@ -21,5 +22,5 @@ export interface VerifiedBackup {
21
22
  }
22
23
  export declare function createVerifiedBackup(db: Database, databasePath: string, sourceSchema: number, targetSchema: number, productVersion: string): VerifiedBackup;
23
24
  export declare function verifyBackupManifest(manifestPath: string): VerifiedBackup;
24
- export declare function restoreVerifiedBackup(manifestPath: string, targetPath: string, confirmation: string): string;
25
+ export declare function restoreVerifiedBackup(manifestPath: string, targetPath: string, confirmation: string, existingGate?: MaintenanceGate, expectedSha256?: string): string;
25
26
  export declare function inspectBackupDatabase(path: string): import("./health").DatabaseHealth;
@@ -6,6 +6,10 @@ export interface DatabaseHealth {
6
6
  counts: Record<string, number>;
7
7
  }
8
8
  export declare function inspectDatabase(db: Database): DatabaseHealth;
9
- export declare function assertHealthyDatabase(db: Database): DatabaseHealth;
9
+ export declare function assertHealthyDatabase(db: Database, options?: {
10
+ verifySchema?: boolean;
11
+ }): DatabaseHealth;
12
+ export declare function assertSchemaV11(db: Database): void;
13
+ export declare function isSQLiteBusyError(error: unknown): boolean;
10
14
  export declare function hasTable(db: Database, table: string): boolean;
11
15
  export declare function hasColumn(db: Database, table: string, column: string): boolean;
@@ -0,0 +1,4 @@
1
+ import type { Database } from "bun:sqlite";
2
+ export declare function assertLegacySchemaIdentity(db: Database, version: number, options?: {
3
+ verifyHealth?: boolean;
4
+ }): void;
@@ -0,0 +1,21 @@
1
+ export interface DatabaseLease {
2
+ databasePath: string;
3
+ release: () => void;
4
+ }
5
+ export interface MaintenanceGate {
6
+ databasePath: string;
7
+ release: () => void;
8
+ assertOwned: () => void;
9
+ retain: () => void;
10
+ }
11
+ export interface MaintenanceRecovery {
12
+ ownerID: string;
13
+ confirmation: "RECOVER_RETAINED_MAINTENANCE_GATE";
14
+ }
15
+ export declare function acquireDatabaseLease(databasePath: string): DatabaseLease;
16
+ export declare function acquireMaintenanceGate(databasePath: string, recovery?: MaintenanceRecovery): MaintenanceGate;
17
+ export declare function recoverStaleMaintenanceGate(databasePath: string, validate: () => void): boolean;
18
+ export declare function assertMaintenanceGateFor(gate: MaintenanceGate, databasePath: string): void;
19
+ export declare function canonicalDatabasePath(path: string): string;
20
+ export declare function ensureDatabaseParent(path: string): void;
21
+ export declare function assertNoSymbolicLinks(path: string, allowMissingLeaf?: boolean): void;
@@ -0,0 +1,3 @@
1
+ import type { Database } from "bun:sqlite";
2
+ export declare function migrateV10ToV11(db: Database): void;
3
+ export declare function assertV10SourceDatabase(db: Database): void;
@@ -1,6 +1,15 @@
1
- import type { Database } from "bun:sqlite";
1
+ import { Database } from "bun:sqlite";
2
+ export declare const APPLICATION_ID = 1095195213;
3
+ export declare const PRODUCT_ID: "agz-memory";
4
+ export declare const HASH_POLICY: "hash-tuple/2";
2
5
  export declare const SCHEMA_TABLES: string;
3
6
  export declare const FTS_V9 = "\nCREATE VIRTUAL TABLE IF NOT EXISTS notes_fts USING fts5(\n title, summary, content,\n content='notes', content_rowid='rowid',\n tokenize='unicode61'\n);\nCREATE TRIGGER IF NOT EXISTS notes_fts_ai AFTER INSERT ON notes BEGIN\n INSERT INTO notes_fts(rowid, title, summary, content)\n VALUES (new.rowid, new.title, new.summary, new.content);\nEND;\nCREATE TRIGGER IF NOT EXISTS notes_fts_ad AFTER DELETE ON notes BEGIN\n INSERT INTO notes_fts(notes_fts, rowid, title, summary, content)\n VALUES ('delete', old.rowid, old.title, old.summary, old.content);\nEND;\nCREATE TRIGGER IF NOT EXISTS notes_fts_au AFTER UPDATE OF title, summary, content ON notes BEGIN\n INSERT INTO notes_fts(notes_fts, rowid, title, summary, content)\n VALUES ('delete', old.rowid, old.title, old.summary, old.content);\n INSERT INTO notes_fts(rowid, title, summary, content)\n VALUES (new.rowid, new.title, new.summary, new.content);\nEND;\n";
7
+ export declare const SCHEMA_V11_TABLES: string;
8
+ export declare function captureEventsTableV11(table?: "capture_events" | "capture_events_v11"): string;
9
+ export declare function schemaFingerprint(db: Database): string;
10
+ export declare function expectedSchemaFingerprint(): string;
11
+ export declare function createSchemaV11(db: Database): void;
12
+ export declare function insertV11Identity(db: Database, databaseID?: `${string}-${string}-${string}-${string}-${string}`, createdAt?: number): void;
4
13
  export declare function createSchema(db: Database): void;
5
14
  export declare function rebuildFts(db: Database): void;
6
15
  export declare function captureEventsTable(table?: "capture_events" | "capture_events_v10"): string;
@@ -3,4 +3,15 @@ export interface OpenedDB {
3
3
  db: Database;
4
4
  close: () => void;
5
5
  }
6
- export declare function openMemoryDatabase(path: string): OpenedDB;
6
+ export type MigrationStage = "source-validation" | "backup-checkpoint" | "v2-import" | "v8-to-v9" | "v9-to-v10" | "v10-to-v11" | "fingerprint" | "deep-health";
7
+ export interface MigrationTiming {
8
+ phases: Array<{
9
+ stage: MigrationStage;
10
+ elapsedMs: number;
11
+ }>;
12
+ }
13
+ export declare function createMigrationTimingCollector(): MigrationTiming;
14
+ export declare function openMemoryDatabase(path: string, options?: {
15
+ timing?: MigrationTiming;
16
+ }): OpenedDB;
17
+ export declare function openReadOnlyMemoryDatabase(path: string): OpenedDB;
@@ -0,0 +1,4 @@
1
+ export type HashTupleValue = null | string | boolean | number | Uint8Array;
2
+ export declare function hashTuple(domain: string, version: number, fields: readonly HashTupleValue[]): string;
3
+ export declare function noteContentHash(kind: string, title: string, summary: string, content: string): string;
4
+ export declare const canonicalNoteHash: typeof noteContentHash;
@@ -32,11 +32,23 @@ export interface BackendHealth {
32
32
  version?: string;
33
33
  errorCode?: string;
34
34
  }
35
+ export interface BackendOperationContext {
36
+ operationKey: string;
37
+ sequence: number;
38
+ fence: number;
39
+ }
40
+ export interface OutboxBackend extends RetrievalBackend {
41
+ readonly outboxProtocol: "agz-memory-outbox/1";
42
+ upsert(document: DerivedDocument, signal: AbortSignal, operation: BackendOperationContext): Promise<void>;
43
+ delete(ref: DerivedRef, signal: AbortSignal, operation: BackendOperationContext): Promise<void>;
44
+ purgeProject(projectID: string, signal: AbortSignal, operation: BackendOperationContext): Promise<void>;
45
+ }
35
46
  export interface RetrievalBackend {
36
47
  id: string;
37
- upsert(document: DerivedDocument, signal: AbortSignal): Promise<void>;
38
- delete(ref: DerivedRef, signal: AbortSignal): Promise<void>;
39
- purgeProject(projectID: string, signal: AbortSignal): Promise<void>;
48
+ upsert(document: DerivedDocument, signal: AbortSignal, operation?: BackendOperationContext): Promise<void>;
49
+ delete(ref: DerivedRef, signal: AbortSignal, operation?: BackendOperationContext): Promise<void>;
50
+ purgeProject(projectID: string, signal: AbortSignal, operation?: BackendOperationContext): Promise<void>;
40
51
  query(projectID: string, query: string, limit: number, signal: AbortSignal): Promise<RankedHit[]>;
41
52
  health(signal: AbortSignal): Promise<BackendHealth>;
42
53
  }
54
+ export declare function validateBackendHits(value: unknown): value is RankedHit[];
@@ -0,0 +1,47 @@
1
+ export interface QuarantineSourceIdentity {
2
+ schema: string;
3
+ projectID: string;
4
+ bindingKey: string;
5
+ kind: string;
6
+ source: {
7
+ system: string;
8
+ opencodeVersion: string;
9
+ pluginVersion: string;
10
+ sessionID: string;
11
+ messageID?: string;
12
+ ordinal?: number;
13
+ toolCallID?: string;
14
+ };
15
+ }
16
+ export interface QuarantineDigest {
17
+ keyID: string;
18
+ digest: string;
19
+ }
20
+ export interface QuarantineKeyReference {
21
+ keyID: string;
22
+ }
23
+ /**
24
+ * Private local keyring for source-only quarantine digests. Windows is deliberately
25
+ * fail-closed until a current-user ACL verifier is available.
26
+ */
27
+ export declare class QuarantineKeyring {
28
+ private readonly path;
29
+ constructor(path: string);
30
+ readActiveKey(): QuarantineKeyReference;
31
+ ensureActiveKey(): QuarantineKeyReference;
32
+ rotate(): QuarantineKeyReference;
33
+ digestSource(source: QuarantineSourceIdentity, payloadFingerprint: string): QuarantineDigest;
34
+ /** Use after lifecycle initialization; a later missing key must fail closed. */
35
+ digestExistingSource(source: QuarantineSourceIdentity, payloadFingerprint: string): QuarantineDigest;
36
+ verifySourceDigest(source: QuarantineSourceIdentity, payloadFingerprint: string, keyID: string, digest: string): boolean;
37
+ private activeKey;
38
+ private keyReference;
39
+ private createInitialKeyring;
40
+ private readDocument;
41
+ private writeAtomically;
42
+ private acquireLock;
43
+ private assertSafeParent;
44
+ private assertSupportedPlatform;
45
+ private assertPermissions;
46
+ private syncParent;
47
+ }
@@ -1,5 +1,5 @@
1
1
  import { McpServer } from "@modelcontextprotocol/server";
2
2
  import type { MemoryStore } from "./store";
3
3
  export declare const SERVER_NAME = "agz-memory";
4
- export declare const SERVER_VERSION: "0.4.1";
4
+ export declare const SERVER_VERSION: "0.5.1";
5
5
  export declare function createMemoryServer(store: MemoryStore): McpServer;
@@ -1,4 +1,5 @@
1
1
  import type { Database } from "bun:sqlite";
2
+ import { QuarantineKeyring } from "../security/quarantine-key";
2
3
  export interface ProjectBindingInput {
3
4
  memoryProjectID: string;
4
5
  opencodeProjectID: string;
@@ -6,6 +7,7 @@ export interface ProjectBindingInput {
6
7
  workspaceID?: string;
7
8
  }
8
9
  export type CaptureMode = "shadow" | "auto-write";
10
+ type CheckpointState = "active" | "idle" | "unavailable" | "closed";
9
11
  export interface CaptureIngestResult {
10
12
  outcome: "shadowed" | "materialized" | "duplicate" | "ignored" | "review" | "rejected" | "quarantined";
11
13
  idempotencyKey: string;
@@ -15,17 +17,23 @@ export interface CaptureIngestResult {
15
17
  export declare class CaptureStore {
16
18
  private db;
17
19
  private indexBackends;
18
- constructor(db: Database, indexBackends?: readonly string[]);
20
+ private readonly quarantineKeyring;
21
+ constructor(db: Database, indexBackends?: readonly string[], options?: {
22
+ quarantineKeyring?: QuarantineKeyring;
23
+ });
19
24
  bindProject(input: ProjectBindingInput): {
25
+ ok: true;
20
26
  bindingKey: string;
21
27
  projectID: string;
22
28
  };
23
- checkpoint(sessionID: string, bindingKey: string, projectID: string, messageID?: string, state?: "active" | "idle" | "unavailable" | "closed"): void;
24
- markReconciled(sessionID: string, state: "active" | "idle" | "unavailable" | "closed", lastMessageID?: string, failed?: boolean): void;
25
- getCheckpoint(sessionID: string): {
29
+ checkpoint(sessionID: string, bindingKey: string, projectID: string, messageID?: string, state?: CheckpointState): void;
30
+ markReconciled(sessionID: string, state: CheckpointState, lastMessageID?: string, failed?: boolean, bindingKey?: string, projectID?: string): void;
31
+ markReconciled(sessionID: string, bindingKey: string, state: CheckpointState, lastMessageID?: string, failed?: boolean, projectID?: string): void;
32
+ markReconciled(sessionID: string, bindingKey: string, projectID: string, state: CheckpointState, lastMessageID?: string, failed?: boolean): void;
33
+ getCheckpoint(sessionID: string, bindingKey?: string, projectID?: string): {
26
34
  sessionID: string;
27
35
  lastMessageID?: string;
28
- state: "active" | "idle" | "unavailable" | "closed";
36
+ state: CheckpointState;
29
37
  } | undefined;
30
38
  ingest(input: unknown, mode: CaptureMode, options?: {
31
39
  allowedKinds?: readonly string[];
@@ -48,4 +56,8 @@ export declare class CaptureStore {
48
56
  private enqueueOutbox;
49
57
  private finishEvent;
50
58
  private binding;
59
+ private hasBinding;
60
+ private checkpointRow;
61
+ private uniqueCheckpointForSession;
51
62
  }
63
+ export {};
@@ -1,14 +1,26 @@
1
1
  import type { Database } from "bun:sqlite";
2
- import type { RetrievalBackend } from "../retrieval/contract";
3
- export type OutboxOutcome = "idle" | "succeeded" | "stale" | "quarantined" | "retry" | "dead";
2
+ import type { OutboxBackend } from "../retrieval/contract";
3
+ interface OutboxRetentionOptions {
4
+ terminalRetention?: number;
5
+ terminalPruneInterval?: number;
6
+ }
7
+ export type OutboxOutcome = "idle" | "succeeded" | "stale" | "quarantined" | "retry" | "dead" | "lost_lease";
4
8
  export declare class OutboxWorker {
5
9
  private db;
6
10
  private backends;
7
11
  private now;
8
12
  private random;
9
13
  private readonly workerID;
10
- constructor(db: Database, backends: ReadonlyMap<string, RetrievalBackend>, now?: () => number, random?: () => number);
14
+ private readonly terminalRetention;
15
+ private readonly terminalPruneInterval;
16
+ private terminalTransitions;
17
+ constructor(db: Database, backends: ReadonlyMap<string, OutboxBackend>, now?: () => number, random?: () => number, retention?: OutboxRetentionOptions);
11
18
  processNext(): Promise<OutboxOutcome>;
19
+ private claim;
20
+ private runBackend;
21
+ private stale;
12
22
  private succeed;
13
23
  private fail;
24
+ private pruneTerminalOutbox;
14
25
  }
26
+ export {};
@@ -1,5 +1,5 @@
1
1
  import type { Database } from "bun:sqlite";
2
- import type { RetrievalBackend, RetrievalRequest } from "../retrieval/contract";
2
+ import { type RetrievalBackend, type RetrievalRequest } from "../retrieval/contract";
3
3
  import type { RecallCard } from "../types";
4
4
  export interface RetrievalResult {
5
5
  cards: RecallCard[];
@@ -14,5 +14,5 @@ export declare class RetrievalStore {
14
14
  private lexical;
15
15
  private graph;
16
16
  private graphPredicates;
17
- private note;
17
+ private notes;
18
18
  }
@@ -1,17 +1,10 @@
1
1
  import type { Database } from "bun:sqlite";
2
+ import { type MutationOperation } from "./contracts/mutation";
2
3
  import type { Edge, Note, Project, ProjectSummary, RecallCard } from "./types";
3
4
  export interface ProjectSelector {
4
5
  projectID?: string;
5
6
  projectName?: string;
6
7
  }
7
- export interface UpdateInput {
8
- kind?: string;
9
- title?: string;
10
- summary?: string;
11
- content?: string;
12
- id?: string;
13
- delete?: boolean;
14
- }
15
8
  export interface UpdateResult {
16
9
  ok: boolean;
17
10
  id?: string;
@@ -31,6 +24,7 @@ export declare class MemoryStore {
31
24
  reason?: string;
32
25
  };
33
26
  listProjects(): ProjectSummary[];
27
+ listProjectsPage(limit: number, cursor?: string, snapshot?: string): import("./core").Page<ProjectSummary>;
34
28
  createProject(nameValue: string): {
35
29
  ok: boolean;
36
30
  project?: Project;
@@ -53,13 +47,20 @@ export declare class MemoryStore {
53
47
  };
54
48
  reason?: string;
55
49
  };
56
- update(projectID: string, input: UpdateInput): UpdateResult;
50
+ update(projectID: string, operation: MutationOperation): UpdateResult;
57
51
  pin(projectID: string, id: string, pinned: boolean): {
58
- ok: boolean;
59
- reason: string;
60
- id?: undefined;
61
52
  projectID?: undefined;
62
53
  projectName?: undefined;
54
+ id?: undefined;
55
+ ok: boolean;
56
+ reason: string;
57
+ pinned?: undefined;
58
+ } | {
59
+ ok: boolean;
60
+ id: string;
61
+ projectID: string;
62
+ projectName: string;
63
+ reason: string;
63
64
  pinned?: undefined;
64
65
  } | {
65
66
  reason?: undefined;
@@ -73,8 +74,25 @@ export declare class MemoryStore {
73
74
  read(projectID: string, id: string): {
74
75
  note?: Note;
75
76
  edges?: Edge[];
77
+ snapshot?: string;
78
+ etag?: string;
79
+ nextCursor?: string;
76
80
  reason?: string;
77
81
  };
82
+ readPage(projectID: string, id: string, limit: number, cursor?: string, snapshot?: string): {
83
+ reason: string;
84
+ } | {
85
+ items: Edge[];
86
+ snapshot: string;
87
+ etag: string;
88
+ nextCursor?: string;
89
+ reason?: undefined;
90
+ note: Note;
91
+ };
92
+ listRevisionsPage(projectID: string, noteID: string, limit: number, cursor?: string, snapshot?: string): import("./core").Page<{
93
+ revision: number;
94
+ created_at: number;
95
+ }>;
78
96
  link(projectID: string, sourceID: string, targetID: string, predicate: string): {
79
97
  projectID?: undefined;
80
98
  projectName?: undefined;
@@ -87,9 +105,18 @@ export declare class MemoryStore {
87
105
  projectName: string;
88
106
  };
89
107
  recall(projectID: string, query: string, limit?: number): RecallCard[];
108
+ recallPage(projectID: string, query: string, limit?: number, cursor?: string, snapshot?: string): {
109
+ cards: RecallCard[];
110
+ snapshot: string;
111
+ etag: string;
112
+ nextCursor?: string | undefined;
113
+ };
90
114
  private getProjectRow;
115
+ private bumpProjectVersion;
116
+ private getProjectByNormalizedName;
91
117
  private projectNameExists;
92
118
  private getNoteRow;
93
119
  private recordCurrentRevision;
94
120
  private enqueueOutbox;
121
+ private immediateTransaction;
95
122
  }
@@ -1,4 +1,4 @@
1
- export declare const SCHEMA_VERSION = 10;
1
+ export declare const SCHEMA_VERSION = 11;
2
2
  export declare const INLINE_LIMIT = 1200;
3
3
  export declare const KINDS: readonly ["decision", "fact", "procedure", "context", "research", "preference", "task"];
4
4
  export type Kind = (typeof KINDS)[number];
@@ -1 +1 @@
1
- export declare const PRODUCT_VERSION: "0.4.1";
1
+ export declare const PRODUCT_VERSION: "0.5.1";