New:Socket for Asana Is Now Available.Learn more β†’
Get Started

aidimag

Package Overview
Dependencies
Maintainers
1
Versions
27
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

aidimag - npm Package Compare versions

Comparing version
1.0.17
to
1.0.18
+66
-0
dist/cli/commands/memory.js

@@ -285,2 +285,68 @@ /**

program
.command("scratch")
.description("Short-term scratchpad (session working memory): TTL-expiring notes, never synced, never durable memory")
.argument("[note...]", "Note to jot down; omit to list current notes")
.option("--session <id>", "Session/topic key", "default")
.option("--ttl <hours>", "Hours until the note expires", "24")
.option("--clear", "Clear notes (this --session, or --all)")
.option("--all", "With --clear: clear every session; when listing: show all sessions")
.action((note, opts) => {
const store = MemoryStore.open();
try {
if (opts.clear) {
const n = store.scratchpadClear(opts.all ? undefined : opts.session);
console.log(`Cleared ${n} scratchpad note(s)${opts.all ? "" : ` in session '${opts.session}'`}.`);
}
else if (note.length) {
const ttl = parseFloat(opts.ttl);
if (!Number.isFinite(ttl) || ttl <= 0)
fail(`invalid --ttl '${opts.ttl}'`);
const entry = store.scratchpadWrite(note.join(" "), {
sessionId: opts.session,
ttlHours: ttl,
createdBy: "human",
});
console.log(`πŸ“ jotted (session=${entry.sessionId}, expires ${entry.expiresAt.slice(0, 16)})`);
console.log(` Scratch notes are short-term. Keep it forever with \`dim remember\`.`);
}
else {
const notes = store.scratchpadRead(opts.all ? undefined : opts.session);
if (notes.length === 0) {
console.log("Scratchpad is empty.");
}
else {
for (const n of notes) {
console.log(`- [${n.sessionId} Β· ${n.createdAt.slice(0, 16)} Β· by ${n.createdBy}] ${n.content}`);
}
console.log(`\n${notes.length} note(s). They expire automatically; \`dim scratch --clear\` wipes them now.`);
}
}
}
finally {
store.close();
}
});
program
.command("audit")
.description("Provenance audit: memories resting on the least ground β€” agent-authored, evidence-free, stale, or long-unverified")
.option("-n, --limit <n>", "Max entries", "20")
.action((opts) => {
const store = MemoryStore.open();
const findings = store.auditMemories({ limit: parseInt(opts.limit, 10) });
if (findings.length === 0) {
console.log("βœ“ Nothing suspicious β€” every memory is human/knowledge-authored, evidenced, and recently verified.");
}
else {
console.log(`${findings.length} memorie(s) worth a look β€” highest risk first:\n`);
for (const f of findings) {
printMemory(f.memory);
for (const r of f.reasons)
console.log(` ⚠ ${r}`);
}
console.log(`\nFix-ups: \`dim update <id> -e TYPE:proof\` adds evidence Β· \`dim verify\` re-checks Β· ` +
`\`dim refute <id>\` / \`dim forget <id>\` removes.`);
}
store.close();
});
program
.command("refute")

@@ -287,0 +353,0 @@ .description("Mark a memory REFUTED (kept as negative knowledge, unlike forget)")

+2
-2

@@ -5,3 +5,3 @@ /**

*/
export declare const SCHEMA_VERSION = 10;
export declare const SCHEMA_VERSION = 11;
/** Idempotent migrations for pre-existing DBs (failures = already applied). */

@@ -29,2 +29,2 @@ export declare const MIGRATIONS: string[];

export declare const EVENTS_REBUILD_V9 = "\nALTER TABLE events RENAME TO events_v8;\nCREATE TABLE events (\n seq INTEGER PRIMARY KEY AUTOINCREMENT,\n id TEXT NOT NULL UNIQUE,\n type TEXT NOT NULL CHECK (type IN (\n 'memory_created','status_changed','evidence_result',\n 'refuted','superseded','forgotten',\n 'proposal_created','proposal_approved','proposal_rejected',\n 'verification_report','updated','evidence_added','evidence_removed')),\n memory_id TEXT,\n payload TEXT NOT NULL DEFAULT '{}',\n machine TEXT NOT NULL,\n schema_version INTEGER NOT NULL,\n created_at TEXT NOT NULL,\n synced INTEGER NOT NULL DEFAULT 0\n);\nINSERT INTO events SELECT * FROM events_v8;\nDROP TABLE events_v8;\nCREATE INDEX IF NOT EXISTS idx_events_synced ON events(synced, seq);\n";
export declare const SCHEMA_SQL = "\nPRAGMA journal_mode = WAL;\nPRAGMA foreign_keys = ON;\n\nCREATE TABLE IF NOT EXISTS meta (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n);\n\nCREATE TABLE IF NOT EXISTS memories (\n id TEXT PRIMARY KEY,\n kind TEXT NOT NULL CHECK (kind IN (\n 'DECISION','CONVENTION','GOTCHA','FAILED_APPROACH',\n 'ARCHITECTURE','INVARIANT','TODO_CONTEXT','GUARDRAIL','SKILL')),\n claim TEXT NOT NULL,\n confidence REAL NOT NULL DEFAULT 0.5,\n status TEXT NOT NULL DEFAULT 'UNVERIFIED' CHECK (status IN (\n 'VERIFIED','UNVERIFIED','STALE','REFUTED')),\n created_by TEXT NOT NULL DEFAULT 'human',\n created_at TEXT NOT NULL,\n verified_at TEXT,\n superseded_by TEXT REFERENCES memories(id),\n updated_at TEXT,\n pinned INTEGER NOT NULL DEFAULT 0,\n guardrail_level TEXT CHECK (guardrail_level IN ('always','ask-first','never')),\n cloud_synced INTEGER NOT NULL DEFAULT 0,\n cloud_seq INTEGER\n);\n\n-- scope: one row per path / symbol a memory applies to\nCREATE TABLE IF NOT EXISTS memory_scopes (\n memory_id TEXT NOT NULL REFERENCES memories(id) ON DELETE CASCADE,\n scope_type TEXT NOT NULL CHECK (scope_type IN ('path','symbol')),\n value TEXT NOT NULL,\n PRIMARY KEY (memory_id, scope_type, value)\n);\n\nCREATE TABLE IF NOT EXISTS evidence (\n id TEXT PRIMARY KEY,\n memory_id TEXT NOT NULL REFERENCES memories(id) ON DELETE CASCADE,\n type TEXT NOT NULL CHECK (type IN (\n 'COMMIT_REF','TEST_RESULT','EXEC_TRACE','STATIC_CHECK','HUMAN_ATTESTED','TICKET_REF')),\n payload TEXT NOT NULL,\n last_run TEXT,\n result TEXT NOT NULL DEFAULT 'UNKNOWN' CHECK (result IN ('PASS','FAIL','UNKNOWN'))\n);\n\nCREATE TABLE IF NOT EXISTS memory_links (\n from_id TEXT NOT NULL REFERENCES memories(id) ON DELETE CASCADE,\n to_id TEXT NOT NULL REFERENCES memories(id) ON DELETE CASCADE,\n relation TEXT NOT NULL CHECK (relation IN ('supports','contradicts','refines')),\n PRIMARY KEY (from_id, to_id, relation)\n);\n\n-- Full-text search over claims (Phase 1 retrieval)\nCREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(\n claim,\n content='memories',\n content_rowid='rowid'\n);\n\nCREATE TRIGGER IF NOT EXISTS memories_ai AFTER INSERT ON memories BEGIN\n INSERT INTO memories_fts(rowid, claim) VALUES (new.rowid, new.claim);\nEND;\n\nCREATE TRIGGER IF NOT EXISTS memories_ad AFTER DELETE ON memories BEGIN\n INSERT INTO memories_fts(memories_fts, rowid, claim) VALUES ('delete', old.rowid, old.claim);\nEND;\n\nCREATE TRIGGER IF NOT EXISTS memories_au AFTER UPDATE OF claim ON memories BEGIN\n INSERT INTO memories_fts(memories_fts, rowid, claim) VALUES ('delete', old.rowid, old.claim);\n INSERT INTO memories_fts(rowid, claim) VALUES (new.rowid, new.claim);\nEND;\n\n-- Phase 2: capture pipeline \u2014 proposed memories awaiting human review\nCREATE TABLE IF NOT EXISTS proposals (\n id TEXT PRIMARY KEY,\n kind TEXT NOT NULL CHECK (kind IN (\n 'DECISION','CONVENTION','GOTCHA','FAILED_APPROACH',\n 'ARCHITECTURE','INVARIANT','TODO_CONTEXT','GUARDRAIL','SKILL')),\n claim TEXT NOT NULL,\n paths TEXT NOT NULL DEFAULT '[]', -- JSON string[]\n symbols TEXT NOT NULL DEFAULT '[]', -- JSON string[]\n evidence TEXT NOT NULL DEFAULT '[]', -- JSON {type,payload}[]\n source TEXT NOT NULL, -- 'commit-miner' | 'session:<agent-id>' | ...\n source_ref TEXT, -- e.g. commit sha\n rationale TEXT, -- why the source thinks this is worth remembering\n created_at TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'PENDING' CHECK (status IN ('PENDING','APPROVED','REJECTED')),\n memory_id TEXT REFERENCES memories(id) ON DELETE SET NULL, -- set when approved\n updated_at TEXT,\n ticket_ref TEXT, -- ticket id (e.g. XXX-2100) when known\n guardrail_level TEXT, -- always | ask-first | never (GUARDRAIL proposals)\n cloud_synced INTEGER NOT NULL DEFAULT 0,\n cloud_seq INTEGER\n);\n\nCREATE INDEX IF NOT EXISTS idx_proposals_status ON proposals(status);\nCREATE UNIQUE INDEX IF NOT EXISTS idx_proposals_dedupe ON proposals(source, source_ref, claim);\n\n-- Phase 6: sync \u2014 deletions must propagate, so deletes leave tombstones\nCREATE TABLE IF NOT EXISTS tombstones (\n id TEXT NOT NULL, -- deleted row id\n tbl TEXT NOT NULL CHECK (tbl IN ('memories','proposals')),\n deleted_at TEXT NOT NULL,\n PRIMARY KEY (id, tbl)\n);\n\nCREATE INDEX IF NOT EXISTS idx_memories_status ON memories(status);\nCREATE INDEX IF NOT EXISTS idx_memories_kind ON memories(kind);\nCREATE INDEX IF NOT EXISTS idx_memories_cloud_synced ON memories(cloud_synced);\nCREATE INDEX IF NOT EXISTS idx_proposals_cloud_synced ON proposals(cloud_synced);\nCREATE INDEX IF NOT EXISTS idx_scopes_value ON memory_scopes(value);\nCREATE INDEX IF NOT EXISTS idx_evidence_memory ON evidence(memory_id);\n\n-- Passive capture: every MCP memory_search is logged locally. Zero-hit\n-- queries are coverage gaps \u2014 things agents needed but memory couldn't answer.\n-- Surfaced via `dim gaps` and the session briefing; never synced.\nCREATE TABLE IF NOT EXISTS search_log (\n id TEXT PRIMARY KEY,\n query TEXT NOT NULL,\n paths TEXT NOT NULL DEFAULT '[]', -- JSON string[] (scope filter, if any)\n result_count INTEGER NOT NULL,\n source TEXT NOT NULL DEFAULT 'mcp', -- 'mcp' | 'cli'\n created_at TEXT NOT NULL\n);\nCREATE INDEX IF NOT EXISTS idx_search_log_created ON search_log(created_at);\nCREATE INDEX IF NOT EXISTS idx_search_log_hits ON search_log(result_count, created_at);\n\n-- SaaS groundwork: local append-only event log (CLOUD_DESIGN sync model).\n-- Every memory-lifecycle change is recorded here and shipped to the sync\n-- server on `dim sync`; the server aggregates evidence_result events from\n-- multiple machines into consensus confidence.\nCREATE TABLE IF NOT EXISTS events (\n seq INTEGER PRIMARY KEY AUTOINCREMENT,\n id TEXT NOT NULL UNIQUE, -- uuid (idempotent server ingest)\n type TEXT NOT NULL CHECK (type IN (\n 'memory_created','status_changed','evidence_result',\n 'refuted','superseded','forgotten',\n 'proposal_created','proposal_approved','proposal_rejected',\n 'verification_report')),\n memory_id TEXT, -- subject memory/proposal id\n payload TEXT NOT NULL DEFAULT '{}', -- JSON event body\n machine TEXT NOT NULL, -- stable per-machine id\n schema_version INTEGER NOT NULL,\n created_at TEXT NOT NULL,\n synced INTEGER NOT NULL DEFAULT 0 -- 1 once pushed to the server\n);\nCREATE INDEX IF NOT EXISTS idx_events_synced ON events(synced, seq);\n";
export declare const SCHEMA_SQL = "\nPRAGMA journal_mode = WAL;\nPRAGMA foreign_keys = ON;\n\nCREATE TABLE IF NOT EXISTS meta (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n);\n\nCREATE TABLE IF NOT EXISTS memories (\n id TEXT PRIMARY KEY,\n kind TEXT NOT NULL CHECK (kind IN (\n 'DECISION','CONVENTION','GOTCHA','FAILED_APPROACH',\n 'ARCHITECTURE','INVARIANT','TODO_CONTEXT','GUARDRAIL','SKILL')),\n claim TEXT NOT NULL,\n confidence REAL NOT NULL DEFAULT 0.5,\n status TEXT NOT NULL DEFAULT 'UNVERIFIED' CHECK (status IN (\n 'VERIFIED','UNVERIFIED','STALE','REFUTED')),\n created_by TEXT NOT NULL DEFAULT 'human',\n created_at TEXT NOT NULL,\n verified_at TEXT,\n superseded_by TEXT REFERENCES memories(id),\n updated_at TEXT,\n pinned INTEGER NOT NULL DEFAULT 0,\n guardrail_level TEXT CHECK (guardrail_level IN ('always','ask-first','never')),\n cloud_synced INTEGER NOT NULL DEFAULT 0,\n cloud_seq INTEGER\n);\n\n-- scope: one row per path / symbol a memory applies to\nCREATE TABLE IF NOT EXISTS memory_scopes (\n memory_id TEXT NOT NULL REFERENCES memories(id) ON DELETE CASCADE,\n scope_type TEXT NOT NULL CHECK (scope_type IN ('path','symbol')),\n value TEXT NOT NULL,\n PRIMARY KEY (memory_id, scope_type, value)\n);\n\nCREATE TABLE IF NOT EXISTS evidence (\n id TEXT PRIMARY KEY,\n memory_id TEXT NOT NULL REFERENCES memories(id) ON DELETE CASCADE,\n type TEXT NOT NULL CHECK (type IN (\n 'COMMIT_REF','TEST_RESULT','EXEC_TRACE','STATIC_CHECK','HUMAN_ATTESTED','TICKET_REF')),\n payload TEXT NOT NULL,\n last_run TEXT,\n result TEXT NOT NULL DEFAULT 'UNKNOWN' CHECK (result IN ('PASS','FAIL','UNKNOWN'))\n);\n\nCREATE TABLE IF NOT EXISTS memory_links (\n from_id TEXT NOT NULL REFERENCES memories(id) ON DELETE CASCADE,\n to_id TEXT NOT NULL REFERENCES memories(id) ON DELETE CASCADE,\n relation TEXT NOT NULL CHECK (relation IN ('supports','contradicts','refines')),\n PRIMARY KEY (from_id, to_id, relation)\n);\n\n-- Full-text search over claims (Phase 1 retrieval)\nCREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(\n claim,\n content='memories',\n content_rowid='rowid'\n);\n\nCREATE TRIGGER IF NOT EXISTS memories_ai AFTER INSERT ON memories BEGIN\n INSERT INTO memories_fts(rowid, claim) VALUES (new.rowid, new.claim);\nEND;\n\nCREATE TRIGGER IF NOT EXISTS memories_ad AFTER DELETE ON memories BEGIN\n INSERT INTO memories_fts(memories_fts, rowid, claim) VALUES ('delete', old.rowid, old.claim);\nEND;\n\nCREATE TRIGGER IF NOT EXISTS memories_au AFTER UPDATE OF claim ON memories BEGIN\n INSERT INTO memories_fts(memories_fts, rowid, claim) VALUES ('delete', old.rowid, old.claim);\n INSERT INTO memories_fts(rowid, claim) VALUES (new.rowid, new.claim);\nEND;\n\n-- Phase 2: capture pipeline \u2014 proposed memories awaiting human review\nCREATE TABLE IF NOT EXISTS proposals (\n id TEXT PRIMARY KEY,\n kind TEXT NOT NULL CHECK (kind IN (\n 'DECISION','CONVENTION','GOTCHA','FAILED_APPROACH',\n 'ARCHITECTURE','INVARIANT','TODO_CONTEXT','GUARDRAIL','SKILL')),\n claim TEXT NOT NULL,\n paths TEXT NOT NULL DEFAULT '[]', -- JSON string[]\n symbols TEXT NOT NULL DEFAULT '[]', -- JSON string[]\n evidence TEXT NOT NULL DEFAULT '[]', -- JSON {type,payload}[]\n source TEXT NOT NULL, -- 'commit-miner' | 'session:<agent-id>' | ...\n source_ref TEXT, -- e.g. commit sha\n rationale TEXT, -- why the source thinks this is worth remembering\n created_at TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'PENDING' CHECK (status IN ('PENDING','APPROVED','REJECTED')),\n memory_id TEXT REFERENCES memories(id) ON DELETE SET NULL, -- set when approved\n updated_at TEXT,\n ticket_ref TEXT, -- ticket id (e.g. XXX-2100) when known\n guardrail_level TEXT, -- always | ask-first | never (GUARDRAIL proposals)\n cloud_synced INTEGER NOT NULL DEFAULT 0,\n cloud_seq INTEGER\n);\n\nCREATE INDEX IF NOT EXISTS idx_proposals_status ON proposals(status);\nCREATE UNIQUE INDEX IF NOT EXISTS idx_proposals_dedupe ON proposals(source, source_ref, claim);\n\n-- Phase 6: sync \u2014 deletions must propagate, so deletes leave tombstones\nCREATE TABLE IF NOT EXISTS tombstones (\n id TEXT NOT NULL, -- deleted row id\n tbl TEXT NOT NULL CHECK (tbl IN ('memories','proposals')),\n deleted_at TEXT NOT NULL,\n PRIMARY KEY (id, tbl)\n);\n\nCREATE INDEX IF NOT EXISTS idx_memories_status ON memories(status);\nCREATE INDEX IF NOT EXISTS idx_memories_kind ON memories(kind);\nCREATE INDEX IF NOT EXISTS idx_memories_cloud_synced ON memories(cloud_synced);\nCREATE INDEX IF NOT EXISTS idx_proposals_cloud_synced ON proposals(cloud_synced);\nCREATE INDEX IF NOT EXISTS idx_scopes_value ON memory_scopes(value);\nCREATE INDEX IF NOT EXISTS idx_evidence_memory ON evidence(memory_id);\n\n-- Passive capture: every MCP memory_search is logged locally. Zero-hit\n-- queries are coverage gaps \u2014 things agents needed but memory couldn't answer.\n-- Surfaced via `dim gaps` and the session briefing; never synced.\nCREATE TABLE IF NOT EXISTS search_log (\n id TEXT PRIMARY KEY,\n query TEXT NOT NULL,\n paths TEXT NOT NULL DEFAULT '[]', -- JSON string[] (scope filter, if any)\n result_count INTEGER NOT NULL,\n source TEXT NOT NULL DEFAULT 'mcp', -- 'mcp' | 'cli'\n created_at TEXT NOT NULL\n);\nCREATE INDEX IF NOT EXISTS idx_search_log_created ON search_log(created_at);\nCREATE INDEX IF NOT EXISTS idx_search_log_hits ON search_log(result_count, created_at);\n\n-- SaaS groundwork: local append-only event log (CLOUD_DESIGN sync model).\n-- Every memory-lifecycle change is recorded here and shipped to the sync\n-- server on `dim sync`; the server aggregates evidence_result events from\n-- multiple machines into consensus confidence.\nCREATE TABLE IF NOT EXISTS events (\n seq INTEGER PRIMARY KEY AUTOINCREMENT,\n id TEXT NOT NULL UNIQUE, -- uuid (idempotent server ingest)\n type TEXT NOT NULL CHECK (type IN (\n 'memory_created','status_changed','evidence_result',\n 'refuted','superseded','forgotten',\n 'proposal_created','proposal_approved','proposal_rejected',\n 'verification_report')),\n memory_id TEXT, -- subject memory/proposal id\n payload TEXT NOT NULL DEFAULT '{}', -- JSON event body\n machine TEXT NOT NULL, -- stable per-machine id\n schema_version INTEGER NOT NULL,\n created_at TEXT NOT NULL,\n synced INTEGER NOT NULL DEFAULT 0 -- 1 once pushed to the server\n);\nCREATE INDEX IF NOT EXISTS idx_events_synced ON events(synced, seq);\n\n-- v11: scratchpad \u2014 session-scoped short-term working memory.\n-- Intermediate findings, plans, and hypotheses for the CURRENT session. Entries\n-- expire automatically (TTL) and are purged on read. Local-only: never synced,\n-- never enters the review queue, never becomes durable memory. Promote anything\n-- worth keeping via `dim remember` / memory_propose.\nCREATE TABLE IF NOT EXISTS scratchpad (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL DEFAULT 'default',\n content TEXT NOT NULL,\n created_by TEXT NOT NULL DEFAULT 'agent',\n created_at TEXT NOT NULL,\n expires_at TEXT NOT NULL\n);\nCREATE INDEX IF NOT EXISTS idx_scratchpad_session ON scratchpad(session_id, created_at);\nCREATE INDEX IF NOT EXISTS idx_scratchpad_expiry ON scratchpad(expires_at);\n";

@@ -5,3 +5,3 @@ /**

*/
export const SCHEMA_VERSION = 10;
export const SCHEMA_VERSION = 11;
/** Idempotent migrations for pre-existing DBs (failures = already applied). */

@@ -280,3 +280,19 @@ export const MIGRATIONS = [

CREATE INDEX IF NOT EXISTS idx_events_synced ON events(synced, seq);
-- v11: scratchpad β€” session-scoped short-term working memory.
-- Intermediate findings, plans, and hypotheses for the CURRENT session. Entries
-- expire automatically (TTL) and are purged on read. Local-only: never synced,
-- never enters the review queue, never becomes durable memory. Promote anything
-- worth keeping via \`dim remember\` / memory_propose.
CREATE TABLE IF NOT EXISTS scratchpad (
id TEXT PRIMARY KEY,
session_id TEXT NOT NULL DEFAULT 'default',
content TEXT NOT NULL,
created_by TEXT NOT NULL DEFAULT 'agent',
created_at TEXT NOT NULL,
expires_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_scratchpad_session ON scratchpad(session_id, created_at);
CREATE INDEX IF NOT EXISTS idx_scratchpad_expiry ON scratchpad(expires_at);
`;
//# sourceMappingURL=schema.js.map

@@ -28,2 +28,18 @@ /**

}
/** Session-scoped short-term working memory note (TTL-expiring, never synced). */
export interface ScratchpadEntry {
id: string;
sessionId: string;
content: string;
createdBy: string;
createdAt: string;
expiresAt: string;
}
/** A memory flagged by the provenance audit, with why and how risky. */
export interface MemoryAuditFinding {
memory: MemoryEntry;
/** heuristic risk score β€” higher = review sooner */
risk: number;
reasons: string[];
}
export declare class MemoryStore {

@@ -130,2 +146,16 @@ private db;

clearSearchGaps(): number;
scratchpadWrite(content: string, opts?: {
sessionId?: string;
ttlHours?: number;
createdBy?: string;
}): ScratchpadEntry;
/** Read scratchpad notes (newest first). Expired entries are purged first. */
scratchpadRead(sessionId?: string, limit?: number): ScratchpadEntry[];
/** Clear scratchpad notes (one session, or all). Returns count removed. */
scratchpadClear(sessionId?: string): number;
/** Drop expired scratchpad entries. Returns count purged. */
purgeExpiredScratchpad(): number;
auditMemories(opts?: {
limit?: number;
}): MemoryAuditFinding[];
/** Last mined commit sha (commit-miner cursor), or null. */

@@ -132,0 +162,0 @@ getMeta(key: string): string | null;

@@ -307,2 +307,9 @@ /**

const statusPenalty = "(CASE m.status WHEN 'VERIFIED' THEN 0 WHEN 'UNVERIFIED' THEN 2 WHEN 'STALE' THEN 10 ELSE 20 END)";
// Provenance weighting: human-authored and
// knowledgebase memories outrank agent/miner-authored ones as a tiebreak.
// Small relative to the status penalty β€” verification still dominates.
const provenancePenalty = "(CASE WHEN m.created_by = 'human' OR m.created_by LIKE 'knowledge%' THEN 0 ELSE 1 END)";
// Recency-aware tiebreak: fresher memories edge
// out long-untouched ones. Pinned memories are curated reference β€” exempt.
const recencyPenalty = "(CASE WHEN m.pinned = 1 THEN 0 ELSE MIN(1.0, (julianday('now') - julianday(COALESCE(m.updated_at, m.created_at))) / 180.0) END)";
let rows;

@@ -314,3 +321,3 @@ if (ftsQuery) {

WHERE memories_fts MATCH ? ${where}
ORDER BY (rank + ${statusPenalty}) ASC, m.confidence DESC
ORDER BY (rank + ${statusPenalty} + 0.5 * ${provenancePenalty} + 0.5 * ${recencyPenalty}) ASC, m.confidence DESC
LIMIT ?`)

@@ -322,3 +329,3 @@ .all(ftsQuery, ...params, limit);

.prepare(`SELECT m.* FROM memories m WHERE 1=1 ${where}
ORDER BY ${statusPenalty} ASC, m.confidence DESC, m.created_at DESC LIMIT ?`)
ORDER BY (${statusPenalty} + 0.5 * ${provenancePenalty} + 0.5 * ${recencyPenalty}) ASC, m.confidence DESC, m.created_at DESC LIMIT ?`)
.all(...params, limit);

@@ -571,2 +578,94 @@ }

}
// ---------------------------------------------------------------- scratchpad (short-term working memory)
// Session workspace: intermediate findings, plans, hypotheses.
// TTL-expiring, local-only (never synced), never becomes durable memory β€”
// promote anything worth keeping via write()/propose().
scratchpadWrite(content, opts = {}) {
const now = new Date();
const ttlHours = Math.min(Math.max(opts.ttlHours ?? 24, 0.1), 24 * 7);
const entry = {
id: randomUUID(),
sessionId: opts.sessionId ?? "default",
content: content.trim(),
createdBy: opts.createdBy ?? "agent",
createdAt: now.toISOString(),
expiresAt: new Date(now.getTime() + ttlHours * 3_600_000).toISOString(),
};
this.db
.prepare("INSERT INTO scratchpad (id, session_id, content, created_by, created_at, expires_at) VALUES (?, ?, ?, ?, ?, ?)")
.run(entry.id, entry.sessionId, entry.content, entry.createdBy, entry.createdAt, entry.expiresAt);
return entry;
}
/** Read scratchpad notes (newest first). Expired entries are purged first. */
scratchpadRead(sessionId, limit = 50) {
this.purgeExpiredScratchpad();
const rows = (sessionId
? this.db
.prepare("SELECT * FROM scratchpad WHERE session_id = ? ORDER BY created_at DESC LIMIT ?")
.all(sessionId, limit)
: this.db.prepare("SELECT * FROM scratchpad ORDER BY created_at DESC LIMIT ?").all(limit));
return rows.map((r) => ({
id: r.id,
sessionId: r.session_id,
content: r.content,
createdBy: r.created_by,
createdAt: r.created_at,
expiresAt: r.expires_at,
}));
}
/** Clear scratchpad notes (one session, or all). Returns count removed. */
scratchpadClear(sessionId) {
return sessionId
? this.db.prepare("DELETE FROM scratchpad WHERE session_id = ?").run(sessionId).changes
: this.db.prepare("DELETE FROM scratchpad").run().changes;
}
/** Drop expired scratchpad entries. Returns count purged. */
purgeExpiredScratchpad() {
return this.db
.prepare("DELETE FROM scratchpad WHERE expires_at < ?")
.run(new Date().toISOString()).changes;
}
// ---------------------------------------------------------------- provenance audit
// Provenance audit: surface memories whose trust rests
// on the least ground β€” agent-authored, evidence-free, or long-unverified β€”
// so humans can periodically confirm, add evidence, or forget them.
auditMemories(opts = {}) {
const memories = this.list(10_000);
const now = Date.now();
const findings = [];
for (const m of memories) {
if (m.status === "REFUTED")
continue;
const reasons = [];
let risk = 0;
const provenance = m.createdBy === "human" || m.createdBy.startsWith("knowledge") ? "trusted" : "agent";
if (provenance === "agent") {
risk += 2;
reasons.push(`authored by '${m.createdBy}' (not human/knowledgebase)`);
}
if (m.grounding.length === 0) {
risk += 2;
reasons.push("no evidence attached β€” unverifiable, only decays");
}
if (m.status === "STALE") {
risk += 3;
reasons.push("STALE β€” evidence currently failing");
}
else if (m.status === "UNVERIFIED") {
risk += 1;
reasons.push("never verified");
}
if (m.verifiedAt) {
const ageDays = (now - Date.parse(m.verifiedAt)) / 86_400_000;
if (ageDays > 30) {
risk += 1;
reasons.push(`last verified ${Math.floor(ageDays)}d ago`);
}
}
if (risk >= 2)
findings.push({ memory: m, risk, reasons });
}
findings.sort((a, b) => b.risk - a.risk || a.memory.confidence - b.memory.confidence);
return findings.slice(0, Math.min(opts.limit ?? 20, 200));
}
/** Last mined commit sha (commit-miner cursor), or null. */

@@ -573,0 +672,0 @@ getMeta(key) {

@@ -12,2 +12,24 @@ /**

const STATUS_PENALTY = { VERIFIED: 0, UNVERIFIED: 0.004, STALE: 0.012, REFUTED: 0.02 };
/**
* Provenance weighting: agent/miner-authored
* memories rank slightly below human-authored and knowledgebase ones.
* Smaller than the status penalties β€” verification still dominates.
*/
function provenancePenalty(m) {
return m.createdBy === "human" || m.createdBy.startsWith("knowledge") ? 0 : 0.002;
}
/**
* Recency bonus: recently touched memories edge
* out long-untouched ones as a tiebreak. ~30-day e-folding; pinned memories
* get the full bonus (curated reference never "ages out").
*/
function recencyBonus(m) {
if (m.pinned)
return 0.002;
const ts = Date.parse(m.updatedAt ?? m.createdAt);
if (Number.isNaN(ts))
return 0;
const ageDays = Math.max(0, (Date.now() - ts) / 86_400_000);
return 0.002 * Math.exp(-ageDays / 30);
}
/** Embed and index one memory (call after write/approve). No-op without a provider. */

@@ -86,4 +108,4 @@ export async function indexMemory(store, entry) {

candidates.sort((a, b) => {
const sa = (scores.get(a.id) ?? 0) - (STATUS_PENALTY[a.status] ?? 0);
const sb = (scores.get(b.id) ?? 0) - (STATUS_PENALTY[b.status] ?? 0);
const sa = (scores.get(a.id) ?? 0) - (STATUS_PENALTY[a.status] ?? 0) - provenancePenalty(a) + recencyBonus(a);
const sb = (scores.get(b.id) ?? 0) - (STATUS_PENALTY[b.status] ?? 0) - provenancePenalty(b) + recencyBonus(b);
return sb - sa || b.confidence - a.confidence;

@@ -90,0 +112,0 @@ });

@@ -164,2 +164,40 @@ #!/usr/bin/env node

});
server.tool("scratchpad_write", "Jot a SHORT-TERM working note for the current session: intermediate findings, plans, hypotheses, task state. Auto-expires (default 24h) and is never synced. NOT durable memory β€” use memory_write/memory_propose for knowledge that should persist across sessions.", {
content: z.string().min(1).describe("The note to jot down"),
session_id: z.string().optional().describe("Session/topic key to group notes under (default 'default')"),
ttl_hours: z.number().min(0.1).max(168).optional().describe("Hours until the note expires (default 24)"),
}, async (args) => {
const entry = store.scratchpadWrite(args.content, {
sessionId: args.session_id,
ttlHours: args.ttl_hours,
createdBy: "agent:mcp",
});
return {
content: [
{
type: "text",
text: `Scratchpad note saved (id=${entry.id.slice(0, 8)}, session=${entry.sessionId}, expires ${entry.expiresAt}).`,
},
],
};
});
server.tool("scratchpad_read", "Read short-term working notes from the current session's scratchpad (newest first). Use at session start or when resuming a task to recover in-flight state. Expired notes are purged automatically.", {
session_id: z.string().optional().describe("Only notes for this session key; omit for all"),
limit: z.number().int().min(1).max(100).optional(),
}, async (args) => {
const notes = store.scratchpadRead(args.session_id, args.limit ?? 50);
const text = notes.length === 0
? "Scratchpad is empty."
: notes
.map((n) => `- [${n.sessionId} Β· ${n.createdAt.slice(0, 16)}] ${n.content}`)
.join("\n") +
"\n(Scratchpad notes expire automatically β€” promote durable learnings with memory_propose.)";
return { content: [{ type: "text", text }] };
});
server.tool("scratchpad_clear", "Clear scratchpad working notes (one session key, or everything). Use when a task completes and its intermediate state is no longer needed.", {
session_id: z.string().optional().describe("Only clear this session key; omit to clear all"),
}, async (args) => {
const n = store.scratchpadClear(args.session_id);
return { content: [{ type: "text", text: `Cleared ${n} scratchpad note(s).` }] };
});
server.tool("memory_verify", "Re-run cheap evidence checks (STATIC_CHECK, COMMIT_REF) and update memory statuses. Use before relying on VERIFIED memories if the repo may have changed, or to verify specific memories by id.", {

@@ -166,0 +204,0 @@ ids: z.array(z.string()).optional().describe("Specific memory ids to verify (prefix ok); omit for all"),

+16
-96

@@ -1,102 +0,22 @@

Elastic License 2.0
MIT License
Copyright (c) 2026 Anup Khanal
## Acceptance
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
By using the software, you agree to all of the terms and conditions below.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
## Copyright License
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
The licensor grants you a non-exclusive, royalty-free, worldwide, non-sublicensable,
non-transferable license to use, copy, distribute, make available, and prepare
derivative works of the software, in each case subject to the limitations and
conditions below.
## Limitations
You may not provide the software to third parties as a hosted or managed service,
where the service provides users with access to any substantial set of the features
or functionality of the software.
You may not move, change, disable, or circumvent the license key functionality in
the software, and you may not remove or obscure any functionality in the software
that is protected by the license key.
You may not alter, remove, or obscure any licensing, copyright, or other notices
of the licensor in the software. Any use of the licensor's trademarks is subject
to applicable law.
## Additional Use Grant: Small Team License
**Free for organizations with 10 or fewer total employees/users.**
If your organization has more than 10 employees or users (including all affiliates
and entities under common control), you must obtain a commercial license from the
licensor. Contact: https://github.com/anup-khanal/aidimag/issues (commercial licensing inquiries)
For determining the user count, "users" includes anyone who:
- Runs commands using the software
- Uses AI agents connected to the software
- Accesses dashboards or IDE extensions provided by the software
## Patents
The licensor grants you a license, under any patent claims the licensor can license,
or becomes able to license, to make, have made, use, sell, offer for sale, import
and have imported the software, in each case subject to the limitations and conditions
in this license. This license does not cover any patent claims that you cause to be
infringed by modifications or additions to the software. If you or your company make
any written claim that the software infringes or contributes to infringement of any
patent, your patent license for the software granted under these terms ends immediately.
If your company makes such a claim, your patent license ends immediately for work on
behalf of your company.
## Notices
You must ensure that anyone who gets a copy of any part of the software from you also
gets a copy of these terms.
If you modify the software, you must include in any modified copies of the software
prominent notices stating that you have modified the software.
## No Other Rights
These terms do not imply any licenses other than those expressly granted in these terms.
## Termination
If you use the software in violation of these terms, such use is not licensed, and
your licenses will automatically terminate. If the licensor provides you with a notice
of your violation, and you cease all violation of this license no later than 30 days
after you receive that notice, your licenses will be reinstated retroactively. However,
if you violate these terms after such reinstatement, any additional violation of these
terms will cause your licenses to terminate automatically and permanently.
## No Liability
**As far as the law allows, the software comes as is, without any warranty or condition,
and the licensor will not be liable to you for any damages arising out of these terms
or the use or nature of the software, under any kind of legal claim.**
## Definitions
The **licensor** is Anup Khanal.
The **software** is the software the licensor makes available under these terms,
including any portion of it.
**You** refers to the individual or entity agreeing to these terms.
**Your company** is any legal entity, sole proprietorship, or other kind of organization
that you work for, plus all organizations that have control over, are under the control
of, or are under common control with that organization. **Control** means ownership of
substantially all the assets of an entity, or the power to direct its management and
policies by vote, contract, or otherwise. Control can be direct or indirect.
**Your licenses** are all the licenses granted to you for the software under these terms.
**Use** means anything you do with the software requiring one of your licenses.
**Trademark** means trademarks, service marks, and similar rights.
{
"name": "aidimag",
"version": "1.0.17",
"version": "1.0.18",
"description": "Persistent, verified memory for AI coding agents. CLI: dim.",
"type": "module",
"license": "SEE LICENSE IN LICENSE",
"license": "MIT",
"author": {

@@ -13,7 +13,7 @@ "name": "Anup Khanal",

"type": "git",
"url": "git+https://github.com/anup-khanal/aidimag.git"
"url": "git+https://github.com/AiDimag/aidimag.git"
},
"homepage": "https://aidimag.com",
"bugs": {
"url": "https://github.com/anup-khanal/aidimag/issues"
"url": "https://github.com/AiDimag/aidimag/issues"
},

@@ -39,3 +39,3 @@ "bin": {

"engines": {
"node": ">=18"
"node": ">=22"
},

@@ -42,0 +42,0 @@ "scripts": {

@@ -10,5 +10,5 @@ <div align="center">

[![npm version](https://img.shields.io/npm/v/aidimag?color=blue&logo=npm)](https://www.npmjs.com/package/aidimag)
[![License](https://img.shields.io/badge/license-Elastic_2.0-blue.svg)](./LICENSE)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
[![Documentation](https://img.shields.io/badge/docs-aidimag.com-blue)](https://aidimag.com)
[![Node](https://img.shields.io/badge/node-%3E%3D18-brightgreen)](https://nodejs.org)
[![Node](https://img.shields.io/badge/node-%3E%3D22-brightgreen)](https://nodejs.org)

@@ -23,8 +23,12 @@ [**Documentation**](https://aidimag.com) β€’ [**Getting Started**](https://aidimag.com/getting-started) β€’ [**AI Dimag Cloud**](https://cloud.aidimag.com) β€’ [**Pricing**](https://aidimag.com/pricing)

**AI Dimag** gives any MCP-compatible agent (Claude, Cursor, Copilot, Windsurf…) a **persistent memory** of your codebase that survives across sessions β€” decisions, conventions, gotchas, failed approaches, **guardrails**, and reusable **skills** β€” stored as **falsifiable claims with grounding evidence** in `.aidimag/` next to your code.
**AI Dimag** is a memory system **for software engineering** β€” not a general-purpose "AI memory" app. It gives any MCP-compatible agent (Claude, Cursor, Copilot, Windsurf…) a **persistent memory of your codebase** that survives across sessions β€” decisions, conventions, gotchas, failed approaches, **guardrails**, and reusable **skills** β€” stored as **falsifiable claims with grounding evidence** in `.aidimag/` next to your code.
### 🎯 The Difference: Verified, Not Just Stored
The subject of memory is your **repository**, not your preferences or chat history. Every capability β€” evidence, git-hook verification, guardrails, pre-commit checks, path-scoped recall, session scratchpad β€” exists to serve day-to-day development work.
Every memory carries **evidence** (a shell check, an anchored commit, a test) that `dim verify` re-runs against the current repo. Beliefs that stop being true go **STALE** instead of silently misleading your AI.
### 🎯 The Difference: Claim-and-Verify, Not Store-and-Retrieve
Most memory systems **store** text and **retrieve** whatever is similar later β€” a stored fact is assumed true forever. That's dangerous in a codebase, where a confidently-retrieved stale fact is *worse* than no memory at all.
Every AI Dimag memory carries **evidence** (a shell check, an anchored commit, a test) that `dim verify` re-runs against the current repo β€” automatically, via git hooks, on every pull, checkout, and rebase. Beliefs that stop being true go **STALE** instead of silently misleading your AI.
### ✨ Works with Every AI Tool

@@ -45,3 +49,3 @@

Requires Node 18+. Ships two equivalent binaries: `dim` (short) and `aidimag`.
Requires Node 22+. Ships two equivalent binaries: `dim` (short) and `aidimag`.

@@ -109,5 +113,24 @@ ## πŸš€ Quick Start

### πŸ“ Scratchpad & Provenance Audit
`dim scratch` (and the `scratchpad_*` MCP tools) hold short-term session notes β€” TTL-expiring, never synced, never durable memory. `dim audit` lists memories resting on the weakest ground (agent-authored, evidence-free, stale, or long-unverified) so you can fix them up like a dependency audit for your repo's knowledge.
### 🎨 Web Dashboard & Extensions
`dim ui` plus VS Code and IntelliJ extensions.
## πŸ₯Š How It Compares
AI Dimag follows a **claim-and-verify** model; other memory systems follow **store-and-retrieve**. The short version:
| | Conversational memory layers | Vector-store memory plugins | Hand-maintained context files | **AI Dimag** |
|---|---|---|---|---|
| **Built for** | Chat assistants remembering *users* | General recall over embedded text | Static instructions for coding agents | **Coding agents in a living repo** |
| **Unit of memory** | Extracted facts / chat summaries | Embedded text chunks | Prose | **Falsifiable, typed claims with evidence** |
| **How memory gets in** | Automatic capture | Automatic embedding | Manual edits | **Human-gated review queue** |
| **When the code changes** | Nothing β€” stored facts stay "true" | Nothing | File silently rots | **Evidence re-runs via git hooks; broken claims flip STALE** |
| **Trust model** | Write-time label, never re-checked | Similarity β‰ˆ trust | "It's in the file" | **Verification status + decaying confidence; trust-ranked retrieval** |
| **Enforcement** | None β€” injection only | None | Hope the model reads it | **Guardrails + pre-commit `dim check` + `memory_critique`** |
| **Failure mode** | Confidently recalls outdated facts | Retrieves similar, true or not | Instructions drift from reality | **Says "this went STALE" instead of guessing** |
Full comparison: **[aidimag.com/comparison](https://aidimag.com/comparison)**
## πŸ“– Documentation

@@ -148,8 +171,14 @@

## πŸ’° Pricing
## 🀝 Contributing
**Free for teams of 10 or fewer users** under the [Elastic License 2.0](./LICENSE).
Contributions welcome! See [**CONTRIBUTING.md**](./CONTRIBUTING.md) for dev setup, project principles, and the PR checklist. All participation is governed by our [Code of Conduct](./CODE_OF_CONDUCT.md).
For larger teams or commercial use beyond this limit, a commercial license is required. See [**Pricing & Licensing**](https://aidimag.com/pricing) for details.
## πŸ’° License & Pricing
**AI Dimag is open source under the [MIT License](./LICENSE)** β€” free for everyone, any team size, forever. Use it, fork it, embed it.
The entire local-first product is free: CLI, MCP server, verification, guardrails, skills, IDE extensions, local dashboard, and self-hosted team sync (`dim serve`).
Want team sync without running a server? **[AI Dimag Cloud](https://cloud.aidimag.com)** is an optional managed sync subscription β€” that's how the project stays funded and open source. See [**Pricing**](https://aidimag.com/pricing).
---

@@ -156,0 +185,0 @@